Writing code is no longer a solo activity. Instead, reading and working within existing code has become far more important.
So how should we be changing our development process knowing that code is meant to be written once, but be read many times?
At Eventbrite, code review has become one of our most productive habits for building better things together. Making sure we write and modify code in a way that others—and our future selves!—can read well requires a new mindset and some deliberate choices.
We don’t work alone anymore.
Plenty of us crave time alone to solve a problem from beginning to end, but we’re not doing ourselves any favors with that approach. Ultimately, shutting out input from a healthy variety of stakeholders is the fastest way to end up with a faulty, less robust solution.
If we want to write and maintain code that’s readable for lots of other people, we need to work with lots of other people.
Iterating through a rapid feedback loop with others before jumping straight into the code is often the best way of arriving at a strong solution—even more so when the code has been written by someone else and we’re modifying it.
Only a broad and deep understanding of the relevant context allows us to ultimately build an elegant solution that benefits anyone affected by it.
Validate the data flow and architecture first.
Showing the building blocks of the solution can be the most fruitful way of presenting code, but it requires preparation. Know the answers to questions like:
- Is this a wizard-like flow?
- Will data be gathered and sent over the wire? Where is that happening?
- Is there a desired “happy path”? What are the other edge cases?
- What metrics need to be tracked?
Questions like these need to have a logical space to live in a presented structure, without irreversibly affecting the data flow in the system.
That structure provides a projection (and the possibility for building agreements and identifying unforeseen scenarios) about the code’s evolution over time.
Plus, when done right, it’s even possible to gather useful feedback from people who don’t know all the specifics or have a complete understanding of the solution.
Commenting adds clarity.
While code can sometimes be self explanatory, we all know it’s not often the case.
Commenting in the code provides a better view of the original developer’s mindset and intentions, and therefore really needs to be done as we work.
Comments can be in 2 distinctive categories, both of which are necessary:
- Clarifications about intended functionality (e.g., related to algorithms or tradeoffs made)
- Context about implementation (e.g., API descriptions or protocols used)
Clear comments can mean the difference between a future developer understanding whether to scrap the code or just polish it up.
Use the tooling as intended.
When we write code, we’re attached (by our own choosing or not) to a certain technology stack. Using this stack as it was meant to be used provides extra information to whoever picks up the code after us.
Don’t pass up free documentation by using your tooling in an inconsistent way.
Since the tooling comes with its own documentation, future developers have even more context for the original code—increasing the chances of code being reused and improved instead of trashed because it can’t be understood.
Testing reveals all.
Finding bugs is the most commonly cited case for code review, but it’s not the most complex return on this time investment.
Instead, testing is arguably the one great display of our work for three main reasons:
- It shows us our own work in a different light with real use cases.
- It provides others with insight into our development mindset.
- It highlights edge cases to address.
On top of that, providing a passing test suite, shows reviewers a genuine commitment and respect for their time—as a result, the bar is raised for everyone on the team seeking code reviews for stronger solutions.
It’s a conversation, not a competition.
Presenting code to others can make us feel very exposed.
What will they think about what I did? Will I need to be able to justify every single decision? Did I leave any profanity in the comments?
Be ready to change everything. Be open to offering explanations to validate rationale. Be ready to teach a perspective as well as learn from the mindset and experience of your peers.
Ideally, we should leave every code review with a better understanding of our role, better context for our solutions, or simply a reassurance that we truly understand what we’ve written.
Compromising must be done well.
Even if we don’t pass off code immediately, we should be able to return to it 3-6 months from now and easily pick up where we left off. But let’s face it: many of us aren’t going to keep working on the same code for much longer.
When it comes time to make tradeoffs, listen to your co-workers since they’re often the ones tackling issues later.
In this case, “listening” doesn’t mean “do whatever they say”—it means listen for root concerns about a given challenge or proposed solution to determine the right compromises.
“We shouldn’t do this because it will be too hard for a junior developer to read it,” is mostly likely weak rationale for a compromise. But maybe the sub-text is, “This isn’t the most straightforward solution so we should find a way to simplify it.”
Tuning your ear to hear the perspectives of others is hands-down the best way to make the right compromises and ultimately end up with longer-lasting, more robust code.
As engineers, we have a responsibility to everyone who works with our code. It’s crucial not only to articulate our ideas into the code itself, but also leave behind breadcrumbs of our intentions and tradeoffs.
In recent years, code review has become a highly respected practice that directly contributes to overall quality. Perhaps most importantly, it’s proven to produce understanding and awareness among developers.
Done well (and often), code review is the single most powerful way to grow as engineers.