name: package-extraction-strategy description: Guidance for deciding when to extract shared monorepo packages and how to structure code for future extraction. Use when evaluating whether logic should move to a shared package or when designing new features that may have multiple consumers.
Package Extraction Strategy
Core principle: Don't extract until a second consumer exists. But always structure for extraction.
Decision Gate
All four conditions must be met before extracting:
- Second consumer exists — another app needs this logic today, not hypothetically
- Interface is stable — proven through real usage in first consumer
- Zero framework coupling — no NestJS, Prisma, React, TanStack imports
- Clear ownership — someone maintains, bumps versions, reviews PRs
If any fails → use lib/ extraction-readiness pattern instead.
lib/ Convention
Isolate pure, framework-free logic into lib/ subdirectory:
src/models/{feature}/
├── {feature}.service.ts # Framework-coupled
├── {feature}.repository.ts # Prisma-coupled
└── lib/ # PORTABLE: pure functions only
├── compute-something.ts
└── validate-rules.ts
lib/ Rules
- No framework imports (NestJS, Prisma, React, TanStack)
- Plain inputs, plain outputs (objects/arrays/primitives)
- No side effects (no DB, HTTP, filesystem)
- Independently testable (no mocking needed)
- Self-contained (import from
lib/siblings only, never../service)
Extraction Procedure
- Create package under
packages/ - Move
lib/files to new package'ssrc/ - Add package exports in
package.json - Replace imports in first consumer
- Add as dependency in second consumer
- Full verification:
lint,typecheck,build,testfor both + package
Anti-Patterns
| Anti-Pattern | What to Do Instead |
|---|---|
| Extract on first use | Use lib/; extract when second consumer arrives |
| Extract with framework deps | Refactor to pure functions first |
| Copy-paste between apps | Evaluate extraction |
| Package with one consumer | Revert to lib/ |
Checklist
- Second consumer exists? If no → keep in
lib/ -
lib/files free of framework imports? - Functions take/return plain objects?
- Testable without mocking?
- Extraction meets all four decision gate conditions?