Same here. Especially in existing large Rails applications, engines present a might steeper learning and introduction curve compared to regular packages
IIRC the reasoning for using engines went something like this:
• lets us set per-package load paths via a rails-native mechanism, so we don’t have to build our own (Shopify had custom scripts for this before moving to engines)
• Opens up a path forward towards more per-component (!) Rails things (translations, gemfile, routes)
• Opens up a path for extraction of components into separate applications, or moving them around between applications
Important to note:
• Shopify added engines later in the process, everything was set up by hand before
• Shopify, at least at first, only used engines for components, not for all packages. Components are essentially very large packages that cover entire domains (in the DDD sense). Packages that are not engines were mostly nested in components and didn’t have their own load paths at all. I think this changed later, but I’m not quite sure what the current state is.
Ah I see where the disconnection might be. Vanilla packwerk is definitely not enough to replace engines.
We use packwerk with packs-rails to make packages “behave” like engines without the extra overhead.
Maybe <@U717FXJ8HWK> and <@U71V6G2PDJS> also meant the same, implicitly
• Would a new package be initialized via bin/rails plugin new ...?
for now that’s the tooling, yes. That said…
• By leveraging Rails engines are you implicitly signing yourself up for per-package dependency (gem) management?
…@AlexEvanczuk and I talked to Rafael Franca quite a bit during RailsConf and he mentioned ideas of making gems more lightweight so they wouldn’t necessarily come with all the overhead they come with today.
• What do folks see as a tripwire for when you should reach for an engine/when you should reconsider an engine?
The only one I can (given that we use packs-rails exentsively) see is <@U70TIGAX94P>’s last point: “Opens up a path for extraction of components into separate applications, or moving them around between applications”
However, even that one… if you have burned down all the todos and worked to separate the entangled things that are not covered by the tooling ecosystem today… the extraction of such a package should be extremely similar to the extraction of an engine.
My litmus test is always how close I am to the following process: “Duplicate the codebase. Delete all packages I don’t want. Remove all no-obsolete initializer code, config code, and dependencies. Profit.”
When gems have a lot of dependencies and / or are engines then it is easy to create a bunch of problems or extra work for yourself:
• if tests are run in the gem’s env:
◦ dependency version drift - unless you’re investing in a test matrix and ensure that tests are run against version of deps in the consuming app, you can’t be sure that the integration of the two works
◦ config drift - monkey-patching and other evils can change how the code behaves in test vs prod
• if tests are not run in the gem’s env:
◦ external dependency definition is simply extra work with no benefit
◦ all sorts of (unused) test harness code can and should be deleted