pxlrbt/faker-better-unique
A better unique() modifier for FakerPHP. Keeps generated values unique across formatters and resets, so tests and seeders avoid duplicate collisions without extra bookkeeping. A drop-in replacement for Faker's built-in unique() with more predictable behavior.
Framing correction. The generic Laravel assessment supplied with this package assumes a service provider, publishable config, migrations, and runtime request-path cost. None of that applies. pxlrbt/faker-better-unique is a Composer utility that modifies how FakerPHP's unique() modifier tracks and retries used values. It belongs in require-dev only and should never ship in a production image. The correct decision frame is closer to "adopt a PHPUnit helper trait" than "add a runtime dependency" — which shrinks adoption cost, blast radius, and review effort by an order of magnitude relative to the generic checklist.
Architecture fit. The package does not sit in the application architecture at all; it sits in the test and seed data architecture. That is its principal virtue from a TPM standpoint: it cannot affect latency, uptime, data integrity, or tenant isolation in production because it is structurally absent from production. The architectural questions it raises are about test design, not application design:
| Concern | What "good" looks like | Why a TPM cares |
|---|---|---|
| State model | Uniqueness registry is keyed/namespaced per formatter, with an explicit reset entry point | Unscoped global state makes test order matter and turns flakiness into a whack-a-mole exercise |
| Failure semantics | Exhaustion fails loudly (exception), never silently returns a duplicate | Silent duplicates in a seeder corrupt demo/staging data — far costlier than a test rerun |
| Call-site neutrality | Existing $faker->unique()->safeEmail() calls keep working unchanged |
Avoids a sweep-and-rewrite of factories and seeders, which is where the real cost lives |
| Boundary | No production class loading, no config surface, no container rebinding required | Keeps supply-chain and ops review in minutes, not sprints |
Integration feasibility. Low effort, but only if the package's differentiator — the "keys for unique faker values" in the description — is real and exposes a usable public signature. The tagline implies per-key scoping so two unrelated fields or seeders don't exhaust a shared pool. Verify that first: if the package is merely a thinner wrapper with no keying and no reset, the build-vs-buy math collapses toward the 30–80 line in-house helper (a static registry keyed by formatter plus a reset hook in the base TestCase), which the team would own, document, and debug faster.
Technical risk. Small and bounded, but not zero:
"1.0.0", not ^1.0) and commit the lockfile.Generator::unique($reset = false, $maxRetries = 10000) already has shape and meaning. Any divergence in argument order breaks existing $faker->unique(true) reset calls silently. Grep all unique( call sites before installing.numberBetween(1, 10) called eleven times) still must fail. If exhaustion is caused by a genuinely small domain, the fix is a wider pool or deterministic sequencing, not this package.Key questions to answer in a time-boxed spike (one day, maximum):
Faker\Generator, decorates UniqueGenerator, or ships a provider? Does fake() resolve to it, or must it be constructed directly?addProvider()?Stack fit. Laravel/Livewire-style stack with FakerPHP already in require-dev, factories and seeders as the primary data-generation surface. The package replaces behaviour behind existing call sites rather than introducing a new API surface, so fit is high provided the spike confirms the unique() signature is preserved.
Migration path. Call-site-neutral by design. Sequence:
OverflowException: Unable to generate a unique ... after 10000 attempts, or a suite that has accumulated defensive $faker->unique(true) resets whose placement is now unclear. If neither exists, stop — do not adopt a dependency to solve a problem the team does not have.require-dev only.tests/Support/UniqueValues.php (or database/seeders/Support/) so the package API is referenced in exactly one file. Cost: an hour. Benefit: a fork-or-remove decision becomes a one-file edit and the exit cost is provably under an hour.TestCase::setUp() to eliminate cross-test state leakage. If no documented reset exists, that is an upstream red flag and a reason to confine usage to seeders only.Compatibility. Pin across the whole team and all CI runners; a divergence in uniqueness semantics between environments is a worse failure than the one being fixed. Verify against the team's Laravel and PHP majors explicitly — assume nothing given zero dependents. Confirm existing seed() determinism is unaffected; uniqueness tracking and reproducibility are separate concerns and this package only addresses the first.
Sequencing. Reproduce → quantify (incident count, average rerun cost, CI minutes lost) → one-day spike against the in-house-helper baseline → adopt or build → record decision with a review date. Do not adopt in the same sprint as a CI overhaul; keep the attribution clean.
Maintenance. Near-zero. No migrations, no config, no runtime surface, no deployment change. The maintenance obligation is: exact version pin, lockfile committed, no auto-merge for dependency bots on this package, and one review date (~90 days) to check for a 1.0.x patch. Absent a patch release, keep it out of any load-bearing seeder path used for sales demos or partner sandboxes.
Support. No support contract, no disclosure policy, no named maintainer beyond one individual. For organisations that gate on those criteria, this package will not clear the gate — and given the low cost of the first-party alternative, it is not worth an exception process. For everyone else, the wrapper seam is the support plan: if the maintainer disappears, vendoring or replacing is an hour of work against one file.
Scaling. Two dimensions:
Failure modes and detection.
| Failure mode | Symptom | Detection | Mitigation |
|---|---|---|---|
| Silent duplicate on exhaustion | Duplicate rows in seeded demo/staging data | Assert uniqueness in seeder smoke test | Verify it throws during spike; reject if it swallows |
| State leak between tests | Order-dependent, non-reproducible flakiness | Single-test runs vs full-suite runs | Explicit reset in base TestCase |
| Signature divergence from stock Faker | Existing unique(true) resets stop working, quietly |
Grep call sites; diff behaviour in spike | Do not install if semantics differ |
| Unbounded pool growth | Memory pressure in long seeders | Log tracker size during trial | Scope by key; cap batch sizes |
| Upstream abandonment | No patches, security advisories unanswered | Review date on the decision record | Wrapper seam → one-file swap or vendor copy |
Ramp-up. Effectively zero for engineers who already write factories and seeders — the API is Faker's own. The only onboarding artefact needed is a short note in the testing docs: "unique value generation routes through tests/Support/UniqueValues; call reset in setUp(); if you hit exhaustion, widen the domain, don't raise retries." Budget half a day of engineer time inclusive of the spike and the wrapper.
Bottom line for the decision record. This is a tactical, dev-only fix with a documented exit, not a platform bet or vendor relationship. Adopt only if the spike shows a measurable reduction in flakiness or seeder failure cost exceeding one day of engineering time; otherwise build the ~50-line helper, which will be better documented and better owned. Either way, the cost of being wrong is a single Composer revert — which is exactly the size of decision this deserves.
How can I help you explore Laravel packages today?