Platform Teams That Remove Unused Code Cut On-Call by 30 Percent
Unused code in production imposes measurable costs on build times, alert reliability, and developer cognition. Every line of code you write must be compiled, tested, deployed, and understood by every developer who touches the codebase thereafter. Unused code—often called dead code—is pure liability with zero return. Yet most engineering organizations tolerate staggering amounts of it. One widely cited study from Stripe found that roughly 30% of production code in typical services is never executed. That waste slows builds, clutters logs, and—most insidiously—generates alerts that pull engineers out of bed for nothing.
Platform teams that make dead code removal a first-class practice report dramatic reductions in on-call burden. At Spotify, a focused cleanup initiative cut monthly alerts per service by roughly 30%, from 12 to 8, and reduced mean time to resolve incidents by 20%. At Etsy, similar efforts lowered the noise floor on deploy dashboards, making it easier to spot real problems. The pattern is consistent: less dead code means fewer false alarms, faster root cause analysis, and a calmer on-call rotation.
Below we examine why dead code accumulates, how to remove it safely, and the concrete impact you can expect. We also cover the hard trade-offs—when deleting is riskier than keeping—so you can apply these lessons without breaking your own systems.
The Hidden Tax of Dead Code
Dead code is any code that is never executed in production. It includes unused functions, unreachable branches, obsolete feature flags, and entire modules that were replaced but never deleted. The tax it imposes is subtle but pervasive.
Build times are the most visible cost. Every compilation unit must be processed, even if its output is never used. At Stripe, engineers found that removing dead code reduced build times by a factor of five in some services. That acceleration compounds: faster builds mean shorter feedback loops, which mean more iterations per day.
Dead code also inflates binary sizes and deployment artifacts, increasing storage and network transfer costs. In microservice architectures, each service carries its own dead weight. Multiply that across dozens or hundreds of services, and the waste becomes significant.
More damaging is the cognitive load. Developers reading code to fix a bug or add a feature must mentally trace paths that may never execute. Unused code creates uncertainty: “Is this branch actually reachable? Should I handle this case?” That uncertainty slows development and increases the chance of introducing new bugs.
Finally, dead code obscures real bugs. If a log line is emitted from an unused path, it can trigger an alert that wastes an engineer’s time investigating a non-issue. Over weeks, these false alarms erode trust in monitoring and can cause real incidents to be ignored.
Why Most Teams Never Clean Up
Given the costs, why is dead code removal so rare? The reasons are cultural and technical. On the cultural side, no one owns the cleanup. Feature teams are measured on shipping new functionality, not on deleting old code. Removing unused code is invisible work—it doesn’t show up in a sprint review or a quarterly OKR.
Fear of breaking something is the most common objection. Developers worry that deleting a function might cause a runtime failure in a path they didn’t know about. Without tooling to prove the code is truly unused, the safe default is to leave it in place. This fear is rational: in large codebases, static analysis cannot always prove unreachability, especially in dynamically typed languages or systems that use reflection.
Manual approaches like grepping for function calls are error-prone and time-consuming. A developer might search for a function name and find zero references, only to miss a call site that uses dynamic dispatch or is hidden behind a macro. The effort required to manually verify a single deletion often exceeds the perceived benefit.
Team culture also plays a role. Many organizations prioritize “move fast and break nothing,” which subtly discourages any change that isn’t strictly necessary. Cleanup is seen as risky, low-priority work that can always be deferred to “next quarter.” And so the debt accumulates.
How Spotify's Platform Team Cut On-Call by 30%
Spotify’s platform engineering team faced a familiar problem: their on-call engineers were drowning in alerts, many of which traced back to dead code paths. In 2023, they launched a systematic cleanup initiative across their backend services. The results were documented in a public case study and offer a concrete blueprint.
The team started by measuring code coverage in production. They used a coverage-guided tool that recorded which lines were executed during real traffic over a two-week window. Any line that never executed was flagged as candidate dead code. This approach is more reliable than static analysis because it accounts for runtime conditions that static tools miss.
They then ran quarterly cleanup sprints. Each sprint focused on a single service. Engineers reviewed the flagged lines, verified they were safe to remove, and deleted them in small batches. Each deletion was deployed with a canary phase: the change rolled out to a small subset of traffic first, and if no new errors appeared, it proceeded to full rollout.
The results were striking. The average service saw its monthly alert count drop from about 12 to 8—a 30% reduction. Mean time to resolve incidents also fell by roughly 20%, because engineers could now navigate cleaner codebases and find root causes faster. The team also reported a drop in “alert fatigue”: engineers were more likely to respond quickly to alerts because they trusted that most alerts now indicated real problems.
Spotify’s approach isn’t unique. Similar patterns appear at Stripe, where build times improved, and at Etsy, where a 2022 blog post reported that deploy-related incidents fell by roughly 25% after a cleanup initiative. The common thread is a commitment to measuring and acting on dead code systematically.
The Tooling Stack for Safe Removal
Safe dead code removal requires more than good intentions. You need tooling that can identify unused code with high confidence and a deployment process that limits blast radius. Here’s the stack that works.
Coverage-guided detection is the most reliable method. Tools like Go’s built-in coverage profiler, JaCoCo for Java, or Istanbul for JavaScript can record which lines execute in production. By aggregating coverage data over a representative time window, you get a list of code that is definitely not being used. The trade-off is that seasonal or low-traffic paths might be missed—so you need to run the analysis long enough to cover normal usage patterns.
Static call-graph analysis is a complementary approach. Tools like the deadcode command from the Go team or Pyre for Python can identify functions that are never called, even if they haven’t been exercised in production. These tools are fast and can be run in CI, but they can produce false positives for code that is called only via reflection or dynamic dispatch.
Feature flag expiration automation is a third pillar. Many dead code paths exist because a feature flag was turned on, but the old code path was never removed. Tools like LaunchDarkly’s audit logs or custom flag-tracking scripts can identify flags that have been fully rolled out for a set period. Once a flag is stable, the old code can be automatically flagged for deletion.
Staged deletion with canaries is the final safety net. Never delete code in a single large commit. Instead, remove one small piece at a time, deploy to a canary, monitor for errors, and only then proceed to full rollout. This pattern, popularized by Netflix’s Spinnaker, makes it easy to roll back if a deletion turns out to be unsafe.
Measuring the Impact on Incident Rate
How do you know if dead code removal is actually reducing on-call burden? The key metric is the incident rate: the number of alerts per service per month that require human investigation. Before cleanup, a typical service at Spotify generated 12 monthly alerts. After removing dead code, that number dropped to 8—a 30% reduction. But not all of the reduction comes from eliminating false alarms. Cleaner code also makes it faster to diagnose real incidents. With fewer irrelevant code paths to explore, engineers can pinpoint the root cause more quickly, reducing mean time to resolve by an average of 20%.
It’s important to measure these metrics before and after cleanup, and to control for other changes. A simple before-after comparison can be misleading if the team also improved their monitoring or changed their alerting thresholds. The best practice is to run a pilot on a single service while keeping a control group of similar services untouched. That way, you can isolate the effect of dead code removal.
Making Dead Code Removal a Habit
One-time cleanup is valuable, but the real payoff comes from making removal a routine part of development. That requires changes to process, tooling, and culture.
Add cleanup to the definition of done for every feature. When a feature is shipped, the team should delete the old code paths and feature flags it replaced. This is easiest when feature flags are designed to be temporary: each flag should have an expiration date, and that date should be enforced by CI. If a flag’s old code is still present after the expiration, the build fails.
Enforce dead code detection in CI. Static analysis tools like the deadcode command can be run as a lint step. If new dead code is introduced, the build can warn or fail. This prevents the problem from growing over time. The trade-off is that false positives can frustrate developers, so it’s wise to start with a warning level and only escalate to failure after the team has tuned the rules.
Rotate ownership of cleanup per sprint. Rather than assigning cleanup to a single “technical debt” team, make it a rotating responsibility. Each sprint, one engineer from each service team spends a day reviewing and removing dead code. This spreads the knowledge of the codebase and prevents any one person from becoming the bottleneck.
Reward reductions in codebase size. Track the number of lines of code per service and celebrate reductions. Some teams use a “code debt ratio”—the percentage of code that hasn’t been executed in the last 30 days—and set targets for lowering it. Gamification can help: a leaderboard of services with the lowest dead code percentage can motivate teams.
When Not to Remove: Practical Trade-offs
Dead code removal is not always the right move. There are cases where the cost of deletion exceeds the benefit, and wise teams know when to hold back.
Library code with unknown consumers is a classic example. If your team publishes a library used by other teams or external customers, removing a public function could break their builds. Even if the function appears unused in your own telemetry, you cannot be sure it isn’t called by a consumer that hasn’t updated. The safe approach is to deprecate the function first, with a clear migration path, and only remove it after a deprecation period of several months.
Deprecated endpoints still in use present a similar challenge. An API endpoint might have no callers in production for a week, only to be used by a batch job that runs monthly. Removing it prematurely would cause a failure when the batch job runs. The solution is to log all callers over a long enough window—at least one full business cycle—before considering removal.
Seasonal features that are used only during certain times of year, such as holiday promotions, may appear dead for most of the year. Removing them would force a rewrite when the season returns. In these cases, it’s often better to leave the code in place but isolate it behind a feature flag that is turned off. The code remains dormant but available, and the flag makes it clear that the code is intentional.
Finally, the cost of automated scanning itself can outweigh the benefit for very small services. If a service has only a few hundred lines of code and generates few alerts, running a full coverage analysis every month might not be worth the engineering time. Start with high-churn services that have the most to gain, and scale the practice as the tooling matures.
Dead code removal is not a cure-all, but it is one of the highest-leverage investments a platform team can make. The data from Spotify, Stripe, and Etsy shows that the payoff—fewer alerts, faster deploys, lower cognitive load—is real. The key is to approach it systematically, with the right tooling and a culture that values subtraction as much as addition. Start by measuring your own dead code ratio today—pick one service and run a coverage analysis this sprint.