There are two ways to learn how a program behaves: read it, or watch it run. Static analysis reads source code without executing it. Runtime (dynamic) analysis observes the program while it handles real input. Teams often treat them as rivals; they answer different questions, and knowing which question you have saves a lot of time.
What static analysis is good at
Static analysers — type checkers such as PHPStan or Psalm, linters, security scanners — inspect every code path, including ones that rarely run. That makes them strong at:
- Whole-codebase guarantees. "No method is called with the wrong type" is only checkable by looking at every call site.
- Catching bugs before they ship. They run in CI on every change, without a database, test data or traffic.
- Rare paths. An error handler that runs once a month is analysed just as thoroughly as the main path.
Where static analysis is blind
Reading code cannot tell you what the code does with real data. In a framework like Laravel a lot of behaviour is decided at runtime:
- Dynamic dispatch. Which class handles an interface depends on container bindings, configuration and environment.
- Data-dependent cost. A loop is cheap with 10 rows and slow with 10,000. A relation loaded lazily inside that loop becomes an N+1 query pattern only when there is data.
- Timing. No amount of reading tells you that an external API answers in 400 ms from your production region.
- Dead or unused code. Static tools can show code that is unreachable in principle; they cannot show code that is reachable but never actually used.
What runtime analysis adds
Runtime analysis measures what happened: which code ran, in what order, for how long, and what it called. Request tracing — recording one request as a tree of timed steps — is the most practical form for web applications. It answers questions such as:
- Which step made this request slow, and by how much?
- How many queries does this page really run?
- Which external services does this endpoint depend on in practice?
Zerethon's Flow is built on this approach: its Laravel adapter captures each request's controller, database queries and wrapped service or external calls as a trace, and ranks the steps by their share of the total time. The article Finding the real bottleneck in a Laravel request walks through reading one.
Where runtime analysis is blind
- Coverage depends on traffic. A path that did not run during observation is invisible. Rare error paths may never show up.
- Observation has a cost. Timing every step adds overhead; sampling only some requests reduces it but also reduces what you see.
- Traces can contain sensitive data. SQL bindings, headers and payloads may hold personal information, so a good tool captures them only on request and masks secrets.
- One request is not the whole system. Background jobs, other services and load-dependent contention need their own tools (queue monitoring, distributed tracing, load tests).
A side-by-side summary
| Question | Static analysis | Runtime analysis |
|---|---|---|
| Will this call ever receive the wrong type? | Yes — checks every call site | Only for calls that happened |
| Why is this endpoint slow in production? | No — cannot measure time | Yes — per-step timings |
| Does this page trigger N+1 queries? | Sometimes, with special rules | Yes — the queries are listed |
| Is this error handler correct? | Yes, even if it never runs | Only if it ran while observed |
| What does this endpoint actually call? | What it could call | What it did call |
Using both
A practical setup uses each where it is strongest: static analysis in CI to stop whole classes of bugs before merge, and runtime traces in staging and production to find what is actually slow or surprising. When a trace reveals a problem — a lazy-loaded relation, an unexpected external call — the fix often becomes a new static rule or test, so the same mistake is caught before it runs next time.
Further reading
- How a request flows through a Laravel application
- HTTP security headers explained — another class of problem you only see by checking the real response.