Skip to content

How to Prevent Spaghetti Code When Using AI Coding Assistants?

Problem

When I watched non-programmer friends use AI coding assistants, I saw a pattern. Their NextJS apps had page files over 1000 lines. Enormous amounts of repetition where there should be reusable components. Code that looked clean on the surface but had terrible overarching structure.

The spaghetti pattern
┌─────────────────────────────────────────────────────────────┐
│ AI-Generated File │
│ │
│ ✓ Proper indentation │
│ ✓ Sensible variable names │
│ ✓ Even design patterns │
│ │
│ ✗ File: 1000+ lines │
│ ✗ Duplicated components │
│ ✗ No separation of concerns │
│ ✗ Business logic mixed with UI │
│ │
└─────────────────────────────────────────────────────────────┘

I asked myself: How do I prevent this when using AI assistants?

Environment

  • AI coding assistants: Claude, ChatGPT, GitHub Copilot
  • Context: Building web applications with AI assistance
  • My goal: Clean, maintainable architecture

What happened?

A Reddit comment (9 points) described the hidden complexity:

The structural problem
"Aesthetically, it looks good. It has classes, inheritance, etc.
But the overarching structure is terrible, and the features
it uses are used poorly."

Another developer (2 points) admitted:

The steering need
"On the architecture level I really have to steer it.
But it is easy to adjust architecture with these agents too."

I realized that AI optimizes for:

  • Immediate functionality over maintainability
  • Local coherence over global structure
  • Copy-paste solutions over abstraction

The Solution

I developed five pillars to prevent spaghetti code with AI assistants:

Pillar 1: Architectural Steering (Active Design)

Define the architecture BEFORE asking AI to generate code. Specify file structure, module boundaries, data flow.

Steering prompt template
You are building a user dashboard. Follow this architecture:
FILE STRUCTURE:
- /src/features/userDashboard/
- components/
- UserList.jsx (<100 lines, presentational)
- UserStats.jsx (<100 lines, presentational)
- hooks/
- useUsers.js (data fetching + state)
- useScoring.js (business logic)
- utils/
- scoreCalculator.js (pure functions)
- index.jsx (container, orchestrates hooks and components)
RULES:
1. Each file <300 lines
2. Business logic in hooks/utils
3. Components only handle rendering
4. All API calls in custom hooks
5. No inline useEffect in components
Generate the feature following these constraints.

Pillar 2: Iterative Review Cycles

Never accept first draft as final. Run security, performance, and architecture audits.

Review cycle
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ AI Generates │ → │ Review │ → │ Refactor │
└─────────────┘ └─────────────┘ └─────────────┘
↑ │
└────────────────────┘
Repeat until clean

Ask AI to review its own code:

Self-review prompt
"Review this file for SOLID violations and propose refactoring"

Pillar 3: DRY Enforcement

Actively search for duplication. Prompt AI to extract shared utilities.

DRY prompt
"Find all duplicated code blocks and extract into shared utilities"

Force component abstraction before file size exceeds 300 lines.

Pillar 4: Incremental Complexity

Start with core architecture, not features. Build skeleton first: routes, state management, API layer.

Incremental building
Phase 1: Design architecture → Define file structure
Phase 2: Build skeleton → Routes, services, state
Phase 3: Add features → Incrementally with clear integration points

Use “building block” prompts:

Building block prompt
"Add this feature following the existing patterns in /src/services"

Pillar 5: Documentation as Code Review Tool

Require AI to document decisions, not just code. Generate architecture diagrams.

Documentation rule
If AI can't explain the structure clearly, the structure is wrong.

The Reason

I think the key reason for spaghetti code is passive acceptance.

A comment (5 points) captured the fundamental gap:

The skill gap
"Knowing how to do it and utilizing AI to expedite the process
is completely different from 'Claude, make me Uber'"

Technical debt compounds exponentially

A “working” 1000-line file becomes:

  • Impossible to test in isolation
  • Nightmare to debug
  • Barrier to team onboarding
  • Expensive to refactor later

The AI maintenance paradox

Code that’s easy to generate becomes hard to maintain:

  • Future AI sessions lack context of original decisions
  • Refactoring prompts become increasingly vague
  • Small changes trigger large regressions
Maintenance cost timeline
┌─────────────────────────────────────────────────────────────┐
│ │
│ Week 1: Fast generation (2 hours) │
│ Week 4: First bug fix (4 hours - finding the code) │
│ Week 8: Feature addition (8 hours - untangling logic) │
│ Week 12: Refactor attempt (16 hours - or abandon) │
│ │
└─────────────────────────────────────────────────────────────┘

Common Mistakes

I identified these mistakes to avoid:

  1. “Make me [complex app]” without structure

    • Fix: Break into phases: “First, design the architecture. Then, implement phase 1.”
  2. Accepting code without understanding

    • Fix: Ask for explanations: “Explain why this structure was chosen”
  3. Ignoring file size limits

    • Fix: Hard limit of 400 lines per file, auto-refactor when exceeded
  4. No automated checks

    • Fix: Add ESLint rules for file size, cyclomatic complexity, code duplication
  5. Skipping refactoring sessions

    • Fix: Weekly “architecture review” prompts

Automated Architecture Check

I set up these rules to catch problems early:

eslint-architecture.config.js
module.exports = {
rules: {
'max-lines': ['error', { max: 400, skipBlankLines: true, skipComments: true }],
'max-lines-per-function': ['error', { max: 50 }],
'complexity': ['error', 10],
'max-depth': ['error', 4]
}
}

Summary

In this post, I showed how to prevent spaghetti code when using AI coding assistants. The key point is that spaghetti code isn’t inevitable - it’s the result of passive acceptance.

By actively steering architecture, enforcing code quality rules, and treating AI as a tool that requires supervision, you can harness AI’s speed without sacrificing maintainability.

Final Words + More Resources

My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me

Here are also the most important links from this article along with some further resources that will help you in this scope:

Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!

Comments