By Greg Nowak. Last updated 2026-07-06.
A tidy composer.json will not rescue a bad dependency strategy, but it does remove a surprising amount of friction. When package metadata is inconsistent, every dependency change becomes harder to review, hand over, and automate. For business owners, operations leads, and agency teams, that shows up as slower maintenance and avoidable uncertainty.
The practical 2026 default is simple: use Composer's own validation tools, enable package sorting for future changes, and add ergebnis/composer-normalize for full-file normalization. The result is not cosmetic perfection. It is a quieter workflow where dependency changes are easier to understand and CI can catch drift before it reaches a release branch.
Why this matters beyond formatting
A clean composer.json gives reviewers a better signal. If a pull request adds one package, removes another, or changes a platform constraint, the diff should show that decision clearly. It should not be buried in reordered keys, spacing changes, or a manually edited block that looks different from the rest of the file.
That matters most when several people touch the same PHP estate: an in-house developer, a freelance specialist, a support agency, and a project manager all looking at the same repository. Normalization gives them a shared baseline.
- Package lists stay predictable.
- Metadata changes become easier to review.
- Lock-file drift is caught earlier.
- Style arguments move out of code review.
Where each Composer tool fits
Composer already includes useful checks, but each tool has a different job. The mistake is expecting one setting to do everything.
| Need | Use | What it gives the team |
|---|---|---|
| Validate the file and lock relationship | composer validate --strict |
Schema checks, lock-file checks, and a non-zero exit code for warnings. |
| Keep new requirements alphabetized | sort-packages |
Cleaner future changes when developers run composer require. |
| Normalize the full file | composer normalize |
Consistent key order, formatting, and package metadata structure. |
| Enforce the rule in CI | composer normalize --dry-run |
A failing build when the file or lock hash needs attention. |
A sensible project setup
For most application and agency projects, install Composer Normalize as a development dependency and commit the resulting configuration. Composer plugins execute code, so Composer's allow-plugins setting should be explicit rather than left to local prompts.
composer require --dev ergebnis/composer-normalize
composer config allow-plugins.ergebnis/composer-normalize true
composer normalize
composer validate --strictThe first command installs the normalizer. The second records that this specific plugin is trusted. Then composer normalize rewrites the file into the expected structure, and composer validate --strict checks that the cleaned file is still valid and that the lock file is up to date when one exists.
Also enable Composer's built-in package sorting so day-to-day package additions do not slowly make the file noisy again:
{
"config": {
"sort-packages": true
}
}This setting is useful, but it is not a replacement for normalization. Composer documents it as a way for the require command to keep packages sorted when new packages are added. It does not normalize the whole document.
Put the check in CI
The real value comes when this becomes a repeatable rule. After the first cleanup commit, add these checks to the project pipeline:
composer normalize --dry-run
composer validate --strict--dry-run is the important flag. In CI, the normalizer should report the problem, show the diff, and fail the build rather than rewriting files behind the team's back. Developers can then run composer normalize locally and commit the result.
Be deliberate with lock-file behavior. Composer Normalize checks whether composer.lock is current and can update the lock hash when necessary. If your release process treats lock-file changes as sensitive, read the available flags before automating broadly. --no-update-lock can prevent the tool from updating the lock file, and --no-check-lock should be reserved for cases where you have a separate lock-file check.
What normalization will not fix
Normalization is hygiene, not dependency governance. Use the cleanup as a chance to inspect the file while it is easier to read.
- Move tools used only for development into
require-dev. - Remove stale packages left behind after old features or integrations.
- Check whether autoload settings should use PSR-4 for new code.
- Review PHP and extension requirements against production, not only local machines.
- Remove a manually maintained
versionfield when the repository can infer versions from VCS tags. - Question old scripts, custom repositories, and platform overrides that nobody owns anymore.
This is where an experienced reviewer matters. The tool can make the file consistent; it cannot decide whether the constraints, scripts, and package choices still match the business.
A good rollout pattern
For an existing codebase, do the normalization in a dedicated pull request with no functional dependency changes. That makes the large formatting diff easy to approve once. After that, every future dependency pull request should be smaller and clearer.
For agencies managing several PHP projects, standardize the rule in your project template, CI checklist, and handover notes. Small rules like this reduce review fatigue and make inherited projects less personal to the last developer who touched them.
If dependency hygiene, CI rules, and handover quality keep slipping between teams, Greg can help make them part of a repeatable delivery workflow.
Related on GrN.dk
- Web Browser Automation for Operations and Agency Teams: A Practical Guide
- AI automations need a spend dashboard before the first runaway bill
- Cloudflare Tunnel observability is better in 2026, making undocumented tunnel sprawl harder to ignore
Need help with this kind of work?
Make PHP delivery more repeatable Get in touch with Greg.