Managing Technical Debt in Software Development

All Articles AI Culture Data Management Level 12 News Python Salesforce Software Development Testing


(courtesy https://vincentdnl.com/drawing)

The Critical Impact of Technical Debt

Technical debt arises from the sum of all those little decisions that take us away from a 100%-engineered solution.

Every software development project that operates on a timeline and a budget will develop technical debt. Technical debt is not avoidable in the real world, given the constraints we have.

If we focus only on building new features and don't care about resolving technical debt, we are liable to paint ourselves into a proverbial corner. Successful software projects continually work toward paying down technical debt rather than allowing it to build up to a critical mass. Ignoring it will send the project careening toward the edge of a cliff.

When Technical Debt Reaches a Tipping Point

Last year, I managed a project where the client pressed the team to keep adding features, even if they would need to be rebuilt later. The situation was not ideal because they wanted fast development and expected the code to always work. That wasn't a realistic approach, and it quickly built up a lot of technical debt.

In that situation, as a team, we had to take a drastic approach and recommend that we pause feature development entirely for a couple of sprints.

We reached a point where putting out the little technical debt fires every day was choking out our ability to deliver improvements to the project as a whole or to deal with bigger, architectural technical debt on our hands. So we needed to pull the plug for a while and implement a new architecture as the foundation for future development.

What would have prevented a drastic measure like that? Simply building maintenance and resolution of technical debt into the process much earlier. It's alright to put off some necessary aspects of development. But if they are necessary, we can't avoid them forever.

Just like financial debt doesn't go away if you ignore it - and often gets much worse over time - the same is true with technical debt: it has to be paid off at some point.

How to Address Technical Debt

Typically, our approach is to identify key areas of maintenance that need to happen. On many occasions, we can resolve a little bit of existing technical debt as we develop new features. If we are aware of the items that need to be resolved, as a team, we can proactively pay that down wherever possible.

That approach presupposes that we are setting realistic expectations with the stakeholders. Trust helps a lot here. We have to establish that rapport with our clients that we are not unnecessarily inflating the timeline, have the project’s best interest in mind, and are engineering well.

When those items align and a continuous-improvement approach is practiced, ongoing technical debt is minimized. We don't eliminate the need to refactor, but situations that need it should arise from previously unknown information instead of technical debt from previous decisions.

Managing Technical Debt

It's important to note when technical debt happens. Just because a use case isn't in the 80% now, doesn't mean it never will be. And just because we can't or won't take the time now to refactor some architecture doesn't mean it's not a good and prudent idea to do so.

Some software teams may talk about it, then decide it's a future problem and just let it go. When it comes up in the inevitable future, nobody remembers the context, and it becomes an entirely new problem.

It’s much better to document things as they are seen. Track an issue on the project board, even if it is low-priority and not likely to be worked on in the near future.

Next, we have to have some tough conversations with stakeholders about the platform’s trajectory. That's not exciting software development from a business perspective. Often, we don't see visual changes when fixing technical debt, and it can be a difficult value proposition to sell to those paying the bills. But it is necessary.

How Technical Debt Develops in Development

Let's say our Chief Executive Developer reviews code for a major feature. In the review, we see many comments of varying importance to be resolved. He identified some places where the code architecture could be improved.

Given where we are on the timeline for development and when the client wants to launch the feature, we have to decide what gets changed now.

Perhaps we focus on quick wins and hit as many of those out of the review as possible. To not delay the platform launch, we punt on the architecture change. The tests still pass, the QA is going well, and the product "works." It's just not ideally engineered.

That development and decision-making process has introduced technical debt.

The Reality of Development Constraints

Software development always has tradeoffs. We could develop all the features we want in an ideal world with unlimited time and money. But in the real world, with finite time and finite money, we get to set up the project’s building blocks so that we (hopefully) hit all of the must-have features within an acceptable amount of time.

To this end, we often need to look for good 80% solutions that cover the known use cases. That is, we refer to the principle that 80% of outcomes come from 20% of the potential causes, and we apply our efforts to those vital few cases.

As a result, we don't generalize every solution, because doing so would be prohibitive for time and money, and the use cases generally don't exist to justify that expense.

We need to avoid majoring on the minors and get a working product out the door. This reality requires us to make certain assumptions about the use case that are now technical debt.

Originally published on 2025-05-02 by Matt Lewellyn

Reach out to us to discuss your complex deployment needs (or to chat about Star Trek)