What went wrong with Module::Build?

Module::Build is a popular module to complain about. Much of it is either whining or completely outdated (sometimes both). I use it in most of my modules and so far it is my preferred module builder, this is not merely a rant on something I don't want to use.

The vast majority of modules, probably something like 95%, don't need anything special. They are just some Perl files, maybe with some XS: their build process is entirely standard. That means that for 95% of all modules out there it doesn't really matter what you use, as long as it doesn't break. Module::Build, Module::Install and ExtUtils::MakeMaker all work fine. Boring, but boring is a good thing in a toolchain. Module::Build is a frequently used option, but lets be honest, it hasn't exactly eclipsed ExtUtils::MakeMaker and Module::Install. In fact, the latter seems to be the usual choice among the "cool modules" (Moose, Catalyst, etc…).

The rest

The other 5% is where things gets interesting. There are basically two approaches to them:

  1. Writing custom code
  2. Writing reusable components that you can reuse next time

Obviously, the second is to be preferred whenever possible. That scenario should have been a homerun for Module::Build. Reality is different, there are hardly any extensions for it on CPAN. In fact Module::Install seems to have more of them. What the hell went wrong?

Module::Build is very extendable, but those extensions are not reusable. IMO there are two design decisions here to blame:


Extending Module::Build is done by subclassing it. This works fine for simple examples, but has a major problem: it only works with well single inheritance. Because of that you can not combine Module::Build::PDL with Module::Build::WithXSpp for example. This greatly limits the usefulness of such extensions. This problem should be obvious to anyone who's familiar with roles. When you compare it with how easy it is to extend Dist::Zilla for example, the problem of this design becomes glaring obvious.

God object

The problem mentioned above is aggravated by the fact that almost all behavior is mashed together in one class. Granted, it delegates a few platform specific behaviors to platform specific subclasses, but almost all other behavior is implemented in Module::Build::Base. It has more than 5600 lines and 342 methods (269 public, 73 protected) including 84 accessors. It's massive. You can't naively add roles to that and expect it to work out.

How to get out of this mess?

Well, that's the topic of my next post… ;-)

Leave a comment

About Leon Timmermans