QIAOPANG.COM · SELECTED ESSAYS (2013–2026) · 260 ESSAYSBrowse Chronologically (2013 → 2026) ↗
QiaoPang · Programmers LifeSelected Essays · Systems, Craft & Agentic Era
Timeline ↗

Building Your Own LEGO Bricks

Building Your Own LEGO Bricks

The swan is white though it never bathes; the crow is black though it never dyes itself. — Zhuangzi

The line above is, in a sense, not quite right. Human nature — which is also an instinct every living thing has evolved — leans toward benefit and away from harm, and without some external force applied, it slides very easily toward the passive side. Take reading, for instance. When we were small, our parents’ paddles and lollipops, our teachers’ pointers and little red flowers — all of them cajoled and coerced us from reciting “Ode to the Goose” all the way to “From the Hundred-Plant Garden to the Three-Flavour Study” (left to our own devices, we’d have gone from Fūma no Kojirō straight to Zhu Xian). Now, after a day’s work, worn down to the bone, I recall that line — “The contented man does not burden himself with profit; the man secure in himself does not fear loss; the man who cultivates within feels no shame without position” — and find my own quiet contentment. On a weekend hike, what my heart hopes for is “if the spring grass has feeling, the hills still keep their green”; and should I happen to pass a lotus pond, I will inevitably murmur that passage: “Over the winding, twisting surface of the pond, what fills the eye are the field-like leaves. The leaves rise high above the water, like the skirts of dancing girls standing tall…”

By the time we are adults, when we pick up the pen to write something, the lines that surface most vividly are almost all those golden sentences we memorized as children — just as Linghu Chong, having learned the Dugu Nine Swords, casually unleashes a stroke and it is “The Phoenix Comes to Perch”; another stroke, and still “The Phoenix Comes to Perch.”

Of course, if you want to write seriously, memory alone isn’t enough — your desk (or your computer) needs a long-accumulated pile of reading notes. These notes are scattered in the margins of book after book, or lodged in note after note of Evernote. They are like Dumbledore’s Pensieve: when you need it, it drops you quickly back into your own context — rather than making you dig and rack your brains for some allusion or passage you once read, that might apply right now, but that you can’t quite recall.

As far as writing goes, if the memory in your head is the L1 cache, then these desk notes are the L2 cache, and the books you’ve read are main memory. The material they weave together is like so many LEGO bricks, which — under the direction of your brain (your thought) — assemble into essay after essay.

Writing code and writing prose are in fact very similar. Both are a kind of morphism: f: A -> B — that is, kneading a pile of raw material into the work we want. So can we learn something useful for writing code from the process of practicing writing from childhood onward?

Absolutely!

First, naturally, is to build our own L1 cache — the poetry and prose, no, the code, that we can produce at will.

An-jie (WeChat account: Dididada) wrote an article that seemed to mention that all code under heaven begins with copying (I don’t remember the exact words). When we start writing code, it’s less writing than copying. Copying is an extremely beneficial act: if reading a book goes through the mind, then copying a book goes through the heart. Copying code is the same. In my early years, in order to understand the handling of page fault exceptions, I once typed out that code line by line. Because that code is obscure — even once I understood the principle of page fault handling, reading the source over and over still felt dreadfully confusing (perhaps my comprehension is just poor). But in the course of transcribing it line by line, my thinking began to be led along by the author; those abstract VMAs, that obscure determination of a bad address, that baffling stack grow — all of them seemed to open their arms and welcome me, the uninvited guest.

Beyond deepening comprehension, copying also lets us forcibly apply an algorithm or formula we don’t understand in order to arrive at a solution. In college I couldn’t really grasp those abstract, awkward things in calculus — Taylor expansions, say, or the Lagrange mean value theorem — but that never stopped me from plugging in the relevant formula when I needed an answer. The same goes for many of the algorithms we meet in software development. Don’t understand dynamic programming? Doesn’t matter — copy until your hand aches, and when a problem comes along, know how to fit the template on. Once you’ve copied deeply enough and used it often enough, you’ll gradually understand. Hmm, at this point I won’t trot out Zhang Wuji on Ice Fire Island as an example again.

Unfortunately, our generation only “truly” started writing code as adults; our parents and teachers could no longer lift the switch, and little red flowers and lollipops had lost their power, so we can rely only on our own effort — and on the struggle between our longing for a better life and that instinct to seek benefit and avoid harm.

While you’re furiously filling your L1 cache, don’t give up on filling the L2. As I said, the L2 cache for writing prose is the reading notes you accumulate day by day; the L2 cache for writing code is the various snippets (gists), boilerplate, control blocks, and utility belts (libraries) you lay down by reading other people’s code and all kinds of papers.

Let’s take them one by one.

A snippet is the most typical kind of code-reading note. When you read good code, don’t hesitate — copy the whole passage down (copy & paste is fast, but bad for memory). Where to put snippets is a pleasant sorrow — Evernote, for all its skill at excerpting and searching text, is not the best tool for code. I myself have used Sublime (for entry and search) + git (for persistence) to manage my snippets, recording them by language category and periodically pushing them to my repo on AWS. It’s passable, but still far from my idea of the perfect snippet manager. Another effective tool is GitHub Gist, but I don’t use it much.

