Your engineer keeps saying her team really needs to dedicate some time to reducing technical debt. WTF does that mean? Why can’t they just focus on building features like they’re supposed to? Did I hire crappy engineers?
I’ve never worked at a company whose engineers – including myself – thought their codebase was just dandy. A favorite pastime of every engineer is griping about code they inherited from departed colleagues. If they’re the first engineer and wrote all the code themselves, they can’t stand the code they wrote 6 weeks ago. They’re older and wiser now and have learned so much.
What is technical debt?
Technical debt is inefficiency in a codebase that compounds over time (hey – just like credit card debt!). The inefficiency may be related to maintainability, performance, bugs, or style. As such, ignoring technical debt will hurt your team in the long term by making it take longer to get things done, making the product sluggish, having more bugs, or making the code difficult to read. Engineers often view technical debt as an internal shame and don’t openly discuss it with non-engineers due to embarrassment or difficultly explaining the technical details, which is unfortunate because it is critical to the team’s short- and long-term performance.
So why does technical debt arise? I’ll cover four broad categories, but there are many others.
Short term hacks
Like everyone, engineers sometimes take shortcuts. You might have been complicit – remember when they said it would take 2 weeks and you said that you have a really important investor demo and it would really help the company if they could do it in one? And – magically – they did? It doesn’t necessarily mean they were sandbagging or that you inspired them to improve their efficiency through your eloquent speech. They may have done it the quick way rather than the best way, and thereby created technical debt that will need to be fixed in the future.
“TODO” is an inside joke among programmers. We comment our code saying “this isn’t ideal, we’ll clean it up later” knowing perfectly well that nobody ever will. Five years later, when the author of the lie has long since left the company, some poor new junior engineer will be trying to figure out some obscure bug and find this “TODO” comment written by someone who can’t help them.
This is one of the big differences I’ve seen (and participated in) between large companies and small companies. Large companies generally have less tolerance for imperfect code, at the expense of taking much longer to get things done. Startups have to get things done fast in order to survive, and getting something working fast may be preferable to getting something working perfectly. That may be the right business decision, but keep in mind that you’ll pay for it later on.
Lack of refactoring
New use cases can cause existing code to no longer be ideal. The best way to engineer three individual features may not be the best way to engineer all three in the same application. You may have heard the term “refactoring.” Let me attempt to explain what refactoring is by way of analogy.
Let’s say you’re writing a story that includes the sentence “Jane went to the store.” You revisit this part of the story and realize you omitted the key plot detail that she went to the store in order to buy apples. An easy way to fix this without much brainpower is to add a sentence: “Jane went to the store. She needed to buy apples.” Okay, maybe not the most succinct, but it gets the job done.
But wait! You returned to your desk the next day and realized it’s very, very important that your readers learn at this juncture that Jane also bought bananas. If your mind is 100% focused on the bananas that you just can’t believe you omitted, and you can’t waste a single second in your scramble to include this fact, you’ll end up with “Jane went to the store. She needed to buy apples. She also bought bananas.”
Of course, taking a small step back, it’s easy to see that this riveting subplot is better written as “Jane went to the store to buy apples and bananas.” Congratulations! You just refactored the sentence.
This is a silly example, but it really is analogous to how engineers work in a professional environment. The easiest way to fix a critical bug is likely to add a couple lines of code somewhere. The product manager requested a new feature? Sure, we’ll just add some code over here. It’s just like the boiling frog metaphor: each small change looks fine on its own, but eventually you end up with a dead frog… er, a messy codebase.
Experienced engineers will try to anticipate the direction of the codebase and ensure that code written today will accommodate future use cases (this is a slippery slope: engineers have a tendency to spend too much time generalizing code to handle elaborate use cases that never materialize). You can help out your engineer by making them aware of future plans that they may want to think about when implementing their current workload. It could save a lot of re-work later.
Technical debt might not be due to objectively bad code, but rather different styles from different engineers. It’s like writing a book where each paragraph is written by a different author. Even if all of the authors are excellent, the book won’t be very coherent. Engineers may have different styles or preferences, which can lead to noticeably different voices throughout the codebase. Sometimes this no worse than different accents in different parts of the code, but sometimes it can be so bad that new engineers don’t know the correct way to implement a new feature because the existing examples are so fragmented.
For example, a senior engineer may begin a large refactor with the intention of converting the whole codebase to the new style over time, but then the engineer gets pulled onto another project, gets promoted, or leaves the company. A new senior engineer takes over and takes a third approach, leaving three different styles scattered throughout the codebase. Any of the three would be better then all three. A new junior engineer who comes along will have a hard time determining which approach to use when implementing their new feature.
Lack of ownership
Ok, sometimes it can be crappy engineers. Or, more commonly in my experience, it’s contractors. If you know you’re not going to be responsible for the code long-term, you’re less likely to expend the effort to ensure it stands the test of time. Their motivation is to get it to the point where they get paid; they don’t care what happens to it. Even if you have a senior engineer supervising their work, they can’t catch everything, or they may be thinking “it’s temporary code, I’ll fix it later” (see: TODO). Short term contractors also compound the problems of more frequent new engineers with different styles.
Non-engineers often see the solution to short term resource constraints as “let’s hire a contractor to get past this hump.” But understand that engineering output is not proportional to the hours you pay for. If the people doing the work don’t have a vested interest in the long term success of your product, your future engineers will pay the price.
Okay, what does this have to do with me?
If you have a stake in the quality of the product and the long term efficiency of your engineering organization, have a frank discussion with your engineering leadership about technical debt. I’ve often felt that there is an assumed disagreement between engineers and non-engineers about quality vs. speed – with engineers wanting to take more time to invest in doing things right and non-engineers applying pressure to get things done quickly – but it doesn’t have to be this way. If you trust your engineer and you find yourself frequently arguing about timelines and resources, consider that your engineer may be thinking more about building something in a robust way rather than leaving work early. In the past, I’ve found it difficult to communicate this because the details of what makes it “robust” may be highly technical, so I’ve spoken in abstract terms, which only escalated the conflict.
In short, you get what you pay for. Especially since working in startups, I’ve realized that technical debt is sometimes ok if it will get the company over a hump that will keep it alive, but you should encourage your engineers to take the time they need to do it right whenever possible.