sandermuller/richter
Richter is a lightweight Laravel package for measuring the magnitude of your code: benchmark execution time, memory usage, and queries with a clean API. Compare implementations, catch performance regressions, and keep your app fast. Easy to install and fits naturally into your test suite.
Richter is a static risk/reachability analyser for Laravel applications. It parses your app into a graph (entry points → controllers/jobs/middleware/models), grades the reachable surface, and emits text, markdown, and HTML reports with per-entry-point risk levels and the reasons behind them.
entryPoints first. This is the single most important config decision — everything else is measured relative to these roots. Typical roots: HTTP routes you expose, console commands, queue consumers, scheduled tasks. Order them deliberately: as of v0.70.0, entryPointKeepSet.kept and the task-slice kept list follow entryPoints order, so the order you write becomes the order in your reports and diffs.Baseline-then-delta in CI. Generate the markdown report as a build artifact and diff it against the previous run. Markdown is the right format for this: it is stable, greppable, and already carries the same risk-cause text the HTML tiles now show. Keep the HTML for human review links.
Entry-point-scoped risk budgets. Grade per entry point rather than per app. With kept lists ordered by entryPoints, you can put your highest-risk surfaces (public HTTP, payment webhooks, anything queue-consumer driven) first and have the report read top-down in priority order. This also makes task-slices — the PR-scoped subset of kept surfaces — line up with your review order.
Model middleware from bootstrap/app.php, not the Kernel. In Laravel 11+ style apps, withMiddleware now correctly hides the app's Kernel groups during analysis. Keep middleware registration in one place; if you register groups in both bootstrap/app.php and a Kernel, the analysis will model the Laravel 11+ stack (bootstrap wins), which may not match what you assume is running.
Make dispatches resolvable. Queued classes that live outside \Jobs\ now read unresolved when the graph contains unresolved dispatches. If you want clean grades, keep ShouldQueue/Dispatchable classes in the conventional namespace and avoid dispatching from shapes the analyser cannot follow (see the spread note below). unresolved is a valid signal, but it is contagious — one unresolvable dispatch site can flip unrelated classes.
Write mass-assignment declarations separately. Combined declarations like $fillable = [...], $guarded = [...] were previously read incorrectly. They are fixed now, but separate $fillable and $guarded statements remain the least ambiguous form for both humans and the analyser.
Cache the graph across CI runs, keyed on package version. Tie your CI cache key to the Richter version plus composer.lock. A cache-format bump is a rebuild, not a failure, and refused cache entries (below) degrade gracefully.
entryPointKeepSet.kept and task-slice kept lists now follow entryPoints order instead of whatever order they previously had. If you snapshot JSON or markdown, re-record the snapshot as part of the upgrade; the semantic content is unchanged.new of a non-dispatchable class inside an accumulated job array is no longer one. If you have ...$jobs-style dispatch helpers, expect new unresolved entries and either refactor the dispatch or accept the noise.appendToGroup, prepend, a second call on the same group, and group(). On middleware-heavy apps this adds report lines that are informational, not hazards — filter on hazard/tier rather than scanning the raw list.group('name', [...]) is now read. If you previously worked around the analyser by inlining group definitions, you can move back to named groups.entryPoints configuration and report consumption rather than plugin hooks. Reach for config, not code, before considering a fork.How can I help you explore Laravel packages today?