red curtain lit up on empty stage
PHOTO: Rob Laughter

Over the past several years, a lot of attention has been given to declarative systems. This is especially true as IT moves toward greater automation in operations and developers build systems on Kubernetes-based platforms.

What Is a Declarative System?

A declarative system is a system where the code represents the final system state. An example of a declarative system would be Infrastructure as Code systems where YAML is used to describe the configuration of hardware and software components which an automation server then implements. Kubernetes is similar. The developer decides, for example, that a container should be able to auto-scale to five copies of the container and Kubernetes insures it happens.

This approach is not new. HTML (without JavaScript) is a declarative language that only defines how a page should be structured. It’s up to the browser to render the page according to what the tags are saying the page should look like. HTML declares a section of code to be a heading with an <H1> tag but the browser has to actually display it according to rules, internal or through CSS, as to what a heading looks like. HTML declares something is a header and the browser makes it so.

Related Article: 4 Skills Modern Developers Need

Declarative vs. Imperative Systems

Declarative systems and languages are different from what we typically think of as computer code. Most computer languages are imperative, not declarative. In imperative systems, code defines a series of steps to be taken which the system then executes. This is the classic programming language paradigm.

For example, a declarative system may be told to create a virtual machine by declaring the existence of the VM as the expected state of the system.

System: FinApp

                State:

                                - Type : VM

                                                - VMType : VirtualBox

                                - RAM : 1G

                                - Storage : 120G

                                - OS : “Ubuntu 19.10”

                                - Target : //myserver/finapps

The system, for example an Infrastructure as Code automation system, would understand how to make that happen and instantiate a VM with 1G of RAM and 120G of storage running Ubuntu 19.10. An imperative system might look like this VirtualBox instantiation instead:

Log into the //myserver server

# cd /finapps

# VBoxManage createvm --name $FinApp --ostype "Ubuntu 19.10" –register

# VBoxManage createhd --filename /VirtualBox/$FinApp/$FinApp.vdi --size 122880

… instructions to instantiate disk controller and attach storage to VM

# VBoxManage modifyvm $FinApp --memory 1024

# VBoxManage startvm $FinApp --type headless

In above example, the hypervisor is told to execute a series of steps to create the VM.

With declarative languages, however, the system reads the desire state and executes whatever steps are necessary to make that state happen. How it does it is unimportant to the developer. The developer cares about the “what” not the “how.”

Related Article: What You Need to Know About Containerization

The Benefits of Declarative Systems

Declarative systems have important advantages. First, developers can focus on outcomes rather than process. The team spends its time thinking about what they want the system to be, free of worrying about how it is accomplished. This is much easier to program since it doesn’t require intimate knowledge of all the interfaces for all the systems. It also aligns directly with the developers’ vision of the system.

The focus on outcomes also means that outcomes are more predictable. Since the process is automated, there are fewer chances of errors and the final state is not subject to an operator’s interpretation of the developers’ desires.

Declarative systems are also more portable and easier to change. Imperative systems require a rewrite of all the scripts if there is a change or migration from one environment to another one. Not so with declarative systems. The targets change but the rest of the code remains the same. For example, what if the developers want to shift the VM environment from VirtualBox, which they use for development, to VMWare which is what is used in production? With an imperative system, everything has to be rewritten since the commands and process of creating a VM is different. With a declarative system, only the VMType — the declaration that decides what type of VM is to be created — and the target need to change to create a VM of the right type on the right server. The development team doesn’t even need to be aware of how to create VMs using VMWare.

Another important feature of declarative systems is that they are self-documenting. It’s obvious from the YAML or JSON code, what the expected system configuration will be. Imperative systems, on the other hand, require a knowledgeable person to interpret the commands and see what the end result should be. This requires that someone be expert in the commands of the individual system components. It also leads to errors in interpretation. Declarative systems are clearer and don’t require a human translation layer.

Related Article: Adopt Chaos Engineering to Preserve System Resilience

Downsides of Declarative Systems

Of course, no system is perfect and there are some downsides to using declarative systems. Declarative systems need a way to take code that describes the state of a system and turn it into action. In some cases, this is how the software is designed. Otherwise, there needs to be a translation layer that turns the declarative code into commands. This is the problem that is common to Infrastructure as Code (IaC) and CI/CD systems that use declarative languages. They need an automation server to turn the code into commands. That means deploying a system to configure the system, which translates into cost and effort.

There is also the issue of support for the vast array of system components available to IT, including older hardware and software. In the case of declarative systems used to configure hardware or software infrastructure (as opposed to being native to the system), there are likely holes in the support for hardware and software. Most declarative systems provide a way to expand the system to accommodate command sets that aren’t available out of the box. If a vendor can provide that, as is often the case with CI/CD and IaC systems, then it’s only a matter of installing the extension. Otherwise, the developers themselves will have to write the extension before they can use the declarative system.

Declarative systems make it easier to visualize, document, and create complex systems than imperative systems. While some software uses declarative languages to describe system configuration, most are still using command sets. Software that creates and configures whole systems commonly use declarative languages but it’s important to ensure that support exists for all system components. Given the advantages, it is expected that declarative systems will become more common, at least as an option. Declarative systems are just easier. And easier translates to lower cost and fewer errors. That’s something everyone can get behind.