Dead Code Detection Agent Across Microservices

AI-generated code demands better visibility into dead code across service boundaries.

Cover illustration for “Dead Code Detection Agent Across Microservices”

Sponsored by UFO

Written by
Working Agents Editors
Published
October 28, 2026
Reading time
8 min read
Sources cited
4 sources ↓

Dead code detection across microservices is a distributed graph problem. A reliable agent needs to trace paths across repositories, distinguish missing evidence from genuine disuse, and remember what earlier investigations established. Use deterministic graph analysis followed by language-model judgment, inside a controlled workflow that validates each removal.

Why dead code compounds across microservices in ways linters cannot see

Editing a method and seeing no change is a familiar kind of frustration. Eventually, the investigation reveals that the code path was already dead. In a monolith, tracing callers may explain where the path stopped. Across microservices, that investigation can reach a network boundary before it reaches an answer, leaving the reviewer unsure whether the code is abandoned or serves a caller elsewhere.

The boundary changes what an unused reference means. ESLint, Pylint, and PHPStan can surface local signals such as an unused variable, but those signals cannot establish whether Service B consumes an interface exposed by Service A at runtime. TypeScript’s noUnusedLocals option deliberately focuses on locals. Exported symbols require evidence beyond that scope, including the contracts through which other services reach them.

Frameworks introduce another layer of indirection into the same problem. Express route handlers, NestJS controllers, and Next.js pages can be invoked by infrastructure. A REST handler with no internal callers may be a live webhook endpoint, while a plugin loaded by convention may have no explicit import. Dead code accumulates because the system’s reach extends beyond the tools’ field of view.

How AI-generated code raises the stakes for every team running microservices

AI-generated changes put more pressure on that visibility gap. Arsham Khosravani and Audris Mockus’s 2026 census, Detecting AI Coding Agents in Open Source, examined 180 million repositories and found commit-attributed agents collectively generating hundreds of thousands of commits per month by April 2026. Claude Code alone accounted for commits across thousands of projects. The census distinguishes PR-deployed cloud agents, whose work is predominantly feature work, from commit-deployed in-editor agents, whose work is predominantly maintenance. Agents in either channel can add a new path while leaving the old one behind.

The cost then reaches beyond the next review. Popham’s 2026 benchmark documents an agent faithfully describing dead features as live when asked to write documentation. Dead code fills context windows, consumes tokens, weakens retrieval precision, and increases embedding costs for agents working on the same codebase. As code production accelerates, human review has to track both the new implementation and the cross-service provenance of what it displaced.

Why the detection problem is a graph problem, not a search problem

A search can show where a symbol’s name appears. It cannot, by itself, establish production reachability. To establish that, the graph needs entry points as its roots and imports, calls, and cross-service contracts as its edges. Within a repository, the graph follows the links that files and functions make to each other. Across services, it must also account for REST, gRPC, or event-bus contracts through which a downstream caller reaches an otherwise locally unused interface.

The choice of roots is just as important as the edges. Route handlers, event listeners, cron jobs, and workers are the start of paths even when application code never calls them directly. Test references need separate treatment because a test can keep a symbol named without connecting it to production. Popham’s benchmark supports ranking candidates from call and dependency graphs. Reachability gives evidence, but how much it can be trusted depends on known false-positive patterns.

Pharaoh’s MCP-based detector illustrates the resulting taxonomy. Its Dead category requires agreement among the signals, including no entry-point reachability, no naming reference from another file, and no production import of the module. Possibly unused findings are unreached but still named or imported elsewhere, while Used only by tests leaves the production connection unconfirmed. The detector includes naming files for ambiguous findings and entry-point call paths for reachable functions, allowing a reviewer to inspect the basis of a verdict.

The deterministic-first, LLM-second architecture that makes agents accurate on this problem

The graph model leads to a clear division of labor. Deterministic analysis constructs relationships and ranks candidates before a language model reviews uncertain cases. Popham’s benchmark covered 60+ runs across 14 real-world repositories, ranging from small libraries to the Next.js monorepo. Agents with graph context did far better than a baseline LLM agent without graph context.

Deterministic stages make the recorded evidence reproducible and explainable. An edge is either in the graph that was built or missing from it, though it may be missing because the model is incomplete. The LLM addresses that remaining uncertainty. It can inspect framework conventions, string dispatch, dynamic loading, and clues about intent in names and comments. Its role is to interpret what the graph cannot confidently classify.

This separation also changes how much context the model needs. A ranked candidate and its supporting evidence replace an indiscriminate view of the whole repository, reducing the context-window burden on large codebases. The graph performs the initial noise reduction. The results can then be structured with severity levels, confidence scores, file paths, and evidence chains that downstream agents can use. In that form, dead code detection becomes a source of evidence for documentation, refactoring, and review.

How MCP connects a dead code agent to the full cross-repo evidence it needs

A graph can show only the evidence the agent can reach. The Model Context Protocol gives an agent a way to connect to sources of git history, dependency relationships, test coverage, and runtime telemetry across service boundaries. History shows when a symbol changed and whether the commit was human- or agent-generated, while the dependency graph supplies call and import relationships. Coverage separates test references from production connections, and per-service telemetry can reveal live traffic to code that looks unused statically.

Pharaoh’s MCP server makes the repository-level interaction concrete. It exposes get_unused_code and get_codebase_map, which a coding agent in Claude Code or another MCP client can query for structured findings. The documented setup uses npx @pharaoh-so/mcp and repository authorization. A request can then target a particular repository and module, keeping Dead, Possibly unused, and Used only by tests results separate. The agent shows the evidence before it proposes removing the code.

