spatie/ray
spatie/ray sends debug output from any PHP app to Ray, Spatie's desktop debugging app. Dump arrays, HTML, queries, and Markdown, measure performance, pause execution, and inspect output with the same syntax you use across Laravel, JavaScript, and more.
Architecture fit. spatie/ray is a developer-facing diagnostics client, not a runtime application dependency. It exposes a global ray() helper (and a Ray class) that serializes payloads and posts them over local HTTP to the Ray desktop app (default 127.0.0.1:23517). For our stack the correct artifact is the Laravel wrapper, spatie/laravel-ray, which pulls this core package, auto-registers a service provider, publishes config/ray.php, and adds framework-aware helpers (queries, models, jobs, views, mail, cache, events, Pest/PHPUnit screens). Architecturally it belongs in require-dev only and should never be installed in production images. Fit for local development and test triage is high; fit for production observability is intentionally zero — this is not a logging or APM substitute.
Integration feasibility. Low effort. composer require --dev spatie/laravel-ray, publish config, install the desktop app per developer. No server-side infrastructure, no database, no queue, no network egress by default. The main non-engineering gate is licensing: the Composer package is MIT, but the Ray application is a proprietary, per-seat commercial product with a free trial capped at 20 messages per session — effectively a purchase decision for team-wide adoption. Budget approval, not code, is the critical path.
Technical risk.
ray() calls committed and deployed cause overhead and potential data exposure (query results, model attributes, env values). Upstream ships a PHPStan rule to detect ray() calls and a Rector rule (RemoveRayCallRector) to strip them — these are the primary mitigations and should be treated as mandatory, not optional.ray()->pause() deliberately halts execution; left in code it can hang a process or CI job.dependents: 0 figure is almost certainly a metadata artifact (laravel-ray depends on this package) and should be verified before weighting.Key questions.
RAY_ENABLED=false default in .env.example, --no-dev in production builds?spatie/laravel-ray only, and treat spatie/ray as a transitive dependency we never require directly?Stack fit. Strong. Works on vanilla PHP and Laravel; supports Laravel through 13, Symfony 6/7, PHPStan 1.x and 2.x, and renders Pest/PHPUnit test output. Capabilities that matter to us: dumping arbitrary structures (HTML, arrays, Markdown, queries), named screens, performance measurement, execution pausing, clipboard/invade, log context, and afterSendCallbacks / pre-send closures for hooking into our own diagnostics.
Migration path. Purely additive — there is nothing to migrate. It displaces ad hoc dd(), dump(), var_dump(), and error_log() habits. Teams on Xdebug can run both concurrently with no conflict. The exit path is clean and already tooled: Rector removal rule plus a PHPStan detection rule, so adoption is reversible.
Compatibility.
require-dev only; enforce composer install --no-dev in prod images and verify the lockfile does not leak it into runtime containers.ray.php and set enable => env('RAY_ENABLED', false) so any non-local environment is inert by default.Sequencing.
composer require --dev spatie/laravel-ray, publish config, add RAY_ENABLED=false to .env.example, verify the app renders.ray() vs. the logger, no ray() in committed code, screens naming, no pause() outside local.Maintenance. Low engineering maintenance: dev-only dependency, MIT client, active upstream with disciplined changelogs. The real recurring cost is version tracking — expect minor bumps synchronized to PHP minor releases and Laravel majors. Batch dependabot PRs for this package rather than merging individually. Note that the desktop app updates independently of Composer, so client/app drift is a separate upgrade concern.
Support. Single-vendor commercial product. GitHub issues cover the OSS client but not the closed-source app; app-level support depends on the license tier purchased. Documentation lives off-repo at myray.app/docs, a mild risk for air-gapped or offline onboarding. There is no internal expertise on day one — designate one champion per team and centralize the setup guide.
Scaling. Cost and value scale with headcount, not traffic. No production scaling exposure provided the dependency is excluded from production builds. Friction appears at the infrastructure edge: many containers and services each needing gateway configuration, and per-seat licensing that must expand with every new hire — a recurring budget line rather than a one-time tooling cost.
Failure modes.
pause() left in code: hangs a process; can stall CI if Ray is enabled there.ray() shipped to production: overhead plus possible PII exposure in payloads; mitigated only by CI rules and env gating.Ramp-up. Near-zero for the API — anyone who has used dump() is productive in an hour. Budget roughly one engineer-week for a mid-size team to cover pilot, the guardrail policy, CI rule wiring, and container/remote setup documentation. The dominant ramp-up cost is organizational (procurement, policy, and a static-analysis gate), not technical.
Recommendation for decision-makers. Approve a time-boxed pilot with an explicit procurement checkpoint; make the CI guardrail (PHPStan detection + Rector removal + RAY_ENABLED=false default) a precondition of rollout; treat the per-seat license as a recurring line item and validate it against the alternative tooling we already pay for.
How can I help you explore Laravel packages today?