Code review as literary criticism

It’s 9:30 AM on a Tuesday and I’ve just sat at my desk with a cup of coffee. I open up Slack, and ping goes the notification bubble in my taskbar. I have a DM asking for a PR review. I open the PR, and as I settle in and open my mind to the text as it scrolls lazily past me, I am teleported. But for once, not to a land of bytes and abstract syntax trees. Instead, this time, I am transported to an undergraduate literature course. It is not merely code I am reading, not just the formal solutions to business problems, ticking its way through a Jira burndown chart. This is something far more important than that: this is a text.

Code as text

All code is, at its heart, a form of writing. It’s a text, like a novel or a poem. Its similarity to written and spoken language is neither a mistake nor happenstance, but a fundamental result of the way our brains are hardwired to reason across abstract ideas. This linguistic and textual form remains the most sophisticated tool humanity has ever developed to communicate ideas, and it can express complexities with remarkable clarity and precision. Just as an author carefully chooses words and constructs sentences to convey a story, a programmer writes lines of code to articulate a solution.

Well-crafted code has a narrative form. Each line, each function, each class tells a part of the story of how a problem is identified and solved, and a good encapsulation has an arc: we parse and extract our inputs, we reason across them, we resolve to form our outputs. It becomes a combined history of the programmer’s thinking and the data’s lifecycle, a path from the problem to the solution.

And like a novel, code communicates on multiple levels. At the most concrete level, it communicates the technical functionality of the program. It tells the computer what to do, what operations a CPU needs to perform. But it also communicates at abstract levels, as a way of sharing ideas between programmers. It’s a way for us to collaborate, to share knowledge, and to preserve our ideas and solutions over time. Each line of code is a reflection of the person who wrote it, and the team that helped bring it to life.

As such, code is a form of communication, and it’s a rich one. Yes, it’s a way of expressing technical ideas with precision, but it’s also a way of telling a story, and for many of us, it is in this latter sense that we spend the majority of careers considering code. “We read much more code than we write,” after all. The textual body of code, then, becomes the artifact of our mutual collaboration, a social product that interpenetrates but is distinct from the social organisms responsible for crafting and maintaining it.

Death of the author

If code is text, let us do as the great critics and kill the author. Just as the interpretation of a piece of writing can be informed by, but shouldn’t be limited to, the author’s intention or biography, the person who wrote the code shouldn’t determine the structure of our critical reading during a code review. Instead, we should concentrate on the code itself, evaluating it based on its functionality, maintainability, and adherence to best practices.

Code review is a team sport, and the more eyes on the code, the better. By divorcing the code from its creator, you can look at the code with fresh eyes, free from any preconceived notions or biases. This allows for a more thorough and comprehensive review of the code, focusing on the code’s merits and deficiencies, rather than on who wrote it. This approach helps to foster a culture of continuous improvement, where the collective knowledge and expertise of the team is valued over individual authorship.

Role of convention

In literature, conventions–stylistic, linguistic, and genre–establish a shared idiom for communication. They provide a framework and a context for the reader to make meaning from the raw textual content. These conventions can be arbitrary in the formal sense, without a strict epistemological grounding, but have taken on explanatory power through continued use and social consensus. There is nothing about film’s shot-reverse-shot convention in-and-of-itself that communicates dialogue, there is quite literally nothing on screen that forces us to understand that the characters are speaking to each other. But this convention is so deeply baked into the grammar of film today that its meaning is obvious and unconscious to all who watch cinema. In fact, this convention is so strong that when there are narrative reasons for a literal dialogue to be impossible–two characters separated by time or space–a shot-reverse-shot sequence will still give us the sense of a dialogue, of a connection between the two characters, a sharedness of moment.

So, to, is the role of convention and idiom in code. There is no concrete formal reason to choose whether an opening curly brace ends a line (as in JavaScript, Go, or Rust) or starts a new one (as in many Java and JVM-targeting codebases). What matters is not even the fact of convention itself, as many developers will often suggest. Instead, what matters is the function of convention: to efficiently communicate shared ideas.

When negotiating conventions with our colleagues, the specific choices we make can be arbitrary, grounded in personal comfort, simple habit, or even IDE preconfiguration. But once those choices are made, every time the convention is followed or, perhaps more importantly, every time it is broken, there is meaning in that choice. The convention and its application communicates some meaning. If my codebase is colocating GraphQL queries with the components that rely on them, and I suddenly create a query near my project’s root, I am communicating to all who read this code that this is a query that is shared broadly across the application, or perhaps is called exclusively by the application’s root component.

Functional analysis of the text

In light of this textual understanding of code, when we review code, we should focus on the functional success of the code. But I do not mean “functional” to refer only to the technical aspects, but I very much include here that social and communicative function. We should read code to understand what it is communicating, what it is accomplishing, and whether or not it communicates what it accomplishes in a way that is expressive and meaningful to the reader. Rather than the arbitrary standards of pattern, form, or convention, our duty is to evaluate the code based on how effectively and efficiently it accomplishes its intended purpose. Our knowledge of a codebase or a community’s conventions, idioms, and styles are important tools in understanding the success of the code in accomplishing those goals, but those conventions are simply a means to the ends of this functional analysis.

By placing ourselves in this functional, authorless analytical mode, we have an opportunity to approach code from a place that is simultaneously critical and humble, without contradiction or tension between these two apparent poles. Humility is about recognizing that there are many ways to skin a cat in programming, and that different people have different ways of working. It’s important to not be dogmatic about your own style and to be open to the idea that other people might have valid reasons for doing things differently. But criticality is also important to ensure that the code meets the necessary standards of quality, performance, and maintainability. This critical evaluation should focus on the code’s functionality, its adherence to best practices, and its overall effectiveness in achieving its goals.