By the time I finished reading the pull request, it had already been merged.
The author had asked me to review it. I was maybe forty minutes in, doing the slow thing: opening the files it touched, then opening the files it didn’t touch, trying to build a picture of what this change actually did to the app. Somewhere in the middle of that, two other engineers approved it, auto-merge fired, and eight commits squashed themselves onto master.
I spent an hour every morning trying to keep up with AI tooling — and I was still behind.
Not behind in the casual, “oh I haven’t read that yet” sense. Behind in the way that matters at work. A colleague would mention an MCP update that shipped last week and I’d nod along, quietly Googling it under the table. Someone would demo a workflow pattern I should have known about, and I’d realize I’d scrolled past the announcement three days ago without registering it.
I spent three days debugging a bug I found in 20 minutes.
The bug was simple: Fragment tracking wasn’t firing correctly in ViewPagers. The fix was obvious once I located it—a timing issue with bytecode injection. But between finding it and feeling confident enough to fix it? Three days of reading through a monolithic file where everything depended on everything else.
That file was the core of our analytics library. It did everything: scanned for annotations, detected class types, chose instrumentation strategies, injected bytecode, logged debug info, handled errors.
The pull request was routine—merging some changes I’d already tested thoroughly. Then Codacy’s coverage gate blocked me: “Insufficient coverage on modified paths.”
I stared at the failure message. Those paths were covered. I’d written the tests myself. Watched them pass. Green checkmarks everywhere except this one gate, claiming I’d forgotten to test code that I knew I’d tested.
Fine. I’ll prove it. I ran the coverage report locally. ./gradlew jacocoTestReport and waited for the HTML output, already composing my “actually, the CI is wrong” Slack message in my head.
We’ve all been there. You’re deep in the flow, building that perfect user experience, when suddenly you remember: “Oh right, I need to track this screen.” Then comes the familiar ritual—add the analytics call, remember the lifecycle method, serialize the parameters, and pray you didn’t miss any edge cases. Rinse and repeat for every single screen and action in your app.
What if I told you there was a better way?