Agentic Engineering in Practice: Governing AI-Generated Code at Scale
What agentic engineering actually means beyond the hype, and the governance layer humans must own when AI agents generate most of your production code.
In February 2026, Andrej Karpathy said vibe coding is now passé. What replaces it is agentic engineering: a structured model where AI agents handle implementation while human engineers own goals, constraints, and quality standards.
This is not a vocabulary upgrade. The operational difference is significant. Vibe coding is an individual practice, somewhat informal, often exploratory. Agentic engineering is a team discipline with defined roles, evaluation infrastructure, and governance mechanisms. If you are running a production codebase in 2026 and most of your code is agent-generated, you need the governance layer or you will pay for it later. This article covers what that layer looks like in practice.
The Actual Distinction: Vibe Coding vs. Agentic Engineering
Vibe coding is what most engineers have been doing since 2023: describe a feature to a model, accept the output, iterate, occasionally get surprised when something works. The human is in a reactive position. The feedback loop is informal. There is no defined quality gate between the model’s output and git push.
Agentic engineering inverts the ownership model. The human defines:
- Goals: what outcome the agent is driving toward
- Constraints: performance budgets, security boundaries, API contracts, test coverage thresholds
- Quality standards: what “done” means, codified in evaluators, not vibes
The agent handles the implementation path to get there. The human’s job is not to write code but to write the specification well enough that the agent can be evaluated against it. This is a meaningfully harder cognitive task than writing code. Most engineers are not trained for it.
The failure mode in pure vibe coding is predictable: you accumulate velocity debt. The code works until it doesn’t, and when it breaks, you have no evaluation infrastructure to tell you what went wrong or when it started.
The Governance Layer
Governance in agentic engineering has three parts: specification hygiene, constraint enforcement, and evaluation infrastructure. These are not process theater. They are what separates a team that ships reliably from one that regularly has to stop and do archaeology on agent-generated code.
Specification hygiene
Agents generate code proportional to the quality of the input they receive. Vague goals produce vague implementations. The governance layer starts with structured task definitions:
interface AgentTask {
goal: string; // Specific, testable outcome
constraints: {
performance: string; // e.g., "p99 latency < 200ms under 100 RPS"
security: string[]; // e.g., ["no direct SQL string interpolation", "all user input validated at boundary"]
coverage: number; // minimum test coverage % for new code
apiContracts: string[]; // paths to OpenAPI/JSON Schema files the agent must not break
};
outOfScope: string[]; // Explicit exclusions
acceptanceCriteria: string[]; // Conditions that make this task complete
}
This is not bureaucracy. It is the prompt that your evaluator will run against. If you cannot write the acceptance criteria before the agent starts, you do not understand the task well enough to delegate it.
Constraint enforcement at the boundary
Governance constraints need to be machine-enforceable, not trust-based. The agent will not consistently self-enforce. What works in practice is a pre-merge evaluation pipeline that runs against every agent-generated PR:
// evaluation/runner.ts
import { execSync } from "child_process";
import { readFileSync } from "fs";
interface EvaluationResult {
passed: boolean;
failures: string[];
metrics: Record<string, number>;
}
async function evaluateAgentOutput(
prBranch: string,
task: AgentTask
): Promise<EvaluationResult> {
const failures: string[] = [];
const metrics: Record<string, number> = {};
// Coverage check
const coverageOutput = execSync(`npx vitest run --coverage --reporter=json`, {
encoding: "utf-8",
});
const coverage = JSON.parse(coverageOutput);
metrics.coverage = coverage.total.lines.pct;
if (metrics.coverage < task.constraints.coverage) {
failures.push(
`Coverage ${metrics.coverage}% below required ${task.constraints.coverage}%`
);
}
// API contract check
for (const contractPath of task.constraints.apiContracts) {
const schema = JSON.parse(readFileSync(contractPath, "utf-8"));
const contractResult = execSync(
`npx @stoplight/spectral-cli lint --ruleset ${contractPath} openapi.json`,
{ encoding: "utf-8" }
);
if (contractResult.includes("error")) {
failures.push(`API contract violation in ${contractPath}`);
}
}
// Static analysis for security constraints
const eslintOutput = execSync(`npx eslint src/ --format json`, {
encoding: "utf-8",
});
const eslintResults = JSON.parse(eslintOutput);
const securityViolations = eslintResults
.flatMap((r: any) => r.messages)
.filter((m: any) => m.ruleId?.startsWith("security/"));
if (securityViolations.length > 0) {
failures.push(
`${securityViolations.length} security constraint violations`
);
}
return { passed: failures.length === 0, failures, metrics };
}
This pipeline runs before any human reviews the PR. Failures block merge. Humans only spend review time on code that already passed the automated bar.
Architectural Patterns for Human-AI Collaboration
Three patterns cover most production agentic workflows. They are not mutually exclusive; teams typically layer them.
The Specification-Evaluator Loop
The human writes a specification. The agent generates an implementation. An evaluator (automated or human) scores the output against the specification. The agent revises until the score crosses the acceptance threshold.
This is the backbone of most agentic workflows. The key design decision is what your evaluator measures. Common mistake: evaluating style (does this look like our code?) rather than behavior (does this do what was specified?). Style can be enforced with formatters. Behavior evaluators need to be custom-built per domain.
The Scaffolded Codebase Pattern
Agents perform better when they operate inside a well-defined structure. Teams running agents at scale typically maintain a “scaffolded codebase”: a set of patterns, interfaces, and conventions that constrain the search space for agent output.
Practically, this means:
- Typed interfaces for every module boundary (agents can be instructed to implement against interfaces, not invent them)
- A test fixture library that covers the expected range of inputs (agents generate tests that cover the fixture surface area, not just happy paths)
- A conventions document in the repo root that agents receive as context on every task
The constraint here is maintenance: the scaffolded codebase is now a first-class engineering artifact that humans own and agents implement against. It needs versioning, change review, and deliberate evolution.
The Parallelized-Candidates Pattern
For high-risk or high-complexity tasks, run multiple agent instances against the same specification, then evaluate and select the best output. This is computationally expensive but useful when the cost of a wrong implementation is high (e.g., payment processing logic, auth flows, data migrations).
// orchestration/parallel-candidates.ts
async function generateCandidates(
task: AgentTask,
count: number = 3
): Promise<{ candidate: string; score: number }[]> {
const candidates = await Promise.all(
Array.from({ length: count }, (_, i) =>
runAgent(task, { seed: i, temperature: 0.3 + i * 0.1 })
)
);
const scored = await Promise.all(
candidates.map(async (candidate) => ({
candidate,
score: await scoreImplementation(candidate, task.acceptanceCriteria),
}))
);
return scored.sort((a, b) => b.score - a.score);
}
Select the highest-scoring candidate. If none cross your threshold, escalate to human implementation.
Testing and Evaluation Infrastructure
Agent-generated code fails in ways that differ from human-generated code. Human bugs tend to be logic errors in code the human understood but mistyped or misreasoned. Agent bugs tend to be plausible implementations of the wrong specification, or correct implementations that miss edge cases not explicitly stated.
This means your test strategy needs to shift:
Property-based tests over example-based tests. Agents will naturally generate code that passes the examples they were shown. Property-based tests (tools like fast-check in TypeScript) define invariants the implementation must hold over a range of inputs. They are harder to game, explicitly or accidentally.
import * as fc from "fast-check";
import { processPayment } from "./payment-processor";
describe("payment processor invariants", () => {
it("never returns a charge greater than the requested amount", () => {
fc.assert(
fc.property(
fc.record({
amount: fc.integer({ min: 1, max: 1_000_000 }),
currency: fc.constantFrom("USD", "EUR", "GBP"),
customerId: fc.uuid(),
}),
async (input) => {
const result = await processPayment(input);
expect(result.charged).toBeLessThanOrEqual(input.amount);
}
)
);
});
});
Regression capture, not just regression prevention. When an agent-generated implementation fails in production, capture the input that triggered the failure and add it to your fixture library. The goal is a growing corpus of edge cases that future agent runs are evaluated against.
Behavioral contracts for module boundaries. Every interface in a scaffolded codebase should have a corresponding test contract. New agent implementations of that interface must pass the contract tests before merging.
Code Review When Agents Generate Most Code
Code review changes when agents generate most of the code. Reading line-by-line for typos and style is wasted reviewer time. The evaluation pipeline already caught those. What human reviewers should focus on:
Specification accuracy. Does the implementation actually match the stated goal, or did the agent optimize for something adjacent? This requires reviewers to read the specification first, then audit the implementation against it.
Missing constraints. What did the specification not say? Agents implement what they are told. Reviewers ask “what happens when the queue is full, when the network drops, when the user sends malformed input, when the database is at capacity?” These are the gaps agents leave because they were not specified.
Downstream effects. Agents scope to the task. They do not reason about systems they were not asked about. Reviewers ask “what else does this change?” and own the system-level view.
A useful heuristic: if a reviewer spends more than 20% of their review time on style and formatting, the pre-merge evaluation pipeline is insufficient. Fix the pipeline, not the reviewer’s habits.
Observability for Agentic Systems
Production observability for agentic systems has two layers that most teams underinvest in.
Agent decision tracing. When an agent takes an action (generates a file, calls an API, modifies a database record), that action needs a trace entry with the task context, the specific decision, and the inputs that led to it. This is distinct from application-level tracing. You need to be able to answer “why did the agent do that?” after the fact.
// observability/agent-tracer.ts
interface AgentDecisionTrace {
taskId: string;
agentId: string;
decision: string;
rationale: string;
inputs: Record<string, unknown>;
timestamp: number;
parentDecisionId?: string;
}
class AgentTracer {
private traces: AgentDecisionTrace[] = [];
record(trace: AgentDecisionTrace): void {
this.traces.push(trace);
// Ship to your observability backend
this.emit(trace);
}
private emit(trace: AgentDecisionTrace): void {
// Structured log to OpenTelemetry, Datadog, etc.
console.log(JSON.stringify({ type: "agent_decision", ...trace }));
}
}
Evaluation drift monitoring. Your evaluation criteria should not just gate merges. They should be tracked over time. If your test coverage threshold is 80% and you consistently see agent PRs land at 81%, your threshold is too low. If you see coverage trending down over a quarter, agents are being given looser specifications. Both are signals worth instrumenting.
Set up dashboards that track: average coverage per agent PR, security constraint violation rate, number of PRs that required human escalation after failing automated evaluation. These metrics tell you whether your governance layer is tightening or loosening over time.
When to Trust vs. Verify Agent Output
Not all agent output carries the same risk. A heuristic that works in practice:
Trust with logging: Stateless, reversible transformations with no external side effects. Text formatting, data transformations, UI components with no backend calls. Let these merge with passing automated evaluation and lightweight human review.
Verify before merge: Code with external side effects (API calls, database writes, queue publishes), auth-related logic, billing and payment flows, any code that touches user data. These require a human reviewer who understands the system context, not just the task specification.
Require human implementation: Security-critical components (cryptography, access control enforcement), architecture-level decisions (new service boundaries, data model changes), anything where the cost of a wrong implementation exceeds the cost of writing it correctly once. Agents can assist here but should not be the primary author.
The mistake teams make is treating this as a permanent classification. It is a calibration exercise. As your evaluation infrastructure matures and your specification quality improves, code that required human implementation can be delegated to agents with high-confidence verification. Track escalation rates and adjust the classification periodically.
Production Considerations
Versioning agent context. The agent’s behavior depends on the context it receives: system prompt, conventions document, scaffolded interfaces, example implementations. Version this context alongside your code. When an agent-generated implementation goes wrong, you need to know what context the agent had when it generated it.
Idempotency in agent-generated workflows. Agents running in automated pipelines will be retried on failure. Any agent action with external side effects must be idempotent. This is not a suggestion. An agent that sends two emails because a network timeout triggered a retry is a production incident. Enforce idempotency keys at the agent action layer, not just the application layer.
Gradual trust expansion. Start with a narrow scope: one agent, one module, one clearly defined class of tasks. Measure evaluation pass rates, escalation rates, and post-merge incident rates. Expand scope when the metrics support it. Teams that go wide too fast accumulate agent debt (incorrect but passing implementations) that is expensive to locate and correct.
Runbooks for agentic failures. When an agentic system goes wrong in production, the debugging process is different. Add runbooks that cover: how to trace a specific agent decision back to its task specification, how to identify which agent run generated a specific commit, how to disable agent automation for a module without blocking the deployment pipeline.
The Discipline
Agentic engineering is not easier than traditional engineering. It requires different skills: specification writing, evaluation design, governance architecture, and calibrated trust decisions. The productivity gains are real but they come from doing this well, not from outsourcing cognition entirely.
The teams that will get the most from agentic workflows are the ones that invest in the governance layer before they need it. The specification quality, the evaluation pipeline, the observability infrastructure: these are boring to build and they pay off asymmetrically when the system scales.
Agents are fast. Humans are accountable. The governance layer is what makes that combination work in production.
More in AI / ML
How Mixture of Experts Works: Sparse Gating, Expert Routing, and the Architecture Behind Efficient Large Language Models
A deep dive into MoE architecture: how the gating network routes tokens to experts, top-k selection, load balancing losses, capacity factor, token dropping, expert parallelism for serving, and the real production tradeoffs between dense transformers and sparse MoE models.
AI Agent Frameworks Compared: CrewAI, LangGraph, AutoGen, and Mastra for Production Systems
A practical comparison of CrewAI, LangGraph, AutoGen, and Mastra for building production AI agent systems. Covers architecture philosophy, state management, tool integration, observability, and deployment patterns with TypeScript code examples.
Google's Agent2Agent Protocol: How A2A Enables Cross-Framework Agent Communication in Production Systems
A deep dive into Google's Agent2Agent (A2A) protocol covering agent cards, task lifecycle, message parts, streaming via SSE, push notifications, and how A2A complements MCP. Includes TypeScript implementation examples, comparison with MCP and direct API integration, and production deployment patterns for multi-vendor agent ecosystems.
How Transformer Models Work: Self-Attention, Positional Encoding, and the Architecture Behind Modern LLMs
A technical deep dive into the Transformer architecture: tokenization, positional encoding, self-attention with Q/K/V matrices, multi-head attention, the encoder-decoder split, training dynamics, and what it all means for engineers building on top of LLMs.