Skip to main content

Enterprise Vibe Coding

The premise

"Vibe coding" — using AI to write code freely, at speed, guided by prompts rather than manual typing — is how most engineering teams now work. The problem is that AI tools don't know your stack decisions. They write code that compiles, but it drifts from your standards on every commit.

Enterprise vibe coding solves this: you define your standards once, as a vybdocs pack, and every AI-generated commit is automatically checked against them.

The problem AI coding creates at scale

You are 10 engineers. You have made deliberate decisions:

  • TypeScript + Next.js, not vanilla React
  • Postgres with Prisma, not MongoDB
  • Zod for all runtime validation, not hand-rolled schema checks
  • date-fns, not moment.js
  • No eval(), no dangerouslySetInnerHTML without sanitization
  • Our custom logger utility, not console.log
  • Error boundaries on every route-level component

These decisions live in your head — maybe in a Notion doc, maybe in a CONTRIBUTING.md that nobody reads. When a developer opens Cursor and says "add user authentication," the AI writes code. It uses what it knows, not what you decided. It might use moment.js. It might use console.log. It might skip the error boundary.

Over 3 months, with 10 engineers using AI heavily, the codebase becomes a collage of the AI's defaults rather than your team's decisions.

vybdocs flips this. You declare your decisions as rules. Every AI-generated commit is checked against them at PR time. Violations are blocked. The AI's output conforms to your standards — automatically, on every push, for every engineer.

Creating your engineering standards pack

A custom pack is a named collection of rules that represents your team's engineering standards. It lives in .vyb/packs/ and is applied with a single command.

Initialize a custom pack

vyb init --pack custom --name acme-standards

This creates:

.vyb/
spec.yaml ← updated to reference acme-standards
packs/
acme-standards.yaml ← your pack definition

You can also create the pack file manually — the format is identical.

Pack file structure

.vyb/packs/acme-standards.yaml
pack:
id: acme-standards
name: Acme Engineering Standards
version: 1.0.0
description: >
Engineering constraints for Acme Corp's TypeScript + Next.js
platform. Enforces the stack decisions made in Q1 2026.
author: engineering@acme.com

categories:
frontend:
rules: []
backend:
rules: []
security:
rules: []
dependencies:
rules: []

Translating stack decisions into rules

Here is how to translate each type of stack decision into a vybdocs rule.

Banned packages

categories:
dependencies:
rules:
- id: acme-pkg-001
name: no-moment
description: moment.js is deprecated. Use date-fns.
severity: warn
pattern: "from 'moment'|require\\('moment'\\)"
remediation: >
Use date-fns instead of moment.js.
moment.js is 2.9x larger and in maintenance-only mode.
Example: import { format } from 'date-fns'

- id: acme-pkg-002
name: no-lodash-full
description: Do not import all of lodash. Use individual methods.
severity: warn
pattern: "from 'lodash'(?!'/"
remediation: >
Import specific lodash functions: import { debounce } from 'lodash-es'
Or use native equivalents: Array.from(), Object.entries(), etc.

- id: acme-pkg-003
name: no-axios
description: We use the native fetch API, not axios.
severity: warn
pattern: "from 'axios'|require\\('axios'\\)"
remediation: >
Use the native fetch() API with our wrapper at
src/lib/http.ts. It handles auth headers and error
normalization consistently.

Required patterns

Sometimes you want to enforce that a pattern IS used, not ban it. Use a negative-lookahead pattern or an annotation check:

- id: acme-fe-001
name: require-zod-validation
description: All API route handlers must validate input with Zod
severity: warn
pattern: "export.*async.*handler|export default.*function"
exclude:
- "**/pages/api/**" # We actually check the INVERSE — this is complex
remediation: >
Every API route must validate its request body with a Zod
schema before processing. Use the validateBody(schema) helper
from src/lib/validation.ts.

Security constraints

