RSSAmplifier

Blog

Rubén Sospedra papers

Highly curated content about JavaScript, clients development, the Internet, and occasionally, philosophy. Not your usual blog. Favour valuable content over long and boring SEO-focused posts. Words are my own. It's dangerous to go unknowing, take some pills 💊

sospedra.meRSS feed ↗27 posts

Latest posts

How to stand inside the total solar eclipse of 12 August 2026

One thousand. That is the light gap between a 99 percent eclipse and a 100 percent eclipse. At 99 percent the open sliver of photosphere still pours about 1,000 lux over you, the light of an overcast noon. Your pupils adapt and nothing feels wrong. At 100 percent the light drops to the level of a full moon. The corona unfolds, Venus switches on, and the horizon runs a 360 degree sunset. On…

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.

The landlords won

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…

The bridge is dead, long live JSI

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…

'use cache' killed ISR

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…

Nobody trains the juniors anymore

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…

The server is just a cache

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…

MCP is CGI for agents

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…

Tailwind 4 speaks CSS at last

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…

Markets don't lie, they leak

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…

The compiler will remember for you

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.

The maintainer was the exploit

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…

View transitions without the framework

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…

Bun ate the toolchain

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.

Worklets: the other JavaScript runtime

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.

The casino is the ladder

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…

app/ is a rewrite, not an upgrade

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.

The :has() heist

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".…

Your effect runs twice on purpose

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.

colors.js and the trust budget

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…

The dual package hazard

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.

content-visibility: the lazy render

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…

The browser lifecycle

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.

On TypeScript discriminated unions

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…

Scroll-based 60 fps animations

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 , a scroll…

Web-safe fonts

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…

GaaS: GitHub as a Service

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,…