lukeed

Small on purpose

Name

small-on-purpose — small on purpose

Synopsis

Ten years of writing 200-byte libraries, and why I still think the size limit is a design tool, not a party trick.

Filed

· 1,143 words · 9 min

Description

I learned to program by buying WordPress themes and breaking them.

There was no school involved. There was a credit card, a folder of PHP files I did not understand, and a lot of evenings spent deleting lines to see what would stop working. It was slow and it was expensive, and I thought it was the only way in.

Then I found GitHub, and the price of reading someone else's code dropped to zero.

I want to be precise about what changed, because it was not access to software. Free software already existed. What changed was access to readable software. For the first time, I could open a project, find the one file that did the thing I cared about, and read it top to bottom in one sitting. Most of what I know, I learned that way: one small file at a time, written by someone I would never meet.

Ten years later, most of what I publish is small files. I do not think that is a coincidence.

#The byte count is a forcing function

People ask why clsx is 239 bytes. The honest answer is that it is 239 bytes because I kept asking whether each byte had earned its place, and eventually the answer was always yes.

That sounds like golf, and some of it is. But the useful part of a size budget is not the number. It is the question. When the whole library has to fit in a few hundred bytes, you cannot add an option because someone might want it. You cannot keep a branch because it was there yesterday. Every feature has to argue with every other feature for space, and the arguments are where the design happens.

A large library can have opinions it never examined. A tiny one cannot hide anything.

The best constraint I ever adopted was a number in a README. It changed how I write code more than any style guide, review, or book.

#Small is not the same as clever

There is a failure mode here, and I have fallen into it. You can make a library small by making it unreadable: one-letter variables, chained ternaries, bit tricks that save four bytes and cost every future reader ten minutes.

I try to keep the source readable and let the minifier do the ugly part. If a byte only goes away when the code becomes a puzzle, I keep the byte. The goal was always the thing I had when I first found GitHub: a file a stranger can read in one sitting and understand. Size is a proxy for that. When the proxy and the goal disagree, the goal wins.

If you cannot hold the whole library in your head, neither can the next person who has to fix it.

That sentence has outlived every benchmark I have published.

#Speed is a side effect

Small libraries tend to be fast. That is not because I am good at performance work. It is because they do less.

dequal is fast at deep equality largely because it refuses to handle cases most people never hit. uvu runs tests quickly because there is very little test runner in between your code and the assertions. polka was fast because it was mostly a router with an HTTP server attached, and routers are not where your request time goes.

Every time I have tried to make something faster by adding a clever path, I have ended up with something larger and only slightly faster. Every time I have made something faster by deleting a feature, it has stayed fast.

#The maintenance math

Here is the part I did not understand at twenty-five.

A library is not done when you publish it. It is done when nobody depends on it anymore, which, for some of mine, will be never. Some of my packages are dependencies of dependencies of projects I will never see. They get installed hundreds of millions of times a month by people who do not know my name, and that is exactly how it should be.

Every line in those packages is a line I am responsible for, forever. Every option is a combination I have to keep working. Every dependency is someone else's maintenance schedule bolted onto mine.

When I count it that way, small stops being a style and starts being arithmetic. A 200-byte library with zero dependencies has a maintenance cost I can actually pay, for decades, in my spare time. A 20-kilobyte library with a dependency tree does not.

#When I break my own rule

Not everything I write is small. sirv is not tiny. bongo, the static site generator this page is built with, is not tiny. lane is a Rust binary with real dependencies.

The rule was never "always be small." It was "be as small as the problem allows, and know why you are not smaller." For a test runner, the problem is small. For a static site generator, it is not. Pretending otherwise just moves the complexity into somebody else's config file.

The discipline transfers, though. When I build something large, I still try to build it out of pieces that would survive on their own: a router you could lift out, a parser with no idea it lives inside a framework. Large software made of small parts is much easier to maintain than large software made of large parts.

#Writing a client instead of a polyfill

A small story about how this plays out in practice.

Late in 2024, I needed a database client inside a runtime that did not support CommonJS. The official client worked fine, but somewhere in its dependency tree something reached for require and module. I spent an afternoon trying to transpile and polyfill my way around it.

Then I read the protocol spec, and it was short. Writing a new client, one that spoke the protocol directly, was easier than making the old one load. It came out at about a kilobyte, it runs anywhere fetch does, and it has a scheduled CI job that checks the upstream spec for changes.

I do not tell that story because writing your own client is usually right. It is usually wrong. I tell it because the reason it was right is the same reason as everything above: the problem was small, and the existing solution was not.

#What I would tell myself

Read more code than you write. Pick a constraint that forces a question, and keep asking it. Make the source readable even when nobody is checking. Count the maintenance before you count the stars. And when you find a file that teaches you something, remember that someone chose to make it that way.

Somebody's small, readable file taught me to program. I am still trying to pay that back, a few hundred bytes at a time.

See also

open-source(7), essays(7), javascript(7), lukeed(1)