All posts

Vibe coding without being reckless

Letting the model drive is fine. Letting it drive into production without a gate is not. A short, practical split between the parts of a change you can vibe and the parts that need a deterministic check.

Vibe coding — describing what you want, accepting what the model writes, running it, and iterating on the result rather than the code — is a genuinely good way to build a lot of software. Most of a product is UI, glue and business logic that is cheap to be wrong about, because being wrong shows up immediately and costs a retry. The reckless part is not the vibe. It is applying the same loop to the small set of changes where being wrong shows up later, at scale, and is not undone by a retry.

So the question is not "should I vibe code" but "which parts of this change are safe to vibe". The answer turns out to be short.

The parts you can vibe

Anything with a fast, honest feedback loop. If a mistake makes a test fail, a page render wrong, or an API return the wrong shape, you will see it within seconds and the model will fix it on the next turn. Components, handlers, transformations, most refactors, most tests. Let it drive. Read the diff the way you would read a colleague's, not the way you would audit a contractor's.

The parts you cannot

Three kinds of change have no fast feedback loop, because the failure mode does not exist in a development environment.

  1. Schema migrations. On a dev database with forty rows, every migration is instant and every migration works. ALTER TABLE orders ADD COLUMN status text NOT NULL (BV002) succeeds on an empty table and fails on a full one. An index built without CONCURRENTLY (BV003) takes a second locally and blocks writes for twenty minutes in production. The model has seen millions of migrations and almost none of them ran against a table with a hundred million rows, so it has no reason to prefer the safe form. Neither does your test suite.
  2. Credentials. A model that needs a database URL to make a script work will happily inline the one it found in .env, and a model that is asked to "make the migration self-contained" will do the same. The commit passes every test. It also passes the connection string into git history, where it stays. Nothing in the vibe loop flags this, because nothing in the vibe loop is looking.
  3. Destructive data operations. DROP COLUMN (BV030), DELETE without a WHERE, a backfill that rewrites every row. These are correct in the sense that they do exactly what was asked. They are only wrong in the sense that the previous deploy is still reading the column, or that the rows were not yours to delete. That context lives in the deploy window and the business, not in the code the model can see.

What these have in common is that the person or agent making the change cannot observe the consequence before committing to it. The loop is open. Something has to close it.

Close the loop with a rule, not with vigilance

The tempting answer is "just review migrations carefully". It does not survive contact with a team that ships forty pull requests a day, half of them opened by agents, at eleven at night. Vigilance is a budget; it runs out exactly when the migration count goes up.

The answer that scales is a gate that does not get tired. For the three categories above, the dangerous forms are known and enumerable — locks, rewrites, missing defaults, missing timeouts, credentials in the diff — which means they can be checked deterministically: the same input, the same verdict, every time, with a rule id and a fix attached. That is what Bolvrk is. It sits at the three places a generated change passes through, and it does not matter which one catches it.

  • Before the draft leaves the agent — the MCP server gives the agent a check tool, and the skills library tells it to call the tool before proposing SQL. The agent revises its own migration; you never see the first draft.
  • Before you commitnpx bolvrk check migration.sql in the terminal, and bolvrk secrets on the diff or on any output the agent pasted. No account, exit code 1 on findings.
  • Before it merges — the GitHub Action fails the job above the severity your team chose and explains each finding on the pull request. This is the one that holds when the first two were skipped.

A working rule of thumb

Vibe everything whose mistakes you would see in the next sixty seconds. Gate everything whose mistakes you would see in the next sixty days. The gate should be a program, not a person, and it should give the same answer to the agent that it gives to you.

That is the whole discipline. It costs one command per migration and one Action in the repo, and it lets you keep the speed for the ninety percent of changes that deserve it.

Check the last migration an agent wrote for you

npx bolvrk check runs the free rules locally — no account, no config, the same verdict every time.