L4 · Quality in the pipeline
L4Go deeper4 min read

Synthetic monitoring and testing in production

Some things are only true in production — real data, real scale, real third parties. Running safe, continuous checks there catches what no pre-release environment could, and is now standard practice rather than heresy.

Every test environment is a simplification of production, and the gap between the two is where a specific class of bugs lives permanently: third-party outages, DNS changes, certificate expiry, regional latency, and traffic patterns no staging environment ever sees. Synthetic monitoring and disciplined testing in production close that gap by treating production itself as a test target, not just a place code lands after testing ends.

Why staging cannot catch everything

Staging environments drift from production in ways teams stop noticing: smaller data volumes, different feature flag states, mocked third-party integrations, and infrastructure that is scaled down for cost. A payment integration that works against a sandbox API can fail against the real provider's rate limiting under real traffic. No amount of staging or CI testing will surface that, because the sandbox does not rate limit the same way.

Synthetic monitoring runs scripted checks against production continuously, simulating what a real user does: logging in, adding an item to a cart, completing a checkout. Unlike passive uptime checks that only confirm a server responds, synthetic checks confirm the actual user journey still works, catching issues like a broken third-party widget or an expired API key that a simple ping would miss entirely.

Building your first synthetic check

Start with the one or two flows that generate the most revenue or the most support tickets when broken, rather than trying to cover everything at once. A single well-built check on checkout catches far more real damage than ten shallow checks scattered across low-traffic pages.

synthetic-checkout-check.ts
import { chromium } from 'playwright'

async function checkCheckoutFlow() {
  const browser = await chromium.launch()
  const page = await browser.newPage()

  await page.goto('https://shop.example.com/cart')
  await page.click('[data-test="checkout-button"]')
  await page.fill('[data-test="card-number"]', '4242424242424242')
  await page.click('[data-test="submit-payment"]')

  const el = await page.waitForSelector('[data-test="order-confirmed"]', { timeout: 10000 })

  await browser.close()
  return el !== null
}

A synthetic check like this should run every one to five minutes from multiple regions, since a failure specific to one region (a CDN edge node, a regional load balancer) is exactly the kind of thing a single-location check will never catch.

Testing in production beyond synthetic checks

Synthetic monitoring is one technique inside a broader practice of testing in production. This also includes canary releases that route a small percentage of real traffic to new code, dark launches that run new logic silently alongside the old path without affecting users, and chaos experiments that deliberately inject failure to see how the system responds. These overlap heavily with safe deployment practices tracked by DORA, since the mechanisms for shipping safely and testing safely are largely the same infrastructure.

A mid-size travel booking company used dark launches to validate a new pricing engine. The new engine ran on every real search request in parallel with the old one, logging its results without ever showing them to users.

Over two weeks, the team compared outputs and found the new engine diverged on roughly 3% of international routes due to a currency rounding difference. This was a bug that a pre-release test suite using synthetic fixtures had never triggered, because it lacked the exact currency pairs where the rounding edge case occurred.

Alerting on synthetic checks without causing fatigue

A synthetic check that pages someone at 3am for a single transient timeout will train the team to ignore alerts within a month. Require two or three consecutive failures before paging, and separate a full outage (the whole flow is down) from a degradation (latency crossed a threshold but the flow still completes), since these need very different urgency. This ties directly into good incident response: the alert that triggers the page should already tell the on-call engineer what kind of problem they are looking at.

  • Single failure: log it, no page.
  • Two consecutive failures from the same region: page on-call, treat as degraded.
  • Failures from multiple regions simultaneously: page immediately, treat as an outage.

Coverage that scales with what matters

Not every flow deserves a synthetic check running every minute. Rank flows by revenue impact and by how often a manual tester would need to notice a break before it became costly, and put your highest-frequency, most reliable checks on the flows at the top of that list. A marketing landing page can be checked hourly; a checkout flow needs minute-level granularity.

FAQ

Questions people ask

How is synthetic monitoring different from real user monitoring (RUM)?

Synthetic monitoring runs scripted checks on a schedule, independent of real traffic, so it catches problems even during low-traffic hours. RUM observes actual user sessions, which gives broader coverage of real conditions but only after a real user hits the bug.

Do synthetic checks need real payment methods and real data?

Use dedicated test accounts and sandboxed payment methods provided by your payment processor for production testing. Never use a real customer's data or a live charge for a routine check.

How many synthetic checks should a team run?

Start with your top two or three revenue-critical flows. Most teams plateau around ten to twenty meaningful checks; beyond that, the maintenance cost of keeping checks in sync with UI changes usually exceeds the value of the extra coverage.

What is the difference between testing in production and just letting bugs reach users?

Testing in production is deliberate and controlled: canaries limit blast radius, dark launches are invisible to users, and synthetic checks catch failures within minutes. Letting bugs reach users unchecked is the absence of any of that discipline.