Skip to content
Jessica Mumby

· 5 min read

Your product is defined by unhappy paths

product

I'm always cooking up a new idea in the spare evening or two I have during the week.

This week I spent almost all my build time on 'When Is Bin', a UK bin day reminder app, and nearly all of it went on the unhappy paths. I didn't touch the happy path, where you type a postcode, get your dates and get a reminder. I worked on what happens when a council is slow to respond, when the API says no, and when notifications aren't triggered. My conclusion is that for a product like this, the unhappy path is more important than the happy path.

I've been having a great time shipping apps - Scroll Books especially - but waiting for App Store reviews is no fun at all. If you'd like to test out When Is Bin, reach out!

A bin app is mostly waiting

When Is Bin reads collection dates from the WhenIsBins API, a free service created by Tom Loosemore. Some councils answer instantly from a cache, others need the server to go off and look your address up on the council's own site, which can take a little while.

My first version treated that wait as a spinner, but that was hiding a bug. The server holds its "wait for this lookup" request open for about 25 seconds, but my app gave every request a 15 second deadline, so every lookup that wasn't cached failed.

Fixing the timeout was the easy bit. The more interesting fix was realising that a spinner on its own says nothing to a user. The loading screen now shows the council's own estimate for your postcode, the stage the lookup has reached, and how many lookups are ahead of yours. Waiting is fine. Waiting without being told anything is how people end up deleting the app.

A failed lookup shouldn't be a dead end

The API is good about telling you why something failed. However, the app was throwing almost all of that away and collapsing every failure into one generic error. The API had already said "address not found, but here are the addresses the council did list, and here's the council's own page", and all the user saw was "something went wrong".

Now a failed lookup gives users a way forward. The council's own page comes first. The addresses it offered can be picked without retyping the form. Where a council allows it, you can choose to see a neighbouring property's dates while yours are checked, which gives users an answer straight away.

That last option needed the most careful product decision of the week. A neighbour's dates are probably right, but "probably" isn't good enough long term. If the app saved them as yours, you could be reminded on the wrong day indefinitely and never know why. So provisional dates are shown with a clear label, "Provisional: your address is still being checked", and nothing is saved and no reminder is set.

Say it in the user's terms

When the API throttles requests, its error message talks about network-address allowances and wait tokens. That's the server's internal accounting, not the user's problem. On a mobile network lots of phones can share one address, so you can be throttled through no fault of your own.

The app now says: "Lots of people are checking bin days right now. Try again in a minute." If the server says how long to wait, you get a real countdown. If it doesn't, you get no countdown at all, because a made-up number is worse than no number.

Much of this came from feedback on how I was using the API. It's fantastic to have someone who knows the API read my integration and find the things I'd assumed were fine.

The bug that only existed in the build I shipped

Then there was onboarding. During the quick onboarding flow, in the 'set reminder' screen the app asks for permission to send notifications and schedules your reminders. It froze there, and it took three releases in three days to fix it properly. The first fix was an Android manifest setting that dropped the answer to the permission dialog. The second was a scheduling call that could hang forever, so onboarding waited on it forever. The third only happened in release builds. Android's code shrinker was stripping something the notifications plugin needed, so in the build people actually install, no reminder was ever saved! Debug builds aren't shrunk, which is why I never saw it on my own phone before submission.

The fix for all three came down to one principle: a failure in something optional must never block the thing that's essential. Onboarding now always finishes. If reminders can't be set up, they're off, and you can turn them on in Settings.

What I'd tell another builder

Write the error states before the success state. Know what every failure says to the user before you find out what it says to you. Test the build you actually ship, not the one on your desk.

The happy path is what you demo. The unhappy path is what people actually use.