Cadence: Building a Second Android App, and What I Changed
My first Android app was built to solve a problem only I had: monitoring my own infrastructure from my pocket. My second one was built to solve a problem someone else had, which turns out to be a very different kind of pressure.
Cadence is an interval timer and reminder app, the kind of thing that nags you to stretch, take medication, or do a repeating task on a schedule, with proper quiet hours and a notification that won’t let itself be ignored. I built it for someone close to me who needed something more reliable than a generic timer app, and who, critically, would actually be using it every single day, not just admiring it from a developer’s chair.
That distinction changed how I worked.
Getting the foundation right before the features
The first version went from nothing to a working app in one sitting: Kotlin, Jetpack Compose, Material 3, a clean dark theme, per-timer sound selection with its own notification channel, and a nag-until-acknowledged alarm loop rather than a single easily-dismissed ping. It took seven build iterations to get right. Android’s notification and alarm APIs are unforgiving of small mistakes, and I hit several: the wrong audio attribute usage meant custom sounds silently ignored the phone’s alarm volume, a dialog ordering bug in the Compose tree, missing icon sizes, one icon accidentally doing double duty as both a notification glyph and a UI element.
None of those are exotic bugs. They’re the ordinary friction of shipping something real, and the only way through them is iterating against an actual device with an actual person waiting to use it, not a simulator, not a demo build.
Once the first version was in daily use, I made a decision I’d skipped the first time round: migrate off SharedPreferences and onto a proper Room database, with a one-time silent migration so nobody’s existing timers vanished in the process. It’s more setup work up front, but it’s the difference between “the app that mostly works” and “the app I trust to wake someone up for a medication reminder.”
The discipline part
Around this point I introduced a new discipline: every non-trivial change would go through an independent second review before it ships, not just my own read of the diff, but a genuinely separate pass by another reviewer, blind to my reasoning, specifically looking for what I might have missed. It sounds like overhead for a personal project. It caught real things: a race condition where acknowledging a reminder and the alarm system re-triggering it could interleave in the wrong order and silently un-acknowledge it; notification IDs colliding between timers in ways that only showed up with enough concurrent alarms.
I violated my own process once, mid-build, shipping a UX change before the review step. The process caught it anyway, not because anyone happened to notice something felt off, but because the review discipline itself flagged the gap: a retroactive review ran, found nothing critical wrong with the change itself, but formally logged the violation for what it was. That’s a better outcome than it sounds. A process that only works when everyone remembers to follow it isn’t a process, it’s a hope. This one caught its own bypass after the fact, which is closer to what “process” is supposed to mean.
What real usage actually surfaces
The gap between “works when I test it” and “works when someone else lives with it” showed up fast, and it showed up in ways I wouldn’t have thought to test for:
- The main action button was half-hidden behind the phone’s gesture navigation bar, invisible in the emulator, obvious on a real device with real system chrome.
- Two reminders firing at the same moment produced two overlapping alarm sounds instead of one coherent notification, technically correct, practically alarming.
- Tapping to edit a timer’s schedule simply didn’t work, a gap that only showed up because someone was actually trying to change their routine day to day, not just set it once and leave it.
Every one of those came from a bug report that was really just “hey, this happened,” not a formal QA pass, just someone living with the software and telling me when it didn’t behave. Two weeks and four shipped versions later, all three were fixed, verified against real device behaviour, not assumptions about it.
Why this is the more useful portfolio piece
The technically flashier project is always going to be the one with the novel architecture or the clever algorithm. But an app that one real person relies on daily, that’s been through actual iteration against actual feedback, that has a documented review discipline behind every change, that demonstrates something a demo project can’t: what it looks like to take software seriously when someone’s routine depends on it working.