Skip to main content

There Is No Royal Road for Programmers

A first-person essay refined from notes I wrote to share with teammates in a development meeting. There are no shortcuts, but there is a repeatable path.

The story goes that when a king asked Euclid whether there was an easier shortcut to learning geometry, he replied, "There is no royal road to geometry." Setting aside the historical accuracy of the anecdote, I want to borrow the sentence directly for programming. There is no royal road for programmers. This is a record of how I teach colleagues and train myself.

How to read this article

Sentences marked "in my experience" describe personal heuristics verified only for me. Even the unmarked generalizations are widely shared observations, not laws proven for everyone. Read the two accordingly: test each heuristic in your own environment, keep it if it works, and discard it if it does not.

Teach direction and invariants, not procedures

There is a simple reason I show teammates the direction without prescribing the process. Problems in real work rarely arrive with a multiple-choice answer saying "there is one solution." Someone who has memorized a fixed procedure can go only as far as the problems that procedure solves. The moment it stops working, a person who does not understand its rationale comes to a halt; a person who understands the rationale redesigns the procedure.

Instead of making people memorize a sequence, I therefore reinforce a few invariants: "validate external input at the boundary," "concentrate state changes in one place," and "surface failures instead of swallowing them." Because these statements remain valid as languages and frameworks change, they survive as decision criteria after the tools are replaced. There is no answer sheet that can replace the sequence of exploring a solution yourself, running into obstacles along the way, and growing through those collisions.

I add one value judgment. This is my belief, not a universal fact. I believe that repeatedly writing simple implementations is the path to becoming a coder, while continually searching for a better way is a virtue a programmer should possess. Not fixing yourself to the teacher or school of thought you learned from—respecting the source of your learning without becoming trapped inside it—is another way of expressing this belief.

Time spent wrestling with errors lasts the longest

Solve errors yourself. This is the core of my experience. An error someone else fixes does not stay in my memory. A problem another person solves at my desk in thirty minutes returns a week later wearing the same face. By contrast, an error I catch myself after searching logs and stack traces until midnight becomes recognizable from similar symptoms even years later. This is only my experience; it does not mean struggling alone is best in every situation.

In a production incident, learning must not delay recovery

The original note said, "If you are working on a company project, preserve the moment the error occurs and solve it in your spare time." The intent is still sound, but production requires a stricter order. Recovery comes before learning. Becoming absorbed in root-cause analysis while a service is down does not promote learning; it expands the damage. Take the fastest proven recovery path first—rollback, restart, hotfix—and postpone the learning until afterward.

Before recovery, however, preserve evidence where possible. Keep error logs, stack traces, the deployed version, and the reproduction path, but do not copy entire requests verbatim. Collect only allowlisted fields, and mask or exclude authentication tokens, session IDs, passwords, and personal information. Apply least privilege, access auditing, and a short retention period to the evidence store as well. Do not collect values that cannot be stored safely. Once the service is stable—and only then—open the snapshot and run the learning loop below. Learning is deferred, not abandoned.

Read a lot of other people's code

The third principle is to read a lot of code written by other people. It is the same reason people who read extensively write well and develop richer vocabularies. Looking only at my own code fixes my way of thinking in place—this, too, is my experience. The moment it starts to feel as though the patterns I know are all the patterns in the world is a warning sign. I read teammates' code, the internal implementations of libraries I use every day, and even language standard libraries, always asking, "Why was it done this way?" I use the same list of questions in code reviews and while reading open source.

  • Can I state the problem this code is trying to solve in one sentence?
  • Where are the inputs, outputs, and invariants guaranteed?
  • Is the failure path as explicit as the success path—is any error swallowed?
  • Do the names reveal intent, or are comments making excuses on behalf of the names?
  • Can I infer why this pattern was chosen in this context—why this rather than an alternative?
  • Do the tests describe expected behavior rather than code internals?
  • If I took one thing from this code and used it in mine, what would it be?

