Effective Vibe Coding

7 min read Original article ↗

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:

  • tests that pass or fail
  • linters and static checks that pass or fail
  • build steps that succeed or fail
  • policy checks that block risky patterns
  • regressions that get caught every time

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:

  • add a new static rule
  • add a new forbidden pattern
  • add a new test that catches it earlier next time
  • you don't do this, the agent does
  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:

  • clear requirements and acceptance criteria
  • unit tests and meaningful coverage
  • readable, idiomatic code
  • comments where intent matters
  • documentation that helps the next person (or the future you)
  • logging, error handling, and basic operational sanity

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:

  • docstrings + type hints (or your language equivalent)
  • configurable logging (not print spam)
  • clear structure and naming
  • a README that explains purpose, how to run, how to test, troubleshooting, and what changed

Add Regression “Tripwires” Beyond Standard Linting

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

  • “fixes” that delete big chunks of code
  • subtle rewrites that break invariants
  • copy/paste duplication
  • suspicious shortcuts that technically pass but violate intent

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:

  • forbid certain APIs in certain layers
  • block known-bad patterns that caused bugs before
  • detect large deletions or sweeping refactors during “small fix” tasks
  • flag TODO explosions, dead code, or duplicated logic

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:

  • try to “cheat” the tests
  • contort the codebase into something unrecognizable
  • wipe or rewrite large parts of the project - Ask me how I know (Actually don't. Ouch.)
  • thrash in loops until everything is worse

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:

  • initialize: read current instructions, establish goal and scope
  • plan: write down approach, identify risky areas
  • test-first: write tests before implementation
  • implement: small changes, tight loop
  • verify: run the full gauntlet (tests + lint + static checks)
  • finalize: record lessons learned, codify regressions, archive/report

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:

  • Put deterministic gates around the agent.
  • Force TDD into the loop.
  • Treat the agent like a junior dev and require junior-dev-quality work.
  • Codify regressions as automated rules.
  • Provide an escape hatch and a stuck protocol.
  • Use phases so progress is real, repeatable, and reviewable.

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.