How a Non-Engineer Ships Production Software
Researched and written in conjunction with Claude. All ideas mine. Future writing will be all my own words, unless otherwise noted.
A non-engineer can build production software by establishing agent governance practices — agents tuned to complement and oppose one another as “supportive adversaries” — under a written record, with human judgment reserved for product and adjudication.
[Note: I previously published another version of this. This one includes additional case examples and more personal experience.]

I had quit my job, I had an idea, and I had never coded in my life.
I didn’t know what the terminal was. But, following a suggestion from Claude, I let it tell me how to launch Code locally. That turned out to be the first and most important skill: let Claude teach you.
I’ll often pause a coding session and ask Claude to summarize the feedback point by point, as if to a child — the root cause, the proposed fix, any deviations from best practice, the possible consequences. Do that again and again, day after day, and you start to see the shape of things.
I’m now building three software companies. The first product is a voice-first assistant for people who don’t work at computers and often don’t have a company email. It includes a Postgres schema of record, a hardened SMS pipeline, a real-time voice stack, and automation armed to act on its own judgment — it takes destructive action without asking. It retires database records it concludes are duplicates, and it sends morning briefings to my whole family. If it is wrong about a time, a real person shows up at the wrong hour. We go to public beta soon; the build is documented at https://maximus.work/build/.
I’ve since lifted its core components — voice AI and the memory structures — to anchor two more builds: an evidence engine for private-equity diligence, and a medicine-titration application.
What I want to share here is the governance system that came out of roughly 1,200 hours in Claude Code.
As of early September 2026, the first product is 30 tables, 68 migrations, 7 async worker loops, and just over 2,000 tests, across 259 merged pull requests in 128 days. No human writes the code. I don’t author lines, I don’t edit diffs, and I don’t fix bugs by hand — I can’t write the async Python that Maximus runs on. I read summaries, I read arguments, I ask questions, I make requests, and I decide.
Here’s the thing: you don’t have to know how to code. You also cannot trust the output of a single agent.
What follows is how I reconciled those two facts — and it started with a lesson the platform taught me.
Case 1 — the platform did what it was designed to doIssue:** May 11. I was developing in Replit, a browser-based workspace that also hosts what you build. Replit’s “publish” step compares the database in your development workspace with the production database it assumes it owns and runs SQL to make production match — including dropping any table it doesn’t see in dev. My production database was hosted elsewhere, on Supabase, and a leftover script from a library I had stopped using defined zero tables. One publish dropped every table in production. The first symptom, fifteen minutes later, was a voice call failing because
public.sessionsdidn’t exist.Resolution: Restored from that morning’s backup within an hour; six hours of data lost, which at that point was one row — mine. Within thirty-six hours the API had moved to Render, the front end to Vercel, and Replit was reduced to a development workspace. Two weeks later it was retired entirely.Learning: Convenience carries assumptions you haven’t read. And the tool that says “all your migrations are applied” is reporting history, not current state — it cannot tell you the tables are gone.
Four structural properties of agent behavior
Model capability is a given. What you engineer against is four structural properties of agent behavior. Each generates its own class of failure, and each needs a different institution to guard against it.
- No periphery. The agent sees the task and nothing around it.
- No calibration. Confidence floats free of evidence, in both directions.
- No past. Every session starts brilliant and amnesiac.
- No signal. Failures look exactly like successes.
With Claude there’s no memory and no ego. You get a renewable source of extraordinary reasoning, pattern recognition, and best-practice knowledge, with those four gaps built in. The four institutions below were developed by trial and error against exactly those gaps.
1. The shop — supplying the periphery
The agent sees the task alongside what you give it, and nothing before, after, or around it. It doesn’t see the decision that produced the request; it doesn’t see the layer beneath the symptom; it doesn’t see the system the change lands in.
So, with narrow context, agents solve problems by piling fix upon fix: the local patch is the shortest path to green, and the agent optimizes for the frame it was handed. They miss the root cause.
The missing periphery is also the source of confident re-derivation. The answer arrives fast, feels reasoned, and is wrong in a way reading the code cannot reveal, because what’s missing is a previous decision. An agent seeing only code will relitigate a settled question — sometimes by fixing a deliberate absence as if it were a bug.
My answer is the supportive adversaries from the thesis: a small number of agents, deliberately kept separate and differently tuned.
- The Builder sits in the repository and builds against product functionality.
- The Architect is tuned to structure and best practice.
- The Sleuth debugs. The last two are frequently the same agent.
I sit in the middle. Development runs locally — Docker, code driven from a terminal — and the product runs on Render, Vercel, and Supabase.
The Builder sees everything: the code, the decisions, the deferrals, the reasons. At scale it can’t hold all of it; its working view is always narrower, so it needs help. It pushes to git. I approve every merge, and migrations go up only on my command.
The Architect and the Sleuth live in the record — their own isolated folder with a partial view of the repo — and they pressure-test the Builder’s output against everything invisible in a diff. All of them operate under standing instructions: push back with arguments, not preferences; concede when wrong and say so; flag anything that contradicts the record; fix at the root; name deviations from best practice.
The contexts are separate on disk, not just by instruction. That isolation is load-bearing. Independent contexts that depend on someone remembering to keep them independent will eventually leak. Isolated folders can’t.
I can’t certify that code is structurally sound, and I don’t have to: one agent reviews another’s work and lays out the deviations, what each deviation buys, and whether the result meets the standard. I let them argue it out, then adjudicate with both arguments on the table.
*Judgment stays human. And, the answer to most disputes falls out of the product requirements, not the code.
Case — the bug that was a decisionIssue:** July 21. An agent reading code and database found that “morning” in a reminder resolved to 6 AM and reported it as a defect. Two months earlier I had chosen that convention on purpose; the reasoning was in the decisions log under a different title. The same day, a second deliberate absence — a reminder phrase that intentionally resolves to nothing rather than a guessed time — was reported as a second bug.Resolution: A new artifact: the deferral index, a table of deliberate absences organized by what is missing rather than by when it was decided, so “why is there no X?” has an answer a fresh session can find.Learning: Code that does nothing on purpose reads exactly like code that’s missing. Only the record can tell them apart. CLAUDE.md instructs the agents to check the log before fixing anything that is “missing.”
2. The loop — supplying the calibration
Agent confidence floats free of evidence. An agent will assert what it never checked, abandon what it verified when a confident counterparty pushes back, and trust its own new code because both halves test green. Left alone, it will plan a surface wider than it can debug in one pass, and it will trust a three-month-old comment, letting prose carry code’s authority without code’s executability.
Case — the test that tested itselfIssue:** August 6. For three months the code path that lets our voice agent call our own language-model endpoint had a passing unit test. The path had never once been used in production. The request format had two errors the vendor rejected, while the test checked that we built the request the way we build it — it asserted our own output back at ourselves. Nobody found it because nothing had ever needed that path. When we did need it, we found the vendor’s hosted model had a hard prompt cap above which it silently drops the end of the prompt — which is where the user’s memory lives. A call over the cap “would sound completely fine while having quietly forgotten the user.”Resolution: The format was fixed by running it against the vendor, the cap became an alarm that must read zero, and a rule went into the orientation file: any code that carries a contract with an external system must have been executed against that system at least once.Learning: Green tests measure what the test author imagined. A contract with the outside world is only verified by the outside world.
Because agents are overconfident, I keep the open surface small and isolated.
A failure can only live in a few places, and it can’t hide behind unrelated change. Surfaces are cumulative — each is verified before the next opens.
Before any unit of work, the Builder verifies the plan’s premises against the live system and opens with a packet: what I checked, what it said, what differed from the brief. It sounds bureaucratic. It’s policy, because an unstated check is an unmade check.
Findings get fixed at the root. The fix is almost always one layer down from where the problem is felt, and before any wrapper, retry, or guard is added, the standing question is: does this remove the failure, or just catch it later?
The table stakes are in place — the full suite runs before anything merges, risky features ship dark behind flags and are armed deliberately, and every fixed bug gets a regression test: the dogfooding log carries ninety-odd numbered findings, and 66 test files cite one by name. The log has an admission standard: a finding is routed to one of four layers — capture, storage, synthesis, generation — and isn't recorded until its mechanism has been reproduced.
Three practices are more distinctive:
- Grading corpus for LLMs: Anything involving a language model gets a grading corpus with its own rubric, never a single example
- Impossible-not-forbidden: Where possible, a failure is made impossible rather than forbidden: to guarantee something, I’ll use a unique index, not a scheduler promise; to remove an ambiguous state, I’ll use a default that makes it unconstructible.
- Mutation testing: Every new connection between components is mutation-tested — delete the line that supplies the value, watch a test go red. A missing connection is invisible on the page; both halves test green on their own. “Be more careful” doesn’t help, because reading cannot catch it. So the seam gets broken deliberately, once, in a test, so it can never break silently in production.
3. The record — supplying the past
Because every agent starts fresh and blind, it will do brilliant work in the wrong direction unless it gets the right context. Orchestration only works if a fresh session becomes competent within minutes, so the repository carries a layered written record, designed for AI cognition as much as human.
In practice, the record is nearly half the system’s text: inside the Python source there are 13,097 lines of comment and docstring against 15,432 lines of executable code.
That has a known failure mode — nearly half the system’s text is unexecutable and can be wrong for months without ever failing — and I’ll come back to it.
The always-loaded orientation file, CLAUDE.md, is held to a strict routing rule: if a competent new session would make a wrong call without knowing it, it’s a principle and belongs in the always-loaded layer. If it’s the record of what we chose and why, it’s a decision and belongs in the log.
The log is append-only — 146 dated entries in 128 days. Each carries the decision, the reasoning, the alternatives, what it commits us to, and when to revisit. It is wired to the code: 66 test files cite dated entries directly. When the log’s own analysis was later measured wrong, the correction was appended and labeled, never silently edited.
Beside it sits the deferral index from the case above — I’m told by Claude it’s the strangest artifact in the repo. Fifty-four rows. The rows carry no status column, because a hand-maintained status goes stale in the dangerous direction. A row claims only that the absence was considered.
Case — the decisions that didn’t existIssue:** August 8. Nine settlements — the morning briefing’s send time, how recurring reminders dispatch, what inbound SMS should do — had been decided with the Architect in a planning document that lived outside the repository. The Builder was then told, in the repo, that these decisions were binding. It could not find them, because for the agent a decision it cannot read does not exist. The round trip cost a day.Resolution: All nine were recorded in the decisions log, two days late, with an entry noting that the lateness had cost a round trip.Learning: For a fresh session, the record is the world. A decision made anywhere else was not made.
The rest of the record: the canonical schema specification; a build log; a postmortem file with tripwires; a dogfooding log; and a standing gates document naming which open items need my judgment and which are pure execution, so decisions get made in batches and everything else proceeds without me. Every layer names which document wins on conflict. Citations run by entry date and title, never by line number — the log grows from the top, and every line citation into it rots on the next entry.
An agent finishing a work arc writes a handoff for the next one: current state, what was decided, what to read first, what’s deliberately unfinished, what’s next.
4. The instruments — supplying the signal
Wrong code eventually fails a test. Wrong judgment produces plausible output and keeps going. Wrong output is as fluent as right output; wrong wiring tests as green as right wiring; a wrong geocode returns 200.
Case — two days without memoryIssue:** August 8–10. The job that rewrites a user’s memory summary after each call failed three times on a parsing error and was set aside as a dead letter. Nothing alarmed. For two days every call opened on the same stale summary — the assistant kept treating a two-day-old “in a couple of hours” as imminent — and the only detector was that the calls felt wrong to me.Resolution: The row was re-queued, and a staleness alarm was built so a summary older than a threshold fires a counted signal. The log’s line on it: felt rather than detected.Learning: A fail-open path hides its own failure behind the feature’s normal quiet. A total failure and a quiet day look the same unless the failure is counted. A log line is not a canary.
So every miss is counted. A skipped send, a closed window, a missing row — each is a queryable, deduplicated signal, because the outage and the not-knowing are separate failures, and the not-knowing is the cheaper one to prevent. Every counter carries a false-positive budget: an alarm its reader has learned to ignore is worse than no alarm, because it reports as coverage. And any autonomous judgment that can destroy data writes an audit whose reading is scheduled work with a date on it. An audit nobody reads is not a safety mechanism.
The instruments themselves can lie, and they did — three times in one week in August, and the third reached production. A measurement relay placed between the voice vendor and the model returned a clean 200 on every request while forwarding compressed bytes labeled as plain text; the vendor retried three times and dropped the call, and I reported it as flaky network. HTTP 200 is a claim about the transaction, not about the bytes. The rule that came out of it: a pass is only as good as the provenance of the verdict, and the tell was in the logs the whole time, unattended.
Since this is armed, destructive automation over real lives, containment is part of the instrumentation. A kill switch stops every worker; each user carries a daily spend cap; consequential outbound — sends, consent, money — fails loud rather than silent; consent is an append-only event log; and because nothing is ever hard-deleted, every autonomous action is a supersede that can be superseded back.
What it misses — the price of asymmetry
A pre-work packet recently surfaced a collision between two recorded decisions that had never met. One held that a null preference means “paused.” Another assumed a default first-day send. Both correct alone, contradictory together, and found only because a build happened to force them into the same room. They had sat in my log for weeks without either knowing about the other, and nothing in my process was looking. The system caught it during the build - perhaps lucky.
In May, an agent had written down why a permissions change broke signups — a specific, mechanism-level explanation, left as a comment in a migration file. Three months later the Builder read that comment and asserted its mechanism into a new file, over the objection of the reviewer, which had just claimed the opposite. Confronted with a citation to a real incident and a claimed verification, the reviewer abandoned its correct position. Neither demanded the one experiment that would settle it. It took two minutes. It falsified the comment.
Two agents failed in opposite directions on one afternoon — one over-asserted from a citation, one over-deferred to it — and briefly established a falsehood as fact inside a record built to prevent exactly that. The misdiagnosis had sat in the repository for three months wearing the authority of documentation. My agents had written it themselves.
The blindness runs the other way too. An external review recommended five amendments to the architecture document. Three were already built. The review was written from the record, and the record lags the code.
That is the price of the asymmetry: The reviewer can’t be captured by the code’s framing, which is the point of putting it in the record — and the cost is that it can be captured by the record’s staleness.
I have measured evidence of both: a Builder wrong about a decision it missed, a Sleuth wrong about work that had already shipped. Each was caught cheaply and converted into a rule. A recorded cause is now a hypothesis until reproduced. The epistemics bind the reviewer explicitly, not only the builder. And disputes resolve by a norm both sides are held to: retract on evidence, and record the retraction with the query that settled it. This week alone the reviewer withdrew a claim about which code reads a column after a grep returned zero, and the builder withdrew a claimed mechanism after the audit rows retired it. Both retractions are in the log, beside the finding they corrected.
I am a failure mode too, and the record catches me. Last week a user’s reminders arrived fully explicit — dates, times, AM and PM — and the day’s log credited the system’s teaching. I had coached the user — my father — myself between sessions. The entry was struck: we don’t get to count that as a win. The institutions govern everyone in the room, including the one holding the gavel.
The agents’ judgment resets every morning. The institutions don’t.
My role — the need for a human
I hold the product. I own the record; most ratifications are mine, though I’m learning when to let the agents run. I adjudicate disputes, which arrive with both arguments attached and usually reduce to a question of values or product intent that no amount of code can settle.
I supply the felt experience: “this briefing reads long” is a detector no agent replicates, and it has found an encoding bug, a frozen memory, and a prompt cap.
And I decide what not to build, which is where most of the governance budget goes — fifty-four catalogued absences against a hundred and forty-six decisions is roughly the right ratio.
If you’ve operated companies, you recognize these jobs: hold the product, keep the record, verify the claims, read the audits, adjudicate honestly, resist scope. They are the jobs of a diligence lead or a board chair. What’s changed is where they apply.
The skillset this really requires
Building this way requires tolerance for unresolved uncertainty — pushing into a system you cannot fully see, on the expectation that testing and argument will resolve it, and making binding choices while the picture is still incomplete. Most of the day is asking, checking, choosing, and being corrected.
I am the least knowledgeable party in the room and still the only one who can settle what matters.
What compounds is knowledge of the agents, more than knowledge of code. A hundred and twenty-eight days in, I can feel where one is about to over-assert, where another will fold under a confident counterparty, which kind of task produces a plan wider than it can debug. The institutions above are built around those habits, and I’m also learning to read them directly, which makes the redirection faster as the institutions get better.
I’m now in the very early days of building autonomous loops that harness multiple agents for coding and correction. Some tasks are suited to this; many are not.
If you’re hiring builders now — and everyone in an executive role should be building — I’d prize curiosity, humility, agency, and tolerance for ambiguity above all else.
The system is in early beta. “Production” here means armed, destructive, daily automation over a system of record for real lives with a live voice path and real instrumentation. It does not mean scale, and the method hasn’t been proven with a cohort; those tests are ahead, and the record will say honestly how they go.
The narrower claim I’ll defend: the failure modes that make AI agents unsafe to build with are governable, and governing them is a discipline with a shape — a matter of institutions, not of better prompts or a better model.