What the Term Describes

“Vibe coding” is an informal name for a workflow in which a developer expresses intent in natural language and an AI agent proposes, edits, or explains code. It is not a replacement for software engineering, and it is not a guarantee that the result is executable, secure, or maintainable.

The productive boundary is simple:

text
intent -> constrained proposal -> inspect diff -> run evidence
       -> review failures -> revise or reject -> merge with normal controls

The model can accelerate drafting. The repository, tests, policy checks, and human owner determine whether a change is acceptable.

Where It Helps and Where It Does Not

AI assistance is often useful for:

  • exploring an unfamiliar module;
  • generating a first draft of a narrow adapter or test;
  • explaining an existing error path;
  • proposing repetitive refactors;
  • comparing implementation alternatives.

It is not a substitute for:

  • threat modeling and authorization design;
  • understanding data ownership and retention;
  • reviewing migrations and external side effects;
  • validating financial, medical, legal, or safety-critical behavior;
  • maintaining a system nobody on the team can explain.

Use a smaller, deterministic workflow when the task has a precise oracle. Use an agent only when its extra autonomy is worth the added context and control risk.

Write a Task Contract

Replace “build the whole app” with a contract. The reproducible Vibe Coding workflow shows how to apply one to a complete Go example:

text
Goal:
  Add pagination to the read-only invoice list endpoint.

In scope:
  packages/billing/src/list_invoices

Out of scope:
  schema migration, pricing, authorization policy, deployment

Inputs and invariants:
  preserve tenant and principal checks;
  reject invalid cursors;
  do not expose full customer records.

Evidence:
  focused tests, type check, formatter, and a diff summary.

Escalate:
  any new dependency, external write, credential access, or public API change.

This contract gives the model useful context without allowing model-generated text to redefine identity, ownership, pricing, or permissions.

Context Is a Budget

Context engineering is a budget and a trust decision. More context can add noise or expose data, so separate it by trust:

Context Example Treatment
canonical checked-in schema, tests, build scripts cite revision and owner
task issue, acceptance criteria, current diff validate scope
generated index result, summary, tool output untrusted evidence
sensitive credentials, customer records, production dumps exclude or use approved redaction

Prefer the smallest relevant set of files. Include the interface and nearby tests; exclude unrelated repositories, generated noise, secrets, and stale duplicates. Record file paths and revisions so an important suggestion can be reproduced.

Issue text, comments, retrieved documents, and test output can contain prompt injection. They are data, not higher-priority policy.

Iterate in Small, Observable Steps

Use a loop that leaves evidence:

  1. inspect the current code and tests;
  2. ask for a plan limited to the declared scope;
  3. request one coherent change;
  4. inspect the diff and dependency changes;
  5. run focused tests and static checks;
  6. classify failures instead of asking for a blind retry;
  7. expand scope only after the previous step is understood.

Small steps reduce review surface and make rollback possible. They do not make an unsafe task safe by themselves.

Prompt Patterns That Age Well

Prefer observable instructions:

text
Use the existing error type and dependency injection pattern.
Do not invent an SDK method; cite the installed version or mark the call as pseudocode.
For each changed behavior, add or update a focused test.
Report commands run, failures, skipped checks, and assumptions.
If the requirement conflicts with the repository contract, stop and explain the conflict.

Avoid:

text
Write perfect production-ready code.
Make every decision autonomously.
Never ask questions.
Return a complete app in one response.

The second group rewards confidence and hides uncertainty. A useful assistant can say “I cannot verify this dependency” or “this needs approval.”

Review the Draft, Not the Vibe

Review every generated diff for:

  • behavior and error paths;
  • authorization, tenant isolation, and input validation;
  • secrets, logs, dependency and license changes;
  • concurrency, retries, idempotency, cancellation, and resource limits;
  • test coverage and negative cases;
  • observability, rollback, and migration impact.

Do not ask the model to prove its hidden reasoning. Ask for a concise change summary, assumptions, and evidence links. A fluent explanation is not a test result.

Example: A Safe Data Transformation Boundary

An AI-generated data transformation should not be called complete until its runtime and data assumptions are explicit. This Go boundary accepts typed records, limits batch size, normalizes only declared fields, rejects malformed addresses, and avoids copying unknown input fields into output:

go
package records

import (
	"fmt"
	"net/mail"
	"strings"
	"unicode/utf8"
)

const maxRecords = 10_000

type InputRecord struct {
	Email       string
	DisplayName string
}

type Record struct {
	Email       string
	DisplayName string
}

func NormalizeRecords(input []InputRecord) ([]Record, error) {
	if len(input) > maxRecords {
		return nil, fmt.Errorf("too_many_records")
	}

	output := make([]Record, 0, len(input))
	for index, item := range input {
		email := strings.ToLower(strings.TrimSpace(item.Email))
		if email == "" {
			continue
		}
		address, err := mail.ParseAddress(email)
		if err != nil || address.Address != email {
			return nil, fmt.Errorf("record_%d_invalid_email", index)
		}

		displayName := strings.TrimSpace(item.DisplayName)
		if utf8.RuneCountInString(displayName) > 200 {
			return nil, fmt.Errorf("record_%d_display_name_too_long", index)
		}
		output = append(output, Record{
			Email:       email,
			DisplayName: displayName,
		})
	}

	return output, nil
}

