As companies begin their transition to DevOps and CI/CD they typically don’t spend much time thinking about their delivery pipelines. After all, pipelines don’t seem difficult at first glance so folks assume they’ll simply script their pipelines. An engineer is assigned the role of DevOps, writing the scripts, and the team moves on to more important matters. Months later, though, we often hear “no one can deploy if the DevOps engineer isn’t here” or “she’s not allowed to take more than a week long vacation.” One company even commented “We don’t let him leave the building anymore.” What happened, and what does it have to do with declarative vs imperative programming?
Scripting deployment pipelines requires the DevOps engineer to predetermine and accommodate all possible dependencies, permutations and possible errors. Scripts include step by step instructions for creating containers, moving data, creating connections, testing and much more. All of which implies our engineer has to know how to perform each of those tasks. Worse, over time new releases add new dependencies. New capabilities may require new quality gates or even architectural changes. All of these will likely require changes to the pipeline scripts. The once simple script multiplies and gains layers of complexity. Not so simple after all. If scripting isn’t the answer, though, what’s the alternative?
Scripting is an example of imperative programming, in which engineers specify step by step instructions to be followed. Thus a script can never do anything the engineer didn’t write or perform a task the engineer didn’t know how to code. An alternative form of programming has been a around for decades, declarative programming. Even if you’re not familiar with the term, you’re likely familiar with at least one example of declarative programming; HTML.
Declarative programming enables an engineer to define what she wants done and use a system to interpret the program and determine how to achieve the goal. Declarative programs are more succinct and readable. If you don’t find that intuitive, consider comparing an HTML file describing this blog page with a C program to create the same screen. Declarative programs are also more reusable and can more readily accommodate errors. Perhaps more important for our discussion though, our engineer doesn’t need to know how perform every task that may be required to achieve the goal.
When considering how to address the complexity of delivery pipelines we felt a declarative approach provided the best solution so that’s where Skopos started – with a declarative application model. Using a model enables Skopos to handle permutations and errors without the developer having to anticipate every situation. The same model can even be used to deploy to different environments without modification. Our DevOps engineer can simply define the required application components and a set of constraints such as start order or version dependencies using YAML. For a simple application the file may only be a few dozen lines.
So how does the model get used? At the outset of a deploy Skopos compares the application model to the running system and what’s been checked in. The system creates a data structure defining the differences. If only one component has updates and there are no dependencies, then only one component will be updated. The differences, the target environment and the model are then used to compile a deployment plan in a domain specific language. The plan specifies how Skopos will actually perform the heavy lifting of application deployment, but that’s a topic for another post.