When Rails Engines Are the Wrong Tool | Davidslv
Skip to content
The Engineer's Notebook
Why Architecture Matters in Rails Applications<br>How to Identify Boundaries in a Rails Monolith<br>The Modular Monolith as the Default Starting Point<br>Rails Engines vs Packwerk: When to Use What<br>When Rails Engines Are the Wrong Tool<br>Testing Strategy for a Modular Rails Application<br>From One Controller to Thirteen Handlers: A Webhook Refactor
← All writing
On this page
I have spent an entire book making the case for Rails engines. Now I am going to tell you when not to use them.
This is not a hedge. It is honesty. Every architectural tool has a cost, and engines are no exception. Using them when they are not warranted creates overhead that slows your team down rather than speeding them up. Knowing when not to reach for an engine is just as important as knowing how to build one.
The Decision Flowchart
Before introducing an engine, run through this:
flowchart TD<br>A["Do you have more than10-15 models?"] -->|No| B["Engines are overkill.Use namespaces."]<br>A -->|Yes| C["Do you have more thanone team or domain area?"]
C -->|No| D["Consider namespacesand conventions first."]<br>C -->|Yes| E["Do you have cleardomain boundaries?"]
E -->|No| F["Identify boundaries first.Engines can wait."]<br>E -->|Yes| G["Will the engine haveits own models andbusiness logic?"]
G -->|No| H["A plain Ruby gemor concern may suffice."]<br>G -->|Yes| I["An engine islikely the right choice."]
style A fill:#e8a838,stroke:#b07828,color:#fff<br>style B fill:#d9654a,stroke:#8a3a2c,color:#fff<br>style C fill:#e8a838,stroke:#b07828,color:#fff<br>style D fill:#4a90d9,stroke:#2c5f8a,color:#fff<br>style E fill:#e8a838,stroke:#b07828,color:#fff<br>style F fill:#4a90d9,stroke:#2c5f8a,color:#fff<br>style G fill:#e8a838,stroke:#b07828,color:#fff<br>style H fill:#4a90d9,stroke:#2c5f8a,color:#fff<br>style I fill:#27ae60,stroke:#1e8449,color:#fff
Notice how many paths lead away from “use an engine.” That is intentional. Engines should be the answer to a specific problem, not the default structure for every Rails application.
Applications That Are Too Small
If your application has fewer than 10-15 models, engines almost certainly add more overhead than value. The ceremony of gemspecs, dummy apps, mountable routes, and inter-engine dependency management is not justified when the entire codebase fits comfortably in one developer’s head.
For small applications, namespaces give you most of the organisational benefit at zero cost:
# app/models/billing/invoice.rb<br>module Billing<br>class Invoice ApplicationRecord<br># All the billing logic, clearly namespaced<br>end<br>end
# app/models/notifications/mailer.rb<br>module Notifications<br>class Mailer ApplicationRecord<br># All the notification logic, clearly namespaced<br>end<br>end
This communicates domain boundaries to developers without introducing any infrastructure. The Billing:: prefix tells you where this class belongs. The directory structure mirrors the namespace. It is not enforced, but it is clear.
Teams That Are Too Small
A team of two or three Software Engineers working on a single application does not need engine boundaries. The communication overhead of a small team is low enough that conventions and code review are sufficient to maintain boundaries.
Engines shine when teams are large enough that not everyone can hold the full codebase in their head. If every developer on your team already knows every model, every controller, and every service object, the boundary enforcement that engines provide is solving a problem you do not have.
The threshold is not precise, but in my experience, engines start paying for themselves when you have 5+ developers working on a codebase with 30+ models across at least 2-3 distinct domain areas.
The Honest Calculation
Before introducing engines, ask three questions:
What is the actual cost of the problem we are solving? Not the theoretical cost. The actual cost. How many hours per month does your team lose to cross-domain coupling? How many production incidents were caused by unexpected dependencies? If you cannot point to specific, recent pain, the problem may not justify the solution.
What is the ongoing cost of the engine infrastructure? Each engine needs its own gemspec, its own test setup, its own factories, its own migration strategy. Someone has to maintain that infrastructure. That someone is usually the most senior developer on the team, which means your most expensive resource is spending time on plumbing.
Is there a cheaper solution that gets us 80% of the benefit? Namespaces, conventions, Packwerk, or even just better code review might address the boundary problem without the full weight of engines.
The Premature Boundary Trap
The most common mistake is drawing boundaries before you understand the domain. You create an engines/billing and an engines/shipping on day one, then discover three months later that billing and shipping share a concept – “order line items” – that does not fit neatly into...