Last Week
Last week was about a tooling decision that became more important than I expected.
Shokken was already live on both Google Play and the iOS App Store in the United States, and the first organic signups had appeared. The next visible milestone was supposed to be marketing: finishing the pitch, testing the flyer flow, and starting real conversations with restaurants.
But the development pipeline was getting in the way. The existing Mac build machine could handle iOS builds, but it was slow enough that even a small authentication fix turned into a long wait through the release process. Since Shokken is public now, that kind of friction matters. A bug fix should not feel like it is queued behind my own infrastructure.
So I made an unexpected hardware purchase: a higher-memory Mac to reduce the iOS build bottleneck. That purchase does not replace the marketing work, but it should make the engineering side less painful while the product moves into real-world use.
This week stayed on that operational theme. The problem was not hardware, though. It was configuration management.
Mobile Apps Need A Different Release Strategy
The app is generally available in the United States on both Android and iOS.
That means Shokken is no longer only a codebase I can change freely. It is software installed on other people’s devices through platforms I do not control. That changes how I have to think about releases.
With a web app, deployment is comparatively direct. I can change the code, deploy the server, and the next person who loads the page gets the new behavior. There are still caches, browsers, network paths, and deployment mechanics, but the core idea is straightforward: the current version of the web app is the one the server is serving.
Mobile apps are different.
The app binary lives on the user’s device. New versions go through Google Play or Apple’s App Store. Review, release timing, staged rollout, user update behavior, and platform rules all sit between my code and the person using it. Even when a build is approved, not every user updates immediately.
The backend is different again. Shokken uses Supabase and Postgres behind the mobile app. Backend changes can be deployed much more like web changes: update the backend, run the migration or deployment, and the new behavior can be live in minutes.
That creates a synchronization problem.
The app and backend have to work together, but they do not deploy on the same timeline. A new frontend feature may be in a mobile binary that takes days to reach users. A backend change may be live in minutes. If a feature requires both sides, I need a way to keep it hidden until both sides are ready.
That is where configuration management enters the project.
What does it mean in English?
Configuration management is a way to control app behavior without shipping a new app every time.
In this case, the most important piece is a feature flag. A feature flag is a server-controlled switch. The code for a feature can already exist in the app, but the app only shows that feature when the server says the switch is on.
That matters for Shokken because app store releases are slower than backend releases. If I build a new QR-code menu feature into the app before the backend is ready, the app could expose screens that do not work yet. If I hide the feature behind a flag, I can ship the app safely, finish the backend, test the full flow, and then turn the feature on from the server.
The result is a safer release process. Bug fixes can still go out even when an unfinished feature is present in the codebase, because the unfinished feature does not have to be visible to users.
Nerdy Details
Web deployment and mobile deployment are not the same
The core difference is control.
A web app can be updated centrally. When I deploy a new version of a web service, users generally receive that version the next time they interact with the site. There are still edge cases, but the deployment model is centralized enough that the server can largely define what the current product is.
Mobile apps are distributed artifacts.
Once a version of Shokken is installed on a phone or tablet, that binary exists independently of my backend. The user may update immediately. They may update days later. They may have automatic updates disabled. Apple or Google may review a build quickly or slowly. A release can be approved but still roll out gradually.
That means the backend may be on version N, while one user is running mobile app version N, another is running N - 1, and another is still on an older build.
This is why mobile release planning cannot assume perfect synchronization. The backend has to tolerate older app versions. The app has to tolerate backend additions. New fields, new routes, and new capabilities need to be introduced in ways that do not break existing clients.
That is not only a large-company problem. Even a small app like Shokken has to respect that shape once it is public.
The QR-code menu feature exposed the issue
The feature that forced this work was QR-code menus.
Shokken already supports QR-code entry into a waitlist flow. A guest can scan a code, join a waitlist, and interact with the restaurant’s guest-side experience. Extending that idea to a menu-only QR code is not conceptually huge: a restaurant should be able to display a code that opens the menu without requiring the guest to join the waitlist.
That feature has two sides.
On the app side, I need UI for the operator:
- create or select the menu
- generate the QR code
- display or print the QR code
- manage the menu destination
- make the flow understandable to someone using the app during service
On the backend side, I need the supporting data and behavior:
- store the menu reference
- resolve the QR code destination
- serve the correct menu content
- enforce the right permissions
- avoid breaking the existing waitlist QR flow
Neither side is useful alone. A frontend screen that assumes backend support will fail if the backend is not ready. A backend endpoint that no app can reach yet is mostly harmless, but it still needs to be compatible with existing app versions.
The QR-code menu feature is small enough to be practical, but large enough to reveal the release coordination problem clearly.
Compatibility is necessary but not sufficient
The first rule is compatibility.
The backend should not break old clients when it adds new behavior. If a new response field appears, old app versions should ignore it. If a new endpoint exists, old clients should not care. If the backend starts supporting menu-only QR codes, the waitlist QR code flow should continue to work as before.
The app also needs to handle missing capabilities gracefully. If a server value is absent, old, or disabled, the app should not crash. It should fall back to the behavior that was already available.
That prevents catastrophic failure, but it does not fully solve release timing.
The harder case is when the app already contains new UI and the backend is not ready for it. Once a mobile binary is submitted, approved, and released, I cannot easily remove those screens from every installed device. If the UI is visible and the backend is incomplete, users can walk directly into a broken flow.
This is especially painful when the unfinished feature is not the only change in the app. If a bug fix is ready, I still want to release that fix. I do not want an incomplete feature to block every other improvement.
That happened recently in a smaller form: frontend work was available before the backend logic was finished, and that made releasing unrelated fixes more awkward than it should have been.
The better answer is to separate shipping code from enabling behavior.
A feature flag is just a controlled branch
The concept behind a feature flag is simple.
Somewhere in the app, there is a condition:
if (flags.qrCodeMenu) {
showQrCodeMenuFlow()
} else {
hideQrCodeMenuFlow()
}
That is the basic idea. The code can exist in the app, but the app checks a flag before exposing the feature.
For the QR-code menu work, the flag can be named something like qr_code_menu. While development is still in progress, the server says the flag is false. The app may contain the new screens, but users do not see them.
When the frontend and backend are both ready, I change the server-side value to true. The app fetches the updated configuration, stores the local copy, and starts showing the QR-code menu flow.
That gives me three useful properties:
- I can ship the app binary before the feature is publicly enabled.
- I can finish or adjust the backend without exposing broken UI.
- I can turn the feature off again if the production behavior is wrong.
The last point matters. Feature flags are not only for rollout. They are also a safety valve. If a feature is technically shipped but operationally risky, the flag gives me a way to disable it without waiting for an app store release.
The server is the authority
The authoritative flag value needs to live on the server.
If the flag only lives inside the app binary, it does not solve the problem. Changing it would still require a new app release, which is exactly the bottleneck I am trying to avoid.
So the app has a local copy, but the source of truth is backend configuration. When the app starts, logs in, or periodically refreshes, it asks the backend which capabilities are enabled. The backend returns the current configuration for that environment, and the app uses that to decide which flows to expose.
For this first implementation, I do not need instant updates. If I flip a flag and a device picks it up a few minutes later, that is fine. QR-code menu availability does not require millisecond precision.
That makes a lazy fetch model appropriate.
The app can fetch configuration:
- when it launches
- after login
- on an occasional interval
- when a relevant app state changes
I do not need a constant WebSocket connection just to update feature flags. A live push model would add cost and complexity, and on mobile it can also have battery implications. For this use case, polling occasionally is enough.
This is a good example of choosing the boring version of the solution because the product does not need the exciting version.
Why not use a configuration service?
There are existing services for this.
ConfigCat is one example. It provides hosted feature flags, SDKs, dashboards, targeting tools, and environment management. For a larger team or a product with more complex rollout needs, that kind of service can make a lot of sense.
I looked at that path, but it did not fit Shokken’s current shape.
The main issue is environments. Shokken has three:
- integration
- staging
- production
That setup is intentional. Shokken is a B2B product, and I want changes to pass through a real progression before reaching production. Integration gives me an early environment for development wiring. Staging gives me a closer rehearsal space. Production is the customer-facing app.
Some hosted configuration services have generous free tiers, but the pricing or limits can become awkward once three environments are involved. From their perspective, that makes sense: a product with multiple environments often looks like a more mature commercial customer. From my perspective, Shokken is not revenue-positive yet, and adding another paid service for a simple Boolean flag felt premature.
The feature I needed was small:
- store a few flags
- read them from the app
- scope them by environment
- update them without releasing a new binary
That does not require a full rollout platform yet.
The simple version lives in Supabase
Because Shokken already uses Supabase and Postgres, the first implementation can live there.
At the simplest level, configuration management can be a table with rows for flags. The app reads the values. The backend controls who can write them. Row Level Security keeps the read and write rules explicit.
The important part is the permission model.
The app may need to read configuration without exposing anything sensitive. That means the flag table can be publicly readable if the values are safe to expose. A value like qr_code_menu = true is not a secret. It only tells the app whether to show a feature.
Writing is different. Only trusted backend/admin paths should be able to change the flags. A user should not be able to enable a feature for themselves by modifying configuration.
So the design is intentionally modest:
- publicly readable values where safe
- protected writes
- environment-specific rows
- simple Boolean capability flags
- lazy fetching from the app
That is enough for the current problem.
It is not a full commercial feature flag platform. It does not have fancy targeting, percentage rollouts, audit dashboards, or per-customer experiments. I may eventually want some of that. Right now, I mostly need to avoid releasing broken UI while backend work catches up.
The production test worked
After putting the basic configuration management path in place, I tested it with the new feature.
The result was what I needed:
- with the flag off, the feature stayed off in production
- with the flag on, the feature appeared in production
- the toggle could be controlled without shipping a new app binary
That is a small milestone, but an important one.
It means I can now carry unfinished or staged feature code in the mobile app more safely. It means backend and frontend work do not have to land at exactly the same moment. It also means bug fixes are less likely to be blocked by a half-finished feature sitting in the same branch.
This is the kind of infrastructure that is easy to postpone because it does not look like user-facing progress. But once the app is public, it becomes part of the release safety system.
This also supports marketing
The immediate connection to marketing is not obvious, but it is real.
As I start talking to restaurants, I may need to move quickly. A restaurant might ask whether a specific flow exists. I might test a small feature with one location before making it available more broadly. I might discover that a feature is confusing and needs to be hidden until the copy or backend behavior improves.
Without configuration management, every one of those adjustments risks becoming an app release.
With server-controlled flags, I get more room to maneuver. I can keep the public app stable, stage new behavior carefully, and avoid exposing unfinished work while still shipping fixes.
That is especially important because the next phase is not just building. It is learning from real users. Learning requires controlled change.
Next Week
Next week happens after July 4th, and the goal is to start getting out to locations and talking to people.
The configuration management work gives me a safer release path while that outreach starts. Now I need to use it: finish the immediate QR-code menu rollout path, keep the app stable, and begin the uncomfortable but necessary work of showing Shokken to restaurants in person.