agentleFS
DrakaniiaDrakaniia/skills/skills/folder-architecture/SKILL.md

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.

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
  1. Distinctive:

    Use this skill to act as a Structure Guardian — proactively enforcing clean folder organization and best-practice file placement before …

    line 22
  2. Distinctive:

    Language-agnostic: Every check applies to any language.

    line 26
  3. Distinctive:

    Prevention over cleanup: Check structure BEFORE creating or modifying files — not after.

    line 27
  4. Distinctive:

    Follow existing conventions: When the project already has a pattern (even if imperfect), match it for new files.

    line 28
  5. Very specific:

    Respect framework defaults: Don't fight established conventions (Rails MVC, Django apps, Next.js App Router, Go's internal/).

    line 29
  6. Distinctive:

    Feature-first by default: Prefer grouping by domain/feature over technical layering.

    line 30
  7. Distinctive:

    Before the AI creates or modifies any file, run through this step-by-step checklist in order:

    line 72
  8. Distinctive:

    Naming convention: Check nearby files for kebab-case, snake_case, camelCase, PascalCase

    line 78
  9. Very specific:

    If detection is ambiguous — mixed signals, blank project, or borderline size (40-60 files, 400-600 files, or unclear business domains) — …

    line 87
  10. Distinctive:

    Before placing a new file in a directory:

    line 104
  11. Distinctive:

    When modifying an existing file:

    line 133
  12. Distinctive:

    When deciding how to organize a project's folders, choose the appropriate pattern:

    line 141
  13. Very specific:

    Check root-level packages/ or apps/ directories for bloat (each should contain ≤30 subdirectories)

    line 203
  14. Distinctive:

    Prefer duplication over wrong abstraction: If only 1-2 features use the code, keep it local.

    line 214
  15. Very specific:

    When to use audit-codebase: If during development you notice pervasive structural issues (multiple oversized files, many bloated …

    line 267
  16. Very specific:

    When to use code-design: After placing a file with this skill, use code-design to ensure every function inside that file follows clean …

    line 280
  17. Distinctive:

    Before creating or modifying any file, verify every applicable item:

    line 284

≈ “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.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

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".

#CheckDefault thresholdSeverityAction
1Directory file count>30 files per directory🟡 WarningSuggest a subdirectory; place the new file per the existing pattern meanwhile
2File line count (growth)File will cross >400 lines after edits🟡 WarningWarn and suggest splitting
3Nesting depth>4 levels below the source root (see note)🟡 WarningPrefer a shallower location when one fits; never fight framework-mandated paths
4Root-level source codeSource file in project root (see exemptions)🟡 WarningSuggest a subdirectory unless the language/tooling expects it at the root
5Config-in-sourceConfig files mixed with source code🟢 SuggestionSuggest config/ only for app config; tool configs stay where the tool expects
6Naming consistencyNew file doesn't match existing convention🟡 WarningName the new file to match; do not rename existing files unprompted
7Misplaced fileFile type doesn't match parent directory🟡 WarningSuggest the appropriate directory
8Dumping groundAdding to utils/, helpers/, shared/🟡 WarningCheck if feature-local placement is better
9Barrel file staleAn existing barrel misses a new public export🟢 SuggestionUpdate it only if the project already uses barrels there (see Barrel File Policy)
10Circular import riskNew import may create a cycle🟢 SuggestionCheck import chain

Precedence: If the project configures any of these limits itself (e.g. ESLint max-lines, max-depth, max-params, Ruff C901, 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 own src/, 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, Go internal/ and cmd/ layouts.

Root-level exemptions: Files that the language or tooling expects at the root are fine there — Go main.go/doc.go in 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.rs files?

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 .py file in images/, don't put a config file in components/)

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.ts or test_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, Ruff I, 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-codebase skill 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/ or apps/ 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__.py are part of the module system. Keep them correct whenever you add a module (Rust requires pub mod; Python re-exports only if the package already re-exports its submodules).
  • JS/TS index.ts re-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. via eslint-plugin-no-barrel-files) and Next.js ships optimizePackageImports to work around them.
SituationAction
Directory already has a barrel that re-exports siblingsAdd the new public export to it
JS/TS directory without a barrelDo 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 importsNever add re-exports
Python package that re-exports via __init__.pyAdd from .new_module import ExportedSymbol (and __all__ if used)
RustAdd pub mod new_module; (re-export with pub use only if the parent already does)
GoN/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

SituationHandling
Empty projectCreate a standard structure: src/ or lib/ based on language
No clear conventionDefault to kebab-case for JS/TS, snake_case for Python/Rust/Go, PascalCase for components/classes
Generated codeSkip checks for files in generated/, dist/, build/ — they follow their own rules
Third-party/vendorSkip checks for node_modules/, vendor/, .venv/
Hidden filesSkip .-prefixed files except config files
SymlinksFollow symlinks but avoid creating new ones
Test-only projectIf the project is exclusively tests, use the test framework's convention (__tests__/, spec/)
User explicitly requests locationIf 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:

Aspectfolder-architecture (this)audit-codebase
WhenBefore/during file operationsAfter-the-fact review
PurposePrevent clutter proactivelyDetect existing issues
ScopeSingle file/directory at a timeFull codebase scan
ActionPlace correctly, warn, split if neededGenerate report with fix plan
Split depthBasic split warning + contextFull 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:

Aspectfolder-architecture (this)code-design
ScopeFile/directory placementFunction-level code design
ChecksDir count, nesting depth, naming, etc.Pure functions, SRP, guard clauses
WhenBefore creating/modifying filesBefore/during writing functions
ActionPlace correctly or warnRefactor 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

Download now (ZIP)

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
What did you use it for? e.g. Reviewing pull requests in a Rails app

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.