How to Program Good - Chapter 1: Why Are We Doing This?
Before learning how to write Good Programs we have to first acknowledge why we are developing the programs in the first place. There is no objective measure of quality, neither in code nor in anything else; it all depends on context. Someone out camping might make a perfectly "good" shelter out of branches and logs, but that same shelter would not be considered a "good" long-term residence by most. In the same way a hacked out script might serve well enough to move some files around on a hobbyist's machine, and they might even be proud of it, but that same script would not be usable by that same person's parents.
So we have to first agree on the context by which we will judge what Good Programs even are, and only then can we move on to how to actually write them.
For the purpose of this series we are going to focus on programs which are developed as a tool to empower the average person in their work and daily life.
This, in fact, covers almost all code written today. Even if something like a database or SaaS offering is only directly used by developers, those developers are almost always building something which will be offered as a product to "users". Or maybe those developers are building a product aimed at other developers, and THAT will be used by those other developers to develop a product or service for users. In any case, there is someone who is going to be relying on this program, regardless of how abstracted away it is, to accomplish something in their life. The program is for them.
The word "tool" is the most important one in that highlighted statement. We can judge whether a program is "good" or not by the same qualities we use to judge whether a tool is "good" or not. And the number one quality of a good tool is reliability.
If I hand you two different hammers and say "which of these is better for building bird houses?", how would you go about answering? Well, if one is a normal hammer and the other is a sledge hammer then the answer is obvious. The sledge hammer is intended for an entirely different context; it's not a "bad" hammer, it's just the wrong hammer. But if I hand you two normal hammers, both about the same size and shape, how do you answer then? Maybe you have no idea, and pick at random. Or maybe you say "well, this one is made of aluminum and is essentially a toy, while this one is steel, has a rubber handle, and has a nice weight distribution. Use the steel one".
In other words, you can RELY on the steel hammer more, both because its weight and grip will help you do the job better from moment to moment and also because its build quality will ensure that the experience of using it remains constant over a long time. The constant experience of using the hammer allows you to accustom yourself to it, and eventually you'll give your hammer no more thought than you give the hand holding it. This won't happen with the toy hammer, which will slowly warp and dent over time, causing the experience of using it to change from moment to moment.
Once you get past the distractions of differing contexts, features, and prices, reliability always becomes the primary differentiating factor between two tools precisely because it allows for this extension-of-self. Reliability might be called something else on the brochure, like "durability", "accuracy", or "material quality", but what is always meant is that the tool can be relied on to do the same thing, everytime, for a long time. And programs are no exception. When we write code in this series our fundamental concern, above all else, is that the resulting program behaves reliably. Only once we've laid a foundation on which we can write reliable code do we begin the work of implementing features.
(Let's take a quick detour here and make a distinction between "unreliable" and "buggy", as one might be confused for the other. A "bug" is the word describing a program doing something the developer did not intend. The existence of a bug does not by itself make a program unreliable; if a bug happens in the same circumstances everytime, such that the user can expect it to happen, then the user will learn to work around it. But a bug which occurs unpredictably, inconsistently, or randomly is not one which the user can work around, and such a bug does render the program unreliable. In this series we are going to explore patterns for minimizing bugs which occur unexpectedly, while also exploring why eliminating all bugs of the consistent variety is not always a useful goal.)
If reliability is our primary metric for determining if programs are "good" or not, what are some metrics or attributes which are outranked? Here's a few:
- Usage of any particular language, library, framework, SaaS, or paradigm
- Lines of code written, and time taken to write them
- Fun experienced by the developer
Do these things matter at all? Of course they do! But only in as much as they contribute to, or at least don't detract from, the reliability of the program. "Fun experienced" in particular is worth being suspicious of. Many of us get into coding as a hobby first, and coding is attractive as a hobby because it can be very fun. But the aspects of coding which are fun do not necessarily overlap with the production of reliable programs.
Similarly we need to critically examine choices we make regarding languages, libraries etc... Our industry is very prone to hype cycles around new developer tools, where it becomes fashionable to write code using The Latest and Greatest Thing for no other reason than some large tech company has the clout to get that thing in front of an audience at a popular conference. These cycles are usually accompanied by various justifications which vary in merit. If you ignore the hype and instead judge all options on the basis of the reliability of the programs produced you will find it much easier to wade through the noise. Remember, the code is only seen by the coder, and what the coder thinks about the code is not the top priority.
I want to really emphasize these last two points. Our top priority here is the experience of the person who is relying on the program. This is a real person in the real world who may well have a bad day or worse due to the work we do, and it's on us to take responsibility for that work and give it the respect it deserves. There will be those in our lives who pressure us to produce sub-par work for various reasons, often using words like "efficiency" or "velocity". As someone who is frequently the most efficient developer on a team let me tell you: the ideas presented in this series will make you faster in the long run, given only some practice to become accustomed to them and the patience to not get caught up on the parts which seem unpalatable. Taking the time to write one piece of a program well speeds up the development of all others which depend on it, and this effect compounds as the program grows.
Next Chapter
In the next chapter we'll start digging into actual concepts by introducing the fundamental tools of reliable programming.
Hi! I'm available for remote contract work. You can learn more about me and my skillset by browsing around this site, then head over to my resume site to find my work history and professional contact form.
This site is a mirror of my gemini capsule. The equivalent gemini page can be found here, and you can learn more about gemini at my 🚀 What is Gemini? page.