The valuable experience of maintaining your own code

I’ve had variants of this conversation with engineers over the years, but this particular blog post was sparked by a conversation I had with a junior engineer in mid-2021.

I’ll start with what I said to them:

One of the things that isn’t often taught to new developers is that working with existing/aging software is hard work. A lot of what we’re taught when we’re new to something (a language, a programming model, etc) is how to get started — “green field” as it were. While that’s good when starting out fresh, it doesn’t really teach how to understand, fix, and replace pieces of an existing piece of code.

I can imagine you're nodding along if you have have worked with a large, established code base for years (in the case of Appsembler, we work with the Open edX open source codebase).

Out standing in your (green)field

If you’re building something new, greenfield development feels great, because you’re unencumbered by existing code, design decisions, etc. It can feel daunting to some developers, but freeing to others.

If you’re doing this to replace an existing system, there well-know issues with this approach, such as the Second-system effect that Brooks described.

One of the other issues, if you’re always allowed (or allow yourself) to start over again, is that you are always building fresh and never learning how to maintain code. You’re never learning difficult, advanced development skills like refactoring.

[A side note: in doing a quick search on greenfield development, I learned about the term “brownfield development”.]

Maintaining someone else’s (awful, ill-designed) code

If you’ve had to manage an existing codebase, then you learn skills like code navigation, debugging, patching, and deploying. Hopefully there was already a test suite there to make the bug fixes go more smoothly. These are all valuable skills to learn as a software developer.

But in this case, you also have free rein to criticize the heck out of the existing system. I’ve seen this play out in small ways (“what were they thinking when they wrote this do-everything method?”) and much larger ways (“this entire codebase is a flaming pile of rubbish and I’m not convinced that the authors had even touched a computer prior to writing it”).

It’s all a form of venting, and it’s at someone else who isn’t present. Sometimes it helps to invent a fictional former developer at whom to lob criticism. At a prior job, my colleague named this guy Randy. In jest, we blamed Randy for a lot of the ills of the system that we’d built over several years.

Maintaining your own code

Years ago, I was working on a codebase that I and a few other developers had created a few years prior. I was debugging a particularly thorny issue, and had it narrowed down to one questionable line of code. “Who the hell wrote this abomination?!” I thought, as I did a quick svn blame (yes, this effectively predated git) to find the culprit:

beals 1459 ....

(shaking fist) Me!!!

If you’ve worked on a project long enough, you’ve had this happen. You’ve felt the impact of your decisions, whether individual commits or major design decisions. I’m a firm believer that these experiences make you a better developer. They help develop better instincts about when “good enough” is truly good enough, when it makes sense to refactor, and when code smells. And it definitely makes you better at writing easy-to-read, well-documented code (document the why of what you did, not the how).

Toward that end, if you want to have this kind of experience, you need to work in the same codebase for a while.

Tenure and what it implies

As a hiring manager, there are some questionable “common sense rules” that are often mentioned. One of them is that a candidate staying at multiple jobs for less than a year is a red flag because it shows a potential lack of commitment, and the person might leave your business in less than a year.

Let’s temporarily look past the fact that there may be other issues at play (maybe they worked for a few short-lived startups, or ended up working at multiple toxic workplaces). The issue I see for a developer is that they never stayed around long enough to maintain their own code over a longer period of time. The same can be true if a developer moves within the same org every 6 months, always working on new greenfield projects.

This isn’t a reason to avoid hiring the person, but you certainly shouldn’t assume that they have the code and design-decision chops that come from having had to maintain their own code. Make sure you’re vetting them for these skills in the interview process (“tell me about a time when you got burned by a design decision you’d made over a year before”), and if they don’t have this experience, don’t hire them right into a role where those chops are required.

How do we teach the skills?

But do talk explicitly with candidates and developers on your team about this, as a key set of skills to build. Too many developers still err on the side of “I’ll just rebuild this”.

Some ideas:

  • Make it clear that code maintenance is an important area of skill, and build it into your engineering ladder if you have one.
  • Give junior developers the chance to develop code that they’ll have to maintain down the line, even if that code is for an internal tool and not in customer-facing production.
  • Ensure that developers at all levels spend time fixing bugs in the codebase.
  • Supplement practical learning with some education, like O’Reilly’s “Building Maintainable Software” or their short course. Or classics like “Working Effectively with Legacy Code” and Fowler’s “Refactoring”.

Bonus content

After posting this, my colleague Shadi had some additional thoughts around finding joy in maintaining code. I love what he had to say, so I’m sharing an edited version here:

Love it, don’t hate it. Yes, you can love it!

If you think of maintaining code like looking through a haystack to find the missing needle, you’ll jump out of the haystack once you find that needle and you’ll never return. You’ll hate returning. But if you think of it like you’re de-puzzling a big mechanical device, searching for the rusty gear, you’ll have a chance to do some cleanup on gears that you suspect will rust soon. And you can also take notes about the complicated parts on which you spent a longer time to understand. Or you might decide to set a future task to redesign a group of gears into more efficient ones. In software engineering, these practices are called patching, documentation, and refactoring respectively.

Make others love it!

Simply by creating a maintainable codebase, you save others time time when they maintain your code. Remember that you might be the maintainer of your own code. This is a very big topic, here are the key points:

  • Use linters, write unit tests, and add docstrings
  • Don’t be an anti-pattern. We had that era of time when programming was an “art”, and everyone’s code was unique. Now we’re in the engineering era, and we have better standards and patterns that everyone can understand
  • Ask for code reviews. If your colleague doesn’t understand part of your code, then you must consider rewriting or documenting
  • Manage your time. Code in rush = rusty gear parts

[AB] Thanks to Shadi Naif, Omar Al-Ithawi, and John Baldwin for reviews and comments on this post.