Notes on shipping a permission prompt
Four rewrites, one dialog, and the part nobody sees. Notes from a change that looked like two lines of CSS and wasn't.
The prompt had been there for years, and nobody liked it. Not the people who had to read it, not the people who had to explain it, and definitely not the team that owned the string. What made it hard wasn’t the design. It was that four different subsystems each believed they were the one deciding when it appeared.1
I started by drawing the call graph on paper, which is the only reliable way I know to find out how much of a system I actually understand. It took three attempts. The third one fit on a page.
The version that fit on one page. Two of these boxes turned out to be the same box.
Deleting the second owner
Once the graph was on paper the fix was obvious and boring: one owner for the decision, everyone else asks it. The interesting part was the shim, which had to keep the old callers working while they migrated.
// one owner, everyone else asks
function shouldPrompt(origin, capability) {
const decision = policy.evaluate(origin, capability);
if (decision.cached) return false;
return decision.requiresUser;
}
A prompt is a confession that the system couldn’t decide on its own.
The numbers afterwards were less dramatic than I’d hoped and more useful than I expected. Fewer prompts, but the ones that remained got read.
| Metric | Before | After |
|---|---|---|
| Prompts per session | 3.4 | 0.9 |
| Dismissed without reading | 61% | 24% |
| Owners of the decision | 4 | 1 |
If there’s a lesson it’s the one I keep relearning: the work is almost never in the thing you can see.
Footnotes
-
Five, if you count the one that only ran on Tuesdays. ↩