The Shedding Skin Heuristic: How Zombie APIs and Missed Dependency Updates Hide in Plain Sight

A software testing heuristic for catching the dependency updates that leave no obvious failure until it's too late.

What Is the Shedding Skin Heuristic?

When a tarantula moults, its shed exoskeleton looks almost indistinguishable from the spider itself. In pet shops, there's a recurring moment of fright: someone spots what looks like an escaped tarantula, only to discover it's an empty shell.

The Shedding Skin Heuristic describes a specific and surprisingly common development strategy problem; one where a dependency is updated, but the old version is left running. Any missed references silently continue working, right up until the old version is removed.

The Problem

There is a product that uses a service or library as a dependency. That dependency needs updating in some way, but the behaviour of the overall product shouldn't change. This could be something like updating a URL for an API endpoint, bumping a version of an API, or something similar that is not a new feature or behavioural change.

The developers decide that rather than removing the original dependency first and observing a failure, they will keep the current version and create a new one, leaving both in a similar state. The product is then configured to interact with the new version, and all references to the dependency should be updated.

If any updates are missed, there will be no obvious failure or behavioural change. The old dependency quietly serves the missed calls. At some point the original dependency is removed, and the product starts experiencing failures.

A Real-World Example

Here's a recent example I encountered:

The product I was testing used an API to generate documents. The URL of that API had to be updated to conform to new company standards, so a new API endpoint was created and deployed. The product was configured to interact with the updated URL.

The problem was that one part of the system, used only in certain scenarios, also needed to call the same API. That part had been missed. When I compared the configuration of the two systems, I noticed the lesser-known one hadn't been updated recently. Checking the Swagger definitions confirmed it was still pointing at the previous version. When testing the documents through the UI, on the surface, everything behaved exactly as before.

A Famous Example: The Stripe Zombie API (2024–25)

The security world has a name for this class of problem: Zombie APIs — deprecated endpoints that remain operational in production, unmonitored and unmaintained.

In 2024–25, a legacy endpoint (/v1/sources) that hadn't been removed from production was still connected to Stripe's payment validation systems and processing card data. This endpoint lacked the security controls of Stripe's modern APIs — advanced rate limiting and fraud detection — and because it wasn't part of the official inventory, it didn't receive the same testing or monitoring. Attackers used bots to flood it with high-frequency, low-value card-testing requests that went undetected for an extended period. The campaign affected at least 49 online retailers.

Read more: Top 5 API Incidents of 2025

What's notable is that this isn't just a security problem. The old skin was still there. Nobody noticed until the damage was done.

How to Challenge It

When you suspect a shedding skin scenario, here are five approaches to investigate it:

1. Find an oracle for the dependency change Check whether any log, trace, or monitoring output shows which endpoint or version is actually being called. You might not know one exists, or you might lack access — ask anyway. If the URL appears in a request log, that's your oracle. Validate against it.

2. Explore the less obvious corners of the product Where might the old dependency be referenced in ways that aren't in the obvious config file? Think: scheduled jobs, background workers, SDKs with baked-in defaults, infrastructure-as-code, deployment scripts, and environment variable fallbacks.

3. Check for evidence of the update's scope Is there a pull request, a change log, or a migration ticket that lists everywhere the update was applied? If there is, use it as a checklist.

4. Document what you find Does this dependency have any documentation that would help a future engineer understand its role, its history, or its expected configuration? If not, consider writing it.

5. Have the conversation about dual-dependency risk Raise with the developers the specific risk of running two similar versions simultaneously. Together, consider whether there's a safer migration path, such as retiring the old version first to surface failures early in a lower environment.

Related topics:

← Back to blogs

The Shedding Skin Heuristic: How Zombie APIs and Missed Dependency Updates Hide in Plain Sight

A software testing heuristic for catching dependency updates and zombie APIs

The Dark Side of Agentic AI: Are We Ready for What's Coming?

From brute-forcing UIs to drone swarms, agentic AI in the wrong hands poses risks we're barely discussing. A web developer's perspective on the threats hiding in plain sight.

BINMEN: A Practical Heuristic for API Testing

BINMEN is a mnemonic heuristic for API testing covering Boundary, Invalid, Null, Method, Empty, and Negative scenarios. Learn how to use it practically.

API Testing Mnemonics: CRUD, BINMEN, VADER & POISED Explained

API Testing Mnemonics: CRUD, BINMEN, VADER & POISED Explained

Don't think of an elephant

Should you do what your told or look where they tell you to not look

There's Something Odd About the Official Playwright MCP Demo

There's Something Odd About the Official Playwright MCP Demo

I was wrong about exploratory testing, are you?

How I came to finally understand what exploratory testing is

The perpetual stew vs the historian

A story about a search for truth that no one asked for

Pushback on crappy testing interviews.

How to demonstrate responsible testing in an interview

Common misconceptions about Scrum

Common misconceptions about scrum

AI has got our wires crossed

How AI has us thinking back to front

How are we still doing Taylorism in 2025

It's 2025, and Taylorism should be long gone. Why are we still seeing it everywhere in 2025?

Testing practice: Irish phone numbers

Tales of testing a web form with field validation for Irish phone numbers

Forget flashy - focus on fundamentals in testing

Why testers should focus on risk and fundamentals instead of over-engineering solutions with automation.

Have you had too much to think?

Are you being asked to test without thinking? be wary.

Setting expectations for tester during agile ceremonies

Setting expectations that testers should follow throught each agile process to make more of an impact and provide value

Thoughts on Estimates in Software Engineering

A deep dive into why software estimations are so tricky, the asymmetry of estimates, and how Scrum approaches them.

Rating testing deifnitions from different orgs

Rating the definitions of software testing from page 1 of Google and explaining why I think they deserve the rating

Testing Financial data using an API

How to test time-series financial data through an API

Tales from Reddit: testing doesn't exist

My thoughts on a bizarre comment from Reddit in which a fellow tester claims testing doesn't exist and what it means to the state of testing

My Accidental Vibe Coding Nightmare

When limitied coding experience meets AI, is it tempting to vibe code or are you entering a debugging nightmare?