beyondcode/laravel-package-tools
Laravel Package Tools streamlines package development with a fluent PackageServiceProvider base class that auto-registers config, migrations, commands, views, translations, routes, and assets. Cut boilerplate and ship Laravel packages faster with less setup code.
Architecture fit. beyondcode/laravel-package-tools is a developer-ergonomics layer, not a product capability. It ships a PackageServiceProvider base class that wraps Laravel's native service-provider primitives — config merging, view/translation loading, migration registration, publish groups, command and route registration — behind a fluent Package object with packageRegistered() / packageBooted() hooks. A TPM should frame the fit question as "how many first-party Laravel packages will we own?" rather than "is this a good library?" It earns architectural space only when a roadmap involves repeated package authorship: internal domain packages, SDKs, integration connectors, modular-monolith extraction, or a partner-facing plugin SDK. For a single Laravel application that merely consumes third-party packages, it is dead weight. The abstraction is thin and trivially substitutable — which lowers switching cost but equally caps strategic value, consistent with the ~0.54 opportunity score and 0.68 composite. The decisive architectural fact is that the base class sits on the boot path of every package that extends it. The unit of risk is therefore "all internal packages," not "one dev-time helper," and that reframing is what moves this from a tooling footnote to a governance line item.
Integration feasibility. Technically trivial — one Composer require, extend the base class, declare capabilities. Organizationally poor. The repository is archived, last released 2019-03-05, with 74 stars, zero dependents, and an MIT license. It predates Laravel 7 through 11 and the PHP 8.x line, and it carries no maintainer, no release cadence, no security-response process, no compatibility matrix, and essentially no community validation. Zero dependents means there is no upstream patch path and no ecosystem signal; every defect becomes ours to find, fix, and carry. A TPM should treat integration feasibility here as a supply-chain question, not an engineering-difficulty question. The decision is cheap and fast — an afternoon of evaluation, tens of lines of diff per provider to migrate off — which argues for making it now rather than deferring until package count and coupling have grown.
Technical risk. High lifecycle risk, low intrinsic security risk. The code performs no I/O and no network access, so direct exploit surface is minimal and it is unlikely to appear in a vulnerability feed. The real exposure is upgrade breakage: Laravel majors changing publish-group and migration-registration semantics, PHP 8.x deprecation noise, stale Composer constraints that block framework upgrades org-wide, and SCA/dependency-review gates flagging an abandoned dependency at release time. This risk compounds silently — it works today, then a single framework upgrade forces a simultaneous migration across every dependent package. There is also hidden coupling in the declared conventions: paths and publish tags are derived from name(), which must be declared first in the chain, so an ordering mistake or a package rename fails at runtime rather than in code review. That moves a class of defects out of review and into production.
Key questions.
spatie/laravel-package-tools — the maintained, conceptually aligned successor — acceptable, or does policy require an internally owned base class?Stack fit. The package targets the Laravel 5.5–6.x era. It is a defensible fit only for legacy packages already pinned there and not scheduled to move. On Laravel 9+, the maintained successor is the correct target: same conceptual model, configurePackage(Package $package) instead of class-property declarations, active maintenance, and broad version coverage. The mapping is close enough that migration is mechanical — hasConfigFile, hasViews, hasMigrations, hasTranslations, hasRoute, hasCommands translate near one-to-one, and the path and publish-tag conventions (config/{name}.php → {name}-config, and so on) are preserved. Do not adopt the archived package for new work under any route. The decision to record in the ADR is "pattern reuse, not dependency adoption": the useful value has largely been extracted already, and the remaining work is replication into something we control.
Migration path. Two viable routes.
configurePackage(), map methods one-to-one, and re-verify name() ordering. Most provider files change in tens of lines; the diff is reviewable in a single sitting. Lowest cost, preserves an upstream patch path, keeps the dependency-review gate quiet.acme/laravel-package-base if policy forbids third-party boot-path dependencies. Higher ownership cost, total control, no upstream drift — and it converts an unmaintained dependency into a small, testable, owned asset with a named owner.Whichever route is chosen, the durable deliverable is the internal package scaffolding template. Fix that first so new packages never start on the archived base and so the convention survives independent of which library backs it.
Compatibility. Validate against the full support matrix — PHP minor versions × Laravel majors × Composer constraints. Watch specifically for behavioral differences in publish tags, migration loading, and boot-order hook timing between the archived base and its replacement. Per package, run the existing suite plus a fresh-install smoke test: config publish, migration run, route resolution, view/translation resolution, and command discovery. Partial migration is the dangerous state — two base classes coexisting in one codebase produce silent behavioral drift in exactly those areas, which is worse than either end state. Close out waves fully rather than leaving them half-done.
Sequencing.
Maintenance. Steady-state cost for the archived package is ownership transfer: we inherit all future defect and compatibility work with no upstream to defer to. That cost is small in absolute terms — the code is roughly a hundred-plus lines and does no I/O — but it is unbounded in duration and lands on the boot path, so it is invisible until it breaks something at startup. The successor route trades a small, ongoing version-bump cadence for an external maintainer; the vendored route trades that cadence for a few hours per year of internal upkeep plus a test file. Both are cheaper than carrying an unmaintained dependency whose first failure arrives bundled with a framework upgrade deadline.
Support. There is no support channel: no maintainer, no issue triage, no security advisories, no documented compatibility matrix. Internal engineers become tier-1 through tier-3 support by default, and the only escalation path is reading the source. For a TPM this means the "support cost" line is not a vendor contract but an engineering-velocity tax paid at the worst possible time — during an upgrade crunch. Note the corollary: because the library is inert at runtime, a support failure manifests as build and boot failure, not as customer-visible behavior, which makes it easy to under-prioritize until it blocks a release.
Scaling. Cost scales linearly with package count and super-linearly with inconsistency. Ten packages on one convention is a one-time setup cost; ten packages split across two base classes is recurring drift in publish tags, migration ordering, and hook timing, plus a codebase where every new package is a judgment call. Standardizing the template is the scaling lever; the library choice is secondary. A TPM should size the payoff as engineer-days per package saved (roughly half a day of boilerplate each, per the package's own framing) multiplied by projected package count — the business case for standardizing is real, but it is satisfied just as well by an owned base class.
Failure modes. (a) Silent drift — two base classes in one repo, divergent publish tags and migration loading, discovered in a customer environment rather than CI. (b) Upgrade blockade — stale Composer constraints pin the org to an older Laravel/PHP line during a security-driven upgrade. (c) Runtime ordering failure — name() declared after dependent capabilities, paths resolving wrong only at boot. (d) Governance erosion — one team installs it as a transitive convenience and the dependency spreads past the documented exception. (e) Gate failure at release — an SCA/dependency-review policy blocks a deploy on an abandoned package with no time-boxed remediation. Each is cheap to prevent with a template, a CI gate, and a named owner; each is expensive to discover in production.
Ramp-up. Learning cost is negligible — an engineer fluent in Laravel's ServiceProvider reads the whole surface in under an hour, and the successor's API is close enough that the mapping is learned on the first file. The genuine ramp-up cost is organizational: getting teams to adopt the chosen template, agree on the convention, and stop hand-rolling providers. Budget for a short internal write-up and one worked example package rather than for training. Total evaluation effort is an afternoon; switching cost per package is tens of lines; the correct posture is to decide quickly, document it, and move on.
One-line summary for the decision log: Useful prior art for Laravel package scaffolding; unmaintained since 2019 with zero dependents — harvest the pattern, own the code, and don't take the dependency.
How can I help you explore Laravel packages today?