


























A version number takes the form MARKETING.NATIVE.OTA:
1 . 4 . 12
│ │ │
│ │ └─ OTA bumped on every over-the-air update (fast)
│ └───────── NATIVE bumped on every new store binary (slow)
└───────────────── MARKETING yours to define: era, year, redesign, or never
0 is reserved for the pre-release era; ship your first stable
release as 1.0.0 and go from there.
A new native build always resets OTA to 0. By default a MARKETING bump resets
NATIVE and OTA too, so a redesign goes to 2.0.0; a team may instead let the
numbers keep climbing to 2.6.2 (see the FAQ). Pick one convention and stay
consistent.
Mobile apps don't ship the way libraries do. A library has one release channel and one question that matters: did the contract break? That's what Semantic Versioning answers, and it answers it well.
An app shipped with React Native, Expo, or a similar framework has two release channels moving at two speeds. Some changes (a new native module, an SDK bump, a new permission) can only reach users as a fresh binary through a distribution channel (App Store, Play Store, TestFlight, enterprise/MDM): slow, gated behind redistribution, impossible to roll back instantly. Other changes (a copy fix, a restyled screen, a JS-only feature) can be pushed over the air and land on devices in minutes, independently, with no store review.
SemVer can't see this. Its MAJOR.MINOR.PATCH describes what changed; it
says nothing about how the change reaches the user, which on mobile is the fact
that governs review latency, rollout speed, and rollback. PaceVer makes that fact the
version.
PaceVer deliberately says nothing about the size of a change. A one-word fix and a screen rewrite both bump OTA by one, because they ship the same way, at the same pace. If your team wants to signal that a release is a big deal, that signal goes in MARKETING, which is yours to spend however you like.
The key words "MUST", "MUST NOT", "SHOULD", "SHOULD NOT", and "MAY" are to be interpreted as described in RFC 2119.
X.Y.Z, where X
(MARKETING), Y (NATIVE), and Z (OTA) are non-negative integers
and MUST NOT contain leading zeroes. Each successive version MUST increase in precedence,
as defined in rule 10. Individual elements need not increase one by one: incrementing an
element resets the elements to its right (rule 6), so the version as a whole always orders
forward even though a reset element drops to 0.
X) is reserved at 0
to mean the app is pre-release, not yet considered stable or shipped to the public. Once
MARKETING reaches 1 or higher, the reason and magnitude of
an increment carry no required meaning and no compatibility promise: a team MAY increment
it for any reason (a new year, a redesign, a rebrand, a rewrite, a milestone, the magnitude
of a change) or never increment it again. Its one fixed constraint is direction: MARKETING
MUST be monotonically non-decreasing over the life of the project.
Y) MUST be incremented when a release
requires a new binary to be installed, that is, any change that cannot be delivered over
the air. This includes, but is not limited to: native code or native dependency changes,
runtime or SDK upgrades, permission or entitlement changes, and changes to compiled
application configuration.
Z) MUST be incremented for each release
delivered over the air to an existing native build, that is, any change deliverable without
a new binary, such as JavaScript, styling, content, and assets running within the current
native runtime.
0, because a new native build
begins a fresh OTA lineage. When MARKETING is incremented, NATIVE and OTA SHOULD be reset
to 0; this is the recommended default. A project MAY instead let NATIVE and OTA
keep climbing across a MARKETING bump (see the FAQ), but it MUST pick one convention and
apply it consistently.
0. The JavaScript and assets bundled inside a native build are its
baseline, not an OTA release, and MUST NOT be counted as OTA 1.
0 if none has been applied.
1.2.0 < 1.2.7 < 1.3.0 < 2.0.0).
1.4.0-rc.1) and a plus sign introduces build metadata (e.g.
1.4.0+ios). PaceVer assigns these suffixes no channel or pace meaning; their
interpretation is left to the team. Build metadata MUST be ignored when determining
precedence.
0.1.0, where MARKETING 0 marks the pre-release era. NATIVE and
OTA increment as usual through the 0.x era. When the team considers the app
stable and ready for the public, it increments MARKETING to 1. With the
recommended reset (rule 6) the first stable release is 1.0.0; a team that does
not reset carries its running numbers instead, so 0.5.3 becomes
1.5.3. The exact moment of going stable is the team's call.
2.5.0 → 2.5.3 shipped over the
air and is already on devices. 2.5.3 → 2.6.0 is a new binary waiting on store
review. You know the deployment story at a glance.
MARKETING.NATIVE.0 as its marketing version (the three integers the stores
expect, with OTA pinned at 0 as the baseline); the live OTA number is composed
on top at runtime.
X.Y.Z string ordered by the same left-to-right precedence as SemVer, so
tooling that sorts or compares SemVer-shaped versions keeps working. PaceVer changes what
the three positions mean, not the format or the ordering.
PaceVer is for user-facing application clients that ship through a native channel and an OTA channel (rule 12). It is the wrong tool when:
Adopting PaceVer means giving up SemVer's compatibility contract and CalVer's at-a-glance recency in exchange for an at-a-glance pace signal. Make that trade on purpose.
| Version | Channel | What shipped |
|---|---|---|
0.1.0 | NATIVE | First build. The pre-release era begins. |
0.1.1 | OTA | Beta copy tweak, pushed over the air. |
0.2.0 | NATIVE | Added a camera module, new binary to testers, OTA resets. |
1.0.0 | NATIVE | First public release on the App Store and Play Store. MARKETING → 1, everything resets. |
1.0.1 | OTA | Typo fix, on devices in minutes. |
1.0.2 | OTA | JavaScript crash fix. |
1.1.0 | NATIVE | Expo SDK upgrade, new binary to both stores, OTA resets. |
1.1.1 | OTA | Restyled a screen. |
2.0.0 | NATIVE | A full seasonal redesign the team wants to flag. MARKETING bump, new binary to both stores, everything resets. |
Every row tells you how it shipped from the number alone: a middle-digit change means a trip through the store; a last-digit change means it was already on devices in minutes.
Not in the binary's store version. An OTA update can't rewrite that, and that's fine. The
app composes its version at runtime: it reads MARKETING and NATIVE from the binary and OTA
from the applied update's manifest (e.g. Updates.manifest / extra
in Expo). A fresh install with no update applied reports OTA 0.
No. OTA counts over-the-air releases, not their size. A typo fix and a new screen both bump OTA by one. If you care about magnitude, spend the MARKETING number on it.
By default, yes. A MARKETING bump goes to X.0.0, exactly as a NATIVE bump
resets OTA, so a redesign from 1.6.2 lands on 2.0.0. This is the
recommended convention, but resetting on a MARKETING bump is not mandatory; see the next
question.
Yes. Instead of resetting, you may let MARKETING float as a pure label while NATIVE and OTA
keep climbing, so 1.6.2 becomes 2.6.2 rather than
2.0.0. The tradeoff: you give up the clean X.0.0 era boundary, but
you keep a single monotonic native and OTA count across the whole life of the app, which
some teams find easier to reason about. Precedence still works either way, since versions
compare left to right. Whichever you pick, apply it consistently. (A new NATIVE build always
resets OTA regardless; that part is not optional, because a new binary starts a fresh OTA
lineage.)
No. MARKETING and NATIVE live in the installed binary (rule 8), so changing either one means
shipping a new native build. Only OTA moves without a new binary, which is why a MARKETING or
NATIVE change always lands with OTA back at 0.
No, and that's by design: like the app stores themselves, PaceVer has no concept of a native rollback. NATIVE only ever moves forward (rule 2's precedence), and an OTA release is scoped to the native build it was produced for (rule 9), so you can't drop users back onto a previous binary's OTA lineage. To recover from a bad native build you re-release the previous good build's content forward under a new NATIVE number, for example shipping what was NATIVE 5 again as NATIVE 7, and go from there. OTA, by contrast, can be rolled back within the same native build, as fast as it ships.
Yes, and on purpose. A NATIVE bump ships a new binary to every platform, even one with no functional change, so all platforms stay on the same NATIVE build. This is for OTA's sake: OTA releases are scoped to a runtime, so keeping every platform on a common runtime lets one OTA release land everywhere under a single OTA number. The seemingly redundant Android build usually isn't empty anyway: bumping NATIVE bumps the runtime, so it's a real new binary, just without feature changes. The cost is one extra store submission; the payoff is one coherent version line for the whole app.
PaceVer doesn't cover web. Web (and PWAs) has effectively one channel (every deploy is over the air), so the native/OTA split doesn't apply. Version web on its own terms.
No. Use SemVer. PaceVer says nothing about API compatibility, which is the whole point of versioning a library. PaceVer is for shipping apps, not contracts.
Because PaceVer is for apps, and this is a document. A spec has no native build and no over-the-air channel. It has a contract that other people rely on, which is exactly what the Scope rule says to version with SemVer. So PaceVer eats its own rule and ships as SemVer. SemVer versions itself; PaceVer versions itself with SemVer. Each tool to its job.
Start at 0.1.0. MARKETING 0 is the pre-release era, and you build
through 0.x until the app is ready. When it's ready for the public, bump
MARKETING to 1. With the recommended reset that lands you on 1.0.0;
if you don't reset, you carry your running numbers to 1.y.z. The recommended
path is simply 0.1.0 → 1.0.0 → onward. The exact moment doesn't really matter;
it's a flag you raise when it feels true.
Don't reset downward. The App Store and Play Store reject a marketing version that decreases,
so an app already at, say, 3.4.1 cannot adopt PaceVer by starting over at
1.0.0. Because MARKETING is a free, monotonically non-decreasing number, keep
your current leading number as MARKETING and start applying PaceVer's NATIVE and OTA
semantics from where you are. The 0.1.0 start and the reset rules are for new
projects; an existing app carries its history forward.
PaceVer was created in 2026 by Pavlos Vinieratos.
This specification is itself versioned with SemVer, not PaceVer. A spec is a document with a contract people rely on, not an app shipping through native and OTA channels, so by PaceVer's own Scope rule (rule 12) it uses the right tool for the job.
Current version: 1.0.0 (SemVer).
PaceVer, the Pace Versioning specification, is licensed under a Creative Commons Attribution 4.0 International License (CC BY 4.0). You are free to share and adapt it, including commercially, as long as you give appropriate credit.
Copyright © 2026 Pavlos Vinieratos.
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。