Traditionally, the software development process is compared to construction. The term “architect” only strengthens the associative connection between these processes. But modern realities have made this model irrelevant, because there are mechanisms that it cannot explain:
- If we make some kind of physical object or product, then why is the software never considered complete?
- If we are talking about engineering solutions, then why can we never confidently plan ahead?
- If we are architects or builders, then why do so many projects end in failure?
- And finally, with so many tips, best practices, principles, books, and software development reports, why do so many projects turn into an unbearable place to work?
I propose a translation of Sarah May's brilliant report “Livable code”, which is close to the text, where she considers all these issues.
Why is it important to have a model?
The model allows us to draw an analogy between an unfamiliar or difficult to understand process and something familiar, understandable to us. Moreover, it should not only be suitable for describing a situation when everything is fine, but, like any good analogy, the model should guide us in borderline situations.
The software development process is a combination of two related, interacting, but nonetheless separate entities: code and team.
Confirmation of this is the law of Conway. The relationship of these entities tells us that problems in the code are related to problems in the team and vice versa. Therefore, we have two options for solving them: on the code side and on the team side.
Some problems seem to be problems only with the code, some only with the team. In fact, all of them are problems with both components. Therefore, the new model should be suitable for modern realities and reflect the relationship between both components of the development process.
Description of the new model
The space in which the application lives
The construction of the building has three distinct stages: design, construction and maintenance. Previously, software development looked similar: the result of development was a finished product. It was installed by the user and transferred to support a separate group, and the developers began to do something else.
But in the world of web applications, deployments and releases several times a day, in fact, nothing is complete. A similar situation is observed in mobile applications and games, when they are automatically updated on the console via Steam (most often at the most inopportune moment). In addition, in a changing business environment, applications must be flexible and capable of changing. The development effort has to be enormous to ensure this flexibility. The same cannot be expected from buildings.
The infrastructure that we use when developing applications is more like a building. What we create is not the buildings themselves, because most application applications take up space in pre-erected structures. It sounds like a step down from architects to interior designers, but there is a lot of power in such a model. Buildings in it are the basis for our applications: the foundation is the OS, the parking level is the programming language, the building itself is the framework used, and we are developing applications already inside them.
The internal space (structure) provided by the buildings is changeable, however it is more difficult to change it than your own code. As much as the demolition of the wall is more difficult than moving the furniture. Thus, the building itself (framework) imposes certain restrictions on the capabilities and appearance of your interior (application).
Each building is optimized for its own purposes. An application can be placed in any of them, but some spaces will be better suited for it, and some will be worse. Therefore, the code that you are going to place in a preformed space will be generated in a certain way by this space.
There is still a huge amount of building work in our industry, but most of us are still working on interiors (applications).
New goal in development
If you add to all this that projects are never considered completed, you come to the conclusion that code is not what we are building, but where we live . This makes you look at the very goal of development from a different angle. The place where we live needs to be made livable : suitable, comfortable and comfortable for living. And not only for ourselves, but also for those who live with us. In the case of code, it’s not enough just to finish the product and move on. It also needs to be made livable : understandable, supported, extensible - for the team that "lives" in it.
What is Livable Code?
Livable in relation to home means that you live in a space where you can do everyday activities with comfort and ease. There is a reading lamp in the bedroom, napkins are on the coffee table, there is a box for joysticks in the living room, and there is a bike holder in the hallway.
But what livable means for a group of people depends on themselves. A comfortable home for young parents with a child is not like a house for students or for the elderly, or for a single parent with a teenager.
Livable for code means the same thing: you can change or add what you want, but without excessive complexity and annoyance. You understand what’s where, so easily jump to the right place in the code and work with it. Here the principle is the same: the fact that livable for one team is not suitable for another. What is livable for one senior and four juniors differs from livable for five seniors, and is very different from livable for an outsourced team.
People come and go, so creating and maintaining code in a livable state is an ongoing process. The appearance of a new member of the team is akin to a new roommate: he has a strange sofa, which for some reason he really loves, so you reluctantly agree to put him in the living room. And after a couple of days, a neighbor suggests replacing curtains and blinds, because there are more ergonomic ones. And the worst thing is that he washes dishes with tabs instead of spaces! But now you live together, so you have to negotiate.
It turns out that your mood and condition depends more on those with whom you live than on specific things in the apartment and even less on its layout. Also with the code: your well-being mainly depends on those with whom you work, and much less on the code itself or the framework.
How a project becomes cluttered
But what if your code looks like this room? How to make it suitable for the team? Where to begin?
Agree, it’s difficult to move around here, not to mention understanding how to put things in order. Why do homes reach this state? It is often said that this is laziness. Psychologists think differently: the house comes into such a state due to a series of seemingly insignificant wrong decisions.
Imagine how it could all start. There is a small room in which you tried to optimize the space: added several shelves and drawers, used a window sill. For a while, everything was fine: you kept order daily. But the evening came after a hard day when you did not get out. In the morning you overslept, and when you returned home, you had dinner right at your desk and forgot to remove the plate. Parents brought your old stuff over the weekend. There was no time to disassemble them and you put the boxes as is. And you feel that the room is no longer in order, but its condition is so depressing that you can’t understand where to begin to correct the situation and soon it turns into an IT. A series of small wrong decisions: the room is already a terrible mess, why not throw books on the floor?
The same thing happens with code. You open a file that is already bad, half a dozen patterns that no longer work. And the manager is standing behind you, waiting for the bug to be fixed. The first thing that comes to mind is to fix the bug with tape as much as possible and get out of here as soon as possible.
These seemingly small, local solutions are exactly what makes the code unlivable . However, there are ways to get out of this hole, which we dig for ourselves.
Salvation through Change in Habits
Have you noticed that in most cluttered and groomed houses often there are many magazines, newspaper clippings with beautiful and cozy interiors? There are also photos, projects, tips for competent arrangement of living space.
People living in cluttered houses want to live differently, they look at all these magazines and fucking want the same interior, but they don’t know how to achieve it. And they think that only some cardinal decision, an event will allow them to make their house the same as in the picture.
There is a Hoarders show on American TV. People living in an eternal mess agree to participate, so that a team of professionals will clean up and make a designer interior in their house. Well, and as always, at the end of the program, beautiful photos BEFORE and AFTER. Interestingly, most of the participants admit that in the end their home returned to its original state. It's simple: the same sequence of small incorrect decisions led them to the previous result.
It turns out that a more effective way is to gradually work longer, not with the contents of the house, but with the people themselves. The goal is to change their habits so that they make maintaining order part of their daily lives. Unfortunately, producers would find this option too boring for a reality show.
The cluttered code behaves the same way: we fight it every day, only to solve the current problem, and dream of rewriting everything. Now, if we could only redo everything, we would definitely not make the same mistakes! We have to rewrite jQuery on Ember! We must cut the monolith into microservices!
Sometimes it works, at least long enough to write a triumphant blog post. But then the light goes out, the cameras stop working, the film crew leaves the site, and if you have not changed your habits, you are in a mess again and even faster than you think.
People think that big mistakes are killing us: the house is too small, the layout is unsuccessful, the wrong framework, monolith, not enough resources. This is not true. We are killed by those very small decisions that are caused by habits and way of thinking.
You can rewrite anything you want. But, if your team does not change its habits, then you will find yourself in an intricate network of microservices, just like you had an intricate network of classes and modules in a monolith.
It should be noted that we are not talking about individual, but about team habits and norms. This explains why the image of the developer as an individual professional, artisan does not reflect reality. This is not a separate developer lazy and unprofessional. This is a team with a lame teamwork culture.
Just like with roommates: everyone should conduct daily cleaning, wash dishes after themselves, periodically clean the bathroom and toilet, clean up the common living room. But there are more complex activities to increase livability , which can be left "only for interested". For example, outweigh kitchen cabinets or choose a suitable table in the living room instead of being too bulky.
But daily work is what everyone must do. That is, you can have people in the team who are interested in global architectural refactoring, splitting too large modules, optimizing the interactions between them and those who do not want to do this. Everyone is not obliged to participate in the rearrangement of furniture, but everyone must wash the dishes.
Two extremes are unlivable or why books do not work
Wonderful interior, isn't it? Everything is perfectly matched and thought out, nothing more. In a word - a dream. Just like a description of patterns showing a beautiful version of a perfectly consistent, perfectly abstract code. So beautiful that the eye rejoices.
Yes, the apartment in the picture is beautiful, but you cannot live in it. Apartments from the pages of magazines are intentionally deprived of clutter, because clutter is something individual.
What this mess consists of is different things for everyone. If you like to read, then these are books, if you like computer games, this is a console and joysticks, and if you like outdoor activities, it's a bicycle or a kayak. The things you use should be at hand, so you have to put up with a certain mess. The main thing is that there shouldn’t be too much of it.
Magazines about beautiful interiors in the world of software development show unrealistically clean code and therefore set the wrong guidelines. These are books on software development, design patterns, tips from blogs and articles. Code that satisfies all their requirements, perfectly consistent and abstract - is not viable in the real world. Even if you manage to achieve this, you cannot live in it.
We need a little mess to feel comfortable. This is true for both the apartment and the code.
Both extremes are unlivable . If you have a piece of code that you don’t understand and don’t know how to work with it, then it’s at one of these extremes. Either it is too littered that it is impossible to distinguish suitable ideas from inappropriate ones, or it is so pure that suitable ideas are simply impossible to find.
Moreover, these two extremes can exist simultaneously in the same project. Sometimes even in the neighborhood of each other. Livable code is somewhere in between. As well as a comfortable space for living, it is somewhere between photos from the covers of magazines and cluttered apartments.
Therefore, you need to be able to take a small amount of clutter because it makes the space livable .
You can say: “This is all, of course, very interesting, but what if my code is already in complete disarray? Or what if the project is not so bad, but I want to be sure that it is not moving in the wrong direction? ”
Let's say your code looks like a cluttered legacy-living room with narrow goat paths of understanding that go through it. Do not worry, you are not a bad person - this can happen to everyone.
If you look at the problem from an engineering point of view, then it simply shouts: “Rewrite! How is it possible to organize something in such a place without free space? Just take everything out of here and start over! ”This top-down planning is well suited for certain engineering tasks, but not for most software. This is not because we are bad engineers or we try poorly, but because in our area the engineering approach does not work. And the code works as a space in which people live. You need to understand that people are exactly what you need to work on.
Here's how to do it. There are four rules that apply to any language and framework.
Do no harm
Almost like in medicine. When opening an already cluttered file, promise that you will not make it worse. Even if you don’t have time to fix those things that are already terrible. If, when conducting a code review, you see how a person violates this rule, just remind him of what you agreed on.
Believe in consistent improvements
A pile of five books on the floor and one tidied up in a bookcase is better than a pile of six books. Perhaps the one who touches this code next time will shift another book from the heap to the shelf. If you wait for the time to put them all at once, this will never happen.
Using this approach can be a problem. Successive improvements are difficult for many, so they continue to live with a burning desire to completely rewrite a module, subsystem, or application. As a result, the pile of books does not decrease and remains on the floor from iteration to iteration.
Make housekeeping part of the workflow
Do not start user stories for refactoring. Do not wait two weeks to fix one file. Learn how to incorporate simple rules into your daily work. The scout rule says: leave the camp cleaner than it was before you arrived. If you follow it constantly, then gradually get rid of "bad habits."
Keep in touch
Be prepared to always communicate with each member of the team about what you are doing. The previous paragraph does not mean that you need to hide what you are doing. You just need to consider refactoring and putting things in order as part of everything you do.
When you refactor:
- Do not ask permission. This is part of your job. But be open to discussing your decisions.
- Do not shirk responsibility for your mistakes and learn from each. Mistakes are also part of the job. It is through them that we come to understand what we are working on. You will do them, humble yourself. Sometimes you can not finish refactoring, sometimes, on the contrary, you can get carried away too much, but the main thing is to learn from your mistakes over and over again.
- Ask for tips, but don't always follow them. Your task as a developer is to determine what you need to refactor and what not. You must have the freedom to make these decisions and make mistakes.
- Do it all together because you live here. It is very important. , , , .
— . — . , , , , .
. — . , , , , .
- , ,
- Related principles from Bob Martin