Technical Debt Management
- Transfer
Ekaterina Sazonova, freelance translator and student of Netologiya , specially for the blog, translated an article by Carl Tashian on how product and project managers can cope with technical debt.
A lot of books have been written about the problems of software development, its evaluation, cost control, testing. I want to share with you proven practices that have helped me, as a technical manager, keep control of technical debt in a growing project.
Design software the way you would design a building. Something in construction is moving slowly, something is moving fast. Some components, such as furniture or wall painting, are not directly related to the building itself. It is very difficult to change the angle at which the walls are located to each other. Need to prioritize.
Understanding what can be flexible in the program and what is not, applies not only to current requirements, but also to future ones. That's why it is so difficult to create software. You are not just supplying your technological product; you are trying to predict the future. And the future is shrouded in darkness, there is always the risk of premature optimization. You have to make a choice: which component is fundamental and which can be easily changed.
The word software (literally “soft product”, appeared as opposed to “hard product” - hardware) distorts the essence of the concept, because it implies flexibility in everything. Flexibility in everything is the ideal to strive for. However, the truth is that a more rigorous system with very closely related components is much easier to build. For example, it can be argued that in a monolithic application the elements are more closely related than in a set of microservices. The obvious advantage of a monolithic application is its simplicity. Want to write at least a line of code for a microservice-based application? First, look at the gigantic list of requirements for data exchange, coordination, and accessibility of system elements.
This does not mean that microservices solve the problem of communication between modules. They connect the modules with a network that allows you to build clear, defined domain boundaries, but the connection itself is inevitable.
Flexibility is expensive. I can’t even count how many times I created something with an idea of how the requirements will change in the future, and then I discovered that it is the “fixed” parts that need to be changed, and the flexible ones do not have to be flexible. At first I thought I was just unlucky, but now I understand that I was simply mistaken in planning.
Software, unlike a building, becomes obsolete in a different way. If the requirements do not change, the foundation will not move and begin to collapse, as is the case with the building. The software does not wear out. However, its foundation is affected by new requirements. It is very difficult for startups to make the right choice. They are trying to solve this problem with the help of cloud services, laying the possibility of scaling from the very beginning. Optimizing cost and speed management as you grow will require a lot of fundamental work. Even with modern cloud solutions, microservice management mechanisms and so on.
Refactoring is one of the best ways to increase your overall speed. This has already been said a lot. In any sufficiently large code base there is something to refactor. The main thing is to refactor in the right place, where simplification and flexibility in the future are needed. So that it does not resemble a guessing game, it will be correct to refactor before introducing new functions.
An easy way to reduce technical debt is to understand that any code is just a temporary experiment. For example, you decide to create a separate code branch and quickly sketch out a prototype that you can implement. Even if everything works well with this feature, you will have to write the code again as it should, and throw out the old one. Such prototyping is specific and carries many risks: some people in your team will have to lower their standards. However, this method will save you time.
Testing is a way of life, a sacred practice. Even in small projects, testing saves more time than the process itself takes. Sometimes it can be seen right away, sometimes later.
The company should make a code review. It doesn’t matter how many tests you write, how well you can refactor. Other people will notice the moments you missed. Mistakes Bugs. Typos. Yes, anything. In many companies, three pairs of eyes check each line of code - I think this could be a general rule.
The frequency of testing and code review should be commensurate with the harm caused to the project in the event of failure. Source code requires more thorough verification than interface code. Any software related to the operation of the insulin pump should be tested more seriously than any mobile application. Can you imagine a team working on an insulin pump with the slogan “Do it fast and break it down”? (Facebook mantra and Mark Zuckerberg's motto “Make fast and break things”).
One engineer friend of mine, Noah Thorpe, once told me: "We pay for every line of code every day." The less code, the less you pay. When I work on a project, it matters to me how each feature works. I regularly gather a team to decide which features to improve and what to remove. This means at times admitting to yourself that the feature you like just doesn't work.
The ideal situation is this: you understand that the feature will not work correctly even before you write the code. On the first line of defense are paper prototypes and user testing. However, you are always pressured. You can always hear a bunch of people using the product and asking to add such terrible features that you never need to realize. Or such features that could be added, but later. This is where product management and technical design meet: even if creating a new feature is a common thing, you still have to pay for it.
You have to pay for addictions too. Dependencies are internal and external. Internal ones are libraries and frameworks on which your software depends. External - these are the services with which your software is associated. You are doubly dependent on services because libraries are usually associated with them.
By adding a library to your project, you pay for the entire library and your use of it. So, you need to justify the need for each library, each plugin. Even the smallest. They are added quickly. If you have a balanced approach, you will be surprised at how fast you progress.
It’s easier to apply all these methods if they become general principles of work in your company and a favorable environment is created for their use. Maintain a high level of code review with pull request requests. Continue to test continuously while maintaining Continuous Integration. Discuss the main features monthly. Keep paper books close on hand that talk about the right approaches, such as “Refactoring: improving the design of the existing code”. And do not forget to read them!
It is important to clearly understand what you are building. Will it be a secret hideaway on the seashore, typical housing or a glass art museum? The answer to this question should be the same for the whole team. That's why technical debt management is not just the job of a technical manager or developer. This is a common work. After all, it affects every step of the product development process, starting with planning.
Netology courses on the topic:
A lot of books have been written about the problems of software development, its evaluation, cost control, testing. I want to share with you proven practices that have helped me, as a technical manager, keep control of technical debt in a growing project.
Maintain flexibility in the right places.
Design software the way you would design a building. Something in construction is moving slowly, something is moving fast. Some components, such as furniture or wall painting, are not directly related to the building itself. It is very difficult to change the angle at which the walls are located to each other. Need to prioritize.
Understanding what can be flexible in the program and what is not, applies not only to current requirements, but also to future ones. That's why it is so difficult to create software. You are not just supplying your technological product; you are trying to predict the future. And the future is shrouded in darkness, there is always the risk of premature optimization. You have to make a choice: which component is fundamental and which can be easily changed.
The word software (literally “soft product”, appeared as opposed to “hard product” - hardware) distorts the essence of the concept, because it implies flexibility in everything. Flexibility in everything is the ideal to strive for. However, the truth is that a more rigorous system with very closely related components is much easier to build. For example, it can be argued that in a monolithic application the elements are more closely related than in a set of microservices. The obvious advantage of a monolithic application is its simplicity. Want to write at least a line of code for a microservice-based application? First, look at the gigantic list of requirements for data exchange, coordination, and accessibility of system elements.
This does not mean that microservices solve the problem of communication between modules. They connect the modules with a network that allows you to build clear, defined domain boundaries, but the connection itself is inevitable.
Flexibility is expensive. I can’t even count how many times I created something with an idea of how the requirements will change in the future, and then I discovered that it is the “fixed” parts that need to be changed, and the flexible ones do not have to be flexible. At first I thought I was just unlucky, but now I understand that I was simply mistaken in planning.
Software, unlike a building, becomes obsolete in a different way. If the requirements do not change, the foundation will not move and begin to collapse, as is the case with the building. The software does not wear out. However, its foundation is affected by new requirements. It is very difficult for startups to make the right choice. They are trying to solve this problem with the help of cloud services, laying the possibility of scaling from the very beginning. Optimizing cost and speed management as you grow will require a lot of fundamental work. Even with modern cloud solutions, microservice management mechanisms and so on.
Refactoring for Speed
Refactoring is one of the best ways to increase your overall speed. This has already been said a lot. In any sufficiently large code base there is something to refactor. The main thing is to refactor in the right place, where simplification and flexibility in the future are needed. So that it does not resemble a guessing game, it will be correct to refactor before introducing new functions.
Before you write the code, reconcile yourself to having to throw it away
An easy way to reduce technical debt is to understand that any code is just a temporary experiment. For example, you decide to create a separate code branch and quickly sketch out a prototype that you can implement. Even if everything works well with this feature, you will have to write the code again as it should, and throw out the old one. Such prototyping is specific and carries many risks: some people in your team will have to lower their standards. However, this method will save you time.
Work with tests and analyze code
Testing is a way of life, a sacred practice. Even in small projects, testing saves more time than the process itself takes. Sometimes it can be seen right away, sometimes later.
The company should make a code review. It doesn’t matter how many tests you write, how well you can refactor. Other people will notice the moments you missed. Mistakes Bugs. Typos. Yes, anything. In many companies, three pairs of eyes check each line of code - I think this could be a general rule.
The frequency of testing and code review should be commensurate with the harm caused to the project in the event of failure. Source code requires more thorough verification than interface code. Any software related to the operation of the insulin pump should be tested more seriously than any mobile application. Can you imagine a team working on an insulin pump with the slogan “Do it fast and break it down”? (Facebook mantra and Mark Zuckerberg's motto “Make fast and break things”).
Kill badly working features
One engineer friend of mine, Noah Thorpe, once told me: "We pay for every line of code every day." The less code, the less you pay. When I work on a project, it matters to me how each feature works. I regularly gather a team to decide which features to improve and what to remove. This means at times admitting to yourself that the feature you like just doesn't work.
The ideal situation is this: you understand that the feature will not work correctly even before you write the code. On the first line of defense are paper prototypes and user testing. However, you are always pressured. You can always hear a bunch of people using the product and asking to add such terrible features that you never need to realize. Or such features that could be added, but later. This is where product management and technical design meet: even if creating a new feature is a common thing, you still have to pay for it.
Minimize Dependencies
You have to pay for addictions too. Dependencies are internal and external. Internal ones are libraries and frameworks on which your software depends. External - these are the services with which your software is associated. You are doubly dependent on services because libraries are usually associated with them.
By adding a library to your project, you pay for the entire library and your use of it. So, you need to justify the need for each library, each plugin. Even the smallest. They are added quickly. If you have a balanced approach, you will be surprised at how fast you progress.
It’s easier to apply all these methods if they become general principles of work in your company and a favorable environment is created for their use. Maintain a high level of code review with pull request requests. Continue to test continuously while maintaining Continuous Integration. Discuss the main features monthly. Keep paper books close on hand that talk about the right approaches, such as “Refactoring: improving the design of the existing code”. And do not forget to read them!
It is important to clearly understand what you are building. Will it be a secret hideaway on the seashore, typical housing or a glass art museum? The answer to this question should be the same for the whole team. That's why technical debt management is not just the job of a technical manager or developer. This is a common work. After all, it affects every step of the product development process, starting with planning.
From the editors
Netology courses on the topic:
- full-time program " Digital Product Manager ": 5 months of training, 20 well-known Runet experts, 100+ hours of practice;
- online program " Profession Product Manager ";
- online program " Flexible development methodologies: Scrum, Kanban, implementation of changes ";
- online program " Product Creation: Analytics, Development, Promotion ".