Development is generally neatly lumped in as an engineering discipline or as a science, if the title on my diploma is anything to go by. And at its core, it is inherently tied to boolean logic. There’s a satisfying objectivity to developing software, the same way there is to solving an equation—programs either run, and do whatever they’re supposed to do—or they fail.
I officially switched my major to C.S. from English after muddling through a graduate class where the postmodernist professor critiqued me for using language that was too “cold and clinical.” It was deeply satisfying to run my first assignment and see the numbers I wanted to get spit out into the terminal. No minus points for my supporting arguments just not hitting the right emotional notes, no comments on my opening sentence not gripping the reader enough. Just a 100% from the TA, and wonderfully correct numbers blinking away in the terminal.
But, after really being in the thick of it for a couple years now, I’ve come to a realization—software development as a practice is deeply rooted in the subjective, in all of its inconvenient glory.
Because, when it comes down to it—what does it mean to write “good code,” exactly?
How Do You Quantify Quality?
To an extent, it’s relative to the person asking. For a consumer, it’s when an app is easy to use and does exactly what the user wants it to. For business leaders, it’s whatever produces value and/or lowers costs. And among developers, it’s generally considered to be whatever is easiest to understand, augment, and maintain.
The concepts are closely related, for sure. Maintainable, easily-understandable code can be molded into something that creates value (by being highly useful to customers), while difficult code is hard to edit, prone to bugs, slow to modify.
My college classes, when they deigned to answer this question, highly stressed speed. Weeks were spent on us memorizing the differences between bubble sort and quick sort, in excruciating detail. Good code implements the fastest algorithms. Good code is O(1), or O(log n).
This has mostly held true within my professional life, but with a caveat: it’s not important for a program to be the fastest, but it is absolutely vital that a program run fast enough. Ruby isn’t close to being a contender to C++ when it comes to performance, for example, but in most cases it would still be better for my client to have a Rails server instead of one written in C++. Fastest isn’t important. But if users running against that same server are experiencing delays moving around the app—huge problem. It still has to run fast enough for the person using the program to interact with it without interruption.
But writing a solid program isn’t just engineering the sequence of lines that accomplishes a task quickly. I’ve heard the term “craftsmanship” used in relationship to it often, and there’s a lot to be said for that concept—but writing good code isn’t just analogous to carving out a chair. It is also about communicating effectively. It’s not just important that the code works—otherwise everyone would be typing with keyboards like this. It’s vital that the code explains to the humans that need to read it what it is doing. Software development is writing, in the compositional essay sense.
Good code isn’t just code that solves a problem the fastest. It’s the code with meaningful variable names. It’s the code that is organized into some kind of sensible file structure. It’s the code that adheres to—and this makes my stomach twinge with a bit of pained de ja vu—style conventions. Constants are named in all uppercase. Variable names are either snake or camel, depending on the language.
Not to mention, the undecided and perpetual debates:
- Tabs or spaces? (And if spaces—how many spaces?) Should brackets be on the preceding line for a block, or on the next? Are languages better off without them entirely, for that matter?
- Should the code be structured around the concept of entities that have responsibilities and cooperate, or stacks of functions that call each other and pass around immutable variables, with no sense of state?
- Is commenting good because it explains to the person reading the file what the file is supposed to do, or is commenting bad because comments are highly susceptible to bit rot and hard to maintain? Code should be self-documenting anyway, right?
- And that’s not even touching on testing—what should unit test coverage be? How much of the functionality should your integration tests cover?
- How long is too long for a function? Five lines? For a class?
All of these (generally unquantifiable) opinions revolve around one thing—making sure that the code communicates to the reader clearly. “Good code” is performant; it effectively addresses the issue it’s supposed to solve. And it’s clear. It speaks to the reader about what it does, and how, in no confusing or uncertain terms.
It almost—almost—makes me think that there should have been an English requirement in my C.S. curriculum, right next to the Calculus one.