Last week I decided to stop trusting a code review that never happened. A free model can generate a perfect-looking webhook receiver, and your laptop will run it without blinking. A free server, though, is a different animal: it ships a fixed Python, an empty package cache, and a disk that forgets everything on reboot. These notes cover the 48 hours I spent trying to make that mismatch visible, and the one probe script that changed the outcome.
Disclosure: This article was prepared as part of MonkeyCode's product outreach. I used MonkeyCode's free model access to generate the service and MonkeyCode's free server option to host it. This is a single, deliberately small walkthrough, not a benchmark or a performance claim.
The Experiment
The prompt was simple: "Write a FastAPI receiver that stores webhook payloads as JSONL files." The model produced a 60-line app with a validator, a rate limiter, and a lovely comment about idempotency. It looked done to me, and the free server looked at it and said no.
I wanted to know how much of that rejection was the model's fault, how much was the free tier's fault, and what a developer can actually do about it. So I kept a timeline of what broke, what I changed, and what I would repeat next time.
Day 1: Build Logs Are Your First Reviewer
The first deploy failed during startup, not because of a syntax error, but because the model assumed a Python feature that the server did not ship. The log showed an ImportError for a module that only exists in a newer runtime. My first reaction was to blame the platform, but the model had never met the platform. It was generating code for an unsaid ideal world.
I tried to fix it manually by adding a fallback import and a compatibility shim. That led to a second failure: the model's handler used a pathlib path pattern that worked locally but broke because the server's filesystem permissions were different from my laptop. At that point I stopped patching and started measuring.
What the Model Assumed
Looking back, the model made three silent assumptions:
- That the target Python version was the latest one.
- That the whole filesystem was writable.
- That the process would stay alive forever.
None of those assumptions were in the prompt, because I had not written them down. The free server was not rejecting the code; it was rejecting my sloppy context.
Day 2: Ask the Model to Probe the Environment First
The reusable idea is simple: before generating or rewriting application code for an unknown runtime, ship a tiny environment probe that prints a JSON fingerprint of the actual constraints. Here is the version I used:
# env_probe.py
import json
import os
import platform
import sys
import tempfile
def read_cgroup_mem():
for path in (
"/sys/fs/cgroup/memory.max",
"/sys/fs/cgroup/memory/memory.limit_in_bytes",
):
try:
with open(path) as f:
return f.read().strip()
except FileNotFoundError:
continue
return "unknown"
probe = {
"python": sys.version.split()[0],
"platform": platform.platform(),
"env_list": sorted(os.environ.keys()),
"cwd_writable": os.access(os.getcwd(), os.W_OK),
"tmp_writable": os.access(tempfile.gettempdir(), os.W_OK),
"cpu_count": os.cpu_count(),
"max_mem_bytes": read_cgroup_mem(),
}
print(json.dumps(probe, indent=2))
On my free server run, the output looked like this (your values will differ):
{
"python": "3.10.12",
"platform": "Linux-5.15.0-",
"env_list": ["HOME", "PATH", "PORT"],
"cwd_writable": true,
"tmp_writable": true,
"cpu_count": 2,
"max_mem_bytes": "512M"
}
How to Use the Fingerprint
Capture that JSON and feed it back into your next model prompt. Instead of letting the model guess "should be fine," you give it the actual Python version, the writable paths, and the memory limit. In my case, that single text file moved the conversation from "why does it break" to "what should we change."
The rewritten app used only the detected Python version and wrote its state to the first writable path the probe confirmed. The second deploy started without an exception. That was the high point of the 48 hours.
A minimal prompt template that worked for me:
You are generating a webhook receiver for the environment below.
Only use Python features available in {python}.
Write any durable state to a path allowed by {cwd_writable} and {tmp_writable}.
Assume the process may be paused or restarted at any time.
Environment: {env_probe_output}
What Broke After It Started
Starting clean was a trap. The free server went to sleep after fifteen minutes of inactivity. The first request after sleep woke the server, but the webhook client had already timed out. The second request arrived two seconds later and succeeded. The model had generated a handler that assumed the server was always warm.
And the JSONL files? They disappeared after every restart, because the server's disk is ephemeral. Here is how I weighed the storage options after that discovery:
| Option | Survives restart? | Works on free tier? | Notes |
|---|---|---|---|
| Local disk file | No | Yes | Good for tests, bad for history |
/tmp |
No | Yes | Even more volatile, but always writable |
| Memory only | No | Yes | Loses everything on sleep/restart |
| External storage API | Yes | Depends | Needs a separate free account |
I ended up accepting that a free server cannot promise persistence. Instead of fighting that, I made the webhook handler forward each payload to an external log endpoint, and kept local storage as a best-effort cache. The model wrote that forwarding logic easily once the constraint was in the prompt.
What I Would Repeat, and What I'd Skip
I would repeat the probe step every single time I point a model at a new runtime. Adding env_probe.py to my prompt template is now automatic. I would also repeat the decision table exercise: writing down what survives a restart changes which abstractions you need.
I would skip the impulse to make the free server do stateful work. If you need durable state, put an external store in the loop. Designing around the free tier's limits was less code, not more.
Why did I ever expect a small microinstance to behave like my laptop? That is the question the next model should answer before the first line of code is written.
Limitations
This is a single, deliberately small walkthrough, not a benchmark. Free-model and free-server capabilities change quickly; verify the current documentation before trusting any of it. My numbers are just one data point, and the probe script is meant to be read before you run it.
The technique also does not handle everything. If your app depends on a GPU, a specific binary library, or a private package repo, an environment probe will not magically make those available. It only reduces the number of silent assumptions.
Who Should Not Use This Approach
If your project requires guaranteed persistence, high availability, or audit-grade logs, a free server is not the right backbone. The probe-first prompt works for experiments, prototypes, and monitoring sidecars, but it does not turn a free sandbox into production infrastructure.
If you try this pattern, start with env_probe.py and a decision table. That will convert "the server is wrong" into a short conversation you can actually settle with a model.
Top comments (0)