On Libraries and Dependencies

Recently, Aura is creating some buzz that components of PHP frameworks should not have any dependencies. To quote Paul M. Jones' recent article on Aura:

The distinction is that all Aura packages (with the exception of the Framework package) are completely independent, and have no cross-package dependencies, whereas at least some of the components from Symfony2 and ZF2 have dependency requirements.

Or, quoting this article on replacing Silex with Aura.Micro:

I was recently working on a small project that used Silex. As I browsed my vendor folder, I realized how much extra "stuff" I had inherited with Silex. There were a bunch of other components required when all I wanted was some quick and easy routing, micro-framework style.

I think this needs some urgent clarification before this way of thinking becomes mainstream and PHP creeps back into its NIH-hole.

Dependencies Are Bad

The general attitude of people arguing against dependencies is that dependencies are a bad thing. Why could they be a bad thing?

  • Because I have to install them all by hand.

    This used to be a problem, but void now that we have Composer.

  • Because they clutter up my hard disk space.

    Hard disk space is cheap, and internet connnections are fast, so whether you upload 500k to your web server or 1M does not really make a difference anymore.

  • Because they eventually might become unsupported.

    That's a problem, but it's a problem of the developer of the library, who should pick dependencies that he trusts. You as a user don't have to care about that.

  • Because they confuse me.

    Composer installs dependencies into a directory "vendor/", so they are hidden away from you. If you ever browse them, you probably know what you're looking for.

  • Because it's not micro-frameworkish.

    I suggest to join the nearest church if you are into religious debates.

Are They Really?

Why do libraries have dependencies at all? Is a library with dependencies less decoupled than one without?

The reasoning for dependencies usually goes like this:

  • If I need some functionality, and if another library does that, I should reuse that library.*

For example, let's look at the Guzzle HTTP Client's composer.json:

"require": {
    "php": ">=5.3.2",
    "ext-curl": "*",
    "symfony/event-dispatcher": ">=2.1"

Guzzle needs event dispatching and relies on the Symfony2 Event Dispatcher to do so. There's no point in coding another one if there's a solid, mature and tested implementation available, right now, for free, that does what you want.

Coupling vs. Cohesion

One major argument for the elimination of dependencies is "decoupling". So let's examine two important quality criteria in software engineering: Coupling and Cohesion.

In software engineering, coupling or dependency is the degree to which each program module relies on each one of the other modules.

Coupling can be expressed in various degrees, ranging from high to low:

Content coupling (high)

Content coupling (also known as Pathological coupling) is when one module modifies or relies on the internal workings of another module (e.g., accessing local data of another module). Therefore changing the way the second module produces data (location, type, timing) will lead to changing the dependent module.


Message coupling (low)

This is the loosest type of coupling. It can be achieved by state decentralization (as in objects) and component communication is done via parameters or message passing (see Message passing).

No coupling

Modules do not communicate at all with one another.

As is generally known, it is preferrable to achieve low coupling, also known as "decoupled code". What about cohesion then?

In computer programming, cohesion refers to the degree to which the elements of a module belong together.

Like coupling, cohesion can be expressed in various degrees:

Coincidental cohesion (worst)

Coincidental cohesion is when parts of a module are grouped arbitrarily; the only relationship between the parts is that they have been grouped together (e.g. a "Utilities" class).


Functional cohesion (best)

Functional cohesion is when parts of a module are grouped because they all contribute to a single well-defined task of the module (e.g. tokenizing a string of XML).

High (functional) cohesion is preferrable over low (coincidental) cohesion. Just because you package some code together doesn't mean that code really belongs together.

To distill the essence of the above quotes:

  • Libraries should be lowly coupled. They should communicate with other libraries through clearly defined interfaces.

  • Libraries should be highly cohesive. They should only implement what they are responsible for (their "core business").

Reducing the dependencies of a library to zero (no coupling) may be a good thing, but only if the library does One Thing Only. If it does not, complete decoupling leads to low cohesion: The library will do more than it should.

Hurray for Dependencies?

What does this mean in practice? Can we just increase the amount of dependencies as we like?

My personal stance on this topic is that a library should require as little as possible dependencies to run (hard dependencies) – as long as it is highly cohesive – but support as many different libraries as it can (optional dependencies). The limit for the latter really is the library's developer's time and what he wants to support. As example, I will quote the composer.json of Behat's Gherkin parser:

"require": {
    "php":             ">=5.3.1",
    "symfony/finder":  ">=2.0,<2.2-dev"
"suggest": {
    "symfony/yaml":        "If you want to parse features, represented in YAML files",
    "symfony/translation": "If you want to use Symfony2 translations adapter",
    "symfony/config":      "If you want to use Config component to manage resources"

Because it needs to look for files in the file system, the parser has a hard dependency on the Symfony2 Finder component. But then it also supports optional dependencies. These optional dependencies help you if you want additional support for YAML files or translation functionality. In my opinion, the Gherkin parser is a perfect example of how you should manage your packages' dependencies.

My Wish

To conclude, I would like to move away from "dependencies are bad per se" to a more differentiated view on dependencies. Dependencies help to achieve high cohesion and to keep DRY. Use what's there already. Simplify the installation of your packages with Composer. Then try to make as many dependencies optional as you can. But don't remove all your dependencies just for the sake of it. And stop promoting your libraries as if that was a good thing, please.

Update 2012/12/18

For clarification: This blog post is not a rant against NIH. The point is not that we should have one implementation only of everything. Variation is good and competition is healthy.

The point is that you should not put all code into one dependency-free package, if it should rather be in two packages that build upon each other. @sebwebdev on Twitter sums it up quite well:

Loose coupling + high cohesion > No coupling + low cohesion