About this course
<p>Modern software systems are increasingly automated. We automate deployments, infrastructure changes, scaling, incident response, and data pipelines.</p>
<p>And now, with AI agents, we are starting to automate operational decisions too. That sounds like progress, but it creates a problem that is easy to miss:</p>
<blockquote>
<p>We are getting better at executing operations without necessarily getting better at specifying what those operations are supposed to achieve.</p>
</blockquote>
<p>A deployment pipeline can run successfully and still violate an important business constraint.</p>
<p>An infrastructure script can finish without error and still leave the system in the wrong state.</p>
<p>An AI agent can complete a sequence of actions and still produce an outcome that nobody can verify afterward.</p>
<p>In many systems, operational intent still lives across:</p>
<pre><code class="language-text">runbooks
tickets
Slack messages
CI/CD configuration
Terraform files
dashboards
monitoring rules
human memory
</code></pre>
<p>Those artifacts are useful. But they are not the same thing as an executable operational specification.</p>
<p>An executable operational specification describes what should happen in a way that can later be evaluated against what actually happened.</p>
<p>That distinction becomes increasingly important as execution gets faster, more distributed, and more autonomous.</p>
<p>In this article, I'll explore:</p>
<ul>
<li><p>why automation alone is not enough,</p>
</li>
<li><p>the difference between execution logic and operational intent,</p>
</li>
<li><p>what an executable operational specification is,</p>
</li>
<li><p>why observability does not solve this problem by itself,</p>
</li>
<li><p>how specifications can make operational behavior verifiable,</p>
</li>
<li><p>how this applies to CI/CD, infrastructure, incident response, and AI agents,</p>
</li>
<li><p>what a minimal specification might look like,</p>
</li>
<li><p>and what problems this approach still does not solve.</p>
</li>
</ul>
<p>The central idea is simple:</p>
<blockquote>
<p>If a system can execute an operation automatically, we should also be able to describe what successful execution means independently of the tool performing it.</p>
</blockquote>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>You should be comfortable with:</p>
<ul>
<li><p>software architecture</p>
</li>
<li><p>CI/CD</p>
</li>
<li><p>infrastructure automation</p>
</li>
<li><p>observability</p>
</li>
<li><p>distributed systems</p>
</li>
<li><p>basic testing concepts</p>
</li>
<li><p>automation workflows</p>
</li>
</ul>
<p>You do not need to use any particular cloud provider, deployment platform, or orchestration tool.</p>
<p>The ideas in this article are deliberately tool-independent.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a href="#heading-prerequisites">Prerequisites</a></p>
</li>
<li><p><a href="#heading-table-of-contents">Table of Contents</a></p>
</li>
<li><p><a href="#heading-automation-solves-execution-not-intent">Automation Solves Execution, Not Intent</a></p>
</li>
<li><p><a href="#heading-operational-knowledge-is-usually-fragmented">Operational Knowledge Is Usually Fragmented</a></p>
</li>
<li><p><a href="#heading-execution-logic-and-operational-intent-are-different-things">Execution Logic and Operational Intent Are Different Things</a></p>
</li>
<li><p><a href="#heading-what-is-an-executable-operational-specification">What Is an Executable Operational Specification?</a></p>
</li>
<li><p><a href="#heading-a-simple-deployment-example">A Simple Deployment Example</a></p>
</li>
<li><p><a href="#heading-turn-success-criteria-into-evidence-requirements">Turn Success Criteria into Evidence Requirements</a></p>
</li>
<li><p><a href="#heading-why-observability-alone-is-not-enough">Why Observability Alone Is Not Enough</a></p>
</li>
<li><p><a href="#heading-specifications-make-automation-verifiable">Specifications Make Automation Verifiable</a></p>
</li>
<li><p><a href="#heading-keep-the-specification-independent-from-the-executor">Keep the Specification Independent from the Executor</a></p>
</li>
<li><p><a href="#heading-a-minimal-typescript-model">A Minimal TypeScript Model</a></p>
</li>
<li><p><a href="#heading-how-this-applies-to-cicd">How This Applies to CI/CD</a></p>
</li>
<li><p><a href="#heading-how-this-applies-to-infrastructure">How This Applies to Infrastructure</a></p>
</li>
<li><p><a href="#heading-how-this-applies-to-incident-response">How This Applies to Incident Response</a></p>
</li>
<li><p><a href="#heading-how-this-applies-to-ai-agents">How This Applies to AI Agents</a></p>
</li>
<li><p><a href="#heading-what-should-go-into-an-operational-specification">What Should Go Into an Operational Specification?</a></p>
</li>
<li><p><a href="#heading-what-should-stay-out-of-the-specification">What Should Stay Out of the Specification?</a></p>
</li>
<li><p><a href="#heading-a-practical-workflow">A Practical Workflow</a></p>
</li>
<li><p><a href="#heading-what-executable-specifications-do-not-solve">What Executable Specifications Do Not Solve</a></p>
</li>
<li><p><a href="#heading-from-automated-operations-to-verifiable-operations">From Automated Operations to Verifiable Operations</a></p>
</li>
<li><p><a href="#heading-conclusion">Conclusion</a></p>
</li>
</ul>
<h2 id="heading-automation-solves-execution-not-intent">Automation Solves Execution, Not Intent</h2>
<p>Consider a deployment pipeline. It might:</p>
<pre><code class="language-text">build the application
run tests
build an image
push the image
deploy it
wait for readiness
mark the pipeline as successful
</code></pre>
<p>If every step completes, the pipeline turns green. But what did the pipeline actually prove?</p>
<p>Usually, something like:</p>
<pre><code class="language-text">the configured steps completed successfully
</code></pre>
<p>That is useful. But it is not necessarily the same as:</p>
<pre><code class="language-text">the intended operational outcome was achieved
</code></pre>
<p>Suppose the deployment succeeds technically, but:</p>
<pre><code class="language-text">the wrong image version was deployed
only two replicas are running instead of three
the error rate increased
a required feature flag is disabled
the service is healthy but cannot reach a dependency
the rollout violated a regional constraint
</code></pre>
<p>The executor did its job. The operation still failed in a broader sense. This is the gap between <strong>execution success</strong> and <strong>operational conformance</strong>.</p>
<p>Automation tells us:</p>
<blockquote>
<p>The steps ran.</p>
</blockquote>
<p>What we often need to know is:</p>
<blockquote>
<p>Did the resulting system satisfy the intended conditions?</p>
</blockquote>
<p>Those are different questions.</p>
<h2 id="heading-operational-knowledge-is-usually-fragmented">Operational Knowledge Is Usually Fragmented</h2>
<p>Most production systems already contain operational knowledge. The problem is that it is scattered.</p>
<p>For example, a deployment rule might exist partly in:</p>
<pre><code class="language-text">GitHub Actions
Terraform
Kubernetes manifests
Grafana dashboards
PagerDuty alerts
a runbook
a ticket
a senior engineer's memory
</code></pre>
<p>One artifact might define how many replicas should exist. Another might define what error rate is acceptable. A third might explain when rollback is required. A fourth might describe which regions are allowed.</p>
<p>No single representation says:</p>
<pre><code class="language-text">This is the operation we intend to perform.
These are its constraints.
This is the evidence we require.
This is how we determine whether it succeeded.
</code></pre>
<p>That makes operations harder to reason about. It also makes automation brittle.</p>
<p>When the rules are distributed across tools, the executor often becomes the de facto specification.</p>
<p>And once that happens, it becomes difficult to ask whether the executor behaved correctly.</p>
<p>The logic that performs the action and the logic that defines success are effectively the same thing.</p>
<h2 id="heading-execution-logic-and-operational-intent-are-different-things">Execution Logic and Operational Intent Are Different Things</h2>
<p>Imagine a deployment script:</p>
<pre><code class="language-bash">kubectl set image \
deployment/orders \
orders=registry.example.com/orders:2026.09.19
</code></pre>
<p>This tells Kubernetes <strong>how to perform an action</strong>. It does not fully describe <strong>why the action is acceptable</strong>.</p>
<p>The operational intent might be closer to:</p>
<pre><code class="language-text">Deploy version 2026.09.19 of the Orders service.
Constraints:
- production must keep at least 3 available replicas
- the error rate must remain below 1%
- p95 latency must stay below 400 ms
- deployment is allowed only in us-east-1
- rollback must remain possible for 30 minutes
</code></pre>
<p>The command and the intent are related. But they are not the same artifact.</p>
<p>This distinction matters because several different executors might be able to satisfy the same intent.</p>
<p>For example:</p>
<pre><code class="language-text">Kubernetes
Nomad
a cloud deployment service
a custom deployment controller
an AI-operated platform
</code></pre>
<p>If the operational goal is expressed independently, the executor becomes replaceable.</p>
<p>If the goal is embedded inside executor-specific code, replacing the executor may also mean rediscovering the intent.</p>
<h2 id="heading-what-is-an-executable-operational-specification">What Is an Executable Operational Specification?</h2>
<p>An executable operational specification is a machine-readable description of an operational objective that can be evaluated against observed evidence.</p>
<p>At a minimum, it should answer questions like:</p>
<pre><code class="language-text">What are we trying to achieve?
What constraints must hold?
What evidence should be collected?
How do we determine whether the operation conforms?
</code></pre>
<p>For example:</p>
<pre><code class="language-yaml">operation: deploy-orders-service
target:
service: orders
version: 2026.09.19
environment: production
constraints:
minAvailableReplicas: 3
maxErrorRate: 0.01
maxP95LatencyMs: 400
region: us-east-1
evidence:
- deployedVersion
- availableReplicas
- errorRate
- p95Latency
- region
</code></pre>
<p>This is intentionally simple. The important part is not the YAML syntax.</p>
<p>The important part is that the specification defines success independently from the mechanism used to perform the deployment.</p>
<p>That gives us a structure like:</p>
<pre><code class="language-text">Operational intent
↓
Specification
↓
Executor
↓
Execution
↓
Evidence
↓
Evaluation
</code></pre>
<p>The specification becomes a stable point of reference.</p>
<h2 id="heading-a-simple-deployment-example">A Simple Deployment Example</h2>
<p>Suppose we want to deploy version <code>2026.09.19</code> of an Orders service.</p>
<p>The operation succeeds only if:</p>
<pre><code class="language-text">the correct version is running
at least three replicas are available
error rate stays below 1%
p95 latency stays below 400 ms
</code></pre>
<p>We can express that as:</p>
<pre><code class="language-typescript">type DeploymentSpec = {
service: string;
version: string;
minAvailableReplicas: number;
maxErrorRate: number;
maxP95LatencyMs: number;
};
</code></pre>
<p>For example:</p>
<pre><code class="language-typescript">const spec: DeploymentSpec = {
service: "orders",
version: "2026.09.19",
minAvailableReplicas: 3,
maxErrorRate: 0.01,
maxP95LatencyMs: 400,
};
</code></pre>
<p>Now suppose execution produces evidence:</p>
<pre><code class="language-typescript">type DeploymentEvidence = {
deployedVersion: string;
availableReplicas: number;
errorRate: number;
p95LatencyMs: number;
};
</code></pre>
<p>For example:</p>
<pre><code class="language-typescript">const evidence: DeploymentEvidence = {
deployedVersion: "2026.09.19",
availableReplicas: 3,
errorRate: 0.004,
p95LatencyMs: 280,
};
</code></pre>
<p>We can evaluate the evidence against the specification:</p>
<pre><code class="language-typescript">function conforms(
spec: DeploymentSpec,
evidence: DeploymentEvidence
): boolean {
return (
evidence.deployedVersion ===
spec.version &&
evidence.availableReplicas >=
spec.minAvailableReplicas &&
evidence.errorRate <=
spec.maxErrorRate &&
evidence.p95LatencyMs <=
spec.maxP95LatencyMs
);
}
</code></pre>
<p>Then:</p>
<pre><code class="language-typescript">console.log(
conforms(spec, evidence)
);
</code></pre>
<p>returns:</p>
<pre><code class="language-text">true
</code></pre>
<p>Now imagine the pipeline technically succeeds but only two replicas remain available:</p>
<pre><code class="language-typescript">const evidence: DeploymentEvidence = {
deployedVersion: "2026.09.19",
availableReplicas: 2,
errorRate: 0.004,
p95LatencyMs: 280,
};
</code></pre>
<p>The executor may still report success.</p>
<p>The specification does not:</p>
<pre><code class="language-text">conforms → false
</code></pre>
<p>That is the distinction we want.</p>
<h2 id="heading-turn-success-criteria-into-evidence-requirements">Turn Success Criteria into Evidence Requirements</h2>
<p>A specification becomes useful when its claims can be evaluated.</p>
<p>Suppose the specification says:</p>
<pre><code class="language-text">error rate must remain below 1%
</code></pre>
<p>Then the system needs evidence for:</p>
<pre><code class="language-text">error rate
</code></pre>
<p>If it says:</p>
<pre><code class="language-text">at least 3 replicas must remain available
</code></pre>
<p>then it needs evidence for:</p>
<pre><code class="language-text">available replica count
</code></pre>
<p>This sounds obvious, but it introduces an important discipline:</p>
<blockquote>
<p>Every operational requirement should imply some form of observable evidence.</p>
</blockquote>
<p>For example:</p>
<table>
<thead>
<tr>
<th>Requirement</th>
<th>Evidence</th>
</tr>
</thead>
<tbody><tr>
<td>correct version deployed</td>
<td>running image/version</td>
</tr>
<tr>
<td>minimum replicas available</td>
<td>replica count</td>
</tr>
<tr>
<td>error rate below threshold</td>
<td>request/error metrics</td>
</tr>
<tr>
<td>latency below threshold</td>
<td>latency metrics</td>
</tr>
<tr>
<td>correct region</td>
<td>runtime placement</td>
</tr>
<tr>
<td>no schema regression</td>
<td>schema validation result</td>
</tr>
</tbody></table>
<p>This relationship matters because vague operational goals are difficult to automate safely.</p>
<p>Consider:</p>
<pre><code class="language-text">Deploy safely.
</code></pre>
<p>What evidence proves that?</p>
<p>The statement is too ambiguous.</p>
<p>A better specification decomposes "safely" into conditions that can be checked.</p>
<h2 id="heading-why-observability-alone-is-not-enough">Why Observability Alone Is Not Enough</h2>
<p>At this point you might ask:</p>
<blockquote>
<p>Isn't this just observability?</p>
</blockquote>
<p>Not exactly. Observability helps answer:</p>
<blockquote>
<p>What is happening?</p>
</blockquote>
<p>A specification helps answer:</p>
<blockquote>
<p>What should be happening?</p>
</blockquote>
<p>Those are complementary questions.</p>
<p>A dashboard might tell you:</p>
<pre><code class="language-text">error rate = 1.4%
</code></pre>
<p>That is an observation.</p>
<p>But whether <code>1.4%</code> is acceptable depends on an expected condition.</p>
<p>The specification might say:</p>
<pre><code class="language-text">maxErrorRate = 1%
</code></pre>
<p>Now you can evaluate:</p>
<pre><code class="language-text">observed: 1.4%
expected: <= 1%
result: non-conformant
</code></pre>
<p>Without the expected condition, the metric is just a number. Without the metric, the specification cannot be verified. You need both.</p>
<p>Conceptually:</p>
<pre><code class="language-text">Specification
+
Observed evidence
↓
Evaluation
</code></pre>
<h2 id="heading-specifications-make-automation-verifiable">Specifications Make Automation Verifiable</h2>
<p>Automation without an independent specification is difficult to verify.</p>
<p>Consider a script that performs:</p>
<pre><code class="language-text">scale service
restart pods
change routing
wait
finish
</code></pre>
<p>If the script is also the only place where expected outcomes are encoded, then asking whether it behaved correctly becomes circular.</p>
<p>You are effectively asking:</p>
<blockquote>
<p>Did the automation do what the automation says it should do?</p>
</blockquote>
<p>A separate specification gives you another reference point.</p>
<p>Now you can ask:</p>
<pre><code class="language-text">What was intended?
What did the executor do?
What evidence did execution produce?
Did the evidence satisfy the specification?
</code></pre>
<p>This makes operations easier to audit and test. It also makes failures more informative.</p>
<p>Instead of:</p>
<pre><code class="language-text">deployment failed
</code></pre>
<p>you can potentially say:</p>
<pre><code class="language-text">deployment execution completed
but specification failed because:
availableReplicas:
expected >= 3
observed = 2
</code></pre>
<p>That is much more useful.</p>
<h2 id="heading-keep-the-specification-independent-from-the-executor">Keep the Specification Independent from the Executor</h2>
<p>One of the strongest properties of this model is executor independence.</p>
<p>Suppose the specification says:</p>
<pre><code class="language-text">Deploy Orders version 2026.09.19
Keep:
- >= 3 replicas
- error rate <= 1%
- p95 latency <= 400 ms
</code></pre>
<p>One executor might use Kubernetes. Another might use a managed cloud platform. Another might use a custom orchestrator.</p>
<p>The specification should not need to change simply because the executor changed.</p>
<p>Conceptually:</p>
<pre><code class="language-text"> ┌── Kubernetes Executor
Specification ───┼── Cloud Executor
├── Custom Executor
└── AI Agent
</code></pre>
<p>Each executor produces evidence. Each execution is evaluated against the same intent.</p>
<p>That gives you a useful separation:</p>
<pre><code class="language-text">what should happen
</code></pre>
<p>from:</p>
<pre><code class="language-text">how it happens
</code></pre>
<p>This separation is common in other areas of software engineering. Interfaces separate callers from implementations. SQL separates queries from storage mechanics. Desired-state systems separate target state from reconciliation logic.</p>
<p>Operational specifications apply a similar idea to operational workflows.</p>
<h2 id="heading-a-minimal-typescript-model">A Minimal TypeScript Model</h2>
<p>A simple generic model might look like this:</p>
<pre><code class="language-typescript">type Constraint<T> = {
name: string;
evaluate(
evidence: T
): boolean;
};
type OperationalSpec<T> = {
name: string;
constraints: Constraint<T>[];
};
</code></pre>
<p>For deployment evidence:</p>
<pre><code class="language-typescript">type Evidence = {
version: string;
replicas: number;
errorRate: number;
};
</code></pre>
<p>You can define:</p>
<pre><code class="language-typescript">const deploymentSpec:
OperationalSpec<Evidence> = {
name: "deploy-orders",
constraints: [
{
name: "correct-version",
evaluate: (evidence) =>
evidence.version ===
"2026.09.19",
},
{
name: "minimum-replicas",
evaluate: (evidence) =>
evidence.replicas >= 3,
},
{
name: "error-rate",
evaluate: (evidence) =>
evidence.errorRate <= 0.01,
},
],
};
</code></pre>
<p>Then evaluate every constraint:</p>
<pre><code class="language-typescript">function evaluate<T>(
spec: OperationalSpec<T>,
evidence: T
) {
return spec.constraints.map(
(constraint) => ({
constraint: constraint.name,
passed:
constraint.evaluate(
evidence
),
})
);
}
</code></pre>
<p>For:</p>
<pre><code class="language-typescript">const evidence: Evidence = {
version: "2026.09.19",
replicas: 2,
errorRate: 0.003,
};
</code></pre>
<p>you might get:</p>
<pre><code class="language-text">correct-version PASS
minimum-replicas FAIL
error-rate PASS
</code></pre>
<p>That is more useful than a single generic success or failure flag. It tells you exactly which part of the intended operation did not conform.</p>
<h2 id="heading-how-this-applies-to-cicd">How This Applies to CI/CD</h2>
<p>CI/CD systems already contain some declarative elements.</p>
<p>For example:</p>
<pre><code class="language-yaml">steps:
- test
- build
- deploy
</code></pre>
<p>But these steps mostly describe execution order. A specification can add operational expectations around them.</p>
<p>For example:</p>
<pre><code class="language-text">Deployment objective:
release version X
Constraints:
tests passed
artifact digest matches approved build
minimum replicas remain available
error rate stays below threshold
rollback remains possible
</code></pre>
<p>The pipeline still performs the work. The specification defines the conditions the pipeline must satisfy. This also makes pipeline replacement easier.</p>
<p>If you move from:</p>
<pre><code class="language-text">GitHub Actions
</code></pre>
<p>to:</p>
<pre><code class="language-text">GitLab CI
</code></pre>
<p>or:</p>
<pre><code class="language-text">Argo
</code></pre>
<p>the executor changes.</p>
<p>The operational objective does not necessarily need to.</p>
<h2 id="heading-how-this-applies-to-infrastructure">How This Applies to Infrastructure</h2>
<p>Infrastructure-as-code already gives us desired state. That is closely related to this idea.</p>
<p>For example:</p>
<pre><code class="language-hcl">resource "aws_instance" "app" {
instance_type = "t3.medium"
}
</code></pre>
<p>But operational intent often extends beyond configuration state.</p>
<p>You may also care about:</p>
<pre><code class="language-text">service availability
cost limits
regional restrictions
security controls
capacity
latency
backup freshness
</code></pre>
<p>Those constraints may live outside the IaC definition. An operational specification can bring them together.</p>
<p>For example:</p>
<pre><code class="language-text">Provision application environment
Required:
3 instances
region = us-east-1
monthly projected cost < $500
encryption enabled
backup age < 24h
</code></pre>
<p>The executor may use Terraform.</p>
<p>The evidence may come from:</p>
<pre><code class="language-text">cloud APIs
cost systems
security scanners
backup metadata
</code></pre>
<p>The specification gives those sources a common purpose.</p>
<h2 id="heading-how-this-applies-to-incident-response">How This Applies to Incident Response</h2>
<p>Incident response is another place where execution and intent are often mixed together.</p>
<p>A runbook might say:</p>
<pre><code class="language-text">restart service
clear cache
scale replicas
</code></pre>
<p>But the actual operational objective might be:</p>
<pre><code class="language-text">restore checkout availability
while:
avoiding duplicate payments
preserving order state
keeping error rate below threshold
</code></pre>
<p>That difference matters.</p>
<p>If restarting the service does not restore checkout availability, the runbook technically executed but the operation failed.</p>
<p>An executable specification could express recovery conditions:</p>
<pre><code class="language-text">checkout success rate > 99%
payment duplication = 0
queue backlog < threshold
error rate < 1%
</code></pre>
<p>Then incident automation can be evaluated by the result it achieves, not just the actions it performs.</p>
<h2 id="heading-how-this-applies-to-ai-agents">How This Applies to AI Agents</h2>
<p>This becomes even more important with AI agents. Traditional automation usually follows predefined execution logic.</p>
<p>An AI agent may choose the execution path dynamically. For example, an operations agent might decide to:</p>
<pre><code class="language-text">inspect metrics
restart a service
change capacity
modify a feature flag
reroute traffic
</code></pre>
<p>The exact sequence may vary from one incident to another. That makes executor-level validation harder.</p>
<p>You cannot always verify the agent by checking whether it followed one exact script. But you can still verify operational intent.</p>
<p>For example:</p>
<pre><code class="language-text">Objective:
restore API availability
Constraints:
do not disable authentication
do not lose queued requests
error rate < 1%
p95 latency < 500 ms
cost increase < 20%
</code></pre>
<p>The agent may choose different actions and the specification remains stable.</p>
<p>That creates a useful control structure:</p>
<pre><code class="language-text">Human / organizational intent
↓
Operational specification
↓
Agent
↓
Actions
↓
Evidence
↓
Evaluation
</code></pre>
<p>The more autonomous execution becomes, the more valuable this separation becomes.</p>
<h2 id="heading-what-should-go-into-an-operational-specification">What Should Go Into an Operational Specification?</h2>
<p>A useful specification often includes several categories.</p>
<h3 id="heading-objective">Objective</h3>
<p>What should be achieved?</p>
<p>For example:</p>
<pre><code class="language-text">Deploy Orders service version 2026.09.19
</code></pre>
<h3 id="heading-scope">Scope</h3>
<p>Where does the operation apply?</p>
<p>For example:</p>
<pre><code class="language-text">environment: production
region: us-east-1
service: orders
</code></pre>
<h3 id="heading-constraints">Constraints</h3>
<p>What must remain true?</p>
<p>For example:</p>
<pre><code class="language-text">available replicas >= 3
error rate <= 1%
p95 latency <= 400 ms
</code></pre>
<h3 id="heading-required-evidence">Required Evidence</h3>
<p>What must be observed?</p>
<p>For example:</p>
<pre><code class="language-text">running version
replica count
error rate
latency
</code></pre>
<h3 id="heading-evaluation-rules">Evaluation Rules</h3>
<p>How do we decide whether execution conforms?</p>
<p>For example:</p>
<pre><code class="language-text">version must match exactly
replicas must be >= 3
error rate must be <= 0.01
</code></pre>
<h3 id="heading-recovery-conditions">Recovery Conditions</h3>
<p>What should happen if conformance fails?</p>
<p>For example:</p>
<pre><code class="language-text">stop rollout
restore previous route
require human approval
</code></pre>
<p>Not every specification needs all of these.</p>
<p>But separating them makes operational intent much clearer.</p>
<h2 id="heading-what-should-stay-out-of-the-specification">What Should Stay Out of the Specification?</h2>
<p>A specification should not become another implementation script. That means avoiding unnecessary executor-specific mechanics.</p>
<p>For example, this is probably too implementation-specific:</p>
<pre><code class="language-text">run kubectl command X
wait 10 seconds
call endpoint Y
run shell command Z
</code></pre>
<p>Those belong in an executor. The specification should focus on the desired operational outcome.</p>
<p>For example:</p>
<pre><code class="language-text">service version = 2026.09.19
available replicas >= 3
health checks passing
</code></pre>
<p>A useful rule is:</p>
<blockquote>
<p>If changing the execution tool forces you to rewrite the specification, the specification may contain too much implementation detail.</p>
</blockquote>
<p>Some executor-specific constraints are unavoidable. But the default should be to keep intent and mechanism separate.</p>
<h2 id="heading-a-practical-workflow">A Practical Workflow</h2>
<p>If I were introducing executable operational specifications into an existing system, I would start small.</p>
<h3 id="heading-1-pick-one-important-operation">1. Pick One Important Operation</h3>
<p>For example:</p>
<pre><code class="language-text">deploy service
rotate certificate
restore backup
scale worker pool
</code></pre>
<h3 id="heading-2-write-down-the-objective">2. Write Down the Objective</h3>
<p>Ask:</p>
<blockquote>
<p>What does success actually mean?</p>
</blockquote>
<p>Not:</p>
<blockquote>
<p>Which commands do we run?</p>
</blockquote>
<h3 id="heading-3-identify-constraints">3. Identify Constraints</h3>
<p>For example:</p>
<pre><code class="language-text">minimum availability
maximum error rate
security requirements
regional restrictions
cost limits
</code></pre>
<h3 id="heading-4-identify-evidence">4. Identify Evidence</h3>
<p>For each constraint, ask:</p>
<blockquote>
<p>What observation would prove or disprove this condition?</p>
</blockquote>
<h3 id="heading-5-separate-the-executor">5. Separate the Executor</h3>
<p>Keep the mechanism that performs the work independent from the specification.</p>
<h3 id="heading-6-evaluate-after-execution">6. Evaluate After Execution</h3>
<p>Collect evidence and compare it against the specification.</p>
<h3 id="heading-7-report-conformance">7. Report Conformance</h3>
<p>Prefer:</p>
<pre><code class="language-text">3 constraints passed
1 constraint failed
</code></pre>
<p>over:</p>
<pre><code class="language-text">operation failed
</code></pre>
<h3 id="heading-8-improve-the-specification">8. Improve the Specification</h3>
<p>Missing evidence and ambiguous constraints will become visible quickly.</p>
<p>That is useful.</p>
<p>The specification becomes better as operational knowledge becomes explicit.</p>
<h2 id="heading-what-executable-specifications-do-not-solve">What Executable Specifications Do Not Solve</h2>
<p>Executable specifications are not a complete operations architecture.</p>
<p>They do not automatically solve:</p>
<pre><code class="language-text">bad requirements
incorrect metrics
missing observability
distributed transactions
security failures
poor executor implementations
organizational ownership
conflicting business goals
</code></pre>
<p>They also introduce their own risks:</p>
<ul>
<li><p>A bad specification can encode the wrong objective.</p>
</li>
<li><p>An incomplete specification can create false confidence.</p>
</li>
<li><p>A stale specification can become another source of drift.</p>
</li>
<li><p>And not every operational decision can be reduced to a simple threshold.</p>
</li>
</ul>
<p>Human judgment still matters. The goal is not to eliminate judgment. The goal is to make operational intent more explicit and more testable.</p>
<h2 id="heading-from-automated-operations-to-verifiable-operations">From Automated Operations to Verifiable Operations</h2>
<p>Software operations have spent years becoming more automated. That trend will continue. But increasing automation creates a new question:</p>
<blockquote>
<p>How do we know the automation achieved the right outcome?</p>
</blockquote>
<p>Execution logs, pipeline success, and agent confidence are not enough. We need something to compare execution against. That is where an executable operational specification becomes useful.</p>
<p>It gives us:</p>
<pre><code class="language-text">intent
↓
constraints
↓
evidence requirements
↓
execution
↓
observed evidence
↓
evaluation
</code></pre>
<p>That structure turns an operation from:</p>
<pre><code class="language-text">something happened
</code></pre>
<p>into:</p>
<pre><code class="language-text">something happened,
we know what was expected,
we collected evidence,
and we can evaluate the result.
</code></pre>
<p>That is a much stronger foundation for automation.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>The more software operations we automate, the more important it becomes to separate <strong>what we want</strong> from <strong>how a tool executes it</strong>.</p>
<p>Pipelines are executors.</p>
<p>Infrastructure tools, scripts, and AI agents are executors. They can all perform actions.</p>
<p>But the operational objective should exist independently from the mechanism carrying it out.</p>
<p>An executable operational specification gives us a way to describe that objective in terms of:</p>
<pre><code class="language-text">desired outcome
constraints
required evidence
evaluation rules
</code></pre>
<p>Then execution becomes something we can verify instead of merely observe.</p>
<p>This matters today for deployments, infrastructure, and incident response.</p>
<p>It will matter even more as operational systems become increasingly autonomous.</p>
<p>Because automation can tell us that an action was executed.</p>
<p>What we really need to know is whether the system ended up where it was supposed to be.</p>