A composite scene, not a specific company: a platform lead rolls out a free AI coding lane to 40 engineers. Six weeks later procurement asks the only question that matters — "if this disappears next quarter, how long are we down?" Nobody in the room knows. The feature matrix was beautiful. The exit path was never drawn.
That gap is the whole decision. Not tokens per dollar. Not which model wins a benchmark this month. The variable that reverses a free-lane decision is usually time-to-leave, and most teams never measure it.
Why feature matrices lie about this
Feature comparisons feel symmetric. Free lane has X, self-hosted has Y, paid has Z. Tidy rows, checkmarks, done.
But the costs are asymmetric, and the asymmetry runs one direction. Adopting is a one-way door you walk through in a sprint. Leaving is a project nobody scoped. So the matrix compares features while the risk lives in the seam between you and the provider.
So ask the uncomfortable version instead: how many files know your provider's base URL? If the answer is "a lot," you don't have a lane. You have a dependency with a nice logo.
The drill: 90 minutes, four numbers
An exit drill is a timed rehearsal of leaving. You don't need permission, budget, or a migration plan. You need a fallback lane and a stopwatch.
Run it once, before you standardize. Run it again every quarter. Here's the shape:
- Draw the seam. List everything that touches the provider: base URL, auth token, prompt templates, eval fixtures, CI job, IDE config. If that list is longer than five items, stop and fix that first.
- Time the export. Snapshot prompts, evals, and lane config into one archive.
- Time the swap. Point the seam at the fallback and get a green build.
- Time the recovery of quality. Run the same eval fixture on both lanes and diff the pass rates.
Four numbers fall out: T_export, T_swap, T_green, and Delta_pass. Those four beat any scorecard I've seen, because they're measured rather than argued.
A drill script you can copy
Template only — I have not executed this against your repo, and your smoke:ai / evals targets are yours to define. The script's only job is honest timing.
#!/usr/bin/env bash
# exit-drill.sh — TEMPLATE. Not executed for this article.
set -euo pipefail
DRILL_DIR="${DRILL_DIR:-./exit-drill}"
STAMP=$(date -u +%Y%m%dT%H%M%SZ)
LOG="$DRILL_DIR/drill-$STAMP.log"
mkdir -p "$DRILL_DIR"
now_ms() { python3 -c 'import time; print(int(time.time()*1000))'; }
step() { # step <label> <command...>
local label="$1"; shift
local start end status
start=$(now_ms)
if "$@" >>"$LOG" 2>&1; then status=pass; else status=fail; fi
end=$(now_ms)
printf '%-10s %-4s %7d ms\n' "$label" "$status" "$((end-start))" | tee -a "$LOG"
}
step export tar -czf "$DRILL_DIR/artifacts.tgz" prompts evals .ai-lane.env
step swap ./scripts/point-lane.sh --base-url "$FALLBACK_BASE_URL"
step build npm ci --silent
step smoke npm run smoke:ai
step evals npm run evals -- --out "$DRILL_DIR/evals-$STAMP.json"
step restore ./scripts/point-lane.sh --base-url "$PRIMARY_BASE_URL"
Two guardrails make the drill meaningful:
-
One seam file only. If
src/lane.tsisn't the single place that readsAI_BASE_URL,T_swapis fiction. - A CI check that keeps it that way. Grep for hardcoded provider hosts outside the seam and fail the build. Crude, effective, five lines.
// src/lane.ts — the only file allowed to know who serves your tokens.
export const lane = {
baseUrl: required('AI_BASE_URL'),
token: required('AI_TOKEN'),
};
function required(name: string): string {
const v = process.env[name];
if (!v) throw new Error(`${name} is unset — lane config is broken`);
return v;
}
Turn the numbers into a call
Now the part teams skip: actually writing down what each result means. Here's the table I'd put in the decision record. Thresholds are illustrative — set yours before you run the drill, or you'll rationalize afterward.
T_swap |
Delta_pass |
Reading | Call |
|---|---|---|---|
| < 1 hour | < 3 pts | Lane is swappable; the free option is a real option | Pilot broadly, re-drill quarterly |
| < 1 hour | 3–10 pts | Cheap to leave, expensive to match quality | Pilot on low-blast-radius work only |
| 1 hour–1 day | any | One-way door in disguise | Fix the seam before adding users |
| > 1 day | any | You're not choosing a lane, you're merging with it | Stop; negotiate terms instead |
Note what the table refuses to do: it never says a free lane is good or bad in the abstract. It says your exit cost decides whether the free lane is a lever or a lock-in with better marketing.
And sensitivity matters more than the point estimate. A T_swap of 40 minutes is fine until the seam grows three integrations and it becomes two days. Track the trend, not just the reading.
Costing it without pretending precision
Free is not zero, and paid is not the alternative. The honest comparison is monthly cost of ownership per lane:
monthly = (tokens_used / 1e6) * price_per_million
+ engineering_hours * loaded_hourly_rate
+ exit_cost / expected_months_on_lane
That third term is the one nobody fills in. exit_cost is roughly T_swap + T_green converted to hours, times the loaded rate, plus a rework buffer. Leave it out and the free lane always wins — which should tell you something about the model, not the product.
Fill it with your own numbers. If the third term is under ~10% of the total, ignore it and move on. If it's over a third, you have an architecture problem that pricing can't solve.
Where a free lane actually earns its place
Here's the useful role for a second, free lane — and this is where MonkeyCode fits the method rather than the marketing. The drill needs a real fallback: a lane you can actually point at and get a green build, not a hypothetical one. A free lane gives you that for zero marginal cash, which means you can rehearse leaving without a purchase order.
Disclosure: This article was prepared as part of MonkeyCode's product outreach.
The operator describes MonkeyCode as an open-source project offering free model access and a free server option. I'm deliberately not quoting a token count or a hardware spec here — those numbers move, and a stale figure in a blog post is worse than no figure. Check the project's current page before you build a plan on it; verify, don't inherit my sentence.
What makes it a good drill target is structural, not promotional: it's a distinct lane, so a swap against it produces a real Delta_pass. A fallback that resolves to the same provider proves nothing.
Who should not use this approach
Be honest about the edges:
- Regulated data-residency work. A free lane you can't audit end-to-end is a non-starter, drill or no drill.
- Anything needing a contractual SLA. Free access is a courtesy, not a commitment. Don't route on-call incidents through it.
-
Teams whose
T_swapis already under an hour on paid infrastructure. You don't need this exercise; you need a decision. - Anyone who reads "free" as "cheap." The drill exists precisely because free lanes hide cost in the exit.
Owner, expiry, exit criteria
A decision without an expiry is a habit. Write three lines into the same doc as the drill results:
- Owner: one name, not a team alias.
-
Expiry: re-drill on the next quarter boundary, or when
T_swapmoves 2x, whichever comes first. -
Freeze rule: if
T_swapexceeds one day, no new integrations until it's back under.
That freeze rule is the one that saves you. Growth in a lane is silent; a threshold is not.
The question to argue about
Forget "would you adopt this?" Nobody learns anything from that. The better argument is narrower: what would have to be true for your drill to fail? A fallback that can't run your eval fixture. A seam that leaked into twelve files. A Delta_pass of 15 points that no amount of prompt tuning closes.
Find that number for your team, write it down, and run the drill. If it passes, the free lane is a lever you can pull and release. If it fails, you just avoided a migration you'd have paid for twice — once in engineering, once in trust.
Top comments (1)
The quality check is an important part of this. Getting a green build after switching providers doesn't necessarily mean the generated code behaves the same in production. The evals can only cover the scenarios you've included.