When teams first start down the automation path, they often begin with what feels familiar: imperative automation.
After all, manual CLI interaction is imperative by nature. You connect to a device, type a command, perform an action. When you start automating, it makes sense to encode those same steps into a script or workflow.
But the more complex your environment becomes, the more this approach starts to break down. You find yourself writing longer scripts, handling more edge cases, tracking more state, and hitting more failures.
Declarative automation offers a fundamentally different and more scalable model for building automation that’s predictable and easy to maintain.
Below, we outline the key differences between declarative vs imperative automation and explain how adopting declarative models can drive more reliable, trustworthy automation for modern infrastructure.
TL;DR
- Imperative automation encodes the steps to achieve a result. Declarative automation describes the result you want and lets the system figure out the steps.
- Declarative models are inherently idempotent. Running them produces the same outcome every time – without unintended side effects.
- Most teams start imperative and gradually shift toward declarative as complexity grows. The goal isn’t to get to 100% declarative. Instead, aim to use declarative approaches wherever they reduce complexity and improve reliability.
Declarative vs imperative automation: Core distinctions
At the heart of the distinction is this:
- Imperative automation focuses on how to do something. It encodes each action, step by step.
- Declarative automation focuses on what the end result should be. It describes the desired state.
In an imperative model, if you want to configure a VLAN, you might write 10 steps to make that happen. You’re telling the system exactly what to do.
In a declarative model, you simply describe the outcome: “I want this VLAN on this interface.” The system figures out the necessary steps and reconciles them against the current state.
The difference is huge. If something goes wrong halfway through an imperative workflow, your system can be left in an inconsistent state. Declarative systems, by contrast, are designed to converge on the intended state, making them inherently idempotent and easier to retry safely.

Seeing the difference in code
Here is the same task (ensuring VLAN 100 exists and is assigned to an interface) handled both ways.
Imperative approach (Python + Netmiko):
from netmiko import ConnectHandler device = { "device_type": "cisco_ios", "host": "192.168.1.1", "username": "admin", "password": "secret", } with ConnectHandler(**device) as conn: # Step 1: check if VLAN already exists output = conn.send_command("show vlan id 100") if "not found" in output: # Step 2: create the VLAN conn.send_config_set(["vlan 100", "name PROD"]) # Step 3: check current interface config output = conn.send_command("show running-config interface GigabitEthernet0/1") if "switchport access vlan 100" not in output: # Step 4: assign VLAN to interface conn.send_config_set([ "interface GigabitEthernet0/1", "switchport mode access", "switchport access vlan 100" ]) # Step 5: save config conn.send_command("write memory")
This script works, but notice what you are responsible for:
- Checking current state
- Handling the “already exists” case
- Sequencing each step
- Saving the config
If the connection drops after step 3 but before step 4, the VLAN exists but isn’t assigned. Your system is now in an inconsistent state with no automatic recovery path. Scale this to 200 devices across 10 vendors, and the complexity multiplies.
Declarative approach (Ansible):
- name: Ensure VLAN 100 exists and is assigned hosts: switches tasks: - name: Configure VLAN cisco.ios.ios_vlans: config: - vlan_id: 100 name: PROD state: merged - name: Assign VLAN to interface cisco.ios.ios_l2_interfaces: config: - name: GigabitEthernet0/1 access: vlan: 100 state: merged
Describe what you want: VLAN 100 should exist → it should be named PROD → it should be assigned to this interface.
Ansible checks current state, computes the diff, and applies only the changes needed. Run it again on a device where VLAN 100 already exists correctly, and nothing changes.
What happens when something fails midway:
| Imperative | Declarative | |
|---|---|---|
| Failure mid-run | System left in partial/inconsistent state | System retries and converges to desired state |
| Running again after failure | May cause duplicate actions or errors | Safe to re-run — only applies missing changes |
| Auditing what changed | Requires manual inspection | Diff shows exactly what will change before execution |
Why engineers begin with imperative automation
Nearly every engineer starts with imperative automation—because that’s what we’ve always done.
Manual CLI interaction is procedural. So when you first move into automation, it’s natural to translate that process into scripts. But as you automate more of the environment, imperative automation becomes harder to scale.
Each imperative workflow must explicitly interact with every target endpoint. As the number of endpoints and process steps grows, the complexity increases exponentially. Every time a vendor changes and you need to update the workflow, complexity increases again.

