top of page

The Antidote to "Vibe Coding" Entropy in Brownfield Systems

Writer: Ravi Subramaniam
Ravi Subramaniam
Sep 29
0 min read

Starting with a confession: This is an actual observation on the ground however, I have obfuscated the example to remove every proprietary information yet explain the subject.


A few months ago, I watched a talented senior engineer, someone who could architect a distributed system in their sleep hand a brownfield checkout service to an AI agent with a single prompt: "Add multi-currency support to checkout discounts."


Twelve minutes later, the agent had produced clean-looking, well-structured code. It had imported dinero.js, written a currency conversion function, wired it directly into the frontend checkout controller, and handled edge cases the engineer hadn't even thought to specify.


It was also completely, silently, catastrophically wrong.


The agent had bypassed the company's internal, audited LedgerService micro-service entirely. It was doing floating-point arithmetic on financial values discount = price * 0.15 introducing rounding errors on real transactions. It had circumvented the compliance requirement for auditable exchange rate snapshots. And worst of all, it looked fine in the pull request. The code was readable. The logic was coherent. It just operated in an entirely different architectural universe than the one the system was built in.


This is what I call vibe coding entropy in brownfield systems. And it is one of the most dangerous failure modes in AI-assisted engineering today.

Vibe Coding Entropy

What is "Vibe Coding" ? Why Does It Work ; Until It Doesn't?

The term "vibe coding" has been circulating in engineering circles for a while now. At its core, it describes a workflow where you interact with an AI coding agent through conversational nudges, iterative generation, and gut-feel prompts. You describe what you want.

The model produces something> You push back> It improves> You ship

For greenfield projects, weekend hacks, and solo builders, it is genuinely magical. The model's ability to pull from statistical patterns across millions of repositories means it can bootstrap a working prototype faster than most teams can finish their sprint planning meeting.


But the moment you take that same workflow and apply it to a production brownfield system a codebase built over a decade, carrying business-critical domain logic, audited service boundaries, architectural decisions made in war-rooms and post-mortems the magic fades.


In brownfield systems, typing speed was never the bottleneck. It was never the constraint. The real engineering challenge has always been about protecting domain invariants, navigating layered architectural rules, and preventing cascading side effects that only a seasoned team member would recognise as dangerous.


When an AI agent operates without a formal specification, it doesn't know your system. It knows systems like yours in aggregate, across every public repository it was trained on. It will reach for dinero.js because millions of projects use dinero.js. It won't reach for your internal LedgerService because it has never seen it.


You are not getting an expert collaborator, rather you have a very fast, very confident intern who just joined the company and has read a lot of Stack Overflow.



The Checkout Currency Bug: A hypotetical case study based on the real-world problem's obfuscation


Diving deeper on that checkout scenario because it is one of the most instructive real-world patterns I have seen play out repeatedly across engineering teams.


The task: Add multi-currency support to checkout discounts.


What happens without a spec: The agent scans the local file, finds a price calculation, and does exactly what a competent open-source developer would do. It imports a well egarded currency library. It places the logic where the problem is most visible in the checkout controller. It handles conversion using standard arithmetic. It ships.


The hidden blast radius:


  1. The company's LedgerService microservice exists specifically because financial operations need to be centralized, audited, and compliant. The agent just walked over it entirely.

  2. Floating-point arithmetic on money is a known, documented, historic engineering crime and every fintech team learns this the hard way. The agent didn't know your team had already learned it.

  3. Regulatory compliance requires that exchange rate snapshots must be persisted and auditable for past several years. The agent's implementation has no such mechanism.


The code looked so clean that it almost made it through code review. That is the specific horror of vibe coding in brownfield systems. The entropy doesn't look like entropy. It looks like progress.


The Spec-Driven Antidote: Constraining the Agent to Your Reality


Here is what the same task looks like when you write a proper spec before touching the agent:


### Target: Multi-Currency Discount Calculation

- **Domain Invariant:** All monetary operations MUST use the internal `MonetaryValue` Value Object. Raw `number` or `float` operations are strictly disallowed.

