A payments team scheduled a dual-write cutover for Sunday night. The checklist arrived as a long chat transcript. A free-tier coding model had drafted every step.
This scene is a composite. It is not a named outage. The failure mode is still common.
The model proposed a traffic split of five percent. It also invented a freeze window and a rollback owner. Nobody pinned those values in version control.
Dual-write cutovers are not brainstorming tasks. They are timed contracts with production data. A free lane can sketch a draft. It must not own the production clock.
Why cutover timing is not a chat task
A dual-write window binds two stores at once. Reads may still hit the old path. Writes must land in both paths without drift.
A wrong retry budget duplicates money movements. A wrong freeze window hides a split brain. A wrong rollback owner pages the wrong humans.
Free models optimize for a complete-looking checklist. Completeness is not correctness under production load. Cutover timing needs pinned owners and pinned numbers.
Idempotency keys are part of that contract. A model can invent a plausible key shape. Plausible shape is not a ledger invariant.
The engineering line
Prompt-built patches can look complete in review. A live dual-write cutover remains a hard counterexample. The work is sequencing, ownership, and halt rules.
Looks-good output from a model is not a merge stamp. A traffic percentage is not a style choice. It is a budget against measured error.
This article stays on when not to use the free lane. It is a field guide, not a product tour.
What the free lane may still do
A free coding model can still help on the edges. Keep that help far from the live switch.
The allowed uses include the following bounded items.
- Draft a glossary of cutover terms for the team wiki
- Turn a human outline into a Markdown skeleton
- Generate unit tests for a pure planner function
- List reviewer checks after humans pin the numbers
Those outputs stay advisory. They never become the live cutover controller. They never become the pull-request merge stamp.
Red flags for a free model
Stop the free lane when any flag below appears.
- The model chooses the traffic percentage without a measured error budget.
- The model invents the dual-write duration from typical folklore.
- The model names a rollback owner who is not on the roster.
- The model writes the feature-flag default to on.
- The model proposes deleting the old writer during the same window.
- The model treats checksum mismatch as a retry, not a halt.
- The model stores the cutover ledger only in chat history.
- The model mints idempotency key formats for money movements.
Any one flag is enough. Two flags mean the draft is unsafe.
Red flags for a free server
A free shared server is the wrong control plane. Cutover control needs a pinned runtime with a named owner.
Stop that host when any flag below appears.
- The process must hold the traffic-shift lease
- The process must write the dual-write ledger
- The process must page humans on checksum failure
- The process must keep the only copy of the freeze clock
- The job needs a stable egress IP for store allowlists
- The host can sleep, preempt, or recycle without notice
Drafting a checklist on a free server can be fine. Running the live controller there is not acceptable.
Disclosure: This article was prepared as part of MonkeyCode's product outreach. MonkeyCode offers free model access and a free server option. Those lanes fit glossary drafts and planner unit tests. They do not fit the live cutover controller.
Artifact: a pinned plan file
The team should store a cutover plan as versioned JSON. Pinned humans must fill every timed field. Automation rejects a free-lane author on those fields.
The schema below is a labeled proposal. It has not governed a live payment cutover.
{
"plan_id": "cutover-ledger-2026-09-19",
"service": "ledger-writer",
"authors": {
"draft": "free-lane-model",
"timed_fields": "human:sre-oncall",
"rollback_owner": "human:payments-tl"
},
"traffic": {
"initial_percent": 1,
"step_percent": 4,
"max_percent_before_freeze": 20,
"error_budget_ppm": 50
},
"window": {
"freeze_start_utc": "2026-09-20T02:00:00Z",
"freeze_end_utc": "2026-09-20T06:00:00Z",
"dual_write_required": true,
"old_writer_delete_same_window": false
},
"halt": {
"on_checksum_mismatch": "halt",
"on_retry_storm": "halt",
"on_free_lane_author_for_timed_fields": "halt"
},
"idempotency": {
"key_format": "human-pinned:ledger-v1",
"author": "human:payments-tl"
},
"controller": {
"runtime": "pinned-paid-path",
"free_server_forbidden": true
}
}
Keep the file next to the service. Teams should review that file like production code. Bot approval on this file should fail CI.
Artifact: a Node validator
The Node validator below is a lab example. Run it in CI against the committed plan. Do not treat it as a production orchestrator.
#!/usr/bin/env node
"use strict";
const fs = require("fs");
const FREE_LANE_MARKERS = [
"free-lane",
"free-model",
"free-server",
"chat-draft",
];
function isFreeLane(value) {
const s = String(value || "").toLowerCase();
return FREE_LANE_MARKERS.some((m) => s.includes(m));
}
function fail(msg, errors) {
errors.push(msg);
}
function validate(plan) {
const errors = [];
const timedAuthor = plan?.authors?.timed_fields;
const rollback = plan?.authors?.rollback_owner;
const traffic = plan?.traffic || {};
const window = plan?.window || {};
const halt = plan?.halt || {};
const idem = plan?.idempotency || {};
const controller = plan?.controller || {};
if (isFreeLane(timedAuthor)) {
fail("timed_fields must not come from a free lane", errors);
}
if (!String(timedAuthor || "").startsWith("human:")) {
fail("timed_fields need a pinned human owner", errors);
}
if (isFreeLane(rollback) || !String(rollback || "").startsWith("human:")) {
fail("rollback_owner must be a named human", errors);
}
if (!(traffic.initial_percent >= 1 && traffic.initial_percent <= 5)) {
fail("initial_percent must sit between 1 and 5", errors);
}
if (traffic.error_budget_ppm == null || traffic.error_budget_ppm > 100) {
fail("error_budget_ppm must be pinned at or below 100", errors);
}
if (window.old_writer_delete_same_window !== false) {
fail("old writer must survive the first window", errors);
}
if (window.dual_write_required !== true) {
fail("dual_write_required must stay true during cutover", errors);
}
if (halt.on_checksum_mismatch !== "halt") {
fail("checksum mismatch must halt, not retry", errors);
}
if (isFreeLane(idem.author) || !String(idem.key_format || "").startsWith("human-pinned:")) {
fail("idempotency rules must be human-pinned", errors);
}
if (controller.free_server_forbidden !== true) {
fail("controller must forbid a free server runtime", errors);
}
if (String(controller.runtime).includes("free")) {
fail("controller runtime must not be a free server", errors);
}
return errors;
}
module.exports = { validate, isFreeLane };
if (require.main === module) {
const path = process.argv[2];
if (!path) {
console.error("usage: node validate-cutover-plan.js <plan.json>");
process.exit(2);
}
const plan = JSON.parse(fs.readFileSync(path, "utf8"));
const errors = validate(plan);
if (errors.length) {
console.error("cutover plan rejected:");
for (const e of errors) console.error("- " + e);
process.exit(1);
}
console.log("cutover plan accepted: timed fields are pinned");
}
Local check commands look like the block below.
node scripts/validate-cutover-plan.js cutover/plan.json
echo $?
A failing plan should exit one. A pinned plan should exit zero. Keep the JSON in the same repo as the service.
Artifact: a tiny unit test
The test below is also a lab example. It locks the halt rule for checksum mismatch. It also locks the free-server ban.
"use strict";
const test = require("node:test");
const assert = require("node:assert/strict");
const { validate } = require("./validate-cutover-plan");
function basePlan() {
return {
authors: {
draft: "free-lane-model",
timed_fields: "human:sre-oncall",
rollback_owner: "human:payments-tl",
},
traffic: { initial_percent: 1, error_budget_ppm: 50 },
window: {
dual_write_required: true,
old_writer_delete_same_window: false,
},
halt: { on_checksum_mismatch: "halt" },
idempotency: {
key_format: "human-pinned:ledger-v1",
author: "human:payments-tl",
},
controller: {
runtime: "pinned-paid-path",
free_server_forbidden: true,
},
};
}
test("accepts a human-timed plan", () => {
const errors = validate(basePlan());
assert.equal(errors.length, 0);
});
test("rejects free-lane timed fields", () => {
const plan = basePlan();
plan.authors.timed_fields = "free-model-bot";
const errors = validate(plan);
assert.ok(errors.some((e) => e.includes("timed_fields")));
});
test("rejects checksum retry and free-server runtime", () => {
const plan = basePlan();
plan.halt.on_checksum_mismatch = "retry";
plan.controller.runtime = "free-server";
const errors = validate(plan);
assert.ok(errors.length >= 2);
});
Run that test with the command shown below.
node --test scripts/validate-cutover-plan.test.js
A red test means the policy drifted. Fix the validator before the freeze window. Do not hot-patch policy during dual-write.
The CI sketch below is a labeled proposal. It only gates the plan file. It does not shift live traffic.
name: cutover-plan
on:
pull_request:
paths:
- "cutover/*.json"
- "scripts/validate-cutover-plan.js"
- "scripts/validate-cutover-plan.test.js"
jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "20"
- run: node scripts/validate-cutover-plan.js cutover/plan.json
- run: node --test scripts/validate-cutover-plan.test.js
Decision table
| Signal | Free model | Free server | Better path |
|---|---|---|---|
| Draft glossary | Allowed | Allowed | Wiki pull request |
| Fill timed percentages | Forbidden | Forbidden | Human plus error budget |
| Name rollback owner | Forbidden | Not applicable | On-call roster |
| Hold traffic-shift lease | Forbidden | Forbidden | Pinned controller |
| Write dual-write ledger | Forbidden | Forbidden | Durable store |
| Pin idempotency key format | Forbidden | Forbidden | Human-owned spec |
| Generate planner unit tests | Allowed | Allowed | CI on a pinned runner |
| Delete old writer | Forbidden | Forbidden | Later, after soak |
The table is a policy aid. It is not a capacity or latency plan. Teams should replace numeric bounds with their own budgets.
Better alternatives
Use the free lane as a stenographer, not a dispatcher. A human should write the outline first. The model only expands already pinned headings.
Pin every timed field in git. Require two human reviewers on that file. Reject a bot LGTM on the plan JSON.
Run the controller on a pinned path. That path needs a named owner and a retention policy. Chat logs are not a retention policy.
Keep checksum failure as a halt. Retries belong only to idempotent downstream workers. They do not belong to the traffic switch.
Rehearse the halt path in a lab. Use synthetic dual-write drift, not customer rows. Record who halted and how long recovery took.
Exit criteria
Leave the free-lane draft the moment any criterion trips.
- A timed field changed without a human commit
- The rollback owner is a model identifier
- The controller hostname maps to a free server
- Checksum mismatch is mapped to retry
- The old writer is scheduled for same-window delete
- The only freeze clock lives in a chat thread
- Feature flags default to on in the draft
- Idempotency key rules arrived from a chat model
After exit, freeze traffic at the last good percent. Restore the human-authored plan from git. Rehearse the halt path before the next window.
Limitations
This guide does not time a real cutover. The validator does not talk to a service mesh. The numeric bounds are examples, not SLOs.
The approach assumes a dual-write style migration. It does not cover shadow reads without writes. It does not cover multi-region leader election.
Do not use this approach when no human rollback owner exists. Do not use it as a reason to skip load tests. Do not use it to bless prompt-built production switches.
Teams without a pinned controller should delay the cutover. A polished checklist is not a control plane.
Who should skip this
Skip this pattern in a few documented cases.
- Solo prototypes with no customer data
- Read-only shadow traffic with no dual write
- Docs-only migrations that never touch stores
- Lossy workloads where a missed write is acceptable
Those cases can stay on a free draft lane. Ledger money, identity, and medical records cannot.
A free model can still write the glossary. A free server can still host validator tests. The live switch stays on a pinned path.
Teams already drafting in MonkeyCode should keep that work narrow. Limit it to glossary text and planner unit tests. Move timed fields into a human commit before freeze.
Top comments (0)