01
Start at the edges, not the entry point
Before opening main, list every way data enters and leaves the system: HTTP handlers, queue consumers, cron entries, migrations. The edges are a finite, honest description of what the system actually does — internal abstractions are only opinions about how it does it.
02
Follow one request end to end
Pick the single most common operation and trace it through every layer, writing down each file you pass through. One complete trace teaches more structure than an afternoon of skimming folders, and the notes become the map you hand to the next person.
03
Let the tests describe the contract
Test names are the only documentation that fails when it goes stale. Read them for expected behaviour and, more importantly, for the edge cases someone was burned by badly enough to encode.
04
Change something small and safe
Understanding is confirmed by feedback, not by reading. Rename a variable, add a log line, break an assertion on purpose. The failure output tells you how the build, the test runner and the deploy pipeline are wired.