# How Dead Code Detection Works in Codebase-Memory-MCP: Graph Analysis Explained

> Discover how Codebase-Memory-MCP detects dead code using graph analysis and SQLite. Learn about inbound degree queries and excluded code types for accurate identification.

- Repository: [Martin Vogel/codebase-memory-mcp](https://github.com/DeusData/codebase-memory-mcp)
- Tags: internals
- Published: 2026-07-13

---

**Codebase-Memory-MCP identifies dead code by querying inbound degrees of `CALLS` and `USAGE` edges in its SQLite-backed dependency graph, classifying functions with zero incoming connections as dead while exempting tests, entry points, and exported symbols.**

Codebase-Memory-MCP (MCP) builds a complete graph representation of your codebase where functions, methods, and classes serve as nodes connected by semantic edges. Dead code detection operates during the 3D layout phase in [`src/ui/layout3d.c`](https://github.com/DeusData/codebase-memory-mcp/blob/main/src/ui/layout3d.c), where the system analyzes the full graph to determine which functions are reachable and which are orphaned.

## The Dead Code Classification Matrix

MCP applies a strict hierarchy of rules to classify each node during the layout phase. The classification depends on node type, metadata flags, and inbound connection counts from the graph store:

- **Structural**: Non-function and non-method nodes (classes, variables, etc.)
- **Test**: Nodes located in test files (identified by `is_test` flags or path heuristics)
- **Entry**: Entry points marked with `is_entry_point` or `route_path` flags
- **Exported**: Symbols explicitly marked with `is_exported` in their properties
- **Dead**: Functions or methods with **zero inbound `CALLS`** and **zero inbound `USAGE`**
- **Single**: Functions or methods with exactly one inbound `CALLS` edge
- **Normal**: All other function/method nodes with multiple connections

## Dead Code Detection Implementation in layout3d.c

The dead code detection algorithm executes in five distinct phases within the layout engine.

### 1. Node Collection and Batch Preparation

The system first collects all node IDs from the current search result set to prepare for batch degree queries:

```c
int64_t *node_ids = malloc((size_t)n * sizeof(int64_t));
for (int i = 0; i < n; i++)
    node_ids[i] = search_out.results[i].node.id;

```

To avoid SQLite parameter limits, the implementation processes nodes in chunks defined by `DEAD_DEGREE_CHUNK`.

### 2. Batch Querying Inbound Degrees

The layout engine queries the store for inbound degrees of both `CALLS` and `USAGE` edge types using `cbm_store_batch_count_degrees`:

```c
/* Allocate buffers for inbound CALLS and USAGE counts */
int *in_calls = calloc((size_t)n, sizeof(int));
int *in_usage = calloc((size_t)n, sizeof(int));

/* Query the store in chunks to avoid SQL limits */
for (int off = 0; off < n; off += DEAD_DEGREE_CHUNK) {
    int cnt = (n - off < DEAD_DEGREE_CHUNK) ? (n - off) : DEAD_DEGREE_CHUNK;
    cbm_store_batch_count_degrees(store,
        node_ids + off, cnt, "CALLS", in_calls + off, deg_dummy + off);
    cbm_store_batch_count_degrees(store,
        node_ids + off, cnt, "USAGE", in_usage + off, deg_dummy + off);
}

```

This batch approach minimizes database round-trips by building bounded `IN (?, ?, …)` clauses.

### 3. Parsing Node Metadata Flags

Before classification, the system parses node-specific flags from the JSON `properties_json` stored with each node:

```c
node_flags_t nf = parse_node_flags(sn->properties_json);

```

These flags indicate whether the node represents a test file, entry point, exported symbol, or route.

### 4. Applying the Decision Logic

The core classification logic applies the decision matrix through a cascading conditional chain:

```c
bool is_fn = sn->label && (strcmp(sn->label, "Function") == 0 ||
                           strcmp(sn->label, "Method") == 0);
int ic = in_calls ? in_calls[i] : 1;   // inbound CALLS
int iu = in_usage ? in_usage[i] : 1;   // inbound USAGE

const char *status;
if (!is_fn)                         status = "structural";
else if (testish)                   status = "test";
else if (nf.is_entry || nf.is_route) status = "entry";
else if (nf.is_exported)            status = "exported";
else if (ic == 0 && iu == 0)        status = "dead";
else if (ic == 1)                   status = "single";
else                                 status = "normal";

result->nodes[i].status = status;

```

### 5. Error Handling and Fallbacks

If the batch query fails due to SQLite errors, the code defaults to `"normal"` status rather than incorrectly flagging code as dead. This safety mechanism ensures transient store problems do not result in false dead code positives.

## Store-Level Queries for Dead Code Detection

The heavy lifting occurs in `cbm_store_batch_count_degrees`, implemented in [`src/store/store.c`](https://github.com/DeusData/codebase-memory-mcp/blob/main/src/store/store.c). This function constructs parameterized SQL queries to count degrees efficiently:

```c
int cbm_store_batch_count_degrees(cbm_store_t *s,
    const int64_t *node_ids, int id_count,
    const char *edge_type, int *out_in, int *out_out)
{
    /* ... build the IN clause like "(?,?,?)" ... */
    bool has_type = edge_type && edge_type[0] != '\0';

    /* inbound degree */
    int rc = count_degrees_direction(s, node_ids, id_count,
        in_clause, has_type, edge_type, true, out_in);
    if (rc != CBM_STORE_OK) return rc;

    /* outbound degree */
    return count_degrees_direction(s, node_ids, id_count,
        in_clause, has_type, edge_type, false, out_out);
}

```

The function calls `count_degrees_direction` twice—once for inbound and once for outbound edges—to populate the degree buffers.

## Data Pipeline and Edge Creation

The dead code detection relies on `CALLS` edges created during the analysis pipeline. The file [`src/pipeline/pass_calls.c`](https://github.com/DeusData/codebase-memory-mcp/blob/main/src/pipeline/pass_calls.c) resolves function calls and inserts these edges into the graph, providing the connectivity data that the layout phase later queries to determine liveness.

## Summary

- **Graph-Based Analysis**: MCP detects dead code by examining inbound `CALLS` and `USAGE` degrees in the complete dependency graph.
- **Dual Edge Types**: A function is only dead if both `CALLS` and `USAGE` inbound counts equal zero.
- **Exclusion Logic**: Tests, entry points, routes, and exported symbols are explicitly protected from dead code classification.
- **Batch Processing**: Degree queries use chunked batch operations via `cbm_store_batch_count_degrees` to handle large codebases efficiently.
- **Safe Defaults**: Store errors default to `"normal"` status, preventing false dead code reports.

## Frequently Asked Questions

### How does Codebase-Memory-MCP define dead code?

Codebase-Memory-MCP defines dead code as any function or method node in the dependency graph that has zero inbound `CALLS` edges and zero inbound `USAGE` edges. This means no other function calls it, and no other part of the codebase references it through usage relationships.

### What prevents entry points and tests from being marked as dead?

The system checks node metadata flags parsed from `properties_json` before applying degree-based rules. Nodes marked with `is_entry_point`, `route_path`, `is_exported`, or test file heuristics receive `"entry"`, `"exported"`, or `"test"` statuses respectively, bypassing the dead code classification regardless of their inbound degree counts.

### Where does the dead code detection logic reside in the codebase?

The primary classification logic lives in [`src/ui/layout3d.c`](https://github.com/DeusData/codebase-memory-mcp/blob/main/src/ui/layout3d.c) within the layout phase implementation. The underlying degree counting queries are handled by `cbm_store_batch_count_degrees` in [`src/store/store.c`](https://github.com/DeusData/codebase-memory-mcp/blob/main/src/store/store.c), while [`src/pipeline/pass_calls.c`](https://github.com/DeusData/codebase-memory-mcp/blob/main/src/pipeline/pass_calls.c) generates the `CALLS` edges that feed the detection algorithm.

### What happens if the SQLite query fails during dead code detection?

If `cbm_store_batch_count_degrees` returns an error, the layout engine falls back to assigning a `"normal"` status to affected nodes. This conservative approach ensures that temporary database issues or connectivity problems do not cause the system to falsely report functioning code as dead.