A developer is ninety-one minutes into a spike, and the original question — does the free server answer before the first byte times out — has quietly been replaced by a retry wrapper nobody asked for. The spike will be reported as successful, because something works, and the reason it ran long will never be written down. What was missing was not discipline in the abstract but a clock that produced records instead of guilt, so the overrun would have shown up at minute seventy-five as data rather than as a vague feeling at minute ninety.
The instrument is small on purpose. A heartbeat script appends one JSON line every time the operator starts a phase, finishes a step, or runs a probe, and it refuses to stay silent about the budget. It does not block the editor, kill processes, or lecture anyone; it writes evidence and prints a warning band that gets earlier the closer the spike gets to its end. The whole file is forty lines and can be read in the time it takes to make coffee.
#!/usr/bin/env bash
# spike-clock.sh — a wall-clock budget that leaves records behind
set -uo pipefail
BUDGET_MIN=${BUDGET_MIN:-90}
WARN_MIN=${WARN_MIN:-75}
LOG=${LOG:-spike-clock.jsonl}
START=$(date -u +%s)
stamp() {
local phase="$1" note="${2:-}"
local elapsed=$(( ( $(date -u +%s) - START ) / 60 ))
printf '{"t":"%s","elapsed_min":%d,"phase":"%s","note":"%s"}\n' \
"$(date -u +%FT%TZ)" "$elapsed" "$phase" "$note" >> "$LOG"
if [ "$elapsed" -ge "$BUDGET_MIN" ]; then
echo "BUDGET SPENT (${elapsed}m): stop editing, write the verdict." >&2
elif [ "$elapsed" -ge "$WARN_MIN" ]; then
echo "WARN (${elapsed}m): the next action must capture evidence." >&2
fi
}
case "${1:-}" in
start) stamp start "${2:-hypothesis-locked}" ;;
step) stamp step "${2:-}" ;;
probe) stamp probe "${2:-}" ;;
verdict) stamp verdict "${2:-}" ;;
*) echo "usage: $0 {start|step|probe|verdict} [note]" >&2; exit 64 ;;
째esac
Replace that stray character in the esac line with a plain esac before running it; the script above is illustrative and has not been executed as printed in this article. The point of start is that the hypothesis file must already exist, because a clock started before the question is fixed measures typing rather than testing. An operator who runs ./spike-clock.sh start first and writes the question afterwards has simply built a faster way to drift.
The question goes into a file that is written once and never edited during the spike, which is the cheapest form of pre-registration a solo developer can buy. The falsifier matters more than the question, since it is the sentence that decides when the spike is allowed to end badly. A spike whose falsifier is "it feels slow" has no end condition, and ninety minutes becomes a suggestion.
# spike.yml — written before the first prompt, edited never
question: can the free server answer /health within the probe budget at cold start
falsifier: three sequential cold hits all exceed 3s total time
budget_minutes: 90
artifact: probe.csv plus a one-paragraph verdict
out_of_scope: warm-cache latency, concurrency, TLS tuning
The probe is where the clock earns its keep, because it converts a subjective impression of speed into three rows that either match the falsifier or do not. Three sequential hits spaced twenty seconds apart are enough to separate a cold-start problem from a permanently slow endpoint, and far too few to say anything about throughput. The distinction is the entire experiment: a spike that reports p95 from three samples has confused a thermometer with a climate model.
#!/usr/bin/env bash
# probe-cold-start.sh — three spaced hits, recorded as they happen
set -uo pipefail
URL=${1:?usage: probe-cold-start.sh https://host/health}
OUT=${2:-probe.csv}
echo "attempt,http_code,connect_s,ttfb_s,total_s" > "$OUT"
for i in 1 2 3; do
curl -sS -o /dev/null --max-time 20 \
-w "$i,%{http_code},%{time_connect},%{time_starttransfer},%{time_total}\n" \
"$URL" >> "$OUT" || echo "$i,000,,," >> "$OUT"
sleep 20
done
cat "$OUT"
One illustrative row, not a measurement from any particular host, looks like 1,200,0.041,0.612,0.640 and tells the operator that connection setup was cheap while the application took most of the time. That single observation usually redirects the remaining minutes: an operator who assumed the network was slow now knows to look at the first-request path instead. Numbers that swing hard between rows are not noise to be averaged away; they are the finding, and the verdict should say so plainly.
This is the narrow place where MonkeyCode participates. The operator-supplied availability claims are free model access and a free server option, and the reason they matter for a spike is economic rather than technical: a hypothesis that costs minutes and no card details is a hypothesis worth falsifying. The free model access covers the boring generation steps, such as drafting the probe script and summarising the CSV into a sentence, while the free server option provides the deploy target that the probe actually hits.
Disclosure: This article was prepared as part of MonkeyCode's product outreach.
Quotas, model names, hardware, and duration limits are deliberately absent from this article because they change and because a blog post is the worst possible place to learn them. The project's own documentation is the primary source, and any limit read there should be pasted into spike.yml under a constraints key before minute one. A spike that discovers a quota wall at minute eighty has learned something real, but it has learned it too late to use it.
| Observation at minute 75 | Verdict | Artifact to keep |
|---|---|---|
| Falsifier reproduced twice with the same command | ship | command, commit SHA, both logs |
| Single run, repeat within a third of the first | extend once, twenty minutes | second run log |
| No 2xx from the free server in three tries | kill | failure log, no retry patch |
| Totals swing by half between rows | kill or narrow | note naming the variance source |
The extend row is the one that needs a hard cap, since "one more attempt" is how a ninety-minute spike becomes an afternoon. Extensions are allowed once, they are logged with ./spike-clock.sh step "extension granted", and they end at the next warning band regardless of how promising the last run looked. The kill row is the one that needs courage, particularly when the endpoint returned a 502 that a retry patch would probably fix; that patch is a new spike with a new falsifier, not a rescue mission for this one.
Worth stating plainly: this workflow is wrong for teams that need a load profile, for anything touching regulated data, and for spikes whose real question is domain research rather than feasibility. It also fails when the operator cannot name a falsifier before starting, which usually means the spike is a wish rather than a test. Anyone on a paid tier with an SLA should measure that tier, because a free-tier number is not a translated version of it.
If the clock idea is useful, run it once against whatever free tier is already in the browser tab, and let the first warning band arrive while there is still time to capture something. The artifact that survives the spike should be a script, a CSV, and one honest paragraph — never a demo that only the person who built it can start.
Top comments (0)