Connection Trust
A customer connected their directory to a security platform, saw a success message, and found out six weeks later that a whole category of data had never arrived. This is what I changed — told in the order I actually worked it out.
- Users
- Groups
- Applications
- Sign-in logs
- Risk events
Scope of the work — not performance claims
The Problem
Three different failures, one identical screen
A denied permission, a throttled API and a genuinely empty tenant all produced exactly the same thing in the product: nothing. No data appeared, and no screen was willing to say why.
Reviewing a quarter of production issues, setup and validation problems were the largest single theme — and almost none of them were visible at the moment of setup. That reframed the brief before I opened a design file: the failure wasn’t in the connection. It was in the reporting of it.
Production Issue Review
Read a quarter of reported issues to find where customers actually lose trust in the platform.
- Setup and validation failures were the largest theme
- Almost none were visible at the moment of setup
- Most surfaced weeks later, via a missing finding
Customer Conversations
Sat in on calls where the missing data had already caused a problem, to hear how people described it.
- “Connected” was read as “collecting everything”
- Nobody re-checked an integration after setup day
- Blame landed on the platform, not the permission
Engineering Walkthrough
Mapped what the backend can genuinely verify at connect time, and what it can only assume.
- Permission checks resolve independently, not as one call
- Throttling is indistinguishable from denial without retry
- An unreachable tenant can’t return a count of any kind
Am I designing one feature, or three projects wearing a trench coat?
The work arrived as three separate items on the roadmap — a test-connection flow, collection observability, and data quality. My first move wasn’t sketching; it was laying all three against the screens that already existed to see where they overlapped.
They overlapped almost completely. All three were asking the same question from different angles: can the customer tell what this integration is actually bringing in? Treating them as three designs would have produced three vocabularies for one idea. I scoped them as one pattern with three moments — at connect, after connect, and over time.
Why are we building this? It’s already built.
The obvious path was a new screen: a connection health page, somewhere to put all this. I’d started specifying it when I went back through the production UI and found the panel where people already edit an integration — with a Test Connection button sitting right there in it.
The project wasn’t missing a home. It was missing a result.
Pressing that button returned pass or fail. Everything the roadmap was asking for — per-entity outcomes, what’s collected, what isn’t, why — could live inside a panel users already knew how to open. That deleted a screen from the plan, and it meant nobody had to learn a new place to go when something broke.
I hold onto this one because the instinct to add surface is strong and almost always wrong. The audit — what already exists that this belongs inside? — now runs before I design any new page.
What does the system actually know, and what is it only assuming?
Most of the early mistakes in this project were mine, and they were all the same kind: a label that claimed more than the backend could support. Correcting the domain model turned out to be most of the design work.
“Collecting logs” is consent, not health. It means the customer approved log collection — it says nothing about whether logs are arriving. I’d drawn it as a status indicator. Removed it from the health vocabulary entirely.
The scope noun is different for every vendor. Organisations, accounts, tenants, projects, directories, repositories, workspaces — sometimes a count, sometimes a name. A fixed three-column layout would break on the second integration. It became one composed meta line that absorbs whatever the vendor has.
Unreachable is not zero. If we can’t reach the integration, every entity has to read “unknown” — never a zero and never “permitted”. A zero is a claim about their environment we have no right to make.
That last one is the whole project in miniature. Almost every bug in the original flow came from the interface asserting something it hadn’t verified.
What are all the ways this ends?
I wrote the outcomes as a table before opening a design file. Fourteen of them, in four families. Two existed in no spec and no support ticket — they came from asking what happens when the connection works perfectly and there is still nothing there.
- Untested — nothing run yet
- Testing in progress
- Config edited since last test
- Stored result, six weeks old
- Couldn’t connect — credentials rejected
- Couldn’t check — throttled or timed out
- Connected, reads nothing back
- Everything collects
- Partial — a permission is missing
- Optional source denied
- Capability not licensed
- Connected, tenant returns zero objects
- Log store readable but empty
- Log destination blocked or missing
The grouping is the design decision. Reachability problems are ours or the network’s. Entity problems are the customer’s, and get a direct link to fix them. Empty results are nobody’s fault and must never be dressed as an error. Colour follows that logic rather than severity for its own sake.
One practical thing came out of drawing all fourteen at once: I stacked them in a single review frame so the team could compare them side by side — with a note in the file saying only one renders at runtime. Without that note, someone reads the frame cold and builds a panel that shows every outcome at the same time.
Where does the answer actually land on screen?
Fourteen outcomes don’t fit under a button. The Test Connection button sat below every field in a single-column form, which meant its answer rendered below the fold — on the exact screen where it was the only thing the user cared about.
The answer to the only question the user has arrives where they can’t see it.
- Users
- Groups
- Applications
- Sign-in logs
- Will collect
- Won’t collect
- Not licensed
Results panel visible from the first second and primed with what’s about to be checked — never an empty box.
Two smaller calls followed from the same logic.
Connect is gated on a test
“Find out later” was the original defect, so Connect stays disabled until a test has run. The two buttons also do different jobs — one validates the vendor’s credentials, the other validates ours — which keeps it clear which side failed.
Re-testing never wipes the result
Rows resolve in place as each check returns, rather than the panel clearing and repopulating. A failed re-test doesn’t erase the record of what was working ten seconds earlier.
How does this survive after I stop looking at it?
A screen decays. A component gets inherited. So the outcome wasn’t a mockup — it was a page in the shared design system: six entity chip states, five result panel tones, and the rules for when each applies.
- Will collect
- Won’t collect
- Couldn’t check
- Not licensed
- Testing…
- +3
One colour changed late and mattered more than its size suggests. Denied entities were originally pink, borrowed from the critical scale. I retoned them amber and reserved pink for things that genuinely don’t exist — because if the strongest colour in the system shows up on a routine missing permission, it means nothing by the time something is actually broken. A colour scale is a promise.
And when the components wouldn’t nest the way I wanted, I shipped the compromise rather than the ideal: the panel header published as a component, the bodies as editable templates. Live instances never broke, and the team could use the pattern that same week.
Problem → Solution
The whole project, on one screen
Where It Landed
What shipped, and what it replaced
14 designed outcomes shipped
Across three milestones — test, edit and reconnect — including a stale stored result and a gated update.
One whole screen avoided
The result lives in a panel users already knew, instead of a new page nobody would find under pressure.
A success message that lied, replaced
“Connected” now names exactly what is and isn’t arriving, at the moment of setup.
What I’d do differently
I modelled one flagship vendor in full detail first, and it turned out to be the least representative integration in the catalogue — roughly two weeks of rework. The product lead’s instinct was right and mine was wrong: design the generic case first and treat the biggest, most-demanded integration as the special case it usually is.
All visuals rebuilt with synthetic data. Product and vendor names withheld under NDA.