Steven A. Thompson

Effective Vibe Coding

Determinism

Vibe_Coding_1

LLMs are stochastic. That’s not a flaw in the design, it’s literally the point. If you ask for the same thing twice you will probably get two different answers, two different implementations, and two different interpretations of what you meant.

The vibe coders problem, of course, is that software is expected to be deterministic. Users want the same input to produce the same output. Teams want reproducible builds. You want a bug fix that stays fixed.

So if you’re vibe coding and hoping for deterministic outcomes, the answer isn’t “find the perfect prompt.” The answer is: bolt deterministic gates into the loop and treat the agent like a junior developer.

I am regularly surprised to see otherwise competent and capable software engineers fail to apply even the most basic standards to agentic coding tools. They'd never let their juniors get away with this stuff, why is it OK when an agent does it? Worse still, I now regularly see entire products released to the public that promise vibe coding results, but the developers have completely failed to understand that these agents are stochastic and design accordingly.

You are Still Programming (English Is Just the New Wrapper)

A program is a set of instructions that makes a machine do a task. LLMs simply let you use English as an intermediary language. Personally, I think it's neat that more people can build, but it also tempts people to forget the basics.

Some developers who would never skip fundamentals in Python or JavaScript suddenly act like quality output is “impossible” because the model didn’t deliver. In reality, many of them would get similarly bad results if they handed a junior dev vague requirements, no acceptance criteria, and no feedback loop beyond “try again.”

Vibe coding doesn’t replace best practices. It just changes how you deliver instructions.

HOWTO: Deterministic Gates Around a Stochastic Agent

Vibe_Coding_2

If you want deterministic outcomes from a stochastic system, you don’t “convince” the model to be deterministic. You constrain it.

That means using multiple deterministic checks that define “done” in a way the agent can’t wiggle around:

The LLM can be creative inside the sandbox. Your gates decide what ships.

All of this is relatively trivial to build with the help of your agent. Just ask it to develop a small "gatekeeper" script that enforces these standards at the end of every phase and doesn't allow it to continue until it passes.

Your AGENTS.md, CLAUDE.md, GEMINI.md, Etc determine when and how the gatekeeper is reached so just ask the agent to build the process into it's own .md file. Feed it this blog post if you want to use it as a head start.

Force TDD Into the Loop

The cleanest gate is still the oldest one: tests.

A practical agent loop looks like this:

  1. Agent writes tests first. You (or even better a more expensive “thinking” model you make an API call to) verify the tests are actually testing the right thing.

  2. Agent writes the code.

  3. Run deterministic hygiene checks. Standard linters + your “regression sniffers” (more on that in a second).

  4. Run the deterministic test suite. Pass/fail. No vibes.

4.5) If it fails, don’t just retry, LEARN. When the agent causes a recognizable regression, codify it:

  1. Only after gates pass do you move to the next phase.

This is the big mindset shift: the agent isn’t the source of correctness. The gates are. The agent is still just throwing stuff at the wall to see if anything sticks, but now we're checking the wall for correctness and can enforce real standards!

Treat the Agent Like a Junior Dev (And Expect Junior-Dev Standards)

Vibe_Coding_3 A competent junior dev is absolutely capable of producing great work if a senior teaches them how to do that:

So require the same from your agent.

One of the easiest wins in vibe coding is documentation. It used to be a PIA. Almost nobody likes writing the docs (excepting a few masochists I know). Now it’s basically free: if you demand it. Make “update the README” part of the definition of done, every phase, every time. Add a FAQ that's updated in real time! Why not, it's basically free!

A solid “junior-dev-grade” checklist usually includes:

Add Regression “Tripwires” Beyond Standard Linting

Standard linting catches style and some correctness issues. Agents also create weirder failure modes:

This is where extra deterministic gates shine—simple static checks that look for patterns you’ve learned to hate.

Examples of “tripwires” you can add without tying yourself to any tool:

The meta-rule: when the agent hurts you once, automate the prevention. Make tools like AST Grep your friend.

You Need an Escape Hatch (Or the Agent Will Get Weird)

If you don’t design an escape hatch, agents will invent one.

When stuck, a model may:

So bake in a sane stuck-state protocol, just like you would with a junior dev:

  1. Consult an expert (a stronger model, a second opinion, a senior dev, I use a little python script that calls a thinking model).
  2. Pair-program with another agent in short rounds: try suggestion → verify → report → refine (with a hard cap on attempts).
  3. Escalate to the human and halt if constraints conflict or progress requires breaking core rules.

That last part matters: halting is a feature. It prevents “progress” from becoming damage. It's inevitable the agent lands between a rock and a hard place at least once in awhile. Give it some way out other than "delete everything" or "refactor the entire Python project into rust for some damn reason."

Teach Patience With Phases (And Don’t Skip Verification)

Agents love to sprint. Good engineering is often a slow walk with frequent checkpoints.

Phase-based workflows help:

This keeps the agent from “doing everything at once” and gives you clear control points.

The Vibe Coding Bottom Line

Vibe_Coding_4

LLMs are stochastic. Software needs to be deterministic.

So:

Vibe coding isn’t magic. It’s engineering with a new kind of teammate.

If you build the right loop, and force determinism the vibes can be fun and the output can be solid.