folder-architecture
Skill9 starsChanged 6 days agoMITView on GitHub
Define and enforce best-practice folder architecture whenever creating or modifying files. Use whenever the AI adds or edits code to prevent structural clutter, directory bloat, misplaced files, naming inconsistencies, and deep nesting.
In this repository4 other files it gives its agents.
- audit-codebaseSkill
- codebase-healthSkill
- code-designSkill
- implement-folder-architectureSkill
The file
Show its rules and text
We split this skill into the rules it gives an agent
17 rules. About 0 are common, 12 are distinctive, and 5 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈12
Distinctive · estimate
This author's own choices. Worth a look.
5
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 22
Use this skill to act as a Structure Guardian — proactively enforcing clean folder organization and best-practice file placement before …
- Distinctive: line 26
Language-agnostic: Every check applies to any language.
- Distinctive: line 27
Prevention over cleanup: Check structure BEFORE creating or modifying files — not after.
- Distinctive: line 28
Follow existing conventions: When the project already has a pattern (even if imperfect), match it for new files.
- Very specific: line 29
Respect framework defaults: Don't fight established conventions (Rails MVC, Django apps, Next.js App Router, Go's internal/).
- Distinctive: line 30
Feature-first by default: Prefer grouping by domain/feature over technical layering.
- Distinctive: line 72
Before the AI creates or modifies any file, run through this step-by-step checklist in order:
- Distinctive: line 78
Naming convention: Check nearby files for kebab-case, snake_case, camelCase, PascalCase
- Very specific: line 87
If detection is ambiguous — mixed signals, blank project, or borderline size (40-60 files, 400-600 files, or unclear business domains) — …
- Distinctive: line 104
Before placing a new file in a directory:
- Distinctive: line 133
When modifying an existing file:
- Distinctive: line 141
When deciding how to organize a project's folders, choose the appropriate pattern:
- Very specific: line 203
Check root-level packages/ or apps/ directories for bloat (each should contain ≤30 subdirectories)
- Distinctive: line 214
Prefer duplication over wrong abstraction: If only 1-2 features use the code, keep it local.
- Very specific: line 267
When to use audit-codebase: If during development you notice pervasive structural issues (multiple oversized files, many bloated …
- Very specific: line 280
When to use code-design: After placing a file with this skill, use code-design to ensure every function inside that file follows clean …
- Distinctive: line 284
Before creating or modifying any file, verify every applicable item:
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Folder Architecture
Use this skill to act as a Structure Guardian — proactively enforcing clean folder organization and best-practice file placement before every file creation or modification. This skill prevents structural decay at the point of change.
Core Principles
- Language-agnostic: Every check applies to any language. Language-specific details are in references/ORGANIZATION-PATTERNS.md.
- Prevention over cleanup: Check structure BEFORE creating or modifying files — not after.
- Follow existing conventions: When the project already has a pattern (even if imperfect), match it for new files. Only proactively restructure when clear rules are violated.
- Respect framework defaults: Don't fight established conventions (Rails MVC, Django apps, Next.js App Router, Go's
internal/). - Feature-first by default: Prefer grouping by domain/feature over technical layering. Let the AI choose the best architecture pattern based on project size and complexity.
- Project rules win: The thresholds below are defaults for projects that have no rules of their own. Anything the project already enforces — linter/formatter config (ESLint, Ruff, golangci-lint, Checkstyle, rustfmt…), framework structure, or written conventions (
AGENTS.md,CLAUDE.md,CONTRIBUTING.md, ADRs) — takes precedence, and these checks must never contradict it. - Stay in scope: Apply checks to the files the current task touches. Never move, rename, or split files the user did not ask about; report structural issues instead and let the user decide.
- Self-contained: See references/ORGANIZATION-PATTERNS.md for language-specific patterns and references/SPLITTING-GUIDE.md for file-splitting mechanics.
When This Skill Activates
This skill activates every time the AI adds or edits code:
- Creating a new file
- Modifying an existing file
- Creating a new directory
- Adding new imports/exports
- Restructuring existing code (moving files, renaming)
Thresholds (Defaults)
These are heuristics, not hard rules. None of them blocks a file operation on its own: a 🟡 warning means "tell the user and suggest a fix", never "refuse" or "restructure unprompted".
| # | Check | Default threshold | Severity | Action |
| 1 | Directory file count | >30 files per directory | 🟡 Warning | Suggest a subdirectory; place the new file per the existing pattern meanwhile |
| 2 | File line count (growth) | File will cross >400 lines after edits | 🟡 Warning | Warn and suggest splitting |
| 3 | Nesting depth | >4 levels below the source root (see note) | 🟡 Warning | Prefer a shallower location when one fits; never fight framework-mandated paths |
| 4 | Root-level source code | Source file in project root (see exemptions) | 🟡 Warning | Suggest a subdirectory unless the language/tooling expects it at the root |
| 5 | Config-in-source | Config files mixed with source code | 🟢 Suggestion | Suggest config/ only for app config; tool configs stay where the tool expects |
| 6 | Naming consistency | New file doesn't match existing convention | 🟡 Warning | Name the new file to match; do not rename existing files unprompted |
| 7 | Misplaced file | File type doesn't match parent directory | 🟡 Warning | Suggest the appropriate directory |
| 8 | Dumping ground | Adding to utils/, helpers/, shared/ | 🟡 Warning | Check if feature-local placement is better |
| 9 | Barrel file stale | An existing barrel misses a new public export | 🟢 Suggestion | Update it only if the project already uses barrels there (see Barrel File Policy) |
| 10 | Circular import risk | New import may create a cycle | 🟢 Suggestion | Check import chain |
Precedence: If the project configures any of these limits itself (e.g. ESLint
max-lines,max-depth,max-params, RuffC901, a documented max file size), use the project's value and skip the default. Treat these defaults as advisory guidance; they are not suitable as CI gates — use linters for that.
Adjusting thresholds: For small projects (<50 files), use stricter limits (≤15 files/dir, ≤200 lines/file). For large projects (>500 files), relax to ≤50 files/dir, ≤600 lines/file.
Nesting depth is measured from the source root, not the repository root:
src/,lib/,app/,src/main/java/<package-root>/, a monorepo package's ownsrc/, etc. Paths a language or framework dictates are exempt — Java/Kotlin package directories (com/acme/...), Next.js/Nuxt/SvelteKit route trees (app/(group)/[id]/settings/page.tsx), Rails/Django conventions, Gointernal/andcmd/layouts.
Root-level exemptions: Files that the language or tooling expects at the root are fine there — Go
main.go/doc.goin single-binary or library modules,setup.py/manage.py/conftest.py,build.rs,Rakefile,Gemfile, tool configs (tsconfig.json,eslint.config.js,vite.config.ts,pyproject.toml,Cargo.toml, …), and single-file scripts in projects that are a single script.
Workflow: Before Every File Operation
Before the AI creates or modifies any file, run through this step-by-step checklist in order:
Step 1: Detect Project Convention
Scan the project to detect:
- Naming convention: Check nearby files for kebab-case, snake_case, camelCase, PascalCase
- Architecture pattern: Is it feature-first, layered, or mixed?
- Framework defaults: Does the framework prescribe a structure (Rails, Django, Next.js)?
- Existing barrel files: Are there
index.ts,__init__.py,mod.rsfiles?
Implementation tip: Read 3-5 existing files from the target directory to detect patterns. If the directory is empty, check sibling directories.
Step 1a: Confirm Architecture Pattern (ask_user checkpoint)
If detection is ambiguous — mixed signals, blank project, or borderline size (40-60 files, 400-600 files, or unclear business domains) — MUST use ask_user tool before placing files:
ask_user(
question: "Architecture pattern is ambiguous. Which should I enforce?",
options:
a) Feature-Based (Recommended for 50-500 files / clear domains) — e.g., src/users/, src/billing/
b) Layered (Recommended for <50 files / simple CRUD) — e.g., src/controllers/, src/services/
c) Hybrid (Layered-by-Feature, for >500 files / scaling) — e.g., src/features/users/application/
d) Keep existing mixed pattern — don't restructure, just match current placement
)When to skip ask_user: Pattern is clear (e.g., src/features/ exists → feature-first; src/controllers/ + src/services/ with <30 files → layered; framework mandates structure like Next.js App Router). When to require ask_user: Transition zone per shared/references/ORGANIZATION-PATTERNS.md Glossary & Decision Tree and Core Concepts above — never silently auto-migrate between Layered ↔ Feature-Based without user confirmation.
Step 2: Check Target Directory Health
Before placing a new file in a directory:
- File count check: Count files in the target directory. If >30 files, suggest a subdirectory to the user. Do not reorganize the existing files as part of an unrelated change.
- Nesting depth check: Is the directory >4 levels below the source root (framework-mandated paths excluded)? If so, prefer a shallower location when an equally good one exists, and mention the depth to the user. Do not flatten existing trees unprompted.
- Dumping ground check: Is the directory named
utils/,helpers/,shared/,common/,misc/? If so, verify the new file truly belongs there (must be used by 3+ features). Otherwise, suggest a feature-local location. - Directory theme check: Does the directory name match the file type? (e.g., don't put a
.pyfile inimages/, don't put a config file incomponents/)
Step 3: Check File Placement
- Root check: If a new source file would go in the project root, prefer
src/,lib/, or an appropriate subdirectory — unless it is one of the root-level exemptions above (language entry points, tool configs, dependency manifests, CI config, README/LICENSE). - Config check: Tool configs (
tsconfig.json,.eslintrc,pyproject.toml,vite.config.ts, …) stay wherever the tool looks for them — usually the root. Only application/runtime config mixed into source directories is worth flagging. - Test mirroring: For test files, ensure the test path mirrors the source path. E.g.,
src/features/users/user.service.ts→tests/features/users/user.service.test.ts. - Feature proximity: Check if there's an existing feature folder related to the new file. Prefer placing the file within that feature's directory tree.
Step 4: Check Naming
- Detect convention: What naming style do nearby files use? (kebab-case, snake_case, camelCase, PascalCase)
- Match convention: Name the new file using the detected convention
- Component naming: If the file exports a React/Vue/Svelte component, use PascalCase for the filename (e.g.,
UserProfile.tsx) - Test naming: Match the source file naming convention (e.g.,
user.service.ts→user.service.test.tsortest_user_service.py)
Step 5: Check Import Hygiene
- Import ordering: If the project has a formatter or linter that orders imports (Prettier plugins,
eslint-plugin-import/simple-import-sort, Biome, isort, RuffI, gofmt/goimports, rustfmt), follow it and run it — do not hand-order against it. Only without such tooling, use: standard library → third-party → local/project → relative, separated by blank lines. Match the project's existing absolute-vs-relative style rather than switching it. - Barrel file update: If the directory already has a barrel file (
index.ts,__init__.py,mod.rs) that re-exports its siblings, add a re-export for the new public symbol. Never introduce a new barrel in JS/TS unless the project already uses them for that kind of directory (see Barrel File Policy). - Circular dependency check: If adding a new import, verify it does not create a direct A→B→A cycle. Trace the immediate import chain of the imported module.
Step 6: Check for Oversized Files (Modifications)
When modifying an existing file:
- Current size check: Read the file's current line count. If it's already >350 lines and the edit will push it near or over 400, warn and suggest splitting.
- Growth tracking: If the file has grown by 100+ lines since the last change, consider whether the new code belongs in a separate file.
- Split recommendation: If splitting is needed, use the
audit-codebaseskill to generate a detailed split plan, or follow the language-specific mechanics in references/SPLITTING-GUIDE.md.
Architecture Pattern Reference
When deciding how to organize a project's folders, choose the appropriate pattern:
1. Feature-First (Domain-Driven) — Best for medium-to-large projects
src/
├── users/ (everything related to users)
│ ├── handlers/
│ ├── models/
│ ├── tests/
│ └── index.ts
├── billing/ (everything related to billing)
│ ├── handlers/
│ ├── models/
│ ├── tests/
│ └── index.ts
└── shared/ (truly cross-cutting, 3+ consumers)
└── middleware/2. Technical Layered — Good for small/simple projects
src/
├── controllers/
├── services/
├── repositories/
├── models/
└── middleware/3. Hybrid (Layered-by-Feature) — Best for scaling projects
src/
└── features/
├── users/
│ ├── application/ (DTOs, handlers)
│ ├── domain/ (business entities)
│ ├── infrastructure/ (database, external APIs)
│ └── presentation/ (UI components, controllers)
└── billing/4. Ports & Adapters (Hexagonal) — For complex business domains
src/
├── domain/ (pure business logic, no frameworks)
├── application/ (use cases / application services)
├── ports/ (interfaces/contracts)
└── adapters/ (implementations: database, web, API)Decision guide: For small projects (<50 files), prefer technical layered. For medium projects (50-500 files), prefer feature-first. For large/complex projects (>500 files), prefer hybrid or hexagonal.
Monorepo Handling
For monorepos with multiple packages/modules:
- Apply checks per-package, not globally
- Each package is treated as its own project with its own thresholds
- Check root-level
packages/orapps/directories for bloat (each should contain ≤30 subdirectories) - Shared packages (e.g.,
@shared/ui,common/) should follow stricter rules: max 15 files per directory - Each package should have a consistent architecture pattern (don't mix feature-first and layered across packages)
Shared / Cross-Cutting Code Policy
The shared/, common/, or lib/ directory is permitted but strictly limited:
- Entry requirement: A function/module must be used by 3+ features before it qualifies for shared/placement
- Size limit: Shared/ directory must not exceed 30 files total. If exceeded, split into
shared/ui/,shared/utils/,shared/types/, etc. - No business logic: Shared/ is for technical cross-cutting concerns only (formatting, types, HTTP clients, logging). Business logic must remain in feature folders.
- Prefer duplication over wrong abstraction: If only 1-2 features use the code, keep it local. Extract to shared/ only when the 3rd consumer appears.
Barrel File Policy
Follow the project. Never create barrel files by default.
Barrel files behave very differently per language:
- Rust
mod.rs/ Python__init__.pyare part of the module system. Keep them correct whenever you add a module (Rust requirespub mod; Python re-exports only if the package already re-exports its submodules). - JS/TS
index.tsre-export files are optional and, in large apps, often a liability: they defeat tree-shaking in some bundlers, slow down test runners and dev servers (importing one symbol loads the whole barrel), and are a common source of circular imports. Many large codebases ban them (e.g. viaeslint-plugin-no-barrel-files) and Next.js shipsoptimizePackageImportsto work around them.
| Situation | Action |
| Directory already has a barrel that re-exports siblings | Add the new public export to it |
| JS/TS directory without a barrel | Do not create one; consumers import from the module file directly |
Package entry point (src/index.ts listed in exports) | Keep it — it is the public API of a library, not an internal barrel |
| Project lints against barrels / uses direct imports | Never add re-exports |
Python package that re-exports via __init__.py | Add from .new_module import ExportedSymbol (and __all__ if used) |
| Rust | Add pub mod new_module; (re-export with pub use only if the parent already does) |
| Go | N/A — same package is auto-visible |
Language-Specific Patterns
For language-specific folder conventions, naming styles, and module mechanics, see:
- ORGANIZATION-PATTERNS.md — How to organize code per language (Python, JS/TS, Go, Rust, Java, C#, Ruby, PHP)
- SPLITTING-GUIDE.md — How to split oversized files per language (import changes, module boundaries, before/after examples)
Edge Cases
| Situation | Handling |
| Empty project | Create a standard structure: src/ or lib/ based on language |
| No clear convention | Default to kebab-case for JS/TS, snake_case for Python/Rust/Go, PascalCase for components/classes |
| Generated code | Skip checks for files in generated/, dist/, build/ — they follow their own rules |
| Third-party/vendor | Skip checks for node_modules/, vendor/, .venv/ |
| Hidden files | Skip .-prefixed files except config files |
| Symlinks | Follow symlinks but avoid creating new ones |
| Test-only project | If the project is exclusively tests, use the test framework's convention (__tests__/, spec/) |
| User explicitly requests location | If the user says "put it in [path]", follow their instruction but flag any structural issues |
Interaction with audit-codebase Skill
This skill (folder-architecture) and audit-codebase are complementary:
| Aspect | folder-architecture (this) | audit-codebase |
| When | Before/during file operations | After-the-fact review |
| Purpose | Prevent clutter proactively | Detect existing issues |
| Scope | Single file/directory at a time | Full codebase scan |
| Action | Place correctly, warn, split if needed | Generate report with fix plan |
| Split depth | Basic split warning + context | Full split plan with diagrams |
When to use audit-codebase: If during development you notice pervasive structural issues (multiple oversized files, many bloated directories, inconsistent patterns), switch to audit-codebase for a comprehensive review.
code-design
This skill (folder-architecture) handles where files go. The code-design skill handles how code is written inside those files — it's the function-level counterpart:
| Aspect | folder-architecture (this) | code-design |
| Scope | File/directory placement | Function-level code design |
| Checks | Dir count, nesting depth, naming, etc. | Pure functions, SRP, guard clauses |
| When | Before creating/modifying files | Before/during writing functions |
| Action | Place correctly or warn | Refactor impure/nested/mixed code |
When to use code-design: After placing a file with this skill, use code-design to ensure every function inside that file follows clean design principles — guard clauses, single responsibility, side-effect management, and top-to-bottom readability.
Execution Checklist
Before creating or modifying any file, verify every applicable item:
Pre-File Creation
- Step 1: Detected project convention (naming, architecture pattern)
- Step 0: Checked for project-defined rules (linter config,
AGENTS.md/CLAUDE.md, framework structure) — they override the defaults here - Step 2: Target directory health checked (file count, depth below source root); any concern reported, not auto-fixed
- Step 2: Not adding to a dumping ground (utils/shared verified)
- Step 3: New source file is not in the project root (unless a root-level exemption)
- Step 3: Tool configs left where their tool expects them
- Step 3: Tests mirror source structure
- Step 4: File naming matches project convention
- Step 5: Imports ordered by the project's formatter/linter (or the fallback order if none)
- Step 5: Existing barrel file updated (no new JS/TS barrels created)
- Step 5: No circular import created
Pre-File Modification
- Step 6: Existing file is not near the 400-line threshold
- Steps 1-5: All applicable creation checks re-verified if adding new exports
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

