In the last post I shared a trick on how to reduce CLI project /vendor size by 70 %. Today we'll trim off a bit more with the no-so-known composer feature.
"Perfection is achieved, not when there is nothing more to add, but when there is nothing left to take away."
Some packages require another set of packages to delegate responsibility to an external source. Like any other dependency in life, that could be helpful and save time, but it also requires attention and care - it depends.
The following applies to any package that is bloated with transitional dependencies we don't use. But I'll use the one I work with daily as an example.
Let's say we install symfony/console
to a brand new empty project:
composer require symfony/console
Have a guess: how many packages do we have in our /vendor
now?
psr/container 2.0.2
symfony/console v6.3.2
symfony/deprecation-contracts v3.3.0
symfony/polyfill-ctype v1.27.0
symfony/polyfill-intl-grapheme v1.27.0
symfony/polyfill-intl-normalizer v1.27.0
symfony/polyfill-mbstring v1.27.0
symfony/service-contracts v3.3.0
symfony/string v6.3.2
Wow, 9 in total.
Briefly looking at the list, 5 packages deal with strings and language. These packages bring value if we use non-standard language operations - but for native English, those are redundant.
We also use symfony/console
just to invoke a PHP method called to render the output. Nothing fancy like console forms or dynamic games in the command line.
That means we don't need the following packages:
How do we know these packages are not needed? Let's verify our thesis.
We check for a "Symfony\Component\String" string in the symfony/console
Github repository in search
We can see 4 cases - primarily to measure the width of the terminal window:
We try to comment out those cases and run code. All good? We can remove it.
If you use this package locally in a single project and have enough space and DevOps take care of PHP extensions, there is little value in removing it.
But if the package:
... maybe it could be helpful for the developers that use it to make it easier to run and install.
I also check the symfony/string
package size to see its download trace:
composer require tomasvotruba/lines --dev
vendor/bin/lines measure vendor/symfony/string/ --short --json
↓
{
"lines_of_code": {
"code": 5561,
"comments": 570,
"total": 6131
}
}
That's 5 561 lines of PHP code with every download and 4 intl/mbstring packages in transition for 4 simple method calls. Those packages can prevent crash runs or installation on some nasty legacy project code, so if we get rid of them, we'll make our product more usable and cheaper to maintain.
Let's remove it. The first solution is using the remove
command in Composer:
composer remove symfony/string
And Composer replies in big red letters:
Removal failed; symfony/string is still present; another package may require it. See `composer why symfony/string`.
Damn... We don't have it in our composer.json
, so we can't really remove it, can we?
What other options do we have? Run rm -rf /vendor/symfony/string
might help, but we'd have to put it in some weird bash script, which seems like a code smell.
How do we use Composer to help us?
The Composer has this special section called "replace":
{
"replace": {
"symfony/string": "*",
}
}
Now run composer update
and see what happens:
Updating dependencies
Lock file operations: 0 installs, 0 updates, 4 removals
- Removing symfony/polyfill-ctype (v1.27.0)
- Removing symfony/polyfill-intl-grapheme (v1.27.0)
- Removing symfony/polyfill-intl-normalizer (v1.27.0)
- Removing symfony/string (v6.3.2)
The packages we don't use are gone, yay!
Wow, it looks like magic! It is, until we read the "replace" documentation.
In short, it tells the Composer: "This root package has the same features as symfony/string
".
The Composer then sees your root package and the symfony/string
package and decides: "Hm, there are 2 packages with the same name. Let's use the root one and remove the dependency from vendor".
Simple but effective solution. It's not for everyone, but if you care about it and having fewer dependencies brings your project more value and less code to worry about, then:
Happy coding!
Do you learn from my contents or use open-souce packages like Rector every day?
Consider supporting it on GitHub Sponsors.
I'd really appreciate it!