When you inherit a PHP application with no documentation, the temptation is to write it all up: every class, every function, every table. Don't. Nobody will read it, it'll be out of date in six months, and you'll have spent the budget you needed for the actual work.
Useful documentation answers the questions the next person will have at an awkward moment: how do I run this, how do I deploy it, what happens at midnight, and why on earth does it work like this? Here's how we approach writing that down, and how to learn it from a codebase that won't explain itself.
Write for the person fixing it at 2am
That person is probably you, or someone like you, six months from now. They know PHP, but they don't know this system. They need to get it running, find the right file, and understand what a change might affect. Anything that doesn't help with that can wait.
What to document first
In rough priority order:
- How to run it, deploy it and restore it. Steps someone can follow without asking you. Include the PHP version and extensions, required services, where configuration lives, and how a release reaches production. Test it by having someone else follow it on a clean machine.
- The map. A page listing every way code gets executed: web entry points, cron jobs, command-line scripts, webhooks coming in, and every outside service the system calls. This catches the things that live outside the codebase and break silently.
- The data. What the main tables mean, which ones are the source of truth, and the relationships that no foreign key records.
- The rules. The business logic that isn't obvious from reading: how prices are rounded, who is allowed to refund, what counts as an active customer.
- The oddities. A running list of the strange things you find, and the reasons if known. "This cron job runs at 03:17 because the supplier's feed isn't ready before then" will save someone a day.
How to learn what the code does
You have to understand the system before you can describe it. These techniques work on code nobody explains:
Trace one real request from start to finish
Pick a typical page, such as the checkout, and follow it from the entry point through every include and function call until the response goes out. A debugger such as Xdebug makes this much quicker, since you can step through and look at the call stack, and its tracing features can record the whole path. Write the trace down as you go. One properly understood request teaches you the architecture.
Let the history show where the risk is
Files that change often are where the work and the bugs are. This command lists the twenty most frequently modified files in the last year:
git log --since=1.year --name-only --pretty=format: \
| sort | uniq -c | sort -rg | head -20
Start your documentation there. Commit messages and old tickets also explain why things were done, and they're often the only record.
Look at what actually gets used
Access logs show which URLs matter. The database's slow query log and general log show which queries really run. Together they tell you what the application does in practice, which is often different from what the code suggests. Code that never shows up in either is a candidate for removal, once you've checked with a test or a staging replay.
Use the database as a map
If the schema is the only reliable description of the system, extract what you can from it. Tools such as SchemaSpy generate browsable documentation and diagrams from a live database. When there are no foreign keys, you can still infer relationships by looking at the joins in the queries and at column names like user_id.
Measure the complexity
Static analysis tools, for example PHPMetrics, can point out the largest and most tangled parts of the codebase. That helps you choose which areas to read carefully and which to leave alone.
Talk to people
Ask the people who use the system: support staff, finance, whoever runs the monthly reports. They know what breaks, what gets worked around, and what happens at year end. This is often the quickest route to the rules that aren't written anywhere. Take notes, and double check what you learn against the code.
Where to keep it
Keep documentation in the repository, in plain Markdown files, next to the code it describes. A README for the essentials and a docs/ folder for the rest is enough. Documentation in a separate wiki drifts away from the code, and people stop trusting it. If you want diagrams, text-based formats such as Mermaid live happily in Markdown and show up in version control diffs.
Update the relevant page in the same change as the code. A short rule in your review process, "does this change need a docs update?", does more than any tool.
Turn behaviour into tests where you can
A test is documentation that fails when it becomes wrong. When you work out a business rule, write a test with a descriptive name instead of a paragraph. It can't go stale quietly. We cover the technique in adding tests to legacy PHP code.
A handover checklist
If you're passing a system to someone else, or taking one over, check that these exist:
- Setup, deployment and restore instructions that someone else has followed successfully
- A list of cron jobs, workers and webhooks, with what each does
- A list of external services and where their credentials are kept (not the credentials themselves)
- The database schema and a note on the main tables
- The known oddities and unfinished work
- Who to ask about what, if anyone is still around
Common questions
Should I document the whole codebase?
No. Document what you'll touch, and the parts that make the system hard to operate. Leave the rest until you need it.
Do I need special tooling?
Not to begin with. A text editor, the version control history and a debugger get you most of the way. Tools like SchemaSpy and PHPMetrics help for specific jobs.
How does this fit with modernisation?
It runs alongside every step. What you learn while documenting informs the inventory, the upgrade, the tests and the decision about what to do next. The modernisation roadmap shows the full order, and our post on rescuing an inherited PHP platform covers the situation where the previous developer is gone.
Inherited a codebase nobody understands?
We'll read it, map it, and hand you documentation you can actually use, before we change anything.
See our legacy system support