How To Spot The Legacy Code Writer In Your Company (Hint: It’s You)

Also available in Czech, kindly translated by Aleš Roubíček

TL;DR

Writing Legacy Code* is a distributed activity.

*In Working Effectively with Legacy Code Michael Feathers defines Legacy Code as “Code with no tests“, which reflects the perspective of legacy code being difficult to work with. I’ll stick to this definition.

Long version

Oh, no! Bad code again

You have been assigned a new task.

Your mission: to add a simple feature to a Corporate project. You know almost nothing about it, but the feature request sounds feasible. You can easily accomplish the task.

wtfperminute

This is what you think. Until you open the code base.

A mess. An ugly, irritating, awful work. Your monitor displays a disappointing mix of spaghetti and lasagna code.

It seems that someone intentionally performed the complete collection of all the known anti-patterns.

You rage at him. You loathe the guy who made this. This huge ball of mud doesn’t allow you to work properly. You wanted to code as a Software Craftsman, and adhere to all your beloved patterns: you promised yourself to produce code with high cohesion and low coupling and blah blah blah.

But you simply can’t.

How could you? The bad guy used globals everywhere, he wrote most of the methods as static, he created a lot of duplication, and he made no tests at all.

Oh, and look here! He’s using a magic number

int HRW_UND = 12;

What does the damn 12 mean? And what’s HRW_UND? May he burn in hell, the bastard.

Yes, because you have no time. The deadline is close. And you’ve been asked for an additional feature, not for the refactoring of the whole system.
This is not your mission, is it?

Legacy Code always wins over Good Code

Ok, the bad ass who wrote this jungle won: you will add your feature without the strict, inflexible rigor you promised. After all, you didn’t produce this mess, it’s not your fault and you have to accomplish your task.

So, you are forced to under-perform.

But it’s not your fault.
You are justified, since it’s the Legacy Code Writer’s fault.

Here, you would like to add a dependency using Dependency Injection, but it would be such an effort to change the whole thing. It’s all static, no interfaces, too much inheritance and a lot of new statements. You have no time, and the ugly code base doesn’t help. Fuck the Inversion of Control.
You better add a static method to the Service Locator. You hate Service Locators, but the Legacy Code Writer, the bastard, wrote one, and removing it is too much expensive.

Copy-Pasta
Look here! He mixed two responsibilities in the same method, producing a monster. You need just a single behavior of this method, but the code is tangled, and it’s pretty hard to separate the two behaviors, especially because the class lacks a test harness.
You end up copy-pasting a selection of lines.

And look at this method! 50 lines of code, with 18 if-thens. Why hasn’t he used a Strategy Pattern? Refactoring the whole thing or adding the 19th if?
The choice comes easy. Legacy Code Writer, may you burn in hell!

Finally, you need a new configuration parameter. You would introduce an injected property. The bad guy used HRW_UND = 12. You have to bleed like hell to refactor his work.
So you end up adding HRW_UND = 98

It’s all his fault.
You produced legacy code because of the Legacy Code Writer.

No one writes legacy code from scratch

Chances are it all sounds familiar to you.

Now, please, reassess your firm belief: are you really sure that the bad Legacy Code Writer does really exist?

Are you sure that every time you find legacy code there must be a mythical programmer who wrote from scratch the whole thing?

200px-Kevin_Spacey

You know what I believe? That this is a very pleasant and comfortable cop-out.
This mythical programmer does not exist at all.
There isn’t a single person who is responsible for the whole mess. The bad programmer, the Legacy Code Writer, is a fictional character we invented to justify our little, continuous, sins.

I came to the conclusion that writing Legacy Code is a distributed activity.
Each of us contribute, eventually not intentionally, to this crowd, incessant, slow process.

Don’t waste your time trying to seek him: we are that programmer.

Legacy Code is like dust: as you rest, it settles, no matter if you aren’t looking at it

dust pc2
Every piece of code, when managed by several people, naturally tends to spaghetti or lasagna code. Entropy tends to increase. It’s a totally natural process.

Don’t you believe?
Try to see this from another point of view: code does not naturally tend to get, perform and look better just because a bunch of programmers are adding features to it.

This is a natural, simple law, to me: live code tends to get worse unless an explicit, intentional force is applied against the natural tendency to entropy increase1.