Handling state transitions gets more complex. Dependencies and failure points proliferate. Testing and debugging imperative workflows becomes a nightmare.
This snarl of complexity is exactly what declarative models are designed to avoid.
The rising importance of declarative approaches
Declarative automation has already become foundational in many parts of modern infrastructure.
In the cloud, tools like Terraform are already using this model. You define what you want—a VM, a network—and Terraform handles the logic of creating, updating, or deleting resources as needed. You’re no longer writing out step-by-step procedures for each action.
Likewise, Kubernetes has brought declarative principles to container orchestration: you describe the desired state of the system (for example, 5 replicas of an application) and the platform ensures that state is maintained.
These concepts are now spreading into network automation. Declarative automation provides a bridge toward higher-level capabilities, like self-healing networks, where procedural scripts simply can’t scale to handle the complexity.
What declarative looks like in network automation specifically
Terraform and Kubernetes are the most cited examples of declarative automation, but they operate at the cloud and container layer. For network and infrastructure teams, the equivalent tools look like this:
- Ansible in declarative mode. Ansible can be used both imperatively (a sequence of shell commands) and declaratively (using resource modules like ios_vlans or nxos_interfaces with state: merged). The declarative approach is significantly more reliable at scale.
- Nornir with desired-state frameworks. Nornir is a Python automation framework that, when combined with libraries like Nornir-NAPALM, supports desired-state configuration management. You define what the device should look like and the framework handles the reconciliation.
- Infrahub generators. Infrahub’s generator framework lets you define the intended state of your infrastructure in a source of truth and automatically render and apply configurations from that state. Changes flow from data, not from scripts. The source of truth becomes the single definition of desired state, and generators ensure the actual state converges to it.
You define what, the tool figures out how.
How declarative automation builds trustworthy systems
Declarative models directly address the shortcomings of imperative workflows by breaking down complexity and embedding reliability into the system by design.
They support many of the principles that are essential for trustworthy automation:
- Predictability: Outcomes are more consistent because you define the end state.
- Idempotency: Re-applying a declarative definition won’t produce unintended changes.
- Resilience: If partial failures occur, declarative systems can retry or re-converge, reducing the risk of inconsistent state.
Declarative automation and idempotency are tied together by definition. A declarative system is inherently idempotent. You define the end state, and the system figures out how to get there, or back out of it if something goes wrong.
Instead of building one giant workflow that touches everything, declarative systems decompose the work. Smaller workflows—or what we call agents—are in charge of implementing the idempotent declarative workflow on each endpoint.
These agents can be developed and tested independently, which simplifies both the architecture and the engineering effort, while dramatically increasing trust in the system.

Declarative automation and trust by design
Declarative automation naturally supports the three technical foundations of trustworthy automation:
- Idempotency ensures repeatable outcomes.
- Dry runs show you exactly what changes will be made before anything is executed.
- Transactional execution ensures that changes either complete entirely or leave the system unchanged if a failure occurs.
All three of these technical foundations are easy to support with declarative automation, but difficult or impossible to achieve with imperative approaches.

How to shift from imperative to declarative automation
Moving from imperative to declarative automation can be a big mindset shift. Instead of asking “What steps do I need to automate?”, you start asking, “What state do I want the system to be in?”
That also means thinking differently about tooling and architecture.
State representation becomes central. You need a clear source of truth for intended state. Agents and controllers become part of the system, responsible for driving convergence. Testing becomes more about validating that your definitions produce the right state.
In most environments, a mix of approaches is healthy: declarative automation for common configurations at scale, imperative workflows for complex or exceptional cases like OS upgrades.
A pragmatic transition looks something like this:
- Start with new workflows, not existing ones. Rather than rewriting your current scripts, adopt a declarative approach for the next thing you automate. Pick something bounded and repeatable, like VLAN management, interface standards, routing policy templates.
- Identify your most painful imperative workflows. These scripts break the most, require the most maintenance, or have the most failure modes.
- Build your intent data foundation. Before you can tell a system “converge to this state,” you need to know what that state is.
- Run both in parallel during the transition. It’s fine to have some workflows imperative and others declarative while you’re shifting.
The goal isn’t to be 100% declarative, but to adopt a declarative approach wherever it reduces complexity and improves reliability.
Common objections
“Our environment is too complex or too legacy for declarative automation.”
Declarative automation doesn’t require a clean, standardized environment to get started, it requires a clear definition of what you want the environment to look like. Brownfield environments are often the ones that benefit most, because they have the most inconsistency for a declarative system to converge away. Start with one device type, define the desired state for a single configuration area, and prove it out before expanding.
“We don’t have a source of truth.”
If your desired state is in scattered YAML files, Git repos, or someone’s head, there’s no way declarative automation is going to work. That being said, even a partial source of truth for one domain is better than none.
“Our team doesn’t know how to write declarative configs.”
Most engineers who have used Ansible in declarative mode, written Kubernetes manifests, or worked with Terraform pick up declarative automation quickly. The harder shift is the mindset, going from “what do I do” to “what should exist.”
Declarative vs imperative at scale
In modern, multi-vendor networks, automation must scale beyond what imperative approaches can handle.
As the cloud world learned years ago, declarative models provide the foundation for that scale: more predictable behavior, better error handling, simpler maintenance, and greater resilience.
Declarative thinking will increasingly shape the next generation of infrastructure automation tools and practices, not as an absolute replacement for imperative methods, but as a smarter default for most automation workflows.
For any team aiming to build infrastructure automation that scales, adapts to complexity, and earns operator trust, adopting declarative approaches is one of the smartest steps you can take.
If you’re trying to make the transition, here are a few extra resources that might help you get there faster:
- How to automate legacy infrastructure without rebuilding (practical advice for starting declarative automation in a brownfield environment)
- Infrastructure version control (how to give your declarative workflows the same review and rollback capabilities that software teams use)
- A deep dive into Infrahub’s generator framework (declarative automation in practice)