In Why Declarative Systems Are Taking Over Everything, I argued that declarative control models are becoming the default across infra, frontend, and data.
That is still true.
It is also incomplete.
Declarative systems do not remove complexity. They relocate it into planners, graphs, and reconciliation loops. If you do not account for that shift, incidents get harder to explain, not easier.
The failure modes are predictable
Most declarative incidents are not random. They cluster around a few recurring patterns.
1. Hidden dependencies and conservative plans
Terraform can infer many dependencies from references, but not all. The docs explicitly call out hidden dependencies and the need for depends_on in those cases (depends_on reference).
The same page also warns that depends_on should be a last resort because it can produce more conservative plans and more unknown values. This is one of the places where declarative clarity starts to erode.
2. Reconciliation lag and non-stable runtime state
Kubernetes controllers are non-terminating control loops that move current state toward desired state (Kubernetes controllers).
The docs also note that a cluster may never reach a fully stable overall state while control loops keep fixing failures and reacting to changes. That is powerful, but it means debugging is a moving target.
3. Frontend abstraction leaks at system boundaries
React is declarative by design, but the official docs still keep an entire section for "escape hatches" to connect to external systems (React escape hatches).
That page is explicit that most app logic and data flow should not rely on those features. In practice, teams hit these boundaries often: focus management, media APIs, realtime subscriptions, and third-party widgets.
4. Data graphs still need procedural escape valves
dbt models are declarative SQL transforms, but advanced pipelines still lean on imperative hooks for platform-specific behavior. dbt documents pre-hook and post-hook for SQL that must run before or after resources are built (dbt hooks).
Incremental models also add branch logic (is_incremental()) and strategy-specific behavior that can fail if keys are not truly unique or predicates are wrong (dbt incremental models).
So even in a declarative-first data stack, there is still procedural complexity at the edges.
Where imperative control is still the better choice
The rule is not "pick one forever." The rule is "pick the right control surface for the risk profile."
| Situation | Declarative-first? | Why |
|---|---|---|
| Long-lived infrastructure and service topology | Yes | Desired-state reconciliation, drift handling, and reviewable plans matter most |
| One-time high-risk migration with strict sequence constraints | No | Explicit ordered steps are easier to audit and stop safely |
| Frontend UI state and rendering | Yes | State to view mapping stays predictable |
| Low-level browser/device integration edge cases | No | Direct imperative control reduces abstraction leakage |
| Routine warehouse transforms and DAG dependencies | Yes | Graph-based execution and lineage are high leverage |
| Adapter-specific maintenance, audit inserts, platform quirks | No | Targeted hooks or scripts provide precise control |
A practical hybrid pattern
The most reliable teams keep declarative systems as the default path and define imperative "break-glass" paths explicitly.
That means writing runbooks and scripts that are:
- rare
- well-scoped
- idempotent where possible
- reviewed like production code
A simple mental model:
- Declarative path: steady-state operation and normal delivery.
- Imperative path: bounded exception handling when sequence and side effects must be explicit.
How to decide quickly
When deciding between declarative and imperative for a specific task, ask:
- Is this a persistent desired state or a one-time operational procedure?
- Do I need exact step ordering with side-effect visibility?
- Can a reconciliation loop safely converge this without ambiguity?
- If this fails at 2:00 AM, which model is easier for the on-call engineer to reason about?
If answers 2 and 4 dominate, imperative usually wins for that slice.
Final note
Declarative systems are winning for good reasons, but they are not magic.
They reduce whole classes of operational burden while introducing their own failure patterns. Mature engineering teams stop treating this as ideology and treat it as control-plane design.
Default to declarative. Keep imperative tools sharp for the moments where explicit sequence and direct control are the safer choice.