Legacy code is cancer

Original author: Bruno Skvorc
  • Transfer
More and more often, I see that people are shying away from the latest technology, choosing in favor of backward compatibility. “We cannot raise the minimum requirements for PHP to 5.5, because we have 50% of users still on 5.4,” they say. "There is no way to upgrade to Guzzle 4+, we have a backend on version 3 and redoing it too long and expensive." And the best argument from WordPress: “We cannot come to a complete OOP, because most users are sitting on shared hosting with 5.1 or do not know about MVC.”


Legacy code is a big NO

Perhaps this is a controversial conclusion, but I am firmly convinced that there is no place for legacy code in modern systems. I will say a few words before you start sharpening your pitchfork and light the torches. I mean, there should not be the slightest reason to support the old functionality, you add updates retroactively to the old version just because some people still use it. Even if the majority of these people - do not do so.

To clarify: correction of errors of previous versions until their long-term support contract is over, yes. Adding new functionality that can be released in the version X, in the version X-1, just so as not to offend users X-1- absolutely and 100% not. Similarly, addingX-1code in version X just because it can come in handy should be declared invalid. If you still charge people for X-1and build your upgrades on top of that, then you have a very bad business plan.

Although, who am I to carry such nonsense? I have never supported a large project with a bunch of stakeholders and support, which is developing super-slow and makes everyone happy. How long it takes and how it works is not important, because it can potentially work 100 times safer and 1000 times faster, right? Not really. My biggest kid was the site of a major publisher with a complex backend on ZF1.

If you have ever done projects onZF1, you know, this framework is a whirlwind of crutches and anti-patterns. When the application started showing signs of deterioration due to increased traffic, I rebuilt the frontend of the most heavily used part of the application to work completely through ajax and API calls. The load fell sharply and that’s why we bought enough time to transfer all the other parts of the application to Zend Framework 2. Those who did something similar know that this is still the same mixture of crutches and anti-patterns, but slightly less dense.

I'm trying to say that big changes and refactoring can only happen if capable people are behind them. If all you do is solid agile rallies and brainstorming sessions, then no amount of LTS contracts can stop you from looking silly for five years.

Even if you are doing free and / or open source work, of course you should not break compatibility for X-1 users. By doing them a favor, developing old versions, with a global update, you may encounter a potential loss of backward compatibility. Just learn one thing - they must either adapt or die.

So still, why should we banish legacy code from modern systems?

Infinite LTS Curse

By subscribing to the “support everything as long as we can” approach, you are buried in a bottomless pit, and looking at yourself after a few years, when you are forced to support four different versions of your product, you will bang your head against the wall because of that did not refuse users V1 and V2, if they could. In an attempt to maintain an audience, developers often go beyond their capabilities and are not doing justice under the yoke of tons of legacy code. For the same reason, WordPress ended up in its current state. Do not let yourself be chained to the old version.

These users are a dead weight, they must be destroyed no matter how much money they bring to you. Give them the opportunity to move and move on; if they are capable, they will catch up with you. If not, then they are not worth it.

By supporting older versions for too long, you cast a WP curse on yourself. Old versions are vulnerable, their support requires more and more effort and effort to fix bugs. Better spend these hours creating new versions and hire developers who will help users with the transition.

You are alienated and negatively affect advanced users

The last time I came across a desperate legacy code when I installedCMS , which turned out to be a very difficult task in the environment Vagrant- not only because of problems symlinkthat are now widely known to everyone (even the creator Vagrant), but also because many people leave outdated versions CMSbecause some modules / plugins have not yet released their updates. Since there are no module updates, why update the kernel? Why rush things if you are not ready for them?

Leaving the legacy code in the new version, you end up with a Frankenstein monster that seems to be working, but badly, and the new code, which has potential, cannot develop it due to a mess in the inherited code base. Although this approach makes the work of development companies that were still stuck somewhere in the 90s easier, but at the same time, it becomes more difficult for advanced users to work with the product. With decisions made for a crowd that has long and hopelessly lagged behind technology, you alienate and negatively influence advanced users who can bring much more money.

You know how this happens: spend too much time supporting functionality in legacy browsers, and as a result, none of the users even use these features. The same applies to users of libraries or content management systems - those who do not care about outdated CMSdo not care about what you are doing in new versions, so do not worry about the excessive support of older versions more than you really need.

