A badly-defined Deployer abstraction impairs Continuous Delivery

As Continuous Delivery patterns are gradually establishing themselves, antipatterns are also surfacing – and a common antipattern is the Deficient Deployer.

When we talk about a Deployer, we are referring to a pipeline task that can be invoked by any post-Commit stage to deliver an application binary to an environment. A Deployer interface should look like:

Deployer#Deploy(Application a, Version v, Environment e)

There are a couple of ways Deficient Deployer can creep into a Deployer implementation:

  • Anemic implementation – this is where the Deployer behaviour is solely specified as “download binary and copy to environment”, ignoring any one-time operations deemed necessary for a binary to be in a valid state. For example, environment configuration should be filtered into the application binary during deployment, as it is a one-time operation decoupled from the application lifecycle. If this configuration step is omitted from the Deployer, then additional manual effort(s) will be required to make the binary ready for use.
  • Over-specified interface – this is where environment-specific parameters are added to the Deployer interface e.g. Deployer#Deploy(Application a, Version v, Environment e, Server s). The server(s) associated with an environment and their credentials should be treated as an implementation detail of the Deployer via a per-environment server mapping. Application version, and environment are the only consistent mandatory parameters across all environments.

The root cause of the Deficient Deployer seems to be a reluctance to treat deployment scripts as first class citizens.