docs(recipes): add deploy versioning boundary
Document deployment and version compatibility for hemx apps: release unit, runtime asset caching, rolling deploy policy, fingerprint mismatch behavior, semver buckets, upgrade notes, and deployment checks without adding platform code to core. req: abi/001 req: abi/002 req: abi/003 req: abi/004 req: abi/005 req: runtime/004 req: failure/005
This commit is contained in:
@@ -85,7 +85,7 @@ and integrate at explicit boundaries. req: laws/002 req: auth/001
|
|||||||
fields and credentials semantics. See `docs/recipes/auth-session-csrf.md`. req: auth/004 req: auth/005
|
fields and credentials semantics. See `docs/recipes/auth-session-csrf.md`. req: auth/004 req: auth/005
|
||||||
- **Observability, feature flags, killswitches, deploy:** use explicit platform
|
- **Observability, feature flags, killswitches, deploy:** use explicit platform
|
||||||
integrations around handlers, routes, and runtime assets. Core hemx must not
|
integrations around handlers, routes, and runtime assets. Core hemx must not
|
||||||
vendor providers or add framework-specific magic.
|
vendor providers or add framework-specific magic. See `docs/recipes/deploy-versioning.md`.
|
||||||
- **PWA/offline/sync:** optional adapters may reuse generated targets/effects,
|
- **PWA/offline/sync:** optional adapters may reuse generated targets/effects,
|
||||||
but core hemx must not gain a mandatory client state graph or local app
|
but core hemx must not gain a mandatory client state graph or local app
|
||||||
runtime. req: canonical_authoring/008
|
runtime. req: canonical_authoring/008
|
||||||
@@ -104,7 +104,7 @@ The generated API, symbols, effect wire schema, and JavaScript runtime carry
|
|||||||
schema/ABI versions. Deploy a matching server, generated output, and runtime
|
schema/ABI versions. Deploy a matching server, generated output, and runtime
|
||||||
asset together. Build fingerprints are derived from the generated surface and ABI
|
asset together. Build fingerprints are derived from the generated surface and ABI
|
||||||
parts; the runtime refuses incompatible effect responses and integrations should
|
parts; the runtime refuses incompatible effect responses and integrations should
|
||||||
fall back to a full page reload when possible. req: abi/001 req: abi/002 req: failure/005
|
fall back to a full page reload when possible. See `docs/recipes/deploy-versioning.md`. req: abi/001 req: abi/002 req: failure/005
|
||||||
|
|
||||||
Before a v1 release, the semver policy and upgrade notes should explicitly state
|
Before a v1 release, the semver policy and upgrade notes should explicitly state
|
||||||
which surfaces are stable: beginner generated helpers and handler shapes; the
|
which surfaces are stable: beginner generated helpers and handler shapes; the
|
||||||
|
|||||||
@@ -0,0 +1,144 @@
|
|||||||
|
# Recipe: deploy and version compatibility
|
||||||
|
|
||||||
|
This recipe describes the production deployment boundary for a hemx app. The
|
||||||
|
server binary, generated Rust helpers, generated symbol/fingerprint metadata, and
|
||||||
|
JavaScript runtime asset must be treated as one release unit. hemx core provides
|
||||||
|
the ABI/fingerprint checks; the application and platform own rollout, caching,
|
||||||
|
observability, and rollback policy. req: abi/001 req: abi/002 req: runtime/004
|
||||||
|
|
||||||
|
Use this for `examples/saas`-style apps before putting multiple app versions
|
||||||
|
behind a load balancer or CDN.
|
||||||
|
|
||||||
|
## Release unit
|
||||||
|
|
||||||
|
A compatible release contains:
|
||||||
|
|
||||||
|
- the Rust server binary built from the same checkout as `build.rs`
|
||||||
|
- generated `hemx.generated.rs` and symbols produced during that build
|
||||||
|
- the `hemx-js` runtime asset served by that server or deployed with the same
|
||||||
|
release
|
||||||
|
- templates, CSS, island JavaScript, migrations, and app config for that release
|
||||||
|
|
||||||
|
Do not mix a newly built server with an old `/hemx.js`, old generated output, or
|
||||||
|
old cached page shell. Build fingerprints are derived from Surface/schema/ABI
|
||||||
|
parts, so mismatches are detected and partial updates fail closed instead of
|
||||||
|
mutating the wrong DOM. req: abi/003 req: abi/004 req: failure/005
|
||||||
|
|
||||||
|
## Asset serving
|
||||||
|
|
||||||
|
Serve `/hemx.js` from the same release as the server when possible:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
async fn runtime() -> impl IntoResponse {
|
||||||
|
hemx_axum::runtime_js()
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
If a CDN is required, publish the runtime under a content-addressed release path
|
||||||
|
and keep the page shell pointing at that exact path:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<script src="/assets/2026-06-05T120000Z/hemx.js" defer></script>
|
||||||
|
```
|
||||||
|
|
||||||
|
Avoid long-lived unversioned CDN caching for `/hemx.js`. If an unversioned path
|
||||||
|
is used, set a short cache TTL or purge it atomically with the server rollout.
|
||||||
|
CSS and explicit island scripts should follow the same release path policy.
|
||||||
|
|
||||||
|
## Rolling deploys
|
||||||
|
|
||||||
|
Rolling deploys are safe when every response serves a self-consistent release.
|
||||||
|
The easiest policy is sticky-by-release routing:
|
||||||
|
|
||||||
|
- page HTML, interaction POSTs, SSE/polling endpoints, and `/hemx.js` come from
|
||||||
|
the same server revision
|
||||||
|
- a load balancer cookie or platform routing key keeps an active browser on one
|
||||||
|
revision during the rollout window
|
||||||
|
- old revisions stay alive until active SSE connections and in-flight forms have
|
||||||
|
drained
|
||||||
|
|
||||||
|
If sticky routing is not available, make the mismatch behavior user-safe:
|
||||||
|
|
||||||
|
- keep full page GETs compatible across one adjacent version when practical
|
||||||
|
- allow interaction responses to fail closed on fingerprint mismatch
|
||||||
|
- prefer redirect/reload fallback over best-effort partial mutation
|
||||||
|
- report mismatch counts so rollouts can be paused quickly
|
||||||
|
|
||||||
|
The runtime must not grow a negotiation protocol or compatibility shim in core;
|
||||||
|
capability negotiation belongs to optional integration crates. req: runtime/004
|
||||||
|
|
||||||
|
## Fingerprint and mismatch behavior
|
||||||
|
|
||||||
|
Initial roots carry the build fingerprint, and effect responses carry the
|
||||||
|
fingerprint for the batch. The runtime compares them before applying effects.
|
||||||
|
On mismatch, the app should recover by reloading or navigating to a full page
|
||||||
|
owned by the current server revision. req: abi/003 req: abi/004
|
||||||
|
|
||||||
|
Recommended app behavior:
|
||||||
|
|
||||||
|
```text
|
||||||
|
fingerprint mismatch
|
||||||
|
-> record metric: hemx.fingerprint_mismatch
|
||||||
|
-> show a short-lived "Updating…" notice if possible
|
||||||
|
-> perform full page reload/navigation
|
||||||
|
```
|
||||||
|
|
||||||
|
Never ignore a mismatch to preserve a partial update. Resource ids are stable
|
||||||
|
within a build and best-effort across compatible symbol paths, but they are not a
|
||||||
|
persistence or cross-version addressing contract. req: abi/005
|
||||||
|
|
||||||
|
## Semver policy for v1 apps
|
||||||
|
|
||||||
|
For v1, document changes in three buckets:
|
||||||
|
|
||||||
|
- **Beginner API:** generated helpers, `#[hemx::app]`, `#[hemx::component]`,
|
||||||
|
`#[hemx::handler]`, `#[hemx::form]`, `hemx::page`, tuple `IntoEffect`, and
|
||||||
|
`Result<impl IntoEffect, E>` mapping. Breaking changes require a major version
|
||||||
|
or an explicit migration note.
|
||||||
|
- **Wire/runtime ABI:** EffectBatch schema, runtime ABI version, and fingerprint
|
||||||
|
inputs. Incompatible changes must bump ABI versions and fail closed at runtime.
|
||||||
|
- **Advanced escape hatches:** raw effects, manual registries, low-level ids,
|
||||||
|
raw render/target construction, runtime hooks, SSE internals, and island
|
||||||
|
internals. These may evolve faster, but must remain named as advanced and must
|
||||||
|
not leak into beginner docs. req: public_api/002 req: public_api/005
|
||||||
|
|
||||||
|
Upgrade notes should explain what changed, whether generated code must be
|
||||||
|
regenerated, whether `/hemx.js` must be rolled with the server, and what fallback
|
||||||
|
users see if an old page talks to a new server.
|
||||||
|
|
||||||
|
## Deployment checklist
|
||||||
|
|
||||||
|
Before promoting a release:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cargo run -p hemx-xtask -- test
|
||||||
|
cargo check --workspace
|
||||||
|
redgate health --strict
|
||||||
|
```
|
||||||
|
|
||||||
|
Then verify deployment-specific behavior:
|
||||||
|
|
||||||
|
- page HTML includes the intended `/hemx.js`, CSS, and island asset release paths
|
||||||
|
- interaction endpoints return the same build fingerprint as the initial root
|
||||||
|
- SSE/polling endpoints stream batches from the same revision
|
||||||
|
- a stale page talking to the new server reloads or navigates instead of applying
|
||||||
|
a partial update
|
||||||
|
- fingerprint mismatch metrics/logs are visible to the platform team
|
||||||
|
- rollback serves a self-consistent old server/runtime pair
|
||||||
|
|
||||||
|
These checks belong in the app/platform pipeline. hemx should provide the small
|
||||||
|
runtime handshake and clear failure boundary, not a deployment platform.
|
||||||
|
|
||||||
|
## Observability hooks
|
||||||
|
|
||||||
|
Track deployment compatibility as app/platform metrics:
|
||||||
|
|
||||||
|
- `hemx.fingerprint_mismatch`
|
||||||
|
- `hemx.effect_decode_error`
|
||||||
|
- `hemx.missing_target`
|
||||||
|
- `hemx.sse_reconnect`
|
||||||
|
- `hemx.full_reload_fallback`
|
||||||
|
|
||||||
|
The metric names are suggestions, not core API. The important behavior is that a
|
||||||
|
team can see mismatches, pause a rollout, and recover with a full page response
|
||||||
|
without weakening the runtime's tiny, selectorless contract. req: failure/001 req: failure/005
|
||||||
@@ -12,7 +12,7 @@ What it proves today:
|
|||||||
- page shell with plain CSS and one explicit metrics island script
|
- page shell with plain CSS and one explicit metrics island script
|
||||||
- compile-time surface generation plus interaction tests
|
- compile-time surface generation plus interaction tests
|
||||||
|
|
||||||
For provider-explicit boundaries, see `../../docs/recipes/sqlx-persistence.md` and `../../docs/recipes/auth-session-csrf.md`.
|
For provider-explicit boundaries, see `../../docs/recipes/sqlx-persistence.md`, `../../docs/recipes/auth-session-csrf.md`, and `../../docs/recipes/deploy-versioning.md`.
|
||||||
|
|
||||||
What it deliberately does not claim yet:
|
What it deliberately does not claim yet:
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user