diff --git a/src/pages/blog/human-in-the-loop-seam.astro b/src/pages/blog/human-in-the-loop-seam.astro
index 9e570d2..b09fd8f 100644
--- a/src/pages/blog/human-in-the-loop-seam.astro
+++ b/src/pages/blog/human-in-the-loop-seam.astro
@@ -4,7 +4,7 @@ import BaseLayout from '../../layouts/BaseLayout.astro';
Human-in-the-loop is a phrase everyone uses. Almost nobody uses it correctly.
- I have been in four meetings this quarter where someone said, "the human is in the loop," and meant, "a person reviews the diff before the merge." I have been in three more where someone said the same thing and meant, "a person approves the deploy to production." I have been in two where the speaker meant, "we are researching this." All seven were talking about different systems, and none of them were using the phrase the way I would.
-
- Here is how I would use it. A human is in the loop at a seam. The seam is a place where the system is underdetermined. The seam is the place where the spec does not say what the right answer is, and the factory has to either ask or guess. The human's job is to be the answer to the questions the spec cannot answer. The factory's job is to surface those questions, at the right time, in the right form, to a human who can answer them.
+ A human is in the loop at a seam. The seam is a place where the system is underdetermined. The seam is the place where the spec does not say what the right answer is, and the factory has to either ask or guess. The human's job is to be the answer to the questions the spec cannot answer. The factory's job is to surface those questions, at the right time, in the right form, to a human who can answer them.
The wrong place to put the human
- The wrong place to put the human is in the implementation. If the spec is complete enough to be compiled, the factory should compile it. Adding a human to "review the code" before the merge is putting the human in a place where the human is duplicating the factory's work. The factory just generated the artifact from the spec. The human reviewing the artifact is asking "did the factory do what the spec said." The factory can answer that question with a provenance graph. The human is not adding information; the human is adding latency.
+ The wrong place to put the human is in the implementation. If the spec is complete enough to be compiled, the factory should compile it. Adding a human to "review the code" before the merge is putting the human in a place where the human is duplicating the factory's work. The factory just generated the artifact from the spec. The human reviewing the artifact is asking whether the factory did what the spec said. The factory can answer that question with a provenance graph. The human is not adding information. The human is adding latency.
- I have watched teams do this. I have watched the human review take two days. I have watched the human approve what the factory produced. I have watched the human introduce small changes that break the spec. I have watched the spec and the artifact drift apart because the human was the seam, and the seam was in the wrong place. The seam should have been in the spec, not in the review.
+ Teams that put the human in the review get a familiar pattern. The review takes two days. The human approves what the factory produced. The human introduces small changes that break the spec. The spec and the artifact drift apart because the human was the seam, and the seam was in the wrong place. The seam should have been in the spec, not in the review.
The right place to put the human
The right place to put the human is on the seam where the spec is underdetermined. There are three of these seams in a dark factory, and they are different from each other.
- The first seam is the policy seam. The policy is the human's signature on the system. The policy says, "this kind of artifact, in this kind of environment, is allowed to promote." The policy is underdetermined because the policy is a human value judgment, not a derived fact. The factory cannot derive what is allowed; the factory can only enforce what has been declared. The human is the one who declares. The factory is the one who enforces.
+ The policy seam. The policy is the human's signature on the system. The policy says: this kind of artifact, in this kind of environment, is allowed to promote. The policy is underdetermined because the policy is a human value judgment, not a derived fact. The factory cannot derive what is allowed. The factory can only enforce what has been declared. The human declares. The factory enforces.
- The second seam is the spec seam. The spec is underdetermined in places. The spec describes intent. The intent does not always fully constrain the artifact. The factory will produce a correct artifact from an underdetermined spec; the artifact will be one of many correct artifacts, and the choice between them is a taste decision. The human is the one who has the taste. The factory surfaces the choice. The human makes the call. The spec is updated with the call. The factory's next artifact is constrained by the updated spec.
+ The spec seam. The spec is underdetermined in places. The spec describes intent. The intent does not always fully constrain the artifact. The factory will produce a correct artifact from an underdetermined spec. The artifact will be one of many correct artifacts, and the choice between them is a taste decision. The human has the taste. The factory surfaces the choice. The human makes the call. The spec is updated with the call. The factory's next artifact is constrained by the updated spec.
- The third seam is the audit seam. The audit is the place where the human checks that the factory did what the spec said, and the spec was the right call. The audit is the only seam where the human gets to ask the second-order question. The audit is the only seam where the human can change the spec. The audit is the only seam that closes the loop. Without the audit, the factory is the only one learning. With the audit, the human is learning too.
+ The audit seam. The audit is the place where the human checks that the factory did what the spec said, and the spec was the right call. The audit is the only seam where the human gets to ask the second-order question. The audit is the only seam where the human can change the spec. The audit is the only seam that closes the loop. Without the audit, the factory is the only one learning. With the audit, the human is learning too.
What the human looks like at each seam
- At the policy seam, the human is a writer. The human writes the policy. The policy is a small artifact, versioned, signed, reviewed. The policy is not code; the policy is intent. The factory is the executor. The human does not need to know the factory's internals. The human needs to know what the factory is for.
+ At the policy seam, the human is a writer. The human writes the policy. The policy is a small artifact, versioned, signed, reviewed. The policy is not code. The policy is intent. The factory is the executor.
- At the spec seam, the human is an editor. The human updates the spec to capture a decision the factory surfaced. The spec is a living document. The spec is not a wiki; the spec is a file the factory reads. The human does not need to be a programmer. The human needs to be a domain expert who can describe what they want in the domain's vocabulary.
+ At the spec seam, the human is an editor. The human updates the spec to capture a decision the factory surfaced. The spec is a living document. The spec is not a wiki. The spec is a file the factory reads. The human does not need to be a programmer. The human needs to be a domain expert who can describe what they want in the domain's vocabulary.
- At the audit seam, the human is a reader. The human reads the factory's output. The human reads the spec the factory consumed. The human reads the policy the factory enforced. The human asks, "did the right thing happen?" The human's answer goes into the next spec, the next policy, the next audit. The human's answer is the loop closing.
+ At the audit seam, the human is a reader. The human reads the factory's output, the spec the factory consumed, the policy the factory enforced. The human asks: did the right thing happen? The human's answer goes into the next spec, the next policy, the next audit.
Why this is harder than it looks
- The hard part is that the seams are not labeled in the artifact. The factory produces an artifact. The artifact has a hash, a signature, a provenance graph. None of those say "this decision was made by a human at the spec seam." The factory has to be designed to make the seam visible. The factory has to be designed to ask the question at the right time. The factory has to be designed to wait for the answer, or to ship a default, depending on the policy.
+ The seams are not labeled in the artifact. The artifact has a hash, a signature, a provenance graph. None of those say "this decision was made by a human at the spec seam." The factory has to be designed to make the seam visible. The factory has to be designed to ask the question at the right time. The factory has to be designed to wait for the answer, or to ship a default, depending on the policy.
- This is the work of designing a dark factory. The work is not the build pipeline. The work is the seam management. The work is the question of when to ask, when to wait, and when to ship.
+ This is the work of designing a dark factory. The work is not the build pipeline. The work is the seam management.
- Most teams skip this work. Most teams put the human in the implementation review, because that is the obvious place. The obvious place is the wrong place. The wrong place is the place where the human is duplicating the factory's work, and the spec is drifting from the artifact, and the audit cannot find the seam because the seam is in the wrong layer.
+ The single rule
- The single sentence
-
- If you remember one thing from this post, remember this. The human belongs on the seam where the spec is underdetermined, not on the seam where the implementation is risky.
+ The human belongs on the seam where the spec is underdetermined, not on the seam where the implementation is risky.
Most teams have it backwards. Most teams put the human where the implementation is risky. Most teams have the human review the code, the test, the deploy. Most teams are duplicating the factory's work, and they do not know it. The factory can review the code. The factory can run the test. The factory can ship the deploy. The factory cannot decide what the code should be. The factory cannot decide what the test should check. The factory cannot decide what the deploy should do.
diff --git a/src/pages/blog/light-vs-dark-factories.astro b/src/pages/blog/light-vs-dark-factories.astro
index a93f8b2..3ff697c 100644
--- a/src/pages/blog/light-vs-dark-factories.astro
+++ b/src/pages/blog/light-vs-dark-factories.astro
@@ -4,7 +4,7 @@ import BaseLayout from '../../layouts/BaseLayout.astro';
- Pure dark factories are for vendors. Pure light factories are for teams that do not trust their pipeline. The real work is in between.
+ Pure dark factories are for vendors. Pure light factories are for teams that do not trust their pipeline. The interesting engineering is in between.
- I have watched three engineering organizations try to go fully dark. Two of them rolled back inside six months. The third one shipped, and the on-call rotation got three times worse, because every failure that used to be caught at a human review was now a 3 AM page.
+ The ends are easy to describe. The middle is where the work is.
- I have watched four organizations try to stay fully light. One of them is still running. The other three quietly automated the gate anyway and stopped calling it "human review."
-
- The lesson is not "dark is bad" or "light is bad." The lesson is that the ends are easy to describe and the middle is where the engineering happens.
-
- The light factory, named
+ The light factory
A light factory is what most teams have when they say "we have a CI/CD pipeline." Code is written. Tests run. A human reviews the diff. A human approves the merge. A human triggers the deploy. A human watches the deploy. A human confirms the deploy is healthy.
@@ -37,41 +33,39 @@ import BaseLayout from '../../layouts/BaseLayout.astro';
This is the team that hits a wall at fifteen engineers. Not because they cannot write code. Because they cannot move code through the human gates fast enough to keep up with the demand.
- The dark factory, named
+ The dark factory
- A dark factory replaces the human handoffs with policy. The spec is the input. The policy is the rules. The artifact is the output. Nothing in the middle requires a person to act.
+ A dark factory replaces the human handoffs with policy. Spec in, artifact out. The policy is the rules. Nothing in the middle requires a person to act.
- This is the team that ships 200 deploys a day. The team is small. The team is bored. The product is moving fast, except when the policy is wrong, at which point the product is moving very fast in a bad direction and no one notices until the customer does.
+ This is the team that ships 200 deploys a day. The team is small. The product is moving fast, except when the policy is wrong. When the policy is wrong, the product moves fast in a bad direction and no one notices until the customer does.
- What the middle looks like
+ The middle is a position, not a transition
- The middle is not a transition. The middle is a position. It has three properties:
+ The middle has three properties.
- - The pipeline is the gate. The merge to main is not gated by a reviewer; it is gated by a policy expression. The policy is code. The code is reviewed.
- - The human is the auditor. The human's job is to inspect what the pipeline produced, not to be the pipeline. The audit is sampling-based, not exhaustive. The audit answers "did the policy work this week," not "is this merge correct."
- - The spec is the source of truth. The code is generated. The test suite is generated. The deploy manifest is generated. The thing the human writes is the spec, and the spec is what gets reviewed.
+ - The pipeline is the gate. The merge to main is not gated by a reviewer. It is gated by a policy expression. The policy is code. The code is reviewed.
+ - The human is the auditor. The human inspects what the pipeline produced. The audit is sampling-based, not exhaustive. The audit answers "did the policy work this week," not "is this merge correct."
+ - The spec is the source of truth. The code is generated. The test suite is generated. The deploy manifest is generated. The thing the human writes is the spec, and the spec is what gets reviewed.
- None of this is new. What is new is that the spec is now something a non-engineer can write in a domain-specific language, the compilation is fast enough to run on every commit, and the audit surface is small enough that one person can look at all of it in an afternoon.
+ What is new is the surface area. The spec is something a non-engineer can write in a domain-specific language. The compilation is fast enough to run on every commit. The audit surface is small enough that one person can look at all of it in an afternoon.
Why the middle is harder than either end
- At the dark end, you only have to maintain the policy. The policy is the system.
+ At the dark end, you maintain the policy. The policy is the system.
- At the light end, you only have to maintain the people. The people are the system.
+ At the light end, you maintain the people. The people are the system.
- In the middle, you maintain the policy, you maintain the people, and you maintain the seam between them. The seam is the part that nobody talks about in conference talks. The seam is the part that fails. The seam is the part that determines whether your factory runs at all.
+ In the middle, you maintain the policy, you maintain the people, and you maintain the seam between them. The seam is what fails. The seam is what determines whether the factory runs at all.
A seam is a place where the human's authority and the pipeline's authority have to be reconciled. Promotion to production is a seam. Incident response is a seam. The decision to roll back is a seam. The decision to skip a test is a seam. The decision to add a new test is a seam.
- Each seam has a default. The default is what the pipeline does when no human acts. The default has to be defensible. If the default is "ship," and the human does not act, the system has shipped. If the default is "do not ship," the system has stalled.
-
- You will be tempted to make every default "do not ship." That is the light factory wearing a different uniform. You will be tempted to make every default "ship." That is the dark factory pretending it does not have humans. The middle is the work of choosing the default for each seam, and defending the choice.
+ Each seam has a default. The default is what the pipeline does when no human acts. If the default is "ship" and the human does not act, the system has shipped. If the default is "do not ship," the system has stalled. The default has to be defensible. The default has to be chosen.
How to know which position you are in
- Ask one question: when the human does nothing, does the system ship or stall?
+ One question. When the human does nothing, does the system ship or stall?
If the answer is ship, you are dark. If the answer is stall, you are light. If the answer is "it depends on which stage," you are in the middle. The middle is where you want to be, and the middle is the part you have to design on purpose.
diff --git a/src/pages/blog/operator-patterns-for-software-factories.astro b/src/pages/blog/operator-patterns-for-software-factories.astro
index 1d246c5..2fd42cd 100644
--- a/src/pages/blog/operator-patterns-for-software-factories.astro
+++ b/src/pages/blog/operator-patterns-for-software-factories.astro
@@ -4,7 +4,7 @@ import BaseLayout from '../../layouts/BaseLayout.astro';
An operator is a control loop with a domain in it. That is the whole definition. Everything else is implementation.
- I have written thirteen production operators. Six of them are still running. The other seven were replaced by better versions of themselves, which is the correct outcome. Here is what I learned.
+ A factory is composed of operators, each one owning a seam. The seams are the spec, the build, the test, the deploy, the promote, the rollback. Each seam gets one operator. The operators do not call each other. The operators watch the cluster and react.
- The operator is the unit of automation in a software factory. The factory does not have one operator; the factory is composed of operators, each one owning a seam. The seams are: the spec, the build, the test, the deploy, the promote, the rollback. Each seam gets one operator. The operators do not call each other. The operators watch the cluster and react.
+ The spec operator
- That is the design pattern. The rest of this post is the implementation choices inside it.
+ The spec operator owns a SoftwareSpec custom resource. A human writes the spec. The operator validates the spec. The operator emits a normalized form into the cluster, signed, with provenance attached.
- Pattern 1: the spec operator
+ The spec operator is the only operator that takes human input. Every other operator in the factory watches a spec-shaped resource. The spec operator is the auditor's first stop: what specs exist, who wrote them, what versions are live.
- The spec operator owns a SoftwareSpec custom resource. A human writes the spec. The operator validates the spec. The operator emits a normalized form of the spec into the cluster, signed, with provenance metadata attached.
+ Keep it thin. The spec operator's job is to validate and emit. It does not run tests. It does not trigger builds. It does not know what a build is.
- The spec operator is the only operator that takes human input. Every other operator in the factory watches a spec-shaped resource. The spec operator is the auditor's first stop: "what specs exist, who wrote them, what versions are live."
-
- I write these in Go with controller-runtime. I keep them thin. The spec operator's job is to validate and emit. It does not run tests. It does not trigger builds. It does not know what a build is.
-
- Pattern 2: the build operator
+ The build operator
The build operator watches a normalized spec. The build operator produces a BuildArtifact custom resource containing the hash, the SBOM, the provenance graph, and the signed manifest. The build operator does not know about clusters. The build operator does not know about deployment. The build operator's job is to take a spec and produce an artifact that other operators can verify.
- This is the operator I have rewritten the most. The first three versions did too much. They knew about tests, about deployment, about promotion. Every time they knew too much, they became unmaintainable. The current version is 400 lines of Go. It does one thing. It is boring. Boring is what I want from a build operator.
+ The build operator is the operator that wants to know too much. Every time the build operator learns about tests, about deployment, about promotion, it becomes unmaintainable. The build operator that survives is the one that does one thing. Boring is what you want from a build operator.
- Pattern 3: the deploy operator
+ The deploy operator
The deploy operator watches a signed BuildArtifact and a target environment. The deploy operator produces a Deployment custom resource. The deploy operator's job is to take a verified artifact and put it in a verified place.
- The deploy operator is where most teams make their first mistake. They have the deploy operator trigger a kubectl apply, or call a Helm release, or invoke a serverless deploy. All of these are wrong. The deploy operator should produce a declarative resource, and a separate, generic Kubernetes primitive (Deployment, StatefulSet, whatever) should reconcile to that resource. The deploy operator is the translator. The cluster is the executor.
+ The deploy operator is where most teams make their first mistake. They have the deploy operator trigger a kubectl apply, or call a Helm release, or invoke a serverless deploy. All of these are wrong. The deploy operator should produce a declarative resource, and a separate, generic Kubernetes primitive should reconcile to that resource. The deploy operator is the translator. The cluster is the executor.
- Why this matters: when the deploy operator produces a resource, the resource can be inspected, audited, rolled back, frozen. When the deploy operator pushes to a remote API, none of those things are true. The remote API is the system. The remote API is what the auditor cannot see.
+ When the deploy operator produces a resource, the resource can be inspected, audited, rolled back, frozen. When the deploy operator pushes to a remote API, none of those things are true. The remote API is the system. The remote API is what the auditor cannot see.
- Pattern 4: the policy operator
+ The policy operator
The policy operator is the seam. The policy operator watches deployments and consults a policy bundle. The policy bundle is a versioned, signed set of rules. The policy operator's job is to decide which deployments are allowed to promote to which environment.
- This is the operator that is hardest to write. The first version I wrote used OPA. The second version I wrote used Cedar. The third version I wrote used a custom DSL because none of the policy engines could express what I needed without a three-page Rego file that nobody on the team could read. The fourth version is the one in production. It is 800 lines. It is the most important operator in the factory.
+ The policy operator is the one that is hardest to write. The off-the-shelf policy engines (OPA, Cedar, Rego, the rest) cannot express what most factories need without a policy file that nobody on the team can read. The version that survives is a custom DSL, small enough to fit on a page, expressive enough to encode the rules the business actually needs.
- The policy operator is where the human's authority lives in a dark factory. The policy is the human's signature on the system. The policy operator enforces that signature. Everything else in the factory can be fully dark; the policy operator is the part that cannot be.
+ The policy operator is where the human's authority lives in a dark factory. The policy is the human's signature on the system. The policy operator enforces that signature. Everything else in the factory can be fully dark. The policy operator is the part that cannot be.
- The pattern I will not use again
+ The anti-pattern
- Early on, I tried to write one operator per application. One operator per microservice. The factory had 30 operators, and every one of them was a slightly different shape. The team spent more time learning the operators than they spent shipping product.
+ One operator per application. One operator per microservice. The factory has 30 operators, and every one of them is a slightly different shape. The team spends more time learning the operators than they spend shipping product.
- The mistake was treating the operator as a wrapper around the application. The operator is not a wrapper. The operator is a wrapper around a type of change. The factory has one operator per type of change, regardless of how many applications use that type. The application is a parameter, not a peer.
+ The mistake is treating the operator as a wrapper around the application. The operator is not a wrapper around the application. The operator is a wrapper around a type of change. The factory has one operator per type of change, regardless of how many applications use that type. The application is a parameter, not a peer.
The composition rule
@@ -71,12 +67,6 @@ import BaseLayout from '../../layouts/BaseLayout.astro';
This rule sounds like bureaucracy. It is not. It is what makes the factory auditable. When operator A triggers operator B, the trigger is a side effect of state, and the state is in etcd, and the etcd is queryable. When operator A calls operator B's API, the trigger is hidden in operator A's code, and the audit is impossible. Choose the auditable version. The factory you build next year will thank you.
- What this gets you
-
- A factory built from these patterns has a single engineer operating it. The single engineer is the spec writer and the policy writer. The single engineer does not write operators day to day; the operators are stable. The single engineer writes the policy when the business changes. The single engineer writes the spec when the product changes.
-
- That is the leverage. A single engineer, writing two kinds of artifact, with a factory that ships 200 times a day. The factory is the platform team's product. The single engineer is the platform team's customer. The factory is dark. The customer is not.
-