There’s overlapping terminology that describes the act of packaging up some new behavior for Rails. I think of two gems I maintain that are of vastly different scales

  • activerecord-has_some_of_many which adds two new tiny association methods to Active Record models in 150 lines of code.
  • GoodJob, which is an entire Active Job backend with a mountable Web Dashboard and database models and custom job extensions in 10k lines of code.

I was pondering the different terminology because I recently saw both ends of the spectrum discussed in the community:

  • A developer on Reddit announced a tiny new gem and a commenter wrote well actually, in your Readme you called it an Engine but you shouldn’t do that.
  • I got pinged on a Rails issue that left me with the belief that some behavior, if not packaged as an Engine, could be expected to break.

I think there are only two dimensions to consider when picking the correct terminology:

  • How the behavior is packaged
  • Whether it’s necessary to package the behavior that way. Which isn’t even a criticism in my opinion, just an observation.

Here’s my opinionated list, in order of somewhat increasing complexity:

  • Rails Extension: A small monkeypatch or tiny new behavior to existing Rails behaviors (Active Record, Active Job, etc.). Especially if it’s not even a gem: simply a file you wrote a blog post about that gets copied into config/extensions and then require_relative’d in config/application.rb.
  • Rails Gem : Reductively, a gem is a load path for some code, and some ownership metadata, and maybe it’s been published to Rubygems.org. Nothing special.
  • ⭐️ Rails Plugin. A generic name covers all situations imo, regardless of size, scope, or complexity.
  • Railtie: When you write a gem that plugs into the Rails framework, you create special file named lib/railtie.rb that has a class that inherits from `Rails::Railtie that contains a DSL to configure how your gem’s behavior interfaces with Rails (configuration, initialization, etc.). I think Railtie is a bit of an odd-duck terminology-wise, but it makes sense considering…
  • Rails Engine: An “Engine” is nearly identical to a Railtie, but the file is named lib/engine.rb and it has a class that inherits from Rails::Engine. But Rails::Engine itself inherits from Rails::Railtie, so this is a matter of degrees. Your gem absolutely needs to use the Engine behavior if it wants to create mountable routes (though I guess you can mount a vanilla Rack app) or inherit from Rails Base classes like ActiveRecord::Base, ActionController::Base, ActiveJob::Base, etc. which live in the Engine’s own app/ directory.

(I’ll clock that the The Rails Guides, under the “Extending Rails” section, has separate guides for Plugins and Engines; the former somewhat surprisngly does not mention the latter.)

So if I go back to the two reasons why I wrote this, and try to be strict with this terminology:

  • If your Plugin has an engine.rb file, it is an Engine. Simple as that. If you don’t need the Engine-specific behavior, you could package it as a Railtie, but I think the difference is negligible.
  • If you don’t have any dependencies on Rails (outside of maybe ActiveSupport) and don’t need to hook into the parent application’s configuration or initialization or framework, then you don’t need a Railtie or Engine at all. Just say it’s a gem that’s compatible with Rails and explain how to use it in that environment.
  • Really, do what you want and tell people about it.