Legacy code is what you naturally obtain if you don’t constantly apply a zealous, exceptional and tireless rigor to code maintenance.

Two bad choices don’t compensate: they accumulate

The first version of the project was probably well designed. It came easy: the programmer had a white paper in front of him.
Gian Marco Gherardi loves to say:

Everyone can write good code from scratch, and sustain the pace the first 3 months.
Only professional programmers can maintain it for the next years

So, don’t criticize the first programmer. He probably made a good job.
But as soon as you rest for a while, you’ll be producing a little, tiny particle of Legacy Code.

The problem with particles of Legacy Code is that they are like gems of corals: they tend to attract other bad code, and they soon become as hard as hell to remove.

ImageJ=1.39u unit=inch

Introduce a temporary variable, and be sure that the next programmer will feel justified to attach some code to it.

Copy/Paste only 5 lines of code, just a tiny duplication, and expect that the future yourself will modify one of the two copies, forking the code and creating the next headache for several people who will struggle in order to refactor it. Probably, resigning.

Miss a unit test, and be sure that the next team will be thinking

The previous programmer felt that covering this with tests was too much expensive. Well, now it’s surely even more expensive than before. Let’s continue his school.

You made the rot start. This is what is called the Broken Windows Theory.

The moment you add a field to a class, the decision to mark it private or public may seem silly and unimportant, especially when you have such a great pressure from your business stakeholders. The programmer who stops and spend minutes thinking about it, could seem a fool to someone.
Should you find him fool too, you will probably get the less expensive decision.
Only 2 months later, some one will realize how hard is to decouple your class from the rest of the system, because your public field invited everyone to use it. The rollback then could be too much expensive: you will have contributed to the birth of Legacy Code.
Congratulations.

Every time you don’t code as a perfectionist, you are condemning the future programmer (and the future yourself) to underperform.

Uberto Barbini once wrote:

Every Brown Fields used to be a Green Field once, and every Green Field will become a Brown Field sooner or later. It’s the order of nature.

Unless an exceptional, intelligent, unnatural, force is constantly applied against this tendency.

So, what to do?

Working on software (even on a single, trivial line of code) you are always in front of a dichotomy:

  • apply the Boy Scout Rule: always leave the base camp better than you found it;
  • or just add the little behavior you are supposed to deliver, eventually making the code just a little more tangled. Just a little little bit.

I realized that the second behavior is the most common one. Admit it.
Being exceptionally strict, every single minute of your career, is too much stressful.

Nowadays, when I face a piece of Legacy Code, I don’t swear against the mythical Legacy Code Writer, since I’m starting to understand that the culprit it’s me, it’s you, it’s almost every one I know.

I started thinking that my mission is always to refactor systems (even when my boss just asks me to add a feature), and that I should be refactoring deeply, at the point that adding the requested feature should be a natural consequence, almost a collateral effect.

A clue that our mission is more “refactoring existing code” than “adding new features” is that it’s much more likely that projects fail because they become unmaintainable, rather than for a lack of features. Don’t you agree?

Since I don’t want to be confused with the legendary Legacy Code Writer, and offer a cop-out to the future Legacy Code Writers, I promised myself to always be a perfectionist (with deadline) or, at least, to try to sense the technical debt I am leaving behind me.

Each little contribution may make a difference between a tangled system and a sustainable future.

Cheers, @arialdomartini

Footnotes

After some weeks I wrote this post I discovered that my sentence

Live code tends to get worse unless an explicit, intentional force is applied against the natural tendency to entropy increase

is astonishingly equivalent to the Lehman’s Law:

As an evolving program is continually changed, its complexity, reflecting deteriorating structure, increases unless work is done to maintain or reduce it. (Meir Manny Lehman, 1980)

I swear I didn’t know it the before I wrote this article. But I’m very happy that my arguments are supported by such a great authority in Computer Science.

Thanks

Many thanks to Eleonora Gorini.

We once worked on a project together, and we must have shared the same mindset, since one month after I quit the Company she wrote me a email telling me how rewarding — rather than demotivating — is refactoring her current, unbelievably messy project.
This what makes the difference between a common programmer and a pro.

