Brainless UI, and why agents work better against it
A brainless UI keeps logic out of components. It makes agent-written frontend code reviewable, testable, and much easier to fix.

A brainless UI component takes data in and renders it. It does not decide whether the user can see the discount. It does not compute the tax. It does not know which of five states the subscription is in. Those answers arrive as props or from a hook, already resolved, and the component's only job is to put them on screen. Old idea, and the usual arguments for it still apply. What is new is that it now changes how well an agent can work in your codebase.
Why a brainless UI helps an agent
Watch an agent write a feature in a component that mixes fetching, business rules, and markup. It has to hold all three in mind and it produces code that looks right and quietly duplicates a rule that already exists two directories away. You end up with the discount logic in three places, agreeing today and diverging in November.
When the rule lives in one plain function, the agent finds it. Its own search hits canApplyDiscount before it invents a second one. The component it writes is short enough to review in a glance. And the interesting part gets a unit test, because it is a function taking inputs and returning a value rather than something buried behind a render.
There is a measurement here anyone can run. Take a component over 300 lines with logic inside it, ask for a small change, and count how many lines the diff touches. Do the same with a thin component backed by a separate rules module. The second diff is usually a fraction of the first, and you can read all of it.
What to move out
- Permission and visibility checks.
if (user.role === 'admin' && !org.trialExpired)inside JSX is a bug waiting for a fourth condition. - Currency, date, and unit formatting. One module, tested, shared.
- Derived state. Anything computed from two or more fields belongs in a function with a name.
- Fetching and caching. The component asks for data. It does not know about retries.
- Anything you would need to explain in a code review comment.
The honest limits
Taken too far, this produces a wrapper for a wrapper and a codebase where following one value means opening six files. That is worse, for humans and agents both. The line we use: extract when a rule is used twice, or when it has a name your product people would recognise. Not before.
It also does nothing for the genuinely visual bugs. An agent can write a perfect thin component that overlaps a modal on a narrow viewport. No architecture saves you from looking at the screen. Keep a screenshot step in your review for anything user-facing.
What to do next
Find your largest component. Pull one rule out of it into a named function with a test, and leave the rest. Then ask an agent for a small change to that component and compare the diff to the last time you asked for something similar. If it is smaller and you can read it end to end, do the same to the next four components. Stop when the extraction stops paying for itself.
If you want help putting this into practice, talk to us.
Related training topics
Related research

AI agent boundaries that hold under pressure
A boundary-setting guide to AI agent boundaries: connector cards, scope ledgers, child receipts, and decision stubs that stop permission drift.

Eval platform governance for AI coding teams
A governance memo on eval platform governance: receipts behind scores, scoped harness access, and owners that stop Goodhart drift.

Agent boundaries for teams running coding agents
How to set agent boundaries for teams: connector ownership, written scopes, and review receipts that keep agent diffs explainable after the session ends.