Working Code Versus Livable Code
When doing code reviews, I often find myself on the fence about seemingly simple changes. Hitting “Request Changes” feels petty when the code works—when my concern is readability or structure rather than behavior. Classes and modules like these build up though, and I look back on these commits like the clutter that too-often invades my bedroom: it would have been better to take the extra time.
Marie Kondo’s voice echoes in my head in moments like this (and when I sift through my drawers of clothes), urging me to only put something down when it’s in the right spot. Maybe in this case, only committing when the code is right.
I lead teams of developers working in very large codebases. Many times, I suspect that we’ve made a mess of things, especially when we can’t quite remember a particular abstraction or when we have multiple ways of accomplishing the same thing. It’s like clutter we’re leaving around the house, and the other teams dealing with our messy APIs are unfortunate housemates. How can we do a better job so that we feel happier “living” in our codebase and it’s a welcome place to visit and cohabitate?
We All Have to Live Here
First, adopt the mentality that the code you write will be read and maintained by everyone on your team. Are you writing something that’s going to get you through the next Jira ticket, or are you writing something that your future self and your teammates will understand in three months or a year? How clever is it? Cleverness has become a code smell to me over my career; code golf used to be impressive, but now I see it as a major liability. Yeah, I can use obscure variables and methods. Do I want someone to git blame me and need to ask me what the heck I was thinking? No. Being more verbose and clear is usually better than being clever. Possible exceptions could be low-level utilities where performance is extremely critical. Draw your “cleverness” around API boundaries and accept that maintainers of the API are going to need to know where the cleverness is and why it’s there.
Maintain the view that you’re going to have to support any of the code in the repository at any point in time. You might not be able to message the teammate who wrote it due to time off, retirement, sickness, etc. When you’re doing code reviews, approach them as if you’re going to have to either change or support that code as soon as it ships. This doesn’t mean endless variable naming flame wars. Be realistic about the level of control you exert over the code your teammates write, but also take responsibility for it.
Paying Attention to the Pain
Are there parts of the code that nobody wants to maintain? At some point, our front-end app was avoided like the plague. I couldn’t blame my teammates. In this particular case, the pain wasn’t in the code itself, but in the environment setup. We had slowly plucked our monorepo clean of all our responsibilities and flung them into microservices. One of these services was our front end. On our development machines, everything was running in separate Docker containers and Docker struggled with synchronizing the number of files the front end had to deal with. The first solution was to move all those services out to EC2 so that a native Linux machine could deal with it. That changed the issue from a local sync problem to a remote sync problem. Before we addressed the issue, page refreshes would take on the order of 120 seconds to render. Terrible and demoralizing.
So I took all the front-end stories I could tolerate.
I wallowed in the pain until I could articulate exactly what my team was experiencing, then advocated for a change. Thankfully, our leadership values the sanity of our engineers, so we got time and support to find a solution. Now nobody avoids front-end stories.
This example was more of a dev/ops issue, but it applied to the codebase too. What areas of your codebase do you see devs avoiding? Do stories get passed up? Are there certain engineers who “specialize” in those pain areas? Pay attention to the areas people avoid: there might be some dead rats you need to take care of in there.
When Housemates Move Out
The worst situation is when the code is so bad, so indefensible, that we lose teammates over it. Or when another team “refactors” away from our codebase, not for scaling or tech stack needs, but because they just can’t move anymore. That’s when you need to hit the pause button and really examine whether you should be moving forward at all until you address the issue.
Several years ago, a team adjacent to mine had a new dev join them. New in the sense of the team, but he had a good amount of seniority. Part of that team’s responsibilities included creating content inside of my team’s domain. The API that we exposed, if it could be called an API, required so much internal knowledge that he needed to know nearly all of my team’s domain in addition to all of his own team’s domain. The ramp-up time was atrocious and the complexity was sky high. We didn’t address it in time and the new engineer requested to transfer teams.
Though we weren’t able to address it in time for that dev, we took his feedback seriously (thankfully he’s still with the company) and invested the time to jointly develop the API contracts necessary to remove the massive amount of domain knowledge required. It took a long time. Maybe more than a year. The result is that the API is easy to work with and has accelerated the other team’s time-to-market for new projects.
When is it Good Enough
How do we know when we’re doing well in terms of livability? Do we have to make everyone happy all the time?
This follows an 80/20 principle. The main technique is to pay attention. Watch your tickets to see what people are avoiding. Have regular engineering meetings so people can vent, and make sure you do something about it when there’s a noticeable pattern in the pain. Then there’s the age-old metric of watching code churn. Do you need to change a particular module every single quarter? Every single story? Every day?
Carve out some time for each of those items once you identify them. Be ruthless until the engineers on your team and those working with your team don’t hate Monday morning. We owe it to ourselves to make sure the work we do is something we’re proud of and that we don’t avoid.
I find that I can gauge the quality of an engineering leadership team by whether they make room to prioritize these high-pain issues. No, you may not be able to drop your quarterly goals to do that much-needed refactor, but you should be able to carve out planned time (dare I say 30% of your time) to address an issue that has become a code-livability issue. Management should understand that replacing a “housemate” is much more costly than cleaning up the socks and underwear strewn all over the living room.