The way we deliver software to users has changed dramatically over the years. In the not too distant past, it was common to deploy to production by running a shell script on a collection of servers that pulled an update from some kind of source control repository. The problems with this approach are clear—scaling this out was difficult, bootstrapping servers was error prone, and deployments could easily get stuck in an undesired state, resulting in unpredictable experiences for users.
The advent of configuration management systems, such as Chef or Puppet, improved this situation somewhat. Instead of having custom bash scripts or commands that ran on remote servers, remote servers could be tagged with a kind of role that instructed them on how to configure and install software. The declarative style of automating configuration was better suited for large-scale...