Use when extracting existing Rails app code into a reusable engine. Trigger words: extract to engine, move feature to engine, host coupling, adapters, extraction slices, preserve behavior, incremental extraction, bounded feature.
79
71%
Does it follow best practices?
Impact
91%
1.49xAverage score across 3 eval scenarios
Passed
No known issues
Optimize this skill with Tessl
npx tessl skill review --optimize ./rails-engine-extraction/SKILL.mdUse this skill when the task is to move existing code out of a Rails app and into an engine.
Prefer incremental extraction over big-bang rewrites. Preserve behavior first, then improve design.
DO NOT extract and change behavior in the same step. Extraction must preserve existing behavior; refactoring and improvements belong in a separate step after the move is complete and verified.
| Extraction Step | Action |
|---|---|
| Identify bounded feature | Choose one coherent responsibility to extract |
| List host dependencies | Document models, services, config the feature needs |
| Define engine boundary | Decide what lives in engine vs host |
| Move stable logic first | POROs, services, value objects before controllers |
| Add adapters | Replace direct host references with config or adapter interfaces |
| Move UI/routes last | Controllers, views, routes only after seams are clear |
| Keep tests green | Regression coverage throughout each slice |
| Mistake | Reality |
|---|---|
| Extracting too much at once | One bounded slice per step; large extractions hide bugs and are hard to revert |
| Direct host references in engine | Engine must use adapters or config; direct constants couple engine to host internals |
| No adapter layer | Without adapters, host model changes break the engine; introduce seams before moving |
Start with:
Delay these until later:
Replace hardcoded host dependencies with:
Do not move code into an engine if it still depends on many private host internals.
First slice (move PORO, no host model yet):
Extract Pricing::Calculator from app/services/pricing/calculator.rb into the engine. It only depends on LineItem and Discount — move those to the engine as engine models in the same slice, or keep them in the host and inject via an adapter in a later slice.
Adapter for host dependency:
# In engine: use config instead of hardcoded User
# Before (in app): OrderCreator.new(current_user).call
# After (in engine): OrderCreator.new(MyEngine.config.current_user_provider.call(request)).call
# Host sets in initializer: MyEngine.config.current_user_provider = ->(req) { req.env["current_user"] }Red flag: Extracting OrdersController in the first slice while it still calls User, Tenant, and AuditLog — too many host ties. Extract the service/PORO first and introduce adapters, then move the controller.
When asked to extract code:
| Skill | When to chain |
|---|---|
| rails-engine-author | Engine structure, host contract, namespace design after extraction |
| rails-engine-testing | Dummy app, regression tests, integration verification |
| refactor-safely | Behavior-preserving refactors before or after extraction slices |
ae8ea63
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.