Operating with debt is a normal component of doing business. Just as financial debt must be controlled and leveraged to take advantage of market opportunities, any software project maintains a level of technical debt as well. Let’s explain what this is and clear up some misconceptions.
What is Technical Debt?
Every decision made during the development process has a consequence. Quick and dirty implementations can be appropriate for hitting short term goals, but projects incur metaphorical ‘debt’ that needs later correction. Paraphrased from Martin Fowler, “technical debt represents future effort needed due to design choices and incurs interest over time.” A key skill is knowing when to obtain debt, when to ‘pay off just the interest’, and when to ‘pay off the principal’.
Having technical debt does not mean you have ‘bad coders.’ In fact, Santiago L. Valdarrama points out correctly that operating with NO debt can be a warning sign. As a startup, the odds are stacked against you and you’re constantly pushing against the boundaries of time and budget. The priority is to successfully ship product but that is contrasted with not shooting yourself in the foot on future development if said product does well. This intentional taking on of debt is business requirement driven and something that should be discussed ahead of time.
A classic example is scalability. Stressing over how your site will handle a million users before you’ve even gotten user #1 is often a waste of time; you might choose a tech stack that is simpler to write and tune, with the knowledge that you can improve/supplement/replace parts of it as you grow. As Twitter grew, they found a need to migrate more and more tools from Ruby on Rails over to JVM-based technologies; a tradeoff made for performance reasons at the cost of spending time replacing working code and an arguable reduction in ease of coding and availability of engineering resources. Complex server configurations or high quality internationalization efforts are often jumping the gun when building out early iterations of your product.
How much should you test your product before you release it? You could spend years testing every tiny bit and never release, or you could launch with bugs, knowing that you’ll fix them down the road. Testing resources are limited; perhaps an experimental feature that might not stick around for the long term needs less exhaustive testing, with the tradeoff that you’ll need to do more of it if the feature is a success. Coding is an inexact science and a developer has to determine what the appropriate level of testing is for any given feature based on the company priorities. A lost ‘like’ on a Facebook post is not nearly as destructive as a lost deposit on a banking site.
How Does Technical Debt Happen?
In addition to intentional design decisions, some debt just gets accrued naturally and is unavoidable. The codebase grows over time and lessons are learned. Common components are found, data storage needs change, and code refactoring becomes necessary. “To-do” notes sprinkled around the code accumulate. Without keeping this all in check, you risk slowing down development, producing more obscure bugs, and a more arcane codebase for new engineers (or potential acquirers) to navigate.
How do you measure debt?
Quantifying technical debt is hard; looking at the rate of new bugs and the velocity at which you are fixing them versus creating new features is a good holistic overview. Test coverage can also point you in the right direction, though it is dangerous to rely too heavily on. In my opinion, the truest indicator is listening to the engineers. An experienced software architect can help meter out what needs improvement and when. It’s something I like to communicate about very clearly to clients, so that they understand the quality of the codebase through my eyes. Constant testing and building through continuous integration tools can do a good job of keeping the general overview accurate, but I encourage engineers to always spend some extra time thinking about the mid-to-long term effects of tool and design decisions. Each sprint can have some extra padding invested to cleanup technical debt and some organizations like to devote entire sprints to nothing but this after a big release. Caveat emptor that like all code changes, technical debt resolution can introduce new bugs, but in general, balancing your release cycle with technical debt reduction is a crucial skill for any growing startup.
In Summary:
The ability to take a step back and discuss how different elements of your tech stack are hindering you and to what degree is an invaluable skill. It is far too easy to fall into the trap of working day-to-day and not keeping your eye on the big picture. Mindfulness in development and design are crucial for growing a healthy product and can be done even while maintaining a rapid development cycle. If you’re looking for a sounding board to discuss technical debt further, please reach out to me here or on Twitter @drh.