← Back

Developing for the Short or Long Term (or How to Avoid Accumulating Technical Debt)

Clean code, tests, and a coherent design reduce technical debt and make it easier to revisit and evolve a project with less effort over time.

PrinciplesBest Practices

Not long ago, I had to pick up development on a project I started a year and a half earlier and that one of my colleagues continued afterward. Sometimes we code thinking we’ll remember every last detail of what we’re doing, when in reality we can’t help forgetting things after a few days, and a few months later we barely recognize that we were the ones who wrote this or that.

For each class, we include a comment section indicating its author and purpose as a quick way to identify that information. Sometimes it’s hard for me to remember that I started a particular class myself… It’s not poor memory (though who knows, maybe that too); I console myself by saying it’s the result of having written thousands of lines of code over the past few years.

We often make the mistake of programming to solve something now without considering that it needs to be solved for good. But how can we measure, more or less objectively, whether something is better solved for the long term? Whether we care about short-term or long-term results makes all the difference.

Picking that project back up once again showed me how writing software cleanly, clearly, and simply, with good test coverage, can save you from wasting days and weeks of work when all you need to do is revisit something you built a long time ago. The alternative is accumulating technical debt that will come back to haunt you sooner or later—or, worse, having a colleague curse your own messes. Yes, it’s probably that person who gives you a dirty look when you bump into them coming from the break room. Who knows?

Likewise, I haven’t always been able to spend as much time as I wanted on the details or on finding the best solution, and I know the result all too well: poor work gets delivered and then deteriorates over time.

We call all those details we leave aside without properly resolving them technical debt. When they pile up, they result in a solution that’s difficult to evolve and maintain. It’s bad, very bad, like accumulating negative karma… It’s every developer’s nightmare: having to take over someone else’s project, one nobody can understand, held together with duct tape.

It’s common to work in a way that leaves a great deal of technical debt. That’s because it’s very difficult to assess the impact it will have on time and productivity months or years later, when a project manager (your boss, that is) wants immediate results (in other words, a short-term gain at the expense of tomorrow).

Since I’m my own boss (though it may sound bad to say so), one of the principles I follow on the projects we develop is that things have to be finished cleanly and clearly. If the goal is to reduce risk (something more easily understood in other company contexts), then this is a guarantee for the future: we reduce risk by refactoring designs, cleaning up code, and simplifying. Interestingly, trying to work this way hasn’t made us miss any agreed delivery dates, which is a good sign.

Since a project is essentially paid for by the time spent on it, we can’t always stop for as long as we’d like to do things as well as possible. Even so, I believe that if you pause to reduce or eliminate that technical debt, all the time you lose in that moment will be repaid many times over later.

It’s also common to encounter people who get things done quickly. But the question isn’t how fast you work in software; it’s how clean and maintainable the work you deliver is. All of this is usually subtle, subjective, and difficult to evaluate, I’m afraid.

This time, after two hours reviewing what had been done back then, I was already able to estimate how long it might take us to add the new feature one of our clients needed.

There’s no magic wand for creating software that you can comfortably take over again some months or even years later. It depends on the type of project and, of course, on the experience you’ve accumulated. However, when a few simple principles are followed throughout the solution, it’s much easier (and more cost-effective) to return to working on it as if it were the first day.

In the case I’m describing, the magic wand was basically this:

  • The module to be extended followed the solution’s overall design architecture. No design exceptions off on their own; things were where you expected to find them.
  • The classes were small enough to make it clear what each one did, what single purpose it served, and how it fit with the rest.
  • The tests showed you how each class was used. Tests don’t just tell you whether your code works; they also document how to use the code you create.
  • Dependency injection lets you quickly find where the injectable parts of the solution are defined and how they’re assembled. If it’s designed well, a large part of the system’s functionality will be injectable.
  • No obsolete or dead code (code that never runs but someone felt bad about deleting). In other words, no clutter to distract you from what’s essential.
  • An exceptionally well-organized, understandable application structure.
  • Clear, well-chosen method and class names.

Sometimes we wonder what to do when we’re in a planning phase or have spare time that we could use productively. The answer is clear: you can always look for ways to apply all the points above to the solution you’re working on.

For almost any part of the final code, its quality didn’t emerge fully formed when it was first developed years ago. It’s the result of many refactorings and many phases of work, some of which had the sole purpose of improving the system’s internal structure and design.

Perhaps the best developer isn’t simply the one who quickly figures out how to do something and implements it. A great developer also takes a moment to improve something that already works, paying close attention to the details that let us return to today’s work easily and comfortably years later. It’s a little like taking a step back so you can make a great leap forward.

The debate over whether a software development effort should be considered awful, decent, mediocre, or excellent is a very old one. There are as many points of view as there are contradictory reviews of a movie or descriptions of the same news story by newspapers with different editorial lines. It’s an extremely subjective matter.

Still, one thing we can be sure of is that a solution with a lot of technical debt is much worse than one without it. The question isn’t just, “Does it work or not?” It’s also, “Will someone who worked on this application be able to pick it up easily in the future?”

As with many things, and as Benedetti said: “When we thought we had all the answers, suddenly all the questions changed.” The software you develop will reflect the questions you ask about the kind of quality you expect from it (is it fast, efficient, usable, maintainable, cleanly designed, good at tracing errors, and so on?).

Do you develop for the short term, or do you look a little further ahead?