A few months ago I worked in an outstaff. This is not a place where you need enthusiasm and faith in the great goal of the project. Together with the team, they simply sold me to customers, and at meetings it was important how many tickets I closed. Perfectionism attacks are rather a bad thing for such a place, but I couldn’t do anything with them. I paid a lot for one of them, got into the infernal crunch and spent the worst two weeks in my life.
My company asked me to come to Moscow for two weeks, to the main office, to work with the team. The request is reasonable, I agreed. A few days before departure, I took another ticket. It was necessary to add a feature to the SDK for a dozen projects that we are working on. This SDK is an impassable legacy that no one in their right mind touches. There is such a swamp that even hard-burned engineers are superstitiously baptized - if only everything worked, how it works. When we need to fix something or add a feature, we carefully back the code with another crutch, and without breathing send it into the hands of CI and QA.
I planned to do the same. The essence of the task was simple. The SDK itself is a wrapper over a very clever binary system that can lift a VPN. She communicates with our back-end, receives all sorts of configs, authorizes the user, fetches a list of countries for VPN, and so on.
I just needed to add another request to the backend, which receives a list of servers. It looks extremely simple - I added a method to the public API, put a request for the desired URL inside, slap the model, parse, give it away. Then he taught client applications to use this request, and you're done. I did the task in two days.
A very big mistake. There are no simple tasks. With one eye I was already sorting through Moscow bars, where I needed to drink all the beer, with the other I saw the first problem. We do not just get a list of servers, we also use one of them in order to raise a VPN tunnel. Previously, we used only countries for this. But some genius decided it would be a great idea to store countries as a list of strings. Of course, the essence of a VPN node is too capacious a thing to fit on a single line.
As a result, a bunch of methods were gathered in the SDK that take a string (country code) and do something there, and now all these methods should take a country code, or a server object - and do something there. Everything that we did in the SDK code and applications with countries, now we need to do both with countries and with servers.
When I understood this, for some reason I was delighted. The task scope now allowed me to slightly refactor the swamp, full of shit in which I swim all the time. I declared my desire, and now I know for sure - they gave me the go-ahead with an expression, as if they had impaired a disabled woman at the price of Maserati. I agreed on a weekly budget and set to work.
There were a couple of days before departure. The week looked like a whole infinity. I decided, until I write the code, I just shake the decision in my head, and as soon as I arrive, I will quickly scatter everything.
When a normal person goes on a business trip, he buys tickets in advance, solves the housing problem, warns his wife, and generally does everything right. I am a short-sighted idiot who trampled to Moscow with his wife and two children. I had to take my wife because I told her about the trip three hours before departure. In two hours - I just started looking for a driver with a minivan, because my car broke down. I rented an apartment at the same moment. Six hours of travel with screaming children and a dissatisfied wife ended as they should have - the apartment was exactly two thousand times smaller than it looked in the pictures. The smell was worst of all - it stank of grass. The maid who hosted us said that they poisoned the cockroaches (cockroaches !!!). My wife believed her, and telling my wife that I distinguish the smell of grass from cockroach poison is not in my interests.
I came to the office, they gave me a laptop. For half a day I installed the environment, I got to know everyone. When everything downloaded and installed, it began to work. The anger from the terrible move has not yet cooled. I opened the project with the SDK, once again cursed tightly to myself, and thought: “What the hell? Yes, here it’s all to hell with dogs, they have to throw it away and redo it. ”
I told the lead about this, and he, of course, answered: “I agree, man.” A project of 15 thousand lines, each of which is a crime against humanity. I was about to fix it in two weeks. And if I had ten hours of travel and three children, what would I suggest a lead? Invent in a week a new PL specifically for the project?
But it seemed to me, if I put in order at least something - it would become easier for me. Now I crucify this govnokod, I will do everything as it should, we will live. I once read books about how to do deep refactoring. There were a lot of clever thoughts about how you paint steps, write tests, think through your actions so that you have confidence at every step - the project works, nothing is broken. Books and best practices, this is very cool, but I never learn from the mistakes of others. At the very beginning of my career, I read a lot about all sorts of basic things, but did not believe in them until I needed to expand the functionality of my first project.
The task of any rake is to give you a forehead. As long as your unsuccessful decisions do not give a shit about you personally, you will not consider them unsuccessful.
I began to do my refactoring without relying on practice. I mentally divided the project into large logical pieces. This was not reflected in his file structure, so I took this file structure to hell. I created a daddy of the highest level: here we have work with the back, here we work with VPN, here helpers, there are exceptions. Existing unit tests, I also rejected. Then it seemed to me that the govnokod had no right to life, but now I understand that I jumped out of the plane and did not take the parachute, because it was put in violation of the instructions.
I began to transfer the govnokod from old daddies to new ones, simultaneously putting it in order. Nothing special, just following good practices. There is a class that describes the user model; it has all the properties optional, all with setters. Turning it into a kid’s inheritance hierarchy. All ridonly, all through the constructor. So that users of this class always know exactly what they have in their hands. There is a govnomethod on four hundred lines - you hit on sub-methods. You see a file with a thousand responsibilities - you turn it into a thousand files with one duty. There is a class that has the Initialize method, which must always be called immediately after creating the class - you apply the “state” pattern
The concepts of good and bad code are very vague. All my life I will learn to distinguish between them. But frankly shitty code I can see now. See and correct. I tried not to shovel the top-level architecture very much and to change the external SDK API as little as possible.
It’s a bad decision to stay at work for a couple of hours when your wife is waiting for you in a strange city, in the worst hut in the world, and she has two young children who roar from stress without ceasing. I lingered for seven hours. On the first day of work, I sat in the office until one in the morning and copied two percent from the strength.
“At home” a deserved scandal awaited me, in the morning - shitty instant coffee. I rented an apartment five minutes from the office, but I was so strange and scared in this terrible city that the first three days I went to work by taxi.
Everyone in the office was very joyful - people were going to drink in the evening. These Muscovites do this every day. They called me too. I promised to try, although I myself knew for sure - there are no options. He set to work again.
Then it seemed to me that I myself was to blame for all the household shit, because I took things on their own. It was better to look for an apartment, to prepare better, to foresee all the problems with family and life. A bunch of problems that could not have been. Looking at the code, I thought - this is what half measures lead to.
He quickly decided that my approach yesterday was too compromise. If you redo it, then that's all. I ceased to be limited to renaming and splitting. Every file I was about to process was carefully analyzed. I’ve been on this project for quite some time, I understand well what the business wants here and what our code should do.
But here I began to constantly meet code that I do not understand - what problem it solves and why it was written at all. And I made a decision - if I do not understand why the code is needed, then the code is not needed. I happily began to send this digital garbage to the underworld.
This approach has created new problems. The public project API was incorrect. There were many methods with an absurd signature (for example, a method takes a string and an object that contains the same string). Many methods had idiotic names. And in general, if you use this SDK with the API that it had, you can break the application in ten thousand ways.
By this moment, I have already gone far enough. The project was not going to, and there was no chance that it would be going in the next few days. Everything that I change in it, I can check only at the end of the work. And since I changed so much already, why not change the public API then? Well, I was smart enough to make copies of the repositories from the SDK and applications, so I could duplicate the API changes in these copies and find errors. After all, I could not even compile the main version of the SDK.
The problems started right away. Changed the signature of one method? Catch plus two hundred errors of compilation time in applications. Because of the wrong signatures, the wrong parameters were passed to the methods, and the applications worked. At the same time, you work in four IDE windows. After I changed the three methods and cured all the errors with them, I thought - since the work has already been done, it will be foolish to change only part of the stupid API - we need to redo it further. And he got into the process even deeper.
I constantly had to make decisions, mostly ethical. So I know how something should work, and now I see the code that should do it. I read it and understand that he does not. But this code is in prod, it passed QA. And who is the fool? How can I take on this responsibility? At first, I thought for a long time about each such case, consulted with a lead and a team. But it took too much time.
I realized that I was working slowly, because I was afraid to break something, tried to figure it out somewhere, to be careful. Then I said to myself: “I’m smarter than the one who wrote this shit, I know what needs to be done, I have a task and a budget for refactoring, and I have the right to everything.” Wimps understand and worry, a man knows what he wants, and does without asking too many questions.
At some point, I entered a strange state of a robot that methodically processes one file with code after another. Without checking anything, without doubting anything. I see a bad code - change. This chain raises new problem areas, repairing them. In each I replace the govnokod with something tolerable.
It looks like a blitz in chess. You play a game where the most important thing is to think over all the possible options, but you have so little time that you cannot even think about what options are worth it to think over them. I always lose blitz. For the second day I did a lot. Here is just a code that you cannot verify - it is not a code. And I knew about it.
One evening, I remembered that I had promised my wife to go to Ikea. The apartment in which we lived was terrible, and my wife wanted to buy any cozy garbage. I wrote code all day, my brain was exhausted, but I had to go. Put car sharing, the nearest car was twenty minutes away. I called a taxi to get to the car. Imagine a typical Moscow dead end jammed with cars to the eyeballs. At the end of this, Renault Kaptur was waiting for me. Having defeated the lagging UI of the Delimobile, I was able to open this bucket. There was a surprise waiting for me - automatic gearbox. I never drove a machine, but I thought I could handle it. If you want to go forward, you need to move the handle forward, if backward - backward. No. I realized that the car was not going exactly at the moment when it was still possible to save the situation.
Depressed brake. Naturally, with the wrong foot, because there are only two pedals in a stupid machine, and my brain misunderstood everything. Wild jerk, the car got up. Two millimeters to the Mercedes S-Class.
Somehow I crawled out of the dead end and realized that all the cars of the world are around me, but I have no idea where and how to go. I drove to the “house”, took my wife, drove around midnight on the most terrible city on earth, missed a dozen turns, left a fortune in ikea.
We returned at four o'clock in the morning, my brain shut off as soon as my eyes saw the bed.
At eight in the morning I was at work again. There everyone is happy again, discussing their Moscow affairs and bars that I can’t reach. I have the SDK again. With a heavy head, I skimmed through the changes I had made. Everything is very bad. Tomorrow the rally was dragging on, and I need another three hundred years to finish it.
I have a good skill - when I get a big ticket, I always decompose it into several small ones. Hence my working flow. One ticket, one or two commits, one pull request. Therefore, in principle, I am not able to break the task into several commits in the coding process - I usually do not need this. But stress finished me off, and the skill failed. In the process, I turned a small task into a giant one, but began to work on it the way I would work on a small one. Without a system, stupidly where I saw the problem, there I fixed it. He did not divide the process into logical steps, but simply as the last June grabbed at once.
You can’t turn it back. The main problem remained - I could not build and run the whole solution, and had no idea how it would work. It was very scary, on the last day I looked at diffs and realized that I had rewritten almost the entire project. It was damn scary.
Everything always slides on the last day. The trip ended, my extended time for refactoring - too. My wife called and said that we were being evicted at one o’clock in the afternoon, although I agreed on a late evening. Naturally, this meant that I could not finish the last day to the end.
Together with three gallons of cold sweat, what came to me when I took up this task, planned refactoring, agreed to a business trip, took children, took off a stash for two weeks - I am the most important idiot in the whole world. It’s worth the psychiatrists to sort out my motives, but I decided to go all the way. Either to punish myself, or to some irrational belief that I am doing everything right.
With a burning ass, I freeze everything in an hour, launch CI and dump home. All the way I scroll through my code in my head and I'm absolutely sure that nothing will work. Too many changes, there will be a hundred bugs, and we will refuse this refactoring.
When I returned home, the news was waiting for me - the project is working. Lead corrected a couple of little things, my changes passed autotests and right now they are successfully undergoing manual testing. I did not believe it. I spoiled the code for a home wheelbarrow, collected and manually checked all the scripts that I did not trust. It worked. It worked !!!
I was so proud of myself. The quality of the code base has grown many times, several features have appeared, I cut out a bunch of bugs that testers have not yet managed to get to. Liba could be used comfortably, and I proved myself to be professionally fit.
Gusts of enthusiasm and self-sacrificing labor - the lot of naive fools. I would be glad to prove the opposite to myself with this story. But a few days after returning, more news was waiting for me.
The customer decided to abandon the project. Almost a hundred people in the outstaff (half of the entire company) was "waiting for a new project." Consider - limbically unemployed. And the remoters in such matters are cut in the first place. I was told to move to Moscow permanently and switch to another project. Or we will fire you.
I asked what would happen to the old project.
At such moments, people become painfully honest. Flattery, embellishments and corporate ethics are no longer needed. I was told that this code is no longer needed by anyone in the whole world, sent to the dustbin of history. The products themselves will be used for a very long time, but no one will support the code base. All this is the quality and readability of the code, scalability, ease of use, unambiguous behavior - everything was done in vain.
I listen and understand that I could just make a feature and walk through the bars. But I sat at night, did not sleep, suffered scandals for the sake of a good code base that no one else needed. Enthusiasm is a lying thing. You say that you are ready to work, even if there is no gain in this. But when you are told that your highest gratuitousness really does not bring benefits - for some reason it kills. You hoped to change with your impulse all universal wrongness.
I think I understood everything perfectly from the very beginning. I just did not want to be a pragmatic guy.
Therefore, I said that they could fire me to hell, and let their mothers be brought to shitty Moscow so that their fat asses would tear non-rubber to a billion small pieces. He politely wrote about this, and stayed at home. There are enough tasas in the world that just need to be made.