Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Dead Code Detector Laravel Package

shipmonk/dead-code-detector

PHPStan extension that finds unused PHP code: dead methods, constants, enum cases, properties, dead cycles, and transitively dead members. Detects dead tested code and can automatically remove unused code. Supports Symfony and other libraries with customizable usage providers.

View on GitHub
Deep Wiki
Context7

Product Decisions This Supports

Build vs. buy: buy. A Laravel-aware cross-reference analyzer that understands container resolution, route registration, event discovery, policy auto-resolution, Eloquent lifecycle hooks, and real-time facades is not a weekend script — it's a continuously maintained model of framework internals that changes with every Laravel minor. This package already encodes that knowledge (routes, events, schedules, gates/policies, commands, jobs, providers, middleware, notifications, form requests, mailables, broadcast events, JSON resources, observers, factories, seeders, migrations, plus Reflection/vendor/builtin/Composer providers). Building an equivalent in-house is a multi-quarter side quest with no product differentiation. MIT license removes procurement friction.

Roadmap: feature sunsetting as a verifiable exit criterion. Most "remove feature X" tickets stall because nobody can prove the code is truly unreachable. This tool converts a judgement call into a CI signal. When a flag is retired, an experiment concludes, or a legacy integration is decommissioned, the dead-member report is the evidence that the removal is complete — including transitively dead members (a dead facade method reveals the entity setters and constants it was the only caller of), which is exactly the tail of a sunset that teams normally miss.

Roadmap: refactor and modernization scoping. Dead cycles and transitive-dead reporting expose self-sustaining islands of abandoned code — clusters kept "alive" only by referencing each other. For a Laravel upgrade, a modularization effort, or a move to a new architecture, this is a cheap way to shrink the blast radius before you start, rather than discovering the dead weight mid-migration.

Quality strategy: coverage is a bad proxy for relevance. The differentiator is "dead tested code" detection with the tests usage excluder, which flags production classes whose only consumers are tests. That's not a lint error — it's a design smell and a roadmap input: either the code should be deleted, or the tests are coupled to internals in a way that will make every future refactor expensive. Very few tools make this visible, and it directly informs decisions about test strategy and internal API boundaries.

Engineering investment: consolidating static analysis into one gate. Because this is a PHPStan extension, it rides an existing, already-funded pipeline. No new tool, no new CI service, no new credential, no new dashboard. The adoption cost is one dev dependency, one includes: line, and a parameters block — which is the sort of decision a PM can approve on a sprint boundary rather than a quarter boundary.

Hygiene program with a measurable burn-down. Enable in report-only mode, capture the count of dead members, and treat it as a tracked metric alongside other tech-debt KPIs (LOC removed, PHPStan baseline entries retired, files deleted). This gives leadership a defensible trend line rather than an anecdote about the codebase "feeling heavy."

Onboarding and maintainability cost model. Dead code inflates the surface a new hire must read, review, and grep through. Reducing it is one of the cheapest levers on time-to-first-PR, and the removeDeadCode output format makes the work mechanical and delegatable to a junior or an agent rather than requiring senior judgement per case.

Due diligence / inherited codebases. For an acquisition, an inherited team, or a contractor handoff, running this once gives a fast, objective read on how much of the codebase is load-bearing. It's a much better signal than LOC or test coverage percentage.

What I would not use it for: as an autopilot cleanup bot. Automatic removal is a real capability, but the value it creates is capped and the downside (deleting a live code path in a dynamically dispatched corner) is not. Treat it as a review-accelerator, not an unattended janitor.

When to Consider This Package

Adopt when most of these are true:

  • The codebase is a Laravel/PHP application of meaningful size and age (roughly a year-plus of accumulated features, multiple sunset features, or an inherited repo). Small greenfield projects get little signal and some CI cost.
  • PHPStan is already in the pipeline, or is a decision you're willing to make anyway. If you have no static analysis, adding PHPStan plus this extension is a larger commitment than the pitch implies — sequence it as a second step.
  • You can run analysis over the whole codebase (src and tests). Partial-path analysis is the single biggest source of false positives, and false positives are the whole risk of this tool.
  • Your "magic" is mostly framework-standard. The Laravel provider list is unusually broad, but it will not know your in-house dynamic dispatch, your custom event bus, your attribute-driven registry, or admin-panel conventions (Filament, Nova, Livewire actions) that Laravel itself doesn't own. Budget for at least one custom MemberUsageProvider — the extension points are genuinely good, which is the main reason this is viable versus a black box.
  • You have a PR-review culture that can absorb a phased rollout: report-only → triage false positives → enforce on new code → only then allow removals.
  • You want a cheap first signal before committing to a larger refactor or modernization effort.