categories:
security:
rules:
- id: acme-sec-001
name: no-eval
description: eval() is banned. Use JSON.parse() or safe alternatives.
severity: block
pattern: "\\beval\\s*\\("
remediation: >
Replace eval() with JSON.parse() for JSON data.
If you need dynamic computation, use expr-eval library
with a strict sandbox configuration.
maps-to:
- framework: soc2
control: CC6.1

- id: acme-sec-002
name: no-hardcoded-secrets
description: No hardcoded API keys, passwords, or tokens.
severity: block
pattern: "(apiKey|api_key|password|secret|token)\\s*[:=]\\s*['\"][^'\"]{8,}"
remediation: >
Move secrets to environment variables.
Use process.env.SECRET_NAME and load from .env.local.
Never commit secret values to git.
maps-to:
- framework: soc2
control: CC6.1

- id: acme-sec-003
name: no-dangerously-set-html
description: dangerouslySetInnerHTML requires sanitization.
severity: block
pattern: "dangerouslySetInnerHTML"
remediation: >
If you must use dangerouslySetInnerHTML, sanitize first
with DOMPurify: { __html: DOMPurify.sanitize(content) }
Never pass raw user input or AI output.

Frontend patterns

categories:
frontend:
rules:
- id: acme-fe-001
name: no-direct-dom
description: Use React refs. Do not query the DOM directly.
severity: warn
pattern: "document\\.(getElementById|querySelector|querySelectorAll)"
exclude:
- "**/*.test.tsx"
- "**/test-utils/**"
remediation: >
Use const ref = useRef<HTMLElement>(null) and attach
it to the element via ref={ref}. Access via ref.current.

- id: acme-fe-002
name: no-class-components
description: Use functional components with hooks, not class components.
severity: info
pattern: "class\\s+\\w+\\s+extends\\s+(React\\.)?Component"
exclude:
- "**/ErrorBoundary*" # Error boundaries must be class components
remediation: >
Rewrite as a functional component using hooks.
Class components cannot use hooks and are harder to test.

- id: acme-fe-003
name: no-inline-styles
description: Avoid inline styles. Use Tailwind classes or CSS modules.
severity: info
pattern: "style=\\{\\{"
exclude:
- "**/charts/**"
- "**/canvas/**"
remediation: >
Use Tailwind CSS utility classes or a CSS module.
Inline styles bypass our design system tokens.

Logger enforcement

categories:
backend:
rules:
- id: acme-be-001
name: no-console-log-in-production
description: Use our logger utility, not console.log.
severity: warn
pattern: "console\\.(log|debug|info|warn|error)"
exclude:
- "**/*.test.ts"
- "**/*.test.tsx"
- "**/scripts/**"
remediation: >
Import the logger: import { logger } from '@/lib/logger'
Use logger.info(), logger.warn(), logger.error().
Our logger adds request IDs, user context, and routes to
the correct sink (local console vs. Datadog in prod).

The complete acme-standards pack

Putting it all together, here is a realistic full pack for a TypeScript + Next.js startup:

.vyb/packs/acme-standards.yaml
pack:
id: acme-standards
name: Acme Engineering Standards
version: 1.0.0
description: >
Engineering constraints for Acme Corp's TypeScript + Next.js
platform. Stack decisions locked Q1 2026.

categories:
frontend:
rules:
- id: acme-fe-001
name: no-direct-dom
severity: warn
pattern: "document\\.(getElementById|querySelector)"
remediation: Use React refs (useRef) instead.
exclude: ["**/*.test.tsx"]

- id: acme-fe-002
name: no-class-components
severity: info
pattern: "class\\s+\\w+\\s+extends\\s+(React\\.)?Component"
remediation: Rewrite as a functional component with hooks.
exclude: ["**/ErrorBoundary*"]

backend:
rules:
- id: acme-be-001
name: no-console-log
severity: warn
pattern: "console\\.(log|debug|info)"
remediation: "Use: import { logger } from '@/lib/logger'"
exclude: ["**/*.test.ts", "**/scripts/**"]

