What are the differences between the default loader and the zeitwerk loader?

Hey,
I am not a zeitwerk user yet, but I am very interested about the pros and cons regarding the dry system default loader and the zeitwerk plugin?

I don’t quite understand why I should need auto loading (eager and lazy) when I have auto registration available (eager and lazy).
Thanks

I don’t use any of Zeitwerk’s eager loading directly, myself. I let Zeitwork load what I need using it’s standard block syntax. I’ve written an article that details this in case it’s of interest. I’m not sure it answers your direct question but might provide additional insight.

1 Like

The original loader was a simple mechanism to perform require for you automatically.

Zeitwerk adds some additional, intelligent behavior that you may want.

  1. module namespacing

Given two different forms of nested class names

module Foo
  module Bar
    class Baz
    end
  end
end

class Foo::Bar::Baz
end

You may want to use the second, terser syntax for convenience.

Zeitwerk will traverse this namespace and generate Foo and Bar for you, if they do not yet exist. This avoids common pitfalls when using one-line syntax that can result in buggy constant resolution. This behavior alone makes zeitwerk worth having.

  1. constant reloading

Since the original Loader utilized require, code changes that happen in development after the container key is resolved would not be loaded. The Zeitwerk loader makes this possible, so that you can have the same reloading ability you have in Rails.

  1. direct constant reference

This can be a double-edged sword, Rails relies too heavily on globals and constants, but Zeitwerk enables you to reference your autoloaded constants directly without requiring them first. The original loader only worked when you resolved a container key, but sometimes referencing a constant is simpler.

For example, you may need to reference an Entity or a ROM Command etc without relying on a specific instance.

2 Likes

Great question, thanks for bringing it here, @wuarmin!

@alassek provides a wonderful answer here that also goes into detail on some of the extra features that Zeitwerk offers.

If I could make my own contribution here, I’d say the difference between the default (original) loader and the autoloading loader (i.e. the one that works with Zeitwerk), it would be this:

  • With the default loader, every time you want to reference another constant within your application, you must require the file that contains that constant. In this way, the default loader will also use require to load any files for components for your container.
  • With the autoloading loader (and a matching Zeitwerk setup, which you can now get via the use :zeitwerk plugin!), you no longer need to write manual requires. In the same way, the autoloading loader will not use require when loading the components in your container; instead, it simply directly references the class constant to load the autoloader do its job to load it.

That’s it, the key difference. You can see it quite clearly via the code for the autoloading loader too:

      class Autoloading < Loader
        class << self
          def require!(component)
            constant(component)
            self
          end
        end
      end

It’s literally one overridden method that will just access the constant instead of calling require.

Everything else that Zeitwerk offers here is just gravy on top of not needing to manually require. You can choose yourself which of those Zeitwerk features to leverage in your application, if any.

My biggest motivation for adding the autoloading loader was just to remove the burden of dry-system users having to “require what you require”. That approach is a fine way to build systems, but it’s definitely extra friction, and with the work I’m doing to bring dry-system and Hanami 2.0 together, I want to make sure we can provide as streamlined an experience as possible.

2 Likes