A visual explainer
Ordinary web software serves the head of the demand curve and strands the long tail of individual needs. LLMs can write the missing code — but only if software gives it a safe place to run. This is an interactive walk through Jeremy Morrell’s argument for a new kind of extensible web software.
Chapter 01 — The problem
Most web software is static. Developers have limited time and attention, so they build for the largest group of users — and leave everyone else stranded.
Picture every feature a mapping app’s users have ever asked for, sorted from “everyone wants this” to “exactly one person wants this.”
That’s a demand curve. The top of it is well served by the software you already use. The rest is a long tail of needs that is different for every user — and no product team will ever get to it.
It’s not laziness. Even a maximally motivated team can’t ship the whole tail, because every added feature complicates the product for every other user. If the market for a feature is small, shipping it actively makes the product worse for everyone who doesn’t need it.
Try it below. Drag the coverage handle to ship more features — and watch what it costs everyone.
Hover the curve — or drag the orange handle.
The curve is the argument: serving more of the tail always trades away usability for the whole base. Static software can’t win this game — it can only choose where to lose.
For decades this was simply the economics of software: the tail was unprofitable to serve, full stop. Then something changed the cost side of the equation.
Chapter 02 — The shift
LLMs are genuinely excellent at building Software for One — personal tools, custom-fit to a single person’s workflow, that side-step all the complexity and accountability of enterprise software.
Pete Koomen at Y Combinator calls the opportunity Small Software: a cloud built for bespoke tools, where sharing a personal app with a colleague is as easy as sharing a Google Doc. Agents make it easy to build personal tools — deploying, securing, and sharing them is the hard part.
The clearest example of what this feels like is Pi: a battle-tested core that is almost endlessly extensible just by asking, with customizations users can share. Morrell calls this shape LLM-native software. In the past year, your users suddenly acquired the ability to speak code into existence. Most software can’t leverage that. Pi leans into it.
Each request compiles straight into a working tool with an audience of exactly one. No roadmap, no prioritization meeting, no waiting for a vendor.
The loop runs by itself. Notice what’s missing: nobody filed a feature request, and nobody else had to inherit these features.
But look at where pluggable software actually lives today: AI agents, developer IDEs, video-game mods, Blender add-ons, CAD extensions. Local, professional tooling with a high barrier to entry. You have to be comfortable running custom software on your own machine — and in a corporate environment, someone has to be comfortable with you running code nobody has ever reviewed.
The web is the most successful software distribution system in the world. It shouldn’t be left behind.
Chapter 03 — The hypothesis
LLMs radically lower the cost of authoring extensions. Modern sandbox primitives lower the cost of deploying them and provide security boundaries. Build a solid, accountable core — and let users safely extend it by having an LLM fill in the missing pieces.
What would that feel like? You wouldn’t file a feature request. You’d just tell your read-it-later app what you want.
Pick one of the requests below. A robot extrudes the silly bits of code, hooks them into the app’s extension points, and makes it happen — and what you make, you can share with anyone else who wants the same feature.
The core stays solid and accountable. The extension is small, generated, hooked into a declared extension point — and shareable.
Contrast that with how web software “extends” today: webhooks. The app posts an event to a URL you control. Which sounds fine, until you price in what that URL actually is: a completely separate service you now have to build and operate — plus whatever delivery failures show up at 3 a.m.
Press the button and watch a new requirement land on both models.
“When I attach this tag to a record, run my function.” “Do this for me on a daily cron.” The bar for extending software should be a sentence, not a service.
Chapter 04 — The territory
Four places the essay wants LLM-native extension — each with its own reason the current answer isn’t good enough.
And underneath all of these, a proof of what “extensible to the core” can look like — from the opencode team, whose agent harness treats nearly everything as a plugin. Here they are, all 68 — click a few to disable them. The harness keeps running.
68 / 68 internal plugins active — agents, integrations, config loading, everything.
an architectural change we made in opencode is nearly everything is an internal plugin — there’s 68 of them that cover our built-in agents, integrations, config loading, etc. this means you can disable any behavior and we also properly dogfood our plugin apis— opencode, August 13, 2026
Chapter 05 — The catch
So far this all sounded easy. It is nothing of the sort.
Consider Obsidian: a humble markdown editor that a few clicks can turn into a kanban board, a database, a semantic-search engine over your own notes. That power has a price — Obsidian’s model requires you to trust every plugin you install. A plugin can do basically anything. Obsidian fights back with review processes and verified authors, and for a low-stakes notes app with a small community, that tradeoff is right.
Now try the same trick in software that holds other people’s data — customer records, financial transactions, private messages. The model falls apart immediately. Executing arbitrary user code raises a wall of threats:
One careless while True or one malicious fetch — and every other tenant pays for it. This is the dream meeting the wall.
Before we write this off as infeasible — someone has already done it. At immense scale. Since 2007.
Chapter 06 — The existence proof
Yes, that Salesforce. A massive multi-tenant programmable platform that has safely run customer code since 2007 — back when S3 and EC2 were barely a year old.
What does “safely running your custom logic in response to app events, within transactions” actually look like? Need a custom endpoint? A few lines — the platform handles routing, authentication, tenant isolation, execution. There is no web server to deploy:
@RestResource(urlMapping='/customer-health')
global with sharing class CustomerHealthApi {
@HttpGet
global static Account getCustomer() {
String accountId =
RestContext.request.params.get('accountId'); // ①
return [
SELECT Id, Name, Health_Score__c, Renewal_Date__c
FROM Account
WHERE Id = :accountId
WITH USER_MODE // ②
LIMIT 1
];
}
}
WITH USER_MODE — the query runs with the caller’s permissions. Tenant isolation and access control are the platform’s job, not yours.Or custom logic on a schedule — flag every account with a renewal coming up in the next 30 days, every night at 2 a.m.:
public class RenewalScanner implements Schedulable {
public void execute(SchedulableContext context) {
List<Account> accounts = [
SELECT Id, Needs_Attention__c
FROM Account
WHERE Renewal_Date__c = NEXT_N_DAYS:30 // ①
WITH USER_MODE
];
for (Account account : accounts) {
account.Needs_Attention__c = true;
}
update as user accounts; // ②
}
}
// Schedule it to run daily at 2 a.m.:
System.schedule(
'Check upcoming renewals',
'0 0 2 * * ?', // ③
new RenewalScanner()
);
Every night at the platform fires customer code — millions of tenants’ worth — and nobody’s pager goes off. That’s the bar.
We can be inspired by this without copying it. So: what would it actually take to build something like this today? First, the technical requirements — then the technologies that might fit.
Chapter 07 — The heart of it
To build extensibility into a web app, you need a primitive for running untrusted code. Five properties decide whether it works. Each card below is a working miniature of the constraint.
Thousands or millions of users running snippets means a container-per-user is a non-starter. It must cost ~$0 idle, tiny fractions of a penny per execution — and memory overhead decides how many users fit on one machine.
User code sits on the critical path of a request. You can’t wait a minute for a container to boot. Cold starts need single-digit milliseconds.
A beloved Heroku getting-started guide once had users deploy while True: print(“hello world!”) — brand-new apps instantly spewing millions of log lines per second, forever. You must be able to limit CPU, memory, network, response size, log volume and rate.
Fault isolation: crashes, infinite loops, and memory bombs must not touch any other user. Security isolation: malicious code must not escape or inspect other tenants — including speculative-execution attacks like Spectre.
Code that can’t affect anything is useless, so untrusted code needs a controlled way to act on the world. How you grant that power is the difference between a secure platform and a breach report. This deserves a full lab — work through it below.
The capability lab: you are the malicious extension.
Below are four ways to let user code touch the outside world. For each one, your job is simple: steal the credential and ship it to evil.example. Try every attack on every model. The network monitor shows what actually leaves the building.
Remove ambient I/O and there is simply no way to leak data — the code can only act through the references it was handed. Bonus: a TypeScript definition of capabilities is far easier (and more token-efficient) for an LLM to generate against than a pile of OpenAPI JSON.
If you know IFTTT, you’ve seen the shape: it never hands you a Twitter API key. It hands you a function.
This is the shape we want for safe extensible software. Now — what technology can actually deliver the five properties?
Chapter 08 — The solution space
If this sounds like the requirements for an agent execution platform — it is. Running logic on behalf of a user you cannot trust is the same problem. Four families of answers, scored against the five properties. Click a row for the honest trade-offs.
← scroll sideways · tap a row for detail
| Approach | Cheap to run | Cold starts | Limits | Isolation | Capabilities |
|---|
MicroVMs earn a special note: heavier than the others, but you get POSIX, real CPU and RAM, and a full OS that boots in under a second. Even if you pick isolates or WASM as your isolation primitive, microVMs remain a fine place to author, compile, and test extensions.
One option, though, stands out as the closest thing in 2026 to a production-ready, out-of-the-box framework for extensible web apps.
Chapter 09 — The highlighted answer
Marketed (understandably) for code-mode and agent use cases — but the fit here is broader. Beyond meeting the five properties, they ship the surrounding machinery you’d otherwise build yourself.
OpenTelemetry tracing is built into the runtime itself, with first-class control over emitted telemetry. Both you and your users can see what their code is doing.
Give every user their own SQLite database with Durable Object facets, or their own R2 bucket. Extensions get real state without shared-schema gymnastics.
Dynamic Workflows let user actions span minutes or days with appropriate retries and backoff — the Temporal lesson, built in.
Users need to version and iterate on extensions, and you can’t expect everyone to use GitHub. Build source control into the product itself.
Workers AI lets extensions call LLMs directly — with appropriate token budgets and rate limits. Drafting extensions, and running them, can both use models.
Most JavaScript tooling is itself JavaScript — so transpiling and testing user code needs no separate container or VM.
export async function analyzeArticle(env: Env, article: Article) {
return result = await env.AI.run( // ①
messages: [
{
role: "system",
content: "Decide whether the supplied article talks about cute kittens.",
},
{
role: "user",
content: article.text,
},
],
)
}
env.AI arrives as a binding — an object capability. The extension can run a model; it cannot see the underlying credentials, and token budgets and rate limits apply.import { transform } from 'sucrase'; // ①
export function transpileUserCode(source: string): TranspileResult {
try {
const result = transform(source, {
transforms: ['typescript'],
disableESTransforms: true
});
return { type: 'success', code: result.code };
} catch (err) {
return { type: 'failure', error: String(err) };
}
}
Cloudflare Workers is a platform for building platforms. It hurts the head a little — and it’s a good way to think about these primitives.
Chapter 10 — Here be dragons
Platforms are hard — hard to design, hard to run, hard to debug. But you can be truly surprised by what your users build: things you never considered, or would have even thought possible.
The long tail was never a lack of ideas. It was a lack of affordable, safe ways to serve them. The authoring cost has collapsed. The sandbox primitives exist. What’s left is to build the cores worth extending.
Platforms are hard. It’s worth it.