- id: acme-be-002
name: require-zod-imports
severity: info
pattern: "joi\\.object|yup\\.object|ajv\\.compile"
remediation: "We use Zod for validation. import { z } from 'zod'"

security:
rules:
- id: acme-sec-001
name: no-eval
severity: block
pattern: "\\beval\\s*\\("
remediation: Use JSON.parse() or a safe expression evaluator.
maps-to:
- framework: soc2
control: CC6.1

- id: acme-sec-002
name: no-hardcoded-secrets
severity: block
pattern: "(apiKey|password|secret)\\s*[:=]\\s*['\"][^'\"]{8,}"
remediation: Use process.env.SECRET_NAME instead.
maps-to:
- framework: soc2
control: CC6.1

- id: acme-sec-003
name: no-dangerously-set-html
severity: block
pattern: "dangerouslySetInnerHTML"
remediation: Sanitize with DOMPurify first.

dependencies:
rules:
- id: acme-pkg-001
name: no-moment
severity: warn
pattern: "from 'moment'"
remediation: "Use date-fns: import { format } from 'date-fns'"

- id: acme-pkg-002
name: no-axios
severity: warn
pattern: "from 'axios'"
remediation: Use fetch() via src/lib/http.ts

- id: acme-pkg-003
name: no-class-validator
severity: warn
pattern: "from 'class-validator'"
remediation: We use Zod for validation throughout.

30-second onboarding for new engineers

This is the adoption story that makes enterprise vibe coding compelling: a new engineer joins your team and is productive with your standards in under a minute.

# New engineer, day 1:
git clone git@github.com:acme/platform.git
cd platform
npm install

# Apply the team's pack:
vyb init --pack acme-standards

# Done. Every AI suggestion in Cursor is now checked against
# Acme's standards before it can be committed.

When they open Cursor and start using AI assistance, every git commit or PR runs vyb check. If the AI suggested moment.js, the commit is flagged. If it used console.log, they see a warning. If it used eval(), the commit is blocked.

Your standards propagate automatically — without documentation that goes stale, without code reviews that miss things, without manual linting rules that nobody maintains.

The startup CTO story

You are a CTO at a 10-person startup. You have moved fast with Cursor and Claude Code since day one — and you have been disciplined. You chose your stack carefully.

The problem: your 10 engineers are 10 different people with 10 different interpretations of your standards. The AI tools they use have none of those interpretations. Every sprint, the codebase drifts.

Here is what your workflow looks like after adopting enterprise vibe coding:

Week 1: You define your stack decisions as a pack. 2 hours of work. You run vyb init --pack acme-standards across your repos.

Week 2: The team gets one sprint's worth of violations. Most are minor — wrong logger, moment.js import, a direct DOM query. They fix them. The codebase converges.

Week 3 onward: New AI-generated code gets checked automatically. Your standards are ambient. Engineers can move as fast as the AI allows, because the AI's output is filtered through your constraints before it lands.

6 months later: A new engineer joins. They clone the repo, run vyb init, and immediately work to your standards. No ramp-up documentation required for tooling conventions. The pack is the documentation.

Sharing packs across teams and repos

Option 1 — Local pack file in a monorepo

The simplest approach: commit .vyb/packs/acme-standards.yaml to your monorepo. All services in the repo inherit it.

For polyrepos, maintain your pack in a dedicated standards repo and reference it:

# In each service repo:
git submodule add git@github.com:acme/standards.git .vyb/upstream

# Then reference in spec.yaml:
.vyb/spec.yaml
spec:
version: 1
project: acme-payments-service
extends: .vyb/upstream/packs/acme-standards.yaml

Option 3 — npm-published pack

Publish your pack as an npm package and reference it by name:

# Publish (from your standards repo):
npm publish @acme/vyb-standards

# Install in each project:
npm install --save-dev @acme/vyb-standards

# Reference:
vyb init --pack @acme/vyb-standards

