Paul (product owner): Ok! To finish, I’d like you to give me an estimate for that feature: “as a user, I am prevented from downloading an app if my device does not run the minimum OS version”.

Pierre (dev #1): that’s an easy one, need to do this, that and that. Should take three days.

Marie (dev #2): Pierre, do you know that the method used to determine the device’s minimum OS version has not been changed in 2 years? It has at least 3 or 4 nested if statements. Oh and the function is too long to be read without scrolling. Dear Paul, I agree with Pierre on the base estimation, but the refactoring of the messy function will take up to two days.

At this very moment, the entire code base’s health relies on Paul’s answer. It is a discussion a great number of dev teams have with their beloved product owners (we love ours!). It has a direct impact on how the team manages technical debt. In this article, we will define the term and detail how we, at Appaloosa, deal with it.

What is technical debt?

“A concept in programming that reflects the extra development work that arises when code that is easy to implement in the short run is used instead of applying the best overall solution.” Wikipedia

Technical debt is just like a regular debt. But in software development, you are borrowing time, and the interest you have to pay will take the form of code refactoring.

The first lines of Appaloosa were written in 2011, by Ruby on Rails enthusiasts! Most of them at night after a long day of work or during the week-end, looking after the kids.

Needless to say there are some areas of our legacy code base that accumulated cobwebs and dust over the years. We used to go there only when we had to, and we brought a buddy and a powerful flashlight!

This made for code that does not rise to our current clean code standards. To us, there are as many definitions of indebted code as there are of dev teams. Here are some examples:

code does not meet our guidelines (detailed in an upcoming article) and needs to be refactored for maintainability reasons,

outdated business rules or custom features we made for potential clients that are not used anymore, usually developed in a very short timeframe,

outdated dependencies,

old standard requirements that are deprecated (Android or browser version…),

part of the code that aren’t tested (around 10% in our case),

Up until recently, when we were a dev team without a tech lead, we dug some of our own “code-graves” knowingly, due to business pressure. And others, innocently, due to the inexperienced of our dev team.

But just like a Lannister, you always pay your debt, don’t you? Whether you do it willingly or unwillingly will depend on your team! A sane dev team will schedule the down payments in the following sprints, whereas an incautious dev team will have to pay all at once weeks or months later. Often, that’s too late to fix it in the right conditions; production will be in flames and you’ll be there trying to pay months of accumulated technical debt. Maybe that’s the way you like to code, but we don’t!

There is never a good time to refactor. You always have to improve your product with new features that at first seem like they are more important than refactoring. “Where’s the customer value here?” would ask an unsupportive manager. Hopefully, at Appaloosa our product owner understands very well the importance of technical debt management and trusts us when we say we need to refactor some code.

When and how do we deal with it?

When to deal with it

Back in the days, when we were a team of 2 devs and a product owner (who occasionally coded!!), we were already convinced we were strongly indebted. But we lacked resources to significantly pay that debt. We experimented with several approaches to try to pay it off.

“Indebted code day”

Throughout the sprint, whenever we would encounter legacy code, we would create a task and add it to a dedicated backlog. Once per sprint, we would try to unstack the ever growing backlog.

It did not work because:

it was skipped in case of emergency (which was quite frequent at that time),

refactoring would take more than one day (as it’s often the case with major refactorings)

One ticket per sprint

We dedicated one ticket per sprint to refactoring one specific legacy code. It worked quite well for a few sprints, but then our old habits came back.

It did not work because:

it was skipped in case of emergency (still happened, though less often)

Reducing indebted code on the go

During ticket estimations, we look at the code that will need to be changed and take into account if there is some refactoring to be done. Our PO understands the value of technical debt reduction and accepts (almost everytime) the price to pay. This is our current method and it is showing promising results. We dramatically reduced the time spent on “fireman mode”, that is to say fixing problems that occurred in production and required immediate actions. A few months ago, we were spending 1 or 2 days per week in “fireman mode”. After 3 weeks of “continuous technical debt reduction”, our efforts were finally rewarded with a sane work environment: we were back to shipping features!

We’ve come a long way

Getting to indebted code on the go took time and a lot of convincing. At first, tackling technical debt was mainly seen as a very expensive task which added no value to our product for the devs to feel just a bit more confident.

At one point, we even dropped our indebted code day for a feature day.

Feature day was a moment when Appaloosa employees submitted and voted for a new feature for our product. This was done without a pre market study, and the development timeframe was rushed for a single day. It mostly resulted in unused code and code shortcuts, which made for school-case indebted code.

A change of mindset was needed.

How do I implement that in for my dev team?

Before even considering refactoring legacy code, your dev team needs to share a common vision on:

what indebted code looks like,

why it’s important to deal with legacy code,

how to refactor.

Additionally, and equally important, a solid test coverage is essential to refactor with confidence. Some of the most critical parts of your application are likely to be the most in need of refactor, so making sure you did not break anything is essential.

But mind you, a good coverage does not mean you’re safe. Code can be indebted but so are tests. Refactoring code also means reading carefully the associated tests and making sure they actually test what you want and stay up to date.

The previous two points could not be possible without a supportive management. They are the ones you must convince that your “simple” task is actually a very complicated one because of all the technical debt that has yet to be payed.

They need to understand the importance of technical debt reduction and trust you whenever you ask for more time to refactor legacy code. To convey that message, the right person is the technical leader (or chief technical officer). We used to be a dev team without a technical leader and convincing our managers was hard. But that changed radically when Alexandre joined as our CTO.

That does not necessarily mean they’ll have to accept every time you ask for more time to refactor, but finding a good balance is essential for sane development cycles.

Now that your team and management agree on what, how and why to refactor, let’s see when.

What are the benefits?

A few months in, the results are great! As we write better code and clean our codebase as we go, we are converging to a sane and stable platform. The direct impacts are faster dev cycles, because writing a new feature is simpler. Also, and less time spent fixing a fire in production.

But for us, the process took time and was demanding. As developers willing to write efficient code, we experienced frustrations and had some tough discussions with management. Thankfully, we managed to change our mindset as a team with the arrival of Alexandre, our CTO. He had the authority to assess the need for a new technical debt management.

How about you? What are your methods to deal with indebted code? Does it happen to your dev team to resort to technical debt? Is your manager supportive?

Going down the rabbit hole with legacy code

If you have questions, remarks or advices, please leave a comment!

This article was written by part of Appaloosa’s dev team:

Benoît Tigeot, Robin Sfez, Alexandre Ignjatovic, Christophe Valentin

Want to be part of Appaloosa? Head to Welcome to the jungle.