Look elsewhere, or defer, when:

  • No PHPStan, and no appetite to adopt it. This is strictly an extension; it's not standalone.
  • Analysis cost is prohibitive. Whole-codebase analysis on a large monolith can be a slow CI job. If your pipeline budget is tight, run it nightly or on a schedule instead of per-PR — but understand you've turned a gate into a report.
  • Heavily dynamic architecture. String-based dispatch everywhere, call_user_func by convention, code generated at runtime, or a plugin system that resolves classes from database rows. The generic providers (Reflection, vendor, builtin, enum, Composer scripts) cover more than you'd expect, but you will be writing and maintaining providers, and every false negative is a deleted feature.
  • You actually want runtime truth. This is static analysis — it finds unreachable code, not unexecuted code. For "what did production actually hit," you want APM/profiling data or code coverage from real traffic; the two are complementary, not substitutes.
  • You mainly want unused Composer dependencies. Different problem; composer-unused is the right tool. This operates at member granularity.
  • Vendor-risk tolerance is low. Note the adoption signal: 500+ stars but effectively no downstream dependents, single-vendor stewardship, MIT but no foundation behind it. Mitigate by pinning the version, treating it as a dev-only dependency (it is one — --dev), and accepting that the worst case is falling back to a fork or removing it, not a production outage.
  • Your team won't triage. A dead-code report that nobody reads is a red CI job that gets ignored or baselined, which actively damages trust in the rest of your static analysis. If there's no owner, don't turn it on.

Adoption sequencing I'd recommend: one sprint of report-only on a single bounded module or a legacy subsystem, not the whole monolith. Measure false-positive rate there. If it's low, expand; if it's high, you've learned the shape of the custom providers you need before you've burned credibility with a noisy repo-wide run.

How to Pitch It (Stakeholders)

One-liner: a free, MIT-licensed PHPStan extension that finds and — with review — removes unused PHP code, and that already understands Laravel's magic well enough to be trusted on our codebase.

For executives: we spend real engineering hours reading, reviewing, and grepping code that nothing calls. This tool finds that code automatically inside the static analysis we already run, and produces a diff we can review like any other PR. There is no license cost, no new vendor, no production runtime dependency — it's a dev-only package and the exit cost is one line deleted from composer.json. The measurable outcomes are lines of code removed, files deleted, and a declining count of known-unused members over time. Secondary benefit: it flags production code that only tests use, which is a leading indicator of refactoring cost.

For engineering leadership: the integration is small — one dev dependency, one includes: in phpstan.neon.dist, one parameters block. It respects our opt-outs (you can disable methods/constants/enum cases/properties individually), supports Symfony, Doctrine, PHPUnit, PhpBench, Behat, PHPStan, PHPat, Nette, Laravel, Eloquent and Twig out of the box and auto-enables them based on composer.json, and exposes two clean extension interfaces (MemberUsageProvider, MemberUsageExcluder) for our in-house magic. The tests excluder is the feature I'd push hardest: it separates "used" from "used only by our tests," which is a much sharper signal than anything we have today.

For the engineers doing the work: it plugs into the PHPStan run you already have. It distinguishes regular AST usages, vendor-overridden members, and genuinely magic calls, so the noise floor is lower than a naive grep-based tool. It reports transitively dead members as tips under the root cause rather than as hundreds of separate errors, and --error-format removeDeadCode produces an actual diff. The one rule I'll set: we adopt in report-only mode first, we triage every reported member before deleting anything, and auto-removal lands only as a reviewed PR — never as an unattended CI job. The value here is a shorter review, not a robot with delete access.

The honest caveats to state up front: it's static analysis, not runtime truth — it proves unreachability, not lack of traffic. It needs whole-codebase analysis to be accurate, which costs CI time. It will not know our bespoke dynamic dispatch on day one, so we should plan for one custom provider. And at ~500 stars with no downstream package dependents, we are early adopters — which is fine for a pinned, dev-only tool, but should be a conscious choice rather than a surprise.

Ask: one sprint, one bounded legacy module, report-only, with a single named owner. Success criteria: false-positive rate low enough to justify repo-wide rollout, and a triaged removal PR merged.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel
draftsmaninc/draftsman
square-bit/laravel-volition
himito/laravel-mailman
htinlynn/genius-helper
maco-studios/hephaestus
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk
martialbe/flysystem-oss
chondal/3312client
jeroen-g/flickr
opscale-co/nova-genbi
itsjustvita/laravel-retention
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log