- **Service Boundary:** Retrieve exchange rates strictly via `LedgerService.getExchangeSnapshot(tenantId, timestamp)`.

- **Prohibited:** Do not install external currency packages. Do not execute rate math in UI controllers.

- **Audit Requirement:** Persist the `snapshot_id` to `CheckoutSessionRecord`.

Thirty lines of markdown, that is the entire antidote. The agent now operates as a Police Sniffer dog rather than a Truffle Hunting Dog. It cannot reach for dinero.js because the spec forbids it, it cannot place logic in the controller because the spec forbids it and it cannot skip the audit trail because the spec mandates it.


The agent becomes, in the best possible way, a highly capable implementer executing within your architectural standards rather than inventing its own.



The Architects and the Developers have the 'Standards', 'Rules', 'Functional Boundaries' and 'Protocols' at the back of their head always. The knowledge that is in the back of their head is more important than the efficiency in writing code using Python, Java or TS. This is why an Architect with a sound functional domain knowledge is highly regarded in the Industry.


The "Negative Space" Principle: What You Forbid Matters as Much as What You Require


When you get a puppy at home, you train the puppy to follow your commands and also train what not to do. You must teach the puppy it is not okay to chew the charging cable or tear the pillow along with you teach how to fetch a ball.


One of the most underappreciated insights in spec-driven development is that negative constraints are architecturally equal to positive requirements.


Consider the SMS OTP verification task. You ask an agent to implement one-time passcode verification on signup. What a well-intentioned model will produce, without negative constraints, is genuinely impressive in scope: a database migration adding phone_number to the users table, a rate limiter, a Twilio SDK integration, a user settings page update for phone number editing.


A 20-minute feature becomes a 40-file pull request.


The agent wasn't hallucinating. It wasn't making mistakes. It was being helpful in the most dangerous possible way completing the problem as it understood it, rather than as your architecture requires it to be scoped.


The spec that prevents this looks like this:

### Negative Constraints (Out-of-Scope & Prohibited):
- NO Database Migrations: Phone metadata via `UserMetadata` KV store only.
- NO Direct SDK Integrations: Dispatch via `NotificationQueueProducer.publish()`.
- NO Rate-Limiter Implementations: Handled at Cloudflare API Gateway.
- Blast Radius: Edits restricted to `src/modules/auth/verification/*`.

The blast radius declaration is the critical piece. It tells the agent exactly how many files it is allowed to touch. Everything outside that boundary is forbidden territory not because you don't trust the agent, but because you have made an explicit architectural decision about the scope of this change. Mature AI systems architects don't just tell the model what to build. They draw a hard perimeter around what it may never touch.



Machine-Verifiable Specs: The Leap from Wishful Thinking to Engineering Contracts


The final frontier of spec-driven development, and the one where elite teams are separating themselves right now, is the shift from passive prose to machine-executable contracts.


A spec written in natural language depends on human interpretation at every step. An engineer reads it. The agent interprets it. Another engineer verifies the output against it manually. Every handoff in that chain is a place where drift can enter.


The more powerful model looks like this:


> [ OpenAPI 3.1 Schema ]  --->  [ AI Agent Terminal Loop ]  --->  [ Contract Test Runner ]
			|
			V
	Exit Code 0: Done
	Exit Code 1: Agent self-corrects

The spec for a webhook integration doesn't just say "validate signatures and handle duplicate events." It points to a contract file (contracts/webhooks/stripe-events.yaml), specifies the exact test command (npm run test:contracts -- --filter=stripe), defines the idempotent fixture that must pass, and includes a rule: the agent does not request human review until exit code is 0.


The agent is now operating in a closed feedback loop. It generates code, runs the contract tests, reads the failure output, and corrects itself autonomously. You review a passing test suite, not raw code. This is the architectural pattern that turns AI coding agents from powerful-but-risky assistants into genuinely trustworthy engineering teammates.



The Domain-Driven Design Renaissance: Is the Skill of the Agentic Era


Something unexpected happened when teams started working seriously with AI coding agents in complex systems. Domain-Driven Design which had been quietly gathering dust in architecture library collections since the microservices gold rush made everyone think CRUD was sufficient suddenly became urgently, practically relevant again.


