Declarative vs. Imperative

Jun 15, 2021

Now that it's getting hotter, I think about thermostats a lot. They are a rare declarative interface in the world: you input your desired temperature. The air conditioning system reconciles the air temperature in the room by measuring and turning on and off the airflow. Contrast that with the imperative control of the microwave. Put leftovers in for 30 seconds. Is it hot enough? Put it in for another 30 seconds. Repeat.

Declarative is about the what. Imperative is about the how. More and more systems in the world are becoming declarative rather than imperative. In programming, every part of the stack is becoming declarative.

React. React solves the problem of creating interactive UIs. Design simple views for each state in your application, and React updates and renders only the components you need when the data changes. By offloading the reconciling commands, React tends to be much more efficient and predictable.

Kubernetes. Kubernetes is self-healing. That means that when machines or applications crash, it maintains the desired state by spinning new ones up. Kubernetes also takes the declarative concept to the next level, applying it to all kinds of infrastructure resources.

Excel. Excel automatically recalculates cells that are dependent on each other. You never have to wonder if cells contain stale data. The declarative interface is cell references and the graph of cell dependencies that Excel has built behind the scenes.

But declaratively thinking can be difficult. We want to see the cause and effect of imperative commands. Data scientists and machine learning engineers have moved back towards imperative programming.

Jupyter Notebooks. Notebooks provide the most granularity for imperative programming. They allow data scientists to execute code on demand, instantly seeing graphs and tables every step of the way. The tradeoff is that the programmer must manage the control flow - running blocks in the correct order and reproducing state. But imperative programming has benefits early in the ideation cycle before a desired goal and operations are known.

TensorFlow. TensorFlow 1.0 shipped with a declarative API. Define your operations and then compile and execute. Large models made it confusing to understand the effects of adding a new edge to the graph. PyTorch provided a much more imperative approach that became more popular. Now, both PyTorch and TensorFlow offer imperative APIs.

The future isn't necessarily declarative or imperative. We will always need both. But as we learn how to measure and define the state of different systems, we can start to offload imperative tasks with declarative interfaces.