data Blog = Blog { me :: Programmer, posts :: [Opinion] }

Mind the Gaps

“If you truly accumulate effort for a long time, then you will advance.” Xunzi.

It’s no secret that many programmers are self-taught. Some of us, myself included, lack formal credentials related to computer science. I’ve worked with good programmers who didn’t graduate high school. It’s a strange profession, with the skills often acquired simply as a side effect of tinkering. You don’t hear about many civil engineers or corporate attorneys who do what they do because they enjoyed playing Zork on their dad’s office computer.

The downside of this relative accessibility is that there’s no curriculum for making a programmer. Universities often presume that their computer science students know how to program before they get to campus, and their curricula are aimed at producing computer scientists—a noble and necessary pursuit, but not one that produces knowledgeable working programmers. Graduates of those programs often end up little better off than anyone else when they’re out of “Advanced Compiler Optimization Techniques” and into “Sav-Mart needs the ‘repeat previous order’ button on their B2B site by next Tuesday.”

Even worse off are those students who stumble into an “industry-oriented” curriculum at a community college or bootcamp. While a CS student from a university sometimes gets an education that has anachronisms and too much theory, these students are taught, at best, a few frameworks in accordance with the desires of local industry. Graduates, if they are capable, acquire that capability through their own effort.

Think of junior programmers in the first few years of their careers: they’ve taught themselves a language or two. They may know some theory, or they may not. If they do, they may still not know how it’s relevant to real problems. They may have been tested rigorously on the trivia of future-proof technology like Silverlight.1 They all “know how to program,” and they can all muddle through, but how can they improve?

How do you know what you don’t know?

There are gaps in all programmers’ knowledge. It’s a source of the famous “imposter syndrome,” where we tend to feel that everyone around us knows more than we do. Despite this feeling that someone’s going to find out that you’re a fraud at any moment, it’s hard to know exactly why we feel that way. (That’s why they call it a “syndrome.”) The only way to escape this amorphous feeling of inadequacy is to try to find our gaps and fill them ourselves.

Knowledge gaps are pernicious, though. You can do day-to-day work without ever really understanding a deficiency because a lot of the gaps don’t show up immediately. They show up when you need to modify or debug your code in a year, and without external feedback you may never understand that your class might have been better if you’d structured it differently. It’s entirely possible to continue to get paid, year after year, for sub-par work.

On the other hand, if you’re a conscientious programmer, you probably want your gaps to become visible. The best ways that I know to discover them are exercises, reading, and third-person review. I don’t mean simple “code kata,” either, nor do I mean flavor-of-the-month agile manuals, or rubber-stamp “peer review:” study works only when you force yourself out of your comfort zone. If you aren’t being challenged, and struggling to understand, you aren’t building understanding.


Find a site that presents exercise with varying degrees of difficulty and in different categories. You should be able to complete them in an editor of your choice, with a real language; don’t bother with sites that require you to write pseudocode into a web editor. Similarly, avoid excessive gamification. The exercises are the point and should be the focus. Some good sites include:

Try to find problems that are hard for you. Of particular interest are “easy” problems that you find difficult: why are they supposed to be easy? What concept are you missing that would make it easy?

In addition, we’re lucky that practice in our field requires very few materials. When you’re working through a book, do the exercises. Copy the code samples. You may be surprised what sticks with you—it’s been years since I read it, but I still remember one of the first exercises in The C Programming Language: “Write a program to print a Celsius to Fahrenheit conversion table.” That was the exercise that taught me printf.


There’s a genre of computer books that I think of as “extruded technical product.” They are produced to fill a publisher’s spreadsheet. They usually have titles like Mastering the Extremely Specific Software Package Version 18, or Teach Yourself Something Complicated in 24 Hours. These are worthless. Other books try to be timeless, but make a categorical mistake. For example, many of the books with “Patterns” in the name ended up being quickly dated because they were cataloging techniques for dealing with Java’s deficiencies. As other languages became more popular, and even as Java evolved, many of these techniques became unimportant or trivial.

Instead, seek out difficulty and concepts. For example, you aren’t going to be working with a mathematical Turing machine any time soon, but Petzold’s The Annotated Turing is still an excellent book. You won’t be wiring up ICs to build your own computer, either, but his Code is also excellent. You may mostly write web applications, but Distributed Algorithms and Communicating Sequential Processes can still teach you a lot about what can go wrong when things happen almost at the same time. JavaScript may be your forte, but The Little Schemer and Thinking Forth may give you a valuable, and surprising, perspective.

One of my own gaps has always been mathematical concepts. I disliked math in school (thanks to the American educational system), but have focused a lot of my professional reading on books like Lawvere’s Conceptual Mathematics and Knuth’s Concrete Mathematics, to complement more programming-focused books like Sedgwick’s Algorithms.


An honest and skilled reviewer is worth their weight in gold. Someone who can take your code and review it for semantics, not just formatting, is a tool to take advantage of while you can. I’ve noticed that many people view code review as a formality. They will cursorily glance over a twenty line PR and throw :lgtm: in a comment, and Bob’s your uncle.2 You want someone who’s a bit slow, who rejects more PRs than they approve.

The second necessity of code review is open-mindedness, of course. Someone else’s perspective should be valued, not immediately viewed as an attack. If someone says: “I think this is hard to read,” take them at their word, and ask them questions. Why is it hard to read? What do you think would be a better alternative? The discussion is valuable. Never say “I like it the way it is” or “it’s too late to change it now.” The moment you do that, you’ve thrown away the goodwill of the reviewer, because you’ve wasted their time.


To boil it down to an aphorism, find something that feels like genuine effort, and do it. Don’t make excuses, either. It’s easy to say that you want to learn, but you’re “too tired this week.” Read, do exercises, ask questions. Even when you feel like you’ve learned a new concept and filled a gap that doesn’t mean that you’re “done.” Find the next one and fill it!

Learning proceeds until death, and only then does it stop. And so, the order of learning has a stopping point, but its purpose cannot be given up for even a moment. To pursue it is to be human. To give it up is to be a beast. Xunzi.3

  1. I once worked for a company that was in the process of rewriting its primary application from Microsoft Access to Microsoft Silverlight because the CTO thought that it would “be around forever.” I didn’t stay very long. [return]
  2. This always makes me think of Ikiru. [return]
  3. Both quotes are from Xunzi: the Complete Text, translated by Eric L. Hutton. [return]