Most WordPress sites outlast the person who built them. You launch a custom theme, pile on a handful of plugins, write a few dozen lines of functions.php logic, and move on. Six months later someone else opens the admin — or worse, the server — and they’re staring at a broken contact form after an Advanced Custom Fields update, with zero clues why. That frustration lands right on the builder who left no trail.
I’m Jonas Venn, and I’ve inherited enough undocumented WordPress installations to know exactly how fast goodwill evaporates. This isn’t about writing a novel. It’s about leaving a few specific artifacts that turn a hostile takeover into a routine handoff. If you build sites someone else will touch, this guide is for you.

Start with the Single Source of Truth
Documentation lives in a lot of places — a README file, a shared Notion page, a printed binder that ends up in a drawer. Pick exactly one location and call it the canonical record. For WordPress projects I keep a PROJECT.md file in the theme root or a top-level /docs folder inside the repository. It’s version-controlled right alongside the code, so it drifts out of date only if the maintainer ignores the commit hook I recommend later.
What goes into this file? The five things every future maintainer needs within the first two minutes of opening it:
- Hosting and access summary. Not passwords — just the names of the host, the DNS provider, the SSL certificate authority, and any CDN in play. The actual credentials belong in a shared vault like 1Password, and the doc should point there.
- Environment map. List staging, production, and local development URLs. Note any environment-specific constants in
wp-config.php. - Plugin and theme inventory. Every active plugin, its version at handoff time, and a one-line reason it’s installed. “Yoast SEO — client insisted on it; alternate sitemap logic in theme” tells a story that a bare list never will.
- Custom code locations. A child theme’s
functions.phpis obvious. A mu-plugin that intercepts WooCommerce order status transitions is not. Call out every drop-in, every must-use plugin, and any custom REST endpoint. - Known fragile areas. If the media library breaks when PHP memory drops below 256M, say so. This section saves hours of blind debugging.
This file isn’t a style guide. It’s a triage document. Write it for someone skimming at 11 p.m. on a Friday.
Map the Plugin Dependency Web
WordPress sites rarely break because of a single plugin. They break because of the invisible contract between five plugins that all assume they own the save_post hook. Document these relationships explicitly.
Create a section called “Plugin Contracts” in your main doc. For each plugin that touches data another plugin also touches, describe the handshake. Example:
Gravity Forms → User Registration Add-On → Ultimate Member
- GF feeds create WP users on form submission.
- Ultimate Member hooks into user_register to assign default role and profile fields.
- If Ultimate Member is deactivated, new user registrations lose the profile mapping silently.
When I inherit a site I check this table before I touch a single plugin update button. If the table doesn’t exist, I spend a day drawing it myself — and I resent every minute.
Also note plugins that are pinned to a specific version. Maybe the client refuses to pay for a license renewal, or a custom integration relies on a deprecated filter. Flag these with the version number and the reason. A future maintainer will see “Gravity PDF 5.3.2 — locked because 6.x changed the template filter signature” and know not to hit “update all.”
Document the Theme’s Opinionated Decisions
Every theme makes choices. A block theme decides where template parts live. A classic theme decides which sidebars register and what widget areas the client actually uses. Document the intention, not just the implementation.
For a block theme, list the custom block patterns and template variations you registered. If you built a “Team Member Card” pattern that pulls from a custom post type, explain where the post type is defined and which file contains the pattern markup. The WordPress Theme Handbook covers the mechanics, but your doc covers your specific architecture.
For a classic theme, document the template hierarchy overrides. If single-event.php exists because the client wanted a completely different layout for their “Events” post type, say so. Better yet, add a comment block at the top of the file that references the project doc:
/**
* Template for single Event posts (CPT 'event').
* Overrides the default single.php because the client requested
* a full-width map and no sidebar.
*
* See PROJECT.md § "Template Overrides" for the full hierarchy map.
*/
File-level comments cost seconds to write and pay back hours. I require them on every project I architect.

