System preferences

Back

SECTOR 02On air

Papers

Papers on JavaScript, TypeScript, the web platform and the occasional politics of software. Not your usual blog: dense over long, niche over SEO chum. Words are my own. It's dangerous to go unknowing, take some pills 💊

P20226 FILES

LATEST TRANSMISSION · JUL 26

Bazaar: adversarial asset direction

The bazaar is not an art project, and this paper holds no prompt tricks. The bazaar is this site's index: a pixel-art night market, ten stalls, one route per stall, a talking keeper in each. gpt-image-class models generated every pixel. Codex CLI drove the jobs, Claude supervised the fleets, and I directed by eye. This paper is the engineering method.

  1. P203

    The landlords won

    · JUL 26

    On the morning of August 9, 1995, a sixteen-month-old company with no profits sold itself to the public. Netscape's bankers had priced the stock at $28. It touched $75 by the afternoon. Jim Clark, its co-founder, became the first internet paper-billionaire before the market closed. Somewhere in the building a browser engineer realized the product had become a footnote to its own ticker. Nothing about the software changed that day. Everything about the Valley did.

  2. P204

    The bridge is dead, long live JSI

    · MAY 26

    React Native's old architecture is dead. Since 0.82 no app can run on the bridge, because React Native ignores newArchEnabled=false. 0.85 deleted CatalystInstanceImpl, the Android bridge's core class. The interop layer for legacy modules stays "for the foreseeable future". The obvious reading is that the bridge was slow. Meta's own numbers refuse it. The new architecture rendered 5.000 views on an iPhone 12 Pro in 266ms, and the old one took 435ms. On fast Android hardware the gains were single-digit. Throughput was never the prize.

  3. P205

    'use cache' killed ISR

    · FEB 26

    Before, one line at the top of a route: export const revalidate = 60. After, one line inside the function that owns the data: cacheLife('minutes'). The obvious reading is a rename. It is not. ISR gave a route with five data sources exactly one freshness contract. Product prices want 60 seconds, the nav menu wants a week, markdown wants forever, and the page has one dial. So we split routes artificially, or we over-revalidated everything, and on-demand revalidatePath was a hammer shaped like a URL. The route was never the natural boundary of freshness. It was the boundary Next could see.

  4. P206

    Nobody trains the juniors anymore

    · OCT 25

    Cutting junior hiring is the correct decision for almost every company that made it. The popular read blames short-sighted CFOs. That read is wrong, and it is comfortable, so it circulates. Every one of those decisions clears its own business case. The damage lands somewhere the business case never looks. Seniors are not found. Seniors are trained, and the training was always paid for in grunt work. Every engineering leader I talk to says the same two sentences, usually in one breath. "We only hire seniors now." And: "it's so hard to find seniors." Nobody laughs. The joke is structural, so it does not register as one.

  5. P207

    The server is just a cache

    · AUG 25

    Why is Linear fast? Fast like local software, on a network product, with multiplayer. The obvious answer is good engineering. It is wrong. Linear is fast because of one inversion, and the details are public. Tuomas Artman's talks lay out the engine. An obsessive reverse-engineering repo maps it, and Artman calls it "probably the best documentation that exists". The client owns a replica, and the server only orders and reconciles. The industry named the family: sync engines. The server is becoming a cache.

  6. P208

    MCP is CGI for agents

    · APR 25

    MCP is CGI for agents. Same genius. Same sins, already arriving. The genius starts in 1993, at the NCSA in Illinois. The web is static files, and Rob McCool needs it not to be. His answer becomes the Common Gateway Interface, later frozen as RFC 3875. Drop an executable in cgi-bin/. The server dumps the request into environment variables (QUERY_STRING, REMOTE_ADDR, REQUEST_METHOD, §4 lists them all). The program prints to stdout. That is the response. Any language, any binary, zero ceremony. The dynamic web was born as a folder of scripts talking through pipes.

  7. P209

    Tailwind 4 speaks CSS at last

    · JAN 25

    A complete Tailwind 4.0 theme, in CSS. No tailwind.config.js. No module.exports. No theme.extend nesting. No dev-server restart, because the config is JavaScript and JavaScript is cached. Tailwind 4.0 shipped this week and the config file died with it. I am here to celebrate at the funeral. For eight years Tailwind was a JavaScript program that emitted CSS. Version 4 is CSS. The content glob array is gone too. Every monorepo got that one wrong once a quarter. Template detection is automatic now, and it respects .gitignore.

  8. P210

    Markets don't lie, they leak

    · OCT 24

    Three weeks out from the US election, the polls report a coin flip. Polymarket reports 60-40 for Trump. The polls have said 50-50 for months, always inside the margin of error. Polymarket carries hundreds of millions in cumulative volume behind the drift. So the discourse picked its fighter: "a whale is manipulating the odds". Maybe. That is the wrong question. The right question is what each number measures.

  9. P211

    The compiler will remember for you

    · JUN 24

    The React Compiler is a build step that memoizes your components for you. Every useMemo, every useCallback, every memo() wrapper you have sprinkled across five years of components just became tech debt in waiting. The compiler is open source on the experimental channel, and it already runs in production on instagram.com and the Quest Store.

  10. P212

    The maintainer was the exploit

    · APR 24

    The xz backdoor starts here, inside a file nobody reads. Andres Freund noticed his SSH logins burned ~500ms of CPU they should not, and Valgrind threw weird errors in liblzma. He profiled half a second. That is the kind of Postgres developer he is. What he found was a backdoor in xz/liblzma wired into sshd. It had shipped to Debian sid, Fedora 40/41 beta and openSUSE Tumbleweed, weeks from stable releases everywhere.

  11. P213

    View transitions without the framework

    · DEC 23

    I gave hours to SPA router animation libraries. Three years ago I published 60fps scroll animations too, and the trick there was staying out of the browser's way. Today's trick is stronger, and it costs one function call. The View Transitions API turns any DOM change into an animated crossfade, morph included. **The browser animates for you.** No framework, no FLIP math, no getBoundingClientRect bookkeeping. It shipped in Chrome 111 this March. Jake Archibald and Khushal Sagar wrote the explainer, and it is the source of everything below.

  12. P214

    Bun ate the toolchain

    · SEP 23

    One Node service of mine carried eleven dev dependencies. Bun deletes nine of them. Speed is the wrong reason to look. Bun 1.0 shipped last week and every benchmark chart is the same shape: tall green bar, short gray bar. Forget the charts. Bun's actual pitch is deletion.

  13. P215

    Worklets: the other JavaScript runtime

    · MAY 23

    I park a while(true) on the JavaScript thread of a React Native app. The thread is dead. I wrote the animation in JavaScript. The Reanimated gesture keeps tracking my finger at 60fps. The obvious read: the animation left JavaScript for native code. It did not. Your app runs two JavaScript runtimes, and the second one lives on the UI thread.

  14. P216

    The casino is the ladder

    · FEB 23

    On November 11 FTX filed Chapter 11. Within a day the commentary settled on its favorite conclusion: the gamblers were idiots. Millions of young people shovel their savings into tokens, options and parlays. The serious people shake their heads. Financial illiteracy, they say. Someone should teach these kids about compound interest. Financial illiteracy is the wrong diagnosis. Run the compound interest lesson on the official numbers.

  15. P217

    app/ is a rewrite, not an upgrade

    · NOV 22

    The app/ directory is not a folder rename with layouts. Next 13 landed two weeks ago and shipped it in beta. app/ inverts the data model of your application, and porting pages/ file by file is the fastest way to hate it.

  16. P218

    The :has() heist

    · JUL 22

    jQuery shipped :has() in 1.1.4, back in 2007. The browser shipped it this March. The obvious reading of that gap is neglect. The real answer is the direction of style invalidation, and Dave Hyatt named it in 2008. Shaun Inman proposed qualified selectors that year and Hyatt shot the proposal down on dynamics: "just not functioning in dynamic contexts... or on particularly large sample sets". Firefox has bug 418039 open since 2008. Snook declared it impossible-ish in 2010. Fourteen years of "too slow".

  17. P219

    Your effect runs twice on purpose

    · APR 22

    You upgraded to React 18. Your effects fire twice. Your fetch runs twice, your analytics event doubles, your WebSocket connects twice. The folklore says "React mounts, unmounts, and mounts again". Close, and wrong in the way that matters. React does not remount anything. The double-invoke is documented, it is dev-only, and it is a test you are failing.

  18. P220

    colors.js and the trust budget

    · JAN 22

    On January 8 at 04:24 UTC the maintainer of colors.js published 1.4.44-liberty-2. At 19:22 he published 1.4.1. On January 9, 1.4.2. The first one got the coverage. The last two burned the ecosystem. The story everyone told was 1.4.44-liberty-2. It shipped as a prerelease, and prereleases never match caret ranges, so it only hit people installing latest. The ecosystem-wide burn came from 1.4.1 and 1.4.2, legitimate-looking patches inside ^1.4.0, the range half the registry declares.

  19. P221

    The dual package hazard

    · OCT 21

    The console says Cannot use GraphQLObjectType "Query" from another module or realm. There is one realm. There is one process. Node loaded graphql-js twice. The library did nothing careless. It ships both formats through conditional exports, and that responsible-looking shape is what splits it. Node runs two loaders with two separate caches.

  20. P222

    content-visibility: the lazy render

    · JUN 21

    Ship content-visibility: auto to a Chrome 89 user and every heading below the fold leaves the accessibility tree. The screen reader gets a page that stops at the viewport. Chrome 90 closed the hole. The property is worth the version floor anyway, because it is the cheapest rendering win of 2021. The name sells the wrong model. content-visibility reads like a visibility switch, so people reach for it as a politer display: none. It is containment machinery. The property did not start as CSS at all.

  21. P223

    The browser lifecycle

    · MAR 21

    Three seconds. Past that mark, 53% of mobile visits are abandoned. That is Google's number, and thousands of developers fight it daily. The reflex is to ship less JavaScript. The reflex has no target. Every speed fix lands on one step of one lifecycle, and you cannot optimize a step you cannot name. So name them first.

  22. P224

    On TypeScript discriminated unions

    · FEB 21

    You typed a response as a union. Then you read a field off it and the compiler stopped you. The union is not wrong. The narrowing is missing. A union declares what a value could be. Discrimination decides what it is right now. Multi-type entities show up every day. API responses adopt several shapes. Collections carry several types. Parsed user input collapses to different scalars. Unions describe all of them. For primitives TypeScript does the work alone. A plain if-else narrows the value and the inference follows.

  23. P225

    Scroll-based 60 fps animations

    · AUG 20

    Every scroll animation you have admired this year is a flipbook. The scrollbar is the thumb. John Barnes Linnett patented that in 1868 as the kineograph. Apple's AirPods Pro page ships it today at 2560x1440, and someone rebuilt it in a pen. Every frame already drawn, every frame already there, and the reader can stop on frame 41 and stare at it. The obvious way to build it is an <img />, a scroll listener, and some offset arithmetic. Wrong, and clunky. Linnett's toy works for three reasons, and each one maps to a rule:

  24. P226

    Web-safe fonts

    · JUL 20

    In 1985 Apple shipped the LaserWriter. Your font stack still pays for it. Adobe licensed Apple 13 fonts for that machine, and three of them sit in the CSS at the bottom of this page. Each comma in a font stack sits between an original and its clone. The easy story blames the browsers. The real one starts at a printer and ends in a bankruptcy. In 1984 HP shipped the HP LaserJet. It was the first laser printer built for mass-market sales. One year later Apple answered with help from Adobe. The LaserWriter carried a built-in PostScript interpreter. With an editor program, anyone could create, publish and distribute their own content. That is the Desktop Publishing revolution.

  25. P227

    GaaS: GitHub as a Service

    · MAY 20

    Stop paying for a backend. You already have one, you have had it for years, and it has never sent you a bill. GitHub is a cloud provider that forgot to publish a pricing page. Since Microsoft bought it in 2018 it shipped Actions, in-house CI/CD, a marketplace, and npm. Everything a web application needs, behind an API you already hold a token for. Call it GaaS. GitHub as a Service. GaaS, Gaas, Gaas!

↑↓ / J K SELECT · ENTER READTEXT SERVICE / CH.02