← All notesSYMFONY·22 Oct 2024·5 min

When to build Symfony bundles (and when to stop)

Bundles are powerful. They’re also a premature abstraction waiting to happen. The trick is knowing which one you’re building.

Symfony's bundle system is elegant engineering. It promises reusable, shareable, portable code — and delivers on that promise roughly 30% of the time. The other 70%, it delivers complexity with a side of indirection, in exchange for a reuse that never actually happens.

The reuse fantasy

‘We might need this in another project’ is not architecture. It's wishful thinking in a trench coat. You probably won't need it elsewhere — and if you do, the requirements will be different enough that your premature abstraction becomes the thing you have to fight, not the thing that helps. You'll spend more time bending the bundle to fit the second case than you'd have spent writing it twice.

01

Build it inline first

Solve today's problem in the app. No interfaces-for-the-sake-of-interfaces, no configuration tree for a thing used in exactly one place.

02

Extract on the third use

By the third time you build something, you finally understand it well enough to draw the boundary in the right place. The first two teach you where it isn't.

A bundle is a public API

This is the part people skip. The moment you extract a bundle, you've signed up for the obligations of a library maintainer — even if the only consumer is you, six months from now, having forgotten every assumption you baked in.

  • Semantic versioning — because a breaking change now ripples across every consumer.
  • Documentation — the config options, the extension points, the gotchas you'll otherwise re-discover by debugging.
  • Upgrade testing across every project that depends on it, every time Symfony itself moves.

The honest test

Before you extract, ask one question: would I pay this overhead for a single project? If the bundle is genuinely cross-cutting — auth, a house API client, a shared design system — the answer is yes, and the abstraction earns its keep across five projects. If you're extracting ‘a service that emails the admin’, the answer is no, and you've just built a maintenance liability with a composer.json.

The best abstraction is the one you were forced into by the third real use case — not the one you imagined before the first.

A bundle is a public API. Semantic versioning, documentation, issue triage, upgrade testing across every consumer. That overhead pays for itself across five projects — and absolutely doesn't for one.

Still with me?

Got a system that reads like this one?