Back to Research

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.

On the Mediterranean-Hermitage Museum, landscape painting by Pierre Bonnard.
Rogier MullerAugust 15, 20263 min read

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

Ready to start?

Transform how your team builds software.

Book a 15-minute sync