Skip to content
Development
Skill

/split-monolith

Safe procedure for decomposing a god file (400+ LOC) into a sub-package without breaking any imports. Load when a file exceeds 400 lines or mixes multiple concerns. Implements vibecodex Principles A1 and A8 — folder-instead-of-file with backward-compatible re-exports.

From plugin
vibe-coding-rules
468 skills
Install
$ npx -y skills add yerdaulet-damir/vibe-coding-rules --skill split-monolith --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/split-monolith

Context preview

The summary Claude sees to decide when to auto-load this skill.

Safe procedure for decomposing a god file (400+ LOC) into a sub-package without breaking any imports. Load when a file exceeds 400 lines or mixes multiple concerns. Implements vibecodex Principles A1 and A8 — folder-instead-of-file with backward-compatible re-exports.

SKILL.md

split-monolith.SKILL.md
name: split-monolith
description: Safe procedure for decomposing a god file (400+ LOC) into a sub-package without breaking any imports. Load when a file exceeds 400 lines or mixes multiple concerns. Implements vibecodex Principles A1 and A8 — folder-instead-of-file with backward-compatible re-exports.

split-monolith

A file split done wrong breaks every caller. Follow this procedure exactly — it is reversible at every step.

---

Step 0 — Confirm the file needs splitting

wc -l app/services/<file>.py

| Lines | Action | |-------|--------| | < 400 | Do not split — you're solving a non-problem | | 400–600 | Plan the split now, execute when convenient | | > 600 | Split immediately (Principle A7 hard cap) |

Also ask: does this file mix multiple concerns? A file that is long but cohesive is better than a premature split.

---

Step 1 — Identify the domain splits

Do NOT split by size. Split by **type of responsibility**.

Good splits (by domain):

wallet_service.py (1200 LOC) →
  wallet/user.py      ← user-facing operations (charge, refund)
  wallet/admin.py     ← admin operations (top-up, override)
  wallet/history.py   ← read-only queries

Good splits (by layer):

generation_service.py (1000 LOC) →
  generation/orchestrator.py   ← coordinates the flow
  generation/cost.py           ← cost calculation logic
  generation/storage.py        ← result persistence

Bad splits (by size only — don't do this):

big_service.py →
  big_service_part1.py   ← meaningless
  big_service_part2.py   ← meaningless

Write the target structure before touching any file.

---

Step 2 — Create the package directory

mkdir app/services/<domain>/

Do NOT move any code yet.

---

Step 3 — Create sub-files one at a time

For each sub-file, copy (not move) the relevant functions:

# Create the new file with the relevant subset
touch app/services/<domain>/user.py
# Copy relevant classes/functions from the original

Each sub-file must:

  • Have its own imports (do not rely on `*` imports)
  • Be under 200 LOC (you're splitting — keep it lean)
  • Contain one cohesive responsibility

---

Step 4 — Create `__init__.py` with ALL old names re-exported

This is the most important step. Every name that existed in the original file must still be importable from the same path.

# app/services/<domain>/__init__.py

# Principle A8: re-export everything so callers don't change.
from app.services.<domain>.user import CreditsUserService
from app.services.<domain>.admin import CreditsAdminService
from app.services.<domain>.user import get_credits_user_service

# Backward-compat alias if the old class had a different name
CreditsService = CreditsUserService  # old name → new class

__all__ = [
    "CreditsUserService",
    "CreditsAdminService",
    "CreditsService",           # backward compat
    "get_credits_user_service",
]

---

Step 5 — Verify no import breaks

# Check every file that imported from the old module still works
python3 -c "from app.services.<domain> import <OldClassName>"
python3 -c "from app.services.<domain> import <AnotherClass>"

# Run the full test suite
pytest tests/ -x -q

All tests must be GREEN before deleting the original file.

---

Step 6 — Delete the original file

Only after Step 5 passes:

rm app/services/<original_file>.py

Run tests again:

pytest tests/ -x -q
bash scripts/lint-architecture.sh

Both must pass.

---

Common mistakes

| Mistake | Consequence | Prevention | |---------|------------|------------| | Split before writing `__init__.py` | Import errors everywhere | Always create `__init__.py` first | | Split by size, not responsibility | Sub-files still coupled | Ask: "what is the single job of this file?" | | Forget to re-export old names | Callers break silently | List every public name before splitting | | Move code instead of copy+verify | Can't roll back | Copy first, delete only after tests pass | | Split and refactor at same time | Impossible to debug | One PR = one split. No logic changes. |

---

Verification

The split was done correctly when:

  • [ ] All sub-files are under 200 LOC
  • [ ] `__init__.py` re-exports every name that existed before
  • [ ] `python3 -c "from app.services.<domain> import <OldName>"` works
  • [ ] `pytest tests/ -x -q` is green
  • [ ] `bash scripts/lint-architecture.sh` exits 0
  • [ ] Original file is deleted
Read more
Ships withvibe-coding-rules

54 production architecture principles your AI coding agent (Claude Code, Cursor) follows automatically. Drop-in CLAUDE.md, .cursor/rules/, and .claude/skills/ for FastAPI, Next.js 15, and Go 1.22+. MIT.

Get the whole plugin
Stats
26
Stars
0
Forks
Maintained
Maintenance
TypeScript
Language
MIT
License
4mo ago
Last commit
4mo ago
Created

Repo: yerdaulet-damir/vibe-coding-rules

Other skills on vibe-coding-rules.