Preventing Test Leakage: Using Composer autoload-dev for Lean Production Runtimes
Learn how to use Composer's autoload-dev feature to separate production logic from test suites, reducing memory overhead and preventing development code from leaking into production.
05 May 2026, 19:37 UTC

The Production Class Map Problem
When building a PHP application, your development environment is cluttered with tools that have no business running in production. You have test suites, mock objects, static analysis configurations, and seeding scripts. While these are essential for quality, including them in your production class map increases the memory footprint of the Composer autoloader and, more importantly, creates a risk where development-only logic could accidentally be triggered in a live environment.
The solution is a strict separation between autoload and autoload-dev. By isolating development namespaces, you ensure that your production environment only knows about the code it actually needs to execute.
Defining the Dev-Only Boundary
Composer allows you to define two distinct autoloading sections in your composer.json. The standard autoload section is for your core application logic. The autoload-dev section is specifically for classes that should only be available during development and testing.
Both sections support the same standards: PSR-4 (the modern standard for mapping namespaces to directories), PSR-0, classmaps, and explicit file lists. The critical difference is that autoload-dev is ignored when the --no-dev flag is passed during installation or autoload generation.
The Architectural Split
- Production Autoload: Maps
App\tosrc/. This code is deployed to every environment. - Dev Autoload: Maps
Tests\totests/. This code is used by PHPUnit and PHPStan but is stripped from the production runtime.
Implementation Example
Consider a project where your business logic lives in src/ and your test helpers and mocks live in tests/. Here is how to configure the composer.json to ensure the Tests namespace never reaches your production server.
{
"autoload" : {
"psr-4" : {
"App\\" : "src/"
}
},
"autoload-dev" : {
"psr-4" : {
"App\\Tests\\" : "tests/"
}
}
}
Deployment Workflow
To apply this separation, you must change how you run Composer on your production server. Instead of a standard install, use the --no-dev flag. This command should be run by the deployment user (e.g., deploy or www-data) in the project root:
composer install --no-dev --optimize-autoloader
What this does:
- It skips the installation of packages listed in
require-dev. - It completely ignores the
autoload-devsection when generating thevendor/composer/autoload_classmap.phpandautoload_static.phpfiles.
The Danger of Cross-Contamination
The most significant risk when using autoload-dev is a Fatal Error caused by dependency leakage. If a class defined in the autoload-dev section is referenced anywhere in your production src/ code, the application will crash in production.
Because the autoloader simply does not contain the path to the dev classes in production, PHP will throw a ClassNotFoundException (or a Fatal Error in older versions) the moment that code path is hit. This often happens when developers use a test helper or a mock class inside a production service for "quick debugging" and forget to remove it.
How to Verify the Separation
You can verify that your production environment is lean by attempting to instantiate a test class after a --no-dev install. Run this via the CLI on your production-like environment:
php -r "require 'vendor/autoload.php'; var_dump(new App\Tests\TestCase());"
Expected Result: The command should fail with a Fatal error: Uncaught Error: Class 'App\Tests\TestCase' not found. If the class is successfully dumped, your --no-dev flag was not applied correctly, and your production environment is carrying unnecessary development overhead.
Trade-offs and Limitations
While autoload-dev improves performance and security, it adds a requirement for strict discipline. You cannot use "test-only" classes to facilitate production debugging. If you find yourself needing those classes in production, they belong in the primary autoload section, perhaps guarded by an environment variable check, rather than in autoload-dev.
Actionable Summary
To clean up your production runtime: move all test namespaces and mock directories into autoload-dev, update your CI/CD pipeline to use composer install --no-dev, and verify the exclusion by attempting to load a test class in a staging environment.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.