Skip to content

Project Structure Optimized for AI

You ask the AI to add a new API endpoint. It finds your route handler, but then spends 8,000 tokens reading through a 2,000-line “utils.ts” file looking for your validation helpers. They are in there — along with 40 other unrelated functions. The AI gets confused, picks the wrong helper, and generates code that breaks existing functionality.

The same task in a well-organized project takes a fraction of the context. The AI finds src/validators/user.ts immediately, reads 80 lines, and generates correct code on the first attempt.

File organization is not just about human readability. It is about AI navigability. The easier your codebase is to traverse, the less context the AI wastes on exploration and the better its output quality.

  • A project structure pattern optimized for AI-assisted development
  • Guidelines for file size, naming, and module boundaries that help AI tools
  • Strategies for organizing monorepos and large codebases for AI consumption
  • Prompts for having the AI analyze and improve your existing structure

AI coding assistants navigate your codebase in two ways:

  1. File reading. The AI reads files to understand context. Every line it reads consumes tokens.
  2. Search. The AI searches by filename, content, or semantic meaning. Clear naming makes search results more relevant.

Poor organization hurts both pathways:

  • Giant files force the AI to read thousands of irrelevant lines to find the one function it needs.
  • Vague names (utils.ts, helpers.ts, misc.ts) make search results ambiguous and force the AI to read files speculatively.
  • Deep nesting increases the number of directory traversals the AI must perform.
  • Circular dependencies confuse the AI about module boundaries and lead to incorrect import suggestions.

Keep files under 300 lines. When a file grows beyond this, the AI must read content it does not need, wasting context. Split by responsibility.

Instead ofUse
utils.ts (800 lines, 30 functions)validators/email.ts, formatters/date.ts, parsers/csv.ts
api.ts (1,200 lines, all endpoints)routes/users.ts, routes/billing.ts, routes/notifications.ts
types.ts (500 lines, all types)types/user.ts, types/billing.ts, types/notification.ts

The filename should tell the AI what is inside without reading it. Semantic search works better when filenames match the concepts they contain.

VagueDescriptive
helpers.tspassword-hashing.ts
index.ts (re-exports)Keep index files thin, under 20 lines
service.tsnotification-service.ts
Component.tsxUserProfileCard.tsx

Put implementation, tests, and types together. When the AI reads your implementation, it can find the related test file immediately without searching.

src/services/
notification/
notification-service.ts
notification-service.test.ts
notification-types.ts
README.md # Brief module description

Cursor’s semantic search indexes your entire workspace. It benefits enormously from descriptive filenames and co-located files because the search can find related code without reading irrelevant files.

Add a .cursor/rules/structure.md to tell Cursor about your conventions:

---
description: "Project structure conventions"
alwaysApply: true
---
## File Organization
- Keep files under 300 lines
- Co-locate tests with implementations
- Use descriptive filenames (notification-service.ts not service.ts)
- Split utils by domain (validators/email.ts not utils.ts)

Monorepos present unique challenges for AI tools because the sheer number of files can overwhelm search and indexing.

Use .cursorignore to exclude irrelevant packages from indexing:

.cursorignore
packages/legacy-app/
packages/internal-tools/
node_modules/
dist/

When working in a specific package, open only that package as your workspace rather than the monorepo root. This focuses Cursor’s semantic search on the relevant code.

Every tool uses .gitignore (and tool-specific ignore files) to skip irrelevant content. Make sure your ignore files exclude everything the AI should not read:

# .gitignore (also respected by AI tools)
node_modules/
dist/
build/
.next/
coverage/
*.min.js
*.bundle.js

Failing to ignore build artifacts means the AI might read minified JavaScript or compiled output, wasting massive amounts of context on unreadable content.

The team resists restructuring. You do not need to reorganize your entire codebase at once. Start by splitting the worst offenders — the 1,000-line files and the catch-all utils. Use the AI to help with the restructuring: it is excellent at extracting functions into separate modules.

Index files become bloated. Index files that re-export everything from a package consume context when the AI reads them. Keep barrel files thin (under 20 lines) or eliminate them in favor of direct imports.

Different tools index differently. Cursor uses semantic embeddings, Claude Code uses file tools (Read, Grep, Glob), and Codex uses its own indexing. A structure that works well for one tool generally works well for all, but pay attention to tool-specific ignore files.

Co-location does not work for shared utilities. Some code genuinely is shared across many features (database connection, logging, error handling). Keep these in a clearly named shared/ or lib/ directory with small, focused files.