mediumDev Processes#150

OTA update gate (runtimeVersion)

Prompt

Model the server-side gate of an OTA system like EAS Update. Given:

  • binary = { runtimeVersion, channel } — what the installed app reports
  • updates = [{ id, runtimeVersion, channel, createdAt }] — published updates (createdAt is a number)

Write selectUpdate(updates, binary):

  1. An update is compatible only if BOTH runtimeVersion and channel match the binary exactly
  2. Return the newest compatible update (highest createdAt), or null if none

Solution

function selectUpdate(updates, binary) {
  const compatible = updates.filter(u =>
    u.runtimeVersion === binary.runtimeVersion &&
    u.channel === binary.channel
  )
  if (compatible.length === 0) return null
  return compatible.reduce((newest, u) =>
    u.createdAt > newest.createdAt ? u : newest
  )
}
Mentor's take

This ten-line filter is the entire safety model of over-the-air updates, and being able to explain why each check exists is what "I've operated OTA in production" sounds like.

  • runtimeVersion equality is a crash prevented. OTA replaces the JS bundle only — never native code. If a JS update calls a native module that the installed binary doesn't contain, the app crashes at launch, for every user, with no way to push a fix because the updater itself is what's broken. The runtime version is a contract string: "this JS requires exactly this native capability set." Exact match, not semver-compatible match — you can't reason about ABI compatibility from version arithmetic.
  • channel is environment isolation. Production binaries point at the production channel; staging at staging. The check is what makes "promote to production" an explicit act (republish to the channel) instead of an accident. Rollback falls out for free: republish the last known-good update, and this same selector picks it as newest.
  • Newest-wins via reduce instead of sort()[0] is a small habit that matters: sort mutates the input array (a rude thing for a selector to do to shared state) and costs O(n log n) for a question that needs O(n).
  • The historical framing that earns credibility: this is the machinery CodePush pioneered — retired with App Center in 2025 — and EAS Update standardized. Answering "CodePush" as your current OTA tool dates you; explaining the gate it shared with EAS Update shows you understand the invariant rather than the brand.

Red flag: treating OTA as "deploy anything instantly." State the boundary unprompted: JS and assets only, gated by runtime version; native changes always ride a store release.

Say it: "The updater filters on exact runtimeVersion and channel, then takes the newest — the runtime version is what guarantees the JS can't call native code the binary doesn't have, and that gate is the whole reason OTA is safe."