Reading List
The most recent articles from a list of feeds I subscribe to.
First day of summer vacation: summer, sun, and e. coli
Today is the first official day of my summer vacation. I’m sitting in Leijon, drinking a morning coffee can I write this. It’s been a pretty crazy 7 months. I haven’t written much lately - first out of diverted focus, later out of stubbornness.
First day of summer vacation: summer, sun, and e. coli
Today is the first official day of my summer vacation. I’m sitting in Leijon, drinking a morning coffee can I write this. It’s been a pretty crazy 7 months. I haven’t written much lately - first out of diverted focus, later out of stubbornness.
Occasionally injected clocks in Postgres
Conferences, Clarity, and Smokescreens
Before saying anything else, I'd like to thank the organisers of JSNation for inviting me to speak in Amsterdam. I particularly appreciate the folks who were brave enough to disagree at the Q&A sessions afterwards. Engaged debate about problems we can see and evidence we can measure makes our work better.
The conference venue was lovely, and speakers were more than well looked after. Many of the JSNation talks were of exactly the sort I'd hope to see as our discipline belatedly confronts a lost decade, particularly Jeremias Menichelli's lighting talk. It masterfully outlined how many of the hacks we have become accustomed to are no longer needed, even in the worst contemporary engines. view-source:// on the demo site he made to see what I mean.
Vinicius Dallacqua's talk on LoAF was on-point, and the full JSNation line-up included knowledgeable and wise folks, including Jo, Charlie, Thomas, Barry, Nico, and Eva. There was also a strong set of accessibility talks from presenters I'm less familiar with, but whose topics were timely and went deeper than the surface. They even let me present a spicier topic than I think they might have been initially comfortable with.
All-in-all, JSNation was a lovely time, in good company, with a strong bent toward doing a great job for users. Recommended.
Day 21 — React Summit 2025 — could not have been more different. While I was in a parallel framework authors meeting for much of the morning,2 I did attend talks in the afternoon, studied the schedule, and went back through many more after the fact on the stream. Aside from Xuan Huang's talk on Lynx and Luca Mezzalira's talk on architecture, there was little in the program that challenged frameworkist dogma, and much that played to it.
This matters because conferences succeed by foregrounding the hot topics within a community. Agendas are curated to reflect the tides of debate in the zeitgeist, and can be read as a map of the narratives a community's leaders wish to see debated. My day-to-day consulting work, along with high-visibility industry data, shows that the React community is mired in a deep, measurable quality crisis. But attendees of React Summit who didn't already know wouldn't hear about it.
Near as I can tell, the schedule of React Summit mirrors the content of other recent and pending React conferences (1, 2, 3, 4, 5, 6) in that these are not engineering conferences; they are marketing events.
How can we tell the difference? The short answer is also a question: "who are we building for?"
The longer form requires distinguishing between occupations and professions.
Occupational Hazards
In a 1912 commencement address, the great American jurist and antitrust reformer Louis Brandeis hoped that a different occupation — business management — would aspire to service:
The peculiar characteristics of a profession as distinguished from other occupations, I take to be these:
First. A profession is an occupation for which the necessary preliminary training is intellectual in character, involving knowledge and to some extent learning, as distinguished from mere skill.
Second. It is an occupation which is pursued largely for others and not merely for one's self.
Third. It is an occupation in which the amount of financial return is not the accepted measure of success.
In the same talk, Brandeis named engineering a discipline already worthy of a professional distinction. Most software development can't share the benefit of the nominative doubt, no matter how often "engineer" appears on CVs and business cards. If React Summit and Co. are anything to go by, frontend is mired in the same ethical tar that causes Wharton, Kellogg, and Stanford grads to experience midlife crises.3
It may seem slanderous to compare React conference agendas to MBA curricula, but if anything it's letting the lemon vendors off too easily. Conferences crystallise consensus about which problems matter, and React Summit succeeded in projecting a clear perspective — namely that it's time to party like it's 2013.
A patient waking from a decade-long coma would find the themes instantly legible. In no particular order: React is good because it is popular. There is no other way to evaluate framework choice, and that it's daft to try because "everyone knows React".4 Investments in React are simultaneously solid long-term choices, but also fragile machines in need of constant maintenance lest they wash away under the annual tax of breaking changes, toolchain instability, and changing solutions to problems React itself introduces. Form validation is not a solved problem, and in our glorious future, the transpilers compilers will save us.
Above all else, the consensus remains that SPAs are unquestionably a good idea, and that React makes sense because you need complex data and state management abstractions to make transitions between app sections seem fluid in an SPA. And if you're worried about the serially terrible performance of React on mobile, don't worry; for the low, low price of capitulating to App Store gatekeepers, React Native has you covered.5
At no point would our theoretical patient risk learning that rephrasing everything in JSX is now optional thanks to React 19 finally unblocking interoperability via Web Components.6 Nor would they become aware that new platform APIs like cross-document View Transitions and the Navigation API invalidate foundational premises of the architectures that React itself is justified on. They wouldn't even learn that React hasn't solved the one problem it was pitched to address.
Conspicuously missing from the various "State Of" talks was discussion of the pressing and pervasive UX quality issues that are rampant in the React ecosystem.
We don't need to get distracted looking inside these results. Treating them as black boxes is enough. And at that level we can see that, in aggregate, JS-centric stacks aren't positively correlated with delivering good user-experiences.
This implies that organisations adopting React do not contain the requisite variety needed to manage the new complexity that comes from React ecosystem tools, practices, and community habits. Whatever the source, it is clearly a package deal. The result are systems that are out of control and behave in dynamically unstable ways relative to business goals.
The evidence that React-based stacks frequently fail to deliver good experiences is everywhere. Weren't "fluid user experiences" the point of the JS/SPA/React boondoggle?7
We have witnessed high-cost, low-quality JS-stack rewrites of otherwise functional HTML-first sites ambush businesses with reduced revenue and higher costs for a decade. It is no less of a scandal for how pervasive it has become.
But good luck finding solutions to, or even acknowledgement of, that scandal on React conference agendas. The reality is that the more React spreads, the worse the results get despite the eye-watering sums spent on conversions away from functional "legacy" HTML-first approaches. Many at React Summit were happy to make these points to me in private, but not on the main stage. The JS-industrial-complex omertà is intense.
No speaker I heard connected the dots between this crisis and the moves of the React team in response to the emergence of comparative quality metrics. React Fiber (née "Concurrent"), React Server Components, the switch away from Create React App, and the React Compiler were discussed as logical next steps, rather than what they are: attempts to stay one step ahead of the law. Everyone in the room was expected to use their employer's money to adopt all of these technologies, rather than reflect on why all of this has been uniquely necessary in the land of the Over Reactors.8
The treadmill is real, but even at this late date, developers are expected to take promises of quality and productivity at face value, even as they wade through another swamp of configuration cruft, bugs, and upgrade toil.
React cannot fail, it can only be failed.
OverExposed
And then there was the privilege bubble. Speaker after speaker stressed development speed, including the ability to ship to mobile and desktop from the same React code. The implications for complexity-management, user-experience, and access were less of a focus.
The most egregious example of the day came from Evan Bacon in his talk about Expo, in which he presented Burger King's website as an example of a brand successfully shipping simultaneously to web and native from the same codebase. Here it is under WebPageTest.org's desktop setup:9
As you might expect, putting 75% of the 3.5MB JS payload (15MB unzipped) in the critical path does unpleasant things to the user experience, but none of the dizzying array of tools involved in constructing bk.com steered this team away from failure.10
The fact that Expo enables Burger King to ship a native app from the same codebase seems not to have prevented the overwhelming majority of users from visiting the site in browsers on their mobile devices, where weaker mobile CPUs struggle mightily:
This sort of omnishambles is what folks mean when they say that "JavaScript broke the web and called it progress".
Asking for an industry.
The other poster child for Expo is Bluesky, a site that also serves web and React Native from the same codebase. It's so bewilderingly laden with React-ish excesses that their engineering choices qualify as gifts-in-kind to Elon Musk and Mark Zuckerberg:
Why is Bluesky so slow? A huge, steaming pile of critical-path JS, same as Burger King:
Again, we don't need to look deeply into the black box to understand that there's something rotten about the compromises involved in choosing React Native + Expo + React Web. This combination clearly prevents teams from effectively managing performance or even adding offline resilience via Service Workers. Pinafore and Elk manage to get both right, providing great PWA experiences while being built on a comparative shoestring. It's possible to build a great social SPA experience, but maybe just not with React:
The unflattering comparisons are everywhere when you start looking. Tanner Linsley's talk on TanStack (not yet online) was, in essence, a victory lap. It promised high quality web software and better time-to-market, leaning on popularity contest results and unverifiable, untested claims about productivity to pitch the assembled. To say that this mode of argument is methodologically unsound is an understatement. Rejecting it is necessary if we're going to do engineering rather that marketing.
The TanStack website cites this social proof as an argument for why their software is great, but the proof of the pudding is in the eating:
The contrast grows stark as we push further outside the privilege bubble. Here are the same sites, using the same network configuration as before, but with the CPU emulation modelling a cheap Android instead:
| Site | Wire JS | Decoded JS | TBT (ms) |
|---|---|---|---|
| astro.build | 11.1 kB | 28.9 kB | 23 |
| hotwired.dev | 1.8 kB | 3.6 kB | 0 |
| 11ty.dev | 13.1 kB | 42.2 kB | 0 |
| expo.dev | 1,526.1 kB | 5,037.6 kB | 578 |
| tanstack.com | 1,143.8 kB | 3,754.8 kB | 366 |
Yes, these websites target developers on fast machines. So what? The choices they make speak to the values of their creators. And those values shine through the fog of marketing when we use objective quality measures. The same sorts of engineers who care to shave a few bytes of JS for users on fast machines will care about the lived UX quality of their approach all the way down the wealth curve. The opposite also holds.
It is my long experience that cultures that claim "it's fine" to pay for a lot of JS up-front to gain (unquantified) benefits in another dimension almost never check to see if either side of the trade comes up good.
Programming-as-pop-culture is oppositional to the rigour required of engineering. We need to collectively recalibrate when the folks talking loudest about "scale" and "high quality" and "delivery speed" — without metrics or measurement — continually plop out crappy experiences but are given huge megaphones anyway.
There were some bright spots at React Summit, though. A few brave souls tried to sneak perspective in through the side door, and I applaud their efforts:
If frontend aspires to be a profession11 — something we do for others, not just ourselves — then we need a culture that can learn to use statistical methods for measuring quality and reject the sorts of marketing that still dominates the React discourse.
And if that means we have to jettison React along the way, so be it.
FOOTNOTES
For attendees, JSNation and React Summit were separate events, although one could buy passes that provided access to both. My impression is that many did. As they were in the same venue, this may have simplified some logistics for the organisers, and it was a good way to structure content for adjacent, but not strictly overlapping, communities of interest. ⇐
Again, my thanks to the organisers for letting me sit in on this meeting. As with much of my work, my goal was to learn about what's top of mind to the folks solving problems for developers in order to prioritise work on the Web Platform.
Without giving away confidences from a closed-door meeting, I'll just say that it was refreshing to hear framework authors tell us that they need better HTML elements and that JSX's default implementations are scaling exactly as poorly ecosystem-wide as theory and my own experience suggest. This is down to React's devil-may-care attitude to memory.
It's not unusual to see heavy GC stalls on the client as a result of Facebook's always-wrong assertion that browsers are magical and that CPU costs don't matter. But memory is a tricksy issue, and it it's a limiting factor on the server too.
Lots to chew on from those hours, and I thank the folks who participated for their candour, which was likely made easier since nobody from the React team deigned to join. ⇐
Or worse, don't.
Luckily, some who experience the tug of conscience punch out and write about it. Any post-McKinsey tell-all will do, but Anand Giridharadas is particularly good value for money in the genre. ⇐
Circular logic is a constant in discussions with frameworkists. A few classics of the genre that got dusted off in conversations over the conference:
-
"The framework makes us more productive."
Oh? And what's the objective evidence for that productivity gain?
Surely, if it's large as frameworkists claim, economists would have noted the effects in aggregate statistics. But we have not seen that. Indeed, there's no credible evidence that we are seeing anything more than the bog-stock gains from learning in any technical field. The combinatorial complexity of JS frameworks may, in itself, reduce those gains; we don't know, so we can't make claims either way.
Nobody's running real studies that compare proficient HTML&CSS or jQuery developers to React developers under objective criteria. In the place of research, personal progression is frequently cited as evidence for collective gains, which is obviously nonsensical.
Indeed, it's just gossip.
-
"But we can hire for the framework."
😮 sigh 😮💨 -
"The problem isn't React, it's the developers."
Hearing this self-accusation offered at a React conference was truly surreal.
In a room free of discussions about real engineering constraints, victim-blaming casts a shadow of next-level cluelessness. But frameworkists soldier on, no matter the damage it does to their argument. Volume and repetition seem key to pressing this line with a straight face.
-
A frequently missed consequence of regulators scrutinising Apple's shocking (lack of) oversight of its app store has been Apple relaxing restrictions on iOS PWAs. Previously, PWA submissions were rejected often enough to warn businesses away. But that's over now. To reach app stores on Windows, iOS, and Android, you need is a cromulent website and PWABuilder.
For most developers, the entire raison d'être for React Native is kaput; entirely overcome by events. Not that you'd hear about it at an assemblage of Over Reactors. ⇐
Instead of describing React's exclusive ownership of subtrees of the DOM, along with the introduction of a proprietary, brittle, and hard-to-integrate parallel lifecycle as a totalising framework that demands bespoke integration effort, the marketing term "composability" was substituted to describe the feeling of giving everything over to JSX-flavoured angle brackets every time a utility is needed. ⇐
It has been nearly a decade since the failure of React to reliably deliver better user experiences gave rise to the "Developer Experience" bait-and-switch. ⇐
Mark Erikson's talk was ground-zero for this sort of obfuscation. At the time of writing, the recording isn't up yet, but I'll update this post with analysis when it is. I don't want to heavily critique from my fallible memory. ⇐
WPT continues to default desktop tests to a configuration that throttles to 5Mbps up, 1Mbps down, with 28ms of RTT latency added to each packet. All tests in this post use a somewhat faster configuration (9Mbps up and down) but with 170ms RTT to better emulate usage from marginal network locations and the effects of full pipes. ⇐
I read the bundles so you don't have to.
So what's in the main, 2.7MB (12.5MB unzipped) bk.com bundle? What follows is a stream-of-consciousness rundown as I read the pretty-printed text top-to-bottom. At the time of writing, it appears to include:
-
A sea of JS objects allocated by the output of a truly cursed "CSS-in-JS" system. As a reminder, "CSS-in-JS" systems with so-called "runtimes" are the slowest possible way to provide styling to web UI. An ominous start.
-
React Native Reanimated (no, I'm not linking to it), which generates rAF-based animations on the web in The Year of Our Lord 2025, a full five years after Safari finally dragged its ass into the 2010s and implemented the Web Animation API.
As a result, React Native Renaimated is Jank City.
Jank Town. George Clinton and the Parliment Jankidellic. DJ Janky Jeff. Janky Jank and the Janky Bunch. Ole Jankypants. The Undefeated Heavyweight Champion of Jank.
You get the idea; it drops frames.
-
Redefinitions of the built-in CSS colour names, because at no point traversing the towering inferno of build tools was it possible to know that this web-targeted artefact would be deployed to, you know, browsers.
-
But this makes some sense, because the build includes React Native Web, which is exactly what it sounds like: a set of React components that emulate the web. This allows RN project to provide a subset of the layout that browsers are natively capable of, but it does not include many of the batteries that the web includes for free.
Which really tells you everything you need to know about how teams get into this sort of mess.
-
Huge amounts of code duplication via inline strings that include the text of functions right next to the functions themselves.
Yes, you're reading that right: some part of this toolchain is doubling up the code in the bundle, presumably for the benefit of a native debugger. Bro, do you even sourcemap?
At this point it feels like I'm repeating myself, but none of this is necessary on the web, and none of the (many, many) compiler passes saw fit to eliminate this waste in a web-targeted build artefact.
-
Another redefinition of the built-in CSS colour names and values. In browsers that support them natively. I feel like I'm taking crazy pills.
-
A full copy of React, which is almost 10x larger than it needs to be in order to support legacy browsers and React Native.
-
TensHundreds of thousands of lines of auto-generated schema validation structures and repeated, useless getter functions for data that will never be validated on the client. How did this ungodly cruft get into the bundle? One guess, and it rhymes with "schmopallo". -
Of course, no bundle this disastrous would be complete without multiple copies of polyfills for widely supported JS features like
Object.assign(), class private fields, generators, spread, async iterators, and much more. -
Inline'd WASM code, appearing as a gigantic JS array. No, this is not a joke.
-
A copy of Lottie. Obviously.
-
What looks to be the entire AWS Amplify SDK. So much for tree-shaking.
-
A userland implementation of elliptic curve cryptography primitives that are natively supported in every modern browser via Web Crypto.
-
Inline'd SVGs, but not as strings. No, that would be too efficient. They're inlined as React components.
-
A copy of the app's Web App Manifest, inline, as a string. You cannot make this up.
Given all of this high-cost, low-quality output, it might not surprise you to learn that the browser's coverage tool reports that more than 75% of functions are totally unused after loading and clicking around a bit. ⇐
-
I'll be the first to point out that what Brandeis is appealing to is distinct from credentialing. As a state-school dropout, that difference matters to me very personally, and it has not been edifying to see credentialism (in the form of dubious boot camps) erode both the content and form of learning in "tech" over the past few years.
I'm remain personally uncomfortable with the term "professional" for all the connotations it now carries. But I do believe we should all aspire to do our work in a way that is compatible with Brandeis' description of a profession. To do otherwise is to endanger any hope of self-respect and even our social licence to operate. ⇐
New zine: The Secret Rules of the Terminal
Hello! After many months of writing deep dive blog posts about the terminal, on Tuesday I released a new zine called “The Secret Rules of the Terminal”!
You can get it for $12 here: https://wizardzines.com/zines/terminal, or get an 15-pack of all my zines here.
Here’s the cover:
the table of contents
Here’s the table of contents:
why the terminal?
I’ve been using the terminal every day for 20 years but even though I’m very confident in the terminal, I’ve always had a bit of an uneasy feeling about it. Usually things work fine, but sometimes something goes wrong and it just feels like investigating it is impossible, or at least like it would open up a huge can of worms.
So I started trying to write down a list of weird problems I’ve run into in terminal and I realized that the terminal has a lot of tiny inconsistencies like:
- sometimes you can use the arrow keys to move around, but sometimes pressing the arrow keys just prints
^[[D - sometimes you can use the mouse to select text, but sometimes you can’t
- sometimes your commands get saved to a history when you run them, and sometimes they don’t
- some shells let you use the up arrow to see the previous command, and some don’t
If you use the terminal daily for 10 or 20 years, even if you don’t understand exactly why these things happen, you’ll probably build an intuition for them.
But having an intuition for them isn’t the same as understanding why they happen. When writing this zine I actually had to do a lot of work to figure out exactly what was happening in the terminal to be able to talk about how to reason about it.
the rules aren’t written down anywhere
It turns out that the “rules” for how the terminal works (how do
you edit a command you type in? how do you quit a program? how do you fix your
colours?) are extremely hard to fully understand, because “the terminal” is actually
made of many different pieces of software (your terminal emulator, your
operating system, your shell, the core utilities like grep, and every other random
terminal program you’ve installed) which are written by different people with different
ideas about how things should work.
So I wanted to write something that would explain:
- how the 4 pieces of the terminal (your shell, terminal emulator, programs, and TTY driver) fit together to make everything work
- some of the core conventions for how you can expect things in your terminal to work
- lots of tips and tricks for how to use terminal programs
this zine explains the most useful parts of terminal internals
Terminal internals are a mess. A lot of it is just the way it is because someone made a decision in the 80s and now it’s impossible to change, and honestly I don’t think learning everything about terminal internals is worth it.
But some parts are not that hard to understand and can really make your experience in the terminal better, like:
- if you understand what your shell is responsible for, you can configure your shell (or use a different one!) to access your history more easily, get great tab completion, and so much more
- if you understand escape codes, it’s much less scary when
cating a binary to stdout messes up your terminal, you can just typeresetand move on - if you understand how colour works, you can get rid of bad colour contrast in your terminal so you can actually read the text
I learned a surprising amount writing this zine
When I wrote How Git Works, I thought I
knew how Git worked, and I was right. But the terminal is different. Even
though I feel totally confident in the terminal and even though I’ve used it
every day for 20 years, I had a lot of misunderstandings about how the terminal
works and (unless you’re the author of tmux or something) I think there’s a
good chance you do too.
A few things I learned that are actually useful to me:
- I understand the structure of the terminal better and so I feel more confident debugging weird terminal stuff that happens to me (I was even able to suggest a small improvement to fish!). Identifying exactly which piece of software is causing a weird thing to happen in my terminal still isn’t easy but I’m a lot better at it now.
- you can write a shell script to copy to your clipboard over SSH
- how
resetworks under the hood (it does the equivalent ofstty sane; sleep 1; tput reset) – basically I learned that I don’t ever need to worry about rememberingstty saneortput resetand I can just runresetinstead - how to look at the invisible escape codes that a program is printing out (run
unbuffer program > out; less out) - why the builtin REPLs on my Mac like
sqlite3are so annoying to use (they uselibeditinstead ofreadline)
blog posts I wrote along the way
As usual these days I wrote a bunch of blog posts about various side quests:
- How to add a directory to your PATH
- “rules” that terminal problems follow
- why pipes sometimes get “stuck”: buffering
- some terminal frustrations
- ASCII control characters in my terminal on “what’s the deal with Ctrl+A, Ctrl+B, Ctrl+C, etc?”
- entering text in the terminal is complicated
- what’s involved in getting a “modern” terminal setup?
- reasons to use your shell’s job control
- standards for ANSI escape codes, which is really me trying to figure out if I think the
terminfodatabase is serving us well today
people who helped with this zine
A long time ago I used to write zines mostly by myself but with every project I get more and more help. I met with Marie Claire LeBlanc Flanagan every weekday from September to June to work on this one.
The cover is by Vladimir Kašiković, Lesley Trites did copy editing, Simon Tatham (who wrote PuTTY) did technical review, our Operations Manager Lee did the transcription as well as a million other things, and Jesse Luehrs (who is one of the very few people I know who actually understands the terminal’s cursed inner workings) had so many incredibly helpful conversations with me about what is going on in the terminal.
get the zine
Here are some links to get the zine again:
As always, you can get either a PDF version to print at home or a print version shipped to your house. The only caveat is print orders will ship in August – I need to wait for orders to come in to get an idea of how many I should print before sending it to the printer.