One of my favorite tech talks is “Livable Code” by Sarah Mei. She’s given a few different versions of this talk, but I saw it for the first time at RailsConf 2018 in Pittsburgh.1 Each time I revisit it, I find that my own evolving philosophy of software design grows closer to the one that Sarah offers. I want to note here a few ideas I’ve taken from “Livable Code” that have stuck with me and that I continue to think about often.
Software design is not architecture
Architecture is a poor metaphor for software design. It suggests that designing, building, and maintaining software are discrete phases of a linear software development process and that afterwards the software will be done. This may have been true to a degree at some point in the past, when software was pressed onto disks and sold in boxes at a store. But nowadays, changes to a single codebase may be deployed hundreds of times a day, and the process of iterating on software happens continuously over its lifetime.
Software is a house you live in
Rather than thinking about software development as analogous to constructing a building, it’s more useful to think about software as a house you live in, as interior design. Today, frameworks like Ruby on Rails provide much of the “building structure” for our code (and we might think of different frameworks as we do different styles of architecture, each with different histories, philosophies, limitations, affordances). Instead of having to construct the building then, we get to focus on living in it, arranging the furniture, deciding where to put our stuff, and changing these decisions over time as our needs and desires change and grow. Certain changes are easier to make than others, like re-arranging the furniture or painting a wall. Others are more difficult like knocking down a wall or putting on an addition. None of these changes are impossible, but the cost of change varies and depends partly on the structure we’ve chosen to live in as well as its age, condition, and environment. Any changes we undertake must also consider the needs of everyone who lives in the house beyond ourselves. And like any house, codebases require regular maintenance and upkeep to prevent them from becoming unlivable.
Keeping your code livable
Sarah offers the following four steps to move your code from a place you have to live to a place you get to live, and she stresses that these steps are focused on people, on habits you can adopt with your team, rather than on any specific software design pattern.
Don’t make it worse. When we change code, even if we don’t have time to fix everything in the bloated, confusing file full of terrible sins that we’re touching, don’t make it worse. Try not to repeat the terrible sin in that file.
Improvement over consistency. Sarah illustrates this point well: “Five books in a pile and one on the shelf is better than six books in the pile. Maybe next time this code is touched, someone can move one more of the books to the shelf. But if you wait until you have time to move them all at once, it will never happen.” This one really resonates with me and can be difficult to practice at times, but I think it’s probably the guideline I return to most often.
Inline everything. “No more stories about refactoring a class. Do it inline while you’re executing on stories that actually have business value.” Do the easy things and tackle the low hanging fruit while you’re working on other tasks. Make the big problem smaller, and eventually the big problems will be small. It’s also easier to refactor inline while you have the context rather than having to regain the context when you come back in the future to try to tackle it all at once.
Talk more. Don’t ask for permission to clean up small pieces of code, but be upfront about what you’re doing. Don’t ask forgiveness either because doing this work is part of your job. Learn from the process, so that over time, you hone your sense of when you should have skipped some cleaning up when you took the time to do it and when you should have taken the time to do it when you didn’t. Seek advice from your team (“Is this the right time to do this?”), but don’t always take it. Moreover, work together because you’re living in the code together.
There is no such thing as absolutely correct software design
Many of us have experienced being in a group where someone invokes an abstract principle of “good design” or “proper architecture” to advocate for a particular design decision or direction. These decisions aren’t always wrong, but they often rely on received wisdoms from design pattern books or lazy justifications like “that’s what Google does, so we should too” or “My 30 years of experience make my opinion correct.” This kind of thinking tends to ignore the importance of local context and lived reality, namely that the right design for a particular codebase and the team working on it are unique to the needs of that codebase and that team at that time. And these needs can and do change as the needs of the codebase change (e.g., new features, new requirements, new iterations) and as the team changes (e.g., a small team grows, team members join or leave, a team diversifies its levels of experience).2 The continuously changing nature of software development and the ways in which software is situated in the social context of a particular team leads to the realization that there is no such thing as absolutely correct software design. If we can accept this idea, then we can free ourselves to refocus our attention on the actual problems we aim to solve and the people we’re solving them with.
“Livable Code” has helped me develop a more grounded, pragmatic approach to software design and development. It’s an approach that pays more attention to the needs of the team (“Will the least experienced engineer on our team be able to understand and change this code?”), that tries to shift debates over abstract principles towards concrete solutions to actual problems, and that focuses on moving one more book to the shelf and always leaving the code a little better than I found it.3
There is a @LivableCode Twitter account with her original thread on these ideas from 2016. Another great, more conversational, version of this talk can be found on the Tech Done Right podcast. But if you haven’t watched her conference talk, I recommend starting there. ↩
There is more to consider with regards to how the social context of a team affects its software design, but Sarah observes that type safety often benefits larger teams at the expense of individual programmer happiness. This is one example in which something like team size might factor into a technical decision for a given codebase. ↩
Perhaps a future post will dig into more concrete examples of this philosophy in action. Thanks to Dave Kroondyk for providing feedback on an earlier draft of this post. ↩