The Last Application · Part II — Meaning as Substrate
Chapter 5 — Projection
14 min read
Every enterprise I have ever worked with has a frontend rewrite story, and they are all the same story. It starts in jQuery, because everything started in jQuery. Then someone senior decides the jQuery is unmaintainable — correctly — and the team rebuilds the interface in Angular. A few years later Angular is deemed unmaintainable in turn, and the team rebuilds again in React. Then React's class components give way to hooks, and the team rebuilds within React, and somewhere in there a migration to Next.js happens, and by now the original engineers are gone and the current ones are, once again, reverse-engineering the product's behavior from the screens — because the screens are the only place the behavior was ever fully assembled.
I want to dwell on that phrase, "reverse-engineering the behavior from the screens," because it names the quiet catastrophe at the center of frontend engineering. Each rewrite does not start from a specification of what the interface should do. It starts from the previous interface, which the team squints at, clicks through, and reconstructs — porting not the meaning but the observable behavior, accidents and all. The frontend is the place where product truth goes to die: it is where the meaning of the product finally becomes visible to a user, and precisely there, at the moment of becoming visible, it stops being written down anywhere except in the rendering code itself.
And here is the part that should make an engineer wince. The UI codebase is, by a wide margin, the largest second description of the product ever maintained. Recall the law from chapter 1: second descriptions always lose. The frontend is a second description at industrial scale — hundreds of thousands of lines that describe, in a rendering language, what the product's pages and flows and rules already are somewhere else. It loses the same way every second description loses: the moment a deadline arrives, the change is made where it takes effect, and the alignment between the interface and the meaning it was supposed to express drifts a little further apart, forever. The frontend rewrite is not a technology-choice problem. Angular did not fail you and React will not save you. The frontend rewrite is what it feels like when a second description gets too far out of sync to trust, and the only remedy the paradigm offers is to stop maintaining a second description at all.
The inversion
So stop. That is the whole move, and it is worth stating as plainly as possible before I dress it in mechanism: the interface should not be a codebase that reimplements the product. It should be a projection of the product — a rendering of the graph, generated on demand, the way a browser renders HTML or a database renders the result of a query.
Consider what a browser actually does. You do not maintain a "rendered version" of a web page beside its HTML, syncing the two by hand. You maintain the HTML — the description — and the browser projects it into pixels every time someone loads it. Change the HTML and every future rendering changes with it, because the rendering was never a separate artifact; it was a function of the description, recomputed on demand. There is nothing to drift because there is no second thing. A database does the same trick with queries: you do not store the answer to SELECT * FROM customers WHERE active; you store the customers, and the answer is projected from them each time you ask.
A semantic application applies exactly this discipline to the entire interface. The pages, components, themes, and actions live as governed objects in the product graph — the experience layer from the last chapter. A renderer, which is code and stays code, walks those objects and produces the interface: it reads the page object, follows the edge to its root component, walks the component's children, applies the theme, wires up the actions, and hands back something a user can see and click. Change an object in the graph and every projection of it updates, because the projection is a function of the graph and was never stored separately. The interface cannot drift from the product because the interface is the product, rendered. There is no second description to fall out of sync, which means there is nothing to reverse-engineer in five years, which means there is no rewrite.
I am aware this sounds like a lot to hang on the word "projection," so the rest of the chapter earns it: what a projected application actually contains, the surprising thing that becomes possible when the same meaning can be projected more than one way, the objection every experienced engineer is already forming, and where the interface meets the messy realities of hosting and custom code.
What a projected application contains
Vendor-neutrally, a projected application is built from a small set of typed objects, and the discipline is that all of them are data — none of them is a special case that sneaks back into code. There is navigation (how the user moves between parts of the product), there are pages (the addressable surfaces, each a route bound to a root component), there are components (the composable pieces that make up a page — a section, a stack, a form mount, a button), there are themes (the visual identity — color, type, spacing — as values rather than stylesheets), and there are actions (what happens when the user does something: navigate here, start that process, submit this form). That is a complete enough vocabulary to express a real application, and the important property is that it bottoms out in objects at every level. A page is an object. Its components are objects. Its theme is an object. The action on its button is a typed declaration, not a function.
The worked example names this bundle an Experience — a hostable product surface with navigation, pages, components, themes, and actions, rendered from the graph. Its component vocabulary is deliberately a small set of typed primitives rather than an open field of arbitrary code: a shell for the app chrome, a nav for route links, a section that does one job, a stack for layout, a form_slot that mounts a process by its identifier, a button that triggers an action, and a handful of others. Actions are similarly constrained to a small typed set — navigate, start a run, submit a form, call an API, open the assistant — so that a button's behavior is a piece of readable data rather than a stripe of logic hiding in an event handler. The constraint is not a limitation the platform apologizes for; it is the source of the guarantee. Behavior expressed as a small vocabulary of typed declarations is behavior a machine can read, a policy can gate, and a renderer can project. Behavior expressed as arbitrary code is behavior that has escaped the graph, and the moment it escapes, the second description is back.
Theming deserves its own beat, because it is where the payoff gets concrete and slightly commercial. When the visual identity of a product is data — a set of brand values the renderer reads — then branding a product for a client is a matter of changing values, not forking a codebase. In the worked example, an organization's brand projects as a set of design tokens that the renderer consults, so an agency can ship the same underlying product to a dozen clients, each fully branded, without a dozen forks drifting apart in a dozen repositories. This is a genuinely shipped capability, not a promise: Experiences carry themes as data, and the chrome is driven by the organization's brand values rather than baked into the rendering code. The old way to give ten clients ten looks was ten frontends. The projected way is one product and ten themes, which is the difference between a business that scales and a business that hires.
One meaning, many projections
Here is where projection stops being a tidier way to do the same thing and starts doing something the old architecture simply cannot.
If the interface is a projection of the graph, then nothing stops you from projecting the same underlying meaning in more than one way. Take a process — say, an intake that collects some information, makes a decision, and routes for review. In a semantic application that process is a set of objects in the graph. And those same objects can be projected as a form wizard for a user who wants to fill in fields and click Next; as a conversation for a user who would rather answer questions in natural language; or as an autonomous agent acting on the user's behalf under policy. Three interfaces, one process. The worked example names these interaction kinds — forms, assistant, agents — and treats them explicitly as projections of a single workflow run.
Now contrast that with how the same three interfaces come to exist in the world we are leaving. There, the form is a React component tree. The chat is a separate bot script with its own intent handlers. The agent is a prompt with a fistful of API keys. Three teams, three codebases, three descriptions of what is supposedly one process — and the annex problem from chapter 2 in miniature, because the bot and the agent were built beside the product rather than as views of it. When the process changes — a new question, a new approval step — you change it in three places and hope they stay aligned, which is to say you maintain three second descriptions and watch all three drift. The reason this is impossible to do well is not that the teams are careless. It is that there was never a single thing the three interfaces were views of. There were three separate implementations of an idea that lived only in someone's head.
Projection dissolves that. Because the form, the conversation, and the agent are all renderings of the same process objects, changing the process changes all three at once — not by a synchronization job that might fail, but because there is only one description and three ways of looking at it. Change the journey once; every interface stays aligned. This is the sentence the platform uses, and for once the marketing line is just a description of the mechanism. The process detail — how those workflow objects are actually shaped, what a node is, how a human gate works — belongs to chapter 6, so I will not open it here. The point for this chapter is narrower and, I think, more surprising: the multiplicity of modern interfaces, the thing that makes omnichannel such a maintenance nightmare, turns out to be free once the interface is a projection, because a projection is cheap to make and there is nothing underneath it to duplicate.
"Isn't this just server-driven UI again?"
Every engineer with scar tissue is by now forming the same objection, and it is a good one, so I am going to state it in its strongest form rather than a strawman. We have seen this. Server-driven UI, where the backend sends down a description of the screen and a thin client renders it. Low-code, where you assemble screens from a palette of widgets. XML layouts, model-driven views, the whole lineage of "define the UI as data." None of it ended the rewrite. Why is this different?
The honest answer has two parts, and I will not pretend the resemblance is imaginary — the rendering mechanism really is similar, and I would be suspicious of anyone who claimed otherwise.
The first part is what the projection reads. Server-driven UI and classic low-code render a UI-only model — a description of the screen that exists solely to be a screen. The screen model is its own island: the workflow logic still lives in code somewhere, the business rules live in a rules engine somewhere else, and the UI description is a fourth thing that has to be kept in sync with all of them. It is, in the end, one more second description — a better-organized one, but a second description all the same, and it loses the usual way. A semantic projection reads the same governed objects that the workflows execute and the intelligence reasons over. The form does not render from a private UI model of a process; it renders from the process, the very objects that also run when the form is submitted and that an agent also reads when it acts. There is one description, viewed several ways, not a UI model shadowing a separate implementation. That is the difference between a projection and a picture: a projection is computed from the source of truth, a picture is a copy of it.
The second part is governance, which the whole of Part III is about and which I will only gesture at: because the projected objects are the same governed objects as everything else, a change to the interface goes through the same draft-review-version machinery as a change to a workflow or a policy. Server-driven UI gave you configuration without accountability — a screen description you could change, but not a governed artifact you could review and reverse. The projection is governed because everything on the substrate is governed. That is not a property you can bolt onto a UI-only model after the fact; it comes from the interface being made of the same stuff as the product.
And then the sharper form of the objection, the one that actually matters in practice: what about the five percent of the interface that genuinely needs custom code — the bespoke visualization, the peculiar interaction the primitives cannot express? I promised honesty in the tone rules and here it is: that five percent does not vanish, and pretending it does is how "define the UI as data" platforms have always lied to their users. The answer is not to forbid custom code but to give it a governed home. A custom component exists as a registered, typed citizen of the graph — an object with an identity and a schema, referenced by the pages that use it, its custom rendering supplied by code that the runtime knows about. It is code, and it lives where code lives, exactly as chapter 4 insisted the runtime does. The discipline is that the custom component is declared in the graph even though its guts are code, so the product graph still knows the component exists, still governs where it is used, and still renders around it. The escape hatch is real, and it is fenced. What you may not do is let the escape hatch become the whole building, which is precisely the failure of every low-code tool that started as "assemble screens visually" and ended as "and here is where you write the JavaScript that actually does everything."
Hosting is graph state
One more thing belongs to the interface, and then I will hand off. A projected application still has to be served — it needs domains, it needs environments, it needs a notion of release. In the paradigm, all of that is graph state too.
An application is hosted at domains, in environments — a development environment, a testing one, production — and each of those is data describing where and how the product is served, not a separate deployment pipeline that rebuilds artifacts. In the worked example, custom domains attach per environment (dev., test., and production hostnames resolving to the same graph, with automatic TLS), and a deploy is the promotion of the product's data from one environment to the next, not the rebuilding of a bundle. Say that slowly, because it is the quiet endgame of the whole chapter: when the product is data and the interface is a projection of that data, then shipping a new version is moving a pinned set of objects forward, not compiling and deploying a fresh frontend. There is no build to break because there is nothing to build — the renderer is already running; it just projects the promoted graph.
I am deliberately gesturing rather than explaining, because environments, releases, and the gates between them are chapter 9's subject and it will do them justice. The reason I raise hosting at all here is to close the frontend rewrite story I opened with. The rewrite existed because the interface was an artifact that had to be rebuilt whenever it drifted too far from the truth. Remove the artifact — make the interface a projection and the deploy a promotion — and you have removed the thing that got rewritten. The frontend rewrite story does not get a better ending in this paradigm. It stops being a story anyone has to tell.
The point
The frontend is the largest second description of the product any enterprise maintains, and like every second description it loses — which is what the endless jQuery-to-Angular-to-React rewrite actually is: not a technology problem but a description drifting too far from the truth to trust. The paradigm's answer is projection: the interface is not a codebase that reimplements the product but a rendering of the product graph, generated on demand by a renderer the way a browser renders HTML, so there is nothing to drift because there is no second description. A projected application is navigation, pages, components, themes, and actions — all typed objects, with branding as data — and because the same governed objects can be projected as a form, a conversation, or an autonomous agent, omnichannel becomes nearly free instead of a three-codebase nightmare. This is not server-driven UI revisited, for two reasons: the projection reads the same governed objects the workflows execute and the intelligence reasons over, not a UI-only model shadowing a separate implementation; and it is governed like everything else on the substrate. The escape hatch for genuinely custom UI is real and fenced — custom components are registered, typed citizens whose guts are code the runtime knows about. And hosting is graph state: deploy is promoting the product's data, not rebuilding artifacts. Remove the artifact and you remove the thing that got rewritten.