How to deal with Technical Debt, in a Startup
As an engineer, you would no doubt know what its like to have to hit specific deadlines, and having to make technical sacrifices or compromises, either individually or collectively as a team, and this is what the industry calls, technical debt, a choice that balances ‘ideal code’ against the timeline in order to meet that deadline.
In an ideal world, developers would be able to put forward their best code, and the architectural and design decisions would be their best work, but certainly not realistic. Besides meeting stringent external deadlines (clients, venture capitalists), the true spirit of agile programming dictates that development work is iterative, not procedural, so “there is value in doing something simpler now, with a view to improving it later (Gary McLean Hall. “Adaptive Code: Agile coding with design patterns and SOLID principles (Developer Best Practices)”).
Technical debt is something that not only is evident in the present time, but further accrues over time, causing what is dreadfully called in our industry, legacy code, as soon as it is part of something that is so deeply entangled that it will take a long time to untangle, like Christmas lights. With that in mind, where do you draw the line? What is considered good debt and what is considered bad debt? That all depends…
“There is nothing wrong with certain types of financial debt. If, for example, you have the option of spreading the payments for a car over 12 months with low-interest repayments, you are in debt. But this could be a good decision if you need the car for commuting and cannot afford the payment in full. The car will allow you to generate the revenue necessary to pay the repayments, because you can now get to work on time… Of course, some debt is bad. If you take out a credit card and pay for something extravagant without first calculating how you will repay the debt, you can end up in a cycle of balance transfers, trying to keep interest payments at a minimum. ” (Gary McLean Hall)
Deciding on what is considered good or bad debt is a trade-off software architects and project managers need to make, what should be considered ‘good-enough’ for now, and what should be fixed now otherwise it will cause havoc down the line. The same applies to UI/UX design or design debt, and infrastructure debt (choosing Firebase versus your own hosting solution).
Agile Evangelist Martin Fowler authored a guide on balancing good versus bad technical debt, called the Technical Debt Quadrant.
The Technical Debt Quadrant
Martin’s quadrant essentially categorizes into x and y quadrants, forcing you to answer two prominent questions, are you accruing this technical debt for the correct reasons, and you can justify adding it, and are you aware of alternatives to avoid this technical debt, and have considered those.
Stepping through the quadrant, starting from the top-left, you would find the worst-case scenario where you are stating due to time-constraints you need this technical debt and consciously making that decision. This shouldn’t happen and at the very lease, the reckless part needs to change, through education and demonstration of coding discipline and transparency. Setting pair-programming, git-flow pull-request strategies and other code-review processes are in need.
The next-worst scenario would be if you were inadvertently reckless, whereby you are not paying attention to the technical debt but not purposely making a decision to keep it. This presents the problem whereby you cannot assign a risk or value to the debt you are about to accumulate, demonstrating a lack of organization, knowledge and experience. This would be common amongst many startups, with young developers whom may not follow refactoring best-practices.
If you are inadvertently prudent, you would be following best-practices, yet you let some code debt creep in over time, or you learn of a better way to address certain architectural or refactoring decisions. In such a scenario, keeping track of that technical debt once found, and repaying it, in a future sprint is in order.
Finally, the best-case scenario of deliberately prudent means you are following best practices, you have already acknowledged the technical debt and like the previous quadrant, will be repaying it in a future-planned sprint.
Repaying the Debt
As with all debt, financial or otherwise, there comes a time to collect, and in the development world, that needs to be paid. This is a very hard exercise because it usually requires convincing the right stakeholders, be they clients, the CEO of a startup, or the board of directors that this needs to be done, despite there no being monetary incentive or visible benefits.
Why would the client pay for your coding errors? Why would the board of directors sanction work on something that won’t affect the customers visibly?
Those questions are definitely ones you would come across, if you haven’t already. The premise you need to go to them with is, if you don’t take your car for regular maintenance, top up it’s oil, you will have long-term damage that will cost more. Technical debt isn’t always the developer’s fault, especially if its deliberate. Sometimes its the technology and working with incompatible or immature libraries, sometimes it is the deadlines that need to be met, but either way, when you open up a debt, it should be repaid back!
This is why you will need, at the time of identifying debt, set a separate story card for that debt, a description, along with the real cost of it, how much of a legacy would this create from an architectural perspective. A good practice would be at that time to also provide a user story point (effort-estimation) to better help the stakeholders and others understand the risk, and hopefully plan a maintenance or refactoring sprint down the line.