<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Muhammad Umer Nadeem</title>
    <description>The latest articles on DEV Community by Muhammad Umer Nadeem (@mumernadeem).</description>
    <link>https://dev.to/mumernadeem</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4237856%2Fa78d64df-953d-42c7-94fa-935fe1f5ca8a.jpg</url>
      <title>DEV Community: Muhammad Umer Nadeem</title>
      <link>https://dev.to/mumernadeem</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mumernadeem"/>
    <language>en</language>
    <item>
      <title>Knight Capital’s $460M Failure: Turning Postmortems into Enforceable CI/CD Deployment Gates</title>
      <dc:creator>Muhammad Umer Nadeem</dc:creator>
      <pubDate>Tue, 10 Nov 2026 20:35:47 +0000</pubDate>
      <link>https://dev.to/mumernadeem/knight-capitals-460m-failure-turning-postmortems-into-enforceable-cicd-deployment-gates-324o</link>
      <guid>https://dev.to/mumernadeem/knight-capitals-460m-failure-turning-postmortems-into-enforceable-cicd-deployment-gates-324o</guid>
      <description>&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;h2&gt;
  
  
  What actually happened
&lt;/h2&gt;

&lt;p&gt;The facts below come from the SEC's October 2013 order against Knight (Release No. 34-70694).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Knight was adding code for NYSE's Retail Liquidity Program. To do it, the team &lt;strong&gt;reused an old feature flag&lt;/strong&gt; that used to switch on a function called Power Peg.&lt;/li&gt;
&lt;li&gt;Power Peg had been &lt;strong&gt;dead since 2003&lt;/strong&gt;, but the code was still sitting in the router.&lt;/li&gt;
&lt;li&gt;In 2005, a change had &lt;strong&gt;moved the logic that counted filled orders&lt;/strong&gt;, so on old code Power Peg no longer knew when to stop. It was never retested.&lt;/li&gt;
&lt;li&gt;The new code was &lt;strong&gt;deployed by hand&lt;/strong&gt; over several days. One of the eight servers didn't get it.&lt;/li&gt;
&lt;li&gt;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."&lt;/li&gt;
&lt;li&gt;Before the open, an internal system sent &lt;strong&gt;97 automated emails&lt;/strong&gt; referencing the problem. They weren't built as alerts, and nobody acted on them.&lt;/li&gt;
&lt;li&gt;When the team tried to roll back, they removed the new code from the servers that had it, which &lt;strong&gt;activated the old behavior on more servers&lt;/strong&gt; and made things worse.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No single step there is exotic. Each one is the kind of thing a team does on a normal Tuesday.&lt;/p&gt;

&lt;h2&gt;
  
  
  Which of these is a deploy gate?
&lt;/h2&gt;

&lt;p&gt;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 &lt;em&gt;before&lt;/em&gt; a deploy completes. Going through the list honestly:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. "Did every server get the new version?" Yes, this is a gate.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A deploy isn't finished when the script exits. It's finished when you've &lt;em&gt;verified&lt;/em&gt; 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.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. "Did we reuse a flag name that used to mean something else?" Partly.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. "Has the code we left in the repo been tested?" Partly.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. "Did a second person review the deployment?" Yes, as a process gate.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. "Is there a kill switch?" No, not as a deploy gate.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A circuit breaker is a runtime control. A deploy check can verify that its configuration is present, but it can't be the breaker.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;h2&gt;
  
  
  Turning the postmortem into a rule
&lt;/h2&gt;

&lt;p&gt;I've been building a small open-source tool called &lt;a href="https://github.com/mumernadeem/graveyard" rel="noopener noreferrer"&gt;Graveyard&lt;/a&gt;. 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."&lt;/p&gt;

&lt;p&gt;Here's what the fleet-version check from #1 looks like as a rule:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;## Deploy Rules
&lt;/span&gt;
&lt;span class="k"&gt;```graveyard
- &lt;/span&gt;type: custom_command
  name: "All routers run the expected build"
  run: "python scripts/verify_fleet_version.py $BUILD_SHA"
&lt;span class="k"&gt;```
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the script it runs, illustrative and deliberately boring:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# scripts/verify_fleet_version.py&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;urllib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;

&lt;span class="no"&gt;EXPECTED&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="no"&gt;HOSTS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;"router-{n}.internal"&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;mismatches&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;host&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="no"&gt;HOSTS&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;urllib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;urlopen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;"http://{host}:8080/version"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;build&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"build"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;build&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;EXPECTED&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;mismatches&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;"{host} runs {build}, expected {EXPECTED}"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;mismatches&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"; "&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mismatches&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;# non-zero exit fails the gate&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;The rule is trivial. The value is that it &lt;strong&gt;stays&lt;/strong&gt;. 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.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this doesn't do
&lt;/h2&gt;

&lt;p&gt;I'd rather say this up front than have you find it in the comments:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It does not replace OPA, Kyverno, Checkov or Trivy. They check what's &lt;em&gt;in&lt;/em&gt; your configuration. Graveyard checks whether you've repeated a &lt;em&gt;specific mistake&lt;/em&gt; your team already paid for. It's meant to run alongside them.&lt;/li&gt;
&lt;li&gt;It can't enforce anything it can't observe. A rule is only as good as the script behind it.&lt;/li&gt;
&lt;li&gt;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.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Try it on one of your own postmortems
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/mumernadeem/graveyard.git
&lt;span class="nb"&gt;cd&lt;/span&gt; graveyard
python3 src/cli/graveyard.py demo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The demo takes about 30 seconds, needs nothing installed, and ends with a blocked deploy.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

</description>
      <category>cicd</category>
      <category>devops</category>
      <category>security</category>
      <category>sre</category>
    </item>
  </channel>
</rss>
