You're viewing the readable version of this site. The interactive extras (search, diagrams, read-aloud) need JavaScript and a current browser. Enable JavaScript; if it is already enabled, update your browser.

Writing

Notes

Field notes on typography, tools, and the craft of building software.

A running collection of essays and working notes. The list below is generated from the posts themselves, newest first. Many articles are about me documenting what I learn from the designs and implementations of the various systems I work with.

Browse by tag 120

2026

  1. Don't Orchestrate What You Can Derive

    Describe the work as semantic operations, establish the laws they obey, and let an interpreter derive the safe schedules.

  2. The Spreadsheet Always Wins

    The spreadsheet is the requirement. Why the rewrite loses, and what a workbook would have to be so nobody needs two tools.

  3. Your Application Is a Graph

    Dependency injection becomes easier to reason about when wiring is treated as a finite requirements graph: compile what is known, preserve genuine runtime choices, and keep authorization separate.

  4. Code From Strangers

    A map of web security before the deep dives — one asymmetry that explains every attack, five questions the mechanisms answer, and the vocabulary you need so none of the rest reads as noise.

  5. Four Hundred Findings

    A scanner report is not a list of your vulnerabilities. It is a list of your dependencies crossed with a database — and about one in thirty is reachable from code you actually run.

  6. HSTS: It's Time to Go HTTPS-Only

    The header that makes HTTPS non-negotiable — no SSL stripping, no click-through, no fast rollback. Turn it on. Then think carefully before you preload, because that part you cannot take back.

  7. Nobody Shares Your Address Space

    COOP, COEP and CORP are presented as three related headers. They are three doors into one process — and Spectre is why the browser cares who is inside it, not just what they are allowed to read.

  8. One Defect, Three Surfaces

    CORS allowlists, OAuth redirect URIs and open redirects have separate vulnerability literatures and the same bug — a structured identifier tested with a string operation. The fix is one sentence, and a standards body finally wrote it down.

  9. Strings Do Not Remember Where They Came From

    Trusted Types does not make XSS impossible — the spec says so itself. It makes unreviewed assignment impossible, which is a smaller claim and a far more useful one.

  10. The Credential That Predates the Origin

    Cookies do not obey the same-origin policy — they never have. Thirty years of attributes, flags and name prefixes are one long retrofit, and knowing which parts actually hold is most of session security.

  11. The Header That Grants Nothing

    CORS does not protect your server. It relaxes a rule about who may read a reply — and almost every misconfiguration comes from believing it does the opposite.

  12. Two Parsers Walk Into a DOM

    Every HTML sanitizer is a second implementation of a parser it does not control. Mutation XSS is what happens in the gap — and it is why the fix eventually had to move inside the browser.

  13. Why Your Progressive Approach to Web Security Fails

    Adding a security header can only tighten what reaches the browser; nothing you add can loosen it. Loosening is a hand edit of the policy already shipped — and that asymmetry is why incremental hardening degenerates into permissive sprawl, and why deny-by-default with a report-only rollout is the only shape that survives a real application.

  14. Don't Write the Policy. Derive It.

    A security policy is a claim about every case you did not think of. State the invariant instead, enumerate what the system can actually do, and let a model checker hand you the counterexample.

  15. The Good, the Bad, and the Bugly

    When browser bisection almost works—and why stopping can be the right result.

  16. Where Did This Request Come From?

    The Sec-Fetch-Site, -Mode, -Dest and -User headers answer a question servers could never ask — and turn a class of cross-site attacks into a four-line check.

  17. The Checkbox Is a State Machine

    The indeterminate square isn't a third value of a boolean — it's a report the tree makes about itself. Model that one distinction right and the classic stuck-parent bug becomes impossible to write.

  18. How Fast Web Apps Boot: The Architecture of Event-Delegated Shells

    Dissecting the client architecture of event-delegated app shells — local service-worker restoration, BigPipe streaming, and declarative attribute event routing.

  19. The URL Is the Hash

    Content-addressing on the wire — how the web quietly became a content-addressed store, where fingerprinted URLs and Subresource Integrity are real Merkle edges and the cache that looks most like one isn't.

  20. Can I Use This Library?

    A strict CSP quietly turns every dependency into a security decision. Here is the tree I walk to make it — per library, and across a whole app.

  21. The Header That Can't Be Cached

    Cache-Control from first principles — and why a page carrying a CSP nonce must be told never to be stored, not merely "don't cache."

  22. Trust No Script

    Why a strict Content Security Policy is one of the hardest headers to deploy — and how to read one with Google's CSP Evaluator.

  23. You Don't Want Separate Repos

    Repository topology decides whether coupled code shares one content-addressed snapshot or negotiates version strings across a registry boundary.

  24. A Language That Can't Loop Forever

    Bazel's Starlark forbids unbounded loops and recursion on purpose, and gets analyzability, caching, and parallelism in return. Buck2 quietly allows recursion back — and the split shows which restriction is load-bearing.

  25. A Little Uncertainty Buys a Lot of Space

    Bloom filters trade a small chance of being wrong for an enormous saving in memory — a bargain storage engines take and build systems, so far, refuse.

  26. node_modules Is the Heaviest Object in the Universe

    The same "the hash is the identity" idea that powers a build cache also explains why pnpm stores on disk what npm copies a hundred times over.

  27. The Build That Restarts Itself

    Inside Skyframe, Bazel's incremental engine — and the strange trick at its heart.

  28. The Grain of the Machine

    A processor isn't a featureless calculator — it has a shape: words, cache lines, vector lanes. Code that moves with that grain runs many times faster than code that fights it, on the very same data. Here's the shape, and a measured case where the same arithmetic runs 22× slower against it.

  29. The Hash Is the Identity

    Content-addressing in the build cache — content-addressed storage and Merkle trees turn a build cache into a shared resource, so your build is proportional to anyone's change.

  30. The Widest Box Is the Bug

    A flamegraph turns thousands of stack samples into one picture where the slow code is, almost literally, the biggest thing on the screen.

  31. There Are No Phases

    Buck2's DICE engine collapses load, analysis, and execution into a single graph — and the decision to rebuild rather than adopt.

  32. Utils Is Where Modularity Goes to Die

    Module boundaries should follow the dependency graph, not your folder intuitions — and "optimal" can be defined precisely.

  33. With the Grain

    The original turned a big integer into bytes one byte at a time, fighting the machine. The rewrite went with its grain — whole machine words, packed in C. That structural choice is why it's still ~15× faster fifteen years on, even as CPython sped up underneath it.

  34. The Build Is Proportional to the Change

    What every build system is really doing — and the one decision that separates the ones that scale.

  35. Kitchen Sink Series — Part One

    A fixture sibling so the kitchen sink has a real previous part to link to.

  36. Kitchen Sink Series — Part Three

    A fixture sibling so the kitchen sink has a real next part to link to.

  37. Scaffolding a Typographic Portfolio Site

    The design philosophy behind this site — why it's built from durable tools and a purposeful type system instead of a framework, and why every choice is aimed at lasting.

  38. The Patch Bram Applied

    A small contribution to Vim, the man who took it, and the editor I still open every morning.

  39. Designing an API That Outlives You

    I wrote watchdog in 2010. Fifteen years and three maintainers later it still ships the same public API I designed — here's what made it last.

2025

  1. Everything Is an Action

    The architecture of Firebase Genkit rests on one primitive — a self-describing, observable, callable function — and the whole SDK is layers of specializations of it.

  2. Reading a Codebase

    A method for taking real software apart at the source level — find the one type everything hangs from, extract the algebra, and learn what the designers refused to allow.

2009

  1. I like the way Aquamacs looks. I don’t use it because it feels different

    Why I keep stock GNU Emacs over Aquamacs — one consistent editor across every OS.

  2. Even faster String.prototype.trim() implementation in JavaScript

    A faster, non-regex String.prototype.trim() — later adopted into early JS frameworks.

  3. Making the browser download scripts in parallel

    Loading multiple scripts concurrently instead of the browser’s serial default.

  4. memcache.js for Google App Engine application front-ends?

    Caching AJAX responses on the client by mapping App Engine’s datastore to JS objects.

  5. Implementing a Pythonic range() function in JavaScript

    A Python-style range() helper instead of typing out a sequence by hand.