Skip to content

The Drift Log · Software inventory you can trust

Detecting Stubs Disguised as Internal Libraries

1 September 2026 · 3 min read · 719 words · established

Shelves of empty glass shells with two lit solid crystals among them

Internal catalogs frequently count scaffolded stubs as real software. Here is how static analysis fails to catch them and how to verify execution.

Your internal catalog reports 412 shared components. Two teams need an auth token refresher; both search the registry, find @org/auth-refresh, import it, and discover within an hour that the exported function returns a hardcoded empty object.

The module has clean TypeScript definitions, a markdown file explaining its architecture, and a CI badge showing passing tests. The tests verify that the module exports a function. The engineer who created the repository left the company fourteen months ago.

A crawler saw a package.json, an export map, and a passing test suite. It logged a valid internal library. In reality, the repository contains three files of scaffolding and a promise that was never kept.

The mechanics of a confident stub

Scaffolding tools generate boilerplate in seconds. When a platform team creates a template repository or an engineer stubs out an RFC, the resulting artifact looks identical to production software from the outside:

export interface TenantPolicy {
  tenantId: string;
  allowedOrigins: string[];
}

export async function resolveTenantPolicy(tenantId: string): Promise<TenantPolicy> {
  // TODO: connect to control plane grpc service
  return { tenantId, allowedOrigins: ["*"] };
}

To a catalog scraper, this file is fully formed. It has typed inputs, a typed return contract, zero syntax errors, and zero linter warnings. A unit test that asserts resolveTenantPolicy("t-123") resolves without throwing will pass.

When an automated pipeline aggregates your component registry by inspecting manifests and git repositories, it registers this as functional capability. The catalog count increments. The team dashboard turns green. The organization believes it owns a tenant policy resolver.

It does not. It owns four lines of typed placeholders.

Static manifests lie politely

Static analysis tools are built to verify grammar and structure, not semantic completeness. They answer whether an interface is valid, not whether work occurs behind the interface.

This is where a software inventory becomes dangerous. If your inventory is populated by manifest scraping, it creates an illusion of capability. Downstream teams make architectural decisions based on what the catalog claims is available:

  1. Team A assumes Team B's stub handles multi-region routing.
  2. Team A builds their service assuming that dependency exists.
  3. Integration fails during staging because the function body is empty.
  4. Team A now writes their own bespoke multi-region router under deadline pressure.

You now have two libraries in the catalog claiming to do the same job: one that does nothing, and one tied directly to Team A's local service context. Neither is a reusable component. This is why a component count is not an inventory. A count measures how many directories contained a configuration file. An inventory measures what can be called, executed, and relied upon.

Proving occupancy with executable interfaces

The only way to separate an operational component from a hollow stub is execution against invariant assertions.

A component is not real because it exports a symbol. It is real because an execution environment can feed it structured input, trigger internal logic, and observe state transformation or deterministic output.

// A stub passes this:
test("exports function", () => {
  expect(resolveTenantPolicy).toBeDefined();
});

// A stub fails this:
test("applies default origin restrictions on invalid tenant", async () => {
  await expect(resolveTenantPolicy("invalid-tenant-id"))
    .rejects
    .toThrow(/TenantNotFound/);
});

Testing internal libraries against real assertions is difficult work. It requires providing valid fixtures, managing dependencies, and isolating side effects. If a component requires a live datastore to run its assertions, dynamic verification will fail unless that environment is simulated.

When a test cannot run because its environment cannot be provisioned, the correct verdict is not "working" or "broken"—it is inconclusive. A reliable inventory separates what has been proven from what is simply unverified. If you want to see how this separation looks in practice, inspect the verification statuses on our execution harness.

The Monday triage

You do not need to rebuild your platform to start cleaning out dead stubs. Run these three checks across your internal repositories on Monday:

  1. Find empty AST function bodies. Run a static query across your internal packages for function bodies containing fewer than two statements, functions returning raw primitives matching the return type, or files containing TODO or NOOP markers inside exported functions.
  2. Audit tests that only assert existence. Search your test files for .toBeDefined(), .toBeInstanceOf(), or tests with no assertions beyond invoking the function. Flag repositories where these constitute more than 20% of the test suite.
  3. Quarantine uninvoked exports. If an internal package has not had a release or an inbound import resolution in 180 days, demote it from the primary registry view to a quarantined tier. If no active repository resolves it within a 30-day grace period, archive the repository and strip the entry from your catalog count.

This post supports the longer argument in A Component Count Is Not an Inventory.

Keep reading

Next in the log

The Strategic Master Library · written and reviewed under the house's own epistemic rules: nothing claimed that we cannot show.