IT Employment optimize

Preventing code rot

Software developers call neglected code that stops working code rot. Chip Camden explains why this often occurs and how to avoid such trouble.

I sat alone eating breakfast at my favorite diner. The cook ambled out of the kitchen to the soda machine to fill her cup with ice, but she couldn't get any out. She called the owner, who came out of the back and fiddled with the machine a couple of times before it finally gave ice. "It just wanted some attention," she said as she left.

A system that needs periodic attention indicates poorly designed automation, I thought to myself. Yet, most systems do seem to require this kind of babysitting. If someone cares for the systems on a regular basis, they run indefinitely without a hitch; if the systems are neglected for too long, they fall apart.

In software development, we call that code rot. When nobody pays attention to a section of working code, it suddenly doesn't work any more. Sometimes after examining the problem, I conclude that it could never have worked -- despite the fact that it had been running for years. This happens so frequently that it almost doesn't surprise me any more. The code didn't change itself (at least, most code can't do that), so how did this happen?

You might suspect that the metaphor "rot" is somewhat inaccurate. Physical materials degrade spontaneously -- or do they? If you could place a tomato in a sealed environment and remove all microbes from it, it wouldn't rot. Nothing rots by itself -- it needs some agent acting on it.

While extending a metaphor doesn't establish fact, it seems to shed light on this case. Code rot often occurs because the requirements for the code have shifted imperceptibly -- as imperceptibly as bacteria infect organic matter. Nobody remembers that the environment in which this code operated successfully for years differed subtly from that in which it is now expected to function. Changes made elsewhere place new, unforeseen, and undocumented demands on this code. Common sense says that it should handle this case, but nobody thought of that specific combination of circumstances when it was designed.

Code can also degrade because it relies on the behavior of other code. The documented behavior of a library or framework often doesn't include specific details on which clients rely. The author may feel free to change an undocumented behavior -- he or she may even consider it a bug. Combine a few of those changes together, and you can easily lose the trail back to "how did this ever work?"

What can we do to prevent code rot?

We can take another cue from our metaphor: avoid exposure, because exposure hastens rot. The less your software relies on other software, and vice versa, the less trouble you'll have. By "less," I don't mean fewer instances of usage, but rather fewer methods of usage. Consider the Unix 'tee' utility, for example. Other than the obligatory proliferation of GNU options, it hasn't changed significantly in decades, and yet, it doesn't rot. Why? Because from the beginning it defined a simple interface with clear expectations. Furthermore, it confines its expectations of the code on which it relies (the standard C library) to clearly documented behavior in the purpose for which it was written.

Documentation helps, but documentation is not the solution -- simplicity is. No matter how well-documented the system, if it has too many interdependencies with other systems, some of those dependencies will change in incompatible ways. Therefore, break the system down into simple, independent components that each do one thing well, and document those things. As requirements change, the alignment of those components may need to be altered, but not so much the components themselves.

You must employ discipline to stick to that philosophy. When a new requirement arises, don't give in to the impulse to just hack in support for this special case by passing in a flag. That will introduce additional, unnecessary complexity to the interface. The more you do that, the more likely you'll create a web of unseen interdependencies that will lead directly to rotten code.

About

Chip Camden has been programming since 1978, and he's still not done. An independent consultant since 1991, Chip specializes in software development tools, languages, and migration to new technology. Besides writing for TechRepublic's IT Consultant b...

16 comments
ITBoss120
ITBoss120

I suppose that when one initiates a significant discussion by referencing erroneous analogies stretched to the breaking point, that one should anticipate comments, scattered throughout the universe, seemingly unrelated and inane. Specifically I refer to the analogy of rotting fruit. The discussion is one rightly worth having but the reality centers in what is anticipated from our code. Anticipating that your coding genius has produced some gem that will survive OS changes, database updates, generator/compiler/assembler updates, managerial mental meanderings, and other environmental evolutions is shear lunacy, though there is a higher probability of surviving these than those imposed by other external influences. A myriad of matters affect code; poor spec, poor coding, operational and functionality requirement changes, governmental regulations, users ???tricking??? the software, and a never ending parade of others leading to the spine chilling, day ending term which haunts all true and purist ???developers,??? ??? maintenance. ???OMG, you want it changed???? It is foolish to ignore the fact that productivity needs and the operational environment change and that code must also change. To label this maintenance requirement as ???code rot,??? well, it is a stretch.

