AI is accelerating our path to the Big Ball of Mud – a hypothesis
It often starts with an innocent subscribe()
Section titled “It often starts with an innocent subscribe()”Over the past ten years, I have seen many Angular applications in my work as a consultant. One pattern kept appearing: components with long subscribe() blocks full of conditions, state updates, notifications, routing decisions, and eventually business logic.
I doubt anyone set out to build a miniature state-management system inside a subscription. It probably started with a perfectly understandable decision:
loadItem(id: string): void { this.api.loadItem(id).subscribe(item => { this.item = item; });}The result goes into a property. Done. There is nothing inherently wrong with this. Calling subscribe() is not automatically an anti-pattern, and imperative code is not forbidden in Angular.
Three weeks later, someone wants a warning after the response arrives. Then certain results require navigation. Later, another part of the application needs the same data, so the method starts filling a cache. Before long, the code looks more like this:
loadItem(id: string): void { this.api.loadItem(id).subscribe(item => { this.item = item; if (item.warning) this.notifications.warn(item.warning); if (item.status === 'archived') this.router.navigate(['/archive']); this.cache.set(id, item); });}This tiny example is not a Big Ball of Mud. But it illustrates a direction of travel: a subscription that originally delivered a value gradually takes ownership of several unrelated responsibilities. The next requirement no longer arrives in a clearly bounded design. It arrives in a method that already seems to do everything.
My suspicion is that coding agents may not only help us implement features faster; they may also replicate structural mistakes faster.
If those mistakes gradually weaken architectural boundaries, the next AI-generated change could become more expensive. That possible feedback loop is what I want to explore here.
A concrete failure can happen much earlier
Section titled “A concrete failure can happen much earlier”Suppose a component loads an item based on a route parameter. A user moves quickly from item A to item B, triggering two independent HTTP requests. The response for B arrives first, and the UI displays the correct item. Then the older response for A arrives and overwrites this.item. The user now sees stale data.
The problem is not that Angular prohibits imperative syntax. It is uncoordinated asynchronous work. With switchMap, changing to B unsubscribes from the previous inner stream; for a directly subscribed HttpClient observable, this aborts a request still in progress. The changing IDs must pass through the same stream. Two independent calls to loadItem() do not coordinate themselves. Client-side cancellation also does not undo an action the server has already performed. Depending on the application, AsyncPipe, toSignal(), or a well-defined read model can coordinate the display state.
Change detection also requires precision. Assigning an ordinary property in a subscription does not itself notify Angular of the change. Whether the view updates anyway depends on other triggers and the change-detection strategy. AsyncPipe calls markForCheck(); updating a signal read in a template also provides a notification. This matters in zoneless Angular too. Angular recommends computed() for derived state and warns against using effect() merely to propagate state changes.
Imperative statements are not the enemy. The risk is replacing an understandable reactive model with an expanding collection of synchronization obligations and assuming that this carries no long-term cost.
Exceptions become examples
Section titled “Exceptions become examples”A workaround rarely remains alone in a repository. Someone reads it, copies it, and extends it. A new developer follows the existing example. So may a coding agent. What was justified as an exception yesterday can become the normal pattern tomorrow. If the architectural boundary is implicit, the codebase begins to give contradictory answers about the intended design.
This resembles the broken-windows effect. As a metaphor alone, it would be a weak argument. But there is empirical work: in 2024, Levén, Broman, Besker, and Torkar examined how existing technical debt affected human developers. Their controlled experiment evaluated 51 solutions from 29 participants. In more debt-laden Java systems, they observed, among other things, more duplication, poorer names, and additional issues detected by static analysis.
That supports the effect in the studied setting. It does not prove an inevitable downward spiral, or that coding agents amplify the same mechanism. Keeping those limits visible matters.
A coding agent does not automatically inherit the intended architecture
Section titled “A coding agent does not automatically inherit the intended architecture”In my own work, even highly capable coding agents occasionally drift toward familiar implementation patterns: business logic inside components, imperative state synchronization, or direct imports across capability boundaries. I have seen this even when architectural guidance is documented, skills exist, and Angular tools are available.
That observation cannot tell us why a particular run made a particular decision. Learned patterns, repository examples, task wording, tools, and success criteria all interact. It would be too simplistic to suggest that a model merely votes on the most common pattern from its training data.
A narrower claim has been investigated. In their 2026 SWE-Shield preprint, Yu and colleagues evaluated 495 issues from six repositories against 1,787 validated design constraints. Fewer than half of the functionally resolved issues satisfied all examined design constraints. Additional design guidance reduced violations but did not eliminate them. Design compliance was assessed mainly by an LLM verifier, with a sample compared against human judgments. This is not a complete architecture audit.
Functional correctness is therefore not a reliable substitute for architectural acceptability. That gap has empirical support. Whether agents specifically reproduce poor Angular patterns remains an observation and an open research question.
Big Ball of Mud means losing dependable boundaries
Section titled “Big Ball of Mud means losing dependable boundaries”For me, a Big Ball of Mud is not simply a large codebase or a long component. The deeper problem is the loss of dependable boundaries. Ownership becomes unclear. Business rules live in several places. Modules reach into each other’s internals. A module is no longer a local workspace but part of a difficult-to-predict network.
Foote and Yoder described in the 1990s how such systems can emerge from locally sensible, short-term decisions. That does not mean they stop working immediately. They can remain functional for a very long time.
Bochicchio, Sas, Gilardi, and Arcelli Fontana (2025) found higher median co-change rates in file pairs where both files or one file had architectural smells than in clean pairs. That suggests broader change impact; it does not establish general causality or a law of inevitable decay.
The open question: What will the next agent task cost?
Section titled “The open question: What will the next agent task cost?”So far, this may sound like an old software-engineering story. The new variable is throughput. Coding agents can alter many parts of a system within a short time. They may therefore multiply local exceptions faster if verification fails to keep up. They can also pay down technical debt faster. The direction is not predetermined.
My economic hypothesis concerns the next task. Where ownership, contracts, and dependency directions are explicit, an agent may only need to understand a relatively small working area. In a heavily coupled system, more files, implicit side effects, and competing conventions may be plausibly relevant. More of them may need to be found, understood, or ruled out.
That could increase context acquisition, tool calls, test scope, repair loops, and ultimately the cost per verified change. Not necessarily in every run, and perhaps offset by more capable models.
In their preprint, Bai et al. (2026) studied eight models using OpenHands on SWE-bench Verified. Input tokens accounted for a substantial share of costs, runs on the same task could vary widely in token use, and more tokens did not automatically yield better outcomes. They did not test whether poor architecture caused that spending. The final connection in my chain remains a hypothesis.
Which links have actually been studied?
Section titled “Which links have actually been studied?”-
Uncoordinated asynchronous requests can overwrite UI state with stale results. TECHNICALLY REPRODUCIBLE; Angular and RxJS provide documented mechanisms.
-
Existing technical debt can encourage further debt. EXPERIMENTALLY SUPPORTED for human developers in a limited Java setting (Levén et al., 2024).
-
Coding agents can produce functionally correct patches that violate design constraints. EMPIRICALLY STUDIED (Yu et al., 2026).
-
Architectural smells can be associated with higher co-change rates. EMPIRICAL ASSOCIATIONS, not universal causality (Bochicchio et al., 2025).
-
Agent token use varies widely and is strongly shaped by input. EMPIRICALLY STUDIED (Bai et al., 2026).
-
Higher AI throughput could accelerate architectural erosion, which could in turn raise future token and verification costs. OPEN OVERALL HYPOTHESIS.
My hypothesis – deliberately labeled as such
Section titled “My hypothesis – deliberately labeled as such”The faster coding agents make changes in systems with weak architectural boundaries, the faster some structural mistakes may spread. If those mistakes enlarge the area that must be analyzed and verified, the costs of subsequent agent tasks may rise as well.
That would be a feedback loop: faster code production changes the system’s structure, and that structure changes the work required for the next round of code production.
The overall causal claim does not follow automatically from the individual studies. A competing hypothesis is also plausible: better agents may navigate a Big Ball of Mud more cheaply, spot smells, and refactor them faster than new ones appear. We do not yet know which mechanism dominates under which conditions.
Why I made the joke about a €25 subscription
Section titled “Why I made the joke about a €25 subscription”During a discussion about guardrails, someone argued that sufficiently capable AI could make all those decisions on its own. Shortly before that, they had complained about running out of usage allowances. My dry response was: “And that’s why my €25 subscription is enough and yours isn’t.”
It was a joke with a point, not a benchmark. Different models, limits, tasks, and billing arrangements make that comparison methodologically useless. I regularly run two or three agent tasks in parallel myself, yet usually remain within my ordinary allowance.
What I do intentionally is scope tasks by capability, avoid renegotiating architecture for every feature, and prefer executable checks over good intentions. I have not measured whether this saves tokens. The metric I would care about is not the first run’s consumption, but the cost per verified, acceptable change – after many more features have been added.
Guardrails: fewer good intentions, more verifiable boundaries
Section titled “Guardrails: fewer good intentions, more verifiable boundaries”The answer is not to keep extending agent instruction files. In the setups examined in their preprint, Gloaguen et al. (2026) did not find a general improvement in task success from repository context files; inference costs increased by more than 20% on average. Extra requirements can increase work even when agents follow them.
I would rather keep prose short where a rule can be executed. An illegal cross-capability import belongs in an architectural check. A business invariant belongs in a test. The reasoning behind a capability boundary belongs in a concise, discoverable document and a few representative examples. Where automated checks are too blunt, targeted architectural review is still necessary.
Guardrails should not micromanage every local choice. They should stabilize the structurally expensive choices until we deliberately change them.
How could we test this?
Section titled “How could we test this?”Comparing a pristine system with complete CI against a neglected repository without tests would tell us very little. Too many variables would change at once.
A better starting point would be an identical functional baseline with deliberately varied architectural boundaries. The same agent works through the same sequence of perhaps 15 features in repeated runs. One variant enforces boundaries mechanically, another documents them, and a third leaves more structural freedom. Models, tooling, tests, and acceptance criteria should be kept as comparable as possible.
We would track design violations, change radius, co-changes, exploration and verification work, repair loops, token consumption, and total cost per accepted change. What matters most is how those measures evolve across the sequence, not just the first task.
Differences would be useful evidence, not an immediate proof of causality. Replication, controls, and a defensible operational definition of architectural quality would still be needed.
Conclusion: measure more than the first change
Section titled “Conclusion: measure more than the first change”The fact that AI can produce more code does not make sound architecture irrelevant. It may instead mean we need to watch its erosion more closely.
I do not believe coding agents are destined to create worse architectures. They can detect and correct structural defects too. But as long as functional success does not reliably demonstrate design compliance, and the direct connection between architecture and agent costs remains open, I see no good reason to discard useful boundaries prematurely.
The interesting question is not only: How quickly can an agent implement today’s feature?
It is: How does today’s implementation affect the cost of the next hundred changes?
Because with AI, we might be accelerating more than delivery. Without appropriate architectural discipline, we might also be accelerating our path into the Big Ball of Mud.
Research and further reading
Section titled “Research and further reading”- William Levén, Hampus Broman, Terese Besker, Richard Torkar (2024). The broken windows theory applies to technical debt. Empirical Software Engineering 29, 73.
- Kai Yu et al. (2026). Does Pass Rate Tell the Whole Story? Evaluating Design Constraint Compliance in LLM-based Issue Resolution. SWE-Shield; Preprint, arXiv:2604.05955v1.
- Matteo Bochicchio, Darius Sas, Alessandro Gilardi, Francesca Arcelli Fontana (2025). An empirical study on architectural smells through a pipeline for continuous technical debt assessment. Information and Software Technology 185, 107783.
- Thibaud Gloaguen et al. (2026). Evaluating AGENTS.md: Are Repository-Level Context Files Helpful for Coding Agents?. Preprint, arXiv:2602.11988v3.
- Longju Bai et al. (2026). How Do AI Agents Spend Your Money? Analyzing and Predicting Token Consumption in Agentic Coding Tasks. Preprint, arXiv:2604.22750v3.
- Brian Foote, Joseph Yoder (1997/1999). Big Ball of Mud. PLoP ’97, WUCS-97-34; Web 1999; Pattern Languages of Program Design 4, 2000.
- Angular: HTTP Observables / cancellation, Zoneless, Signals / computed, Effects, RxJS interop.
- RxJS: switchMap.