AI Can Make Every Local Decision Look Reasonable - While Making the System Worse
DEV Community

AI Can Make Every Local Decision Look Reasonable - While Making the System Worse

AI coding agents are very good at making small decisions. Move this logic into a helper. Add a cache here. Create a service for that. Split this module. Add another abstraction. Introduce one more dependency. Each decision can look perfectly reasonable. That is exactly the problem. A system can get worse even when every local decision looks correct. This is not really an AI problem. It is an architecture problem that AI can now accelerate.

The Dangerous Part Is That Nothing Looks Obviously Wrong

Imagine this over a few weeks:

  1. Task 1 → Add validation
  2. Task 2 → Extract helper
  3. Task 3 → Add cache
  4. Task 4 → Add service
  5. Task 5 → Add retry logic
  6. Task 6 → Refactor module
  7. Task 7 → Add another integration

Every pull request passes. Every change looks clean. Every test is green. But six weeks later:

  • two services now own the same business rule
  • validation exists in three places
  • caching is inconsistent
  • dependencies point in both directions
  • nobody knows which layer owns what
  • a “simple” change touches eight files

No single commit destroyed the architecture. The system drifted there.

Local Correctness Is Not System Correctness

This is the key idea. An AI agent usually works on the task in front of it. You ask: “Add retry support.” It finds the easiest reasonable place to add retries. You ask: “Reuse this logic.” It extracts a helper. You ask: “Clean up this module.” It introduces a new abstraction. Each answer may be correct for that prompt. But architecture is not just a collection of locally correct choices. Architecture is about how those choices interact over time.

The Agent Optimizes for the Task

The developer has a different responsibility. The agent asks: How do I complete this task successfully? The developer should ask: What does this change do to the whole system? Those are not the same question.

The agent may optimize for:

  • fewer lines
  • cleaner separation
  • less duplication
  • faster implementation
  • easier reuse

The system may actually need:

  • stronger boundaries
  • fewer abstractions
  • one clear owner
  • deliberate duplication
  • less coupling
  • simpler dependencies

A locally elegant change can still be globally harmful.

Example: The Helpful Helper

Suppose you have this:

calculateInvoiceTotal()

One module uses it. Then another feature needs similar behavior. AI suggests: Move it into shared/utils. Makes sense. Later another module imports it. Then another. Then someone adds customer-specific logic. Then tax rules. Then discount behavior. Now your “shared helper” contains business logic used across the entire system. Nothing looked unreasonable at the time. But the architecture quietly changed.

What started as:

Invoice owns invoice logic

became:

Everyone depends on shared/utils

That is architecture drift.

AI Makes Architecture Drift Faster

Before AI, introducing five new abstractions took effort. Now it can happen in minutes. That changes the economics. Developers can generate:

  • new services
  • new interfaces
  • new layers
  • new adapters
  • new helpers

faster than they can evaluate whether those things should exist. The risk is not that AI generates obviously terrible architecture. The more interesting risk is: It generates plausible architecture faster than teams can maintain a coherent design.

Green Tests Do Not Protect Architecture

This is important. Your test suite may say: Everything works. That does not mean: The system is getting better.

Tests can catch:

  • broken behavior
  • regressions
  • invalid outputs

They usually do not catch:

  • unclear ownership
  • unnecessary abstractions
  • dependency creep
  • duplicated business rules
  • architectural drift

You can have: 100% tests passing and still have a codebase becoming harder to change every week.

Code Review Can Miss It Too

A reviewer sees one pull request. Maybe 80 lines. The diff looks reasonable. But architecture problems often become visible only across many changes.

  • PR 1 adds a helper.
  • PR 2 adds a service.
  • PR 3 adds another dependency.
  • PR 4 reuses the helper.
  • PR 5 bypasses the service.

Individually: Fine. Collectively: Messy system. This is why reviewing only the current diff is not always enough.

Ask One Extra Question During AI-Assisted Review

When reviewing AI-generated code, ask:

If we repeat this pattern 20 times, what does the system look like?

That question is surprisingly useful. If every feature adds:

  • another service
  • another config
  • another queue
  • another shared helper

then the pattern itself may be wrong. Architecture is often about what happens when a decision gets repeated.

Protect Ownership Boundaries

