No Silver Bullet
A tree so broad you cannot wrap your arms around it grows from a tiny sprout; a nine-story terrace rises from the first basket of earth; a journey of a thousand miles begins beneath your feet. — Tao Te Ching
In the blink of an eye, my daughter has reached the age for piano lessons. My love of instruments is pure Lord Ye–loves-dragons posturing: the house holds a piano, a violin, and an accordion, yet I cannot even read a staff, so I am in no position to supervise the girl’s practice. That burden fell to my wife. She is a tiger mom, and when she turns strict there is no ambiguity about it: if the piano isn’t practiced, there is no dinner — and no dinner there is. The girl throws tantrums, rolls on the floor, wails at the heavens, while Grandma stands by with red-rimmed eyes trying to intercede, all to no avail. At moments like these I of course cannot bear it, but outwardly I must stay in lockstep with my wife, so I can only quietly roll off to one side and play the part of a placid, unperturbed lump of a man — though inwardly I still fret: is this approach going to grind the child’s interest away entirely?
My worries, as it turned out, were unfounded. She has her lesson on Saturday, and Saturday and Sunday are the hardest days — the wailing is at its worst then. The reason: she has just learned a new piece and is not yet familiar with it, so she hits wrong notes constantly, and every wrong note means starting over; the teacher’s assignment takes a very, very long time to finally pass. By Tuesday or Wednesday she is growing comfortable, her fingers begin to fly, and the crying grows rarer; by Thursday and Friday she has it well in hand, and in most cases plays through happily. Because of her mother’s strictness she suffered a good deal at first, but gradually bitterness turned to sweetness and she tasted that “hoist the cloud-sail, cross the vast sea” exhilaration. At her most miserable she sobs, “Mom, I never want to do the teacher’s assignment again!” — yet at the monthly “family recital” she still beams with pride, showing off her chops to the relatives back in China, brimming with a sense of accomplishment.
Many readers ask me in the comments how coding ability is built. It is the same principle. Practice more, read more, write more.
Software development education is a very young discipline, having passed only a few decades — a drop in the ocean beside the centuries-old history of piano pedagogy. Piano has mature training and certification systems; software, to this day, is a roll of the dice. And really, piano and software have a great deal in common: learning piano means first learning a fingering technique and a language (the staff); learning to code means first learning a fingering technique and a language (Python, say). The piano’s hello world is “do-re-mi-fa-mi-re-do”; software’s hello world is, uh, hello world. Once you have nailed the fundamentals of piano, you can compose pieces to order; once you have nailed the fundamentals of code, you can write fitting code to spec.
But writing code does not demand what piano does — drilling the fundamentals to mastery, working through every score famous and obscure, leveling up one grade at a time until the so-called Grade 10 certifies you as trained. Usually, once a programmer understands how to use a language’s or a framework’s basic constructs, they cannot hold back — they dive right in. Don’t know how to use a function? There’s Google. Don’t know how to write the code? Stack Overflow! And so, for the people the code is meant to serve, writing code becomes a high-risk activity: we are desperately reliant on the code’s author happening to be someone of real ability — someone who can deliver high-quality work while simultaneously keeping her fundamentals solid.
Take a simple task: iterate over a list, extract the tuples inside, and insert them into a priority queue to sort.
Here is how a software engineer with years of experience goes about it (assuming a Python implementation):
- Give it a rough think, then roll up the sleeves and start coding;
- Ugh, I don’t quite remember how
mapworks — is the first argument the function or the iterator? Let me try it in ipython first — oh, the first argument is the function. What a memory I have; - I rarely use tuples — how do I pull the second element out of one? I’ll have to google that — ah, you can destructure. Nice! (And a sideways glance of contempt at PHP.)
- Does Python support priority queues? Never used one. No matter — bring out Stack Overflow — Python is genuinely great, there is a ready-made
heapqfor even this, and this Stack Overflow snippet is exactly what I need, only five or six lines, straight copy and paste. Done in no time, yeah! (Another sideways glance of contempt at PHP.)
Hold back your laughter; I’d bet this is the daily reality of no fewer than half of all engineers. In fact, this example is not something I made up — it is what I saw over the past year of interviews from the majority of so-called “senior software engineers” I met: getting stuck on the most basic functions, forced to Google. And that isn’t even the worst of it. I have seen people Stack Overflow their way from the first line of code straight through to the last.
I don’t know how the writing profession conducts its interviews, but if a candidate had to consult a dictionary and flip through the Complete Anthology of Classical Poetry while drafting a passage on the spot, would they pass?
How, then, should we write code properly?
Before we “create” new code, we should already know how to write it cold — the language’s fundamentals and the libraries we use regularly should never be obstacles. The entire process of writing code should be fluid, like composing music: wherever the mind drifts, the fingers land, all in one unbroken breath. Of course, this depends on deliberate practice in ordinary times: don’t know how to use map? Fine — take a list, a dict, and a tuple, and run map / filter / reduce over each of them, once with lambdas and once without.
That sounds familiar, doesn’t it? It is exactly how the teacher assigns my daughter’s piano homework: do-mi-sol, do-mi-sol, left hand, ten times. That “do-mi-sol” is our map function — a fundamental.
Someone will say: that makes for a long maturation period — hundreds upon thousands of libraries, endless version upgrades; we programmers cannot afford ten repetitions of each. It is unrealistic.
See — programmers are that precious: three months of training and you draw a relatively high salary (that is the Silicon Valley reality), so why on earth should I do these tedious drills?
So let us compromise. When we write code like the example above, at minimum, properly practice in the REPL the functions we have forgotten how to use; then take that code copied and pasted from Stack Overflow and write it yourself once, drilling the libraries and functions involved in the REPL — so that the next time the same need arises, we are not obliged to go hunting online for the answer. That is not too much to ask, is it?
Those willing to do it remain a minority.
People who run marathons regularly often use races to drive their training. But to use races as training, you first need a large base of training, so you can withstand round after round of relatively dense racing. With that base, the races further tune your body to the right rhythm, building your cardio and forging your muscles — the races themselves serve as training. To use the marathon analogy: every big feature we build in our daily work amounts to a race. If you haven’t built up a base, that is when the difference shows: something a colleague completes in one iteration at high quality takes you two or three iterations of stumbling, and your code is a veritable scrapbook of Stack Overflow clippings.
Intuitively, software development should be even more amenable than marathon running to using races as training — even to racing as training: every feature you build can help solidify your skills. The premise, of course, is that you write a large volume of code every week to back it up. Write enough code and you will inevitably reuse a handful of important functions and libraries, and your ability to use them correctly gets reinforced. Unfortunately, for a great many software engineers, this is a false premise:
- Your actual weekly code volume isn’t that large. Most of your time gets consumed maintaining a large system, so on average you don’t write even two or three hundred lines a week (note: not diff lines). Don’t rush to dispute that number — in two years at Tubi TV, I built three or four systems from zero to one; Node and Elixir combined, at the most generous count, came to about forty thousand lines. A hundred weeks in two years: at most four hundred lines a week.
- Even if your code volume isn’t small, if the scope you own is narrow, writing more is just repetition — like practicing handwriting: someone else practices a thousand characters fifty times each; you practice a hundred characters five hundred times each. The same fifty-thousand total, the same effort, and wildly different results: they have mastered the strokes of 亡口月贝凡, while you are still scratching out 个十百千万.
On the matter of improving as a coder, there is no silver bullet — only deliberate practicing, like piano training. Without practicing, you are defeated by 1); with enough practicing but not enough deliberation, you are defeated by 2).
So it comes back to the same words: practice more, read more, write more. Spread a layer of earth, tamp it down, then spread another. What your job cannot give you, what there is no time to practice on stage — you must make it up backstage, on your own time. That is the secret to leveling up as a coder.
The weather has turned cold all at once. Here is a bowl of chicken soup to warm your lungs.