Problem: Your TypeScript Project Became a Maze
Your codebase hit 10,000+ lines and now imports are circular, modules are tangled, and onboarding takes weeks. Manual refactoring would take months.
You'll learn:
- How AI tools analyze dependency graphs automatically
- Proven folder structures for 100K+ line projects
- Automated refactoring without breaking production
- When AI suggestions actually help vs. hurt
Time: 30 min | Level: Advanced
Why This Happens
TypeScript projects grow organically. Features ship fast, but architectural debt accumulates. By the time you notice:
Common symptoms:
- Build times over 60 seconds
- Circular dependency warnings in console
- "Utils" folders with 50+ unrelated files
- Import paths like
../../../shared/helpers/validation/user - New developers can't find where code lives
Root cause: No enforced boundaries. TypeScript allows any file to import anything, so structure degrades without active maintenance.
Solution
Step 1: Map Your Current Architecture with AI
Use GitHub Copilot CLI or Cursor to generate a dependency graph:
# Install dependency analysis tool
npm install -g madge
# Generate visual dependency graph
madge --circular --extensions ts,tsx ./src --image deps.svg
# Use AI to analyze the output
gh copilot explain "$(madge --json --circular ./src)"
Expected: AI identifies circular dependencies and suggests which modules to split first.
Why this works: AI tools parse your entire codebase context faster than manual review. They spot patterns humans miss across thousands of files.
Step 2: Implement Feature-Based Structure
Stop organizing by technical role (components/, utils/, hooks/). Use feature-based architecture:
// ❌ Before: Technical organization
src/
├── components/
│ ├── UserList.tsx
│ ├── OrderTable.tsx
│ └── ProductCard.tsx
├── hooks/
│ ├── useUser.ts
│ └── useOrders.ts
└── utils/
├── validation.ts
└── formatting.ts
// ✅ After: Feature-based organization
src/
├── features/
│ ├── users/
│ │ ├── components/
│ │ │ └── UserList.tsx
│ │ ├── hooks/
│ │ │ └── useUser.ts
│ │ ├── api/
│ │ │ └── userClient.ts
│ │ └── index.ts # Public API
│ ├── orders/
│ │ ├── components/
│ │ ├── hooks/
│ │ └── index.ts
│ └── products/
│ └── ...
├── shared/ # Only truly shared code
│ ├── ui/ # Design system components
│ ├── utils/ # Pure functions
│ └── types/ # Global types
└── core/ # App-wide concerns
├── auth/
├── routing/
└── config/
Use AI to migrate: Paste your file list into Cursor and ask:
Analyze this file structure and generate a migration plan to feature-based
architecture. Group files by business domain, not technical function.
Show which files depend on each other and suggest migration order.
Step 3: Enforce Boundaries with ESLint
Prevent future chaos with automated rules:
npm install -D eslint-plugin-boundaries
// .eslintrc.js
module.exports = {
plugins: ['boundaries'],
settings: {
'boundaries/elements': [
{
type: 'feature',
pattern: 'src/features/*',
mode: 'folder',
capture: ['featureName']
},
{
type: 'shared',
pattern: 'src/shared/*'
},
{
type: 'core',
pattern: 'src/core/*'
}
],
'boundaries/ignore': ['**/*.test.ts', '**/*.spec.ts']
},
rules: {
'boundaries/element-types': [2, {
default: 'disallow',
rules: [
{
from: 'feature',
allow: ['shared', 'core', ['feature', {featureName: '${from.featureName}'}]]
},
{
from: 'shared',
allow: ['shared'] // Shared can only import other shared
},
{
from: 'core',
allow: ['shared', 'core']
}
]
}]
}
};
Why this works: Features can't import from other features directly. Forces you to move shared code to /shared or create explicit APIs via index.ts exports.
If it fails:
- Error: "Import of 'X' is not allowed": Move the imported code to
/sharedor expose it through feature'sindex.ts - Too many violations: Start with
warnlevel, fix gradually, then switch toerror
Step 4: Use AI for Automated Refactoring
Let AI handle tedious import path updates:
// Use Cursor's AI refactor (Cmd+Shift+R)
// Before: Select scattered validation code
const validateEmail = (email: string) => { /* ... */ }
const validatePhone = (phone: string) => { /* ... */ }
const validateAddress = (addr: Address) => { /* ... */ }
// Prompt: "Extract all validation functions to shared/utils/validation.ts
// and update all imports across the codebase"
// AI generates:
// 1. New file: src/shared/utils/validation.ts
// 2. Updates 47 import statements
// 3. Adds proper TypeScript exports
// 4. Preserves all JSDoc comments
Test the changes:
# AI-generated validation should pass existing tests
npm test -- --testPathPattern=validation
# Check type safety
npm run type-check
# Verify no circular dependencies introduced
madge --circular ./src
Step 5: Create Clear Module Boundaries
Each feature exports a public API via index.ts:
// src/features/users/index.ts - Public API
export { UserList } from './components/UserList';
export { useUser, useUsers } from './hooks';
export type { User, UserRole } from './types';
// Private internals (not exported)
// - components/UserForm.tsx
// - utils/userValidation.ts
// - api/userClient.ts
// Other features import like this:
import { UserList, useUsers } from '@/features/users';
// ❌ NOT like this (breaks encapsulation):
import { UserForm } from '@/features/users/components/UserForm';
Configure path aliases:
// tsconfig.json
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/features/*": ["src/features/*"],
"@/shared/*": ["src/shared/*"],
"@/core/*": ["src/core/*"]
}
}
}
Ask AI to generate barrel exports:
Review src/features/orders/ and generate the index.ts file.
Export only the components and hooks that other features need.
Keep internal utilities private.
Step 6: Document Architecture Decisions
Use AI to generate living documentation:
# Install documentation tool
npm install -D typedoc typedoc-plugin-markdown
# Generate docs with AI summaries
npx typedoc --plugin typedoc-plugin-markdown --out docs
Create an architecture decision record (ADR):
// docs/architecture/001-feature-based-structure.md
# ADR 001: Feature-Based Project Structure
**Status:** Accepted
**Date:** 2026-02-15
**AI Analysis:** Generated via GitHub Copilot
## Context
Codebase reached 15K lines with 200ms build time degradation.
Circular dependencies between `/components` and `/hooks` folders.
## Decision
Adopt feature-based structure where each domain (users, orders, products)
is self-contained with its own components, hooks, and utilities.
## Consequences
**Positive:**
- Colocated related code (easier debugging)
- Clear ownership per team
- Enforced via ESLint boundaries
**Negative:**
- Initial migration took 2 sprints
- Shared code requires more planning
- Learning curve for new developers
## Compliance
- ESLint plugin prevents boundary violations
- Pre-commit hooks check for circular deps
- Monthly AI audits via madge + Copilot
Automate checks:
// package.json
{
"scripts": {
"arch:check": "madge --circular src && eslint src --rule 'boundaries/element-types: error'",
"arch:report": "madge --json src > architecture-report.json && gh copilot suggest 'Review this dependency graph and suggest optimizations'"
}
}
Verification
Run comprehensive checks:
# 1. No circular dependencies
madge --circular src
# Expected: "No circular dependencies found"
# 2. Boundary rules enforced
npm run lint
# Expected: 0 boundary violations
# 3. Build time improved
time npm run build
# Expected: <30s for 10K lines (was >60s)
# 4. Type safety intact
npm run type-check
# Expected: 0 errors
You should see:
- Clean dependency graph visualization
- Faster builds (30-50% improvement typical)
- ESLint passing with boundaries plugin active
- Feature folders clearly separated
What You Learned
- AI tools excel at analyzing large codebases and suggesting structural improvements
- Feature-based architecture scales better than technical role separation
- Automated boundary enforcement prevents architecture decay
- Dependency visualization reveals problems manual review misses
Limitations:
- AI can suggest refactors but can't understand business logic nuances
- Automated tools might suggest splitting code that should stay together
- Initial setup requires human architectural decisions
When NOT to use this:
- Projects under 5K lines (premature optimization)
- Prototypes or MVPs (structure too rigid)
- Microservices (different boundary rules)
AI Tools Comparison (2026)
| Tool | Best For | Limitations |
|---|---|---|
| GitHub Copilot CLI | Dependency analysis, migration planning | Requires GitHub account |
| Cursor | Interactive refactoring, bulk updates | Paid tier for large projects |
| Codeium | Free alternative, good for smaller teams | Less context window |
| Tabnine | Enterprise with on-prem requirements | Slower than GPT-4 based tools |
Recommended stack: Cursor (daily dev) + madge (CI/CD checks) + ESLint boundaries (enforcement)
Common Pitfalls
Pitfall 1: Over-fragmenting Features
// ❌ Too granular
src/features/
├── user-list/
├── user-detail/
├── user-edit/
└── user-delete/
// ✅ Right level
src/features/
└── users/
├── components/
├── hooks/
└── pages/
Rule: If two "features" always deploy together and share 80%+ types, merge them.
Pitfall 2: Polluted Shared Folder
// ❌ Dumping ground
src/shared/utils/
├── formatDate.ts // Actually only used by analytics feature
├── validateEmail.ts // Used everywhere - correct
└── calculateShipping.ts // Only used by orders feature
// ✅ Keep shared truly shared
src/
├── features/
│ ├── analytics/
│ │ └── utils/formatDate.ts // Move here
│ └── orders/
│ └── utils/calculateShipping.ts // Move here
└── shared/
└── utils/
└── validateEmail.ts // Only truly shared code
AI prompt to clean this:
Review src/shared and identify utilities used by only one feature.
Show import counts per file and suggest which to move back to features.
Pitfall 3: Trusting AI Blindly
AI suggested moving auth logic to /shared. Wrong - it's core infrastructure.
// AI suggestion (WRONG)
src/shared/auth/ // ❌ Not "shared" between features
// Correct placement
src/core/auth/ // ✅ Core app infrastructure
Always validate AI suggestions against:
- Your business domain knowledge
- Deployment boundaries
- Team ownership structure
Advanced: Monorepo Migration
For 50K+ line projects, consider workspace-based structure:
// With AI migration assistance
packages/
├── web-app/ # Main TypeScript app
├── mobile-app/ # React Native (shared types)
├── shared-components/ # Design system
└── shared-types/ # TypeScript definitions
// AI command:
// "Analyze this codebase and suggest which modules should become
// separate packages in a monorepo. Consider bundle size, deploy
// frequency, and team ownership."
Tools: Turborepo or Nx with AI-powered migration scripts.
Tested with TypeScript 5.5.4, ESLint 9.x, Node.js 22.x on Ubuntu 24.04 & macOS Sonoma AI tools: GitHub Copilot, Cursor 0.42, madge 8.x
Appendix: Quick Reference
Pre-Migration Checklist:
- Backup codebase (git tag before refactor)
- All tests passing
- CI/CD pipeline green
- Team reviewed structure proposal
During Migration:
- Move one feature at a time
- Keep CI green (feature flags if needed)
- Update documentation as you go
- AI-generate import updates
Post-Migration:
- Add ESLint boundaries to CI
- Schedule monthly dependency audits
- Train team on new structure
- Monitor build time improvements