On August 1, 2012, Knight Capital deployed new code to its eight order-routing servers. A technician missed one of them. Forty-five minutes after the market opened, the firm had sent more than 4 million orders into the market to fill just 212 customer orders, and the SEC later put the loss at more than $460 million.
Most retellings stop at "one server out of eight." That's the interesting part, but it isn't the whole story. The SEC's order reads like a list of gates that didn't exist. I want to walk through them, because each one is a check a pipeline could have run before the deploy was called done.
What actually happened
The facts below come from the SEC's October 2013 order against Knight (Release No. 34-70694).
- Knight was adding code for NYSE's Retail Liquidity Program. To do it, the team reused an old feature flag that used to switch on a function called Power Peg.
- Power Peg had been dead since 2003, but the code was still sitting in the router.
- In 2005, a change had moved the logic that counted filled orders, so on old code Power Peg no longer knew when to stop. It was never retested.
- The new code was deployed by hand over several days. One of the eight servers didn't get it.
- On August 1, orders carrying the reused flag hit that server. It woke up Power Peg, which kept sending child orders with no notion of "done."
- Before the open, an internal system sent 97 automated emails referencing the problem. They weren't built as alerts, and nobody acted on them.
- When the team tried to roll back, they removed the new code from the servers that had it, which activated the old behavior on more servers and made things worse.
No single step there is exotic. Each one is the kind of thing a team does on a normal Tuesday.
Which of these is a deploy gate?
This is the question I care about, and it's where I think a lot of "this tool would have prevented X" writing gets sloppy. Not every failure is something you can check before a deploy completes. Going through the list honestly:
1. "Did every server get the new version?" Yes, this is a gate.
A deploy isn't finished when the script exits. It's finished when you've verified that every host in the fleet is running the build you meant to ship. A manual copy to eight servers with no check afterwards is exactly the hole Knight fell into.
2. "Did we reuse a flag name that used to mean something else?" Partly.
You can't catch every semantic reuse automatically, but you can keep a registry of retired flags and fail the pipeline when a new change touches a name on that list.
3. "Has the code we left in the repo been tested?" Partly.
Knight never retested Power Peg after the 2005 change. A pass-rate threshold wouldn't have helped, because dead code has no tests to fail. The gate here is coverage or a "no untested modules ship" rule, not a percentage.
4. "Did a second person review the deployment?" Yes, as a process gate.
The SEC found Knight had no written procedure requiring a second technician to review a deploy. That's a rule a pipeline can enforce with a required approval.
5. "Is there a kill switch?" No, not as a deploy gate.
A circuit breaker is a runtime control. A deploy check can verify that its configuration is present, but it can't be the breaker.
So of five findings, a deploy-time gate gives you real coverage on roughly two or three. That's still a lot, because #1 covers the failure that actually triggered it. But it's worth saying plainly that no deploy gate would have replaced runtime risk controls.
Turning the postmortem into a rule
I've been building a small open-source tool called Graveyard. The idea is simple: your postmortem markdown contains a fenced block of rules, and every deploy is checked against all of them. The incident that caused each rule is recorded alongside it, so the reason a deploy was blocked is never "because policy says so."
Here's what the fleet-version check from #1 looks like as a rule:
## Deploy Rules
```graveyard
- type: custom_command
name: "All routers run the expected build"
run: "python scripts/verify_fleet_version.py $BUILD_SHA"
```
And the script it runs, illustrative and deliberately boring:
# scripts/verify_fleet_version.py
import json, sys, urllib.request
EXPECTED = sys.argv[1]
HOSTS = [f"router-{n}.internal" for n in range(1, 9)]
mismatches = []
for host in HOSTS:
with urllib.request.urlopen(f"http://{host}:8080/version", timeout=5) as r:
build = json.load(r)["build"]
if build != EXPECTED:
mismatches.append(f"{host} runs {build}, expected {EXPECTED}")
if mismatches:
print("; ".join(mismatches))
sys.exit(1) # non-zero exit fails the gate
Graveyard runs the command, treats exit code 0 as a pass and anything else as a failure, and blocks the deploy. The record it writes for that decision names the Knight Capital incident as the reason the check exists.
The rule is trivial. The value is that it stays. Six months and three reorganizations later, when nobody remembers why the check is there, it's still running, and the file next to it explains why.
What this doesn't do
I'd rather say this up front than have you find it in the comments:
- It does not replace OPA, Kyverno, Checkov or Trivy. They check what's in your configuration. Graveyard checks whether you've repeated a specific mistake your team already paid for. It's meant to run alongside them.
- It can't enforce anything it can't observe. A rule is only as good as the script behind it.
- It's early: a Python CLI with no dependencies and nine rule types. The part I most want to improve is turning a messy real-world postmortem into good rules automatically, and right now that part is the weakest.
Try it on one of your own postmortems
git clone https://github.com/mumernadeem/graveyard.git
cd graveyard
python3 src/cli/graveyard.py demo
The demo takes about 30 seconds, needs nothing installed, and ends with a blocked deploy.
The more useful test is a postmortem from your own team. Pick one, write down what "what we should have checked" would have looked like as a command that exits 0 or 1, and see whether it's easy or awkward to express. I'm looking for the cases where it's awkward.
If you try it, tell me what's wrong with it, in the comments or as an issue. That's more useful to me than a star.
Top comments (1)
The fleet-version gate is the part that stuck with me. A deploy isn't done when the script exits. I built a small linter for CI configs for the same reason, and the checks that actually survive are the ones that block the deploy.