The block step: where the machine has to stop and ask
An autonomous pipeline should do almost everything on its own. The word 'almost' is the entire design. Somewhere near the end, before the irreversible thing, there has to be a gate the machine cannot open by itself.
By Andrew Pyle
There's a purist view of automation that treats any point where a human has to intervene as a defect — a piece you haven't finished automating yet. I used to hold that view. Building an autonomous command center cured me of it, because the more capable the automation got, the more obvious it became that the goal was never zero human involvement. The goal was to spend the human's attention on exactly the decisions that deserve it, and nothing else.
The purest expression of that is the block step: a pipeline that does everything by itself, right up to a single deliberate stop placed just before the point of no return. Not a stop because the machine can't proceed. A stop because it shouldn't — not without a person saying go.
01Gate the irreversible
Automate the reversible, gate the irreversible
The line I draw is between actions I can take back and actions I can't. Everything reversible, the machine should do freely — build, test, prepare, stage, compute the plan. None of that changes the world in a way I'd lose sleep over, so requiring my approval for it would just be friction that trains me to click through without reading. Approval fatigue is real, and the way you cause it is by asking for consent on things that didn't need it.
The irreversible actions are the opposite. Shipping to production, sending something outward, deleting something for good — these are the moves where a wrong call is expensive and can't be quietly undone. Those are the ones that earn a gate. By reserving the human stop for exactly the actions that can't be taken back, each approval carries real weight, which is what keeps me actually reading it instead of reflexively approving.
The mistake people make is treating this as a spectrum where more gates mean more safety. It's the reverse. Every gate on a reversible step spends a little of the attention you'll need at the one gate that matters, and attention doesn't refill. A pipeline studded with confirmations doesn't make me careful; it teaches me that the fastest way through the work is to approve without looking. The discipline is subtractive — remove every gate that isn't guarding something irreversible, so the one that remains still commands a real read.
Ask for approval on everything and people approve without reading. Ask only before the irreversible, and the approval means something again.
02A wall, not a promise
The gate the machine can't open itself
The mechanical shape is a pipeline that runs its automated steps to completion and then hits a block — a step that isn't a script the machine runs, but a door only a person can open. The build is done, the tests are green, everything is staged and ready; the deploy sits one click away, and that click is mine. The machine did all the work and stopped precisely where its authority ends. It cannot proceed to the irreversible part by itself, by construction, no matter what it thinks it should do.
What I love about this shape is that it makes the boundary explicit and physical rather than a matter of trust. I don't have to believe the automation will restrain itself; the restraint is built into the pipeline as a place where it structurally cannot continue. The safety isn't a promise, it's a wall — and walls, unlike promises, hold up when everyone's tired and the pressure's on.
03A real line of YAML
The block step is a real line of YAML
This isn't a metaphor in my pipeline; it's a literal step type. Buildkite ships a step called `block`, and it does exactly one thing — it stops the pipeline and waits for a person. My production pipeline runs the backend checks, the tests, and two frontend builds, all unattended, and then it reaches a step that is not a script at all:
- block: ":rocket: Deploy to production?"
key: deploy-approval
depends_on:
- backend-checks
- frontend-build
- label: ":rocket: Deploy to Production"
key: deploy
depends_on: deploy-approval # the deploy names the gate as its preconditionThe whole pattern lives in that `depends_on`. The step that ships to production names the block as its own precondition, and Buildkite will not run the deploy until the block is resolved. The only thing that resolves a block is a person clicking it in the UI, or an authenticated call to the block API — an identity the pipeline does not possess. The machine that built everything cannot click its own button. That is the wall, made out of configuration.
What I keep admiring is how thin the boundary is. Everything above the block is scripts; that one step is a door. The reversible work all runs on its own, and the door sits at the exact seam where the next commands become `git reset --hard origin/main` on the live box, real migrations, and a gunicorn restart. The gate isn't near the irreversible border. It is the irreversible border, drawn as a step.
04When the gate opens itself
The gate that knows when to open on its own
There is one deliberate hole in the wall, and it's the part of the design I'm most pleased with, because it proves the gate is a judgment and not a reflex. A nightly scheduled build refreshes the site's verified dev-activity stats and redeploys while I'm asleep. Forcing it to wait for me to click a button every night would defeat its purpose. So the block step carries a condition: `if: build.branch == "main" && build.source != "schedule"`. On a human merge the gate is present; on the nightly scheduled build it simply isn't there, and the pipeline deploys itself.
That looks like it contradicts everything above — I just spent three sections insisting the irreversible gets a gate — until you look at what makes the unattended deploy safe enough to skip one. `main` is the vetted branch; nothing lands on it without passing the merge review first. And the deploy step has health checks and rolls itself back to the last-known-good config the instant a check fails. The nightly build isn't ungated. Its gate is a different mechanism: an automated one that verifies reality after the change instead of a human weighing intent before it.
So the real rule underneath the block step isn't 'always ask a human.' It's 'match the gate to the risk.' A one-off deploy carrying hand-written code that has never run in production gets a person, because a person's read is the cheapest insurance available. A repeatable, narrow, self-checking deploy of already-vetted content gets an automated verifier, because a human clicking a button at 3am would add fatigue and subtract nothing.
05Where humans are good
The gate is where a human is actually good
There's a nice division of labor here. The machine is far better than I am at the tedious, exhaustive work — running every check, building every artifact, never getting bored or skipping a step. I'm better than the machine at one specific thing: the judgment call about whether, given everything, we should pull this particular trigger right now. The block step puts each of us on the side of the line we're good at. It does the ten thousand things it does tirelessly; I do the one thing that wants a human's read.
And because the machine did all the exhausting preparation, I arrive at the decision with a clear head and full context instead of buried in the grunt work. The gate isn't where the human slows the machine down. It's where the machine hands the human a single, well-prepared, consequential decision and gets out of the way — not replacing the judgment, but clearing everything around it so the judgment gets my full attention.
It even works when an agent is the one preparing the request. My own operating rules pre-authorize unblocking that deploy gate — an agent can push a merge-or-deploy request to an approval queue, where I one-click it from a dashboard. That sounds like the human is being cut out, but it's the opposite: the agent does every reversible thing up to the decision and stops at the click. The block step doesn't care whether a script or an agent filled the pipeline; it only cares that the last door is opened by someone accountable for it.
06One standing gate
One gate, standing, so the rest can run wild
The counterintuitive result is that the block step is what lets me be aggressive everywhere else. Because I know there's a guaranteed stop before anything irreversible, I can let the automation be bold in the reversible territory — fan out, run fast, try things — without anxiety, because none of it can slip through to production without passing the gate I control. The single standing wall near the end is what makes the wide-open field before it safe to run across.
That's the pattern I keep reaching for whenever I hand real capability to a system: automate expansively, gate narrowly, and put the one gate exactly where the irreversible begins. It's the same instinct as everything else I build — the impressive autonomy people see is possible precisely because of an unglamorous stop they don't. The machine can do almost everything. Designing where the 'almost' lives is the actual job.