Vibe Coding: A Guide for Engineers and Teams
What is vibe coding?
Vibe coding is an approach to development where the engineer describes intent and AI generates the code. In a professional context, it's systematic collaboration, not a replacement for programming: the engineer defines architecture, rules, and quality gates, and AI executes the implementation within those constraints. The key difference from amateur vibe coding is enforcement — automated checks that guarantee the quality of AI-generated code.
TL;DR
- -Vibe coding for engineers means systematic collaboration with AI at the level of architecture, process, and enforcement — not just 'AI writes your code for you'
- -Four stages of evolution: chaotic prompting → prompt engineering → context engineering → enforcement (quality gates, pre-commit hooks)
- -Architecture: Brain (CLAUDE.md, rules files) + Hands (agents, MCP, skills) + Conscience (hooks, linters, tests) — conscience matters most
- -Rules-driven development: rules in the repository replace verbal agreements and teach AI the project's context
- -Teams benefit from shared rules, structured code review for AI-generated code, and velocity/quality metrics — individual prompt hacks don't deliver this
Andrej Karpathy coined the term in February 2025: “I just see things, say things, run things, and copy paste things, and it mostly works.” A month later, vibe coding was a meme. Six months later, it was an approach reshaping professional development.
The problem is how people understand it. Most articles describe vibe coding as “describe it in words — AI writes the code.” That gives the impression of autocomplete on steroids. Maybe for beginners. Not for engineers.
Vibe coding for an engineer means AI handles the routine implementation, architectural decisions stay with the human, and quality gets verified by automated hooks. “AI writes code by your rules, and those rules are code.”
Here are the concrete approaches, tools, and patterns. For people who write code professionally and want to fold AI into their process — no magic, no marketing.
What Vibe Coding Is (and Isn’t)
A Definition for Engineers
Vibe coding is an approach to development where the engineer focuses on intent and architecture, and AI generates the implementation. The key word is “engineer.” Not “user.” Not “prompter.”
Three components make vibe coding an engineering approach:
- Project context is encoded in files (CLAUDE.md, .cursorrules), not in the developer’s head
- Quality rules are automated, not verbal: hooks, linters, tests
- AI operates within constraints: architecture, patterns, and dependencies are defined up front
Without these three components, vibe coding is just prompting. With them, it’s an engineering process with AI acceleration.
What It Isn’t
Not autocomplete. Copilot finishes a line. Vibe coding is when AI implements an entire module, following the project’s rules.
Not “I’m not a programmer, but…”. Articles about “build an app with no coding skills” are about something else. Vibe coding for engineers assumes you understand the code AI generates. You’re the architect, AI is the contractor.
Not a replacement for code review. AI-generated code needs review — often more thorough than human-written code. AI confidently writes code that compiles and passes tests but violates system invariants.
Not a silver bullet. Vibe coding speeds up implementation — not design, not debugging complex distributed systems, not domain understanding.
Why Engineers Are Skeptical — and When They Change Their Minds
The skepticism is justified. The first experience usually goes like this: you ask AI to write a function, get something that works, and spend an hour polishing it. Net savings: minus thirty minutes.
The shift happens when an engineer moves from one-off requests to a system:
- The first time they set up CLAUDE.md — and Claude stopped suggesting patterns the project doesn’t use
- The first time a pre-commit hook caught AI-generated code without tests — and it became clear why enforcement matters
- The first time they committed a rules file to the repository — and a teammate got the same context without a verbal explanation
The system works. Individual prompts don’t always.
Evolution: From Chaos to System
Vibe coding has gone through four stages. Most teams are stuck on the first or second.
Stage 1: Chaotic Prompting
“Write me a sorting function,” “Make a React form component,” “Fix this bug.” Every request starts from scratch. No context. Unpredictable results.
Characteristics:
- Every prompt is isolated
- The result depends on phrasing
- No repeatability: the same request produces different code each time
- The engineer spends more time on the prompt than they would writing the code by hand
This isn’t vibe coding. This is chatting with AI.
Stage 2: Prompt Engineering
System prompts. Few-shot examples. Chain-of-thought. The engineer formalizes how they communicate with AI.
You are a senior TypeScript developer.
Follow functional programming patterns.
Use zod for validation.
Return Result<T, Error> instead of throwing.
Characteristics:
- Prompts are structured and reusable
- Quality improves thanks to context in the prompt
- But the prompt lives in the developer’s head or personal notes
- No versioning, no team synchronization
Better than chaos. But a 500-token prompt doesn’t convey the context of a 100,000-line project.
Stage 3: Context Engineering
Project context gets encoded into files. CLAUDE.md, .cursorrules, .windsurfrules — these files load into every AI session and give the model full context: stack, architecture, conventions, prohibitions.
# CLAUDE.md
## Stack
- TypeScript 5.4, strict mode
- React 19 + TanStack Router
- Drizzle ORM + PostgreSQL
- Vitest for testing
## Conventions
- No default exports
- Zod schemas co-located with API routes
- Error handling: Result<T, Error>, never throw in business logic
- File naming: kebab-case
Characteristics:
- Context is versioned in git
- Every team member works with AI in the same context
- AI “knows” the project: stack, conventions, architecture
- Results are more stable, but still not guaranteed
This is already vibe coding. But without guarantees.
Stage 4: Enforcement
Rules are enforced, not just described. Pre-commit hooks block a commit if the code fails checks. CI/CD rejects a PR with violations. AI can’t bypass quality gates.
# .husky/pre-commit
npx lint-staged
# lint-staged.config.js
export default {
'*.{ts,tsx}': [
'eslint --fix --max-warnings 0',
'prettier --write',
() => 'tsc --noEmit',
'vitest related --run',
],
};
Characteristics:
- Quality is guaranteed automatically, independent of discipline
- AI generates any code it wants — bad code doesn’t get through
- The cycle: AI generates → hook rejects → AI fixes → hook accepts
- The engineer verifies the result, not every line of quality
This is mature vibe coding. AI writes. The system checks. The engineer stays in control.
The Vibe Shift: Why 2025–2026 Is the Turning Point
Three factors converged:
-
Models became good enough. Claude Opus 4.8, GPT-5.5, and Gemini 3.5 Flash generate code that passes tests on the first or second try. Just two years ago, this required 5–10 iterations.
-
Tools matured. Claude Code (CLI + desktop app + Agent SDK), Cursor 3.5, Devin Desktop, OpenAI Codex — not prototypes, but production-ready platforms supporting rules, agents, hooks, and cloud delegation.
-
Enforcement became standard. Pre-commit hooks, CI/CD with AI checks, automated testing — the infrastructure for controlling AI-generated code exists and works.
The shift from “AI as an assistant” to “AI as a contractor under control” — that’s the vibe shift.
The Architecture of Systematic Vibe Coding
Systematic vibe coding rests on three components. The Vibe Framework calls them “the three pillars” — a useful mental model.
Brain: Context and Rules
The brain is everything AI knows about the project before it starts working.
Context files:
CLAUDE.md— for Claude Code. Loads automatically on every run.cursor/rules/*.mdc— for Cursor (the current format since 2025; the legacy.cursorrulesis still supported but ignored in Agent mode).windsurfrules— for Devin Desktop (ex-Windsurf)copilot-instructions.md— for GitHub Copilot
What to include:
# Project Context
## Commands
npm run dev # Dev server
npm run test # Run tests
npm run build # Production build
## Architecture
- Monorepo: Turborepo
- API: tRPC + Drizzle
- Frontend: React 19 + TanStack Router + TanStack Query
- Database: PostgreSQL 16
## Conventions
- Named exports only
- Barrel files: src/features/*/index.ts
- Tests: co-located (*.test.ts next to source)
- Errors: never throw in business logic, use Result<T, E>
- SQL: raw queries through Drizzle, no query builder for complex joins
## Do NOT
- Add dependencies without explicit approval
- Use default exports
- Put business logic in API route handlers
- Use any/unknown without explicit reason in comment
What NOT to include:
- The obvious (how to write functions in TypeScript)
- Temporary decisions (TODOs, hacks, “we’ll fix it later”)
- Excessive context (every extra token reduces the model’s attention to everything else)
The optimal CLAUDE.md size is 100–300 lines. Longer, and the model loses focus. Shorter, and there’s not enough context.
Hands: Tools and Agents
The hands are how AI interacts with the project.
Tools:
- Reading/writing files
- Running shell commands
- Searching the codebase (grep, glob)
- Git operations
- MCP servers (external integrations: GitHub, databases, monitoring)
Agents:
- Subagents in Claude Code — isolated AI threads with separate context
- Agents Window in Cursor 3 — parallel agents, local and in the cloud
- Devin Local in Devin Desktop — multi-step operations (Cascade deprecated as of July 2026)
Skills (Claude Code):
- Reusable commands:
/commit,/review,/test - Defined as markdown files
- Include a specific prompt and a set of allowed actions
Example skill file:
# /review
Review the current git diff for:
1. Logic errors
2. Missing error handling
3. Deviation from project conventions (see CLAUDE.md)
4. Missing tests for new functions
Output: list of issues with file:line references.
Do NOT auto-fix — only report.
Conscience: Enforcement and Quality Gates
The conscience is the most important component. Without it, the brain and the hands generate plausible-looking but not reliably quality code.
Pre-commit hooks:
- Lint (eslint/biome) — style and potential errors
- Format (prettier/biome) — consistent formatting
- Type-check (tsc —noEmit) — type safety
- Test (vitest related) — tests for changed files
CI/CD:
- Full test run
- Coverage check (coverage threshold)
- Security audit (npm audit, snyk)
- Bundle size check
Additional checks:
- Architectural rules (eslint-plugin-boundaries — banning imports between layers)
- Dependency check (no adding dependencies without approval)
- Commit message format (conventional commits)
Agent isolation (the 2026 norm):
- Each agent works in a separate worktree/branch — changes don’t overlap until an explicit merge
- Permission modes: allow-deny lists for tools, network controls for agents making external requests
- MCP governance: server allowlists, OAuth scopes, banning dangerous tools at the config level
- Agent logs as a PR artifact: what it read, what it changed, what commands it ran, which tests passed
AI review gate (a recommended practice):
- Automated AI review of every PR before human code review: Bugbot (Cursor), Codex Security (OpenAI)
- Prompt-injection gate: scanning issues, PR comments, doc pages, and MCP outputs for injections
- AI-written code goes through the same SAST checks as human-written code (snyk, semgrep, bandit)
Why the conscience matters most: AI confidently generates code that looks correct. Without automated checks, that code lands in main. With them, it doesn’t — not until it passes every gate. AI fixes its own mistakes fast, given concrete feedback from a linter or a test.
The cycle works like this:
AI generates code
→ pre-commit hook runs lint
→ lint finds an unused import
→ AI removes the import
→ hook runs tests
→ a test fails (missed an edge case)
→ AI adds handling
→ all checks pass
→ commit created
In agentic mode (Claude Code, Cursor Composer), this cycle can happen without manual intervention — the agent fixes its own mistakes based on hook output. In interactive mode, the engineer sees the result of each step and decides whether to commit.
Project Phases with AI
AI changes both the implementation and the order of work. A linear waterfall and chaotic agile mix equally poorly with AI. An effective process is phased.
Phase 1: Specification
Before generating code — a specification. AI works better with a clear description than with a vague “make it good.”
## Feature: Rate Limiter Middleware
### Behavior
- Limit requests per IP: 100/minute for API, 20/minute for auth endpoints
- Return 429 with Retry-After header when exceeded
- Use sliding window algorithm
- Store counters in Redis (ioredis client from existing infra)
### Interface
```typescript
type RateLimiterConfig = {
windowMs: number;
maxRequests: number;
keyGenerator: (req: Request) => string;
};
function createRateLimiter(config: RateLimiterConfig): Middleware;
Constraints
- No new dependencies (use existing ioredis)
- Must handle Redis connection failure gracefully (allow requests, log warning)
- Tests: unit (mock Redis) + integration (real Redis via testcontainers)
A specification is a contract between the engineer and AI. The more precise the contract, the fewer the iterations.
### Phase 2: Design System and Tokens
For frontend work — design tokens before implementation. AI with tokens generates a consistent UI. AI without tokens hardcodes values.
```typescript
// tokens.ts
export const spacing = {
xs: '0.25rem', // 4px
sm: '0.5rem', // 8px
md: '1rem', // 16px
lg: '1.5rem', // 24px
xl: '2rem', // 32px
} as const;
export const colors = {
primary: 'hsl(222, 47%, 11%)',
accent: 'hsl(43, 74%, 54%)',
surface: {
base: 'hsl(0, 0%, 4%)',
raised: 'hsl(0, 0%, 7%)',
overlay: 'hsl(0, 0%, 10%)',
},
} as const;
In the rules file:
## Design System
- Use tokens from src/tokens.ts — NEVER hardcode colors, spacing, font sizes
- Components must accept className prop for composition
- No inline styles except dynamic values (e.g., width from props)
Phase 3: Decomposition
A big task → epics → features → tasks. Every task is atomic: one prompt, one commit, one verifiable unit.
Epic: User Authentication
├── Feature: Email/password registration
│ ├── Task: Registration form component
│ ├── Task: API route POST /auth/register
│ ├── Task: Email verification flow
│ └── Task: Tests for registration
├── Feature: Login flow
│ ├── Task: Login form component
│ ├── Task: API route POST /auth/login
│ ├── Task: Session management
│ └── Task: Tests for login
└── Feature: Password reset
├── Task: Reset request form + API
├── Task: Reset confirmation + token validation
└── Task: Tests for reset flow
Wave-based parallelism: tasks without dependencies run in parallel. One subagent on the registration form, another on the API route. Both work from the same CLAUDE.md.
Phase 4: Implementation
Every feature goes through a cycle: brainstorm → build → test → commit.
Brainstorm: discuss the approach with AI before generating code. What edge cases? What trade-offs? What existing utilities should be reused?
Build: AI generates the code. Don’t hand-edit at this stage — let AI finish.
Test: AI generates tests. Review the tests before running them — tests are a specification. If the tests don’t cover an edge case, ask for it to be added before implementation.
Commit: Automated: pre-commit hooks, conventional commit message.
Phase 5: Verification
AI generates the tests — the engineer verifies them. Not the other way around.
What to check in AI-written tests:
- Do the tests check behavior, not implementation? (mocking internal functions is bad)
- Are edge cases covered? (null, empty, concurrent, timeout)
- Any tautologies? (a test that mirrors the implementation instead of verifying it)
- Are assertions specific enough? (
toEqualvstoBeTruthy)
Tools for Professional Vibe Coding
Claude Code
Anthropic’s flagship agent. Available as a CLI (terminal), desktop app, and IDE plugin; one account, shared context.
Desktop app (as of April 2026): redesigned for parallel sessions. A sidebar for managing multiple agents, drag-and-drop layout, a built-in terminal and file editor, a fast diff viewer. Parity with the CLI: plugins, MCP, skills work the same in both modes.
Routines (cloud automations): saved tasks that run in Anthropic’s cloud — no need to keep a laptop on. Triggers: schedule, a GitHub event, Slack, etc.
Rules: CLAUDE.md at the project root. Hierarchy: ~/.claude/CLAUDE.md (global) → project CLAUDE.md → .claude/ directory with additional rules.
Agents: Subagents — markdown files in .claude/agents/. Each agent is a specialized AI with isolated context. Parallel agents run in separate worktrees/branches.
Skills: Reusable commands in .claude/commands/. Invoked via /skill-name.
Hooks: Pre/post-tool hooks on every lifecycle step of an agent. Example: run tests automatically after every git commit; log the command before every Bash call.
Agent SDK (Python/TypeScript): the same agent loop that powers Claude Code internally — tools, context, hooks, subagents, MCP, permissions — as a library for your own pipelines.
Non-interactive / headless: claude -p, JSON output, stream JSON, schema output, --max-turns. Used in CI/CD and automations without interactive input.
MCP: Model Context Protocol — integration with external services. GitHub, Notion, databases, monitoring — via a standard protocol.
// .mcp.json
{
"mcpServers": {
"github": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"]
},
"postgres": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-postgres"],
"env": {
"DATABASE_URL": "postgresql://..."
}
}
}
}
Cursor
A VS Code-based IDE. Version 3.5 (May 2026) is a full-fledged agentic platform with cloud and marketplace support.
Rules: .cursor/rules/*.mdc — separate .mdc files in the .cursor/rules/ directory. They support YAML frontmatter with activation modes (always/auto-attach/agent-requested/manual). The legacy .cursorrules file at the project root is still read, but ignored in Agent mode.
Composer: Agentic mode. Creates/edits multiple files in a single request.
Agents Window: Parallel agent execution locally, in worktrees, in the cloud (isolated cloud VMs with a terminal and browser), and over remote SSH. Agents create PRs and work asynchronously.
Cloud Agents (v3.5): agents in cloud VMs with full access to a terminal, browser, and multiple repositories in parallel. Results come back into the IDE.
Bugbot: AI review on every PR. Finds bugs and potential issues automatically (~90 second average cycle). Included in the team plan.
Customize page: a single management page for plugins, skills, MCP, subagents, rules, commands, and hooks — at the user, team, or workspace level. A team marketplace for sharing configurations.
Models: Supports Claude, GPT, Gemini. You can switch between them per task.
Devin Desktop (ex-Windsurf)
As of June 2, 2026, Windsurf was renamed Devin Desktop: Cognition folded the IDE into its agentic ecosystem via a standard over-the-air update. All Windsurf settings migrated automatically.
Agent Command Center (Spaces): a Kanban interface for managing local and cloud agents from one place. Running multiple tasks in parallel is the default mode.
Devin Local: a new local agent that replaced Cascade (end-of-life July 1, 2026). Up to 30% higher token efficiency, subagent support — parallel task processing that Cascade didn’t have.
Memories: Persistent context across sessions carried over.
SWE-1.6: Cognition’s own coding model (released April 2026). Runs on top of base models; included free in paid plans. Less overthinking, more parallel tool calls.
OpenAI Codex
OpenAI’s agentic coding platform. A single brand for CLI, desktop app, and cloud tasks.
Codex CLI: a local agent that runs in the terminal. Reads, edits, and runs code. Open source, written in Rust. A direct alternative to Claude Code CLI.
Codex App (Mac + Windows): a desktop app for coordinating multiple parallel agents. Worktree, automation, and Git management. The Windows version shipped in May 2026.
Codex Cloud / Web tasks: background agentic tasks in OpenAI’s cloud environment. Delegation without tying up a local machine.
Codex Security: AI-driven code security analysis. A local plugin and a cloud scan with a repo-specific threat model, validation evidence, and suggested fixes.
Models: runs on GPT-5.5 — the first fully retrained base model with agentic-first training (released April 2026).
Positioning: if Claude Code is a terminal-first Unix tool, Codex is OpenAI’s full-stack agentic platform with a built-in security layer.
Comparing Approaches
| Aspect | Claude Code | Cursor | Devin Desktop | OpenAI Codex |
|---|---|---|---|---|
| Interface | CLI + Desktop | IDE (VS Code fork) | IDE (VS Code fork) | CLI + Desktop + Cloud |
| Rules files | CLAUDE.md, .claude/ | .cursor/rules/*.mdc | .windsurfrules | — (context via CLI flags) |
| Agents | Subagents + Routines | Cloud Agents + Agents Window | Devin Local + cloud agents | Parallel threads + Cloud tasks |
| MCP support | Full | Partial | Partial | Limited |
| CI/CD integration | Native (CLI, GitHub Actions) | Via terminal | Via terminal | GitHub bot, webhooks |
| Hooks | Pre/post-tool (lifecycle) | No | No | No |
| Skills/Commands | Yes (.claude/commands/) | No (snippets) | No | No |
| Headless mode | Yes (Agent SDK, non-interactive) | No | No | Partial (API) |
| Security gate | No (external tools) | Bugbot (PR-level) | No | Codex Security |
| Cost | $20–200/mo or API | $20/mo (Pro) | $20/mo (Pro) | $20/mo or API |
| Best for | CLI-oriented work, backend, DevOps | Frontend, IDE-based work, teams | Multi-agent command center | OpenAI stack, security focus |
Common Tools
Regardless of IDE/CLI:
- Pre-commit hooks: husky + lint-staged (JS/TS), pre-commit (Python), lefthook (universal)
- Linters: eslint, biome (JS/TS); ruff (Python); clippy (Rust)
- Formatters: prettier, biome (JS/TS); black, ruff (Python); rustfmt (Rust)
- Type checking: tsc —noEmit (TS); mypy, pyright (Python)
- Testing: vitest (JS/TS); pytest (Python); cargo test (Rust)
- CI/CD: GitHub Actions, GitLab CI — running all checks on push/PR
Vibe Coding on a Team
Individual vibe coding is a skill. Team vibe coding is a process. The difference is fundamental.
Shared Rules
Rules files get committed to the repository. Every team member — and every AI assistant — works from the same context.
repo/
├── CLAUDE.md # Claude Code users
├── .cursor/rules/ # Cursor users (.mdc files with frontmatter)
├── .windsurfrules # Devin Desktop users (format survived the rebrand)
├── .eslintrc.js # Enforcement (all)
├── .husky/pre-commit # Enforcement (all)
└── vitest.config.ts # Testing (all)
Rule of thumb: if something matters to AI, it matters to a human too. Rules files replace verbal agreements. A new developer opens CLAUDE.md and understands the conventions without reading 50 pages of Confluence.
Code Review for AI-Generated Code
AI-generated code needs a specific kind of review. Checklist:
Architecture:
- Does the code follow the project’s established patterns?
- No new abstractions where an existing one would do?
- Is it in the right layer? (AI loves mixing UI and business logic)
Dependencies:
- No new dependencies added without discussion?
- Do the utilities duplicate existing ones? (AI often writes a helper without knowing one already exists)
Edge cases:
- Is error handling real, not a catch-all?
- No race conditions in async code?
- Is input validation in place?
Tests:
- Do the tests check behavior, not implementation?
- Are there negative tests (what should NOT work)?
“Smell” of AI-generated code:
- No excessive comments explaining the obvious?
- No over-engineering (3 abstractions for one use case)?
- No stubs with
// TODO: implement?
Onboarding: A New Developer + AI
Steps for a new team member:
- Read CLAUDE.md — it’s both documentation and AI configuration at once
- Run
npm testandnpm run lint— confirm enforcement works - Do the first task with AI — with a mentor showing the workflow
- Add a rule — if AI did something that had to be corrected, add a rule to the rules file
Rule of thumb: every bug from AI → a new rule in CLAUDE.md. The file grows organically from real problems, not theoretical best practices.
Metrics
What to measure to gauge the effect of vibe coding:
| Metric | How to measure | What to watch for |
|---|---|---|
| Velocity | Story points/week, PRs merged | Growth in the first month, then stabilization |
| Time-to-merge | Time from PR creation to merge | Should decrease (code is cleaner thanks to hooks) |
| Defect rate | Bugs per 1,000 lines | Watch that it doesn’t grow (AI code can be fragile) |
| Test coverage | % coverage | AI generates tests well — coverage grows |
| Rule additions | Rules/week in CLAUDE.md | Active growth = the team is learning to work with AI |
| AI cost | $/developer/month | Typical range: $100–250/month with daily use |
| Human intervention rate | Manual edits to AI PRs / total AI PRs | Decreasing = the rules file is working |
| Rework rate | Reverted AI PRs / total AI PRs | Increasing = insufficient enforcement or spec |
| Cost per merged PR | AI spend / PRs merged | Efficiency of the agentic workflow |
| Security findings (AI code) | Confirmed SAST hits in AI PRs | Should trend toward zero with good rules |
Team Anti-Patterns
“Everyone has their own prompt.” Developers use personal system prompts instead of shared rules. Result: inconsistent code, different patterns within the same project. Fix: a rules file in the repo, personal settings only extend it, they don’t override it.
“AI will handle everything.” The team stops making design and architectural decisions, relying on AI. AI generates “something that works” without systemic thinking. Result: architectural chaos within 2 months.
“Don’t touch it — it works.” AI-generated code doesn’t get refactored because “it works.” Technical debt accumulates unnoticed.
“One tool for everyone.” Forcing the entire team onto a single AI tool. Some people are more productive in a CLI, others in an IDE. What matters is shared rules, not a shared tool.
Rules-Driven Development
Rules-driven development (RDD) is an approach where AI behavior is governed by declarative rules, not ad-hoc prompts.
Rules Instead of Advice
Advice: “Please use named exports.”
Rule: NEVER use default exports. Named exports only. This is enforced by eslint rule import/no-default-export.
The difference:
- AI can ignore advice (especially with a long context)
- A rule is stated imperatively and backed by enforcement
- A rule includes the “why” — AI follows rules better when it understands the reason
The Structure of a Rule
Every rule contains three elements:
## Error Handling
**WHAT:** Use Result<T, E> pattern for business logic errors. Never throw.
**WHY:** Thrown exceptions bypass TypeScript type system. Callers don't know
a function can fail until runtime.
**WHEN:** All functions in src/domain/ and src/services/.
API route handlers can throw — framework catches them.
Examples of Rules Files
Backend (Node.js/TypeScript):
## API Routes
- Route handlers in src/routes/ — thin, only validation + delegation
- Business logic in src/services/ — never import from routes
- Database queries in src/repositories/ — never import from services directly, use interfaces
## Error Handling
- Domain errors: src/errors.ts, extend AppError base class
- Services return Result<T, AppError>, never throw
- Route handlers: try/catch at top level, map AppError to HTTP status
## Database
- Migrations in src/db/migrations/ — generated by drizzle-kit
- Never modify database schema without migration
- All queries use parameterized statements (drizzle handles this, but verify in raw SQL)
## Testing
- Unit tests: *.test.ts next to source file
- Integration tests: src/__tests__/integration/
- Mock external services, never mock internal modules
- Test file structure: describe(module) > describe(function) > it(behavior)
Frontend (React/TypeScript):
## Components
- Feature components: src/features/{name}/components/
- Shared components: src/components/shared/
- No prop drilling beyond 2 levels — use context or composition
- Every component that accepts children must type them as React.ReactNode
## State Management
- Server state: TanStack Query (no Redux for API data)
- Local UI state: useState/useReducer
- Global UI state (theme, sidebar): Zustand store in src/stores/
## Styling
- Tailwind CSS — no CSS files, no CSS-in-JS
- Use tokens from tailwind.config.ts (colors, spacing, breakpoints)
- No arbitrary values in Tailwind classes ([32px]) — add to config if needed
- Responsive: mobile-first (sm: md: lg:)
## Forms
- React Hook Form + Zod
- Schema in src/schemas/ — shared between frontend validation and API
- Error messages in src/i18n/ — never hardcode strings
Enforcement via Hooks
Rules in CLAUDE.md are soft enforcement. Hooks are hard enforcement. Combined:
// lint-staged.config.js
export default {
'*.{ts,tsx}': [
// Style and patterns
'eslint --fix --max-warnings 0',
'prettier --write',
// Type safety
() => 'tsc --noEmit',
// Tests for changed files
'vitest related --run',
],
};
ESLint rules that are especially useful with vibe coding:
// eslint.config.js (flat config)
export default [
{
rules: {
'import/no-default-export': 'error',
'no-console': ['error', { allow: ['warn', 'error'] }],
'@typescript-eslint/no-explicit-any': 'error',
'@typescript-eslint/no-unused-vars': 'error',
'no-restricted-imports': ['error', {
patterns: [{
group: ['../../../*'],
message: 'Avoid deep relative imports. Use path aliases.',
}],
}],
},
},
];
AI generates code with any? ESLint blocks it. AI adds console.log for debugging? ESLint blocks it. AI makes a deep relative import? ESLint blocks it. AI fixes it — automatically.
Markers in Code for AI
Markers are comments in the code that give AI extra context:
// AI:CONTEXT — This module handles rate limiting.
// See ADR-007 for why we use sliding window instead of fixed.
// AI:INVARIANT — maxRequests must never exceed 1000 per window.
// This is a hard business rule, not a technical limitation.
// AI:CAREFUL — This function is called from 15+ places.
// Changing its signature requires updating all callers.
// AI:NO-MODIFY — Generated by protobuf. Do not edit manually.
Markers aren’t comments for humans. They’re metadata for AI. They help the model understand what not to touch, what matters, and where to look for context.
Real-World Cases
Solo: MVP in 2 Weeks
Context: A solo developer building a SaaS for API endpoint monitoring. Stack: Next.js 15, Drizzle ORM, PostgreSQL.
Approach:
- CLAUDE.md: 180 lines (stack, conventions, DB schema, API contracts)
- Pre-commit: eslint + prettier + tsc + vitest
- Claude Code for implementation. Each feature — a separate prompt with a clear spec
Result:
- 14 days to a working MVP
- 47 API endpoints
- 89% test coverage
- One serious bug in production (a race condition in the health check — AI didn’t account for concurrent requests)
Key takeaway: Specifying each feature took 20–30 minutes. AI implementation — 5–15 minutes. A 2:1 ratio — the spec costs more than the code. That’s normal.
A Team of 5: Legacy Refactor
Context: An Express.js monolith, 80,000 lines, no types, 12% test coverage. Goal: migrate to TypeScript and split it into modules.
Approach:
- Shared CLAUDE.md with migration rules: how to convert routes, how to add types, which patterns to use
- Each developer takes a module and converts it with AI
- Pre-commit hooks: strict TypeScript (no-any), eslint, tests
- Weekly rules review: updating CLAUDE.md based on problems found
Metrics (3 months):
| Metric | Before | After |
|---|---|---|
| TypeScript coverage | 0% | 78% |
| Test coverage | 12% | 61% |
| Bugs/month (prod) | 14 | 6 |
| Deploy time | 45 min | 12 min |
| Rules in CLAUDE.md | 0 | 340 lines |
Key takeaway: CLAUDE.md grew from 50 to 340 lines over 3 months. Every rule was the result of a real problem. By month three, AI made fewer mistakes: the rules file covered all the project’s typical patterns.
Enterprise: AI in an Existing Process
Context: A fintech company, 40 developers, strict compliance. You can’t “just start using AI.”
Approach (6 months):
Months 1–2: pilot with 5 developers on a non-critical internal tool. Goal — prove safety and effectiveness.
Months 3–4: expand to 15 people. Shared rules. Mandatory security review for AI-generated code. Metrics.
Months 5–6: 30+ developers. AI-generated code goes through the same checks as human-written code. Plus: a SAST scanner on AI-generated PRs.
Result:
- Velocity: +35% (measured as PRs merged per sprint)
- Defect rate: unchanged (enforcement is working)
- Security incidents from AI-generated code: 0 (SAST + manual review)
- Adoption: 82% of developers use AI daily
Key takeaway: Enterprise adoption isn’t a technical problem. It’s change management. Rules + enforcement + metrics = the arguments that convince management.
Problems and Limitations
Vibe coding has real problems. Knowing them matters more than knowing the benefits.
AI-Generated Technical Debt
AI optimizes for “works right now,” not “easy to maintain in a year.” Typical problems:
- Copy-paste instead of abstraction. AI copies a pattern 5 times instead of creating a reusable function. Every copy works. Maintaining 5 copies doesn’t.
- Over-engineering. The flip side: AI creates an abstraction for a single use case. A factory for an object that’s only ever created in one place.
- Inconsistent patterns. On Monday AI used one approach for error handling, on Friday — another. Both work. The codebase turns into a zoo.
Mitigation: a rules file + architectural linting (eslint-plugin-boundaries) + regular refactoring.
”Works, but Why?”
AI generates code that passes tests. The developer merges it. A month later — a bug. The developer opens the code and doesn’t understand how it works, because nobody reviewed the implementation, only the result.
Rule of thumb: if you can’t explain every line, don’t merge it. AI-generated code requires the same level of understanding as a colleague’s code.
Over-Reliance: Skill Loss
A real risk, especially for junior and mid-level engineers. If AI always writes the implementation, the developer stops thinking at the code level.
Mitigation:
- Regular code katas without AI
- Architecture reviews where the human explains the decisions, not AI
- Rotation: “no AI” periods for critical modules
Security Concerns
AI generates code with vulnerabilities:
- SQL injection (if an ORM/parameterization isn’t used)
- Hardcoded secrets (AI “invents” credentials)
- Insecure defaults (disabled validation, CORS *)
- Dependency confusion (nonexistent packages in imports)
Mitigation: SAST in CI/CD, npm audit in pre-commit, security-focused rules in the rules file, manual security review for auth/payment/data modules.
Managing Cost
AI tools cost money: models are billed by token, and a large context hits the budget.
Practice: monitor spending through the provider’s dashboard. Average budget — $100–250 per developer per month with active use. Savings from context engineering: fewer tokens in context = lower cost per request.
How to Get Started
Minimum Setup (Day 1)
- A rules file. Create CLAUDE.md (or
.cursorrules/.cursor/rules/*.mdcfor Cursor) with basic information: stack, build commands, 5–10 key project rules.
# CLAUDE.md
## Commands
npm run dev / npm test / npm run build
## Stack
TypeScript, React, Drizzle, PostgreSQL
## Rules
- Named exports only
- Tests next to source files (*.test.ts)
- No console.log in committed code
- Error handling: Result<T, E>, never throw in services
- All API inputs validated with Zod
- Pre-commit hooks. Set up in 10 minutes:
npx husky init
npm install -D lint-staged
# .husky/pre-commit
npx lint-staged
- Tests. Make sure the test framework is set up and one basic test passes. AI will generate tests — the infrastructure needs to be ready.
Progressive Buildout (Weeks 2–8)
Week 2: Expand the rules file. After every AI session where you had to make corrections, add a rule.
Week 3: Add project-specific eslint rules. import/no-default-export, no-restricted-imports, architectural boundaries.
Week 4: Try subagents (Claude Code) or the Agents Window (Cursor) for tasks that touch multiple files.
Weeks 5–6: Set up CI/CD with a full test run, coverage check, lint. Automate what you used to check manually.
Weeks 7–8: Skills/commands for repetitive operations: /review, /test, /refactor. MCP servers for integrations (GitHub, database).
Team Checklist
- Rules file (CLAUDE.md / .cursorrules) in the repository
- Pre-commit hooks: lint + format + type-check
- Test infrastructure: framework set up, basic tests pass
- Code review process updated: checklist for AI-generated code
- Onboarding document: how to work with AI on this project
- Metrics: velocity, defect rate, AI cost — baselines recorded
- Rules review: weekly update to the rules file based on experience
- Security: SAST in CI/CD, rules in the rules file for auth/data modules
- Budget: spending limits on AI tools set and monitored
Vibe coding is an evolution of the engineering process, not a revolution. The models are good enough. The tools have matured. The enforcement infrastructure exists. All that’s left is to integrate it.
Engineers who treat AI as a contractor with clear rules get acceleration. Those who expect magic from AI or drop control get technical debt.
CLAUDE.md. Pre-commit hooks. Tests. Start there. The rest follows.