This is the recommended approach for organizations with 5+ repos. Versioning is handled by npm semver.

Option 4 — URL reference

Reference a pack directly by URL (useful for quick trials):

.vyb/spec.yaml
spec:
extends: https://standards.acme.com/packs/acme-standards.yaml

URL-referenced packs are fetched at vyb check runtime and cached for 24 hours.

Pack versioning and updates

Semantic versioning

Pack versions follow semver. A 1.x.y bump is non-breaking (new rules at warn or info). A 2.x.y bump may upgrade rules to block.

pack:
id: acme-standards
version: 1.3.0 # bumped because we added no-class-validator at warn

Announcing a version bump

When you upgrade a rule from warn to block, announce it to the team first. Engineers may have existing violations that would start blocking their PRs.

Recommended workflow:

  1. Bump pack to 1.4.0-rc.1 with the upgraded rule
  2. Run vyb check --dry-run across all repos to count violations
  3. Give the team 1 sprint to clean up
  4. Release 1.4.0 final, which starts blocking

Pinning pack versions

Projects can pin to a specific pack version to control upgrade timing:

.vyb/spec.yaml
spec:
version: 1
extends:
pack: "@acme/vyb-standards"
version: "1.3.0" # pinned — won't auto-upgrade

Integration with MCP — authoring new rules through Claude Desktop

Once your pack is set up, the Claude Desktop MCP server makes it easy for engineers to propose new rules without editing YAML by hand.

The rule authoring flow

In Claude Desktop:

User: We've decided to ban the use of axios. Can you add a rule
for that to our acme-standards pack?

Claude: [calls proposeRule]
Proposed rule:
id: acme-pkg-002
name: no-axios
severity: warn
pattern: "from 'axios'|require\\('axios'\\)"
remediation: Use fetch() via src/lib/http.ts instead.

[calls dryRunRule]
Dry run result:
Violations: 4
src/services/auth.ts:3 import axios from 'axios'
src/services/payments.ts:2 import axios from 'axios'
src/api/webhooks.ts:8 const axios = require('axios')
src/lib/http.ts:1 import axios from 'axios' ← the wrapper itself

I found 4 violations. Note src/lib/http.ts is the wrapper
we want people to use — you'll want to exclude that file.

Should I commit this rule with an exclusion for src/lib/http.ts?
User: Yes, exclude src/lib/http.ts and commit it.

Claude: [calls commitRule with exclude: ["src/lib/http.ts"]]
Rule acme-pkg-002 committed to .vyb/packs/acme-standards.yaml
Pack version bumped: 1.3.0 → 1.4.0

The MCP tools (proposeRule, dryRunRule, commitRule, readSpec, suggestPack) are all subject to the one-way ratchet. Claude can propose a rule at any severity, but cannot downgrade an existing rule.

See Claude Desktop MCP for setup instructions.

Measuring the impact

After rolling out enterprise vibe coding, track these metrics to quantify the ROI:

Standards drift rate
Count of constraint violations per 100 AI-generated lines. Should trend toward zero.
Time to standards compliance for new hires
How long until a new engineer's commits are violation-free. Should drop to 1 sprint or less.
Review comment category
Track what fraction of code review comments are about standards vs. logic. Should shift away from standards.
Evidence Pack completeness
Percentage of merged PRs with a signed Evidence Pack. Target: 100% in regulated code paths.

Anti-patterns to avoid

Too many block rules out of the gate. Start with everything at warn and run a dry-run sprint before promoting rules to block. Engineers need time to understand new constraints.

Rules that are too broad. A pattern like console would match legitimate code like console.log inside test utilities and navigator.console. Be specific.

Missing remediation copy. Every rule — especially block-severity ones — must explain what to do instead. Without remediation text, engineers will just delete the violation line without understanding why.

Not versioning the pack. Treat your pack file as code. Tag releases, write changelogs, and communicate breaking changes (rule severity upgrades) in advance.


Next: Custom Packs