MCP supports local subprocess execution through stdio and network-distributed SSE/HTTP for cloud pipelines. That lets the dead code server support both a developer session and a CI/CD agent without changing its architecture. Connecting one repository does not establish that no caller exists elsewhere, so the broader arrangement needs the relevant history, graph, coverage, and telemetry sources within the agent’s authorized reach.

What persistent context lets a cross-service agent do that a one-shot scan cannot

One scan is rarely enough to finish cleanup across services. An agent may classify one candidate, defer another, and discover a loading convention that changes how an entire directory should be understood. Without persistent context, the next session repeats those investigations or contradicts their results. Continuity requires a record of what the project has already established about its own structure.

Persistent markdown files loaded at session start provide a practical way to preserve that knowledge. With the pattern that Claude Code made popular, teams can write down architecture decisions, naming conventions, and framework entry points before graph traversal starts. For dead code work, memory also needs prior classifications, deferred findings, applied removals, and their post-removal build results. Generated code in target/ directories and plugins loaded by convention belong in the same record as known false-positive patterns. Because the codebase is versioned, these findings must respect the commit graph.

When tool registries, credentials, and earlier findings are kept, injected instructions or stored biases can shape which tools the agent chooses later. Memory is not automatically trustworthy evidence. Audit trails and bounded autonomy are therefore part of the runtime design, especially when a remembered classification can influence a future deletion. Continuity is useful only when the agent can retain findings without turning every stored claim into unquestioned authority.

The bounded orchestration shell a deletion-capable agent must operate inside

Permission to investigate does not imply permission to delete. A cross-service agent needs a bounded orchestration shell with scoped identities, tool permissions, approval gates, and rollback. Christopher Cruz’s 2026 AI Runtime Infrastructure describes costly cases where agents fail as they carry out work, after planning has begun. For a deletion agent, a wrong classification may look like a sound conclusion without showing up as a system error. Its effects can emerge later as a failed build or a silent regression.

The controls therefore belong throughout execution. The agent can have read access across repositories while it can write only to branches other than main. Deletion proposals become pull requests, and a human reviewer or secondary validation agent confirms the evidence chain before a deletion is committed. After a candidate is disabled, the workflow runs the build, runs tests, and checks runtime telemetry. Any validation failure triggers an automatic revert to restore the repository.

Establishing production reachability requires incremental removal and the broader validation loop together, with approval distinct from a green build result. Hüseyin Özgür Kamalı and colleagues’ 2026 Rethinking Code Review in the Age of AI proposes reviewers acting as supervisory operators while retaining human judgment at key decision points. The same model fits deletion work. Humans assess the evidence and the decision, while the shell limits what the agent can do before and after that assessment.

A phased agent recipe for detecting and safely removing dead code across services

To put this architecture into practice, begin with graph construction and an explicit entry-point inventory. Parse the services with a structural parser that covers PHP, Python, TypeScript, JavaScript, and Vue to build per-service import and call graphs. Enumerate HTTP handlers, gRPC implementations, event-bus subscribers, cron registrations, CLI registrations, and convention-loaded plugin directories. Load persistent project conventions and prior findings before interpreting reachability. This gives the traversal its roots and preserves known exceptions.

Next, find and rank candidates across the connected repositories. Include unused files, named exports that have no importers outside tests, and symbols that have no internal callers and no detected external contract. Apply known filters for framework entry points, reflection, string dispatch, and dynamic imports before invoking the LLM. Only ambiguous candidates need model review. Where Pharaoh supplies the graph interface, connect through npx @pharaoh-so/mcp, authorize the repositories, and use get_codebase_map and get_unused_code to retrieve the relevant structure and findings.

For each ambiguous candidate, have the agent inspect its surrounding source, naming files, intended module entry points, and remembered loading conventions. Require a Dead, Possibly unused, or Used only by tests classification with an evidence chain. Ambiguous findings stay out of automated removal. Surface Possibly unused and Used only by tests candidates for human review, since a test-only export may need to be connected to the application.

For Dead-classified candidates, comment out the code before deleting it, then run the build, tests, and runtime checks. Confirm removal only when all validation passes, and roll back automatically on failure before touching the next candidate. Keep batches small enough for a human to review, attach their evidence to pull requests, and keep the approval gate in place before deletions are committed. Write each confirmed removal and its validation evidence into persistent memory. Each confirmed deletion tightens the graph, reduces context noise for the next session, and accelerates the documentation, refactoring, and code review work that reads the same codebase.

This article is sponsored by UFO.

Methodology & sources

  1. Detecting AI Coding Agents in Open Source: A Validated Multi-Method Census of 180 Million Repositories

    Provided the repository census data and agent commit statistics cited in the section on AI-generated code raising visibility stakes.

  2. Rethinking Code Review in the Age of AI: A Vision for Agentic Code Review

    Provided the model of human reviewers as supervisory operators retaining judgment at key decision points, cited in the bounded orchestration section.

  3. What to Cut? Predicting Unnecessary Methods in Agentic Code Generation

    Provided benchmark evidence that agents with graph context outperform baseline LLM agents, and the ranking approach using call and dependency graphs.

  4. AI Runtime Infrastructure. Christopher Cruz February 2026 Abstract

    Provided the framing of costly agent failures during execution after planning has begun, cited in the bounded orchestration section.

Every new piece from Working Agents, as it's published.

Follow