Product Decisions This Supports
- Build vs. buy for plugin/extension architecture. If the roadmap includes an extensible workflow engine, node system, billing handlers, or admin panel widgets, Lody is a ~2-day buy that replaces hand-maintained registration arrays — the classic source of merge conflicts and "forgot to register the new handler" bugs in multi-team Laravel repos. The real investment is not the dependency, it's the convention: naming the directory, defining the qualifying interface, and writing the plugin authoring guide. Budget one to two weeks for the full increment (wrapper, manifest cache, observability, docs), not one line.
- Roadmap sequencing for modular monolith decomposition. Discovery-based registries are the cheapest first step toward module independence: extensions declare themselves by contract in an agreed path instead of being wired into a central provider. That decouples teams without the cost of a service split, and it is reversible if modules later become separate services.
- Feature-level decision: auto-registration vs. explicit lists. For any feature where "which implementations exist?" is answered today by a hardcoded array, Lody converts a maintenance liability into a convention. Apply it where the list changes often and is owned by multiple people; do not apply it where the list is stable and small — see below.
- Platform and internal-tooling leverage. Auto-registering Artisan commands, health checks, export formats, or admin resources by interface or trait gives platform teams a reusable primitive that many squads consume — a small dependency with outsized surface area, provided it is wrapped.
- Explicitly does not support: per-request discovery. Filesystem traversal plus reflection per candidate is proportional to files scanned; that cost belongs at worker boot or in a deploy-time manifest, never in a request cycle.
When to Consider This Package
Adopt when all of the following hold:
- You have (or are about to build) a plugin/extension point with a clear qualifying contract — an interface, an abstract base class, or a PHP attribute — and extensions live in agreed directories.
- Multiple teams or contributors add implementations on their own schedule, so a central registration file creates coordination cost.
- Discovery can run once per deploy or per worker boot, with results cached in a manifest; you can measure the cost at your realistic file count (100 / 1,000 / 10,000).
- You scan first-party directories only. Filtering by instance or trait autoloads candidates, so a single class with an include-time fatal can break the boot path — discovery is unsafe against partial or untrusted third-party code.
- You accept a
0.x, single-maintainer, 95-star, zero-dependent dependency with a pin, a fork plan, and a named owner.
Look elsewhere (or use Symfony Finder directly) when:
- The application is a conventional CRUD product where a ten-line explicit registration list already works. Discovery buys indirection with no product payoff.
- You are not on Laravel, or your value proposition leans on
base_path() and Laravel collections — setBasePath() makes it work, but a plain PHP service should just use Finder.
- You run
--classmap-authoritative or otherwise have autoloading that doesn't map namespaces cleanly to directories; PSR-4 reverse-resolution is a heuristic and will silently drop classes that fail classExists().
- You need discovery to be fault-tolerant and loud. Lody's failure mode is quiet: unresolvable classes vanish from the collection, and v0.7.1 formalised an empty collection for missing paths. "Registered fewer handlers than exist" is a high-severity production bug for a registry. If you can't wrap it with a count assertion or a boot-time report, don't adopt it.
- Your framework upgrade cadence is slower than the maintainer's. Release history is explicit: each version adds the newest Laravel major and drops the oldest (v0.7.0 added Laravel 13, dropped 10). Adopting means inheriting their calendar.
How to Pitch It (Stakeholders)
For executives (one line): A small, free MIT utility that lets our Laravel app discover its own plugins automatically, replacing hand-edited registration lists that cause merge conflicts and forgotten-registration bugs — roughly a one-to-two-week increment, not a platform project.
For engineering: composer require lorisleiva/lody gives us Lody::classes('app/Plugins')->isNotAbstract()->isInstanceOf(Plugin::class) as a chainable, memory-efficient lazy collection with filters for inheritance, traits, and methods. It resolves filenames to classnames via our existing PSR-4 map, so nothing needs configuring to start.
The honest caveats, stated up front:
- Maturity is thin. 95 stars, zero dependents, one maintainer,
0.x versioning. We pin it and we own an exit plan — the API surface we use is three methods, so a replacement is days, not months.
- Its failure mode is silent. A class that doesn't resolve disappears without an error. We mitigate by asserting a non-zero registry count at boot and logging discovered-vs-expected, so under-discovery pages us instead of quietly disabling a plugin.
- Cost is boot-time, not request-time. We discover once per deploy into a cached manifest and measure it at production file counts before rollout.
- It autoloads candidate files, so we scan our own directories only — never vendor.
What we're asking for: approval to adopt with a pin and a named owner, and agreement on the qualifying contract (interface vs. attribute) before the first extension point ships. The contract decision is the one that's expensive to reverse; the dependency itself is not.