Product Decisions This Supports
- Buy vs. build for lifecycle logic. Every product with a status field eventually grows bespoke
if ($status === ...) guards scattered across controllers, jobs, and admin panels. This package makes the transition graph a first-class, reviewable artifact on the enum, so the PM can point at one file and say "these are the states and the only legal moves." That converts a recurring refactor cost into a one-time modeling decision.
- Roadmap sequencing of workflow features. The enum graph is a cheap way to prototype a lifecycle before committing to UI and API surface. Whether an order can go
pending → cancelled after capture, or a subscription can go past_due → active without a payment retry, is a product rule — writing it as transitions on day one forces the PM and design to answer edge cases that otherwise surface as production bugs.
- Scoping audit and history as an explicit product requirement. The headline trade-off — no extra tables — means no transition log out of the box. The PM must decide whether "who changed this and when" is a support nicety, a compliance obligation (payments, KYC, healthcare, insurance), or a customer-facing timeline feature. That answer determines whether this package is the whole solution or only the enforcement layer with history bolted on via model events.
- Support and operations tooling. A finite, enumerable state list is what makes admin tooling, filters, saved views, and CS dashboards tractable. Because states stay native enum cases, they serialize cleanly into API responses, CSV exports, and analytics event schemas without translation layers — useful for deciding whether to expose status to customers or keep it internal.
- Build vs. buy on a spectrum, not a binary. This is the "small dependency" end of buy: MIT, no schema changes, no service, no vendor. It is reversible in an afternoon. That changes the calculus — the PM can approve it without an architecture review board, whereas adopting a workflow SaaS or a heavier state-machine library is a multi-quarter commitment.
- De-risking a low-signal dependency. Five stars, zero dependents, a single maintainer, and a future-dated release (2026-05-27) are all procurement facts, not engineering trivia. A PM should treat this as an internal convenience dependency rather than a strategic platform bet, and budget for a vendored fork if it stalls.
When to Consider This Package
Adopt when:
- The product has one or a few bounded lifecycles — order status, ticket status, onboarding/KYC stage, subscription state — with a graph that fits on a whiteboard and changes quarterly, not weekly.
- You are on Laravel with PHP 8.1+ enums already cast on the model; the marginal cost is one attribute and transition methods on the enum.
- No compliance-mandated transition audit exists, or you are content to capture history through Eloquent observers / event listeners into your own table. Confirm this explicitly before adopting — retrofitting auditability is easy, retrofitting backfilled history is impossible.
- Transitions are synchronous and request-scoped. Guards and side effects run in-process; there is no lock, queue, or saga coordination. Concurrent writers can race on the same row.
- Team size and turnover are small, so the implicit convention "the graph lives on the enum" is discoverable without dedicated documentation.
- You want a thin, removable layer rather than a framework commitment — a strong fit for a team that expects to outgrow it.
Look elsewhere when:
- Audit trails are a hard requirement and you want them maintained by the library rather than by you — mature alternatives ship transition history, transition classes, and per-transition authorization as first-class concepts.
- The workflow needs guards that hit the database or external services (credit checks, inventory reservation), human approval steps, time-based escalation, or parallel branches. That is workflow-engine territory, not enum territory.
- You need cross-model orchestration or long-running distributed transitions.
- Supply-chain policy bars single-maintainer, low-adoption packages for anything on a revenue-critical path. Zero dependents means zero field testing by others.
- You are not on Laravel, or you need the same state model enforced outside PHP (mobile clients, event consumers, DB constraints). Enum graphs in application code give you no protection against a bad migration or a direct SQL update.
- The status field is genuinely free-form or rapidly mutating — forcing a premature graph will generate churn and false confidence.
How to Pitch It (Stakeholders)
For executives (30 seconds). Order, ticket, and subscription lifecycles are where our worst bugs and most of our support tickets come from — illegal state jumps, silent double-charges, statuses nobody can explain. This package lets us write the allowed moves down once, in code, and have the application refuse everything else. It is MIT-licensed, adds no database tables, no new service, and no vendor. Cost to adopt is roughly a day; cost to remove is roughly a day. The one thing it does not do is keep a history of who changed what — if we need that for compliance or for customer-facing timelines, we build it ourselves on top, which is the same work we would do anyway.
For engineering. Native PHP enums with the transition graph declared on the enum; existing model casts are untouched; one attribute registers the field. No base state classes, no trait, no migrations. Enforcement happens before persistence, so it is testable in isolation and the graph is reviewable in a diff. Caveats to accept knowingly: no transition log, no row-level locking (guard against concurrent writers at the job/controller layer), and the dependency is small and single-maintainer — pin the version, keep the enum definitions self-contained so a fork is a copy-paste, and add a contract test that asserts each model's field is registered. Recommendation: adopt for internal, reversible lifecycles; write a one-page ADR recording that transition history is deliberately out of scope, and revisit if a compliance requirement lands.