Best practices
Best practices
A checklist distilled from the package’s invariants. Each item links to the page that explains why.
Correctness under load
- Never replace the atomic claim with a read‑then‑write. The conditional
UPDATEin
RedemptionService::claimSeat()is the only sanctioned path that bumpscurrent_uses. Ship a
two‑process concurrency test for any change to it.
→ Atomic idempotent redemption - Treat idempotency as a database invariant, not code discipline — rely on
UNIQUE(code_id, redeemer_id)andUNIQUE(idempotency_key), and handle the violation (catch →
release → idempotent success) rather than trying to avoid the race.
Tenancy
- Scope every query through
forTenant()/ theBelongsToTenanttrait. An unscoped
where('code', …)mixes tenants and is a bug. - Bind a real
TenantResolverin multi‑tenant hosts; single‑tenant apps need none.
→ Multi‑tenancy & host seams
Codes
- Increase the length, don’t loop forever when you see
collision_exhausted— the keyspace is too
small for your issue volume. - Don’t add
I L O Uor duplicates to the alphabet — the generator refuses them because
normalization would shrink the keyspace.
→ Invite codes
Secrets
- Set
INVITE_SIGNING_KEYandINVITE_PII_SALTto dedicated, rotatable secrets in production.
TheAPP_KEYfallback is a dev convenience; rotatingAPP_KEYwould otherwise orphan signed codes
and PII hashes.
→ Configuration reference
Privacy
- Leave
store_network_fieldsoff unless abuse review genuinely needs ip / fingerprint. - Schedule
invite:prune-piiso PII is anonymized past the retention window — in place, so
aggregates survive.
→ GDPR & data privacy
Authorization
- Append your RBAC gate to
routes.admin_middlewarebefore exposing the admin API — the default is
authenticate‑only, not authorize.
→ The HTTP API
Anti‑abuse
- Tune thresholds and velocity rules; don’t disable the gate to “fix” false positives — use the
allowlistinstead. - Never surface the tripped
signal_typeto callers, and never make the gate throw on the
redemption path (it must fail‑open).
→ Anti‑abuse scoring
Provisioning
- Keep provisioners additive and idempotent (
firstOrCreate, additive roles). The contract is
GRANT‑never‑REVOKE, best‑effort.
→ Per‑invite entitlement grants
Observability
- Listen to the domain events (
CodeRedeemed,InvitationSent,InvitationAccepted) for perks,
mail, and projections —CodeRedeemedfires once per genuine redemption.
→ Domain events - Read metrics from the API / MCP /
MetricsService— they reconcile against the canonical rows.
→ Virality analytics
All of these invariants are encoded in the package’s CLAUDE.md, so an AI pair‑programmer inherits
them automatically. The package’s quality gate is composer check (Pint + PHPStan level max +
PHPUnit, on a PHP 8.3/8.4/8.5 × Laravel 12/13 matrix).