nunomaduro/laravel-code-analyse
Laravel Code Analyse brings static code analysis to your Laravel apps. Run the artisan command to scan your codebase against configurable checks, catch common issues early, and keep your code clean, consistent, and production-ready.
Architecture fit. Larastan is a development-time static analysis extension for PHPStan, not a runtime library. It contributes no routes, service providers, container bindings, migrations, or production code paths. Adoption is a require-dev line, a phpstan.neon (or the wrapper's defaults), and a CI invocation — removable by reverting a single lockfile entry. For a TPM this is the cheapest defensible risk class available: no deployment window, no data migration, no on-call rotation impact, and no customer-visible behaviour change.
The one architectural decision that must be made explicitly is which layer we standardize on, because the supplied metadata conflates two:
| Layer | Package | Role | Ownership model | Planning stance |
|---|---|---|---|---|
| Wrapper | nunomaduro/laravel-code-analyse |
Curated opinionated defaults, one-command run, baseline handling, Pint + Rector + GH Actions wiring | Third-party opinion; smaller surface; may lag the engine | Accelerator — adopt for time-to-first-signal, plan to outgrow |
| Engine | larastan/larastan |
Laravel-aware inference for Eloquent, facades, collections, container resolution | Upstream, high velocity (6.5k stars, regular releases, new contributors) | Durable dependency — plan around it, pin it, track it |
Standardizing on the wrapper and then discovering we need custom rules, targeted suppressions, stubs, or coexistence with other extensions pushes us into raw PHPStan configuration anyway. The wrapper's defaults must therefore be reconcilable with team policy, not a black box we inherit. Recommendation: adopt the engine as the durable dependency and treat the wrapper as an optional onboarding convenience for repos with no analysis today. Mixing the two across repositories creates two divergent profiles and two upgrade cadences — a governance problem disguised as a tooling choice.
Integration feasibility. High. v3.13.0 is purely additive and fix-oriented — no removals, no deprecations, no config-breaking changes, and unchanged PHP/Laravel requirements versus 3.12.x. The only hard constraint is inherited from the engine: PHPStan ≥ 2.3.0 within the 2.x line. That makes this a coupled dependency bump, not a routine patch, and it should be scheduled as such rather than opportunistically merged.
Relevant capability deltas to budget review time for:
relation-of<Model, Relationship> custom type plus inferred relationship types inside with(), withOnly(), load(), loadMissing() — improves safety of eager-loading-heavy code, which is exactly the layer unit tests under-cover.concat(), merge(), shift(), splice() — may surface previously silent key-collision and renumbering assumptions.with(['posts:id' => $callback])) — a genuine correctness and performance pitfall, and the strongest single argument for making analysis a blocking gate rather than advisory.toBase() forwarded builder call resolution — directly relevant to any repo with custom Eloquent query builders, which is common in mature Laravel codebases.Technical risk.
| Risk | Severity | Why it matters to a TPM | Mitigation |
|---|---|---|---|
| Metadata/provenance inconsistency | High — blocking for org-wide rollout | Package name, repo URL, star count, dependents (0), and a future-dated release (2026-10-23) do not line up. Approving a supply-chain dependency on ambiguous provenance is a governance failure, not a tooling preference | Confirm canonical package name, publisher identity, and that any wrapper's release cadence tracks the engine before going beyond a pilot |
| PHPStan lockstep coupling | Medium | A split upgrade leaves Larastan resolving against an unsupported PHPStan — a broken intermediate state worse than not upgrading | Ship PHPStan and Larastan in one PR; treat as a scheduled maintenance item |
| First-run error delta | Medium–High | New inference legitimately surfaces pre-existing issues. Unmanaged noise is the documented primary failure mode of static analysis adoption | Budget triage capacity before enabling the gate; baseline only after triage |
| Extension coexistence | Medium | Other PHPStan extensions (strict rules, deprecation rules, IDE helper, Doctrine, PhpStorm integrations) may not be co-compatible with 2.3 | Verify the full extension set against 2.3 in the pilot repo |
| Baseline rot | Medium | A baseline without a documented reason per entry is a green checkmark that verifies nothing — worst outcome, because it manufactures false confidence | Named owner, per-entry justification, quarterly review |
| Zero-config illusion | Low–Medium | Defaults are an adoption asset but a scaling liability; first custom rule or suppression puts you in PHPStan config | Decide up front that config is expected, and version it centrally |
Key questions (these should be answered before the rollout decision, not after):
composer package and who publishes it? Why is the dependents count zero and the release date in the future?Stack fit. Strong fit for any actively developed Laravel application or shared Laravel package where Eloquent relationships, query builders, facades, and collections carry business logic — that is, the layer that tests routinely under-cover and where refactoring is perceived as risky. Poor fit, and should be declined, for prototypes, throwaway spikes, short-lived internal tools (setup plus triage exceeds value returned) and any non-Laravel PHP project (generic PHPStan already covers it; the entire value is framework-aware inference).
Positioning constraints to communicate widely, because mis-selling this creates the wrong expectations: it is not a security scanner, a performance profiler, a replacement for tests, or a runtime safeguard. It changes nothing about production behaviour, so it is not an input to availability, scaling, or incident-response planning.
Migration path. A three-stage rollout, each with an explicit exit criterion:
phpstan.neon with the agreed level, ignore paths, and extension set. Publish a short adoption runbook covering install, upgrade, triage, and baseline rules. Validate the runbook by applying it to a second repo without the pilot owner driving.Compatibility. The compatibility surface is small but strictly coupled:
| Dimension | Requirement / constraint | Action |
|---|---|---|
| PHPStan | ^2.3 within the 2.x series |
Bundle with the Larastan bump; do not split |
| PHP / Laravel | Unchanged from 3.12.x | No platform work required |
| PHPStan extensions | Strict rules, deprecation rules, IDE helper, Doctrine, IDE/PhpStorm integrations must be 2.3-compatible | Audit co-installed extensions before the upgrade window opens |
| Laravel versions | Repos outside the Larastan 3.x support matrix | Stay on 2.x; record as divergence |
| Custom query builders | toBase() forwarded-call resolution changed |
Explicitly validate these classes in repos that override toBase() |
| Eager loading with callbacks | New invalid-column-selection diagnostic | Expect the largest single source of new failures here |
Sequencing. Frame this as a maintenance-window item, not a background chore:
^2.3 + Larastan ^3.13) in a single PR; inspect the lockfile diff for transitive changes.relation-of to the highest-value signatures — query-builder returns, repository methods, and model scopes.Rationale for the ordering: steps 1–3 are cheap and prevent an expensive rollback; step 4 is atomic by necessity; steps 5–6 are where the actual product value is either captured or lost; step 8 is where the investment compounds.
Maintenance. Recurring cost is modest but not zero: roughly one engineer-day per repository per upgrade cycle plus baseline review. Three recurring obligations must be resourced, not assumed:
phpstan.neon plus a documented policy for suppressions. Without this, per-repo configs drift into inconsistent gates within two quarters and the org-wide comparison loses meaning.Support. Support burden is deliberately low because there is no production surface. Model it as:
There is no on-call implication. Escalation is a delivery-velocity concern (a blocked PR), never an availability concern.
Scaling. Scaling is a function of repository count, not traffic, and it behaves well:
toBase() resolution once improves analysis accuracy for every consumer.Failure modes.
| Failure mode | Symptom | Detection | Response |
|---|---|---|---|
| Baseline rot | Gate passes; production defects the tool should have caught still ship | Baseline grows while error count stays flat; entries lack reasons | Freeze regeneration; audit entries quarterly; require reasons in review |
| Noise-driven abandonment | Teams add blanket ignores or stop reading output | Rising ignoreErrors counts; declining time-to-green |
Cap suppressions per repo; route false positives to the analysis owner |
| Split-version breakage | Larastan resolving against unsupported PHPStan | Composer resolution errors, inconsistent results across repos | Enforce lockstep bump via shared config/CI; never merge one without the other |
| Wrapper lag | Wrapper pins an old engine; new diagnostics unavailable | Wrapper version vs engine version drift | Standardize on engine + internal defaults; drop wrapper if it lags |
| Advisory-gate complacency | Analysis has run for months, nothing changed | Zero failures reported since adoption | Time-box advisory mode; enforce a promotion date per repo |
| Provenance incident | Unexpected package content or an unmaintained dependency in the dev chain | Dependency review / SBOM tooling | Resolve identity before rollout; pin and verify on every bump |
| Bleeding-edge churn | Non-deterministic new failures from opt-in inference | Failure count fluctuates without code changes | Keep bleeding-edge flags out of the blocking gate |
Ramp-up. Realistic enablement estimates:
Success measures to commit to now, so the investment is falsifiable in two quarters: (a) number of genuine defects surfaced during the pilot and first three rollouts; (b) share of PRs failing analysis before merge, tracked as an early-warning lead indicator; (c) baseline entry count trending flat or down per repo; (d) refactor throughput on Eloquent-heavy modules — the number of modernization PRs greenlit that would previously have been judged too risky. If (a) is zero and (d) does not move, the tool is a green checkmark and should be re-scoped or dropped rather than maintained out of inertia.
How can I help you explore Laravel packages today?