Designing Composer Autoloading for Development and Production
Learn how to configure Composer’s PSR‑4 autoloading for instant class availability in dev and optimized class‑map lookups in prod, with verification steps and failure‑mode guidance.
23 Feb 2026, 16:35 UTC

Problem
Projects need a class‑loading mechanism that works instantly during development (so new classes are available without a rebuild) and that minimizes filesystem overhead in production (to keep request latency low). Composer’s PSR‑4 autoloading can satisfy both, but the choice of when to generate an optimized classmap affects performance and correctness.
Takeaway
Define a PSR‑4 map in composer.json, let Composer generate the PSR‑4 loader for development, and run composer dump-autoload -o to create an optimized classmap for production. Verify the loader works and regenerate the classmap whenever classes are added, removed, or renamed.
Requirements
- Autoload PSR‑4 namespaces without manual
requirestatements. - In development, reflect new classes immediately after they are saved.
- In production, avoid per‑request filesystem scans for known classes.
- Respect optional exclusions (e.g., test suites) and non‑class files.
Smallest Suitable Design
The design uses only two Composer‑generated files:
vendor/autoload.php– boots the Composer autoloader.vendor/composer/autoload_psr4.php– PSR‑4 namespace‑to‑directory map (always present).- Optionally,
vendor/composer/autoload_classmap.php– optimized class‑to‑file map (produced with-o).
In development the autoloader relies on the PSR‑4 map and scans directories on each request. In production the classmap replaces the PSR‑4 lookup for known classes, eliminating stat calls.
Trust/Data Boundaries
- Input trust: The
autoloadsection ofcomposer.jsonis trusted to contain correct namespace‑to‑path mappings. Composer writes these mappings intoautoload_psr4.php; any error here results in a missing class. - Data flow:
vendor/autoload.phpreturns a loader instance that registers aspl_autoload_callcallback. The callback first consults the classmap (if present), then falls back to the PSR‑4 map, then to classmap/file autoloaders. - Boundary: The generated files live under
vendor/composer/and are considered part of the Composer runtime; they should not be edited manually.
Operational Checks
Perform these steps to confirm the autoloader behaves as expected:
- Create a fresh project:
composer init -n
composer require "psr/log:^3.0"
# add autoload section
cat ><<EOF > composer.json
{
"autoload": {
"psr-4": {
"App\\": "src/"
}
}
}
EOF
- Add a class matching the namespace:
mkdir -p src/App
cat > src/App/Greeting.php <<EOF
- Install dependencies and test the loader:
composer install
php -r "require 'vendor/autoload.php'; \
$g = new App\\Greeting(); \
echo $g->say();"
# Expected output: Hello
- Generate an optimized classmap for production:
composer dump-autoload -o
# Check that autoload_classmap.php contains an entry for App\\Greeting
- Verify production behavior by removing the source file (should fail):
rm src/App/Greeting.php
php -r "require 'vendor/autoload.php'; new App\\Greeting();"
# Expected: Warning: failed to open stream: No such file or directory
- Restore the class and regenerate the classmap to recover:
cat > src/App/Greeting.php <<EOF
say();"
# Should print Hello again
Failure Modes
- Missing or malformed
autoloadsection: Composer skips autoloader generation;require 'vendor/autoload.php'returns a loader that has no registered callbacks, leading to "class not found" errors. - Stale classmap: After adding, renaming, or deleting a class without running
dump-autoload -o, the classmap points to a non‑existent or wrong file, causing autoload failures. - Overlapping PSR‑4 prefixes: If two prefixes map to the same directory, Composer uses the first entry in
composer.json. Changing the order can silently change which namespace resolves to a given class. - Opcode cache interference: Frequent
dump-autoloadcalls invalidate the cached bytecode of the autoloader files, reducing the benefit of OPcache in high‑traffic environments. - Mixing a custom
__autoloadfunction: Because Composer registers its loader viaspl_autoload_register, a global__autoloadwill be called after the Composer loader, potentially causing duplicate loads or fatal errors if the class is already defined.
Conditions That Would Change the Design
- If the project prefers zero‑runtime overhead and can tolerate a longer build step, using a pure classmap (generated with
-o) and omitting the PSR‑4 map entirely would be the smallest design. - When working in a monorepo with many packages that share a common vendor directory, isolating each package’s autoloader via separate
composer.jsonfiles may be necessary to avoid namespace collisions. - If the project relies heavily on non‑class files (e.g., function helpers) that must be loaded on every request, the
filesautoload directive should be added, and the classmap optimization should exclude those files to avoid stale references. - In environments where opcode caching is disabled or unavailable, the performance gain from
-ois smaller; teams might choose to keep the development workflow (no-o) even in production to simplify the release process.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.