The case study
It looked done.
A scheduling system. Six presets. A selection screen with icons and descriptions and a frequency badge that appeared in the user's profile. The UI was complete, the interaction was clean, and users could tap any preset and see it reflected in their profile.
Nothing else happened.
No reminder fired. No nudge appeared. No part of the app changed its behavior based on what the user had selected. The label implied a contract. The app never signed it.
The obvious fix wasn't the right fix.
The instinct is immediate: add reminders. Schedule notifications. Collect an email address and send a weekly nudge. It's what every other app does.
This product doesn't have email addresses. By design. The privacy architecture — no accounts, no server, no data transmitted — isn't a gap or a limitation. It's a promise. The community this product is built for is technically sophisticated and privacy-conscious. They chose this product partly because it doesn't ask for anything. Breaking that promise to add a scheduling feature would be a poor trade.
So the question shifted. Not how do we add reminders — but what does accountability look like when you can't contact the user?
That's a more interesting question. And it led somewhere better.
But first — does anyone care?
Before designing a solution, there was a more fundamental question to answer.
Practice scheduling is a Layer 2 feature. It only matters after a user has practiced enough to want to optimize their schedule. At early beta, users are still finding their footing with the core learning loop. They haven't been in the product long enough to feel the absence of scheduling guidance.
Building a complete accountability system for a feature that might be invisible to users is waste. The right move was to find out if it mattered before building anything.
The instrument before the feature.
Diagnosing the silence revealed a second problem. The analytics platform being used silently discards custom event properties on the current subscription tier. An event tracking preset changes was firing correctly — but the data attached to it was being dropped before it could be recorded.
The same was true for five other event types across the product. Input device mode. Practice category. Training level. Content preset. All firing. All dimensionless.
The fix was architectural and economical: encode the dimension directly into the event name.
Instead of passing properties that would be discarded, the dimension becomes part of the name. Two sibling events fire alongside the original. Full signal. No plan upgrade required. Historical continuity preserved.
What the signal will say.
With the instrumentation in place, the data will answer a specific question: are users engaging with the scheduling feature at all?
If the event never fires — if every user stays on the default preset — the feature is invisible. It may need repositioning. It may need to be removed. It may need to surface earlier in the experience.
If it fires consistently, the next question answers itself: which presets do users move to? That distribution reveals which scheduling concepts resonate with real learners — not assumed personas, but the actual people using the product.
The build decision waits for that answer.
The designed solution — ready when the signal arrives.
When the data says build, the implementation is already scoped. The solution is deliberately matched to what the architecture actually supports.
Session logging — after each practice session, the date, duration, and active preset are written to local storage. No server. No PII. Just enough to know when someone last practiced and whether that matches their stated intention.
The on-open nudge — at app launch, the last session date is compared to the preset's frequency expectation. If the user is overdue, a contextual suggestion surfaces. If they're on track, nothing appears. The component that displays this already exists — it just needs the logic layer connected.
PWA browser notifications — the product is a Progressive Web App. The browser's notification system can schedule reminders from the device itself, with no server required. The user opts in. The reminder fires locally. Nothing leaves the device.
What this case study is really about.
There's a version of every product where every feature gets built as soon as it's imagined. This isn't that product.
The scheduling feature existed as UI for months before this work began. It could have been wired to a backend. Reminders could have been added. The feature could have been declared done.
Instead: the instrumentation was built. The signal was defined. The build trigger was written down. And the feature waits — not because it isn't worth building, but because the product deserves to know it's needed before spending the effort.
That discipline — building the instrument before the feature, waiting for behavioral evidence before writing code — is harder than it sounds when you're the person who designed the UI and wants to see it come alive.
It's also the difference between a product that grows with its users and one that accumulates features they never asked for.
When restraint becomes the feature — the product earns the trust to build what users actually need.