No code shipped to it. No bug introduced. It just stopped working. Welcome to dependency drift on a shared runtime.
Here's a bug report that will eventually land in every super app team's queue: a mini-app is broken in production, and the team that owns it swears — correctly — that they changed nothing. Git history confirms it. No deploy, no commit, no config change. And yet last week it worked and today it doesn't.
This isn't a bug. It's drift. And it's the failure mode that defines super apps at scale, because a super app isn't one versioned thing — it's dozens of independently versioned mini-apps running on a shared runtime that is also versioned and moving. Let's build the governance for it.
The setup that guarantees the problem
Three mini-apps, one runtime. At launch, everything is in sync:
runtime: v1.0
├── miniapp_wallet (built against v1.0) ✓
├── miniapp_rewards (built against v1.0) ✓
└── miniapp_support (built against v1.0) ✓
Now let time pass. The wallet team ships features and starts using a v2.0 runtime API. The support team hasn't touched their mini-app in a year. The platform upgrades the runtime to patch something. Suddenly:
runtime: v2.0 ← platform upgraded to fix something unrelated
├── miniapp_wallet (needs v2.0) ✓
├── miniapp_rewards (needs v1.x) ⚠️ relied on v1 behavior
└── miniapp_support (needs v1.x) 💥 broke — nobody touched it
miniapp_support broke because the ground moved. No one on that team did anything. This is what "ungoverned" looks like.
Fix #1: version contracts, not "always latest"
The root mistake is forcing every mini-app onto the newest runtime. Instead, a mini-app declares what it needs, and the platform honors it:
{
"appId": "miniapp_support",
"version": "3.2.0",
"runtime": { "requires": ">=1.4 <2.0" },
"dependencies": {
"@platform/ui-kit": "1.x",
"@platform/payments": ">=2.1"
}
}
requires: ">=1.4 <2.0" is a contract. The platform now knows this mini-app must not be served a v2.0 runtime, and can keep a compatible runtime available for it instead of breaking it. Compatibility becomes explicit instead of accidental.
// Platform resolves a compatible runtime per mini-app, instead of one-size-fits-all
function resolveRuntime(miniApp) {
const range = miniApp.manifest.runtime.requires;
const runtime = runtimePool.findSatisfying(range);
if (!runtime) {
throw new IncompatibleRuntimeError(miniApp.appId, range);
// caught BEFORE the user sees a broken mini-app, not after
}
return runtime;
}
The key shift: incompatibility becomes a deploy-time check the platform enforces, not a runtime surprise the user discovers.
Fix #2: roll out change gradually
Even a compatible update can interact badly with something. So no version — mini-app or runtime — goes to everyone at once:
await release.publish("miniapp_wallet", {
version: "4.1.0",
rollout: { stage: "canary", percent: 5 }, // 5% of users first
healthCheck: { errorRateThreshold: 0.02, window: "10m" },
autoRollbackOn: "threshold_breach", // revert itself if it goes bad
});
A bad version now damages 5% of users for ten minutes and reverts automatically — instead of breaking everyone and waiting for the bug reports.
Fix #3: roll back one thing, instantly, without the app store
When something does slip through, you need surgical reversion — one mini-app or one runtime change, decoupled from any app-store review cycle:
// Revert a single mini-app platform-wide in seconds
await release.rollback("miniapp_wallet", { to: "4.0.3" });
// Or pin a runtime back for the cohort a bad upgrade affected
await runtimePool.pin({ cohort: "v1-dependents", runtime: "1.6" });
The host app never updates. The other 39 mini-apps never notice. You reverted exactly the thing that broke.
Fix #4: make drift visible before it bites
Drift is dangerous because it's invisible. So the platform keeps a live map of what's running where:
const drift = await registry.auditCompatibility();
// [
// { appId: "miniapp_support", needs: "<2.0", servedRuntime: "1.6", status: "ok" },
// { appId: "miniapp_rewards", needs: "1.x", servedRuntime: "1.6", status: "ok" },
// { appId: "miniapp_legacy", needs: "<1.2", servedRuntime: "1.6", status: "DRIFT" }
// ]
Now miniapp_legacy shows up as drifting before a user files a ticket. Drift becomes a dashboard entry, not an incident.
Putting it together
Version contracts, gradual rollout, granular rollback, and a live compatibility map — together they let the platform control how change propagates instead of hoping dozens of teams stay coordinated. Here's the shape:
This is a build-vs-buy point because none of it can live inside the mini-apps — the whole purpose is to govern them from above, with a vantage point no single team has. Teams that hand-roll a super app build the runtime that launches mini-apps and reach this layer late, usually right after their first ungoverned upgrade breaks something in production. A platform built for it ships this as foundation. FinClip, for example, provides hot updates, gray releases, one-click rollback decoupled from the app stores, and version/dependency management out of the box.
The test
- If you upgrade the runtime today, can you guarantee no mini-app that depends on old behavior breaks? (Version contracts.)
- Does a new mini-app version reach all users at once, or a few first? (Gradual rollout.)
- Can you roll back one mini-app without an app-store cycle? (Granular rollback.)
- Can you see which mini-apps are drifting before they break? (Compatibility map.)
A "no" on #1 means your next runtime upgrade is a gamble. Which of these is hardest to answer "yes" to right now? 👇
More on super app architecture, versioning, and runtime governance → https://super-apps.ai/

Top comments (0)