Across my previous articles, I’ve kept coming back to one central argument:
Once UI rendering no longer owns the entire data flow, State, Derived State, Effects, and Async Work can no longer be carelessly compressed into the lifecycle of a single component.
At first, this might sound like a frontend architecture problem:
- Components accumulating too much local state
- Effects becoming overloaded with unrelated side effects
- Async work calling
setStatewhen it completes, tying its lifecycle directly to the UI - Queries, Mutations, and Streams being treated as interchangeable operations despite having different semantics
But as I began using AI agents more extensively to implement, refactor, and validate systems, I arrived at a broader conclusion:
Whether an agent understands the semantic boundaries of a system matters far more than how much code it can generate.
Code Is No Longer Scarce. Semantic Boundaries Are.
Modern AI agents can generate code at remarkable speed. They can create directory structures, complete TypeScript types, write tests, generate reducers, hooks, adapters, and API clients, and even connect an entire feature through a seemingly coherent implementation.
On the surface, this looks very close to automated software development. The danger, however, is usually hidden inside code that works while leaving its semantic boundaries ambiguous.
The code works on the first run, yet nobody can clearly answer a fundamental question:
Who actually owns the data?
Once ownership becomes unclear, several architectural questions turn into invisible time bombs:
- Who is responsible for maintaining state consistency?
- Who is allowed to trigger invalidation?
- Which value is the source of truth, and which is merely a derived result?
- When async behavior is interrupted, should it be cancelled, retried, or discarded?
- Is the UI a continuously subscribed observer, or is it only consuming a snapshot from a particular point in time?
When these boundaries are not explicitly defined, a more productive agent does not create a better system. It simply builds an unmaintainable one faster.
Without Boundaries, Agents Only Accelerate the Chaos
If developers themselves do not have a clear understanding of the system’s architectural boundaries, an AI agent will not automatically invent those boundaries for them.
Instead, it will usually follow the most direct and intuitive implementation path:
Data arrives
→ Put it into component state
The data needs to be shared
→ Move it into a global store
Something must stay synchronized
→ Add an effect
Duplicate requests must be avoided
→ Patch together a cache
A race condition appears
→ Add a boolean flag
Stale data becomes a problem
→ Add another timestamp
In isolation, each step seems reasonable. Taken together, however, they gradually erode the structure of the system. State becomes a catch-all container. Effects become a dumping ground for unrelated side effects and synchronization patches. The lifecycle of async work becomes tightly coupled to the UI.
This is one of the biggest blind spots in current AI coding workflows:
We obsess over what AI can write, while ignoring whether it knows what should never be written together.
From Requirements to a Semantic Map
Many assume that sufficiently detailed prompts will produce a great architecture. That’s only half true. Requirements tell an AI what to do. Architectural boundaries define what must stay separated.
I ran into this distinction repeatedly while building my open-source project, signal-kernel. When the core semantics of a system are explicit, the improvement goes beyond faster code generation. The agent operates within a much clearer decision space, while the architecture provides a set of natural guardrails.
In signal-kernel, I deliberately removed control of the data flow from the UI framework:
- UI adapters for React and Vue act only as snapshot consumers.
- The reactive graph exclusively defines and owns dependency relationships.
- The async runtime owns async correctness independently of the UI lifecycle.
- Resources explicitly represent the lifecycle and state of asynchronous data.
- Mutations and Streams remain semantically distinct: Mutations carry invalidation contracts, while Streams distinguish event flow from stable values.
Once these semantics are encoded into the infrastructure, an AI agent is far less likely to place every responsibility into the same layer. It does not need to guess where state belongs. It does not need to use Effects to repair synchronization gaps. It is less likely to confuse a cache with derived state.
The system’s semantic map already constrains what a valid implementation looks like.
The Invisible Debt of Vibe Coding
I am not opposed to AI-generated code. Its value in rapid exploration, concept validation, and reducing repetitive implementation work is undeniable.
My main concern with so-called vibe coding has never been that it might produce bugs. Bugs can often be caught through testing. Architectural trade-offs that were never consciously made are much harder to detect. They quietly erode the system over time.
An AI-assembled system often does not hold up under sustained questioning:
- Why is this value stored globally instead of locally?
- Why does this successful Mutation modify the local cache directly instead of invalidating the relevant node?
- Why is a Stream event allowed to overwrite the current stable value?
- Which layer owns cancellation and retry behavior?
- What prevents two boundaries from becoming competing sources of truth?
If the reasoning behind these decisions cannot be verified or explained, the system is already carrying architectural debt. Such a system may be built extremely quickly while the business logic remains simple.
But once the data flow becomes more complex, the team grows, or the system encounters edge cases involving async timing and cross-boundary synchronization, the architecture begins to collapse.
The system does not lack code. It lacks boundaries.
AI Did Not Eliminate Architecture Problems. It Exposed Them.
AI agents have undeniably reshaped software development workflows, but they have not made architectural thinking obsolete. If anything, they've made it far more critical.
As the marginal cost of producing code approaches zero, the value of engineering shifts upward—from producing implementations to defining the constraints those implementations must follow.
Previously, the scarce capability was:
Who can implement the feature?
The increasingly scarce capability is:
Who can define data-flow boundaries, design invalidation contracts, and provide a semantic map that both humans and AI agents can follow?
This is why my work began with the design of a reactive library, moved into questions of Ownership, Render Boundaries, and Async Correctness, and eventually connected to AI agents.
These topics may appear unrelated on the surface.
At the architectural level, however, they all point to the same principle:
In an era when AI can help us write more code, we need to become much clearer about which code should never be written together.
Top comments (1)
This resonates. AI has dramatically reduced the cost of writing code, but it hasn't reduced the cost of making architectural decisions. If ownership boundaries, state contracts, and async responsibilities aren't explicit, an agent will usually optimize for the shortest implementation path not the most maintainable one.
We've seen the same pattern while building AI-powered systems at IT Path Solutions. The projects that scale well are the ones where retrieval, orchestration, business logic, and UI responsibilities are clearly separated before the first prompt is written. Once those boundaries exist, AI becomes a force multiplier. Without them, it simply accelerates technical debt.
The shift really is from "who can write the code?" to "who can define the constraints the code has to respect?"