Taming the beast: legacy code, tests and you

Legacy code is an “old” code, the age of which can be both 2 months and 10 years. Often, developers wrote about it, which the company vaguely remembers. Perhaps they did not exist at all, and the legacy code was born with the Universe during the Big Bang. Since then, the requirements for it have changed many times, the code was corrected in the “it was necessary yesterday” mode, and no one wrote the documentation, like the tests. The Legacy code is confusing and fragile, it does not show either the beginning or the end. How to approach him?


Hereinafter shots from the series "Rick and Morty." Authors Justin Royland and Dan Harmon.

You need to get to him with tests, but get ready for pain. Problems will begin already from the moment you decide to undertake such a project. You need to understand why you want to undertake it, convince management to approve testing of the legacy code, and help your colleagues. After that, questions arise, where to start the study, which tests to run first and how not to break everything? But the main thing is how not to fall into despair when you realize that there is no end to work.

Kirill Borisov 12 years in the industry, over the years he has come a long way in crutches, broken code and rotting frames of old systems: from monolithic accounting systems to authorization microservices. The journey awarded him experience and stories that he will share in the form of valuable advice.


I have a dream - someday to work on a new project. Everything will be fine in it from the very beginning and as fresh as the first snow: tests, architecture and meaning. But this is only a dream, because for 10 years I have been selling my talent for money and moving from one legacy project to another.

During this time I have no nerves left, but I can save yours by sharing my experience of interacting with legacy. I will tell you how to tame the beast (legacy code): work with code and people, implement testing, whether it needs to be done and how developers relate to this.

What will not be here:

  • Tips for writing tests. Many books, articles and various videos cover this question.
  • Discussion methodologies. BDD, TDD, ATDD - all at your discretion.
  • All that may violate the NDA. People have a long memory, and lawyers have long arms.

What is legacy code


There are many definitions. I believe that this is a " fairly old " code from 2 months to 10 years old. The legacy code is confusing and fragile, but like a giant snake devours its tail.

This is what does not allow you to start testing it calmly. All developers, from beginners to experienced ones, when they come to a legacy project, grab a spear of tests and rush to kill this monster. The spear breaks, and with it people. As a result, there remains a developer without signs of life, who has been working on a legacy project for decades.

Is it possible to overcome this beast? Yes, but preparation is needed.

Training


Fighting the beast is not as important as the preparatory phase. It begins with three questions for oneself.



“Why am I doing this?” Seriously, why? After all, there are only two options.

  • , , , .
  • , .

Do you want to work for the comfort of others or your own glory? If for the latter, then with the first success interest will fade away, you will fade away, everything will fade away. You will switch to something else, and the rotting remains of your endeavors will clog the project for a long time to come.

“Do I know what I'm doing?” If you wrote tests, you know. If not, then before you rush at the monster, master the basics: write 3-4 tests, cover a small part of the code, fill your hand and feel the power.

"Do I have time for this?" It's great to intervene in the code with good impulses and improve it, working for the future. But perhaps there is no time for this when the present burns. If so, then the project needs you, not a bright image of the future.

When you answer all the questions in the affirmative, proceed to the next step.

Reconnaissance


Examine the structure of the project . Do you have an idea about the structure of the project, the components and the principle of work? Surely yes, but perhaps it does not coincide with reality. It is important to understand what you have to face before starting work. Devote some time to go through the project and study it thoroughly.

Make a dependency diagram . No project lives in a vacuum. Databases, external services, libraries - all this can be used in the project.

What has been done to you? You may not be the first to fight the beast. Examine the practices of the “ancestors” who burned down and left the project.

After reconnaissance, we move on to hostilities.

Fight with organization


The first round is a fight with your organization. The main thing in it is your manager, direct boss.

Manager . He is not as scary as he seems. This is an ordinary person with ordinary needs: to deliver the project on time and without unnecessary problems, get money and bonuses for it and live on.

The leader is not against your undertakings. He is against you rushing to the project with shouts: “Tests! Tests! Tests! ” If you do so, he will look at you as the person who spends his time and slows down the rest.

Show the benefit. The manager speaks the language of good, time and money. Understand that they are driven by the desire to close the project on time and get more results for less resources.

The test should not be submitted like this:

- Oh, it will be cool!

Our ideas should be promoted as follows:

- In the last quarter, we had 50 crashes that could be fixed at the product development stage. You can fix it using tests. They will confirm that the changes did not change the functionality, if we do not expect it. We will save the hours spent on resolving these problems and reduce the amount of the penalty that was paid due to a broken system.

