agentsclimarketplace

Split monolith

Skill yerdaulet-damir/vibe-coding-rules/.claude/skills/split-monolith

54 production architecture rules for vibe coding with Claude Code & Cursor. Drop-in CLAUDE.md, .cursor/rules, and .claude/skills for FastAPI, Next.js 15, and Go 1.22+ — turn AI-assisted coding from prototype hack to production.

Install
npx -y skills add yerdaulet-damir/vibe-coding-rules --skill split-monolith

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 8 stars8 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

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

4.4 KB, as published. Nobody here has run it

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
LinesAction
< 400Do not split — you're solving a non-problem
400–600Plan the split now, execute when convenient
> 600Split 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

MistakeConsequencePrevention
Split before writing __init__.pyImport errors everywhereAlways create __init__.py first
Split by size, not responsibilitySub-files still coupledAsk: "what is the single job of this file?"
Forget to re-export old namesCallers break silentlyList every public name before splitting
Move code instead of copy+verifyCan't roll backCopy first, delete only after tests pass
Split and refactor at same timeImpossible to debugOne 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

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.