One project’s composer.lock had 180 packages. The ones used directly, counted by hand, were twelve. The rest — over a hundred packages — were mostly dependencies dragged in by packages installed for a small function.

Typing composer require feels free. It is not. Every dependency is a door into your codebase, opening onto someone else’s code — and that door never fully closes again.

Every dependency is a door

When you install a package, that package’s code runs on your server, with your application’s privileges. It can reach your database, see your file system, read your environment variables.

This does not mean the package is malicious — but if it gets compromised one day, your application gets compromised too. This is what a supply-chain attack is: the weak link is not the code you wrote, it is the code you trust. If you trust hundreds of packages, that means there are hundreds of teams you have to trust.

The line items on the bill

A dependency’s cost does not end at installation:

  • Security surface. Every package is a piece of code you have not audited. With transitive dependencies, no one fully knows what you are actually trusting.
  • Maintenance. A package gets abandoned, breaks on a new PHP version, conflicts with another package. Its problem is now your problem.
  • Upgrade friction. When you want to upgrade the framework, you are only as fast as your oldest, least-maintained dependency.

These line items are paid for a small package just as they are for a large one.

Small job, big door

The real issue is the asymmetry. A few lines of helper code that generate a slug, format a date — installing a package for these opens a wide door for a small gain. The gain is a few lines of code; the cost is the entire bill above.

There is a famous example: in the JavaScript world, an eleven-line package that left-pads a string was unpublished one day, and the builds of thousands of projects collapsed. For eleven lines, all of them had tied themselves to a door they never audited.

When to use a package, when to write it yourself

The rule is not ideological, it is economic:

  • Use a package: for hard, well-tested, security-critical work. Cryptography, HTTP clients, database drivers, the framework itself. Never write your own crypto code — this is where the package wins, no argument.
  • Write it yourself: for small, stable work you understand well. A few lines of helper code are cheaper than inheriting an entire dependency tree.

The measure is this: is the problem the package solves bigger than the bill the package brings? If it is bigger, install it. If not, write those few lines and own them.

Choosing a dependency

If you have decided to install a package, look at which one you pick: is it actively maintained, when was its last release, how many transitive dependencies does it drag in, is its license suitable, how broad is its community? Choosing a dependency follows the same logic as the innovation token in the boring architecture piece: pick the one that is mature, predictable, and will not be abandoned.


Adding a dependency looks like a way to avoid writing code. In reality it is getting someone else to write the code while you keep the responsibility.

Before opening an entire door for a small job, ask: is writing those few lines really more expensive?