Saying "optimization, money, saving time", you speak the language of the manager. When he hears these words, he is imbued with the idea. He sees in you not the next rabid programmer who is passionate about fresh technology, but a person who is interested in improving the product. He will not approve all your ideas at once, but he is highly likely to propose Proof Of Concept.

Proof of Concept increases the chances.Provide the manager with a separate isolated piece of code, a subsystem that is covered by tests, starts and runs. This can be done if you take one of the sore bugs that pops up at a certain frequency and try to catch it and fix it with a test. PoC will confirm your intentions, show that you have a plan and your work brings results.

Do not promise much. For the manager, the numbers are important: what are the results, the timing and by what forces. But the manager is a creature greedy for results. Do not promise too much from the start. If you promise to solve all problems at once, the manager will go to the authorities with this. The authorities will say: “Great!”, But will reduce funding and cut deadlines in the hope that we will hand over the system much earlier.

When we agree with the manager, we turn to those with whom we have to work every day.

Colleagues


They do not like change. A typical colleague on a typical legacy project is a person who has lost faith in life and the future. He is not inclined to change and resigned himself to fate: "I am here forever, there is no way out of the swamp." The problem is that you start to stir up water in this swamp. You demand that he write and run some tests, but he wants to do his job, close the task and go home.



Engage your colleagues with benefits — explain why they will feel better. For example, they constantly spend time and effort, remaining after work to heal some bugs. Press on it: “If you don’t deploy a broken code for production, you won’t have to spend time fixing it. We’ll write tests, we will catch such code, it will break less. ”

Show patience and empathy.You communicate with people - ask why they are worried about your idea? Suggest finding a common ground to understand each other. This is the main tactic for working with people: do not quarrel, do not clash your foreheads, be friendlier.

You may be prevented from presenting the idea before the meeting of colleagues at the next team stand-up. The mechanism of “group thinking” works in the team: no one wants to make a decision, everyone looks at each other and sees that no one is burning with enthusiasm.

There is one dirty trick to solving this problem. Unfortunately, in my life I used it more than once.

Divide and rule. Go to one of your colleagues at lunch or in the corner and say: “The whole team has already signed up, you are the only one slowing down the process. Maybe we can find a common language? ”

After going through all in turn, you sign everyone. Everyone will be ashamed to admit that they thought that everyone else had already signed up. It is dishonorable and terrible, but it works. Use this technique responsibly and as a last resort. Remember - you still have to work with these people.

When we sorted out with colleagues, we are waiting for another greedy beast.

Fight with the car


This is the trick of the code called the product. Let's start with the basics.

Dismantle the trash. It is necessary to test so that with a minimal impact on the system to obtain a verifiable result. But any legacy-system is full of data: they have been added for years since the launch and affect the behavior of the system. Therefore, it is necessary to test "from scratch."

Prepare a “spherical system in a vacuum”: empty the data sources, make the minimum configs that the system launches, disable all possible “hacks” and “features”. Make the system start up. If it starts, you have the minimum data set that is necessary for functioning. This is already a good starting point - a "clean slate".

Using some measurable effects, for example, pressing a certain button, you will get a measurable working result. With this, you can proceed to the next step.

Unravel the data. Any legacy project works on the principle of "must be delivered yesterday." Everything that you went to university or read in books does not work here. When you start testing, you will encounter, for example, a cyclic dependency, impossible to recreate in the program, but necessary for functioning.

Start with the “main object”. To deal with the dependency forest, try to think about which object is the main one. For example, for the warehouse accounting system, the main object is the “box”. A “shelf” object is associated with it, and a “row” object is associated with a “shelf”.

Recreate the required minimum.If you look at the links between objects and go deeper into the dependency tree, you can determine the necessary minimum of data for dependent objects. You need to recreate it so that the system works and can function to test your functionality.

Do not be afraid to change links. You may have to roll up your sleeves and dive deep into this mess: delete and change links, change the structure of the database. You came to improve the system, so do not be afraid to make changes.

We pass to testing. For confusing old products, a good strategy is smoke tests.

Smoke tests


The concept of "smoke testing" came to us from the world of electronics. One engineer assembled a giant circuit with a bunch of light bulbs and wires. But before I started testing, I just plugged the circuit into a power outlet. If smoke started, then something went wrong.

In information systems, the concept of smoke tests is quite simple. Imagine a web service, it has an endpoint. Let's try to send him a GET request without parameters. If for some reason the product suddenly broke (error 500), then something went wrong.

