Tips for avoiding technical debt and regret
Last week I was talking about how it’s easy to conflate debt and regret when it comes to technical decisions. Technical debt is the set of simple, shorter paths in software development that you follow intentionally. Regret is more about getting lost and following unsafe paths, often blissfully unaware that you're lost. Technical debt will feel good in the long run, as it helps you get somewhere faster at a reasonable cost. Regret, on the other hand, feels bad, as you can see the wasted time and effort spent on a path that was clearly followed by mistake. It's easy to feel unqualified to measure technical decisions, especially if you're not technical. You may be disconnected from the planning process or you may not understand the jargon and details of an approach. How can you ask intelligent questions about risk when you feel so separated from what's happening? How can you make clear decisions about risks with incomplete technical knowledge? Luckily, most regrettable technical decisions fail to satisfy even the most basic of principles, and risky debt is all about the poor ratios of cost versus gain.
Know the scale of your product and features
In terms of debt and regret, you can measure the risks by understanding two things:
- the basic scale of your product, and
- the basic dependencies in the features your product offers.
Understanding these doesn't require much technical knowledge, and that understanding is anchored to what your product does. Thinking of risk starting with facts and how they relate to what your product provides you a rational base to start from. Scale includes things like the number of users, the size of the things they do, and how fast they need to do them. When you have a lot of something, you can ask questions around your features and those abundant things: how can we report on X with Y users? Can we also do Z? Dependencies are simply how the features and pieces of your product relate, as there will be certain features that are more important to your product than others. Those features are riskier, as your system wouldn't be viable without them, and other features may not be possible without them. Our product has to do X to do Y and Z; what if X is too slow? What if we can't do Y? Bigger, more fundamental things are obviously more important. Understanding the scale and dependent risks gives you a set of facts you can use to anchor your thinking. The truths don't change as you develop your software either, unless you change the focus of the product.
Know which risks to focus on
To simplify thinking about risk, you can place it on a gradient of principles:
- Decisions that are never questioned or justified represent the risk of the unknown and unseen.
- Decisions that follow known bad paths represent the risk of the known bad.
- Decisions that don't follow known good paths represent the risk of the likely bad.
- Decisions that follow new paths represent risk, that of the unknown.
- Decisions about the most fundamental parts of your system are risks of increased or root dependency.
- Decisions about the biggest parts of your system are risks of scale.
- Decisions that defer costs are a risk of future expense.
- Decisions that prevent key opportunity are a risk of reduced momentum.
When you think about risks in terms of principles, you can separate some of the technical from the rationalization. When a team wants to build a custom framework (a classic example), it's easy for a non-technical manager to see that the approach isn't a known good, and that it's a new path. These principles don't prevent following that path, but they do make it clear that the risks and likely costs are not insignificant.
Know when to consider risks
Considering risks is a crucial part of a healthy software development process. Knowing the size and dependencies of your product gives you a place to anchor your thinking. Identifying the bigger, more fundamental issues in your product using principles helps you identify the most basic major risks, as well as giving you a way to describe the risks themselves. But, do you consider every risk? Do you weigh every change? Most organizations can be improved by considering the risks of only a small portion of their technical decisions. Anything foundational or large should be considered carefully, and anything that violates one ore more of your team's core principles should be actively avoided.
Know your history
Finally, another great way to identify risks is to learn more about the history of software failures. The principles that identify risk are clear throughout the history of failed projects, and the ways that the risks remained hidden help to identify future failures.
- PSU's list of well known software failures,
- Wikipedia's list of famous software bugs (check out the long list of references),
- Why software fails (IEEE),
- TAU's software horror stories,
- Jeff Attwood's history of software project failures. Also see his mega list of recommended reading for developers,
- Steve McConnel's Classic Mistakes Enumerated (based on his book Rapid Development),
- Software Project Secrets: Why Software Projects Fail (book),
- The Mythical Man-Month: Essays on Software Engineering, Anniversary Edition (book)