youri babakhanians - Fotolia
In computer science, we tend to write computer programs in one of two ways. Declarative programming is focused on the outcomes, not the steps to reach them. Imperative programming is custom code that says how to get to the outcome.
DevOps tools tend to take a declarative approach. With declarative DevOps, the team specifies what -- such as configuration information and the desired state of the program -- and the tools figure out how to get there. Throughout software development and delivery, teams use both declarative and imperative programming. It's helpful to know what types of projects best fit either approach.
Imperative vs. declarative programming
To illustrate the difference between imperative and declarative styles, you can use each one to set up a test environment. Software teams set up test environments for code frequently.
In imperative programming, you run a build manually, get a copy of a Java Archive (JAR) file, then connect to a hypervisor and request a virtual machine, handing it an International Organization for Standardization (ISO) file that contains an OS image. Then, you run a shell command to install all the dependencies and the JAR file. You told the system each step to build the test environment.
In a declarative model, you define the end state: a test version of the application running on a certain branch, with a specific configuration. The tool spins up the VM running the correct OS image for the test environment.
On the surface, declarative programming seems like only an added level of abstraction, meaning that the imperative programmer in the example could code the test environment build and wrap it in a shell script to run whenever needed. But declarative DevOps tools are more than that.
Declarative DevOps benefits
In an imperative DevOps model, programmers benefit from automation, but only if they write custom code. As in the above example, the programmer must tell the software to connect to a specific hypervisor and set up the test environment via the command-line tools of that hypervisor. The imperative directions might include a monitoring loop that analyzes the load so the program can scale resources as needed. The same program for this test environment might require instructions to call a tool such as Jenkins to perform a build. Imperative programs rely on this glue code to connect a string of actions to meet the desired end state.
Here are three ways that declarative DevOps differs from the imperative approach.
Declarative tools are driven by configuration, not code. Rather than hard code into the program a specific number of cores, memory and disk size, and other details for the environment, the programmer puts this information into a configuration file, typically formatted in JSON or YAML. When the programmer asks for a test environment, the system uses this standard configuration file to create it. The configuration file might point to another tool, like the hypervisor, to accomplish the task. Configuration files can be maintained in a version control system to keep track of changes. Declarative DevOps enables a change without breaking the sequence of automation steps.
Configuration files, as opposed to hard-coded configuration rules, prevent vendor lock-in. Take the example of a container deployment set up by configuration files in Kubernetes: The configuration files and OS images are available so that particular workload can spin up on any number of different infrastructures, such as AWS or Microsoft Azure public cloud. The container cluster configuration is written into version-controlled code, which means recreating a cluster or porting it between cloud-based systems is as easy as changing the variables specific to each environment. Container management tools like IBM's Red Hat OpenShift enable hybrid and even multi-cloud implementations, programmatically controlled. Set the variables, tell the software how to manage load and scale, and the software generates the environment as specified.
Declarative DevOps adjusts to changes. With an imperative DevOps model, you write the code to manage the system. If the subsystems change, you rewrite the code to make it work with the new information. If programmers change default values, they often have to recompile and redeploy the program. With declarative tools, a subsystem change means that the programmer updates the appropriate configuration file. The code to execute that configuration file isn't affected.
Use cases and potential adopters
Declarative DevOps tools give users a template to work with, rather than scripts to write. Declarative DevOps enables self-service: Anyone who needs the test environment in this example can request it, and the tool builds one without outside help. There's no custom code to maintain and no manual work from an infrastructure administrator in operations.
Larger programming teams are more likely to benefit from the adoption of a declarative DevOps model than small teams. Multiple teams can work much faster if they get an individual templated build in minutes, rather than waiting on their turn with a single shared test server.
An organization with a legacy platform in place might not see benefits from declarative DevOps immediately, or as much as one that is newly adopting DevOps or creating a new cloud platform. Modern open source cloud tools -- examples include IBM Red Hat's OpenShift and Ansible, as well as Docker, Kubernetes and OpenStack -- tend to be declarative.
Containers and Kubernetes
The container was one of the first major declarative DevOps tools, as containers are small and easy to manage with version control. But there are still plenty of imperative DevOps processes happening with containers.
Programmers who write imperative code around Docker tell the OS to spin up a container, and where to run it. A declarative setup with Kubernetes means that the programmer doesn't need to specify where the code runs, the resources it needs or the scale at which to run it for every container. Instead, programmers rely on a previously configured cluster. In Kubernetes, you can configure the Horizontal Pod Autoscaler to create more pods of containers when the net CPU consumption hits 30%. This is the declarative approach. With the imperative model, ops monitors CPU use in a while loop and run a command to create more pods when it exceeds 30%.
Kubernetes introduced the configuration.yaml file that stores information such as how many resources a given pod typically needs, when to scale up and what ports to run on. Here's an example from the Kubernetes documentation:
- name: command-demo-container
args: ["HOSTNAME", "KUBERNETES_PORT"]
Tools like OpenShift take the next step; they manage and patch the running containers. You can configure OpenShift to manage hybrid cloud environments or move machines from the data center to public cloud and back. The imperative approach requires a great deal of custom code to accomplish those same tasks.
If you want to build a cloud-based application at web scale today, you should probably look seriously into declarative DevOps. Organizations can put together a set of tools in-house or use a vendor-defined stack built for the purpose. Imperative tools might seem simpler to get started with, but declarative DevOps provides sustainable programmatic infrastructure and avoids rigid configurations and lock-in.
A mix of declarative and imperative programming
It's one thing to manage infrastructure, but servers still need applications to run on them. Back-end services include web services, microservices and serverless computing. Custom application code serves business demands.
There's still a place for traditional, curly-brace imperative languages, said Bob Reselman, a developer and consultant at CogArtTech. Programmers use these languages to write custom code. Configuration files, used to deliver applications onto hosting environments, however, can accomplish a lot more with a declarative model.
Declarative DevOps tools like Kubernetes enable self-service and safe updates. Software teams don't need to worry about operations delivering patches to the Debian OS. Instead, operations updates the configuration file for the OS, delivering the new version without changing other parts of the code.