One Question, Three Dead Ends, One Exit Table: A Pairing Session on Free-Tier Infrastructure
The session produced no new prompt and no new vendor. It produced one script, one table, and a stop rule that outlived the afternoon. The kept decision was a portability proof: a single command that rebuilds the application from the repository and re-runs the same contract tests against whichever model route or host happens to be configured that week. Everything else discussed in the call was recorded as a dead end, with the reason it failed written down next to it.
The decision, stated before the story
A small internal tool had been assembled quickly on top of free model access and a free server option, and nobody could answer a simple question about it. The pair on the call were a mid-level engineer who had written most of the code and a senior engineer who had reviewed none of it. The senior's contribution was one question, asked early, and then repeated whenever the conversation drifted. The output of the session was deliberately narrow, because the alternative was another week of prompt tuning.
The tool itself is unremarkable and that is the point. It accepts a unified diff, calls a model, and returns a structured summary containing three fields: a short prose summary, the list of touched files, and a coarse risk label.
Step 1 — Record what the application actually depends on
Before any code was touched, the pair wrote down an inventory of state, sorted by who owns it. Purely local logic covered the diff parser, the response validator, and the rendering layer, and all of that could run with no network at all. The model call was external by definition, and the job store lived on the free server that had been used for the first deploy. The inventory mattered because it converted a vague worry about free tiers into three named dependencies with three different exit paths.
A grep over the source produced the configuration surface, which is usually smaller than people assume. The command below is the exact one used in the session, and it works for most TypeScript or Python services without modification.
grep -RIn --include='*.ts' --include='*.py' -E 'process\.env\.[A-Z_]+|os\.environ\[["'"'"'][A-Z_]+' . \
| sort -u
The result was reviewed line by line against .env.example, and any key that appeared in code but not in the example file was treated as a defect. That single check removed the most common cause of "it works on the free server but not locally" reports.
MonkeyCode's free model access and free server option were the starting point for this build, and both are availability claims rather than service guarantees. Quotas, limits, and hardware characteristics should be read from the project's own documentation on the day you depend on them, because free-tier terms change. Disclosure: This article was prepared as part of MonkeyCode's product outreach.
Step 2 — Three dead ends, and why each one closed
The first dead end was the most tempting one: add more context to the prompt so the model would stop returning malformed responses. Roughly half the call went into this before anyone noticed that the failure class had not changed at all. The parser was rejecting a field that the prompt had never asked for in a machine-checkable way, so better prose could not fix it.
The second dead end was architectural politeness. Someone proposed a managed queue and a small cache to smooth over cold starts on the free host, which would have added two dependencies that each needed their own exit path. The senior's objection was blunt and correct: hiding a cold start does not remove the dependency, it only makes the dependency harder to observe.
The third dead end was the argument about reliability numbers, which nobody could settle inside the session. Neither engineer had a primary source open, and both were quoting remembered figures about uptime and rate limits. The pair agreed to stop discussing any number that could not be pasted from documentation with a date attached.
| Dead end | What it looked like | Why it closed |
|---|---|---|
| Prompt expansion | More instructions, more examples, same rejection | Failure was in the parser contract, not the prose |
| Managed queue and cache | Two new services to absorb cold starts | Converted one observable dependency into three |
| Uptime debate | Recalled figures with no source | No primary source available in the session |
Step 3 — The kept decision: make portability a command
The decision that survived was to prove, mechanically, that the application can be rebuilt and booted from the repository alone. This is not a migration plan and it is not a claim about anyone's uptime. It is a report that either passes or fails on a laptop, and it takes a few minutes to run. The script below writes a Markdown report so the result can be attached to a pull request instead of described in a meeting.
#!/usr/bin/env bash
# scripts/portability_check.sh
set -euo pipefail
REPORT="${REPORT:-portability-report.md}"
APP_DIR="${APP_DIR:-.}"
RUNTIME="${RUNTIME:-docker}"
BASE_URL="${BASE_URL:-http://localhost:8080}"
{
echo "# Portability report"
echo
echo "Generated: $(date -u +%Y-%m-%dT%H:%M:%SZ)"
echo
echo "| Check | Expected | Observed | Verdict |"
echo "| --- | --- | --- | --- |"
} > "$REPORT"
row() { printf '| %s | %s | %s | %s |\n' "$1" "$2" "$3" "$4" >> "$REPORT"; }
fail=0
if "$RUNTIME" build -t portability-proof:local "$APP_DIR" >/dev/null 2>&1; then
row "image build" "exit 0" "exit 0" "pass"
else
row "image build" "exit 0" "non-zero" "fail"; fail=1
fi
"$RUNTIME" run --rm -d --name pp -p 8080:8080 \
--env-file .env.example portability-proof:local >/dev/null
sleep 3
if curl -fsS --max-time 5 "$BASE_URL/healthz" >/dev/null; then
row "health endpoint" "200" "200" "pass"
else
row "health endpoint" "200" "unreachable" "fail"; fail=1
fi
"$RUNTIME" stop pp >/dev/null 2>&1 || true
exit "$fail"
The second half of the proof is a contract test that never touches the network. It validates the response shape against a recorded fixture, so the parser can be exercised on a train with no connectivity.
# scripts/contract_check.py
import json, sys
REQUIRED = {"summary": str, "files_touched": list, "risk": str}
ALLOWED_RISK = {"low", "medium", "high"}
def check(payload: dict) -> list[str]:
errors = []
for key, kind in REQUIRED.items():
if key not in payload:
errors.append(f"missing key: {key}")
elif not isinstance(payload[key], kind):
errors.append(f"{key}: expected {kind.__name__}")
risk = payload.get("risk")
if isinstance(risk, str) and risk not in ALLOWED_RISK:
errors.append(f"risk outside allowed set: {risk}")
return errors
if __name__ == "__main__":
payload = json.load(open(sys.argv[1]))
problems = check(payload)
print("\n".join(problems) if problems else "contract ok")
sys.exit(1 if problems else 0)
Step 4 — Write the exit table next to the code
The exit table is the artifact most likely to be reused by someone who was not on the call. Each row names one external dependency, the reason it exists, the concrete path away from it, and the signal that would force the team to walk that path. The detection signal column is what separates this from a wish list, because it has to be observable in CI or in the report above.
| Dependency | Required for | Exit path | Detection signal | Re-check |
|---|---|---|---|---|
| Model route | Summaries | Swap base URL and key; parser already covered by fixture test | Contract check fails twice on main
|
30 days |
| Free server | Hosting | Image builds and boots from the repository | Build or health check fails | 30 days |
.env.example |
Configuration | Only listed keys are read by the app | Env grep finds an undocumented key | 30 days |
A short decision record sits above the table, so the reasoning survives the people who wrote it. The pair used five lines and no ceremony, because a heavier format would not have been filled in.
Decision D-14 — portability proof before new features
Kept because: the boot path is the only thing the team controls outright
Rejected: prompt expansion, managed queue, uptime debate
Re-check: rewrite this record after the next dependency review
Owner: whoever changes the model route or the host
Step 5 — Attach a stop rule, not a reminder
The last thing agreed in the session was a stop rule with no discretion built into it. If any row of the exit table fails twice on main in the same week, the feature under development does not ship until that row is fixed or deleted. Deletion counts, because a dependency nobody needs has no exit path to maintain.
The rule works because it is boring and mechanical. It removed the need to renegotiate the same trade-off in every review, and it gave the junior engineer a defensible answer when a stakeholder asked for a faster turnaround on the free tier.
Limitations and who should not use this
The portability proof says nothing about model quality, and it cannot validate any quota, limit, or hardware claim made by a free-tier provider. Terms change without notice, so the re-check column exists precisely because the report has a shelf life measured in weeks.
Teams handling regulated data, or operating under a contractual availability target, should not begin on a free tier at all. The overhead of building this proof is justified only when the free route is genuinely provisional, which is an organisational judgement rather than a technical one.
The scripts above assume a container runtime, curl, and a health endpoint that already exists; the contract checker assumes a response shape the reader must define. None of it was benchmarked against a paid provider, and no comparison is implied.
If the same exercise is attempted with MonkeyCode's free model access and free server option, the exit table belongs in the repository next to the code it describes, not in a document nobody opens.
Top comments (0)