Describe the Content Model, Not Just the Menus
WordPress content is more than pages and posts. Custom post types, taxonomies, meta fields, and user roles form a data model you can’t see from the admin sidebar. When I take over a site and find a “Portfolio” post type with five custom taxonomies and 30 Advanced Custom Fields, I need to know why each piece exists.
Build a table that maps every custom content type to its purpose, its public visibility, and any template that renders it. Include the field group names if you’re using ACF or Meta Box. If a field like “hero_overlay_opacity” expects a value between 0 and 1, write that down. The next developer will otherwise discover it by breaking the layout.
If your site uses the block editor extensively, document the block patterns and reusable blocks the client relies on. A reusable block called “Pricing Table — 2024” that gets embedded on five pages is a single point of failure; flag it so nobody deletes it accidentally.
Earlier this year I wrote about the shock of opening a site and seeing nothing where content should be. The checklist in What to Fix First When a New WordPress Site Says Nothing Found covers the immediate triage, but proper content model documentation prevents that moment from happening in the first place.
Write Down the Build and Deployment Steps
Most modern WordPress workflows involve some kind of build tooling — Sass compilation, JavaScript bundling, Composer autoloading, or a CI/CD pipeline that deploys to WP Engine. If the person taking over the site can’t reproduce the build in their first ten minutes, they’ll hack around it, and that hack becomes the new normal.
Your documentation should answer three questions immediately:
- How do I get a local environment running? List the exact commands. If you use Laravel Valet, say so. If you rely on a Docker container with a specific
docker-compose.yml, link to it. If the database import requires a search-replace for the local URL, include thatwp search-replacecommand. - How do I build assets? Document the Node version you used, the package manager, and the build commands.
npm run buildmight seem obvious, but if you have separate commands for development and production, spell them out. - How do I deploy? Even if it’s “push to main branch, Kinsta auto-deploys,” write that down. Note any manual steps like clearing a specific cache or running a database migration script.
Keep this section operational. The goal is to prevent someone from reverse-engineering your Webpack config at midnight.
Log the Admin Quirks
Every WordPress site accumulates admin-specific oddities. A settings page that lives under “Tools” instead of “Settings.” A dashboard widget that must remain active because it triggers a critical cron job. A user role editor that silently resets capabilities when a specific plugin updates.
Collect these in an “Admin Notes” section. They don’t need long explanations — just enough context to stop someone from undoing a deliberate choice. Examples:
- “The ‘Regenerate Thumbnails’ tool under Tools runs a custom action that also updates image metadata. Do not remove the plugin without replacing that hook.”
- “The ‘Editor’ role was modified via Members plugin to allow theme options access. This is intentional for the client’s in-house team.”
- “The WP-Cron is disabled in wp-config.php and triggered via a server-side cron job every 15 minutes. See the hosting notes for the crontab entry.”
These notes transform a site from a mystery box into a known system. They cost nothing to write and save a support ticket — or a data loss incident.

Keep the Documentation Alive
Outdated documentation is worse than no documentation. It breeds false confidence. The maintainer follows a stale instruction, breaks something, and then distrusts every other word in the file.
I enforce documentation updates with a simple Git hook. In the project’s .git/hooks/pre-push, I add a check that compares the last modified date of PROJECT.md against the last modified date of any changed PHP file. If a developer modifies functions.php without touching the docs, the hook prints a reminder — not a block, just a nag. The team quickly learns to update the doc alongside the code.
If you’re solo, tie documentation updates to your release checklist. Every time you tag a version or deploy to production, spend three minutes reviewing the project doc. Did you add a new plugin? Update the inventory. Did you register a new REST endpoint? Add it to the custom code section. Three minutes per release compounds into a document that stays trustworthy for years.
FAQ
What’s the minimum documentation I can leave and still be useful?
A single PROJECT.md file containing the hosting summary, environment URLs, plugin inventory with reasons, custom code locations, and a list of known fragile areas. That’s roughly two pages and covers 80% of the situations a new maintainer faces. You can write it in under an hour.
Should I document every custom field and its validation rules?
Document the fields that have non-obvious behavior. A text field called “Subtitle” doesn’t need a paragraph. A number field that controls layout and breaks outside a specific range deserves a note. Focus on the fields that can silently corrupt the front end if misused.
Where do I store passwords and API keys if not in the documentation?
Use a shared password manager like 1Password or Bitwarden. Your documentation should reference the vault name and the specific entry title, e.g., “See 1Password vault ‘Client Projects’ → ‘Example.com WP Admin’.” Never store secrets in plain text inside a repository or a shared document.
How do I document a site that uses a page builder like Elementor or Bricks?
Document the global settings, any custom breakpoints, the theme style defaults, and any dynamic data integrations. Page builder sites often hide critical logic inside builder-specific settings panels. Note which pages rely heavily on builder widgets that could break on update, and export the builder templates to version-controlled JSON files if the builder supports it.
Documentation is not a favor you do for the next person. It’s a professional obligation that protects your own reputation long after you’ve moved on. The maintainer who opens your project and finds clear, concise, and current notes won’t hate you. They might even buy you a coffee.