Making time to fix technical debts
It is impossible to avoid technical debts. We try to escape them by creating great design and architecture, use the right tools, practice TDD, etc. And yet, we keep creating them. There are many good reasons why we create them in the first place. Unfortunately, there are also many reasons why we don’t fix them.
I think that most of these reasons are excuses, which are created because we don’t manage these tech debts as we should. In this post I want to share my point of view regarding tech debts and some personal methods that help me to find time to fix them.
You cannot escape tech debts
There are so many good reasons (I mean it!) to create tech debts. Hack a quick fix for a bug in the middle of night, drop some tests when you face a super tight dead line, not upgrading your tools to their newest versions (what is your MongoDB version?). Even when everything goes great, we create tech debts simply because the product itself changes. We apply lean principles and move fast using practices like MVP. However, we cannot always predict how the product will change, hence the design of the system sometimes require us to make technical compromises in order to move fast and keep bringing value to our users.
Moving fast sometimes makes us compromise on technical aspects like performance, tests, quality, design, documentation, logs and more.
Creating these tech debts is perfectly fine, as long as we make a conscious decision to create a tech debt, and that we make sure to come back and eventually fix it.
Realizing that we are creating a tech debt is pretty easy. We convince ourselves that we’ll fix it later when we have time.
But who has time? When was the last time you came to work and had plenty of time to wander around and do whatever you wanted?
We are here to build products that bring value to our users. There will always be another feature with another deadline-actually, there is a whole backlog full with your next features. As engineers, we never have “a features-free” time. When you finish a feature, you’ll start the next one.
Tech debt == Product Feature
This is why I treat tech debts the same way I treat a product feature. I make sure they are documented as tasks in my backlog, along with all the other features. I prioritize them along with the other features and make sure they enter the sprint backlog when their time is right.
We are the product managers of our tech debts
Don’t hold your breath while waiting for your product manager to suggest you refactor that ugly class or treat some of the error logs you experience. We are responsible for making sure the tech debts are prioritized and added to the sprint. We can agree on leaving a fraction of each sprint for technical issues. It can work, until you need more than just a fraction.
The best way to make sure this happens is to help the product manager understand the importance of the tech debt. There is a reason we are so eager to fix it, so let’s help the PM be part of our concerns. How? When the PM introduces a new feature that she wants to be developed, we expect her to provide the user impact, the goals, the motivation and the supporting numbers that justify the development of this feature. Well, this is exactly what the PM expects from us.
Transforming technical debts to user impact
We need to explain why this tech debt is important and why it should be fixed now. We need to transform the technical issues into user experience damage, number of affected users, time and revenue loss. This is the currency the product manager uses when prioritizing the backlog.
Sometimes it is pretty hard to get all this data. It requires us to be able to understand how a bad design or lack of automation actually harms the user experience, or how error logs or old software versions stop us from reaching our business goals. There are many cases when we’ll need to invest time to get to a point where we are able to fetch all this data.
- Too many unhandled error logs — we need to create a simple way to visualize all of our error logs, indicating the number of affected users, what their experience was due to the error and show how it affected a certain business KPI.
- Performance issues — we need to provide industry benchmarks for the experience, test the performance of our competitors (try test my site), fetch analytics like exit rate — usually they will be much higher in low performance flows.
3. Lack of automatic tests — we need to fetch the bugs that occurred in the components where are missing tests, what percentage of these bugs could have been avoided if there were tests for it (unit/integration/E2E/production). It is hard to deny a day or two’s investment that will make some experiences much more stable.
Developers time is also important
Not everything should have a direct user impact. Low velocity due to old and bad design should be treated, since eventually it slows us down from bringing value to our users.
- Lack of automation — how much time do the developers invest in manual testing? Sanity testing? Manual deployments? Configuration of environments? All these actions take precious time that could have been invested in the development of new features. We need to calculate and present the waste that is caused due to lack of automation in every sprint. Investing in automation will reduce any effort to a matter of minutes, if any at all.
- Slow developers environment — it feels hard to explain to a product manager why three minutes’ compilation time is something that must be fixed. But when you sum the total time per day that the developers team waits for their IDE to compile, you’ll get a huge amount (3 min X 30 times a day X 4 devs = 6 hours each day) of pure waste that could be used for actual development.
- Code base health — when the code health declines, it becomes more expensive to develop new features. We need to fetch the tasks from the last month and go over their estimations. Then, we can predict how these estimations would change in case the code base or a specific component would have a better design.
Not all technical debts should be fixed. We need to be mature enough to understand that sometimes the code doesn’t have to be perfect. We don’t need to have 100% test coverage and we don’t need to refactor every piece of code in every corner of your system. The same way we don’t need to add features to places where there are no users.
We need to have a clear ROI for our tech debts, in such a way that both the R(return) and I(investment) are perfectly clear to the PM. Don’t forget, she will also need to justify to her managers why her team is upgrading their MongoDB version instead of adding more features to the app. It should be clear in such a way that anyone could easily prioritize the tech debt over another feature.
I believe in quick and dirty — sometimes I even suggest to start with quick and dirty, and create tech debts on purpose. I encourage you to hack something quickly rather than wasting time on making the perfect solution for a bad feature. However, make sure to always document it, add it to the backlog and tackle it based on its priority in the upcoming sprints. As engineers, we own the technical aspects of our products. We are the product managers of our tech debts, so if we want to fix them we need to act accordingly.