| name | maintainable |
| description | Enforce single-responsibility, LOC limits, and decomposition standards across functions, components, and files. Generates/updates docs/architecture.md to map how pieces connect. Use when the user says 'check maintainability', 'is this too big', 'decompose this', 'break this down', 'refactor for maintainability', or before any commit-push / prepush-check workflow. Also trigger when reviewing large files, refactoring components, or when the user mentions code smell, single responsibility, or component size. |
maintainable
Enforce maintainable code architecture: small functions, focused components, manageable files. Each unit solves one problem. Every piece is reusable or adaptable. The codebase stays navigable for both humans and agents.
Invocation Modes
This skill operates in two modes depending on context:
Standalone mode
Analyze the target files/directories passed by the user. Report violations, suggest decomposition, and offer to refactor. Update docs/architecture.md.
Prepush-check mode
Invoked automatically by /prepush-check. Analyze only changed/new files. Report violations (Yellow = WARN, Red = FAIL). Flag connections to other components. Update docs/architecture.md if the architecture changed.
LOC Thresholds
| Scope | Green (Good) | Yellow (Watch) | Red (Refactor) |
|---|
| Function | < 20 LOC | 20–100 LOC | > 100 LOC |
| Component | < 200 LOC | 200–500 LOC | > 500–600 LOC |
| File | < 400 LOC | 400–1000 LOC | > 1000–2000 LOC |
±20% tolerance: All thresholds above have a ±20% tolerance band. A unit within 20% above a boundary is treated as the lower severity (e.g., a 22 LOC function stays Green, a 110 LOC function stays Yellow). A unit within 20% below a boundary gets an early warning at the higher severity (e.g., a 17 LOC function that is growing gets a soft Yellow hint). Apply the tolerance to every boundary in the table — Green/Yellow, Yellow/Red.
Counting rules:
- Count only executable lines (exclude blank lines, import statements, type definitions, and comments)
- For components: count the entire component function body including JSX
- For files: count everything except blank lines
- Nested functions count toward both their own threshold AND the parent's
Framework Detection
Detect the project framework to apply appropriate decomposition patterns:
| Framework | Detection | Component patterns |
|---|
| React / Next.js | next.config.*, react in deps, src/app/ or app/ | Split into subcomponents, custom hooks, compound components |
| Vue | vue in deps, .vue files | Extract composables, split into child components |
| Svelte | svelte in deps, .svelte files | Extract stores, split into child components |
| Angular | @angular/core in deps, .component.ts | Extract services, split into child components |
| Node/Express | express in deps, route files | Extract middleware, service layers, utility modules |
| Python | *.py files, requirements.txt or pyproject.toml | Extract helper functions, class decomposition, module splitting |
| Go | go.mod, *.go files | Extract into packages, interface decomposition |
If no framework detected, apply language-agnostic thresholds and decomposition patterns.
Workflow
Step 1: Identify target files
Standalone mode:
Use whatever the user points at. If no target specified, ask.
Prepush-check mode:
Run git status --porcelain + git diff --name-only HEAD to get changed/new files. Filter to code files only (exclude assets, configs, lockfiles, generated files).
Step 2: Detect framework
Read package.json, go.mod, pyproject.toml, or equivalent. Identify the framework per the detection table above. This informs the decomposition suggestions in Step 5.
Step 3: Analyze each file
For each target file, measure:
- File LOC — total non-blank lines
- Functions — identify each function/method, count its LOC
- Components — identify each component (React functional components, Vue SFCs, etc.), count its LOC
- Responsibility count — how many distinct concerns does each unit handle? Look for:
- Multiple API calls in one function
- Mixed data fetching + rendering logic
- Multiple unrelated state variables in one component
- God functions that orchestrate too many steps
- Utility files that mix unrelated helpers
Step 4: Classify violations
For each unit (function, component, file), assign a status:
- Green — within thresholds, single responsibility. No action needed.
- Yellow — approaching limits or mild SRP violations. Report as warning.
- Red — exceeds thresholds or clear SRP violations. Report as failure requiring refactor.
Step 5: Generate decomposition plan
For each Yellow or Red violation, produce a specific decomposition recommendation:
For oversized functions:
- Identify the distinct steps/concerns
- Suggest extraction into named helper functions
- Name the helpers based on what they do (the name IS the documentation)
For oversized components (framework-aware):
- React/Next.js: Extract subcomponents, custom hooks (
useX), separate data-fetching logic
- Vue: Extract composables, child components, computed properties
- General: Split into container/presenter, extract shared logic
For oversized files:
- Group related functions/components that belong together
- Suggest new file names based on responsibility
- Identify what should be co-located vs. separated
For SRP violations:
- Name the distinct responsibilities
- Suggest which responsibility stays and which moves
- Identify the interface between them (props, params, return values)
Step 6: Execute or report
Standalone mode:
Present the decomposition plan. Ask the user: "Should I execute this refactoring?" If yes, perform the decomposition. After refactoring, re-run analysis to confirm all units are Green or Yellow.
Prepush-check mode:
Report violations in this format:
## Maintainability Check: [PASS | WARNINGS | FAIL]
### Red (must fix)
- src/components/Dashboard.tsx: 642 LOC component (limit: 500)
→ Split into: DashboardHeader, DashboardMetrics, DashboardChart
→ Extract: useDashboardData hook for API calls
### Yellow (watch)
- src/lib/utils.ts: formatDate() is 45 LOC (watch threshold: 20)
→ Consider: extract date parsing vs. date formatting
### Connections
- Dashboard.tsx imports from: utils.ts, api/metrics.ts, hooks/useAuth.ts
- Breaking it up would create: components/dashboard/ directory
Decision rules for prepush-check:
- Any Red violation → FAIL
- Only Yellow violations → WARN
- All Green → PASS
Step 7: Update architecture map
After analysis (and after any refactoring), update or create docs/architecture.md in the project root.
Architecture Map (docs/architecture.md)
This file serves as a living map for both humans and agents to understand how the codebase connects. Update it every time this skill runs.
Structure
# Architecture
> Auto-generated by /maintainable. Last updated: [date]
## Overview
[1-2 sentence description of what this project does and its primary framework]
## Directory Structure
[Tree showing the main directories and their purpose — not every file, just the organizational structure]
## Module Map
### [Feature/Domain Name]
- **Entry point**: `path/to/main-file`
- **Components**: list of components and their single responsibility
- **Hooks/Utilities**: shared logic extracted for reuse
- **Data flow**: how data moves through this feature
### [Another Feature/Domain]
...
## Shared Infrastructure
- **Utilities**: `src/lib/` — what each utility module provides
- **Hooks**: `src/hooks/` — reusable hooks and their purpose
- **Types**: `src/types/` — shared type definitions
## Dependency Graph
[Which features depend on which shared modules. Keep this as a simple list, not a visual diagram]
## Decomposition Log
[Track recent decompositions so agents know the history]
- [date]: Split `Dashboard.tsx` → `DashboardHeader`, `DashboardMetrics`, `DashboardChart`
- [date]: Extracted `useAuth` hook from `LoginForm.tsx`
Update rules
- First run: Generate the full architecture map by scanning the project
- Subsequent runs: Only update sections affected by the current analysis. Don't regenerate the whole file.
- After refactoring: Add entries to the Decomposition Log
- Module Map: Only document modules that have been analyzed or refactored — don't try to document the entire codebase on first run unless in standalone mode targeting the whole project
- Keep it concise: This is a map, not documentation. One line per component/function. The code itself is the documentation.
Single Responsibility Heuristics
Use these to identify SRP violations beyond just LOC:
- The name test: If you can't name the function/component without using "and" or "or", it does too much.
- The change test: If a unit would need to change for more than one reason, it has multiple responsibilities.
- The reuse test: If you'd need to copy-paste part of a function to reuse it elsewhere, that part should be extracted.
- The test test: If testing a unit requires mocking more than 2-3 dependencies, it's likely doing too much.
- The scroll test: If you can't see the entire function on one screen (~40 lines), consider splitting.
Edge Cases
- Generated files (
.generated.ts, *.g.ts, proto output): Skip entirely. Don't count, don't flag.
- Test files: Apply relaxed thresholds (2x the normal limits). Test setup can legitimately be verbose.
- Type definition files (
.d.ts, types.ts): Skip LOC counting. Type files can be long without being unmaintainable.
- Index/barrel files: Skip. These are structural, not logic.
- Migration files: Skip. These are append-only by nature.
- Configuration files: Skip. Webpack/Next configs can be long without being a problem.
- Single large JSX return: If a component is Yellow purely because of JSX (not logic), suggest extracting JSX sections into subcomponents but classify as Yellow, not Red.
- Monorepo: Analyze only the package/app that contains the changed files. Don't scan sibling packages.
- No
docs/ directory: Create it. Create docs/architecture.md fresh.
- Existing
docs/architecture.md not generated by this skill: Preserve it. Append a ## Maintainability Map section rather than overwriting.
What this skill does NOT do
- Format code (that's prettier/eslint)
- Check types (that's TypeScript/build)
- Review business logic correctness (that's
/codex)
- Optimize performance (that's
/vercel-react-best-practices)
- Run tests (that's
/health)