Plan a coordinated Custom API, data, and storefront release
Order database, API, and storefront artifacts by dependency, keep old and new clients compatible, and rehearse feature recovery before activation.
In this guide
- Versioned data change
- Compatible Custom API
- Storefront behind feature gate
- Smoke checks
- Enable
- Forward correction or disable
Treat the release as one behavior change
A feature can span a database migration, Custom API, and storefront. Plan those changes as one user journey. Otherwise a new control may appear before its API exists, or a new API may read a column that has not been added.
The manifest below describes account support notes. It names three artifacts, their dependencies, the accepted request versions, activation checks, and recovery choices. This is a project-owned deployment record; no Znode process reads these field names automatically.
release-manifest.json json
{
"releaseId": "support-notes-2026-09",
"feature": {
"key": "account-support-notes",
"initiallyEnabled": false
},
"artifacts": [
{
"id": "project-data-002",
"kind": "project-owned-database-migration",
"change": "add-nullable-note-category",
"dependsOn": []
},
{
"id": "notes-api-1.4.0",
"kind": "custom-api",
"dependsOn": [
"project-data-002"
],
"acceptsContractVersions": [
1,
2
]
},
{
"id": "storefront-2.8.0",
"kind": "webstore",
"dependsOn": [
"notes-api-1.4.0"
],
"sendsContractVersion": 2
}
],
"activationChecks": [
"existing-note-readable-with-missing-category",
"new-note-readable-by-previous-client",
"cross-account-read-denied",
"feature-disabled-hides-entry-point",
"timeout-does-not-create-second-note"
],
"recovery": {
"firstAction": "disable-account-support-notes",
"retainCreatedRecords": true,
"databaseStrategy": "reviewed-forward-correction",
"previousApiRollbackRequires": "compatibility-with-data-already-written"
}
}
Deploy project-data-002 first, then notes-api-1.4.0, then storefront-2.8.0. The API accepts request versions 1 and 2, so the previous storefront can still operate while the new one rolls out. Keep the feature disabled until the named checks pass. The supplemental orderedArtifacts function validates the dependency graph and returns this order; it does not deploy the artifacts.
Order changes for compatibility
Prefer additive data and API changes first. New readers should tolerate absent optional data while the migration is pending. New writers should not require a storefront version that has not arrived. Enable the user-facing feature only after the dependent artifacts pass their target-environment checks.
Do not edit an already executed migration as a rollback strategy. Journaled migrations and deployed data need forward corrections that account for actual state. For project-owned data, define whether a rollback removes a feature only, preserves new records, or performs a separately reviewed repair.
- Prepare versioned artifacts and deployment notes.
- Apply additive data prerequisites.
- Deploy API capable of old and new safe states.
- Deploy storefront with feature disabled.
- Run smoke and negative-path checks, then enable deliberately.
Check the states created during rollout
Check the combinations that exist during a rollout: the previous client with the new API, a new client with the feature disabled, records without the new optional value, and an unavailable API. Include unauthorized and cross-account requests. Each check should identify the behavior and its expected result.
Rehearse recovery before the change window. Identify who can disable the feature, who prepares a failed migration's forward correction, and how to confirm the expected storefront artifact is active. Disabling a control preserves data already written and does not stop work already accepted.
- Browser and API health checks.
- Authorized and unauthorized behavior.
- No-data and partial-migration behavior.
- Observability and alert paths.
- Recovery owner and decision threshold.
Record the deployed contract
Record the deployed artifact versions, target environment, activation time, and results of each named check alongside the manifest. Use immutable build identifiers in the deployment record so a support engineer can connect an observed request to the code and data contract serving it.
Treat rollback as another compatibility decision. Disabling the interface stops new user actions, but it does not cancel work already accepted or undo data already written. Keep the required worker and reader versions available until those records have a defined recovery path.
Verification checklist
Validate the manifest against the exact target platform and hosting model. Exercise the feature both before and after enabling it, then verify recovery in a permitted non-production environment. Recheck the guide when platform deployment guidance changes.
Reject a release manifest with missing or cyclic dependencies
Pass the parsed release-manifest.json object. The function validates up to 30 artifact IDs and their dependsOn lists, then returns a dependency-safe order. With the article's manifest, the result is project-data-002, notes-api-1.4.0, storefront-2.8.0. A duplicate ID, missing dependency, or cycle throws before deployment begins. The deployment process must still verify that each artifact succeeded before moving to the next.
release/ordered-artifacts.ts typescript
export function orderedArtifacts(input: unknown): string[] {
if (!input || typeof input !== "object" || Array.isArray(input))
throw new Error("A manifest object is required.");
const rows = (input as Record<string, unknown>).artifacts;
if (!Array.isArray(rows) || rows.length < 1 || rows.length > 30)
throw new Error("Supply 1 to 30 artifacts.");
const graph = new Map<string, string[]>();
const validId = (id: unknown): id is string =>
typeof id === "string" && /^[a-z0-9][a-z0-9.-]{0,79}$/.test(id);
for (const raw of rows) {
if (!raw || typeof raw !== "object" || Array.isArray(raw))
throw new Error("Each artifact must be an object.");
const row = raw as Record<string, unknown>;
if (!validId(row.id) || graph.has(row.id) || !Array.isArray(row.dependsOn) ||
row.dependsOn.length > 30 || !row.dependsOn.every(validId) ||
new Set(row.dependsOn).size !== row.dependsOn.length)
throw new Error("Artifact identities and dependencies must be valid and unique.");
graph.set(row.id, row.dependsOn);
}
const visiting = new Set<string>(), complete = new Set<string>(), order: string[] = [];
const visit = (id: string): void => {
if (complete.has(id)) return;
if (visiting.has(id)) throw new Error("Dependency cycle.");
const dependencies = graph.get(id);
if (!dependencies) throw new Error("Missing dependency.");
visiting.add(id);
dependencies.forEach(visit);
visiting.delete(id);
complete.add(id);
order.push(id);
};
graph.forEach((_, id) => visit(id));
return order;
}
References and further reading
Bring your next engineering question.
Need help planning a safe commerce release?
Independent guidance from GCG. Znode is a trademark of its owner. Examples use fictional data and are not official platform documentation. Suggest a correction.