This function still does not define JSON or CSV parsing, file paths, character encoding, output atomicity, retention, encryption, or who may process the records. Those boundaries belong in the caller's contract and tests. A prompt must never grant arbitrary path access or permission to upload records to an untrusted destination.

Tests Are Part of the Request

Ask for tests that exercise behavior, not tests that merely mirror implementation:

  • valid and invalid input;
  • empty and oversized input;
  • duplicate and replayed requests;
  • timeout and partial failure;
  • unauthorized and cross-tenant identifiers;
  • escaping, injection, and malformed external data;
  • cancellation and cleanup;
  • backwards compatibility and rollback.

Then run the project’s real test, type, format, lint, security, and build commands. If a command was not run, say so.

Tools and Permissions

When an AI agent can execute commands, the executor should enforce:

  • authenticated principal and repository scope;
  • read-only defaults;
  • command and network allowlists;
  • isolated worktree or sandbox;
  • time, output, file, and process budgets;
  • cancellation and cleanup;
  • approval for merges, releases, deletions, credential changes, and external writes;
  • audit events with redaction.

Prompt constraints and model refusals are hints, not authorization. The same rule applies to a coding assistant, IDE extension, or terminal agent.

Use an explicit execution policy:

Capability Safe default Escalation evidence
Read tracked source and tests Allow inside the task repository Path and revision in the audit record
Run formatter and focused tests Allow from a command allowlist Command, exit status, and bounded output
Install or update dependencies Ask Reason, lockfile diff, license, vulnerability scan
Read secrets or production data Deny Separate approved workflow with redaction
Use arbitrary network endpoints Deny or host allowlist Destination, purpose, data classification
Write to external systems Ask Idempotency key, dry run, owner, rollback
Merge, deploy, delete, migrate Human approval Reviewed evidence bundle and recovery plan

The policy must be enforced by the host or sandbox. Repeating "do not deploy" in a prompt is not equivalent to removing deployment authority.

Build a Verification Ladder

Run the cheapest decisive checks first:

  1. format and compile the touched package;
  2. run focused unit and contract tests;
  3. run type, lint, static, dependency, and secret checks;
  4. run integration tests in an isolated environment;
  5. inspect the complete diff and generated artifacts;
  6. run broader build or end-to-end gates;
  7. require explicit approval for external side effects.

Every rung should produce an artifact: command result, test report, review decision, or deployment record. Model confidence and prose summaries are not rungs.

Working With a Team

Treat shared instructions as versioned engineering artifacts:

  • keep the project contract short and provider-neutral;
  • assign an owner and review date;
  • link to real scripts and canonical documents;
  • use synthetic examples instead of secrets or production dumps;
  • test instruction changes with representative and adversarial tasks;
  • measure successful outcomes, review effort, defects, cost, and developer learning;
  • roll back configuration that regresses a high-risk slice.

Do not enforce a single workflow on every repository. A policy for a read-only documentation project should not grant the permissions needed by a deployment agent.

Measure accepted outcomes

Do not use prompts sent, tokens consumed, or lines generated as productivity metrics. Measure:

  • accepted tasks per developer-week;
  • lead time from task acceptance to reviewed merge;
  • first-pass acceptance rate;
  • human review and rework minutes;
  • escaped defects, incidents, and rollbacks;
  • cost per accepted change;
  • developer understanding, sampled through review or maintenance tasks.

Segment results by greenfield versus mature code, task type, risk tier, and tool version. An aggregate average can hide a useful gain in test drafting and a damaging regression in authorization work.

Choosing a Provider Without a Ranking

Product capabilities, file paths, models, pricing, and retention terms change. Compare the current version against your workload:

Criterion Evidence
context selection, provenance, exclusions, stale-data behavior
execution sandbox, command policy, network, cancellation
privacy retention, training use, residency, deletion
review diff, tests, approvals, audit export
portability exportable instructions and provider independence
operations quotas, outages, latency, version pinning
economics subscription, inference, review, migration

Choose from evidence, not a permanent “best IDE” claim.

Common Failure Modes

  • trusting generated code because it looks idiomatic;
  • providing an entire production repository when a few files suffice;
  • treating an issue or retrieved document as trusted instructions;
  • granting broad shell, network, or credential access;
  • allowing the model to choose identities, owners, prices, or roles;
  • calling a partial snippet complete and runnable;
  • generating tests after the design is already assumed correct;
  • merging because the model says tests pass;
  • retaining prompts and source code without a purpose and deletion path;
  • measuring speed while hiding rework, defects, and review cost.

Practical Checklist

  • [ ] Write a bounded task contract with scope, invariants, and escalation.
  • [ ] Select minimal, versioned, trusted context and exclude secrets.
  • [ ] Treat retrieved content and tool output as untrusted data.
  • [ ] Request small diffs and inspect dependency and permission changes.
  • [ ] Require deterministic tests and security checks.
  • [ ] Sandbox commands and keep external side effects behind approval.
  • [ ] Record assumptions, skipped checks, failures, and rollback steps.
  • [ ] Review behavior, authorization, data handling, concurrency, and operations.
  • [ ] Version and replay shared instructions.
  • [ ] Measure successful outcomes, quality, cost, and learning instead of prompt volume.

Conclusion

Vibe coding is most useful as a disciplined interaction loop: express intent, constrain the change, inspect the evidence, and keep ownership with the engineer and the repository. Natural language can accelerate a draft; it cannot replace tests, policy, security review, or responsibility for the resulting system.

Primary Sources