Smoke test is a good start . This is a test that tests some functionality and makes it clear that the system is working or broken. Even a simple request to the simplest endpoint already affects more than 1% of the code. With such small tests, we are preparing a springboard for further testing.

Smoke test reveals many problems. It is possible that for the entire period of the functioning of the service no one guessed to send a request without parameters.

Use this tactic to cover several main entry points into your program: login / password input form, basic web services, buttons. This is something you can already show to the manager and colleagues.

Function tests


These are not tests of individual classes or a method, but the highest possible level of testing a certain part of the functional.

Imagine the functionality to “generate a report in the service”. Instead of checking individual parts, we test the situation of the request to create a report with certain parameters and get a data file. It is not necessary to know the mechanism for generating the report, but if the service gives certain output data with certain input data, then this black box with some probability works as it should.

Coverage of the main functionality with such tests allows you to quickly start and immediately covers large areas. You will be sure that the code works at least approximately as you imagine, gain more confidence, fill your hand and reveal even more problems.
Functional tests are a means, not an end.
It’s easy to get hooked on the functional test needle: “I’m testing real functionality! This is what users are facing. ”

A functional test involves large chunks of code that can interact with gigantic amounts of data. Therefore, 3-4 functional tests are good, 10 are worse, and thousands of tests that take 9 hours are too much. Unfortunately, this also happens.

After functional tests, take on unit tests. But I will not talk about them - you already know everything.

We went through the basics of machine testing and return to the main topic. Colleagues and manager are not the worst enemy in a battle with legacy. The worst enemy is yourself.

Fight with oneself


Be prepared for the fact that the path will seem endless . Work for a week in your plan will take six months without the prospects of completing the project.



Resistance is inevitable . All allies will eventually begin to doubt, try to get off track, persuade them to quit tests and move on to features. Be prepared for this. Remind everyone why you got involved in all this, how much effort and time was invested. Weak argument, but might work.

No one guarantees success . Even if you show heroic efforts, put all of yourself into work, your project can still burn out, and the crusade with testing will end in nothing.

This is normal, this is not the end of life and career. This is not even confirmation that you are a poor professional. The only conclusion here is that this particular project failed.

But then you have experience and knowledge. Next time, when you take a new spear in your hand and your horse accelerates to another windmill, you will be ready to break this spear too, but later, by a different method and with less damage.

Now offensive, bitter and eternal.

Parting words


Do not be afraid of feedback. I had to step into this trap and see how others fall into it. I did something and brought colleagues to boast: “I did it!” But suddenly it turns out that my convenient mechanism is inconvenient for colleagues, and I did not ask.

Write tests, try what you implement . Often the introduction of a new test framework is fascinating, but you do not write the tests themselves. Then it may happen that as soon as you write them, you will understand that you cannot use the tests. Perhaps colleagues also see this, but are silent, or simply do not write tests.

Help colleagues with problemseven if they don’t ask for it. Help doesn’t mean taking all the work on yourself - it relaxes colleagues and relieves them of responsibility, and the “bus” number drops to unity. Then you become a human tester: something is broken, CI is red, a test guide. Help within the framework of the reasonable.

The "bus" number is not a joke. You cannot always drag the project on yourself. Everyone can burn out, go on vacation or quit. Therefore, pass on to your colleagues your knowledge and responsibility, which is necessary to cope without you. This will help to avoid unpleasant calls when you relax on the beach, and CI is red again.

Improve Testing Mechanisms. Many problems can be avoided simply because slow tests suddenly became fast. Previously, they occupied 20 lines of code, but now one. You did not notice this, because once you wrote something and forgot: “It works - don’t touch it!” But this rule is not always applicable.

You are not the center of the universe. Again, I repeat that the "bus" number is not a joke. More than once I came across a situation when a person began testing, and then received an offer to the project fresher: he left everything, ran away, but did not leave comments and documentation. Everything works until a new commit, but it is impossible to fix it - no one understands how everything works.

I do not want you to be this person. Do not turn into a limiting factor.

  • Write the documentation.
  • Conduct trainings.
  • Share your experience.

When all the colleagues are on the same level as you (plus or minus), the process will turn from a race of one person into a team relay race with the passing of the flag. Only through the support of your colleagues will you succeed. If you are alone on the project, think that someone else after you will also suffer alone. Give your follower a friend in the form of documentation, do not let die alone.

27 Moscow Python Conf++ Python 2 Python 3 — 2020 .

, (fb, vk, twitter) telegram- . !

All Articles