A visual explainer

Extensible Software in the Age of LLMs

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.

THE HEAD — well served THE LONG TAIL — yours, probably

Chapter 01 — The problem

The long tail of unmet needs

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.

Interactive — the demand curve & the usability tax

Hover the curve — or drag the orange handle.

Usability for every user86%
The product’s toolbar, as features pile up
of users fully served
needs left in the tail

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

Software has gotten all… squishy

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.

Interactive — speaking software into existence
You, to your agent

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

A new opportunity for extensible software on the web

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.

Interactive — ask, and the app extends itself
readlater.app — the accountable core
Articles, faves, tags, parsers. Solid, boring, trustworthy. Exposes extension points:
Others can install what you made.

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.

Interactive — webhook vs. in-app extension

The webhook way separate service

    What you operate

    The extension way in-app

      What you operate

      “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

      Where this matters

      Four places the essay wants LLM-native extension — each with its own reason the current answer isn’t good enough.

      Interactive — four frontiers

      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

      Extensible software on the web is… harder

      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:

      Interactive — run one user’s untrusted code, unsandboxed
      CPU
      3%
      Log lines / sec
      12
      Outbound requests
      0
      [platform] all tenants healthy · 99.99% uptime

      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

      But surely someone has done this? Salesforce.

      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.

      2006
      AWS launches S3 and EC2
      The cloud era begins. Renting someone else’s whole machine becomes the default shape of infrastructure.
      2007
      Salesforce ships Apex
      Cutting against the grain: no containers. Customers write a weird, custom, Java-like language — and Salesforce runs it, multi-tenant, inside its own platform. Squint, and it’s a precursor to modern serverless.
      2026
      We have far more options
      Interpreters, V8 isolates, microVMs, WASM. The Salesforce path no longer requires building a compiler, a type system, a runtime, a debugger, and an educational ecosystem — though that’s exactly what the business value justified.

      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:

      CustomerHealthApi.clsApex
      @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
              ];
          }
      }
      1. No routing, no auth boilerplate, no server. The platform wraps your class in a real REST endpoint.
      2. 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.:

      RenewalScanner.clsApex
      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()
      );
      1. SQL integrated straight into the language — Salesforce built a compiler, type system, runtime, and debugger to make this safe.
      2. Writes happen inside the platform’s transactions, with the user’s permissions — not with a god-mode service key.
      3. A cron expression, not a cron server. The scheduler is part of the platform.

      Every night at 2:00 a.m. 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

      A new primitive, and its five required properties

      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.

      Property 1

      Cheap to run

      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.

      Cost while idle
      $0.000000 /hr — it isn’t running
      Users packed onto one machine (memory overhead is everything)
      Property 2

      Fast cold starts

      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.

      Race a request against two runtimes
      Container
      Isolate
      Property 3

      Control over limits

      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.

      The Heroku tutorial app
      press deploy…
      Property 4

      A solid isolation boundary

      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.

      32 tenants, one machine — click a tenant to make it crash & loop
      everyone is healthy
      Property 5 — the subtle one

      Safe action through capabilities

      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 progression: pure functions (safe but limited) → raw API keys (powerful but instantly leakable) → validating proxies (better, but an unmaintainable tangle) → narrow capabilities (the answer). Enter the capability lab ↓

      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.

      Interactive — the exfiltration lab
      extension.tsTS
      Network monitorcredential safe
      Pick a model above, then launch an attack.

      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.

      TWITTER_API_KEY=… vs. twitter.post_new_tweet() email.send_me_email()

      This is the shape we want for safe extensible software. Now — what technology can actually deliver the five properties?

      Chapter 08 — The solution space

      What technology fits?

      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.

      Comparison — sandbox technologies × the five properties

      ← scroll sideways · tap a row for detail

      Approach Cheap to run Cold starts Limits Isolation Capabilities
      They compose WASM can run inside a V8 isolate or a microVM — none of these options are mutually exclusive. And where the primitive lacks its own capability model, an object-capability protocol like Cap’n Web can supply one on top of any of them.

      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.

      Disclosure The essay’s author works at Cloudflare, says so in the piece, and credits Kenton Varda’s writing for shaping much of this thinking. The case below is theirs — the alternatives above were covered first, and this explainer keeps the disclosure visible rather than hiding it.

      Chapter 09 — The highlighted answer

      Cloudflare Dynamic Workers

      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.

      [01]

      Observability, built in

      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.

      [02]

      Multi-tenant data storage

      Give every user their own SQLite database with Durable Object facets, or their own R2 bucket. Extensions get real state without shared-schema gymnastics.

      [03]

      Durable Execution

      Dynamic Workflows let user actions span minutes or days with appropriate retries and backoff — the Temporal lesson, built in.

      [04]

      Source control, 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.

      [05]

      Hosted LLMs

      Workers AI lets extensions call LLMs directly — with appropriate token budgets and rate limits. Drafting extensions, and running them, can both use models.

      [06]

      Self-hosted JS tooling

      Most JavaScript tooling is itself JavaScript — so transpiling and testing user code needs no separate container or VM.

      analyze-article.ts — an extension calling a hosted LLMTS
      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,
            },
          ],
        )
      }
      1. 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.
      transpile.ts — the platform builds user code in-placeTS
      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) };
        }
      }
      1. The toolchain is JavaScript too — transpiling and testing extension code can happen inside the same kind of sandbox, no separate build container required.

      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.

      Read the original essay →

      View more demos Get $10 off Kimi K3