Bonnie Eisenman bio photo

Bonnie Eisenman

Software engineer, author, knitter, Esperantist. Member of NYC Resistor and author of Learning React Native.
Email me: [email protected]

🐦 Twitter 🤖 Github 🔶 RSS Feed

Recently, I needed to add a new feature to an unowned codebase at work.

What do I mean when I say unowned code? I think of unowned codebases as having a few qualities:

  • There are no planned improvements or changes to the code.
  • The original author(s) are unavailable for questions. (Perhaps no longer working for the company, or nobody knows who wrote it, etc.)
  • While there may be people who have familiarity with the codebase, nobody is responsible for it.

How does this happen? It’s not such an odd thing. You write some software, it’s useful, it sticks around. But once it’s feature-complete, or close enough—you move on. Not all software needs constant changes. Who owns it? Not your team, because they’re not investing in it anymore. It carries on, stewardless, borne ceaselessly into the future…

I don’t think it’s a bad thing to have unowned code lying around, but sometimes you do need to make changes! What happens then?

In my particular case, I poured myself a cup of coffee and settled in to read the code. My change was small in scope; I wanted an existing tool to handle a new kind of file format. The codebase that I needed to dive into was about 1,500 lines of Ruby.

I was reasonably lucky, because:

  • I knew that this code is currently in a good state, i.e., it’s working (and has even been re-deployed recently! amazing!)
  • There is a README, explaining how to get it running locally, and it mostly works
  • There are comments in the code, and the structure is generally reasonable
  • There’s one other person left at the company (!) who’s read the code and figured out how to run & deploy it.

That being said, there are some smelly bits. Not surprising, for a single-author, unowned codebase:

  • There are TODOs scattered around that obviously will never be cleaned up.
  • You can see the organic growth of this code: there are layers of abstraction that probably built up as the author understood the problem space better. (I think this is a common single-author problem. Peer code review tends to strip this kind of scaffolding away.)
  • It’s difficult to fully onboard a developer - you need to track down one person at the company to get proper permissions setup (for getting a functioning developer environment with staging, deploying permissions, etc).
  • There are no tests.
  • There is a lot of dead code which never gets executed.

Tackling an existing system like this isn’t so bad, especially when it’s reasonably-sized. Here’s how I handled it:

  • Developer setup: can I run it in its current state?
  • Look through the git history to figure out if anyone still at the company has modified the code (there’s one other person, perfect)
  • Read the source code to figure out how the code is structured, and where I should add my new feature
    • this involves a combination of “start at the beginning” and reading from the entry point inward, as well as judicious use of grep
    • incidentally, I’m not super familiar with Ruby, so I end up learning a lot about things like blocks
  • Add my new feature, implementing it in tiniest-possible pieces and testing frequently on my local machine
  • Discover bugs in the existing code, and fix them
  • Continue implementing my new feature
  • Manually test that my new functionality works
  • Write some tests for the bugs I found, to prove that they haven’t snuck back in

At this point, I now had Choices! I could clean up this code. In fact, as the most recent editor, I probably had the most context on this codebase, because I’d already dumped it into my head.

  • I could write tests for all of it.
  • I could do some cleanup, like deleting the unused code.
  • I could leave it as-is, once I’ve added my new feature.

Option #3 would contribute directly to the poor quality of the codebase, and in fact make it worse (there is now more code in this unowned little codebase, bah). But! It might be the right choice for the business. I had higher-priority things on my plate, and I could fully justify leaving this alone.

Buuuuut…..Option #2 isn’t substantially more effort. So that’s what I did: add a couple of tests around the new feature, delete some of the unused code, and call it a day.

This was a fun detour from my usual job! I enjoyed the process of dumping a bunch of new code into my brain and then poking at it until it did what I wanted, and hopefully I left things in a better state than I found them in. (But really…who knows?)

Every time I have to do this I remember that new codebases don’t have to be particularly scary; they just require patience, and some time to explore and poke around. Just because there isn’t a map doesn’t mean that you’ll get lost!