advanced 3 min answer

On 1 August 2012 Knight Capital lost roughly $460M in about 45 minutes. The SEC's 2013 order records that new order-routing code had been rolled out to eight servers over several days and a technician did not copy it to one of them, where a flag reused by the new code reactivated dormant functionality. Every unit and integration test had passed. Which variable was never under test, and what check would you add?

knight-capitaldeploymentrelease-verificationblast-radiustrading
Show the full answer Hide the answer

What is being tested

Whether you can see that a test suite verifies the code in the repository, and an incident is caused by the code that is running. The two are only the same if something checks.

The diagnosis

Knight's tests exercised a build. Production ran a fleet, and the fleet's state had three properties no test asserted:

  • Which artefact version each of the eight servers held. Seven had the new order router, one had the old one. Nothing compared them.
  • What a reused configuration flag meant on each server. The new code gave an existing flag a new meaning. On the server still running old code, setting that flag meant what it used to mean, and activated a code path that had been dormant for years.
  • Whether the running behaviour matched the intended behaviour. The eighth server began sending orders within a minute of the market opening. The loss rate was roughly $10M per minute, and the order flow itself was the only evidence that anything was wrong.

The failure is not a missing test of the trading logic. It is the absence of any assertion about deployed state, in a release process where partial deployment was the normal way of working.

What to change, in order

  1. Assert the artefact set after every deployment. Every host reports the build identifier it is running; the deploy fails, loudly, if the set is not uniform. This is a cheap check and it closes the exact gap: one host out of eight.
  2. Never reuse a flag name for new semantics. Retire the old flag, introduce a new one. The cost is one extra config key; the alternative is that meaning depends on which binary reads it.
  3. Delete dead code before the release that would make it reachable, rather than leaving it behind a flag nobody sets. Code that cannot be deleted safely is code you do not understand.
  4. Put a behavioural kill switch in front of the money. For a system sending orders, a pre-trade limit on notional per minute would have capped the loss at a fraction of it, regardless of why the orders were wrong.
  5. Alert on the business signal, not the process signal. Order volume per minute against the recent baseline would have fired in under a minute. Host health checks were green.

When this is the wrong answer

For a stateless service behind a load balancer where a stale host serves slightly older but correct behaviour, this machinery is over-investment. The check earns its place when a mixed-version fleet is not merely older but semantically different — flags reinterpreted, message formats changed, dead code reachable — and when the per-minute cost of wrong behaviour is large. A blog's CMS is not Knight's order router.

Common weak answers

  • "They needed better testing." They had tests. The untested object was the deployment, not the code, and more unit tests would have changed nothing.
  • "Automate the deployment." Automation removes the manual copy step and gives the same outcome if the automation reports success on seven of eight hosts. What matters is the assertion after the fact, not the mechanism that performed the copy.
  • "Add a rollback button." Knight's staff spent the window trying to diagnose, then removed the new code from the seven correct servers, which made things worse. Rollback without a correct diagnosis is another change under pressure.

What a strong answer adds

That this is a general class: any release that reinterprets existing state is unsafe under partial deployment, and most deployments are partial for some window. Database migrations, serialisation formats, feature flags and routing rules all share it. The discipline is that either the old and new code must both behave correctly on the same state, or the state must be new. And the financial layer: the pre-trade risk limit is the control that bounds the damage when the software reasoning fails, which is why regulators wrote it into the market access rule rather than asking firms to test harder.