Hard Lessons Learned from Half a Lifetime Practicing Programming
I’ve spent half my life now practicing the art of software development. Like any other art, some development principles are generally applicable to any human endeavor. I’d like to share these with you.
Programming consists primarily of two related but separate tasks. The first is translating definitions from one language to another; the second is managing complexity as those definitions grow.
When you think about translation, you probably think first of translation between natural languages: English to French, Japanese to Turkish and so forth. Programming has a remarkable similarity, except the translation goes from any natural language to an artificial one, which is essentially arithmetic.
At their core, computers aren’t much more than glorified calculators. Programs whose output appears rich, organic and analog generate those results through massive amounts of calculation - basic arithmetic, just performed millions of times per second. Since every program’s output consists of this arithmetic, another way to think about a program is as a giant mathematical equation. Facebook to Fortnite, Instagram to Indesign, from the calculator’s point of view it’s just an equation to be solved, with different variables provided at the time of execution.
Between natural languages, concepts can get “lost in translation”. Some phrase in one language doesn’t have an exact match in the other, and the translator has to construct an equivalent. Programmers hone in like bomb-sniffing dogs on words like “active” and “valid” and “soon”, because they are loaded with implication and inference, which the calculator cannot tolerate. Software development has taught me that natural language is inherently imprecise. Someone says something and you nod, thinking you agree, only to find a couple sentences later that you and they are not talking about the same thing at all. Worse, I’ve learned if you question people for further clarity, you often find that they don’t know exactly what they want.
Natural language intentionally trades away precision for speed and brevity. Someone says, “I saw a car”, you can guess what they mean based on probability: four wheels, enclosed body, less than 40 or 50 years old. Programmers think a lot about what we call “edge cases”: is a big pickup truck with dual back wheels still a “car”? what about those three wheel, two seat, open body vehicles? If that weird thing is a car, is a motorcycle?
Very smart people have come up with terribly clever strategies to help manage this complexity, but ultimately these techniques still fall on the developer to implement. On a large project, developers can’t handle the collective weight. In the software, this results in bugs; for the developers personally, frustration and burnout.
In the late 90s, a group of developers invented a non-technical strategy called “Agile”. Agile has gotten a bad rep from some folks over the years, but people experience a bad instance and then blame the concept.
Agile acknowledges the difficulty of imprecision in natural language, instead of pretending it doesn’t exist. In a situation where definition is vague or incomplete, and complexity building, Agile provides a method to keep making forward progress. People make Agile way more complicated than it needs to be, because it can be summed up very simply, and it goes like this.
Pick the most important thing. Work on that first until it’s done. Then move on to the next small piece, while checking to make sure the current work hasn’t affected the previous work. If it did, it means the two were somehow related, and you took on too much at once. But now you should have enough information now to go back and change the first piece as needed. Once you have two independent and stable pieces, move to the third, and repeat indefinitely.
Novice software developers make this mistake more often than any other: they try to do too much at once. They change several parts of the code at the same time, and when something breaks, they can’t tell which part caused it.
Every January, many people make the same mistake in their personal lives. They resolve to get more exercise, eat right, quit drinking and write a novel. Then they get surprised and disappointed when none of those things work out.
Agile philosophy suggests you pick only one of those things, the one most important to you, and break it down into the smallest achievable step. Don’t get a gym membership and hit the free weights; don’t even buy a FitBit. Instead, just walk around the block at lunch. But do it every day, every week, every year. The smallest piece is always hard enough. Small changes you can consistently keep up are a million times better than lofty goals you can’t. Maintain your gains.
Which brings us back to the practice of programming. Like music or yoga or any other practice, success requires the courage and patience to start working on one small thing at a time, and the humility to accept you can never achieve total perfection. It’s not really about the arithmetic, or the coding languages; the thing we’re really doing when we program is interpreting. We express our interpretations in different ways, and that expression is what we practice.
It’s hella hard, but worth it. I wish you the best in your practice.