Here is why: LLMs are fundamentally pattern-matching on language. When your codebase uses the word "Account" to simultaneously mean a tenant organization, an authenticated user session, and a Stripe billing customer, the model cannot reliably navigate that ambiguity. It will cross bounded contexts. It will mix concerns. It will produce code that compiles but violates domain boundaries in ways that create subtle bugs months later. In a ride-hailing system viz Uber, the prompt "set trip to arrived" produces radically different (and mostly wrong) code from a spec that says:


- Bounded Context: MobilityDispatch
- State Machine: RideState.DRIVER_EN_ROUTE -> RideState.DRIVER_AT_PICKUP
- Domain Event: Emit `DriverArrivedAtPickupDomainEvent` via `DomainEventPublisher`
- Prohibited: Do NOT trigger notifications or billing directly inside this aggregate

The difference between those two is the difference between a state corruption bug and a clean, encapsulated domain transition. Ubiquitous language, explicit state machines, bounded contexts, and aggregate roots are not ivory-tower abstractions anymore. They are the most token-efficient, high-leverage context you can feed a coding agent.


Teams that invested in DDD years ago, and were occasionally mocked for it in the era of move-fast frameworks, are now discovering they have a significant structural advantage. Their systems have language the agent can reason about precisely.



What 'Engineering Teams' should start doing


If you are a senior engineer, tech lead, or architect working with AI agents on production systems, here is the practitioner's guide to escaping vibe coding entropy:


  1. Audit your agent workflows for architectural guardrails. Every task you hand to an agent should have three things explicitly stated: what it must use (internal services, data types, patterns), what it must not touch (blast radius declaration), and how success will be measured (executable acceptance criteria). If any of those three are missing, you are vibe coding.

  2. Build a spec template library, not a prompt library. Most teams obsess over clever prompts. The more durable investment is a library of spec templates indexed by task type feature addition, bug fix, refactor, integration, migration. These templates encode your architectural standards once and apply them consistently across every agent interaction.

  3. Make your domain model machine-readable. Your bounded contexts, ubiquitous language dictionary, aggregate roots, and state machine definitions should live in structured files that can be injected into any agent context. If this knowledge only exists in an engineer's head or in a Confluence page nobody has read since 2021, you are dependent on human memory to prevent architectural drift.

  4. Shift spec verification left. Invest in the tooling that lets agents self-verify. OpenAPI contract tests, AST lint rules, custom architecture fitness functions these are the mechanisms that close the feedback loop and let agents operate autonomously without requiring human oversight on every output.

  5. Calibrate spec depth to blast radius. This is the discipline that prevents spec-driven development from collapsing into bureaucratic overhead. Two-line change? Micro-spec. Cross-service feature? Structured spec. System migration? Full architectural RFC. The proportionality principle keeps the process lean without sacrificing rigor.

  6. Treat every brownfield constraint as a first-class citizen. Before any agent engagement in a brownfield system, run a pre-flight check: What compliance requirements apply? What service boundaries must not be crossed? What internal utilities exist that the agent doesn't know about? What data types are mandated for this domain? Capturing these as explicit spec sections is not overhead it is the difference between shipping a feature and shipping a liability.



The AI coding debate is polarized for a reason: One camp thinks agents will replace engineering teams, the other thinks they produce garbage that costs more to clean up than they save. Both are mostly wrong and both are judging by teams using these tools naively. The teams winning with agents did the unsexy architectural work first: documented domain models, defined service boundaries, machine-verifiable test contracts. The teams drowning in entropy handed a decade-old system to an agent and said "figure it out." The agent always figures something out. That's precisely the problem.

The antidote to vibe coding entropy isn't less AI, it's more architecture: The discipline to define and encode your system's design before an agent touches it. The most valuable engineers of the agentic era won't be the ones with the best prompts, but the ones who built systems worth specifying. Where have guardrails made the difference for your team and where did the entropy get in anyway?

 
 
 

1 Comment


Nilotpol Bhattacharya
Sep 29

Very nice article Ravi. You presented solid arguements on Do’s and Dont’s. Really great read!!

Like
bottom of page