Tony Hopkinson
Tony Hopkinson

Mitigate, which is basically what you are talking about, I can live with. It's not code that doesn't change that rots, it's code that has or should have.

jkameleon
jkameleon

In real life, this is usually not the case, and that's why our code rots.

ITBoss120
ITBoss120

The idea of discrete units of code to simplify programs and this mitigate "code rot" was part of the modular programming concepts from the 1970s. Welcome to Paschal Mr. Camden.

Sterling chip Camden
Sterling chip Camden

Some of the most rotten code I've ever encountered came from my very own fingertips. My learning comes from experiences I'd rather not recall -- but I do anyway in order to learn from them.

Tony Hopkinson
Tony Hopkinson

to be maintainable. It's changing it without maintaining it. It's copy paste and twiddle. It's temporarily permanent hack. It's permanently temporary hack. It's bugs that remain unfixed because everybody has got used to them and they've beome a feature. It's re-use abuse. It's old language / operating system features. It's using cheap numpties to start and then competent people to keep it going. It's not realising that the requirements have changed so much since the original design, the people who did it now look like morons. It's killing your modular design in favour of monolithic for a better short term result for less effort. You can do change and maintain / increase sales without addressing any of those issues. The longer you don't though the more expensive change will be . There will always be technical debt, there will always be code in various stages of rot on any non trivial software development, simply because stuff changed while you were doing it and it's very existence will change things. Pretending it doesn't exist is the problem not the solution. I

AnsuGisalas
AnsuGisalas

"make maintenance easier for yourself" Same recipe. Maybe adding "check periodically for needed maintenance".

AnsuGisalas
AnsuGisalas

that your boss' stated expectations were so vague or hard to make sense of that you ended up coding something that could cover anything it could have possibly meant, just in case? Couldn't be, after all, your boss probably went to business school to learn to furmulate eggspegtations.

seanferd
seanferd

Or Pascal? Anyway,. funny how people still don't practice these things. Worth discussing, then, no?

Tony Hopkinson
Tony Hopkinson

Code has been rotting since we invented it, the only thing new about code rot would be someone in charge actually admitting it happens and dealing with it as part of the process instead of relying on teh self defense skills of experienced developers as constrained by their paymasters.

AnsuGisalas
AnsuGisalas

What must be done with one system may not be mandatory with another system - however, not all optional things are good to ignore. I'd say it seems that the less discipline is built into the system, the more discipline must be hammered into the developer.

Tony Hopkinson
Tony Hopkinson

Great idea in theory and a pretty good one in practice. Things to bear in mind though. It isn't make it easier for just yourself, but for your team, or perhaps the next guy. Point being that making it easier now, might make it harder later. Unit test unit test, and unit test some more. Which brings you to the killer "If it ain't broke don't fix it", broke as far as the business being concerned means a customer complained about it. If something does go wrong with your change (this does not mean it's doing it wrong now, in fact it could be it's doing it too right :( ), and management start saying things like who authorised this change etc, you can end up in deep poo... There's just no way to win this one, in fact a regular and apparently respected TR consultant and contributor claimed that code wouldn't rot if we "got it right first time". It's not an uncommon attitude in business...

Jay24/7
Jay24/7

I totally cosign this post. Transparency can have a huge impact on this. If it is admitted and addressed within the change management process code rot should be minimized tremendously. Not meant to be an insult, but on the fly subroutines lead to further fragmentation of any system. Old artifacts like flow charting, comment codes and documentation really do have a place in the industry. Getting back to that will be more good than bad.

apotheon
apotheon

Unit tests are self-reinforcing use case specifications that are, unfortunately, not quite as easy to read and assimilate as we'd like (which is half the reason documentation is still needed for use case specs).

Sterling chip Camden
Sterling chip Camden

as you said, is that requirements change over time. It helps, though, to keep components small and well-defined, because the act of adapting code to the new requirements is more like reconnecting components in different ways instead of making massive changes to a monolith. When you do have to change the purpose of something, you make that change of definition consciously. Definitely agree with you on unit testing. Essentially, that's verifying that each component meets the definition you've established for it.