This post originally appeared in my Chainline Newsletter.
I've been thinking about the half-life of code.
In his Software that Fits in Your Head talk, Dan North defines the half-life of software as (I'm paraphrasing) "the amount of time required for half of an application's code to change so much that it becomes unrecognizable."
In that talk he tells the story of working on a high quality, non-trivial application whose code's half-life was six weeks.
Yup. Six. Weeks.
I saw that talk nearly a year ago and have been distracted by its implications ever since.
The upsides of a short code half-life are significant. Imagine how much better your life would be if your application's code always reflected the most accurate, up-to-date understanding of the problem at hand. Think about how much costs would go down if you never had to navigate dead code. Consider the value of having of an application that is free of speculative additions that were thrown in to support features that will never arrive.
I want these things, and am intrigued by the thought that taking half-life into consideration might help me achieve them.
Code is read many more times than it is written. Writing code costs something, but over time the cost of reading is often higher. Anyone who ever looks at a piece of code has to invest brain-power into figuring out what it does.
Dead code and speculative cruft make it hard to decipher the intention of code. It follows that you can reduce overall costs by optimizing code for reading rather than writing. The easiest code for subsequent readers to understand would be frugal, i.e. it would be simple, correct, and without embellishment.
I suspect, however, that I'm preaching to the choir. I suspect that you already believe everything stated in the prior paragraph. It's not that you disagree that it would be a good thing to have frugal code, it's that the applications that you work on are so far removed from this ideal that you despair of ever reaching it.
In my experience, most applications are a mess. Successful business rely on long-lived applications that endure a constant barrage of new requirements. Changes are commonly made under urgent time pressure, which drives applications towards disorder. As entropy increases, it becomes harder and harder to add features except by way of one more hack. The accumulated mess leads to hacks, hacks lead to more hacks, and then you're in a loop. Velocity gradually slows, and everyone comes to hate the application, their job, and their life.
If it makes you feel any better, there's a way in which having a big mess is a sign of success. The reason your competitors don't have messes is that they went out of business. You won, and your prize is an application that betrays the ravages of time.
And, as if that's not alarming enough, I fear that the coding culture that led to your current success may be dooming you to future failure. If your existing application impedes change, nothing good will come of doing more of what you've done. If this is the state you're in, it's time to change how you write code.
Here's where the concept of half-life matters. You can lower your costs by reducing the half-life of your least stable code.
The parts of your applications that change the most also cost the most. These dynamic parts are often fundamental to your business. They are the result of lengthy, evolutionary development, and suffer from never ending churn. You depend on them the most, yet they are hard to understand, and only getting worse.
Vast other swaths of your application are likely equally unpleasant, but relatively stable. This stability essentially makes them free. It's not ugly code that costs money--it's change. Ugly code just exacerbates costs.
Time is in appallingly short supply. The good news, however, is that you're not obligated to fix things that aren't costing you money. The most efficient way to improve long-lived applications is to focus on the code that churns--this is where your efforts most pay off. For maximum effect, commit to crafting solutions that are both frugal and easily replaceable.
The last item above is key. Code that isn't easy to replace doesn't get replaced, instead it gets expanded. Its conditionals get bigger. The number of class names it knows about grows larger. These sorts of expansions tightly couple the code you're changing to other parts of your application. This coupling makes it difficult to swap in alternative implementations, which it turn leads to a long half-life for the code.
Unstable code that has a long half-life inevitably accumulates cruft. This complicates the code, and programmers hesitate to neaten what they don't understand. The trick to maintaining frugality over the course of many changes is to insist on code that's easily replaceable. Achieving replaceable code necessitates developing a culture that values polymorphic objects and loosely-coupled code.
You have a bargain with other programmers about how you will write code. Your current application is this bargain made manifest. If you're finding that the original pact has outlived its usefulness, the first step to improving your life is to start talking to one another about how you wish you were writing code. Dan's talk, and the idea of the half-life of code, can spur this discussion.
Start today. :-)
Thanks for reading,
Sandi
News: 99 Bottles of OOP in JS, PHP, and Ruby!
The 2nd Edition of 99 Bottles of OOP has been released!
The 2nd Edition contains 3 new chapters and is about 50% longer than the 1st. Also, because 99 Bottles of OOP is about object-oriented design in general rather than any specific language, this time around we created separate books that are technically identical, but use different programming languages for the examples.
99 Bottles of OOP is currently available in Ruby, JavaScript, and PHP versions, and beer and milk beverages. It's delivered in epub, kepub, mobi and pdf formats. This results in six different books and (3x2x4) 24 possible downloads; all unique, yet still the same. One purchase gives you rights to download any or all.