Software developers tend to imagine the job is programming: poking, prodding, coaxing, and massaging ambiguous concepts from the human realm into concrete instructions for the digital one. Coffee in, code out. Dev in the middle.
But it turns out that computers are the easy part. Tell them to do something and it’s done. Tell them to do something wrong, and that’s done, too. Exceptions arise, but only rarely—in fifteen years of professional practice I’m still waiting to collect a bug hatched from anything more exciting than human error.
The hard part isn’t telling the machine what to do. The hard part is telling humans why the machine is doing what it does, how to use it, and how to fix it when it breaks. The job isn’t programming. It’s communication.
“Know your audience,” they say, and programmers have three: the computer running the program; the customer using it; and the poor soul that winds up maintining it.
With practice, the computer is easy. While there are plenty of non-trivial problems known to computer science, most development roles face only a well-understood (and generally tractable) subset.
Customers are somewhat trickier, but only just. Since systems’ success is accounted in terms of customers, entire disciplines—product management, user experience, and human-computer interaction, to name a few—have sprung up in support. Validation and familiar design patterns help reveal how software is meant to be used and documentation handles the rest. It’s not always pretty, but customers’ needs tend to get met.
That leaves the maintainer. Playing marshal to a parade of bugs and support requests is thankless work at the best of times. In a suitably impenetrable system, it’s nothing short of misery. Not only must the maintainer mediate between human needs and the constraints of software systems, but they must do it on someone else’s terms. No matter if it’s an open-source author, a teammate, or just a past self, software is always easier to write than understand.
Ship enough software and something bad will happen. Ship a fix, and there’s that much more to understand and maintain. Unless we actively campaign against it, software begets more software.
The easiest path to maintainable software is not to write it. But if we must write it, we can at least:
Write it well. Compose systems from clear, minimally-dependent units. Since software that is simple to write is usually simpler to read, minimize cleverness. Also logic and the range of input the logic must accommodate. Bounding user I/O, IPC calls, and runtime configuration will enable simpler systems beneath.
Use code review to challenge design and legibility before merging new maintenance requirem—er, features—and address problems while they’re still fresh.
Supplement documentation with tests, effective commit messages, and inline references to wrap context around design decisions that might mystify future readers.
Make runtime behavior transparent with adequate logging and monitoring facilities.
“Thou-shalts” can’t replace thoughtful effort, and this is hardly an exhaustive list. But it’s somewhere to start.
So write like someone’s reading! Programs aren’t written in the bytewise babble of the CPU because they’re meant for people. Fallible, imprecise, confused people. People like us.
Keep that in mind, the next time you’re programming. You’re writing for the computer as far as you have to. And for the customer, but only at the business end of I/O. But mostly for your teammates, your future self, and anyone else that’s going to read this down the line.
Yeah. You’re writing to us.