Two Days of Polish in EspressoLytics
I had a free weekend, opened EspressoLytics on my phone, and within ten minutes had a list of things that bugged me. Here is what I fixed.
I had a free weekend, opened EspressoLytics on my phone, and within ten minutes I had a list of things that bugged me. The save button gave zero feedback. Toasts hid behind the keyboard. My grinder goes to 3.5.2 and the field rejected that. Old shots from before I added the notes column just crashed.
None of it was broken enough to stop using the app, but all of it made the app feel rough. So I closed Xcode features and spent two days just fixing the annoying stuff.
Key takeaways
- Trust is built through repeated moments, not one big feature.
- Feedback quality shapes confidence as much as interface aesthetics.
- Reliable data behavior is part of product experience, not just engineering hygiene.
- Small iteration cycles can create outsized user-facing improvements.
Why I stopped building features
I had a whole list of new things I wanted to add — bean ratings, brew comparisons, export to CSV. But every time I used the app to actually log a shot, the little stuff got in the way.
So I made a deal with myself: two days, no new features, just make the existing experience feel solid.
Haptics on save and better toasts
The first thing I tackled was the save button. You would tap it, and... nothing. The shot saved, but there was no physical feedback. I added a light haptic tap on successful save, and immediately the whole interaction felt more real.
Then I fixed the toast messages. They were rendering behind the keyboard, so if you just finished typing a note and hit save, you would never see the confirmation. I moved them above the keyboard frame and shortened the display time so they feel snappy instead of lingering.
Grinder settings that match reality
My wife's Breville grinder uses a notation like 3.5.2 — that is size 3, sub-setting 5, micro-adjustment 2. My original validation only accepted plain numbers, so she could never log the actual setting she used.
I updated the input to accept dotted notation while still rejecting genuinely invalid entries like empty strings or random letters. Small fix, but it meant the app finally matched how we actually use the grinder.
SwiftData migration for older records
This one bit me. I added a notes field to the shot model a few versions back, but older records did not have it. When I tried to load history, the app crashed on those rows.
The fix was straightforward — optional fields with sensible defaults — but it reminded me that data migration is not a nice-to-have. If your old data breaks, users lose trust in the app instantly. I also wrote targeted tests for loading legacy records so this would not sneak back in.
Keyboard and save flow cleanup
I also spent time on smaller interactions: dismissing the keyboard when you tap outside a field, making sure the save button is always reachable without scrolling, and adjusting animation timing so transitions between screens feel quick but not jarring.
Individually these are tiny. Together they made the app feel like it respects your time.
What I took away from this sprint
I used to think momentum meant shipping new capabilities fast. Now I think it means reducing the number of moments where a user hesitates or second-guesses the app.
A good save confirmation or a grinder field that actually accepts your grinder's notation — that stuff matters more than a flashy new feature nobody asked for.
What comes next
Next up I want to improve the history and trend views — better charts, smarter grouping — without adding complexity to the logging flow. The bar is simple: every change should either reduce friction or help you make a better next shot.
If you are building iOS apps and want to swap notes on polish sprints like this, reach out — I am always happy to chat.