"Works on my machine" is usually not a claim about the machine. It's a claim about uptime.
Your local process lives for forty seconds. The production one lives for three weeks. A whole class of defects needs time before it shows up, and time is the one variable you rarely provision locally.
Token refresh is the clearest case. An access token with an hourly lifetime never expires inside a local run, so the refresh branch ends up the least-exercised code in the integration, and it's the first thing that runs unattended, without you watching. In payment integrations that branch often is the integration: the happy path is decoration.
Then you get the slow accumulators. A client that leaks one connection per failed request is correct on your machine and fatal after a week of uptime. A module-level cache loaded at import is fresh in a local run, and arbitrarily stale in a worker that hasn't restarted since the last config change. A retry schedule with long backoff rarely reaches its later attempts, because you stop the process first.
This isn't environment drift. A short-lived process just hides the bugs whose trigger is duration.
So I treat lifetime as a test parameter, not an environment property. Shorten TTLs in the test config until expiry lands inside the suite. Run workers long enough to cross at least one refresh and one reconnect. In-memory state you don't rebuild from a durable store is a bet that the process dies before it goes wrong.
Staging proves the code runs. Very little proves it keeps running.
I write up more of these production-shaped failure modes here: https://polycratia.com/c/c613f1ab
Which duration-triggered bug cost you the most: token expiry, pool exhaustion, or a stale in-process cache?