{"slug": "can-i-delete-this-code-a-practical-dead-code-audit-with-ai", "title": "Can I Delete This Code? A Practical Dead Code Audit with AI", "summary": "A developer published a practical guide to auditing dead code with AI assistance, using a fictional store campaign as a running example. The method starts by finding candidates, tracing their execution roots, checking product intent and external contracts, then making one small change and validating it against a recorded baseline of type checks, tests, and builds. The guide warns that AI can speed up the investigation but must not turn guesses into deletion instructions, and that removing source code does not automatically improve performance because bundlers may already exclude it.", "body_md": "You search for a component. There are imports. There are tests. Nothing looks broken.\n\nThen you follow the imports one level further and realize nobody can reach the feature from the application.\n\nSo, can you delete it?\n\nMaybe. Or you have just found a feature that was built correctly and never connected to the product.\n\nThat distinction matters when cleaning up a real repository. This guide walks through an evidence-based audit, using a fictional store campaign as the running example. It also shows how AI can speed up the investigation without turning guesses into deletion instructions.\n\n**The short version:** find candidates, trace their execution roots, check product intent and external contracts, then make one small change and validate it.\n\nDead code might be an unreachable branch, a function without callers, or a disconnected group of components, hooks, and services. Editor warnings, lint, and settings such as [TypeScript's noUnusedLocals](https://www.typescriptlang.org/tsconfig/noUnusedLocals.html) help with some local issues. They do not establish that every feature has a real consumer.\n\nAsk which valid execution root reaches the code. That root might be a page, API, command, or worker. A migration need not run through the UI. A component does not become an active product feature just because a test imports it.\n\nDeleting source also does not automatically improve speed. The bundler may already exclude it. [Tree shaking](https://webpack.js.org/guides/tree-shaking/) concerns build output. Reducing maintenance ambiguity is valuable independently; performance claims need separate measurements.\n\nStart with an old feature or a particular folder, rather than the entire repository. Record the commit, local changes, and investigation scope:\n\n```\ngit status --short\ngit rev-parse HEAD\n```\n\nThen run type checking, relevant tests, and the build through the project's existing scripts. Inspect their definitions in `package.json`. These initial results are your baseline: if a test fails after deletion, you can distinguish a new regression from an existing problem.\n\nRecord checks that already fail or cannot run. You need not repair the entire project before removing a helper. However, if an existing failure prevents you from evaluating affected behavior, establish another valid check or address that limitation first.\n\nSearch the suspected component name, exports, and file path. For an illustrative component called `PromoBanner`:\n\n```\nrg -n 'PromoBanner' src tests scripts\n```\n\nReplace the folders with those in your project. Read the matches rather than counting them. A name may appear in a comment, test, or central export. Aliases and indirect loading can escape a simple search.\n\nFor JavaScript and TypeScript, tools such as Knip can report suspicious files, exports, and dependencies. In a project where it is installed, run analysis with `pnpm exec knip`. Consult the [official getting started guide](https://knip.dev/overview/getting-started) for setup.\n\nBefore relying on the report, identify application entries, workers, commands, and public package interfaces. Do not mark every file as an entry merely to suppress warnings, or ignore something you have not investigated. Initial output is an investigation list; automatic bulk removal is premature.\n\nFor a component, ask who imports it, whether it renders in JSX, where its parent is mounted, and whether that page is reachable. Do not stop at the first import.\n\nFor a function, trace the caller back to a root. The caller may itself lack consumers. A re-export in `index.ts` is not sufficient evidence; find what reads that export.\n\nCheck side effects too. Loading a module may perform registration or initialization without providing a value used later. An apparently unused import can still support behavior. Public packages also require consideration of consumers outside the repository.\n\nReachability does not mean execution on every request. A dialog may require a click; an administrative report may require a particular role. An absence of runtime observations does not automatically establish the absence of a valid path.\n\nA route may be discovered by filename, a command started through a package script, or a job launched by CI or cron. When conventional imports are absent, inspect configuration, workflows, and deployment.\n\nDynamic loading matters too. A module name may come from configuration, or plugins may be discovered from a directory. That discovery contract is part of the consumption evidence.\n\nFor an endpoint, no requests from your frontend does not exclude another service as a consumer. If the external contract is unclear, record an unknown status and a specific follow-up question. “No consumer found in this repository” is a narrower claim than “no consumer exists.”\n\nImagine a store that previously had a campaign page. Searching for `PromoBanner` reveals an export, a test, and a component called `CampaignPanel`. At first glance, the banner looks used.\n\nFollow the chain: the panel uses a hook and campaign service, but the old route has been removed and no active page renders the panel. Connections inside the group remain. The group's connection to the application is missing. This is an isolated subgraph.\n\nIf the campaign has ended and no valid commitment remains, the group may be removable. If the page was missed during a redesign while the campaign remains necessary, restore its connection. The banner test cannot decide between these outcomes.\n\nDo not treat downstream importers as proof that a group is active. Investigate its root and current product purpose. You may have found obsolete code, or a capability whose user entry point is incomplete.\n\nIf only tests reach an implementation, mark it test-only and investigate why production usage is absent. Its behavior may be obsolete, or its wiring may be missing. Fixtures and mocks are different: they naturally have no production consumers.\n\nFor background workflows, inspect producer and consumer together. A scheduler may create jobs while its worker is not running in deployment. Deleting the worker can conceal the integration problem.\n\nIf the capability is required, restore the connection. If it is being retired, consider the producer, schedule, and existing work too. One quiet day does not prove a monthly workflow unused; observations must match its execution cycle.\n\nSimple statuses are enough, provided “no consumer found” is kept separate from “removal approved”:\n\n| Finding | Next action | \n|---|---|\n| Removal candidate | Final review, then a small deletion | \n| Test-only | Establish whether behavior is needed and why wiring is absent | \n| Intentionally inactive | Retain with reason, ownership, and a review point | \n| Missing connection | Complete the capability or retire the workflow | \n| Generated output | Review its generator and retention policy | \n| Unknown | Specify missing evidence; no deletion yet | \n\nRecord the path, established consumers, open question, and proposed action. For the campaign, a useful note is: “Consumed inside the panel and a test; no active route found; external service usage remains unchecked; do not delete the panel alone.”\n\nExplain confidence levels if you use them. Coverage of relevant paths matters more than the tool's certainty. Feature-flagged code and generated output need assessment against their own contracts.\n\nOnce the decision is clear, limit the change. Avoid mixing unrelated refactoring into the deletion. Check shared dependencies: a helper used by active checkout must remain when an old campaign disappears.\n\nRepeat baseline checks. Type checking helps expose broken dependencies, tests exercise covered behavior, and builds reveal some output and discovery problems. For a sensitive change, execute the affected user journey or background process as well.\n\nA passing build does not validate an external consumer. Checks should address the relevant contract and environment. Explain in the pull request what was removed, why, and which evidence supports the result.\n\nAI can collect references, follow consumption chains, and organize findings. But “delete unused files” oversimplifies the task. Divide the work into three stages: investigation, decision, and a focused change.\n\nGive the assistant a defined scope and require inspectable file paths and references. A practical prompt is:\n\n```\nReview the campaign module for unused-code candidates. For each candidate, trace consumers to entry points; separate test and production usage; inspect scripts, registration, and dynamic loading. Record possible external consumers and unanswered questions. Do not modify files during this pass.\n```\n\nThis encourages a reviewable record instead of an authoritative deletion list. If the assistant says the panel is unused, you should be able to inspect which paths it checked and which remain unresolved.\n\nAsk the assistant to critique its report: “For each proposed deletion, which execution path or external contract might the analysis have missed?” This can reveal new leads, but it is not independent confirmation. The same assistant can repeat its original assumption.\n\nVerify important references yourself. Compare “no usage exists” with the actual investigation boundary, and do not convert missing evidence into certainty. Product decisions, such as whether a campaign should continue, must come from a valid project requirement.\n\nAfter the decision is established, constrain the next request: “Remove only the retired campaign group. Preserve the shared checkout helper. Avoid unrelated behavior changes. Report the diff and actual validation results; stop before deleting any unresolved item.”\n\nThe assistant should distinguish checks it executed from checks it did not run. “Tests should pass” is not a test result. Review the diff and compare before-and-after evidence. AI accelerates investigation; confidence comes from evidence and validation.\n\nGive the assistant access to the relevant project context, including package scripts, framework configuration, and known deployment entries. A model that sees only one component cannot reliably judge the entire feature's reachability. If it cannot inspect a worker configuration or external contract, that limitation belongs in the report.\n\nRequest a compact evidence table: candidate, known consumers, entry path, test usage, unresolved question, and proposed action. For the campaign example, an acceptable row should expose the missing route and unresolved service consumer. A bare “unused” label leaves you with nothing to review.\n\nAlso separate proposed actions from completed work. An assistant may describe the correct validation commands without executing them, or suggest a deletion without producing the expected diff. Ask it to report actual changes and observed results. This keeps the workflow concrete whether you use an editor assistant, a terminal agent, or manual analysis.\n\nWhen retiring a route or feature, inspect its components, hooks, tests, and services. Give inactive code an owner and a review criterion. “Maybe later” should not become an indefinite status.\n\nIntroduce analysis into CI gradually: establish configuration and resolve false positives before preventing new issues. Hundreds of unexplained failures often encourage disabling the tool rather than understanding the code.\n\nChoose an old capability, trace its consumers, and resolve the open questions. The outcome may be fewer files or a restored connection. Either way, you should be able to explain why that action makes sense.\n\nHave you found a feature that had tests and internal imports but no reachable entry point? What helped you decide whether to remove it or reconnect it?\n\nOriginally published on [my blog](https://www.hassanaminidev.ir/en/blog/safe-dead-code-audit).\n\n*This article was adapted with AI assistance. The campaign scenario is illustrative.*", "url": "https://wpnews.pro/news/can-i-delete-this-code-a-practical-dead-code-audit-with-ai", "canonical_source": "https://dev.to/hassan95eb/can-i-delete-this-code-a-practical-dead-code-audit-with-ai-936", "published_at": "2026-10-07 09:36:49+00:00", "updated_at": "2026-10-07 09:46:56.548933+00:00", "lang": "en", "topics": ["ai-tools", "developer-tools", "ai-products"], "entities": ["Knip", "TypeScript", "webpack"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/can-i-delete-this-code-a-practical-dead-code-audit-with-ai", "markdown": "https://wpnews.pro/news/can-i-delete-this-code-a-practical-dead-code-audit-with-ai.md", "text": "https://wpnews.pro/news/can-i-delete-this-code-a-practical-dead-code-audit-with-ai.txt", "jsonld": "https://wpnews.pro/news/can-i-delete-this-code-a-practical-dead-code-audit-with-ai.jsonld"}}