Designing a rules engine humans can read
Expert users, an immovable third-party core, and a design that survived losing its favorite idea.
Insurance runs on rules. Not metaphorical rules, literal ones: if the risk score is C and the burglary subscore is F, order an inspection. If three or more losses hit a policy, send it to review. At this company those rules governed real money on real policies, and they lived in the worst place rules can live: spreadsheets, memory, and personal tracking files.
No dates. No version history. No record of who changed a threshold or why. Changing a rule meant filing a ticket so an engineer could edit code. The experts who owned the rules were the only people in the company who couldn’t touch them.
This is the story of designing the tool that fixed that, and of the moment, about a week in, when I had to give up the design I wanted in order to build the design the system could actually hold.
Layers, not lists
Discovery came first, in the open. My kickoff recap went to the project channel on day one, and the structural insight that shaped everything came out of it: rules aren’t a list, they’re a hierarchy. Rule sets, by carrier and program, contain rule groups, which contain individual rules, because rules share inputs. A replacement-cost threshold, a state and an occupancy type together trigger a decline. You can’t edit that row by row, only at the group level.
The underwriting lead confirmed the mental model in review: underwriters think in layers. They don’t want a thousand-row list with a search bar. They want to click into loss reviews, then the carrier, and see only what’s relevant. The tracking spreadsheets we were replacing were already organized that way. The shadow system is always the real spec.
Two disciplines from week one paid off later: IA before UI, so hierarchy, history and navigation were settled before any visual design, and no high-fidelity work on the editing screen until engineering confirmed what the editor could actually support. That second one turned out to be the whole story.
The dream, and the wall
What I wanted to build was obvious: a rule builder that speaks the user’s language. Pick the rule type from a dropdown. Name the levers. Set the bounds. Let an underwriter say “inspect anything with a risk score worse than C” and let the computer do the computing.
The wall: the rules live in a third-party engine. Its editing table is rigid, its expression syntax is its own, and putting a friendly translation layer between the user and that table means building middleware, a whole application, that the project could not staff. I pushed on this hard, on a recorded call, from several directions. Can’t we skin it? Map the fields to entry boxes? The quote flow already turns human answers into table rows, so why is this different? The answer held every time: because that would make us the engine’s maintainer, forever.
What I said when it finally landed:
“I’ll stop fighting you on this eventually. I just want to understand.”
That sentence turned out to be the design method. Fighting the constraint produced nothing. Understanding it produced the entire map of what was still mine: everything before the table, everything after it, and everything written down about it. The table was maybe five percent of the experience. I’d been trying to redesign the five percent.
The cut list
The IA came together in working sessions through early August, and what I’m proudest of isn’t what we designed. It’s what we deleted. One session recorded twelve decisions, and seven of them were removals.
First the swap: the initial selection screen gave way to filters for carrier, line of business and type, which match how underwriters actually narrow. Then the cuts. The search bar: in a layered hierarchy with filters, search was a crutch for an IA that no longer needed one. The “New Rule” button: underwriters overwhelmingly adjust existing groups, and adding rows to a group is the real job. The group count in the top bar, the status column on the active view, the approver’s name on the active view, and the pipeline graphic, cut down to a single status pill.
And the deletion I’d defend in front of anyone: the “Approved” status itself. The workflow draft had rules passing from Review to Approved to Active. But a rule that’s approved and not active is a state in which nothing is true. It isn’t running, isn’t being examined, isn’t doing anything but making a diagram look thorough. In a system being rebuilt specifically so that every rule change is traceable, the last thing it needed was a status that exists for the org chart instead of the record. Rules go Review → Active — and every waiting state that survived (In QA, Ready for approval) names a person’s queue. ‘Approved’ named nobody’s: work finished, nothing running, accountability nowhere. A named approver is captured at the moment of the edit, where accountability actually lives, and the interface tells no polite lies in between.
The accountability graft
The core table couldn’t change, so the record around it did. Every rule change now carries an effective date, a creator, a named approver and a description: a plain-language note of what changed and why, written for the next reviewer, not the computer. A versioning indicator shows that a group changed, and the design shows the diff, not the archive: what changed between versions, instead of asking an expert to re-read a thicket of boolean logic and spot the difference. QA reviews first, then approval, in a sequence agreed with the people doing the reviewing.
None of this required touching the engine. All of it was missing from the engine. That’s the quiet thesis of the whole project: in a constrained system, design’s job is the path people walk to reach the core, and the truth of the record wrapped around it.
Testing the rules themselves
The last design surface wasn’t a screen for editing rules. It was a surface for doubting them. With the engineering lead, we designed a test panel for the rules table, four states deep: give it inputs, state the outcome you expect, and check every change against real-shaped cases before formal QA. Regression testing for underwriting logic.
Two details carry the idea. Every changed row shows whether any test reaches it, so a row nothing triggers is flagged before it ships. And the expected result doesn’t have to come from memory: pick what should happen, or run it and accept what does. What the rules actually do becomes the expected value, and the next change gets checked against it. If you’ve read the Method page, you’ll recognize the reflex: a check you haven’t watched fail isn’t a check. It applies to insurance logic exactly as hard as it applies to code.
The ending I didn’t write
The designs went into build the way everything I make ships now: as working code with an extraction manifest, through the delivery system in Part One. The short version of what happened next is told fully in Part Three. An implementation engineer I’d barely worked with picked the materials up cold on a Tuesday morning and posted a matching review build on Wednesday.
“between that and the figma I was able to pretty much exactly match the design”
The design survived translation to a different component stack than the prototype assumed. That’s the proof that matters for this story: what transferred wasn’t pixels. It was the IA, the vocabulary and the intent. The parts of the design that were actually the design.
Where it stands
Honestly: in build now, with the feedback loop running. The implementing engineer has a standing request from me for the useful-versus-useless verdict on every artifact, and the answers go back into the process docs. The usage story, what changes for the underwriting team when the spreadsheets finally die, is being written as you read this. One open question from the IA sessions is still open, on purpose, waiting for real usage to answer it: whether reviewers need past versions visible, or whether the diff is enough. Designing for experts means knowing which questions you don’t get to answer at your desk.
Discovery through IA ran Jul 15 – Aug 5, 2026. The build transfer was verified by message timestamps, Oct 27–28, 2026. Every rule example on this page is invented, with production-true shapes. The quoted line appears with the engineer’s consent, attributed by role.
Building for expert users inside a system you can’t change? Email me.