| name | file-refactor |
| description | Automatically optimize and split large files that exceed 600 lines. Use when a file is too large (over 600 lines), when you need to decouple a monolithic module, when a .tsx/.ts or .py file has mixed concerns (types, utils, hooks/components, CLI handlers, formatters), or when splitting a screen component or service module into smaller files. Applies to React/TypeScript frontends and Python backend modules. |
File Refactor Skill
This skill optimizes file sizes by extracting types, utilities, hooks/components, formatters, and constants into separate focused modules. Default target: keep files under 600 lines unless the repo documents a different limit.
When to Trigger
- File exceeds 600 lines (or the repo's documented limit)
- Module has mixed concerns (types, utils, handlers, rendering, constants in one file)
- Screen/component has inline sub-components that could be extracted
- Python service has long pure helpers, dataclasses, or CLI subcommands mixed with orchestration
- File has long utility functions that could be separated
- You need to decouple a monolithic file
Language Routing
| Stack | Typical targets |
|---|
| TypeScript / React | types.ts, utils.ts, hooks/, components/, constants.ts |
| Python | models.py, *_format.py, *_sections.py, utils.py, constants.py, thin __init__.py re-exports |
Pick the stack that matches the file you are refactoring. Do not invent a new layout if the surrounding package already has a pattern — mirror neighbors first.
TypeScript / React Splitting Strategy
1. Types First
Extract all TypeScript interfaces, types, and enums to types.ts:
export interface MyProps { ... }
export type MyType = "a" | "b";
export const MY_CONSTANT = ...;
2. Pure Utility Functions
Extract formatting, grouping, and data transformation functions to utils.ts:
export function formatDate(date: Date): string { ... }
export function groupItems(items: Item[]): GroupedItems { ... }
3. Complex State Logic → Hooks
Extract complex useState/useCallback logic to hooks/useXxx.ts:
export function useMyFeature() {
const [state, setState] = useState(...);
return { state, actions };
}
4. Sub-Components → components/
Extract UI sub-components to components/SubComponent.tsx:
interface SubComponentProps { ... }
export function SubComponent({ ... }: SubComponentProps) { ... }
5. Inline Constants/Config
Extract hardcoded arrays and config objects to constants.ts:
export const OPTIONS = [
{ value: "a", label: "A" },
{ value: "b", label: "B" },
];
Python Splitting Strategy
1. Data Models / Types → models.py
Extract @dataclass, TypedDict, Protocol, Literal aliases, and small immutable value objects:
from __future__ import annotations
from dataclasses import dataclass
@dataclass(frozen=True)
class Order:
id: str
total: float
Keep orchestration and I/O out of models.py.
2. Pure Helpers → sibling module
Extract formatting, parsing, grouping, and transformation with no side effects:
- Flat package:
utils.py or domain-specific *_format.py
- Large renderer: split
report.py → report_format.py + report_sections.py
def format_pct(value: float | None) -> str:
...
Import types from models.py; avoid circular imports (extract shared types upward if needed).
3. Orchestration Stays Thin
The original file should remain the coordinator — CLI entry, service facade, or pipeline driver. Move branches into focused modules:
myapp/reports/
├── __init__.py # public re-exports only
├── models.py # data types
├── loader.py # file I/O
├── runner.py # orchestration
├── report.py # top-level render entry
├── report_format.py # string/format helpers
└── report_sections.py # section builders
4. CLI / Command Groups
When a CLI module grows, extract subcommand handlers to dedicated modules and keep main wiring thin:
myapp/cli/
├── main.py # argparse / click wiring only
├── repl.py # REPL loop
├── repl_session.py # session state/helpers extracted from repl
└── stream.py # streaming path extracted from chat handler
5. Constants / Prompts / Config
Extract module-level string templates, frozensets, and default dicts:
DEFAULT_TIMEOUT = 30
ALLOWED_COMMANDS = frozenset({"read", "write", "list"})
Or keep prompts near the orchestrator if only one consumer exists — do not over-split one-liners.
6. Preserve Public API
- Re-export stable names from
__init__.py when callers import the package root.
- Prefer the project's existing import style (absolute vs relative).
- Respect module boundaries and layering rules defined in the repo (architecture docs, lint rules, import tests).
7. Python Conventions
- Start new modules with
from __future__ import annotations when the repo already does.
- Match existing naming:
snake_case files/functions, PascalCase classes.
- Lazy imports inside functions are fine to break cycles.
- Update or add the smallest targeted test when behavior moves across modules.
Step-by-Step Process
Step 1: Analyze the File
Read the target file and identify:
- Types —
type/interface/enum (TS) or dataclasses / TypedDict / Protocol (Python)
- Pure Functions — no component state, no IPC, no DB/network side effects
- Stateful UI logic — React hooks (TS) or long async orchestration blocks (Python)
- Sub-units — JSX sub-components (TS) or CLI subcommands / report sections / handlers (Python)
- Constants — inline arrays, objects, prompts, config
Step 2: Create Supporting Files
Mirror the package's existing layout. Create only directories/files you actually need.
mkdir -p components/ hooks/
Step 3: Extract and Create
Move code in dependency order: types/models → pure helpers → sections/hooks → orchestrator trim.
Step 4: Update Original File
- Add imports for extracted modules
- Remove extracted code from the original file
- Keep exports stable for existing callers (
__init__.py / barrel files / tests)
Step 5: Verify
TypeScript / React
- Run the repo's typecheck command (e.g.
npm run typecheck, tsc --noEmit)
- Verify imports/exports and UI behavior
Python
- Run targeted tests for the affected area
- Run any import-boundary or architecture guard tests the repo provides
- Confirm public imports still resolve from package roots
Example Layouts
TypeScript screen module
MyScreen/
├── MyScreen.tsx
├── MyScreen.css
├── types.ts
├── utils.ts
├── hooks/
│ └── useMyFeature.ts
└── components/
└── SubPanel.tsx
Python service package
myapp/billing/
├── __init__.py
├── models.py
├── calculator.py
├── invoice.py
├── invoice_format.py
└── invoice_sections.py
Output Checklist
After refactoring, verify:
Example Extraction
TypeScript — 1000-line MyScreen.tsx
interface Props { ... }
const CONSTANT = [...];
function formatData() { ... }
function useMyHook() { useState... }
function SubComponent() { ... }
export function MyScreen() { ... }
Python — 900-line invoice.py
@dataclass
class LineItem: ...
def format_currency(x): ...
def summary_section(data): ...
def render_invoice(data): ...
Key Principles
- Single Responsibility — Each file does one thing well
- Cohesion — Related code stays together
- Importability — Extracted modules can be imported independently
- Testability — Smaller units are easier to test
- Preserve Exports — Don't break existing imports from other files
- Mirror the neighborhood — Follow patterns already used in the same package