← Back to Blog

Rethinking Software Architecture: When Best Practices Hold Us Back

By Nolan & ClaudeAugust 30, 202511 min read

What if many of our cherished software "best practices" aren't actually best for the software—but just coping mechanisms for human cognitive limitations? A recent debugging session revealed this uncomfortable truth.

The Build That Changed Everything

During a licensing system implementation, we hit a wall. TypeScript was happy, the architecture looked "clean," but the build system refused to cooperate. After multiple attempts at "proper" solutions, the fix was surprisingly simple: put everything in one file.

This simple change forced a fundamental question: If the "messy" solution works better than the "clean" one, maybe our definition of clean is wrong.

The Incident That Started It All

The "Clean" Architecture

/types/license.ts       (20 lines - type definitions)
/hooks/useLicense.ts    (100 lines - business logic)  
/components/licensing/  (200+ lines - UI components)
/utils/helpers.ts       (40 lines - utility functions)
/constants/license.ts   (10 lines - constants)

Total: 6 files, 370+ lines, complex import graph

This architecture followed every best practice we've been taught:

  • Separation of concerns
  • Single responsibility principle
  • Clean folder organization
  • Logical file boundaries

Yet it wouldn't build. Vite's module system couldn't resolve the exports, despite TypeScript's approval.

The Solution That Worked

/hooks/useLicense.ts    (370 lines - everything co-located)

// Types, enums, business logic, utilities, constants
interface LicenseInfo { ... }
enum Feature { ... }
const LICENSE_CONSTANTS = { ... }
export function useLicense() { ... }
export function validateLicense() { ... }

Total: 1 file, 370 lines, zero import complexity

The Uncomfortable Truth

This incident revealed a fundamental truth: many software architecture decisions exist to accommodate human cognitive limitations, not technical requirements.

Why Humans Separate Files

Human Limitation

Working Memory Limit

~7±2 items in working memory

Coping Strategy

Small, Focused Files

Break code into digestible chunks

Human Limitation

Categorical Thinking

Think in rigid categories

Coping Strategy

Folder Organization

/types, /hooks, /components

Human Limitation

Visual Scanning

Can only look at one screen

Coping Strategy

Small Files

Files that fit on one screen

What AI Doesn't Need

AI assistants like Claude don't share these human limitations:

  • Unlimited Working Memory: Can hold entire codebase in context
  • Relationship Thinking: Sees connections, not just categories
  • Instant Search: No need to "know where things are"
  • Parallel Analysis: Can analyze multiple files simultaneously

The Real Cost of "Clean" Architecture

Our human-optimized patterns create real technical debt:

Hidden Costs of Separation

  • Build Complexity: More files = more module resolution = more failure points
  • Bundle Size: Unnecessary boundaries prevent optimal tree-shaking
  • Runtime Performance: Extra module loading overhead
  • Development Velocity: Time spent managing imports and finding files
  • Tooling Conflicts: TypeScript says it's fine, Vite disagrees

What Really Matters vs. What We Think Matters

What We Obsess Over

  • • "Separation of Concerns"
  • • "Single Responsibility Principle"
  • • "Clean Architecture"
  • • "Proper File Organization"
  • • "Following Best Practices"

What Actually Matters

  • Does it compile?
  • Does it run correctly?
  • Can it be modified safely?
  • Is it performant?
  • Does it solve the problem?

The Pattern Emerges

Once you see it, the pattern is everywhere. Many software "best practices" are actually human coping mechanisms:

Small Files

Exist because humans can't hold large contexts in working memory

Category Folders

Exist because humans think categorically, not relationally

Import/Export Ceremonies

Exist because humans need explicit boundaries to navigate code

Separation Patterns

Exist because humans get confused by multiple concerns in one place

AI-Optimized Architecture

When AI leads development, we can optimize for what actually matters:

Optimize For

  • Build Tool Happiness: Structure code how build tools prefer
  • Runtime Efficiency: Minimize module boundaries
  • Change Safety: Co-locate related changes
  • Feature Completeness: Everything about a feature together

Stop Optimizing For

  • Human Scanning: Files that fit on screen
  • Category Comfort: Artificial type/logic separation
  • Mental Models: Boundaries that help humans navigate
  • Best Practice Compliance: Following rules for their own sake

Example: AI-Optimized Structure

// license-system.ts

// 500 lines but completely self-contained

// Types, business logic, UI components, all co-located

// Zero import complexity, builds instantly, runs fast


interface LicenseInfo {...}
enum Feature {...}
const CONSTANTS = {...}

export function useLicense() {...}
export function LicenseGate() {...}
export function LicenseDialog() {...}

This approach eliminates import complexity, builds instantly, runs efficiently, and can be modified safely—everything that actually matters for software quality.

When to Break the Rules

This doesn't mean abandon all structure. It means being intentional about why we structure code the way we do:

Questions to Ask

  • • Is this separation helping the software, or just making humans comfortable?
  • • Does this boundary solve a technical problem, or a cognitive one?
  • • Are we optimizing for build success or developer ego?
  • • Would the computer care if we combined these files?

Keep Separations That Matter

  • Security boundaries: Different privilege levels
  • Performance boundaries: Code splitting for loading
  • Team boundaries: Different ownership areas
  • Technology boundaries: Client vs server code

Question Separations Based On

  • File size limits: "This file is getting too big"
  • Category thinking: "Types go in /types"
  • Visual comfort: "I can't see it all at once"
  • Convention following: "That's how we always do it"

The Meta-Lesson

This discovery reveals something profound about software development: we've spent decades creating complex architectures to work around human limitations that AI doesn't share.

The Core Insight

Human Tech Debt is real and pervasive. Many "best practices" exist solely to compensate for human cognitive limitations, not technical requirements. With AI partners that don't share these limitations, we can build simpler, more robust systems.

The "messy" single-file solution wasn't messy at all—it was the cleanest solution from the perspective of the system that actually matters: the one that compiles and runs.

Practical Implications

For Human Developers

  • Question whether architectural decisions serve the software or just human comfort
  • Consider co-location when it simplifies the build process
  • Optimize for compilation success, not organizational aesthetics
  • Remember that readable doesn't always mean separated

For AI-Human Teams

  • Let AI suggest structures that optimize for technical requirements
  • Don't impose human organizational patterns on AI-generated code
  • Focus on what works, not what looks familiar
  • Be willing to challenge decades of conventional wisdom

For the Future

As AI becomes more integrated into development workflows, we need new patterns that optimize for human-AI collaboration rather than just human-human collaboration. This means questioning fundamental assumptions about how code should be organized and being open to approaches that prioritize function over form.

The Uncomfortable Question

If you discovered that many of your core beliefs about software architecture were actually just workarounds for human limitations, would you be willing to change them?

The single-file solution that "worked" challenged everything we thought we knew about clean code. But it compiled instantly, ran efficiently, and could be modified safely. Maybe it's time to redefine what "clean" really means.

Ready to Challenge Your Architecture Assumptions?

Learn how UpNorthDigital.ai can help you discover more effective development patterns that optimize for what actually matters in software systems.

Explore Modern Architecture Patterns