Tackling Technical Debt at TransLoc

Curtis Martin
TransLoc TechLog
Published in
5 min readFeb 8, 2018

--

Like it or not, there’s one thing every software company has in common (other than building software): technical debt. Try as we might to be forward-thinking, use the latest and greatest standards and libraries, and decouple our code for easier testing, we will undoubtedly incur technical debt at various points throughout the development of our products.

Let’s address the elephant in the room: this is not a bad thing! As needs change for our customers and users, we too must adapt our products to meet that change, and sometimes that means needing to rewrite sections of code in ways we hadn’t foreseen. It’s inherent in the nature of agile software development.

Here at TransLoc, we are constantly adapting to the ever-evolving transit space so that we can deliver the right value at the right time, and sometimes that means making hard decisions about where to draw the line on technical debt. What amount of debt is okay to put in our product backlog? Should we just bite the bullet and do a large refactor now in order to save time later? What is our plan to address any incurred debt in the future? These are questions we needed to be able to answer.

When answering hard questions like these in the moment, it’s easy for emotions and personal preferences to get in the way and lead a team down an undesirable path. Rather than repeatedly run into that situation when we encounter tech debt, we chose to create a set of heuristics that takes our emotions out of the equation and gives us consistent guidance we can return to going forward.

What does technical debt mean to us?

We started by asking ourselves how we defined tech debt, and after talking for a bit we determined that there was already a pretty solid definition that we all gelled with on Wikipedia:

“A concept in software development that reflects the implied cost of additional rework caused by choosing an easy solution now instead of using a better approach that would take longer.”

This is exactly the question that comes up every time we talk about tech debt. If we choose the easy/quick path as opposed to the “better” path now, what will it cost us in the future?

To further address this, we needed to define what we mean by “better”. We came up with the following definition:

Factors that make one approach “better” may include: UX fidelity, maintainability (including conventionality), extensibility, testability, scalability, performance, feature blocking, and (non) functionality.

That’s a pretty broad list, but it’s somewhat intentional. There isn’t a true checklist you can go down to determine if something is or isn’t tech debt. Rather, there needs to be a conversation with your team to weigh the pros and cons and talk about different approaches to solve the problem at hand, and these factors can help guide that conversation.

Criteria for Creating, Extending, and Preserving Debt

After coming up with a process to determine how to identify tech debt, we needed a way to judge whether it should be addressed immediately or added/maintained in our backlog. There are three scenarios we focused on: creating new debt, extending existing debt, and preserving existing debt.

Creating technical debt

Debt representing a particular unit of work is being added to the product for the first time. Example: While working on a fuzzy matching algorithm, we decided to specialize it in a way that would be difficult to extend rather than finding a generalized solution. This let us quickly deliver the desired results for an initial customer base, but we knew we’d need to rework it later to make it possible to provide a more general solution.

Extending technical debt

Writing new code that adds to a preexisting unit of debt. Example: Our old fuzzy match logic used to match against a person’s first and last name, but now a customer needs it to match their address too. We choose not to fix the underlying fuzzy matching code, but to add new functionality to it now so that we can deliver a necessary feature to the customer sooner.

Preserving technical debt

Leaving an existing unit of debt intact for a prolonged period of time without modification. Example: The part of our application that uses the fuzzy matching is not widely used and we’ve decided to focus on more critical components that our customers need in the immediate term. We choose not to address the debt until we resume development on the feature that uses the fuzzy matching.

Our team spent some time coming up with separate criteria for debt creation, extension, and preservation, and after some discussion we determined that the criteria for each of these scenarios could be the same. This was good for a few reasons:

  1. We have a consistent set of questions we can ask, no matter the problem we’re trying to address.
  2. We can revisit those same questions about the same tech debt over time and see what has changed.
  3. As our scrum master pointed out, we’d be less likely to follow the guidelines if we made them overly complex.

With that in mind, we arrived at the following criteria to consider when evaluating tech debt.

Tech debt may not be Created/Extended/Preserved unless all of the following are true:

1. Using this list of heuristics, the squad has discussed the debt and committed to taking it on. The PO (product owner) will be the final arbiter when there is no consensus.

Things to consider:

  • What’s the business value obtained through faster delivery?
  • What are the risks to the customer experience and to the business of addressing (or not addressing) the debt?
  • Does the business value obtained through faster delivery outweigh the risks?

2. At least one of the following situations is at play:

  • We don’t have enough product information to justify a better, higher cost solution.
  • There is an emergency that is solved by taking on the debt.
  • We know that the debt is isolated to scaffolding (temporary/throw-away) code.

3. None of the following conditions apply:

  • The debt is part of new code that is foundational to the application.
  • We already have plans to extend the part of the code that contains the debt.

4. A prioritized story or task has been created in the backlog to document the work that’s being deferred, and the code has been commented to explain the issue as well.

  • The story outlines criteria for deciding when it will become necessary. For example: when we decide to make a long-term commitment to this experimental product feature, we need to invest the time to correct this debt.

5. If the debt has performance or scaling implications, we’ve added monitoring that will help us know when (or if) it actually becomes a problem.

We found these heuristics to be fairly exhaustive, but also concise enough that they would be easy to refer back to in the future. However, as with everything in an agile environment, nothing is set in stone! We will likely revisit this from time to time to see what works and what needs improvement or should be removed entirely. For instance, we store our heuristics in a documentation Git repo, and changes to the heuristics go through our normal pull request review process, requiring approval from team members before merging. Remember though, the point isn’t to have the ultimate set of criteria, but to force a conversation with the team and guide us to make the right decisions at the right times.

We hope this can be of use to other product teams out there. Let us know what you think and if you’ve come up with strategies of your own to address technical debt!

--

--