code-structure
Skill0 starsChanged 31 days agoMITView on GitHub
Structuring and naming a codebase for long-term navigability: enforces a 696-line file ceiling, splits files by single responsibility, applies snake_case / PascalCase / SCREAMING_SNAKE consistently, wires structured logging with full error context at every boundary, adds file-level navigation headers, and scaffolds a clean folder hierarchy where every directory has exactly one job. Use when you want to structure a project properly, clean up a messy codebase, split a large file, add proper logging, set up a folder structure, enforce naming conventions, make the code easier to navigate, avoid a god file, or set up a clean architecture.
- Reads credentials
16 other projects use a version of this.
Adopted by
Most recently taken up by
- NemesLaszlo/claude-code-harnesstheir own version, first seen Nov 8, 2026
- kellykampen/agent-skillstheir own version, first seen Nov 1, 2026
- vijaysuperpositionist/skillstheir own version, first seen Nov 1, 2026
- michaelshimeles/rstacktheir own version, first seen Nov 1, 2026
- Rajveerx11/skillstheir own version, first seen Oct 25, 2026
- michaelshimeles/skillstheir own version, first seen Oct 15, 2026
Other versions16 projects use a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 16
- michaelshimeles/rstack★ 1.4ksame name
- michaelshimeles/skills★ 1.4ksame name
- The-Utopia-Studio/skills★ 6same name
- Spardutti/claude-skills★ 3same name
- jaydubya818/MissionControl★ 2same name
- kellykampen/agent-skills★ 2same name
- nitikeshq/skills★ 2same name
- rikvanriel/code-hygiene★ 2same name
- voundbrand/overnight★ 2same name
- Nagacash/nagacodex-factory-skill★ 1same name
6 more not listed
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository68 other files it gives its agents.
- AGENTS.mdAGENTS.md
- .claude/CLAUDE.mdCLAUDE.md
- accessibilitySkill
- api-designSkill
- api-versioningSkill
- authSkill
- background-jobsSkill
- cachingSkill
- capacity-planningSkill
- changelogSkill
- chaos-engineeringSkill
- ci-cdSkill
- circuit-breakerSkill
- claude-best-practicesSkill
- code-commentsSkill
- code-hygieneSkill
- code-reviewSkill
- commit-styleSkill
- concurrency-and-lockingSkill
- database-transactionsSkill
- data-consistencySkill
- debug-infra-startupSkill
- debug-sessionSkill
- demo-video-creatorSkill
- demo-video-script-creatorSkill
- dependency-injectionSkill
- dependency-updateSkill
- disk-cleanupSkill
- distributed-tracingSkill
- dockerSkill
- env-configSkill
- env-setupSkill
- error-handlingSkill
- event-driven-architectureSkill
- feature-flagsSkill
- file-storageSkill
- graceful-shutdownSkill
- idempotencySkill
- input-validationSkill
- jars-engineering-blogsmithSkill
- jars-workshop-builderSkill
- licensingSkill
- loggingSkill
- memory-and-allocationsSkill
- migrate-dbSkill
- monitoringSkill
- multi-tenancySkill
- notificationsSkill
- openapiSkill
- paginationSkill
- perf-profileSkill
- pr-descriptionSkill
- query-optimizationSkill
- rate-limitingSkill
- refactorSkill
- releaseSkill
- runbook-writerSkill
- searchSkill
- secrets-auditSkill
- simplifySkill
- The first 60 shown.
The file
We split this skill into the rules it gives an agent
6 rules. 0 are common, 4 are distinctive, and 2 are very specific to this project.
0
Common
Most of the 16 edited versions we read keep these.
4
Distinctive
This author's own choices. Worth a look.
2
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 15
No file — including comments and blank lines — exceeds 696 lines.
- Very specific: line 18
When a file needs "and" in its description (user_auth_and_session.py), split it now.
- Distinctive: line 131
Every file starts with a module docstring that answers three questions: what it does, what it depends on, and who depends on it.
- Distinctive: line 151
Keep it to 10 lines max. It is a navigation aid, not documentation prose.
- Very specific: line 250
Never raise bare Exception.
- Distinctive: line 366
Run through this before every commit:
“Common” is measured: kept by at least 6 in 10 of the 16 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
code-structure
Apply this skill whenever starting a new project, adding a major feature, or cleaning up an existing codebase. Every rule here is a constraint, not a suggestion.
1. The 696-line ceiling
No file — including comments and blank lines — exceeds 696 lines.
- At ~500 lines, extract proactively. Do not wait until the ceiling forces a messy split.
- When a file needs "and" in its description (
user_auth_and_session.py), split it now. - Tests count separately. A 400-line module may have a 400-line test file. Both are fine.
When to split and where to put the pieces:
| File is getting large because of… | Extract into… |
| Multiple data models | models/<entity>.py per entity |
| Multiple service operations | services/<domain>_service.py per domain |
| Multiple route groups | routes/<resource>.py per resource |
| Utility functions | lib/<concern>.py (e.g. lib/pagination.py) |
| Config loading + validation | config/settings.py |
| DB query logic | repositories/<entity>_repository.py |
2. Folder scaffold — organise by responsibility, not file type
my_app/
├── main.py # entrypoint only — wires app together, no business logic
├── config/
│ └── settings.py # loads and validates all env vars / config
├── models/
│ ├── user.py # User dataclass / ORM model
│ └── order.py
├── schemas/
│ ├── user_schema.py # request/response validation (Pydantic, Marshmallow)
│ └── order_schema.py
├── repositories/
│ ├── user_repository.py # all DB queries for User — no business logic here
│ └── order_repository.py
├── services/
│ ├── user_service.py # business logic for users — calls repository, never DB directly
│ └── order_service.py
├── routes/
│ ├── user_routes.py # HTTP handlers for /users — calls service, never repository
│ └── order_routes.py
├── lib/
│ ├── logger.py # shared logger setup
│ ├── exceptions.py # custom exception hierarchy
│ └── pagination.py # shared utilities
└── tests/
├── services/
│ └── test_user_service.py
└── repositories/
└── test_user_repository.pyWhat each layer is allowed to call:
routes → services → repositories → database
→ lib/
→ models/- Routes never import from
repositoriesdirectly. - Services never import from
routes. - Repositories never contain business logic.
main.pywires everything together and nothing else. Ifmain.pyexceeds ~80 lines, move the wiring into anapp_factory.py.
3. Naming conventions — every case has one job
| Thing | Convention | Example |
| Variables | snake_case | user_id, order_total |
| Functions | snake_case | get_user_by_email, calculate_tax |
| Classes | PascalCase | UserService, OrderRepository |
| Constants | SCREAMING_SNAKE | MAX_RETRY_COUNT, DEFAULT_PAGE_SIZE |
| Module files | snake_case | user_service.py, order_repository.py |
| Packages / dirs | snake_case | services/, lib/ |
| Private helpers | _leading_underscore | _parse_address, _build_query |
Bad → good naming:
# ❌ meaningless
data = get()
res = process(x, obj)
temp = db.query(q)
# ✅ describes what it is and what it does
user_record = fetch_user_by_id(user_id)
invoice_total = calculate_invoice_total(line_items)
active_orders = query_orders_by_status(status="active")Function names must be verb phrases:
# ❌
def user(id): ...
def data(): ...
# ✅
def get_user_by_id(user_id: int): ...
def fetch_pending_orders(since: datetime): ...Boolean variables and functions use is_ / has_ / can_ / should_ prefixes:
is_admin = user.role == "admin"
has_active_sub = check_subscription_status(user_id)
can_retry = attempt_count < MAX_RETRY_COUNT4. File-level navigation header
Every file starts with a module docstring that answers three questions: what it does, what it depends on, and who depends on it.
"""
services/user_service.py
------------------------
Business logic for user management: registration, profile updates, and deactivation.
Depends on:
- repositories/user_repository.py (DB reads/writes)
- lib/exceptions.py (UserNotFoundError, DuplicateEmailError)
- models/user.py (User)
- config/settings.py (EMAIL_VERIFICATION_REQUIRED)
Used by:
- routes/user_routes.py
"""Keep it to 10 lines max. It is a navigation aid, not documentation prose.
5. Structured logger — wired once, used everywhere
Set up the logger once in lib/logger.py. Import it everywhere. Never use print.
# lib/logger.py
"""
lib/logger.py
-------------
Shared logger factory for the entire application.
Depends on: nothing (stdlib only)
Used by: every module that logs
"""
import logging
import sys
def get_logger(name: str) -> logging.Logger:
"""Return a named logger with structured formatting."""
logger = logging.getLogger(name)
if logger.handlers:
return logger # already configured — avoid duplicate handlers
handler = logging.StreamHandler(sys.stdout)
formatter = logging.Formatter(
fmt="%(asctime)s | %(levelname)-8s | %(name)s | %(message)s",
datefmt="%Y-%m-%dT%H:%M:%S",
)
handler.setFormatter(formatter)
logger.addHandler(handler)
logger.setLevel(logging.INFO)
return logger# Usage in any module
from lib.logger import get_logger
logger = get_logger(__name__) # __name__ = "services.user_service" — auto-namespaced6. Logger levels — one rule per level
| Level | When to use |
logger.debug | Detailed internals — disabled in production; use for tracing data flow |
logger.info | Normal milestones — request received, job started, record created |
logger.warning | Unexpected but recoverable — retry triggered, deprecated path hit, config fallback used |
logger.error | Operation failed, user impact possible — always include context and remediation hint |
logger.critical | System cannot continue — database unreachable, config missing |
Every logger.error and logger.warning must include:
- What operation failed
- The inputs / identifiers involved
- The exception message
- What will happen next (retry, fallback, propagate)
# ❌ useless
except Exception as e:
logger.error("Something went wrong")
raise
# ❌ too terse
except UserNotFoundError:
logger.warning("User not found")
# ✅ full context — tells the on-call engineer exactly what happened
except UserNotFoundError as exc:
logger.warning(
"User lookup returned no record — returning 404 to caller. "
"user_id=%s error=%s",
user_id, exc,
)
raise
except EmailProviderError as exc:
logger.error(
"Failed to send verification email — user registration incomplete. "
"user_id=%s email=%s provider=%s error=%s "
"action=check EMAIL_PROVIDER_API_KEY and provider status page",
user_id, email, settings.EMAIL_PROVIDER, exc,
)
raise RegistrationError("Email delivery failed") from exc7. Custom exception hierarchy
Never raise bare Exception. Define a hierarchy in lib/exceptions.py so callers
can catch at the right granularity.
# lib/exceptions.py
"""
lib/exceptions.py
-----------------
Application exception hierarchy. All custom exceptions inherit from AppError
so callers can catch broadly or narrowly as needed.
Depends on: nothing
Used by: all services, repositories, routes
"""
class AppError(Exception):
"""Base for all application exceptions."""
class NotFoundError(AppError):
"""A requested resource does not exist."""
class UserNotFoundError(NotFoundError):
"""Specific to user lookups."""
class ConflictError(AppError):
"""An operation conflicts with existing state."""
class DuplicateEmailError(ConflictError):
"""Email address already registered."""
class ExternalServiceError(AppError):
"""A third-party service call failed."""
class EmailProviderError(ExternalServiceError):
"""Email delivery provider returned an error."""8. Apply the rules to a real service file
# services/user_service.py
"""
services/user_service.py
------------------------
Business logic for user management: registration and profile retrieval.
Depends on:
- repositories/user_repository.py (fetch_user_by_id, create_user)
- lib/exceptions.py (UserNotFoundError, DuplicateEmailError)
- lib/logger.py (get_logger)
- models/user.py (User)
Used by:
- routes/user_routes.py
"""
from models.user import User
from repositories.user_repository import UserRepository
from lib.exceptions import DuplicateEmailError, UserNotFoundError
from lib.logger import get_logger
logger = get_logger(__name__)
# Injected at construction — makes the service testable without a real DB.
class UserService:
def __init__(self, user_repo: UserRepository) -> None:
self._user_repo = user_repo
def get_user(self, user_id: int) -> User:
"""Fetch a user by ID. Raises UserNotFoundError if not found."""
logger.info("Fetching user. user_id=%s", user_id)
user = self._user_repo.find_by_id(user_id)
if user is None:
logger.warning(
"User not found. user_id=%s action=returning UserNotFoundError", user_id
)
raise UserNotFoundError(f"No user with id={user_id}")
logger.info("User fetched successfully. user_id=%s", user_id)
return user
def register_user(self, email: str, display_name: str) -> User:
"""Create a new user. Raises DuplicateEmailError if email is taken."""
logger.info("Registering new user. email=%s", email)
existing = self._user_repo.find_by_email(email)
if existing is not None:
logger.warning(
"Registration rejected — email already in use. "
"email=%s existing_user_id=%s",
email, existing.id,
)
raise DuplicateEmailError(f"Email already registered: {email}")
new_user = self._user_repo.create(email=email, display_name=display_name)
logger.info(
"User registered successfully. user_id=%s email=%s",
new_user.id, email,
)
return new_user9. Checklist before committing any file
Run through this before every commit:
- File is under 696 lines (count with
wc -l <file>or editor line count) - Module docstring present: what it does, depends on, used by
- All variables and functions use
snake_case - All class names use
PascalCase - No variable named
data,result,temp,obj,res,x, orvalwithout a qualifier - Every function name is a verb phrase
- No
print()— onlylogger.* - Every
logger.error/logger.warningincludes: operation, identifiers, error, next action - No bare
except Exception: passorcatch(e) {} - Custom exceptions raised, not bare
ExceptionorError - File only imports from layers it is allowed to call (routes→services→repos→db)
Rules
- The 696-line limit is a hard ceiling, not a target. Aim for 300–400 lines per file.
- A file that needs a long name is already doing too much. Split it.
- Logger context is for engineers on call at 3 am. Write it like you will be that engineer.
- Folder structure is an API. Once established, moving files breaks imports across the team — establish it before writing business logic, not after.
- Apply these rules to new code first. Refactor existing code incrementally — one file per PR, tests passing at every step.
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.

