We built a kill switch before we had a single user
Why a small podcast app shipped with remote maintenance mode and a forced-update gate on day one, and how to add the same safety net without over-engineering it.
The day before we opened Podshot to a small group, I spent my time on a feature none of those people would ever see. I built a way to remotely turn the app off.
That sounds dramatic. It is not. Let me explain why a one-person project with zero users needs an off switch, and how to build one without turning your weekend into a platform engineering project.
The problem with shipped apps
A website is easy to fix. You push a change and everyone has it in seconds. A mobile app is not like that. When you ship a bug to the App Store, that bad version sits on people's phones until they choose to update, which might be never. The review process to push a fix can take a day. In between, you get to watch your app misbehave and you can do nothing about it.
So you want two things you can control from a server, without shipping a new build:
- A way to tell every copy of the app "stop, something is wrong, try again later."
- A way to tell an old version "you are too out of date, please update before continuing."
The first is a kill switch. The second is a forced-update gate. Together they buy you time, which is the thing you have least of when something breaks in front of real people.
How we built it without going overboard
The whole system is driven by a few rows in a config table the app reads on launch and when it comes back to the foreground. No new service, no fancy feature-flag platform.
The kill switch is a single flag plus a couple of text fields for the title and message. When the flag is on, the app shows a screen that cannot be dismissed and stops doing real work. It rechecks every thirty seconds or so, so when you turn the flag back off, people recover on their own without reinstalling or even closing the app.
The update gate works the same way. The config holds a minimum required version and a softer recommended version. Below the hard floor, the app blocks until you update. Below the soft line, it nudges but lets you keep going. The app already knew its own version number, so the check is a comparison and nothing more.
The two rules that mattered
A couple of decisions kept this from becoming a liability instead of a safety net.
Fail open. If the app cannot reach the config, or the response is garbage, it assumes everything is fine and lets the user through. The opposite choice is a disaster. Imagine your config endpoint hiccups and every copy of your app locks itself for no reason. A safety mechanism that can take down a healthy app is worse than no mechanism at all.
Ship it dormant. All of this went out turned off. The flags are set so the app behaves exactly as if none of this code exists. It sits quietly until the day I actually need it, and on that day I change a value in a table instead of writing code under pressure.
Was it worth it for a project this small?
Yes, and the reason is not really about scale. It is about the gap between "I found a problem" and "my users stopped feeling it." For a mobile app, that gap is naturally long. A kill switch and an update gate shrink it from days to seconds.
You do not need a big system. You need a value you can flip and an app that checks it. Build that early, leave it switched off, and hope you never touch it. It is cheap insurance, and the calm of knowing it is there is worth more than the hour it took to write.