robertboes/laravel-interlock
Laravel Interlock is a lightweight package for preventing concurrent execution of critical code in Laravel apps. It helps avoid race conditions, duplicate jobs, and overlapping tasks with simple, configurable locks and a clean framework-friendly API.
Assessment basis: this evaluation is derived solely from the v0.1.0 release notes; no source review, version constraints, or benchmarks were available. Confidence is therefore moderate on capability, low on compatibility and performance. This is the initial public release, so there is no prior assessment to diff against.
Architecture fit. Laravel Interlock is an opinionated state-machine layer that sits directly on top of Eloquent models rather than beside them. That is the key architectural decision: it enforces state integrity inside the ORM path (attribute guards, builder-level mass-write rejection, initial-state enforcement, compare-and-swap transitions, system-only scopes) instead of asking teams to route writes through a separate service. For Laravel applications whose domain already lives in Eloquent — order lifecycles, subscription billing states, approval workflows, onboarding, fulfillment — the fit is strong because it closes the classic failure mode of state-machine libraries: the state column remains writable by any controller, seeder, or tinker session. Capabilities that matter for real domain modeling are present from day one: multiple machines per model, nested states, final-state declaration with a soundness check, guards, authorization-before-apply, named transition classes with apply and after-commit steps, and query scopes including "time in state."
Integration feasibility. The package follows conventional Laravel distribution mechanics — publishable config, migrations, discovery of interlocked models with a cached registry, and artisan commands (interlock:signalbox for diagram export, plus a trigger command that apps can disable or reschedule). That is a low-ceremony install. The non-trivial prerequisites are operational rather than code-level: the model registry is cached, so deploy pipelines must clear or warm it; the trigger system requires queue workers and a scheduler entry; and the history feature requires a migration and a shared polymorphic-style table that supports integer, UUID, and ULID keys. Export to Mermaid, XState (JSON/TypeScript), and Stately Sketch is a genuinely useful adoption lever because it lets a TPM produce reviewable artifacts from the actual code rather than from a stale wiki diagram.
Technical risk. The dominant risk is maturity, not design. At 0.1.0 there is no semver stability guarantee; every minor release can break the API, and the notes show a wide surface area (enforcement, transitions, triggers, history, export, testing) landing at once. Second risk is fail-closed enforcement. Rejecting direct writes to state attributes and denying transitions that declare no authorization is exactly right for integrity, but it will break existing seeders, imports, admin tooling, and data-fix scripts on contact. The bypass mechanism (reasoned bypass, factories, opted-in tests) mitigates this, but only if teams adopt a written bypass policy; otherwise bypass becomes the new direct write. Third is concurrency semantics: compare-and-swap with a "stale state" exception is correct behavior, but it pushes retry/conflict handling into every caller and needs an explicit UX contract. Fourth is the trigger engine — self-applying transitions, optional authorization bypass for triggered transitions, run caps, and chunk fan-out — which is powerful but introduces background-write and potential-loop risk into what is nominally a data-integrity library. That "triggered transitions skip authorization" behavior deserves a documented system-actor model before production use.
Key questions for the maintainer / spike.
DB::table() updates, bulk update(), raw SQL, model events — or only Eloquent model/builder paths?Stack fit. Standard Laravel package shape: service provider, publishable config, migrations, artisan commands, queue and scheduler hooks. It assumes a queue-backed application and a shared cache store; apps without either can still use the core state machine but should defer triggers. Export commands are useful immediately and carry no runtime risk.
Migration path. Treat this as a staged adoption, not a drop-in.
UPDATE.Compatibility. The shared history table supporting integer, UUID, and ULID keys is important for mixed-key estates, but it means migration ordering and index review must be done deliberately. If existing models already use enum casts or status columns written by third-party packages, expect conflicts at the boundary and plan for bypass adapters rather than parallel writers.
Sequencing and dependencies. The critical path is: version pin → config/migrations → discovery-cache handling in the deploy pipeline → bypass policy agreed and documented → enforcement rollout model by model → transitions → history → triggers → diagram export wired into docs/PR checks. Do not enable triggers before the write paths feeding those same attributes are consolidated; overlapping writers plus autonomous triggers is the highest-risk combination in this release.
Maintenance. Pre-1.0 means maintenance cost is real: pin exact versions, read every changelog, and budget upgrade work per minor release. Domain models will carry package traits and transition classes, creating coupling in the most-edited files of the codebase — an argument for adopting on a bounded set of aggregates first, with tests written against the package's own fakes and assertion helpers to keep refactors cheap.
Support. Single-maintainer open-source package with no stated SLA. Before depending on it for revenue-critical flows, check issue responsiveness, release cadence, license terms, and whether the maintainer has published a compatibility policy. Given the breadth of v0.1.0, expect rapid follow-on releases.
Scaling. The cached registry keeps discovery cost off the hot path. Compare-and-swap adds a write per transition, which is acceptable and arguably necessary. The two scaling hotspots are history-table growth (unbounded without a retention policy — plan pruning from day one) and triggers, which fan out into chunk jobs: per-run work is capped, but total work scales with the number of rows whose trigger condition holds, so a large backfill can produce sustained queue pressure. Route triggers to a dedicated connection and monitor queue depth and trigger lag explicitly.
Failure modes to design for.
Ramp-up. Expect meaningful onboarding: the team needs state-machine fluency (guards, final states, nested states, system actors) plus the package's conventions. Mitigations are strong — Mermaid/XState/Stately exports make designs reviewable by non-engineers, Laravel Boost guidelines help AI-assisted development, and built-in fakes and transition assertions lower test friction. Realistic estimate: one engineer for roughly a week to reach a credible pilot on a single aggregate, plus review time from whoever owns the affected domain writes, plus a short written bypass and conflict-handling policy that both engineering and audit can point to.
How can I help you explore Laravel packages today?