ariby/ulid
Ariby ULID adds lightweight ULID support to Laravel. Generate compact, timestamp-sortable, collision-resistant identifiers and use them in your Eloquent models. Ideal for primary keys and public IDs when you need unique, URL-friendly values with simple helpers and minimal setup.
Architecture fit. The package addresses a real design need — replacing auto-incrementing integer primary keys with ULIDs (26-char, lexicographically sortable, time-prefixed identifiers) to avoid ID enumeration, support offline/client-side generation, and improve index locality versus UUIDv4. However, the architecture fit is poor for any Laravel 9+ application: the framework ships Illuminate\Database\Eloquent\Concerns\HasUlids, Str::ulid(), and $keyType = 'string' / $incrementing = false conventions out of the box. This package is therefore a thin trait wrapper around functionality the framework already owns, which is the weakest possible position for a third-party dependency.
Integration feasibility. Mechanically trivial: it is almost certainly a trait applied to Eloquent models plus a migration column-type suggestion (char(26) / ulid / string). There is no service provider surface area worth speaking of, no infrastructure, no external service. Adoption cost is low — which is precisely why the lock-in risk is asymmetric: you gain little, and you take on a dependency you may have to remove later.
Technical risk. High, driven by provenance rather than code complexity:
| Risk | Assessment |
|---|---|
| Adoption signal | 0 stars, 0 dependents, single-author repo — no community vetting, no production evidence |
| Maintenance continuity | No evidence of a release cadence, CI, or version-constraint policy; abandonment is the base case |
| Supply chain | Unvetted Composer package executing in the model layer of every write path — highest blast radius in the app |
| Duplication | Overlaps framework-native HasUlids; creates two competing ID strategies if partially adopted |
| Version drift | Unknown constraints against Laravel 10/11/12 and PHP 8.2+; may block framework upgrades |
| Behavioral gaps | Unclear handling of same-millisecond monotonicity, key-type coercion, route-model binding, JSON serialization, and factory support |
Technical risk, mitigated view. The code itself is likely small enough to audit in an hour and vendor into the repo if necessary. The real risk is organizational: a TPM who approves this creates a dependency that no one owns and that will surface in a future upgrade or security review.
Key questions.
HasUlids does not, on our target Laravel version? If the answer is "nothing," the decision is made.Stack fit. Fits cleanly into a conventional Laravel monolith or API service using Eloquent. It does not fit well where IDs cross service boundaries with strict contracts, where IDs are exposed in URLs consumed by third parties, or where existing integer-keyed tables must remain. PHP/Laravel is the only supported stack; nothing here transfers to a non-Laravel service.
Migration path. Two paths, and the recommendation is the second.
$keyType = 'string' and $incrementing = false, define char(26) columns, backfill or dual-write for existing tables. Fast to start, accumulates debt.HasUlids — same code shape, zero third-party surface. For Laravel < 9, depend directly on symfony/uid (which Laravel itself wraps) rather than on this package.For existing integer-keyed tables, the migration is not a trait swap — it is a schema change requiring a new column, backfill, foreign-key rewiring, and a cutover. Scope that separately; it will dominate the timeline regardless of which library generates the ID.
Compatibility. Verify against the current framework version before anything else. The likely failure mode is a stale composer.json constraint that blocks a Laravel major upgrade. Also confirm database support: native ulid column types exist in some engines, but char(26) with utf8mb4_bin (or ascii_bin) collation is the portable choice and matters for index behavior and case sensitivity.
Sequencing. (1) Confirm framework version and whether HasUlids suffices — expect it does. (2) If adopting anyway, audit the source and pin an exact version with a committed lockfile. (3) Introduce on new models only, behind a short-lived internal trait of our own so the underlying implementation is swappable. (4) Validate route-model binding, factories, seeders, API resources, and any queue payloads that serialize models. (5) Defer retrofitting existing tables until the new-model path has run in production for a full release cycle.
Maintenance. The package is a maintenance liability with no upside: we would own the upgrade-tracking burden for code we could replace with a one-line trait change. If adopted, budget for periodic audits and an exit plan. Realistically, plan to fork or vendor it, which converts a dependency into owned code — at which point the framework-native trait is strictly better.
Support. None. No vendor, no SLA, no security disclosure process, no issue-triage history to infer responsiveness from. Any defect becomes an internal debugging exercise against someone else's unmarketed code. This is acceptable for a dev-only utility; it is not acceptable for the primary key generation path of a production system.
Scaling. ULIDs themselves scale well: 26 bytes versus 16 for binary UUIDs, sortable, decent B-tree locality because of the time prefix. Two caveats apply at scale. First, index size and collation: use a binary/ascii collation or you pay for utf8mb4 on every index. Second, clock behavior across multiple app servers — if the package's monotonicity guarantees are weaker than expected, near-simultaneous writes from different hosts can produce out-of-order IDs, which surfaces as subtle pagination and cursor bugs rather than clean failures. Validate this explicitly under concurrent load.
Failure modes.
varchar(255) or wrong collation) causing index bloat or case-insensitive lookups that collide.$incrementing / $keyType misconfiguration producing 0 IDs or failed inserts on models that inherit from a base class.Ramp-up. Effectively zero for a Laravel developer — it is one trait and two model properties. The ramp-up cost sits in the migration work for existing tables and in the review effort to establish that this dependency is safe, both of which are proportional to the number of models touched and are identical whether we use this package or HasUlids.
Recommendation. Do not adopt. Use HasUlids on Laravel 9+, or symfony/uid directly on older versions. If a team insists on this package for a specific reason, require a source audit, an exact version pin, an internal wrapper trait, and a documented exit plan before the first production write path depends on it. This is a low-cost decision to reverse now and an expensive one to reverse after the schema is populated.
How can I help you explore Laravel packages today?