Blog Post #6

The test passed. The integration still broke.

A developer signs up for your product. They follow the quickstart. They run the test. It passes. They ship.

Two weeks later, something breaks in production — and when you trace it back, the test that passed was never testing the thing that failed.

This isn't a story about bad code or careless engineers. It's a pattern that shows up across almost every developer product, and it's remarkably consistent once you start looking for it.

The pattern

Every product validates something. A test suite, a sandbox, a status code, a preview environment, a webhook — each one confirms that a specific thing worked.

The problem is that each one covers less than the developer believes it does.

Not because the product is misleading. Not because the docs are wrong. Because validation is always narrower than the assumption it creates.

A product can only confirm what it controls. A developer assumes it confirms what they depend on.

Those are rarely the same set.

Here's what that looks like in practice.

A messaging API: a status code that means two different things

A transactional messaging platform's sandbox mode returns a realistic 202 QUEUED response. Same shape as production. Same fields. The docs recommend using it for CI smoke tests.

That's a sensible testing story. The sandbox skips execution entirely — no message is sent, no provider is called, no balance is touched. You can run thousands of tests without sending a single real message.

The catch is what happens when a developer wires it into CI and sees green across the board.

The test passes. The integration looks validated.

But sandbox skips the layer that determines whether a message actually gets delivered.

Delivery failures, carrier behavior, provider errors — none of it can occur in sandbox, because execution never happens.

So a suite can pass for months, and the first real delivery failure is the first signal the team gets.

Not as a failing test.

As a customer saying the message never arrived.

A CI platform: a migration that ran successfully

A CI platform lets teams migrate existing GitHub Actions workflows. The blog frames it simply: migrated workflows "just work."

And they do run.

The migration command discovers workflows, maps runner labels, applies compatibility fixes, and writes the new files. The build goes green.

So far, so good.

Then the CLI reference adds something important further down: unsupported triggers like release and schedule get removed. Jobs using unsupported features can be commented out entirely, with a note explaining why.

The migration succeeded.

But the workflow may no longer behave like the workflow the customer migrated.

A workflow can migrate cleanly and simply never fire again on the trigger it used to fire on.

Nothing errors.

There's no failed run to investigate because the run that would have happened never happens.

A release pipeline can therefore look successfully migrated while quietly stopping part of the customer's deployment process.

A serverless code platform: a secret that stays private

A hosted code platform tells developers their environment variables are private, even in public functions.

That's true, and it's a genuine reassurance.

The same docs also say that anyone who knows an HTTP or email trigger URL can invoke the code behind it.

Both things are true.

The secret stays hidden.

The operation stays callable.

And until you reach the Permissions page — which puts it plainly as "neither one implies the other" — it's easy to read the first statement as covering the second.

A developer can build a public function with an HTTP trigger, keep an API key in an environment variable, and reasonably feel the whole setup is protected.

Then someone with the URL fires the trigger, the function reads the key, and the external service does whatever the code allows.

The key never leaked.

The operation was still accessible.

A browser automation platform: an execution that succeeded

A browser automation platform runs workflows against real software — Slack, GitHub, Stripe — and reports a clear status: execution.success.

That status is accurate for what it describes.

The defined browser steps completed and returned the workflow's output.

But look at the dry_run option.

It lets a workflow execute successfully while skipping the write in the target system.

That feature exists because execution and business-state change are different things. The platform knows this, and built for it.

Which means in production, execution.success confirms the agent finished.

It doesn't necessarily confirm that the order was placed, the ticket was closed, or the record was updated.

And the retrieve endpoint returns an errors array even on successful runs, with was_recovered: true.

A run can succeed while errors happened during it.

For anything that writes to another system, that's an important distinction.

The agent finished.

The status is green.

The customer's actual state is still a separate question.

Why this keeps happening

Products test what they control.

Developers depend on what happens beyond that boundary.

A sandbox tests the request contract. A developer depends on the delivery outcome.

A migration tool tests that files were written. A developer depends on the workflow continuing to run.

A secret store protects the value. A developer depends on the operation being protected.

An automation platform tests that the steps ran. A developer depends on the target system changing.

None of these are necessarily bugs.

Every one of them can be reasonable product behavior.

The problem appears when the boundary between what the product confirms and what the customer needs to know isn't obvious during implementation.

That's where an implementation can look healthy until it reaches production.

Why it's expensive

The cost isn't always the production failure itself.

Sometimes the cost appears much earlier.

A developer gets stuck during implementation and never opens a support ticket. They close the tab, try another product, or decide the integration isn't worth the effort.

The company may never know exactly what happened.

A different developer gets the integration working but encounters an edge case in production. Now there's a support ticket, an engineering investigation, and potentially a customer whose confidence in the product has dropped.

Both outcomes can originate from the same underlying issue:

the customer's implementation path depended on something the product's validation did not actually establish.

One outcome is visible.

The other can disappear without a trace.

What to look for

You can't test everything.

The useful question is not simply:

Does the test pass?

It's:

What does this test actually prove, and what does a customer need it to prove before they can safely move forward?

Those answers don't always match.

The same applies beyond tests.

Look at the API response a developer treats as confirmation.

Look at the SDK method they assume completed the operation.

Look at the webhook they rely on to represent a real-world event.

Look at the sandbox they use before moving into production.

Look at the deployment or migration step they assume preserved existing behavior.

Look at the authentication and permissions model they believe protects the operation.

Those are implementation boundaries worth examining because that's where customer expectations and actual product behavior can diverge.

And those gaps are much easier to deal with when they are found during implementation review than after a customer discovers them in production.

The bigger question

For a developer-facing product, the important question isn't only whether the product works.

It's whether a customer can successfully implement what they believe the product enables them to implement.

That's a different question.

It crosses the API, SDK, authentication, configuration, deployment, third-party dependencies, and the customer's own application logic.

And the further a customer gets from the controlled environment into a real production integration, the more those boundaries matter.

That's the part of the implementation path worth examining.


At DaxMac, I look at developer-facing products from the customer's implementation side: how a developer would actually integrate, configure, deploy, and operate the product, and where that path can create support work, integration failures, or production issues.

The goal isn't to review whether a product has "good docs."

It's to identify the implementation gaps that are easy to miss from inside the product.

I wrote a case study showing what this looks like in practice here.