A large Drupal site doesn’t have to be difficult to maintain
A site with thousands of publications and several editorial teams has a lot going on. Adding a report or changing a listing shouldn’t become a development project of its own.
Drupal is good at managing that kind of information. But a Drupal site can also reach a point where updates feel risky and a small improvement breaks something elsewhere. Keeping it running starts to eat into the budget for making it better.
Some complexity comes with the job. Some comes from overlapping modules, undocumented changes, or custom code doing something Drupal already knows how to do.
We want the people who publish and maintain the site to understand how it works. That means using Drupal’s established tools where they fit, keeping custom code focused, and thinking past launch day.
That’s where lower long-term costs can come from. Hosting is only part of the bill. Time spent editing, testing, troubleshooting, upgrading, and figuring out someone else’s code counts, too.
Plan the content before building the pages
Maintainability starts with understanding the information an organization manages and the work its team needs to do.
Consider a public information library containing reports, news, events, and program information. Treating everything as an individually designed page may feel flexible initially. Over time, it can leave editors copying layouts, manually maintaining related links, and fixing inconsistent information in several places.
We would start by identifying the meaningful differences between those content types and the relationships between them. A publication might have a publication date, topic, program, and downloadable file. That structure can support consistent listings and filters without asking an editor to rebuild them for each new document.
Drupal’s core Views and Views UI modules provide configurable ways to build content listings. For an ordinary filtered publication index, that is an important option to evaluate before writing a separate listing system.
The planning question is practical: when the organization adds another year of publications, another editor, or another program, how much work should that create? A well planned model lets the content grow without multiplying the number of special cases.
Work with Drupal’s bootstrap and request lifecycle
When we say we build things “the Drupal way,” we mean using the framework’s supported mechanisms for the work they are designed to handle.
Drupal’s bootstrapping process prepares the application to handle a request. Its kernel initializes the site environment and manages the service container, which makes shared capabilities available to the application. Drupal’s front controller and kernel implementation show how that request enters the system.
Our extensions should fit into that lifecycle: routes and controllers for request handling, access checks for permissions, the Entity API for content operations, and the render system for output. A separate PHP endpoint that manually loads pieces of Drupal can create another execution path to secure, debug, and maintain.
For custom classes, dependency injection makes the services they need explicit. A small service that receives its dependencies is easier to understand and test than business logic scattered through templates and global lookups.
Supported extension points also matter for upgrades. Drupal’s backward compatibility policy distinguishes public APIs from internal implementation details. Depending on the documented API gives future maintenance a more stable foundation than depending on incidental behavior inside core.
For a site owner, the benefit is straightforward: another experienced Drupal developer has a recognizable system to work with, rather than a second framework hidden inside the first.
Make custom code justify its ongoing cost
Before adding functionality, we ask whether Drupal core can meet the requirement through configuration. If it cannot, we evaluate maintained contributed modules and the smallest sensible custom implementation.
An additional module needs a clear purpose. Its maintenance status, compatibility, dependencies, and security advisory coverage all matter. Advisory coverage is useful information, not a guarantee that a module is free of vulnerabilities.
Custom code can be the right choice for a genuinely specific business rule or integration. When it is needed, we want a clear responsibility, a supported extension point, and a way to verify its behavior. We avoid modifying core or contributed files directly; any necessary patch should be explicit, reproducible, and reviewed for removal when the underlying issue is resolved.
The goal is the smallest ongoing maintenance burden. A stack of loosely related modules can be harder to support than one focused custom module. Rebuilding a mature Drupal capability from scratch can be equally costly. The decision should follow the actual requirement, rather than a blanket preference for either approach.
Keep configuration changes reviewable
A website includes both content and the configuration that defines how it works. A new publication is content. Its field definitions, display settings, and listing configuration belong to the site’s structure.
Drupal’s configuration management system allows configuration to be exported to YAML files and kept in version control. That makes structural changes reviewable and allows them to move through development and staging before production.
We plan around that distinction. Editors should be able to publish ordinary content without a deployment. Structural changes should have a documented path that can be repeated and checked. Secrets and environment-specific credentials need separate handling, and configuration synchronization does not replace a content migration or a database backup.
This reduces reliance on someone remembering exactly which administrative settings they changed on the live site last month.
Use Composer to make builds repeatable
Composer manages the PHP packages a Drupal project depends on. Drupal’s recommended project template provides a conventional starting structure, and its dependency management guidance explains how those packages are maintained.
Two files are central: composer.json describes the project’s requirements, and composer.lock records the resolved package versions. With the lock file committed, composer install installs that recorded dependency set. composer update resolves changes and updates the lock file; it belongs in a deliberate update workflow. Composer’s documentation explains this distinction.
That consistency helps connect a developer’s environment, automated checks, and a release build. The PHP version and required extensions still need to match the project’s requirements; a lock file cannot make different environments identical by itself.
Composer also supports named project scripts. These can provide consistent entry points for the project’s code checks and test suites, so developers and automated jobs run the same instructions. Its validation and audit commands help check package definitions and identify known dependency issues.
Composer does not test whether an editor can publish an article or whether a member can access a document. It provides a repeatable foundation on which those checks can run.
Continuous integration testing
Continuous integration testing means automatically checking proposed changes as they enter the shared codebase. For a Drupal site, those changes include dependency updates and configuration changes as well as custom code.
Drupal’s community uses GitLab CI to run automated checks for contributed projects. A client website also needs coverage for its particular configuration, integrations, and user journeys. A module passing its own tests cannot prove that every combination on a specific website works correctly.
We’re developing an in-house continuous integration testing workflow for our Drupal maintenance work. It is designed to make important checks repeatable and give changes a clear review point before release. As we introduce it, each site’s test coverage will be defined around its actual risks, integrations, and editorial workflows.
Test the behavior the organization depends on
Drupal provides several layers of automated testing. They answer different questions:
| Test layer | A useful question for a Drupal project |
|---|---|
| Unit tests | Does a small piece of custom business logic produce the right result, including unusual inputs? |
| Kernel tests | Does custom functionality work with the Drupal services, storage, and extensions it depends on? |
| Functional tests | Can the appropriate user complete a server-rendered workflow, and is an unauthorized user denied? |
| Functional JavaScript tests | Does an important interaction work in a browser when JavaScript or AJAX is involved? |
For the publication library, useful checks might confirm that an editor can create and publish a document, a topic filter returns the expected records, and unpublished or restricted material stays inaccessible to visitors who should not see it.
We would prioritize those outcomes over writing a test for every implementation detail. Tests are code that must be maintained too. A focused suite with clear failure messages is more useful than a large suite that is slow, fragile, or routinely ignored.
Give each proposed release a repeatable check
Our planned workflow follows a clear sequence:
- Build a clean test environment from the committed code and Composer lock file, including the development tools needed for testing.
- Validate dependency definitions, review known security advisories, and run the project’s coding and static-analysis checks.
- Exercise the relevant installation or upgrade path, database updates, and configuration import in a disposable environment.
- Run the tests for custom behavior and essential editorial, access, and integration workflows.
- Review failures and complete the agreed human checks before approving a release.
Where an upgrade needs representative content, sanitized fixtures or a suitably sanitized database copy can make the rehearsal more meaningful. Test environments should use controlled integrations and must not send real customer messages or make live transactions.
Continuous integration does not require automatic production deployment. A passing test suite supplies evidence for a release decision; it cannot replace all accessibility review, visual checks, security judgment, or editorial acceptance.
For ongoing maintenance, its value is cumulative: a useful regression test can keep checking an important behavior after the original feature has been delivered, including when a later dependency update changes something underneath it.
Let Drupal’s caching do its job
Performance and maintainability are connected. A site that repeatedly rebuilds expensive output may create pressure for additional infrastructure or emergency fixes when the underlying problem is missing cache information.
Drupal’s render cacheability model gives developers a way to describe when output varies and when it becomes outdated. Cache contexts describe variations, such as language or permissions; cache tags identify dependencies that can invalidate output; maximum age describes time-based validity.
Custom output needs the right metadata so Drupal can reuse it safely. Disabling caching across a page to fix one changing component can increase unnecessary work. Caching personalized or restricted output without the correct variations can create a correctness or access problem.
Our aim is to use the existing cache system correctly, measure representative pages, and address specific bottlenecks before adding more moving parts.
Make Drupal increasingly affordable to maintain
Maintainability needs an operating routine. Updates still require someone to review changes, address deprecated APIs, assess advisories, and check the result. Automated testing makes that work more repeatable; it does not eliminate it.
We want a project to leave future maintainers with an understandable content model, a clear purpose for its dependencies, documented custom behavior, and repeatable build and release instructions. Backup and recovery procedures should cover the code, database, and files together. Restoring old code alone is not a reliable recovery plan after a database change.
For an existing Drupal site, improvement can begin with a focused review: identify duplicated functionality, unsupported dependencies, undocumented patches, and the workflows most vulnerable to regression. That review may support incremental cleanup rather than a wholesale rebuild.
Drupal is not the least expensive answer to every website requirement. A small brochure site may need a simpler foundation. For an organization with substantial content, editorial responsibilities, and integrations, however, a carefully built Drupal system can provide room to grow without making unnecessary complexity the price of admission.
The goal is a site your team can keep using and improving without every change becoming a puzzle.
If your Drupal site has become harder to maintain than it should be, explore our Drupal development services or tell us about the system you need to improve.