Skip to content
Software Architecture

Why Plugin APIs Fail at Malleable Software: Sandboxing vs. Readability

Updated

Knowledge on this page was mainly distilled from Malleable Software Needs a Kernel You Can Still Change.

Plugin APIs are the default answer to extensibility: publish extension points, sandbox what people build, and let them get on with it. The approach protects the base system from extensions. It does not protect extensions from the base. That asymmetry is why plugin architectures fail when the underlying system needs to change, which is exactly the scenario malleable software guarantees.

The 10x Problem

A Hacker News commenter who actually runs a plugin ecosystem described the result: there is 10x more code in the various plugins than in the core code that was supposed to be the 80%, because unsurprisingly everyone needed a different 20%. From the plugin author's perspective, this is a success. The base stayed clean. The person who needed hash-chained audit logs for German auditors got them without making the system worse for everyone else.

From the vendor's perspective, the picture inverts. Ten times your own codebase now lives outside your codebase, written in a language you did not define. You cannot read it, search it, or change it. The plugin authors are pleased for the exact reason you should be worried.

Sandboxed Is Not the Same as Readable

The most careful version of the plugin answer sets two conditions on custom code: it must inherit the base (so permissions and history apply automatically), and it must be bounded in the sense that custom code can break itself but cannot corrupt the base. Both conditions run at runtime. Both protect the base from the code. Neither protects the code from the base.

Sandbox a plugin perfectly and it still goes blank the day you rename a column. The sandbox has no opinion about what the code inside it meant, so the mass of functionality keeps migrating upward until the plugins are the system and the base is a footnote. This is the distinction the article draws: bounded in the plugin sense means sandboxed (isolated at runtime), while bounded in the kernel grammar sense means readable (expressed in domain-level words the system can search, verify, and migrate).

Bounded Languages in Production

The pattern of enforcing a bounded language rather than sandboxing arbitrary code already ships at scale in two well-known systems.

eBPF in Linux. An eBPF program must be written in a bounded instruction set. A verifier refuses it before it can load, and the kernel enforces everything after that. The program never runs arbitrary code inside the kernel; it runs only what the verifier has already proven safe to compose.

Google's generative UI spectrum. Google sorts generative UI by what the model may hand back. At one end, the model picks from components the team built. At the other, it writes raw HTML. The option Google rates as balanced is neither: the model returns a description of an interface against a schema, and the system decides what to build from it. That is the same instinct one floor up. Google is protecting the rendering layer. A kernel grammar protects the domain layer.

Why the Escape Hatch Defeats the Purpose

Many plugin architectures offer an arbitrary-code rung at the top of a capability ladder for the hard cases. The moment such a rung exists, everything expensive migrates onto it. The hard cases are exactly the ones with the most business value, so they are the first to climb. Once they do, you are back to owning code you cannot read, and the bounded lower rungs become ceremonial.

A kernel grammar takes the opposite position: no escape hatch. The cost is that the hard cases have nowhere to go, and you will hear about it. But that constraint is what makes the grammar's promises durable. Every capability stays expressed in words the system defined, so when the system changes, a single edit behind a word propagates to every capability that used it. A plugin API cannot offer that, because it never knew what the code inside the sandbox was trying to say.

Q&A

If plugin APIs are so problematic, why are they still the dominant extensibility model?

Plugin APIs solve the problem most vendors optimize for first: keeping the core clean. They succeed at that. The failure mode only appears later, when the base needs to evolve and thousands of plugins silently break. By then the ecosystem has its own gravity, and migrating away is more expensive than living with the brittleness.

How does a kernel grammar handle the 'different 20%' problem that plugins solve?

A kernel grammar publishes a fixed list of words and combination rules. Users compose those words into sentences that express their specific needs. The number of possible sentences far exceeds what any single user needs, so the different 20% is covered by composition rather than by arbitrary code. The key difference is that every resulting capability is searchable and migratable because it uses only words the system defined.

Does removing the escape hatch mean some use cases simply cannot be served?

Yes, in the short term. If the grammar cannot express what a customer wants, that customer is stuck this quarter. However, the structured refusal from the grammar becomes a signal for what primitive to add next. The constraint is not permanent; it is a feedback mechanism that grows the grammar in the direction of real demand rather than speculative extension points.

Is eBPF really comparable to a domain-level kernel grammar?

The analogy is structural, not literal. Both enforce a bounded language that a verifier checks before anything runs, and both keep enforcement in the kernel rather than in the extension. eBPF operates at the systems level (network packets, tracing), while a kernel grammar operates at the domain level (business objects, permissions). The shared principle is that the extension never writes in a language the host cannot read.

What happens to existing plugins if a team wants to migrate to a kernel grammar approach?

The article does not prescribe a migration path from plugins to a grammar. It argues that the grammar must be built under its three rules from the beginning because you cannot add them afterward. For teams already deep in a plugin ecosystem, the practical implication is that new extensibility should be grammar-based, while legacy plugins remain sandboxed until they can be rewritten as grammar sentences.