kinoba / dead-code-bundle
Generate code coverage on live server
Package info
github.com/Kinoba/DeadCodeBundle
Type:symfony-bundle
pkg:composer/kinoba/dead-code-bundle
Requires
- php: ^8.1
- ext-pcov: *
- predis/predis: ^2.0
- psr/log: ^3.0
- symfony/console: ^7.0|^8.0
- symfony/framework-bundle: ^7.0|^8.0
- symfony/routing: ^7.0|^8.0
Requires (Dev)
- carthage-software/mago: ^1.49.0
- phpunit/phpunit: ^10.5|^11.5|^12.0
Suggests
- symfony/twig-bundle: To enable the dashboard controller and render the coverage report.
Provides
None
Conflicts
None
Replaces
None
This package is not auto-updated.
Last update: 2026-09-17 16:50:24 UTC
README
🚧 Beta: DeadCodeBundle is in active development. APIs, configuration keys and storage formats may still change. All feedback, bug reports and pull requests are very welcome!
DeadCodeBundle
A Symfony bundle that detects dead code in production by sampling real-traffic line coverage with PCOV and aggregating the results in Redis, so you can see exactly which lines of your application are never executed by real users.
Overview
Unlike static analysis or test-coverage tools, DeadCodeBundle measures coverage from live HTTP traffic. On each request (or a sample of them), it starts PCOV, lets the request run, then stops PCOV and stores the per-line hit counts in Redis. Over time this builds a picture of which files, classes and lines are actually exercised in production versus code that is never reached — a strong signal for safe removal candidates.
Features
- Live production coverage — collects real per-line execution data via PCOV during actual HTTP requests, not synthetic tests.
- Configurable sampling — collect coverage on a percentage of requests (
sampling_rate) to keep overhead negligible on high-traffic apps. - Redis-backed aggregation — coverage from many requests/processes is merged and stored in Redis with a configurable TTL, so data self-expires and stays fresh.
- Path exclusion — ignore vendor code, cache, tests, or any other paths via
ignored_paths. - Web dashboard — a built-in
/dead-code/dashboardpage showing global coverage percentage, per-file breakdowns, and source code with line-by-line coverage highlighting. - JSON API —
/dead-code/apiexposes the same data for scripting or external tooling. - CLI report —
php bin/console dead-code:reportprints a coverage summary and highlights the least-covered files, ideal for CI or terminal use. - One-click reset — clear all collected coverage data from the dashboard or via the
/dead-code/clearroute.
Documentation
Configuration reference
Configuration lives under the dead_code key, typically in config/packages/dead_code.yaml:
dead_code: enabled: false # master on/off switch redis_dsn: "redis://localhost:6379" # Redis connection used to store coverage sampling_rate: 100 # % of requests to sample (1-100) cache_ttl: 86400 # seconds before stored coverage entries expire ignored_paths: # substrings matched against reported file paths - "vendor/" - "var/cache/" - "tests/"
| Key | Type | Default | Description |
|---|---|---|---|
enabled |
bool | false |
Enables request collection. Keep false in environments where you don't want overhead. |
redis_dsn |
string | redis://localhost:6379 |
DSN passed to the Redis client used for storage. |
sampling_rate |
int | 100 |
Percentage (1-100) of main requests that are actually profiled. |
cache_ttl |
int | 86400 |
TTL (seconds) applied to stored coverage entries in Redis. |
ignored_paths |
list | ['vendor/', 'var/cache/', 'tests/'] |
Paths excluded from collected/reported coverage. |
Routes
The bundle registers three routes (see config/routing.php):
| Route | Path | Method | Description |
|---|---|---|---|
dead_code_dashboard |
/dead-code/dashboard |
GET | HTML dashboard with global stats and per-file view. |
dead_code_api |
/dead-code/api |
GET | Same data as the dashboard, as JSON. |
dead_code_clear |
/dead-code/clear |
POST | Clears all stored coverage data. |
CLI command
php bin/console dead-code:report
Prints the global coverage percentage and lists every file with less than 100% coverage.
How it works
CoverageListener::onKernelRequestdecides whether to collect (based onenabledandsampling_rate) and starts PCOV for the current request.CoverageListener::onKernelTerminatestops PCOV, normalizes the collected line hits (PCOV's-1sentinel for "executable but not executed" is clamped to0), and stores them.RedisStoragemerges hit counts across requests/processes by summing them, keyed with the configuredcache_ttl.CoverageReporterreads back the aggregated data, reads matching source files from disk, and builds the per-file/per-line breakdown used by the dashboard, API and CLI command.
Installation
Make sure Composer is installed globally, as explained in the installation chapter of the Composer documentation.
Requirements
- PHP 8.1 or higher
- the
pcovPHP extension (php -m | grep pcovto check) - a reachable Redis server
- Symfony 6.4, 7.0 or 8.0 (
symfony/framework-bundle,symfony/console,symfony/routing) - optionally
symfony/twig-bundle, to render the HTML dashboard
Step 1: Download the bundle
composer require kinoba/dead-code-bundle
If the pcov extension isn't installed yet, Composer will refuse the requirement. Either
install the extension first (pecl install pcov), or, for local testing only, bypass the
platform check:
composer require kinoba/dead-code-bundle --ignore-platform-req=ext-pcov
Step 2: Enable the bundle
If you don't use Symfony Flex, enable the bundle by adding it to
config/bundles.php:
// config/bundles.php return [ // ... Kinoba\DeadCodeBundle\DeadCodeBundle::class => ['all' => true], ];
Step 3: Import the routes
Register the bundle's routes so the dashboard, API and clear endpoints are reachable, e.g. in
config/routes/dead_code.yaml:
dead_code: resource: "@DeadCodeBundle/config/routing.php" type: php
Step 4: Configure the bundle
Create config/packages/dead_code.yaml:
dead_code: enabled: "%env(bool:DEAD_CODE_ENABLED)%" redis_dsn: "%env(DEAD_CODE_REDIS_DSN)%" sampling_rate: 10 cache_ttl: 86400 ignored_paths: - "vendor/" - "var/cache/" - "tests/"
Then set the corresponding variables in your .env (or .env.local) file:
DEAD_CODE_ENABLED=1 DEAD_CODE_REDIS_DSN=redis://localhost:6379
Start with a low sampling_rate (e.g. 10) on production traffic to keep overhead minimal,
and increase it if you need more data faster on lower-traffic environments.
Step 5: Verify the installation
- Make a few requests to your application with
enabled: true. - Visit
/dead-code/dashboardand confirm coverage data is appearing. - Or run
php bin/console dead-code:reportto see a summary in your terminal.
If the dashboard is empty, double-check that the pcov extension is loaded and that
redis_dsn points to a reachable Redis instance.
Contributing
Contributions are very welcome, especially while the bundle is in beta!
-
Fork the repository and create a branch for your change.
-
Install dependencies:
composer install. -
Run the test suite before opening a PR:
vendor/bin/phpunit -
Keep changes focused and add/update tests for any behavior change.
-
Open a pull request describing the motivation and the change.
If you run into a bug, have a question, or want to suggest an improvement, please open an issue — feedback at this stage is especially valuable and directly shapes the bundle.
This project is supported by Kinoba
Released under the MIT License