📜 ⬆️ ⬇️

Legacy code is cancer

More and more often, I see that people shy away from the latest technologies, making a choice in favor of backward compatibility. “We cannot raise the minimum requirements for PHP to 5.5, because we have 50% of users by another 5.4,” they say. "There is no way to upgrade to Guzzle 4+ , we have a backend on version 3 and redo it for too long and expensive." And the best argument from WordPress: “We cannot come to a complete OOP, because most users sit on shared-hosting with 5.1 or do not know about MVC”.

Nonsense.

Legacy code is a big NO.


Perhaps this is a controversial conclusion, but I firmly believe there is no room for legacy code in modern systems. I will say a few words before you begin to sharpen your pitchfork and light torches. I mean, there shouldn't be any reason to maintain the old functionality; you add backdating updates to the old version just because some people still use it. Even if these people are the majority - do not do that.

For clarification: correcting errors of previous versions until their long-term support contract ends, yes. Adding new functionality, which can be released in version X , in version X-1 , only in order not to offend users of X-1 - absolutely and 100% not. Similarly, adding X-1 code to version X only because it can be “useful” should be considered invalid. If you still charge X-1 and build your upgrades on top of this, 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 a support that develops super-slowly and makes everyone happy. How long it is done and how it does not matter, because potentially it can work 100 times safer and 1000 times faster, right? Not really. My biggest child was the site of a major publisher with a complicated backend on ZF1.

If you have ever done projects on ZF1 , then you know, this framework is a whirlwind of crutches and anti-patterns. When the application began to show signs of deterioration due to increased traffic, I rebuilt the frontend of the most heavily used part of the application to fully work through ajax and API calls. The load plummeted and we bought enough time to transfer all other parts of the application to Zend Framework 2 . Those who did something similar are aware that this is still the same mixture of crutches and anti-patterns, but a little less dense.

I am trying to say that big changes and refactoring can happen only when there are capable people behind them. If all you do is solid agile meetings and brainstorming, 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. Doing them a favor by developing older versions, with a global update, you may face the potential loss of backward compatibility. Just learn one thing - they must either adapt or die.

So 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 a few years later, when you find yourself forced to maintain four different versions of your product, you will be banging your head against the wall because that did not abandon users V1 and V2, if they could. In an attempt to keep the audience, developers often go beyond their capabilities and are unsupported under the weight of tons of legacy code. For the same reason, WordPress turned out to be in its current state. Do not allow to chain yourself to the old version.



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

By keeping the old versions too long, you cast a WP curse on you. Older versions are vulnerable, their support requires more and more efforts and efforts to fix bugs. Better spend these hours creating new versions and hire developers who will help users with the transition.

You are alienated and have a negative impact on advanced users.


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

Leaving the legacy code in the new version, you end up with the Frankenstein monster, who seems to be still working, but poorly, and the new code with potential cannot develop it due to the disorder in the inherited code base. This approach, although it makes the work of development companies that are still stuck somewhere in the 90s, is easier, but at the same time it becomes more difficult for advanced users to work with the product. The decisions made for the crowd, which has long and hopelessly lagged behind the technology, you alienate and adversely affect the advanced users who can bring much more money.



You know how it happens: spend too much time supporting the functionality in obsolete 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 the outdated CMS , do not care about what you are doing in the new versions, so do not bother with over-dimensional support for older versions more than you need in reality.

Failures sometimes foreshadow success.


Of course, sometimes this is simply not possible, and such exceptions are very rare and valuable teaching material. One of the interesting 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 much as a language built specifically for your purpose could, this is a common flaw in the "master-of-all-up" languages ​​- they can do something very well, but there is not a single option to do the work with them. impeccable. When Python 3 arrived, some features were introduced into it that break compatibility and users of version 2 could no longer easily switch to it.

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

In fact, now the confrontation between Py2 and Py3 has already turned into a rift, with programmers on both sides. But many people don’t think that by the time Python 4 is in place, people who so vehemently refused to upgrade to version 3+ will still remain on Py2 and the incompatibility will become even greater. By the time they could have mastered another language, and not resist changes in the current one. On the other hand, those who "ventured" to step over the fault and rewrote their code to 3+ without long hesitation, will receive all the latest features of future versions with zero labor costs.



The breakdown of backward compatibility is quite effectively the lazy bay and has prepared Python for the 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 interfere with those who can.

Applications vs Libraries / Packages


There are also questions about the opposition of applications and libraries. In other words, if the “not obsolete” rule is applicable for applications, for example, when a new release of the framework is received, should it be extended to libraries?



Yes.

Libraries that have received the X+1 bump must clearly follow the path to progress - since the moment when your version of the library became publicly available, support only the bug fixes of the latter.

Applications that use such libraries are in a more difficult situation because of their dependence on APIs, which may be changing. A sensible approach would be to wait for feedback from the community about 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 should be removed. After all, it does not take much time, right?

There is no large enough application.


“But Bruno, some applications are huge and rewriting them will take several months,” you say. “The transition from ZF1 to ZF2 is a year of work!”, To which I reply: bullshit. There is not a large enough web application, the upgrade of which will take similar deadlines. I will go even further and say that there are actually no large enough web applications that can be compared in size with Symfony or Zend.

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

Update scheme


How best to update? There is only one acceptable software update option that developers should follow regardless of the popularity of their application / library:

  1. Create a new thread for the new version.
  2. Mark old version / branch as deprecated
  3. State publicly how much you will support the old version.
  4. Warn all users of the old version
  5. Implement new functions ONLY in the new branch, in the old one - bug fixes
  6. When the life of the old version expires, cut off all the ends. Do not correct, advise, generally remove the mention of the old version from the documentation. Kill her.

After going through this procedure, you will never get into trouble inheritance.

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

Conclusion


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

What do you think? Should the 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 problems in the libraries he uses? Am I mistaken 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.

Source: https://habr.com/ru/post/250667/


All Articles