Contact

Inherited platforms

Inheriting a Drupal site nobody documented

The code is never the missing piece. Drupal is a known quantity and it reads fine. What is missing is intent, and intent is not in the repository.

You can read every line of an inherited Drupal site and still not know which parts you are allowed to touch.

Nobody hands over an undocumented platform at a calm moment. It arrives because something broke, or because a deadline is close and the people who built it are not answering. The agency taking it on has usually already promised a date.

So the instinct is to open the custom modules and start reading. That is the wrong first hour. Reading tells you what the code does; it does not tell you whether anything still calls it, whether the feature has a user, or which of these things ends the client relationship if it stops working. You can lose a full day understanding a module that has not run since 2021.

Before you read anything, find out what actually runs.

Day one is reproducibility, not understanding

The first question is not what the site does. It is whether you can stand it up again from what you were given.

Can the repository rebuild the site on its own?
composer install
drush status

If that fails, or if it succeeds and the result does not resemble production, you have found the first item of the audit before you have looked at a single bug. A site that cannot be rebuilt from its repository plus a database dump is not a codebase, it is a running instance with a backup, and every estimate you make on it is a guess.

The rest of day one is the recovery path. A dump you have restored, a branch you can reset to, and a revert you have performed once on purpose while nothing was wrong. And a rule that costs nothing to adopt and everything to skip: work never happens on production, and if production is the only environment that exists, then building a second one is the first deliverable rather than a nice-to-have.

Read the log before you read the code

An inherited site has usually been telling anyone who would listen exactly what is wrong with it, every day, for years.

What the site has been complaining about
drush watchdog:show --count=100 --severity=Error

Two outcomes, both useful. Either you get a wall of repeating errors, in which case the top three by frequency are a better starting list than anything you would have picked by reading, or the logging module is switched off, in which case you have learned that nobody has been watching this site at all, which is its own finding and belongs in the report in those words.

Recent errors are also the fastest way to find the parts of the system that are alive. Dead code does not throw.

The three inventories

Before intent, establish surface. Three commands cover most of it.

Everything enabled that is not core
drush pm:list --status=enabled --no-core

Split the result in two. Contributed modules tell you what the build was trying to be, and each one that is abandoned or unreleased for your major is a future decision rather than a present problem. Custom modules are the part nobody else can price, and they are where the next section goes.

Is the repository the source of truth, or a souvenir?
drush config:status

This one command answers a question that changes the entire shape of the engagement. If it reports nothing, configuration is exported and the repository is authoritative, so you can work in branches like a normal project. If it reports pages of differences, the site has been configured through the interface for years and nothing was ever exported. That is not a bug to fix in an afternoon. It means any deployment you attempt can silently overwrite live configuration, and establishing a safe deployment process becomes work in its own right, ahead of everything the client actually asked for.

A site whose configuration was never exported has no safe deployment. Say so before you promise a release date.

Which content types are alive, and which are furniture
drush sql:query "SELECT type, COUNT(*) AS total,
  FROM_UNIXTIME(MAX(changed)) AS last_touched
  FROM node_field_data WHERE status = 1
  GROUP BY type ORDER BY total DESC;"

This is the cheapest insight on the list and the one that most often changes a scope. A content type with four published nodes last edited in 2019 is not a feature the client uses, whatever the specification says, and the fields, views and templates hanging off it are not worth migrating. Bring that table to the first meeting. It is difficult to argue with and it usually saves somebody money.

Custom code exists for exactly four reasons

Every custom module you inherit was written for one of four reasons, and which one it is determines what you do with it. This is the judgement the client is actually paying for.

Reading inherited custom code
Why it existsHow you recognise itWhat to do
Contrib did not exist yetA maintained module now does the same job, often betterReplace it, and the estate gets smaller
Contrib existed, nobody found itA well-known module matches the behaviour closelyReplace it, after confirming the edge cases
A real client requirementNothing in contrib matches, and the business can explain whyKeep it, document it, and put a test around it
Somebody’s preferenceIt reimplements something core already doesLeave it until you have credit to spend

The first two rows are where the money is. Replacing custom code with a maintained contributed module is the only kind of work that makes an estate cheaper to own afterwards, and it is invisible in a screenshot, so it has to be argued for in writing.

The fourth row is a trap worth naming. The code that reimplements a core feature is frequently the code somebody was proud of, and if that somebody is still at the client, deleting it in week one buys you an enemy in exchange for a small diff.

In the first two weeks you may not delete anything. You may write down everything you would delete.

The three questions no command answers

Everything above is recoverable from the estate itself. The expensive gap is not, and it is always the same three questions.

  • Who uses this, and when? The feature that looks central in the code may be used twice a year by one department, and the ugly form nobody documented may be how the revenue arrives.
  • What broke last, and what did you do about it? This maps the fear. It also tells you which parts of the system have already been repaired badly under pressure.
  • What must never break? Ask it plainly. Almost everyone can answer it in one sentence, and that sentence should govern the order of everything you do next.

If you are working white-label, these do not go to the client directly. They go to the agency, and the agency carries them, which is the real cost of discretion and the reason a good agency contact is worth more to an estimate than a good repository.

How white-label subcontracting actually works, and what I need from your team to keep it invisible

What the first week actually produces

Not a fix. A document, and it answers five things:

  • whether the site can be rebuilt from the repository, and what is missing if not,
  • whether configuration is exported, and therefore whether deployment is currently safe,
  • what runs, what is enabled but idle, and what is content nobody has touched in years,
  • which custom code is load-bearing, which is replaceable, and which nobody can explain,
  • the three things that would hurt most, each with the cost of fixing it and the cost of leaving it.

That last line is the one clients read. Costing the option of doing nothing is what turns an audit into a decision they can actually make, and it is also the honest position: on an inherited estate, some findings genuinely are not worth the money to fix.

Write the runbook into the repository as you go, not at the end.

Not for tidiness. If the knowledge lives only in your head, you have replaced an undocumented site with an undocumented site plus a dependency on one contractor, and the client is not better off than they were in week zero.

Why it was undocumented in the first place

It is rarely negligence, and saying so out loud earns more trust than any audit finding. Documentation decays because nobody owns it: a build team hands over to a maintenance team, the maintenance budget covers tickets rather than knowledge, and every shortcut taken under a deadline is a paragraph that never got written.

Which means an inherited estate is not a broken thing to be fixed once. It is a thing that was allowed to drift, exactly like the upgrade that now costs a hundred hours instead of sixteen, and the same maintenance decision fixes both.

What a Drupal 9 to 11 jump actually costs, and why sixteen hours is the wrong number to plan with

If nobody owns the documentation after you leave, it will be undocumented again within a year.

Sitting on one of these?

Send me the URL, or the repository, and what you have been told about it. I will tell you which of the five questions above your estate cannot answer yet, and what that is likely to cost.

johan@johanweb.net

No charge and no pitch. The written audit, with the detail behind it, is the paid part.