One of the easiest ways to reduce drift is to make ownership explicit. For example:

  • Payments owns payment rules.
  • Orders owns order lifecycle.
  • Users owns identity.
  • Notifications only sends messages.

Then tell the agent: Do not move business rules across these boundaries unless explicitly requested. That is better than letting the model decide architectural ownership from scratch every time.

Give AI Constraints, Not Just Goals

Bad prompt:

Refactor this to make it cleaner.

Better:

Refactor this module. Constraints:

  • preserve current architectural boundaries
  • do not introduce new dependencies
  • do not create new shared abstractions
  • keep business logic in the existing domain
  • propose changes before implementing

The goal is not to make AI less useful. It is to stop every task from becoming a mini architecture redesign.

Ask for the Architecture Impact Before the Code

Before implementation, try:

Before changing code, explain:

  1. Which architectural boundary this touches.
  2. Which modules will depend on the change.
  3. Whether this introduces a new abstraction.
  4. Whether this creates a new dependency.
  5. Whether similar logic already exists.
  6. How this affects future changes.

That forces the discussion one level above syntax.

Watch for Dependency Direction

One signal I pay attention to is dependency direction. Healthy systems usually have clear relationships. For example:

Controller ↓ Service ↓ Repository

Then AI introduces:

Repository ↓ Utility ↓ Service

Now layers start depending on each other in strange ways. Each import may compile. But the system becomes harder to reason about. If dependency direction becomes unclear, architecture is usually drifting.

Shared Code Is Not Always Better Code

AI often tries to remove duplication. Usually that is good. But sometimes two pieces of code only look similar today. For example:

calculateSellerFee()
calculateCreatorFee()

AI may combine them into:

calculateFee(type)

Cleaner? Maybe. But if seller and creator rules evolve differently, you now have one abstraction carrying two separate concepts. A little duplication can be cheaper than the wrong abstraction. This is something humans still need to judge carefully.

Architecture Needs Invariants

I think AI-heavy teams need explicit architectural rules. Not hundreds of pages. Just a few things that must remain true. For example:

  • Controllers contain no business logic.
  • Domains cannot access each other's database tables directly.
  • Shared utilities cannot contain business rules.
  • External APIs go through adapters.
  • Background jobs call services instead of repositories directly.

Now both humans and agents have something concrete to check. These are architecture invariants.

Use AI to Detect Drift Too

AI can also help find the problem it creates. Periodically ask:

Review this codebase for architecture drift. Look for:

  • duplicated responsibilities
  • unclear module ownership
  • circular dependencies
  • new abstractions with little value
  • shared utilities containing business logic
  • inconsistent patterns
  • dependency direction violations

Do not refactor anything yet. Rank the findings by impact.

That can be a very useful review. The important part: Do not refactor anything yet. First detect. Then decide.

Small Changes Need System-Level Review Too

The dangerous phrase is: “It’s only a small change.” Small changes repeated hundreds of times become architecture. Every small change teaches the codebase a pattern. If that pattern is weak, AI can replicate it extremely quickly. That is why local quality is not enough.

A Simple Review Checklist

Before merging an AI-generated change, ask:

  • Does this introduce a new abstraction? If yes, do we actually need it?
  • Does this move responsibility? If yes, is the new owner correct?
  • Does this add another dependency? Could an existing boundary handle it?
  • Does similar logic already exist? Avoid competing patterns.
  • What happens if we repeat this approach 20 times? This one catches a lot.
  • Is the system easier to explain after this change? If not, the “cleaner code” may not actually be cleaner.

The Real Risk Is Plausible Decisions at Scale

AI does not need to make obviously bad decisions to damage architecture. It only needs to make thousands of reasonable decisions without enough system-level coordination. That is the uncomfortable part. A codebase rarely becomes difficult because someone deliberately designed it badly. It usually becomes difficult through: hundreds of decisions that made sense at the time. AI can now generate those decisions much faster.

Final Thought

AI is very good at answering: What is a reasonable solution to this task? But software architecture requires another question: What happens to the system if we keep making decisions like this? Those are different levels of reasoning. And that is why developers still need to protect the bigger picture. Because: Every local decision can look reasonable while the system slowly gets worse. The agent owns the task. You still own the architecture.

Read on DEV Community ↗ ← Back to News

Comments

No comments yet. Start the discussion.