Product Decisions This Supports
- Freezing the public error contract before v1. The single highest-leverage decision in an API product is what an error looks like. This package gives you a default envelope (status code, machine-readable code, human message, optional debug detail) so you can ratify a schema in a design review instead of discovering it accidentally across six controllers.
- Build vs. buy for cross-cutting plumbing. A consistent error layer is roughly 1–3 engineer-days to build well and forever to maintain (Laravel's exception handler changes across majors). Buying it converts a recurring maintenance tax into a pinned dependency.
- Defining an error taxonomy. The package's mapping layer forces the conversation product actually needs: which failures are 4xx-with-guidance vs. 5xx-with-apology, which get stable codes that mobile clients can branch on, and which messages are safe to show end users.
- Client-facing integration roadmap. Stable error codes are a prerequisite for mobile SDKs, partner integrations, and retry/backoff logic. This unblocks those roadmap items earlier than a hand-rolled solution would.
- Support-cost reduction. Predictable, actionable error payloads cut the "what does this 500 mean?" ticket class and shorten partner onboarding from days to hours. That is a measurable support-line item, not a nice-to-have.
- Security posture decision. Environment-aware verbosity (stack traces in local/staging, sanitized payloads in production) becomes a config default rather than a code-review hope — directly relevant to any SOC2/ISO questionnaire.
- Localization and multi-tenant messaging. If your roadmap includes i18n of user-visible errors, having one central place to swap message resolution is worth more than the package's other features combined.
- API versioning and deprecation. Centralized rendering makes it cheap to introduce a
v2 envelope behind a header or route prefix without touching domain code.
When to Consider This Package
Adopt when most of these are true:
- Laravel or Lumen is already the backend, and the API is JSON-first (or JSON-only) with no server-rendered error pages to preserve.
- You have — or are about to have — more than one client team consuming the API, so contract stability has real value.
- You have no existing error envelope, or you have three inconsistent ones and no appetite for a refactor sprint.
- Your team is small enough that "one fewer thing to own" outweighs "one more dependency."
- You can wrap it behind your own handler/adapter so the vendor type never leaks into domain code.
Look elsewhere when:
- The API is GraphQL, gRPC, or event-driven — the request/response exception model doesn't map.
- You need strict RFC 7807 Problem Details plus generated OpenAPI error schemas; confirm this package's envelope matches before committing, or expect to fork the renderer.
- An API gateway or service mesh already normalizes error responses upstream — you'd be solving the same problem twice.
- You require vendor support, an SLA, or a security review of every dependency. 39 stars and 0 dependents is a real signal: this is a single-maintainer package with a bus factor of one. That's acceptable for a thin, isolated adapter; it is not acceptable for something buried in your auth path.
- You need deep observability integration (structured logs, tracing spans, Sentry fingerprinting) as first-class features rather than something you bolt on.
- Your app is hybrid HTML + JSON; you'll fight the renderer on content negotiation.
How to Pitch It (Stakeholders)
To executives: This is a small MIT-licensed library that standardizes how our API reports errors. It replaces an estimated 1–3 days of custom plumbing plus ongoing maintenance, and it lets us lock a stable error contract before we onboard partners and ship mobile clients — which directly reduces integration support load and time-to-first-successful-call for anyone consuming our API. Risk is low and reversible: it sits behind a single exception handler, so removal is a one-file change if we ever want to walk away.
To engineering: One integration point (Handler/bootstrap config), config-driven exception-to-status mapping, environment-aware verbosity, and no framework forking. We'll wrap it in our own adapter so the dependency stays at the edge of the codebase, pin the version, and add contract tests around the response shape so an upstream release can't silently break clients. Before committing, we do a half-day spike to confirm it covers our validation, auth, 404/405, and rate-limit paths.
The honest caveat to state up front: low star count, single maintainer, zero dependents. That's fine for a thin, well-tested adapter. It's a reason to keep the blast radius small and to own the contract tests ourselves — the schema is ours, the package is just the renderer.