RORK LABJP
MAX — Rork Max generates native Swift apps for every Apple platform: iPhone, iPad, Apple Watch, Apple TV, and Vision ProPUBLISH — Rork Max supports 2-click App Store publishing without Xcode, cutting the steps from build to submissionFUND — Rork raised $2.8M from a16z, and the platform now draws over 743,000 monthly visits at an 85% growth rateNATIVE — Rork turns plain-language prompts into native React Native (Expo) apps that can use device features like the camera and notificationsOWN — You keep full ownership of the generated code, free to refine and extend it laterMVP — Rork can stand up a working MVP in hours rather than weeks, so you can validate an idea fastMAX — Rork Max generates native Swift apps for every Apple platform: iPhone, iPad, Apple Watch, Apple TV, and Vision ProPUBLISH — Rork Max supports 2-click App Store publishing without Xcode, cutting the steps from build to submissionFUND — Rork raised $2.8M from a16z, and the platform now draws over 743,000 monthly visits at an 85% growth rateNATIVE — Rork turns plain-language prompts into native React Native (Expo) apps that can use device features like the camera and notificationsOWN — You keep full ownership of the generated code, free to refine and extend it laterMVP — Rork can stand up a working MVP in hours rather than weeks, so you can validate an idea fast
Articles/Dev Tools
Dev Tools/2026-07-24Advanced

Resolving App Config in Three Layers: Merging Defaults, User, and Remote With Bounded Overrides

A single type-safe layer that merges compiled defaults, user preferences, and remote config. So a broken remote value never takes your app down, each key gets its own override strength, plus schema validation and range clamping, built from a real production incident.

Rork519React Native211Config ManagementRemote Config7Architecture19

Premium Article

Late one night I opened my own wallpaper app on my iPhone, and a full-screen ad appeared every time I swiped to the next image. Twice, three times, inside half a minute. My stomach dropped.

It wasn't a bug in the code. The interstitial cooldown value I kept in remote config had somehow been set to something tiny, and that value had shipped straight to the app. Worse, the same config was shared across all six of the wallpaper apps I maintain as an indie developer, so every one of them broke at once.

What this drove home for me was how dangerous the assumption "remote config is safe because I can fix it remotely" really is. Yes, you can push a fix quickly. But for the twenty-odd minutes it takes that fix to reach every device, the app keeps running, exposed. The place to defend was never the delivery side. It was the device receiving the value.

This article is about a design that merges three sources, compiled defaults, user preferences, and remote config, into one type-safe layer, so that a broken value can arrive without taking the app down. It's based on the code I put into all six apps after that night.

Start From the Fact That Config Has Three Sources

In most apps, config quietly ends up being three-layered whether you plan for it or not. Let me lay them out in order.

The first is the defaults compiled into the binary. They're always present, even offline. This is where the source of truth for types and initial values lives.

The second is user preferences. Stored locally on the device (AsyncStorage or MMKV), they represent choices the user made deliberately, things like prefetch count or theme.

The third is remote config. External input arriving from something like Firebase Remote Config, holding values the operator wants to change quickly for everyone, typically ad frequency or the timing of a review prompt.

The trouble is merging these three with a naive "last one wins" override. That night's incident happened precisely because remote could overwrite anything. Having three layers is unavoidable. What you must avoid is giving every layer the same strength.

Give Each Key Its Own Override Strength

The heart of the design is declaring, per value, who is allowed to override it and how far. I settled on three policies.

locked — nobody can override

A flag like which onboarding version to show breaks the experience if it flips by accident, so I mark it locked. Neither remote nor the user can touch it; it stays at its default. It's a safety valve, deliberately made immovable.

user — user preference wins

A pure preference like prefetch count gets the user policy. Remote stays out of it, and only the value the user chose overrides the default. It simply isn't a value the operator should be changing behind their back.

remote — allow overrides, but only within a range

A value the operator wants to tune, like the ad cooldown, gets the remote policy. But I don't trust it wholesale. Each key defines a min/max, and the incoming value is always clamped into that range. That 3-second value from the incident now arrives raised to a floor of 60 seconds.

Thank you for reading this far.

Continue Reading

What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.

WHAT YOU'LL LEARN
A type-safe merge layer that resolves defaults, user prefs, and remote config with per-key override strength (locked / user / remote)
How to keep one broken remote value from cascading, per-key fallback to defaults plus min/max clamping, written out in full
The real config incident across six wallpaper apps and the operational calls it forced (how to set bounds, what to log)
Secure payment via Stripe · Cancel anytime

Unlock This Article

Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.

or
Unlock all articles with Membership →
Share

Thank You for Reading

Rork Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • Copy-paste ready implementation code
  • New advanced guides published daily
  • $5/mo or $10 for lifetime access
View Membership →

Related Articles

Dev Tools2026-07-24
Collapsing Duplicate Requests Into One: A Reference-Counted Single-Flight Layer
When several components fire the same API call at launch, you get a burst of identical requests. Here is a single-flight layer that shares one in-flight promise instead, avoiding the trap of handing out a failed promise forever and the trap of one caller's abort cancelling everyone, with the real network numbers alongside.
Dev Tools2026-07-14
Designing Seams That Survive AI Regeneration in Rork
Every follow-up prompt to Rork can quietly wipe out logic you wrote by hand. Protecting it with prompts is a patch, not a fix. Here is how to separate generated code from code you own, and draw a boundary that regeneration cannot reach, with working Zustand and service-layer examples.
Dev Tools2026-06-22
When a Poisoned Cache Crashes Your App on Every Launch — Designing a Safe-Mode Boot Your Users Can Escape On Their Own
When a persisted cache goes bad and the app crashes at the same spot on every launch, the only option left to the user is to reinstall. This article designs a safe-mode boot for Expo (React Native): the app counts its own early crashes, confirms a launch only once it becomes interactive, and resets just the dangerous state in graduated steps.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links
See all →