agentleFS
yaohuangguanyaohuangguan/agent-playbook/skills/structure-code/SKILL.md

structure-code

Skill1 starsChanged 14 days agoMITView on GitHub

Organise code the way a careful engineering team would - clear module boundaries, one job per file, folders grouped by domain, one source of truth for permissions, config, errors, text and flags, and no dead code left behind. Use this whenever you create a file, module, package, service, component or folder, add a feature that spans several files, or touch code that is already too large, in any language, even if the task only asks for behaviour.

2 other projects use a version of this.

Adopted by

Most recently taken up by

The file

We split this skill into the rules it gives an agent

5 rules. About 0 are common, 4 are distinctive, and 1 is very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈4

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 1

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    Learn the project's structure first: Read the architecture or contributing docs, the lint and build config and the top-level source folders.

    line 25
  2. Distinctive:

    Place each new piece before you write it: Write a short list of the files you will add or change, and why each lives there:

    line 32
  3. Very specific:

    Group folders by domain: When a folder holds more than about 15 files, group them into subfolders by feature or domain, such as billing/ …

    line 52
  4. Distinctive:

    Remove what you replace: When new code takes over from old code, delete the old module, its routes, data access, config, messages, styles, …

    line 91
  5. Distinctive:

    Update the map: If the project documents its modules, such as in an architecture file or a README, update it in the same change.

    line 96

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

Structure code

Code that works can still be expensive to change. Left alone, agents add to whichever file is open until it is thousands of lines long, drop every new file into one folder, let any module import any other, and copy the same rule, constant, permission check or error text into every place that needs it. Each copy drifts. Each of these makes the next change slower and riskier, for people and for the next agent. This skill applies to every layer: services, APIs, jobs, command-line tools, libraries and user interfaces.

When to use

Any change that adds code. Apply it to the code you add or touch. To reorganise existing code without changing behaviour, follow refactor-safely as well. For specific layouts, read references/backend.md for services and APIs and references/frontend.md for a user interface.

Steps

  1. Learn the project's structure first. Read the architecture or contributing docs, the lint and build config and the top-level source folders. Find where it already keeps entry points, domain logic, data access, external clients, config, shared helpers and tests. The project's own convention wins over this skill. Copy the pattern of the nearest well-organised module, not the nearest file.
  2. Place each new piece before you write it. Write a short list of the files you will add or change, and why each lives there:

    Kind of codeBelongs in
    Entry points: routes, handlers, commands, jobs, viewsA thin layer that parses input, calls the domain and shapes output
    Business rules and decisionsDomain modules with no framework, transport or storage imports
    Reading and writing stored dataOne data-access module per table, collection or aggregate
    Calls to another service or APIOne client per service, which owns its timeouts and retries
    Settings and secretsOne config module that reads and validates them at startup
    Rules used in many placesOne shared module that owns that rule (step 6)
    Interface text and stylesThe message catalog and a style file per feature
  3. Keep each file to one job. Aim for under 300 lines in a hand-written file. Past 500, split it before you add more. Split by responsibility, such as entry point, rules, data access and formatting, never by cutting a file in half. Generated files, fixtures and data files are exempt. The same applies to functions: one that needs a long comment to explain its sections is several functions.
  4. Group folders by domain. When a folder holds more than about 15 files, group them into subfolders by feature or domain, such as billing/ and devices/, not by kind, such as utils/ and helpers/. Use the same domain names in every layer, including tests, messages and styles, so that one search finds everything about a feature. Do not add a level of nesting that holds one file.
  5. Keep dependencies pointing one way. Entry points depend on domain modules, and domain modules depend on interfaces for storage and outside services, never the reverse. Shared code depends on nothing feature-specific. Do not create an import cycle. If two features need the same thing, move it down into a shared module instead of importing one feature from the other.
  6. Give every cross-cutting rule one source of truth. Each of these gets one named module, function or table, and every caller uses it:

    • Permissions: can(user, "billing:manage"), not user.role === "owner"
    • Config and environment: read and validated in one place, never ad hoc
    • Error codes: one list, each mapped to a status and a message in one place
    • Constants and limits, such as plans, quotas and timeouts
    • Feature flags, kept apart from permissions
    • Logging, metrics and audit events, through one helper with a fixed shape
    • User-facing text, through the message catalog, one file per language

    A copied condition drifts, and then two places disagree. Checks in a user interface are for display only. The server must still check every request, so follow secure-change for that side.

  7. Pass shared context explicitly. Values that many layers need, such as the current user, request id, language or a database handle, travel through one context object, dependency injection or a provider. Do not reach for a hidden global or a module-level singleton that tests cannot replace. State used by one module stays in that module.
  8. Guard the edges. Validate and convert input once, where it enters the system, into typed values. Inside, trust those types instead of re-checking everywhere. Convert outside errors into your own error codes at the client that received them.
  9. Remove what you replace. When new code takes over from old code, delete the old module, its routes, data access, config, messages, styles, tests and fixtures, and redirect or deprecate old URLs and APIs. Dead code left beside new code gets copied by the next agent.
  10. Update the map. If the project documents its modules, such as in an architecture file or a README, update it in the same change.
  11. Check the shape of your diff. List the line counts of the files you added or grew, and any new imports between features. Search your diff for repeated conditions, raw constants, environment reads outside config, and user-facing text outside the catalog. Fix what you added, then follow verify-and-report.

Stop and ask when

  • The project's convention conflicts with this skill and you cannot tell which the user wants.
  • Placing the code well means moving a lot of existing code. Propose a separate refactor and do the task in the existing structure.
  • A split would change a public API, exported names or a stored format.
  • The project has no config module, message catalog or permission helper, and adding one would mean a new dependency or a wide change.

Common mistakes

Tempting moveWhy it failsDo this instead
Add the feature to the open fileFiles grow to thousands of linesPlace it by step 2
Put every new file in one folderNobody can find anythingGroup by domain at about 15 files
Create utils/ for everythingIt becomes a second, unowned codebasePut helpers in the domain that owns them
Query the database from a handlerRules and storage tangle togetherGo through the domain and data access
Copy a role check or a limitCopies drift apartUse the one shared rule
Read an environment variable inlineMissing config fails late, in productionRead it in the config module
Hard-code interface textIt cannot be translated or reusedAdd a catalog key per language
Import one feature from anotherHidden coupling and cyclesMove the shared part down
Keep the old version "just in case"It gets copied and maintainedDelete it and redirect
Reorganise the whole repositoryUnreviewable, and out of scopeStructure what you touch

Example

Task: "Let owners change their subscription plan."

Weak plan: "Add a /plan endpoint to server.js and a button to the settings page."

Strong plan:

Files:
- api/billing/change-plan.ts: parse input, call the domain, map errors
- billing/plans.ts: which changes are allowed (the domain rule)
- billing/billing-store.ts: read and write the subscription row
- clients/payments.ts: the payment provider call, with timeout and retry
- auth/permissions.ts: add "billing:manage" for owners; the API and UI call can()
- errors.ts: PLAN_CHANGE_NOT_ALLOWED with its status and message key
- ui: page, hook and styles in billing/ folders; text in locales/en and locales/es
- architecture doc: add the billing module
Not changed: server.js only gains the route.

Report

Placement: <each new file and why it lives there>
File sizes: <touched files with lines before -> after>
Dependencies: <new imports between modules, and their direction>
Shared sources used: <permissions, config, errors, flags, messages>
Removed: <old code and data deleted, and redirects added>
Seen but not changed: <structure problems outside this task>

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.