Failures sometimes portend success

Of course, sometimes this is simply not possible, and such exceptions are very rare and valuable training material. One of the curious cases of versioning is 2nd and 3rd Python. Python is an amazing language, with it you can do almost anything. But it will not work as well as a language built specifically for your purpose, this is a common shortcoming of master-of-all-hands languages ​​- they can do something very well, but there is no option to do the work with them impeccably. When Python 3 arrived, it introduced some features that break compatibility and users of the 2nd version could no longer easily switch to it.

Most of the excuses like “Py3 packages are still not enough” and “we have too much code in Py2 to rewrite all this” get answers from me - “port what you need” and “Poor programmers, they are forced to write code”, respectively. I agree, there were several arguments , but those, as a rule, take as an example projects that were originally incorrectly designed, which resulted in their absurdly large size.

In fact, now the confrontation of Py2 against Py3 has already turned into a rift, on both sides of which there are programmers. But many do not think about the fact that by the time Python 4 comes, people who so vehemently refused to switch to version 3+ will still remain on Py2 and incompatibility will become even greater. By that time, they could already have mastered a different language, and not resist changes in the current one. On the other hand, those who “dared” to cross the fault and rewrote their code to 3+ without any hesitation will receive all the latest features of future versions with zero labor costs.

The backward compatibility scrap effectively enough lazy compartment and prepared Python for a new generation of developers. In my opinion, this is a huge success. Programming, like many other areas of life, is still based on the survival of the fittest - and if you cannot use new technologies adequately - get out of the way, do not bother those who can.

Applications vs Libraries / Packages

There are also questions about the confrontation between applications and libraries. In other words, if the “do not expire” rule is applicable to applications, for example, when a new release of a framework is received, should it also apply to libraries?


Libraries that receive the bump X+1should clearly follow the path to progress - from the moment your version of the library becomes publicly available, only support the bug fixes of the latter.

Applications that use such libraries are in a more difficult situation due to their dependency on the API, which may be subject to change. A sensible approach would be to wait for feedback from the community on stability before starting the transition. During the transition period, both the old and new versions of the library / framework can remain in use, and after all the necessary parts have been upgraded, the old version must be removed. It doesn't take long, right?

There is no big enough application

“But Bruno, some applications are huge and it will take several months to rewrite them,” you say. “The transition from ZF1 to ZF2 is a year of work!”, To which I reply: nonsense. There is no big enough web application which upgrade will take similar terms. I will go even further and say that in fact there are no web applications large enough that could be comparable in size to Symfony or Zend.

Of course, all that I said does not apply to any of these frameworks, they are hypercomplex hippos from a very professional code, but if you follow the concept of separation of tasks, encapsulate your services and API your application, then you can write the front separately from the backend , which, in turn, frees you from many obstacles to the current code, regardless of the size and / or popularity of the application - provided that you have programmers in your team, not theorists. No matter how many structures and algorithms you know, the main thing is to know how to use them to be effective enough during migrations.

Update Scheme

What is the best way to update? There is only one acceptable option for software updates, which developers should adhere to regardless of the popularity of their application / library:

  1. Create a new branch for the new version
  2. Mark old version / branch as deprecated
  3. Publicly declare how much more you will support the old version
  4. Warn all users of the old version
  5. Implement new features ONLY in the new branch, in the old one - bug fixes
  6. When the lifetime of the old version expires, break off all the ends. Do not correct, do not advise, generally remove the mention of the old version from the documentation. Kill her.

After completing this procedure, you will never inherit trouble.

One of the projects following this path is Guzzle . He has a 3+ version and 4+ for those who want to live up to date and always be at the peak of progress.


As a web developer, I strongly believe that legacy code should be thrown away when it comes to new features or major version upgrades. If you have a large project that uses the code of versions released two or more months ago, then you should stop all operations with it and rewrite it with a fresh version, especially if the products you use are critical for business. There are no large enough applications in the world that need more than two months to complete the transition, and if there are, they should be rewritten from scratch - the web is much easier than you always expected.

What do you think? Should legacy code be stored indefinitely? A certain number of versions? Maybe not all? How do you feel about legacy code in third-party projects? Should a developer worry about legacy issues in the libraries he uses? Am I wrong in this article? With this post I wanted to start a discussion about this - after all, my views on problems and experiences are just my point of view. Let me know in the comments below.

Also popular now: