Shadow Traffic: Testing Without Responding to the User
- date
- category
- CI/CD
- also in
- Reliability Engineering
- reading
- 2 min / 302 words
Canary still affects users.
Even if only 1% of traffic reaches the new version, that 1% receives its response.
Shadow traffic appeared for a different question:
can we show real traffic to the new version without using its response?
The stable version serves the user.
The new version receives a copy of the request.
Minimal model
The logic looks like this:
client
|
v
stable version -> response to client
|
+-- copy request -> shadow version
The user does not see the shadow response.
We look at:
logs
metrics
traces
response differences
execution time
errors
The simplest example in a test gateway:
const stableResponse = await fetch("http://api-v1/orders", request);
fetch("http://api-v2/orders", request.clone()).catch((error) => {
logger.warn({ error }, "shadow request failed");
});
return stableResponse;
This is not a production-ready pattern.
It shows the mechanism: the response from the new version does not go back to the client.
What it is really for
Shadow traffic is useful when we want to see how a new version behaves under the real shape of traffic.
It fits:
reads
new search engines
new scoring algorithms
response comparison
performance tests
dependency validation
It is especially useful when synthetic traffic does not represent production.
Real traffic has strange headers, old clients, unusual payloads, and event ordering that tests often miss.
The largest problem: side effects
Not every request may be copied.
If the request performs a write:
creates an order
sends email
charges money
publishes an event
changes inventory
the copy can cause damage.
Shadow traffic requires control over side effects.
Possible approaches:
mirror only GET
block writes in shadow
separate test database
mock external integrations
idempotency keys
mark requests as shadow
What shadow traffic does not test
Shadow traffic lets the new version see real requests, but it does not test the full production behavior.
The user still receives the stable version's response, so we do not see effects that would appear only after real traffic takeover: client reaction, retries, session changes, browser-side cache, or follow-up requests that depend on the response.
The second boundary is cost. A traffic copy still consumes CPU, memory, network, and dependencies. If the shadow version is slower, the user does not feel it, but the infrastructure can already receive extra load.
That is why shadow traffic is mainly for comparing behavior under real traffic.
It does not replace canary, because canary tests the moment when the new version actually responds to the user.