Boilerplate is the great killer weapon of the efficient programmer. In our daily work we should consciously organize all kinds of boilerplate code — say, react-redux-webpack boilerplate, electron boilerplate, or the boilerplate for a more complex phoenix + react/redux + react native project. Most frameworks have decent-quality boilerplate to be found on GitHub, and standing on the shoulders of giants always feels good; but sometimes you’re locked inside a company’s existing architecture and can’t use open-source software — then you have to consider consciously building your own boilerplate to avoid repeated labor.

Control blocks are a very personal thing; they only work for a specific language. When I write code I like to extract some basic patterns. For instance, chaining is a very comfortable way of expressing things: Ruby has object chaining, JavaScript has prototype chaining and promise chaining, Elixir and Clojure have pipe. But in the course of chaining, you can’t get around these data types: maybe (option) and exception. If somewhere along a chain a function gets a null (nothing) result, or throws an exception, the whole chain becomes very uncomfortable: either every function on the chain has to deal with these annoying things, or the chain stops halfway, handles the exception, and then continues — and the beauty of the chain is destroyed. Promise offers a very elegant solution; it is what Scott Wlaschin calls ROP (Railway Oriented Programming) implemented in JavaScript. Since promise wraps asynchronous computation (for more, see: ), and synchronous computation is a special case of asynchronous computation, promise applies to almost every occasion in JavaScript that needs chaining, and handles both maybe and exception perfectly. What if your language has no such mechanism? You can look for open-source alternatives (e.g. ReactiveX, supported in many languages), or build your own control block — in Elixir, for instance, you can use a macro to build, atop the not-so-robust pipe (|>, itself a macro), a super pipe that supports maybe and exception.

The benefit of having your own handy control blocks is that you no longer have to weigh the pros and cons of some approach while writing code — the control blocks on your list already represent the highest level of your coding ability (and your most fluent way of working) at this stage. Just use them directly.

Finally, the utility belt (library). The utility belt covers a wide area. In JavaScript, the better-known utility belts are lodash and ramda. These basic components form the foundation of your ability to write code. Essentially most algorithms are covered by some open-source utility belt; often you simply don’t know they exist.

For example, Elixir has tucked in an easter egg: it quietly placed this string-correlation function in its standard library (as far as I know, no other language has added this to its string library):

iex(1)> String.jaro_distance("tyr","try")
0.5555555555555555

(Actually jaro_distance isn’t really an easter egg — probably because it’s so widely used in CLIs, it was built in directly.)

A similar string-similarity analysis exists in Clojure as clj-fuzzy. The best part is that clj-fuzzy supports ClojureScript, which means it can be compiled into JavaScript for front-end use.

> fuzzy = require('clj-fuzzy');
{ phonetics:
   { metaphone: [Function],
     double_metaphone: [Function],
     soundex: [Function],
     nysiis: { [Function: yj] b: [Function], a: [Function], v: 2 },
     caverphone: { [Function: bj] b: [Function], a: [Function], v: 2 },
     mra_codex: [Function: sj],
     cologne: [Function] },
  stemmers:
   { lancaster: [Function],
     lovins: [Function],
     porter: [Function],
     schinke: [Function] },
  metrics:
   { levenshtein: [Function],
     dice: [Function: mk],
     sorensen: [Function: mk],
     mra_comparison: [Function],
     jaccard: [Function: ck],
     tanimoto: [Function: ck],
     hamming: [Function],
     jaro: [Function: lk],
     jaro_winkler: [Function],
     tversky: { [Function: c] v: 0, A: [Function], m: [Function: d] } },
  clj_fuzzy: [Circular] }

Rich beyond belief. This reflects from another angle just how important it is to be a polyglot: good stuff from Clojure suddenly becomes usable in JavaScript too — practically a cheat code.

Beyond accumulating various utility belts day to day, building your own personal utility belt matters too. Some things you use often, and calling them always satisfies a certain pattern — just write your own library to wrap them. Over time, that library (or libraries) becomes your secret weapon for making a name in the world.

After Boss Zhu raised his banner, he laid down the strategic course of “build high walls, store abundant grain, and delay claiming the throne.” We programmers, in our daily work, should know to do the homework above — to “build high walls and store abundant grain” — and polish our LEGO bricks one by one.

Most importantly: once you have the LEGO bricks, can LEGOLAND be far behind?

MORE IN Languages & Craft

Further Reading in This Column

View all essays in “Languages & Craft” →

Time to Meet jj: Git's Next Evolutionary Step

Git has ruled version control for twenty years, but its staging area, brittle rebases, and blocking merge conflicts are becoming liabilities in the AI era. jj rebuilds the mental model from the ground up — while staying fully Git-compatible. Here's why it deserves your attention.

New Hope (2025–2026)5,588 chars · 12 min read