There is this saying about tech debt: When you move fast in a software team, you build up debt that you must pay off later. The engineering team uses this to get a break and align the current understanding of the product with the code.
The TL;DR of this post is that it is often inaccurate to talk about tech debt. Instead, we should consider the tax a project accrues – A self-incurred tax based on the decisions and culture of an organization.
Traditionally, tech debt is understood as the "short-cuts" an engineer makes to reach a solution. Sometimes these are actually short-cuts, but rarely. There is usually a deeper meaning to this introduction of tech debt and it can be divided into the following categories:
- Architectural incoherence: New code does not follow the architectural contracts for the code base. The reason is typically that the architecture can't handle the proposed change, or the engineer is not yet trained in how to make the changes within the architectural confines. The consequence, or "interest payment," is lower productivity.
- Low performance: A feature's performance is sub-par. This can also be due to an operational discrepancy, ie. the infrastructure needs change. The reason is typically an attempt to make complex code more verbose or because the optimal solution was not yet found. The consequence is reduced scalability for the system.
- Requirement discrepancy: The feature does not implement the proposed design or requirements in full. The reasons are typically that the requirements or design requires for time than dedicated to the feature. For me, it is typically because of injustification of the time spend and the value provided.
These elements are all completely natural and will happen in all software projects regardless of size. However, I think this notion of debt is flawed. "Debt" can be fully settled however "tech debt" can not. Debt has a single-dimensional target: to reach 0 in owed funds. The above issues does not.
Though the word debt somewhat works, I think the notion of a tech-tax is more fitting for a number of reasons:
- It is an organization decision how much resources it wants to spend towards taxes.
- It is inherently on-going.
- It is understood as necesaarry but also something that inhibits business growth
The Engineering Tax
Software engineers need to prioritize tasks that does not yield business value. These are a tax. But how is the tax decided?
Architecture tax: In some teams, members have a high variance on architectural aspiration leading to architectural incoherency. Some teams can handle spaghetti code while others struggle to even release minor updates or onboard new engineers. They should pay more in taxes on the architectural incoherence.
A team should allow incoherence and learn from it. I spend some hours a week to remove unused code, and do various refactorings. But to what extend this should be done is teams specific? Some teams prefer more individual freedom while others prefer stronger rules on the architecture. Knowing what the architectural target is, is a good start, without being dogmatic about implementation.
Infrastructure tax: The infrastructure is necessary as a software product does not run without it. But there is a vast difference in running a static website on Netlify and setting up an entire cluster using Terraform and Kubernetes. However, a company is forced to pay the infrastructure tax when the product is being used. The more usage, the higher tax.
In my experience, to manage the infrastructure tax, the infrastructure should be implemented for realistic usage (as opposed to ideal or aspirational). There are no reasons to build out a microservice architecture before it has been proven that there is a need. Make a monolith. When the product is successful, there are usually also more funds to pay extra on this tax.
Requirement tax: Requirements change. The app changes. Everything changes. Making sure that all new and existing features live up to requirements, and changing requirements, imposes a tech tax. In particular in larger organizations with dedicated QA teams to discover these discrepancies.
To counter this tax, take one step back and look at who you are developing for and what you are developing. Are satisfying requirements becoming a bureaucratic exercise? Or does it solve actual business needs? Product leads should appreciate when engineers push back on features or requirements. Take the time to really understand why a requirement is important.
Taxes are ... Taxing
Most software engineers get exhausted by doing things that do not move the needle. Spending days implementing an animation widget that is seen by only a handful of people. Maintaining a Kubernetes cluster with less than a single daily visitor. Not only are these bad business expenses, but they are also hard for the software engineer.
People tend to enjoy meaningful work more. Managing tax will yield a better product for less cost at higher flexibility and higher velocity.