LLMs: a cause of, and solution to, UI drift
Software has a natural tendency to drift and sprawl, starting with a pristine design but ending up with a hodgepodge of similar but distinct ways of displaying data or accomplishing tasks. Avoiding that drift requires deliberate effort, and fixing it after the fact is hard. Fortunately, an LLM used right is a great tool to reduce drift.
I suspect the design system drift challenge is growing, not shrinking, with LLM-supported engineering. Human-created drift—the kind we addressed here—happens for a few unavoidable reasons:
- Seemingly trivial new components or tweaks get hard-coded, not abstracted
- We overestimate the value of conformity, preferring to craft the perfect new design every time
- Designing components flexible enough to handle the variety of a large product is hard
- Even on a single-developer project, it’s hard to remember every component, so duplication is inevitable
Combatting this is laziness, if nothing else: when writing code takes time, engineers are inclined toward reuse.
LLMs may not have our human love of craft, but they share our limited context windows, and notably their cost to produce code is effectively zero—so the impulse toward laziness that causes us not to write more code than necessary is missing.
The first step is admitting you have a problem
Accumulating UI cruft is like gaining weight: it happens so slowly at first that you may not realize it’s happening, and by the time it’s impossible to deny, it’s also hard to undo. Bit by bit we added similar components, telling ourselves that each was different enough to deserve its own special treatment, or maybe just convincing ourselves it was okay to hard-code one more component rather than do the more difficult work of creating a global abstraction.
That’s how we ended up here:
| A person on an email thread | DHDiane Halsted |
| A removable person chip | DHDiane Halsted |
| A teammate in the activity feed | ACAlex Chen |
| A contact in the activity feed | DHDiane Halsted |
| A team in the activity feed | Leadership |
| What a task is linked to | Halsted Law Group |
| A milestone on a task | Q3 launch |
| How many threads a file is in | 3 threads |
| Who sent a file (list view) | Alex Chen |
| Who sent a file (grid view) | Alex Chen |
In a vacuum, none of these is bad. Even taken together, they’re not terrible: these (mostly) read as being members of the same family, and some of the places they diverge help them fit into the surrounding interface. But the net effect of these and similar inconsistencies is a feeling of inconsistency or even carelessness, along with more complexity for engineers who have to decide which approach to take for the next similar instance.
Standardizing our entity tokens across the UI
Before LLMs, standardizing these approaches would have been a straightforward but slow task. LLMs (in this case, Opus 4.8 in Claude Code) can streamline that work, but they can’t yet independently deliver something that meets our standards. Here’s the approach that worked:
Catalog—exhaustively—before touching anything. We had the LLM review every surface for any component that could plausibly be migrated to a new standard entity token. Guardrails that impose structure and process are helpful to make sure the search doesn’t miss anything—spin up an agent per module, outline the types of UI elements that might qualify, and push for false positives over false negatives. If the LLM doesn’t propose anything that you reject as not fitting the pattern, there’s a good chance you haven’t scanned extensively enough.
Collect your artifacts. We’ve found one of the best ways to make the LLM “thoughtful” is to build a styleguide in collaboration with our AI agents. It lets us spend time polishing one small surface until it shines rather than optimize for rapid changes. We wrote every one of our disparate token components to the styleguide, mapped it to a common alternative, and had a single place to assess the changes as we made them.
Think siblings, not identical twins. You don’t need (and shouldn’t want) to drive all differences out of your components. We’re striving for a clear design family—an unmistakable sense that everything we show is part of one harmonious interface, not that we are building a UI with the smallest possible set of tools.
After quite a bit of iteration, here’s what we ended up with in our styleguide:
| Before | After | |
| A person on an email thread | DHDiane Halsted | DHDiane Halsted |
| A removable person chip | DHDiane Halsted | DHDiane Halsted |
| A teammate in the activity feed | ACAlex Chen | ACAlex Chen |
| A contact in the activity feed | DHDiane Halsted | DHDiane Halsted |
| A team in the activity feed | Leadership | Leadership |
| What a task is linked to | Halsted Law Group | Halsted Law Group |
| A milestone on a task | Q3 launch | Q3 launch |
| How many threads a file is in | 3 threads | 3 threads |
| Who sent a file (list view) | Alex Chen | ACAlex Chen |
| Who sent a file (grid view) | Alex Chen | ACAlex Chen |
| Full-width list rows | ||
| A file in a thread sidebar | Bankshot MSA — Arcwell Health.pdf | Bankshot MSA — Arcwell Health.pdf |
| A person in a thread sidebar | DH Diane HalstedPartner · Halsted Law Group | DH Diane HalstedPartner · Halsted Law Group |
| A contact in a list | AC Alex ChenCEO · Bankshot | |
| A recent file on a contact | Pricing model.xlsxArcwell Health MSA thread | Pricing model.xlsxArcwell Health MSA thread |
| A group you have shared with | Leadership 3 members | Leadership3 members |
| A person you have shared with | DH Diane HalstedCan edit | DH Diane HalstedCan edit |
The set of tokens isn’t the end of the process, though. Applying these tokens is easy, mechanically, with an LLM—but doing it right isn’t a rote task.
To that end, don’t be afraid of some (thoughtful) inconsistency. When you standardize components, you raise the floor but can potentially lower the interface design ceiling: nothing is bad, but perhaps not enough delight. Our rule is that when something is different enough from our standard use cases, and we’re able to dedicate the attention to make it sing, we can break out of the token library. So be inconsistent on occasion, so long as the inconsistency makes things clearly better.
Evaluate each element in its own place. The styleguide catalog is essential, but it inherently lacks context. Once we’re happy with our tokens in isolation, we have the LLM apply them across the application. Then we review every one in its own setting, and often we find it doesn’t work. For instance, in file rows we have text below the attachment name that says From [sender] in [thread], using tokens for both entities. In practice, this is clunky to read—instead of reading the sentence, my brain just sees two disconnected tokens. We decided to make the sender plain text, so we have more readable text that doesn’t quite match the global pattern. It’s worth it.
Leave things better than you found them. All software has dark corners that haven’t garnered attention. When you find yourselves in them, spend a few extra minutes tidying up. We did this with file chips: instead of showing a generic icon, we now show a color-coded icon specific to each file type, which matches a few other places in the application. Not necessary—and more work—but a nice improvement to something that otherwise wouldn’t have gotten a second thought.