Always ask "why?" and compare alternatives

The final principle is to be critical. I know that attaching "why?" to every decision can be painful for the listener. But the purpose of the question is not to attack the other person. It is to think once more and check whether the answer is truly right and whether there really are no alternatives. The structure in Applying Agile 0x0002, where a planner explains Why and What and a developer responds with How and When, is an example of institutionalizing the same question at the organizational level. The notes I use to review papers and presentations likewise attach "why was this used?" to every component and place strengths and weaknesses side by side.

Criticism needs a form if it is to lead to practical action. When making a design decision, I place at least two alternatives next to each other and record why the unchosen option was rejected. A choice without a comparison of alternatives is not a choice; it is inertia.

My learning loop

The principles above settle into practice in one form. I pass every problem I encounter—an error, unfamiliar code, or an incident—through these six steps.

  1. Problem definition — Do not stop after transcribing the error message. Write the difference between "expected behavior" and "actual behavior" in one sentence. If the problem cannot be captured in a sentence, you do not understand it yet.
  2. Hypothesis — Write not "it seems to be X," but "if it is X, then Y should be observed." Only a falsifiable statement is a hypothesis.
  3. Smallest experiment — Choose the smallest single action capable of killing the hypothesis: add one log line, create one reproduction case, or comment things out by repeatedly halving the search space. Do not change two things at once.
  4. Evidence — Record what you observe as it is, not what you expected. Do not erase observations that contradict expectations. They are seeds for the next hypothesis.
  5. Explanation — You are done only when you can explain causally why it happened. "It is fixed" and "I understand it" are different statements.
  6. Reusable note — Leave it in a form that your future self, or a colleague who knows nothing about it, can find and use with one search. Follow this site's blog editorial guide for the format of the note.

This loop is not a new invention. It scales the "measure → modify → measure" iteration described in Applying Agile 0x0001, and the scientific method's hypothesis-testing procedure, down to everyday size. What matters is not the loop's existence but its repeatability. If yesterday's loop and today's loop have different forms, it is improvisation rather than a loop.

Three common traps

Here are three traps that anyone, myself included, tends to fall into more often as they apply these principles.

  1. Cargo cult. Copying a pattern wholesale from a famous codebase or technical blog without the context of the problem it solved. Bring a pattern together with the problem it killed. A pattern placed where there is no such problem is decoration rather than a solution, and decoration becomes a comprehension cost for the next reader.
  2. Endless solo struggle. The most common distortion of "solve errors yourself." Struggle can indeed be learning time, but struggle without progress needs an upper bound. By my standard—and this is my heuristic, not a universal standard—if the set of hypotheses has not narrowed after several hours, I organize the evidence observed and experiments attempted so far and turn them into a question. Refining a good question is itself learning, and much faster than simply enduring.
  3. Performative criticism. Using "why?" as a weapon for visibility rather than a validation tool. Criticism without an alternative is not criticism. When criticizing, bring one alternative and the cost that alternative incurs. I apply this rule to myself as well as to others.

Weekly practice checklist

Principles become slogans unless they are checked periodically. These are the five questions I ask myself every week. The value of retrospection itself is also well documented in the retrospective notes in Applying Agile 0x0002.

  • Did I solve one error all the way through myself this week and record the process as a learning-loop note?
  • Did I read code I did not write and record one pattern to adopt and one to discard?
  • Did I ask "why?" about one design or implementation decision and compare it beside an alternative?
  • Did I explain direction and invariants to someone instead of prescribing a fixed procedure?
  • Did at least one of this week's commits make something simpler?

Closing thoughts

There is no royal road. But that does not mean there is no path at all. Show the direction but let people take the steps themselves; let them wrestle with errors; make them read other people's code; never let them stop asking "why?" These four are everything I know about a path that is not a shortcut, but does not break. These statements, too, are heuristics born from my experience. The reader need only test them in their own environment, keep what works, and discard what does not. Even that act of discarding will be part of the loop described in this essay.