AI made features cheap. It did not make fundamental changes cheap. Generated capabilities break the day you rename a column, and nobody has said who owns that repair.
One of your customers asks your product for a view nobody at their company has ever had. Jobs by installer, by week, shaded by whether the permit cleared. Sixty seconds later they are looking at it, and it is good.
Eleven weeks later, someone on your team renames a field. The view goes blank. No error, no warning, just an empty rectangle where the answer used to be.
That empty rectangle is the bill for malleable software, the idea that people should be able to reshape their tools with so little friction that reshaping stops being an event. Somebody has to pay it. The somebody is you.
Your first thought is a right one. The assistant that built that view can rebuild it, probably in another sixty seconds. But nothing tells the assistant that the field moved. The view lives in your customer's account, not in your codebase, and no release note is addressed to it. Generating the thing is solved. Noticing that the ground moved under it is not.
You own the schema that view read, the permissions it honored, and the pager that goes off at 2am when ten thousand of those views break at once.
There is an answer, and it is narrower than it sounds. Your kernel publishes a grammar: a fixed list of words, and the rules for combining them. Everything your customers build gets composed in that grammar. So what your customer owns is not code you have to read. It is a sentence in words you chose.
Three rules decide whether that holds. Drop any one of them and you get a system that generates features beautifully and can never be changed underneath them.
Ink & Switch, whose essay named this world, set the whole question aside as future work, openly and in so many words: who pays to maintain what a user generates, what it is allowed to touch while it runs, and what you still sell once anyone can conjure a feature over lunch.¹ That is a fair thing for an essay to do. It is not a thing you can do.
The rest of this article is what the three rules buy, what they cost, and why almost every other answer on offer gives up at least one of them.
End-user programming never solved the part after it works
The usual story is that end-user programming lost because programming was too hard for regular people.
That story was already wrong before AI touched it. In 2001, more than 45 million Americans used a spreadsheet or a database at work, and only one in five of them said they did any programming.²
Widen the frame to the whole workforce and about 11 million people said yes to the word, against two to two and a half million who held a programming job.
So the people willing to claim it already outnumbered the professionals by more than four to one. And four in five spreadsheet users would not claim it at all. They called it a spreadsheet.
Writing the thing was never the bottleneck. AI did not open that gap. It found it open.
Say someone in finance writes a macro (a small program living inside a spreadsheet) that pulls the regional numbers into one sheet every Monday morning. It works, and it saves the team an afternoon a week. That person now owns it, permanently and alone. When the source report changes its column headings, nobody tells the spreadsheet. It breaks quietly, and the discovery lands on the day the quarterly numbers are due, because that is the day anyone looks. When that person moves to another team, the file becomes haunted: everyone routes around it, nobody opens it.
Every generation looked at that and shipped a better authoring surface. Gentler slopes, friendlier syntax, drag and drop, then natural language. We kept improving the half that already worked.
AI is the best authoring surface anyone has ever built, and it changes nothing about the other half. If the fix exists it has to live underneath, in what the generated thing is written in.
The base gets stricter as the surface gets softer
Malleable software needs three layers, each with a different job.
At the bottom, a rigid domain kernel. You build it, and it holds the laws of your domain: the primitives, the invariants, the permissions, the facts that stay true no matter what anyone builds above them. It also holds the implementations.
In the middle, a kernel grammar. It is the same set of ideas, published as names: a word for each of the kernel's primitives, and the rules for combining them. The names, not the implementations. A word is a promise about meaning, and the code behind that promise can change on any Tuesday.
On top, a personal userland, as soft and strange and specific as your customer likes. Your customer never writes the grammar by hand and never meets it as a language. They ask for something in their own words. What they end up owning is a sentence in yours, composed by a model that is only allowed to use words on your list.
The direction is what makes this odd. Most products loosen the base so people can build on it. Here the base gets stricter as the surface gets softer, and the strictness underneath is what makes the softness on top survivable.
Stricter underneath, softer on top.
The reason to have a middle layer at all is that what your customer ends up owning is a sentence in a grammar you defined, instead of code you have to interpret.
Two small things follow at once. You can search it, so you can answer which capabilities read the salary column without reading every program your customers wrote, as long as you store the sentence as structure and not the prompt that produced it. And you can verify it, if you design it so that verification terminates: no unbounded loops, no arbitrary predicates, a ceiling on how large one sentence can get. A finite word list does not buy you that. You have to design for it.
What the middle layer has to obey
Two different things happen at two different times, and everything below depends on keeping them apart.
When the AI builds a capability. Your customer describes what they want. The model translates that into a sentence in your grammar. Your system checks the sentence: do these words exist, is this combination legal, do the types agree. Then it either refuses with a reason, or stores the sentence in your customer's account. Nothing has touched data yet. This check answers one question: can this be said?
When the capability runs. Your kernel loads the stored sentence and checks that it is still legal, because the grammar may have moved since the sentence was written. It resolves each word to what that word means today. Then it checks permissions for the person running it right now, against the rows in front of it. This check answers a different question: may this person do this, now?
So the grammar gets checked twice, and only the second check is a security boundary. The first one asks whether the sentence is legal, and it finishes. The second is the one still on duty while the capability is doing something.
Compose time asks one question. Run time asks another.
Three rules, and what each one costs
Three rules hold the middle layer together, and each one costs something.
One grammar, and no way around it. No escape hatch, no arbitrary-code rung at the top of a ladder for the hard cases. The moment such a rung exists, everything expensive migrates onto it, and you are back to owning code you cannot read. The cost is that the hard cases have nowhere to go, and you will hear about it (but that can be a good thing, more on that later).
The grammar translates, the kernel enforces. The grammar turns your customer's sentence into your own operations, and it never decides who may read what. Your kernel decides that, while the thing is running. It is tempting to give the grammar that job too, because a bounded language looks like a safe one. Do that and you can never change a word again, because a security boundary has to hold still and a translation layer has to be free to move. The cost is that you build the permission model yourself, in the kernel, in terms it can check on every call.
Every word is a definition you own. Not a name for something underneath, a definition. The cost is that you can never quietly drop a word, because publishing one is a promise.
Why not just let the agent do it?
The strongest objection comes from Karri Saarinen, co-founder and CEO of Linear. The middle layer, on that account, is one abstraction too many. "Agents are already almost infinitely malleable", so customizable software "kind of loses its purpose". You "don't need a complicated middleware system of malleable blocks or ways to customize. You need a system and agent to run it."³
The people making that case are not wrong about the surface. Most people, Saarinen writes, "do not want or have time, or capacity to build their own tools".³ True, and irrelevant to whether the layer should exist. Nobody is asking the customer to write in it. Bounded is also not the same as limited. A short list of words and a handful of rules for combining them produce more sentences than your customers will ever compose, which is why a fixed list of words is not a fixed list of things they can build.
The post makes a second objection. "Malleable systems expand the amount of information the agent has to understand before it can do useful work. Every configurable layer, custom workflow, and user-defined structure adds more context to parse, more exceptions to reason through, and more ambiguity to resolve."³
That cost is real, and it is right to price it in tokens, latency and failure rates. It is also the reason the middle layer has to stay small.
But the cost comes from configuration, not from the grammar. A system of malleable blocks makes the model work out how this particular customer set things up. A grammar makes it work out a fixed list of words that is the same for every customer you have.
The first grows with every account you add. The second is the same list on every account, and it moves only when you decide to move it.
Then Saarinen says what people want from a tool: something they can "rely on, share with others, and return to later without having to reconstruct how it was put together".³ Those are not preferences. They are three requirements, and each can be tested.
Rely on: it still works next quarter, after someone on your team renames the field. Share with others: a second person can use it without inheriting a hole in your security. Return to later without reconstructing: a year on, it can still say what it was for. Nobody opens source code to find that out. It is made of words you published, so your system can read it back in plain language.
Call the first one reliability and the third one legibility, because they are not the same test. A spreadsheet macro that has run every Monday for six years is perfectly reliable and completely illegible. Reliability fails when the world moves underneath the thing. Legibility fails because nobody ever wrote down what it meant. A tool can lose either one and keep the other.
An infinitely malleable agent satisfies none of the three, and it cannot. All three describe something holding still, and holding still is the one thing it does not do.
The post's own conclusion says as much. "The best systems will be more opinionated at the core. They will define clear objects, clear workflows, and clear expectations. On top of that, agents provide adaptability and flexibility within it."³ That is a rigid core with a soft agent surface above it. It is my top layer and my bottom one. Saarinen argues for both while arguing against me. His version has no middle, and that is the whole of the disagreement.
The last line of the post says why he does not need one. The promise of this era, he writes, "is not that everyone shapes and customizes their own tools with malleable systems. It is that agents become capable enough that we no longer need to."³ Capability is his answer. No amount of it tells an agent that a field moved eleven weeks ago.
Ten thousand capabilities, one edit
Go back to that view of jobs by installer, shaded by whether the permit cleared. Without a kernel grammar, what your customer owns is generated code that reads a column called permit_status. You rename that column and you now hold ten thousand private artifacts, each of them arbitrary code, with no way to find which ones care. You cannot rewrite them. You cannot list them without parsing every one.
With a kernel grammar, what your customer owns is a sentence in your words. Something close to this, and they never see even this much:
capability "jobs by installer"
source jobs
group_by installer
bucket_by week(scheduled)
shade_by permit_cleared
Every word in there is one you published, and every one of them lives in the grammar. jobs and permit_cleared name primitives your kernel owns. group_by, bucket_by and shade_by are rules for combining them. The model arranged those words and invented none of them.
Now look at what is not in there. No column name, no query, no join. No permission check either, because the sentence does not say who may see this. Your kernel answers that when somebody runs it. The grammar holds the word permit_cleared. The kernel holds what it means, on your side of the line, in terms your customer never sees:
permit_cleared = job_records.permit_status == "approved"
The word is jobs. The table is job_records. Nothing above the line knows the second name exists.
Now rename the column. You change that one line to read job_records.permit_state, in your own repository, once. Ten thousand customer capabilities keep working and not one of them changed, because not one of them ever knew the column existed.⁴
A rename is the simplest instance, not the size of the thing. Anything that stays behind a word costs the same single edit: a join that changes shape, a field that moves to another table, a definition that grows to count conditionally approved permits as cleared too, once you have decided that is what the word should have meant all along. None of it reaches the sentence, because the sentence never named any of it.
None of that is a new idea. What is new is who is on the other side of the interface. It used to be your own engineers. Now it is a model composing on behalf of a customer who will never read either side.
This is where the assistant earns its keep. When a word does have to go, a model can rewrite every capability that used it, and you can check the rewrite, because both sides of the translation are written in a grammar you defined. Turn the same model loose on arbitrary generated code and it is guessing, and you have no way to tell whether it guessed right. This is the maintainer nobody ever hired, and the seam where end-user programming always tore is the exact seam a model is shaped to sit in.
One kind of change stays hard, and it is the same move made without anyone deciding to make it. Suppose permit_cleared narrows from every approved permit to every approved permit with the fee paid, because that is what the operations team meant this quarter. Nothing breaks. Every capability that used the word is now wrong in a way no test will fail. Nothing in the grammar notices, because the word still resolves and the sentence is still legal. A person has to know the meaning moved.
Knowing is where the two worlds separate. Without a middle layer, the person who notices holds a suspicion and no way to act on it, because the affected capabilities are arbitrary code and there is no list to check. With one, the meaning sits behind a single word, and every capability that used that word is a search. It also gives you an option that does not otherwise exist. Leave the word alone and publish a second one, and nothing anybody built changes meaning underneath them, because you never changed the meaning of the word they used.
Noticing is the job you are keeping. Acting on it is the job the grammar does.
A kernel is a list of things that must stay true
An assistant that can rewrite anything can also rewrite the things it should never touch, just as fluently and in the same confident tone.
The malleable.systems community has been working on this since 2024. They call it fearless extensibility. Letting extension authors change everything, they wrote, "may seem like a malleable path to take, but it carries with it maintenance and security headaches".⁵ A reply in that thread puts the requirement in one line: "To truly get security the system needs to publish invariants."
An invariant that lives in your team's heads cannot be checked by a model. Written down, in a form a machine can read, it can be checked against every single thing the model generates.
I did not get here first. An architect posted most of this shape to r/softwarearchitecture in August 2026: a stable host owning the domain invariants, a capability ladder, retrieval before generation, provenance, governance, and a feedback loop that clusters what people tried to build without uploading what they were doing.⁶ It has been sitting there in public ever since, at zero points, with one comment, which is the author's own reading list.
Two things in it are worth arguing with rather than repeating. Arbitrary code stays on the ladder as an escalation path for the hard cases, and the capability layer does the enforcing. Take away the escape hatch, and let that layer translate while the kernel enforces instead, and you get an answer to the item the post itself files under what it cannot resolve: what happens to generated extensions when the thing underneath them has to move.
It ends by asking where the model breaks first, and lists the candidates: API evolution, capability design, component conflicts, supply-chain security, discovery, support cost, and the absence of maintainers. Ink & Switch leaves a shorter list open, and two of the items are the same ones: security, and who pays for support and maintenance.¹
API evolution is the item the three rules exist for. Capability design, support cost and the absence of maintainers get answers further down.
The other three dissolve rather than get solved, and for the same reason. Nobody ships code. There is no extension supply chain to secure, and two sentences cannot fight over a layout or an event handler, because neither one has behavior of its own.
Discovery is the same. The question is whether somebody can find what already exists instead of building it again. That gets hard when the thing to find is code somebody else wrote, because you cannot read it at a glance. So you need install counts and a maintainer's name and somebody to vouch for it. A sentence needs none of that. It is made of your words, so your model can search for the ones it needs, and it carries no code and no permissions of its own, so there is nothing to vouch for. A duplicate costs nothing either, because there is no second implementation to maintain.
What is left is smaller, and it is still yours. Two capabilities can want the same thing, and somebody has to arbitrate. Those sentences also sit in your customers' accounts, so finding a good one across accounts is a privacy question before it is a search problem.
What every one of those items needs first is a kernel, and a kernel here means something narrower than "the core product". It is the part of your system that states, in a form a machine can verify, what must remain true.
An invoice total equals the sum of its lines. A room cannot be double-allocated. A permission cannot be widened by the thing it was meant to constrain.
It is the part of the system whose job is to say no.
A plugin API cannot refuse anything
The standard answer to all of this is a plugin API. Publish extension points, sandbox what people build, and let them get on with it. The people who run one will tell you how that ends.
"A 'solid base' is just another name for 'a library ecosystem'," one commenter wrote on Hacker News. Somebody who actually runs one described the result: "there is 10x more code in the various plugins ... than the core code that was supposed to be the 80%, unsurprisingly everyone needed a different 20%".⁷ They offer that as a success, and from where they stand it is one. The base stayed clean. The person who needed hash-chained audit logs to satisfy German auditors got them without making the system worse for everybody else.
Now stand on the vendor's side of the same fact. 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. They are pleased for the exact reason you should be worried.
The most careful version of the plugin answer sets two conditions on custom code. It has to inherit the base, so permissions and history apply to it automatically. And it has to be bounded, defined precisely: "Custom code can break itself, but it cannot corrupt the base."⁸
Both conditions run at run time. 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 same advice tells you to choose your base carefully, because data, history and permissions accumulate and are hard to re-pick in two years.⁸ Two years is long enough for a base to move underneath a customer many times, and nothing in the sandbox has an opinion about what the code inside it meant, so the mass keeps migrating upward until the plugins are the system and the base is a footnote.
So we are using one word for two boundaries. Bounded there means sandboxed. Bounded here means readable.
A kernel grammar behaves differently. It is bounded, it is typed, and it describes your domain rather than the runtime. Generation happens inside it. So your system can refuse a request, and it can say which rule the request broke.
Linux has shipped this shape for years. An eBPF program has to be written in a bounded instruction set, a verifier refuses it before it can load, and the kernel enforces everything after that.⁹
Google sorts generative UI by what the model may hand back. At one end it picks from components you built. At the other it writes raw HTML. The option they rate as balanced is neither: the model returns a description of an interface against a schema, and your system decides what to build from it.¹⁰
That is the same instinct, one floor up. They are protecting the rendering. A kernel grammar protects the domain.
Refusals are the roadmap
The kernel grammar gives you a second thing at no extra cost. Say it cannot express what your customer wants. Today that is a dead end and a support ticket. It should be the most valuable event in your system.
Not every refusal, though. Two kinds hide behind the word. One is about the person: this user may not read that column. That is access control, every product already has it, and the fix is to ask an administrator. The other is about the product: there is no way to say this at all, and nobody's permissions would change that.
Only the second kind tells you anything about what to build next, and it is the kind most systems never record. Without a kernel grammar there is usually nothing to record. Nothing refuses, because nothing was ever said in terms your system could check. The request dissolves in a conversation with an assistant, and reaches you, if it reaches you at all, as a paraphrase in a support ticket.
A product refusal carries more information than a feature request, because the ambiguity is already gone. Your customer did not describe a wish in product language and leave you to interpret it. They tried to say something exact, in your words, and your words ran out. Your system knows which primitive was missing, and what they were reaching for when they hit the wall.
Now multiply that by every customer you have.
The shape of your next platform version stops being a guess assembled from interviews and win/loss calls. It becomes an ordered list: which primitive is missing, and how many times somebody hit the wall where it should have been. Ten thousand dead ends turn into product research that nobody had to schedule.
It is also blind in one direction, permanently. The log only hears from people who tried to say something. Somebody who opens your product, cannot imagine a sentence worth attempting, and closes it again never generates a refusal at all. No signal built out of usage can find that person, because there is no usage. The refusal log included.
This is also the one job in the system where an assistant clearly belongs. Refusals arrive already structured, because the grammar is finite. What you are allowed to collect from one is a separate question, and the answer depends on how far your customer got before your words ran out.
Sometimes the word exists and does not apply. Somebody asked to shade a view by a field that has no shade rule, or to group by a column that cannot group. That failure is made entirely of words you already own, so what you collect is a count over a fixed list.
Sometimes there is no word anywhere near it. They wanted a rolling median and your grammar has never heard of one. Nothing on your list was reached for, so the count records nothing. What you get instead is the model saying what it could not express: an operation that turns a series of numbers into one number over a moving window. That is the gap, described in your own types.
Either way the input is your own word list, plus a description of what was missing. So none of this has to touch what your customers are doing, and it does not have to be a quarterly report. A model can sit on that stream permanently: which words get used, which get reached for and are not there, which have quietly stopped being used at all.
What it does with that is propose. It can cluster the refusals and name the primitive that would clear the most of them. It should not add that primitive. Every word you publish is a promise to keep that word working forever, and accepting the promise is a decision with somebody's name on it.
There is an honest complaint about bounding anything. Ink & Switch made it about plugin surfaces: when no surface exists for the customization a user wants, that user is simply out of luck.¹ A fixed word list fails in the same shape, and this is the part of the answer to it. The customer who hit the wall is still stuck this quarter. But they are not stuck structurally, because their failure is the input that grows the grammar.
Your word list can only get longer
Run that loop for a few years and it turns on you.
Every refusal argues for one more word. Every word you accept is a promise you cannot take back. Nothing in the system pushes the other way, so the list has exactly one direction it can move.
Four hundred words in, you have rebuilt the thing you were escaping. A vocabulary nobody can hold in their head is not legible, and a legible middle layer was the whole point of having one.
Saarinen's objection comes back here in its strongest form, and it is stronger than the version he made. A small fixed grammar answers him, because it is the same short list on every account. A large one stops answering him, because now the model is parsing four hundred words to compose five.
The third rule looks like what built the trap. It is also the way out of it.
The promise you made is about what the word means. It says nothing about what your kernel does to deliver that meaning. So a word that has been superseded does not have to keep its own machinery forever. Redefine it over the word that replaced it, and it goes on meaning exactly what it always meant while your kernel stops carrying a second route to get there.
permit_cleared can end its life as one line over a newer word about permit state, and every capability that used it goes on working without anyone touching it.
That shrinks what you maintain. It does not shrink what the model reads, because the word is still published and still composable.
Retiring it for good is the further step, and you already have the mechanism. A model rewrites every capability that used the old word, and you can check the rewrite, because both sides are in your grammar.
What that step asks is who owns the upkeep. If you do, you have the right to make the edit, and making it is the thing your customer is paying for. If you do not, the view goes back to being an empty rectangle, and the person who built it deals with that.
Most of this architecture exists to make that question rare. Nothing makes it go away.
The same log tells you when to ask it. It records the words nobody composes any more. A word that has gone a year without being used is the candidate, and nothing else in your system knows that.
Designing the grammar is a week of work. Defending it is the rest of the product's life.
Everything changes when a second person touches it
None of that needed a second person. The migration, the refusal log, the word list that only grows, all of it is already true when the tool has exactly one user. The moment a colleague asks for a copy, a different problem starts.
That r/softwarearchitecture post has one sentence in it that does more work than the rest. Keeping your host code private may protect your IP, "but it is not a security boundary. The security boundary is what an extension can read, write, call, and consume."⁶
That is why the grammar has to name capabilities and not only shapes. A word that means "chart" tells your kernel nothing. A word that means "read the salary column" tells it everything it needs to know.
The kernel is what acts on that. It answers whether this generated thing may read the salary column, call an outside endpoint, spend money, or see rows belonging to somebody else. And it answers while the thing is running, not in a linter beforehand.
The move from private to shared is where malleable software becomes a product or becomes an incident. A capability always runs with somebody's permissions. While it has one user those are the author's own, so a script that reads everything the author can read is harmless.
Copy it to ten colleagues and one of two things happens. Either it runs with each new person's permissions, so it quietly shows them less than it showed the author and the team stops agreeing on the numbers. Or it carries the author's access with it, and ten people are now reading a salary column nobody granted them.
The second one is a data breach with a friendly name and your logo on it. Neither one is a bug in the generated capability. Both are yours.
Constraint is what makes improvisation legible
There is an older version of this argument, and it is not about software.
A musician improvising over a jazz standard can play almost anything. Going outside the chord changes is a real move with a name, and the good ones do it on purpose. What nobody touches is the form underneath: which tune it is, how many bars, where the top is. Everyone in the room is on bar seventeen of the same thirty-two, and a player who drops a bar is not being expressive. They are lost, and the band stops.
Because the form holds still, a player who has never met the band can walk in, hear the name of the tune and a count-in, and play. A year later somebody can transcribe that solo and follow it, because the form says what each note was answering.
Take the form away and you have not added freedom. You have removed the thing that made the freedom mean anything to anyone else. A solo with no form under it is notes in a row.
An agent that can generate anything is a soloist with no tune underneath. Astonishing for a minute. Impossible to join.
Features got cheap. Rules did not.
What people pay for is relief from owning the upkeep. That is the part that survives when anyone can build anything, and this is what the upkeep turns out to be.
When AI can generate the feature, the feature stops carrying the product. What stays expensive is the part that has to be right for everybody at once: the invariants, the permission model, the migration when the kernel moves under ten thousand private capabilities, the arbitration when two of them want the same thing, the decision about what your grammar may say next, and the answer to who is accountable when a generated capability does something costly at 2am.
That list has a dull name. Governance. Generation did not touch any of it. Every item on the list needs a decision that survives contact with every customer you have, and a person who answers for that decision. So it gets more valuable as generation gets cheaper. It is the only thing standing between a million private capabilities and a system nobody can use.
Two items on that list are what you are selling. The first is the migration, and it is only possible if the kernel grammar was built under all three rules from the beginning. You cannot add them afterwards. The second is the decision about what the grammar may say next, and what it will never say. The refusal log is what makes that a decision instead of a guess. Without it a bounded grammar is only a cage.
Those two are not byproducts of the architecture. They are the reason to choose it.
So what you sell is the conditions under which something new can exist without breaking what already does. Vendors will keep selling features, and they have to. Nobody buys a kernel on its own. Features are how a customer reaches one, and how anyone can tell what it is for. But the features are the surface now. The kernel is the product.
Which leaves you with one question for your next architecture review. Not what your customers can build.
What can you still change underneath them after they build it?
Rabbit Hole
- Static UI Isn't Legacy. It's Institutional Memory You Can Click. (Free tool included): Decide which parts of your interface must hold still before you let anyone reshape the rest.
- Can AI Fix SaaS?: See why shared definitions, not endpoints, are what let separate tools actually compose.
See this concept in another subject
- Satisfied Customers Have Nothing to Say: Your support queue is a refusal log with a blind spot: the people who said nothing.
Footnotes
- Ink & Switch, "Malleable software: Restoring user agency in a world of locked-down apps", 5 June 2025. https://www.inkandswitch.com/essay/malleable-software/ The essay that gave this conversation its vocabulary. The out-of-luck line is theirs, and they make it about plugin surfaces: "If there's not a plugin surface available for a given customization, the user is out of luck." Its own out-of-scope list is privacy and security, business models including how support and maintenance get paid for, and culture.
- Christopher Scaffidi, Mary Shaw and Brad Myers, "Estimating the Numbers of End Users and End User Programmers", IEEE Symposium on Visual Languages and Human-Centric Computing, 2005, pages 207 to 214. http://www.cs.cmu.edu/~Compose/eu_20050923_vlhcc.pdf The 2001 figures used here are measured rather than projected, drawn from Current Population Survey data. Two different bases sit behind them. Over 45 million American workplace users of spreadsheets or databases, of whom 20 percent said they also did programming. And, across all American workplace end users, around 15 percent said they did programming, about 11 million people, against 2.0 to 2.5 million professional programmers between 2000 and 2005. The paper's headline numbers, 90 million end users and over 13 million self-described programmers, are projections to 2012. The authors offer the same reading of the gap: many end users do write programs "but they prefer not to use the term 'programming' to describe their own activities".
- Karri Saarinen, "The Malleable Software That Never Was", LinkedIn, 20 March 2026. https://www.linkedin.com/pulse/malleable-software-never-karri-saarinen-me7gc The post opens by calling the skepticism long-standing, "For a long time, I've been skeptical of malleable software", which makes it a considered position rather than a reaction to agents. Every quotation in that section is from this post.
- Ink & Switch reached the migration problem first, from the other side. Project Cambria decouples the schema data is written in from the schema it is read in, proposing "an isolated software layer that translates data between schemas on demand" instead of one-time migrations: "Project Cambria: Translate your data with lenses", Ink & Switch. https://www.inkandswitch.com/cambria/ That work translates data between tools that disagree about schema. This section is about translating capabilities when one kernel moves underneath them. They also name their own limit: "we do not pretend to have delivered a fully formed, production-quality solution."
- "Challenge problem: Fearless extensibility", forum.malleable.systems, 17 July 2024. https://forum.malleable.systems/t/challenge-problem-fearless-extensibility/205 The invariants line is from akkartik, 18 July 2024, who adds that a principled set of extension points will create a force against new features, "which seems like a good thing". A follow-on thread works at the mechanism itself with object capabilities and effect handlers, and is worth reading for how hard it turns out to be: https://forum.malleable.systems/t/fearless-extensibility-capabilities-and-effects/262
- u/Consistent-Issue1766, "Could everyday apps become stable cores with AI-authored extensions?", r/softwarearchitecture, 3 August 2026. https://www.reddit.com/r/softwarearchitecture/comments/1ve1wb0/could_everyday_apps_become_stable_cores_with/ Score zero, one comment, which is the author's own reading list. The escalation-path position is stated there as "Arbitrary JavaScript should be an escalation path, not the universal intermediate representation."
- Hacker News discussion of "Malleable software = solid bases + custom code", 31 August 2026, 111 points. https://news.ycombinator.com/item?id=49508059 The "library ecosystem" line is from oulipo. The 10x-plugin-code line is from mickael-kerjean, who maintains Filestash and is describing that project.
- Michael Dubakov, "Malleable software = solid bases + custom code", 18 August 2026. https://www.mdubakov.me/malleable-software-solid-bases-custom-code/ Source of both conditions on custom code and of the closing advice about choosing a base, where "data, history and permissions accumulate and are relatively hard to re-pick in two years". Dubakov opens by noting that his own 2019 bet on the no-code revolution "aged so-so", which is a more honest starting line than most of this genre gets.
- "What is eBPF? An Introduction and Deep Dive into the eBPF Technology", ebpf.io. https://ebpf.io/what-is-ebpf/ Verification runs before the program is allowed to attach, and the bounded part is blunt: "eBPF programs cannot call into arbitrary kernel functions." The same page draws the line this article's second rule depends on: the verifier "is meant as a safety tool", not "a security tool inspecting what the programs are doing".
- "Generative UI", Google Cloud. https://cloud.google.com/discover/generative-ui A vendor page, cited as one published statement of a spectrum that is not specific to that vendor.