Thanks for the unexpected present, Eleonora.

25 thoughts on “How To Spot The Legacy Code Writer In Your Company (Hint: It’s You)

  1. Great blog post! I believe a lot of developers (including myself) occasionally find that the easiest solution is to blame the legacy code writer. Thanks for reminding us to be better versions of ourselves 🙂

  2. > I started thinking that my mission is always to refactor systems (even when my boss just asks me to add a feature)

    I have come to think the same exactly. Now, before I correct *any* bug, I always refactor a bit and/or add a test. Over the years, it has greatly paid off. It was difficult and painful at first, but my skills in refactoring vastly augmented, and in the end all the legacy spaghetti code has progressively become clean and pleasant (it took years of bug-fixing).
    I also have become more able to identify when some code looks bad (i.e. 18 ifs) but isn’t actually a maintenance problem in practice; and code that looks good (patterns) but that apply to a problem that doesn’t exist anymore — code that should be just deleted/refactored into the simplest thing that covers only the right needs.

    1. Sometimes I think that it’s a pity that most of the (wonderful) technical books I read are about writing code on a Green Field: they try to teach me the best way to write the best code, starting from scratch.

      Thanks a lot! But, unfortunately, in our profession, “from scratch” is a very, very rare case. Brown Field is much more common.

      A real pro should find herself at ease with Green Fields as with Brown Fields.

      That’s why I’m starting to realize that books like Working Effectively With Legacy Code are as much enjoyable as Test Driven Development by Examples.

      It’s just a little harder to convince others that the game is the same fun. I’m enjoying it.

  3. Broken window theory, that’s what I thought just a couple of sentences before you quoted it.

  4. A few observations:

    1. “Legacy” to me implies something inherited (often in a metaphorical sense) or something which is here as opposed to what will be here (once the current project is done) resp. something older with which the current project will interact.

    In other words, “legacy” need not be bad. Often it is just outdated (e.g. due to use of an older technology) and in worst-case scenario the quality can be better than the replacement…

    Of course, this does imply that “it’s you” remains the correct answer—just in a very different sense than above: Assuming a sufficiently successful product/company, today’s code will be outdated and require replacement in, say, five, ten, or twenty years.

    2. I have seen a great number of developers who write very poor code even in the first version of a program/module/whatnot—sometimes to the point that your use of “legacy” would immediately apply. If not, sufficiently poor that we are half-way there and the second half will follow far sooner than with better original code (through mechanisms such as the ones you describe.)

    3. Refactoring and good automated tests can both prevent and turn around such developments—provided that enough developer time is allocated.

    1. 1. With Legacy Code I mean “code without tests” (Michael Feathers) “which you need to modify (Alberto Brandolini).
      It needn’t be old — even if aged code is much more likely to be Legacy.

      2. I agree. It happens often.

      3. I agree. TDD is the cure.
      As for the time argument, I believe that if there’s not enough developer time allocated, it won’t be working sploppily to generate the extra time needed; on the contrary, it will make things worse.

  5. Brandolini has recently generalized his definition of Legacy Code, to all Legacy: “Legacy is anything you are afraid to change”. By consequence, there can also be messy or imperfect things that you are not afraid to change. If you create technical debt, but you know how to fix it and you plan for that, it’s not legacy. That’s why I propose to make a clear distinction between Managed and Unmanaged Technical Debt: http://verraes.net/2013/07/managed-technical-debt/

    1. I remember I once proposed to Alberto Brandolini that Michael Feathers’s definition of Legacy Code “code without tests” should be extended with “that you need to change“.

      I don’t agree so much with Alberto that fear is necessary for the definition of legacy code (even if it’s very evocative). On the contrary: I see that a lot of developers dare to work on convoluted and dangerous code with no fear (and no tests), just because they ignore that the lack of tests makes that code “legacy”.

      I agree with you that the mere fact that some code is without tests does not mean it’s legacy, since it could be perfectly working and not needing any evolution.

      The need for change, rather than the fear of change seems much more meaningful to me.

    2. Partially echoing my earlier comment: I consider such use of the word “legacy” to be highly unfortunate.

      Firstly, it can lead to confusion when communicating outside an “inner circle” with a common terminology.

      Secondly, this type of word use/annexation/what-not brings dangers in areas like rhetoric, truth-finding, and similar. Consider e.g. various political parties or ideologies and their respective takes on, say, “freedom”, “justice”, or even “democracy”. (Notably, countries with “democratic” in the name tend to be anything but according to Western standards for democracy.) In the end, we could end up with a great many camps all declaring code conformant to the standards of the other camps to be “legacy” (or some other, similarly misused, word).

      I strongly recommend using more precise and accepted terms with a lesser degree of subjective evaluation, e.g. by calling code without test case, radical as it may seem, “code without test cases”.

  6. You’re right, the need for change is the determining factor.

    I have observed again and again that developers who do not address the legacy, are in fact afraid to change code. Their workaround is to build new features using Append-Only Development. The (false) idea is that by only adding code, you will not risk breaking old code. Instead of extraction and composition, they use copy/paste and inheritance as their code reuse strategy. When a block of code does become obsolete, they rather comment it than remove it.

    To me these are all indicators of fear, and perhaps attempts to save face. But of course, YMMV 🙂

  7. I would like to say you congratulate for this blog as you are made here in very informative discussion as it would be more important just to be aware. As a developer, I am and I think more of the developers face the problem while dealing with legacy code that often cause for the complexity.

  8. I’ve been as guilty as anyone of making code a little bit worse than it was before, due to a deadline, pressure from the boss, etc. I can deal with code that’s gotten a bit soft around the edges. What I can’t deal with is code that has fundamentally broken design, and has for years… something that just a *little bit* of reflection would have told the original programmers was a bad idea… typically it’s something that is too light weight for the purpose that it ultimately gets used for — a data structure that wasn’t up to the task, an oddly specific class that can’t be extended, whatever. Having to jump through hoops to work around fundamental such design issues is maddening.

  9. Unfortunately, your argument about the “mythical legacy code writer” is wrong.

    I know people whose starting project from scratch and don’t want to use Dependency Injection or Interface Segregation for instance. They have very explicit idea: “It increases the complexity”.

    Those people are real and most of them already know what is Dependency Injection, but they prefer Singleton Pattern. Because most probably they never successfully implemented the DI to their projects before.They mostly prefer technical debt on something but after that they always facing with new issues. And this becoming a vicious cycle.

    So in short time not thinking about disasters may be OK, but when the disaster came if you don’t have a plan, you will be going to mess everything. This is the biggest difference between legacy code and clean code IMHO.

    Sorry for this bad news 🙂

    1. I’m glad you are not amongst those developers who don’t know Dependency Injection.
      And I’m happy for us all that whenever you stumble upon their bad code you never use it as an excuse to diminish your code clearness but, on the contrary, you always find the energy to fix it.
      I must admit I am not always able to maintain this integrity.

  10. I think this problem isn’t only connected to projects that multiple developers work on. I find that it can also happen on projects where I’m the sole developer. I start out writing a code base that’s nice and tidy, and correctly decoupled. Then, down the line, I’m asked to add a new feature to the existing code base. Upon looking at my own code, I realize that there’s a need for a deep or deepish refactoring in order for the new feature to be implemented in a tidy and correct manner. However, because of the time limits imposed by those who requested the feature, I’m forced to implement the feature without refactoring upfront. Then I’ll find myself in the same mess as you describe, even if there has been no-one else interfering with my code in the mean time.

  11. Get over the legacy code business. All code is legacy code. It is not needed for make test of you are skill programma. Best skill programma are for China people.

  12. Maintain the excellent job mate. This web blog publish shows how well you comprehend and know this subject. dbecdkcbfgdb

  13. In my career, I have inherited legacy code done by:

    1. bad project manager with mission impossible deadline.

    2. bad “no care” developer at start – “we found a contractor to do this”. He made mess, taken money and run away.

    3. “we have no time to fix this now, common man, just add new feature”.

    So, I prefer to be fired than dealing with stupid assholes.

  14. I certainly enjoyed reading this. In all the time that I have been programming, I can say that I am an over perfectionist. I have to wind myself down so I can make some real progress, otherwise I will just be looking at the same code for an hour.

    Nice inside joke with the good code vs. bad code. I could barely breath.

Leave a reply to Arialdo Martini Cancel reply