Facet's Drupal 7 Best Practices Checklist
I’m often surprised when our Drupal 7 best practices catch seasoned Drupal developers off-guard. We’ve worked with a good number of codebases over the years, and as with any optimization-driven developer, we always try to improve our processes and maximize efficiency.
While our workflow is opinionated, our approach simplifies the knowledge transfer for onboarding new developers into projects. Without knowing the particulars of a project, our developers know how security updates are maintained, our standards for code review, and deployment best practices among other things.
I want to share our experience with you so I'm publishing Facet’s Drupal 7 Assessment (with plans to release our Drupal 8/9 Assessment in the future!). We’ve gotten pretty good at assessing and adopting old code bases and applying our Drupal best practices, so I sincerely hope these checklists help you as a developer, Drupal agency owner, or in-house Drupal development team manager. Please, share your feedback and any additional items that you feel would improve the checklist!
Why Drupal 7?
We still maintain quite a few Drupal 7 sites, and while Drupal 8 is on the horizon for many, I believe Drupal 7 will be around for quite a few years. As Drupal 7 approaches end of life (EOL), more sites will need appropriate best practices and standards in place to simplify security and maintenance processes.
What about Drupal 8 and Drupal 9?
We will be publishing our recommended best practices for Drupal 8/9 soon. Just as with Drupal 7, our best practices simplify the development and release management process—focusing on stability and bandwidth of developer output.
Drupal 9 shouldn’t have significant changes to the best practices required versus Drupal 8. Once CMI 2’s roadmap lands a few deployment operations may be simplified, but otherwise it’s business as usual.
Who does this Drupal Checklist Help?
Drupal 7 Assessment for Agencies
While I’ve personally spent more than 10 years with Drupal and in this industry, I’ve never quite found resources like the one I’m sharing with you today.
Most agencies and service providers in particular are extremely protective of their standard operating procedures.
We all seem to protect how we deliver value to the world so that we can continue to earn a living.
But along the way, we solve the same problems over and over—often only slightly tweaking this process to reflect new experiences and challenges.
Here is to hoping that with this checklist in hand your agency can thrive and you face new challenges.
Drupal 7 Assessment for Businesses
Not all sites require every checkbox in our Drupal 7 Assessment, but every checkbox in the assessment should apply to all websites.
Sometimes there is just a trade off in configuration, stability, scalability, or maintainability in your site.
Knowing the difference between which items to implement or ignore comes from experience, so feel free to reach out if you have questions about your particular case.
In the end we have a number of benefits we tout to our customers:
- Decreased costs. The time it takes to implement changes to the site are reduced, as deployments and testing are automated (or you approach that golden threshold of coverage).
- Faster turnaround times for work requests. We reduce context switching into a code base’s peculiarities—instead focusing on consistency for development standards.
- Increased stability. Our approach has the benefit of compounded experience across many different websites and code bases. This experience in turn lends itself to dependability.
- Key-man risk reduction. With multiple developers understanding best practices and conventions, we like to tell prospective customers that even if Facet were to one day blow up, many other Drupal developers would understand our best practices and be able to carry them forward.
How to Navigate These Resources
Copy from one of the Google Doc Template or the Notion Checklist Template in order to start working with a live document. The rest of this page is, essentially, search engine food. If you want to browse the full list, please go right ahead and keep scrolling.
Drupal 7 Assessment Google Doc Template
Facet Drupal 7 Assessment Template as a Google Doc
Drupal 7 Assessment Notion Checklist Template
Facet Drupal 7 Assessment as Notion Checklist
Drupal 7 Best Practices Checklist
This is Facet's Drupal implementation best practices checklist and survey for onboarding new clients. It is not an exhaustive list, and it is fairly opinionated—but it's the best thing we have for mitigating risk and ensuring reduced total cost of maintenance when onboarding new clients.
Hosting Infrastructure
We assess the hosting infrastructure for each new client as they are onboarded in order to ensure they have an appropriately sized, supportable, performant, and scalable infrastructure.
From time-to-time, we may need to update the infrastructure hosting requirements for our clients depending on changing traffic, static file storage, or authenticated user traffic. Other variables may also apply.
Come back to this checklist as needed to demonstrate evolving needs to our clients.
- Drupal-tuned hosting Provider?
- When onboarding new clients who are already using Drupal, we ensure that our development workflow is streamlined and the total cost of ownership is reduced for ongoing updates, maintenance, and deployments.
- We also don't present ourselves as infrastructure experts to our clients, and we depend on the expertise of these tuned-Drupal hosting providers to ensure ongoing infrastructure updates, performance optimizations, and other under-the-hood implementations are in place.
- Pantheon
- Platform.sh
- Acquia
- Amazee
- Other?
- File Storage
Document the following information about the client's site in order to identify potential risks around supporting the site. We want to be aware of exceptional amounts of files to ensure the file storage infrastructure will continue to scale with the site's growth.
- Size
- Number of Files
- File Types
- File Tree/Structure
- On-Server vs. 3rd Party Object Storage (s3)
- CDN
- Does the site use a CDN?
- Is the CDN provided by the hosting provider?
- Would a different/additional CDN provide additional performance?
- What configuration is the CDN in? Origin Pull? Push?
- Database
- What database and version?
- Is the database using an appropriate collation for the data?
- Where is the database stored?
- Caching
- What caching information is already in place?
- Is the authenticated user experience cached?
- Search (Solr)
- Is Solr currently set up on the site?
- How is Solr configured?
- Is Solr included in the platform service provider, self-hosted, or is it external PaaS?
- Offsite Backups
- What is the current offsite backup configuration?
- Who has access to offsite backups?
- In a step-by-step outline, how would the backups be restored?
- HTTPS Secure Site
- HSTS (Available on Pantheon)
- Disaster Recovery / Continuity Plan
- Does the site have a current disaster recovery plan?
- Has the disaster recovery ever been tested?
- What is the maximum allowable downtime for the site?
Telemetry
- Production Performance Monitoring
- NewRelic
- DataDog
- Uptime Monitor
Security
- Drupal Version
- Drupal Contrib Versions
- PHP Version
- If Low, Evaluate with PHPCompatibility for an upgrade to PHP 7.2+
- Using our standard
.lando.yml
configuration, we can run PHPCompatibility with the following command from the Drupal web root.
lando phpcscomp
- Using our standard
- If Low, Evaluate with PHPCompatibility for an upgrade to PHP 7.2+
- Library Versions (e.g. CKEditor)
Development Practices
- Local Development Workflow
- Standup the site with our standard
.lando.yml
if it doesn't already exist for this project: https://github.com/FacetInteractive/facet-d7-upstream/blob/master/.lando.yml
- Customize the
name
,config.php
version,proxy.mailhog
.
- Complete the rest of the steps detailed in the Lando setup, lower on this list: https://www.notion.so/facet/Drupal-SOP-41d3e419ba9f4b7c9aba04ba85748222#37cc19a222b74c5ebd604af7594bcaee
- Customize the
- Standup the site with our standard
- Deployment Scripts / Cloud Hooks
Assuming that we're on a Drupal-tuned hosting provider, we should have scripts in place to handle consistent deployment operations.
Standard operations for Pantheon Quicksilver Platform are available on GitHub: https://github.com/pantheon-systems/quicksilver-examples.
- On Deploy
We deploy the following operations on each deploy:
- Post to New Relic
- Update Database
- Revert Features / Config Import
- Environment Switch
- Clear Caches
- Post to Slack
- On Backfill / DB-import
On backfill from the
live
environment, it is important to perform standard operations to clean up and reset the database for development.- Post to New Relic
- Sanitize DB - User Emails, Credit Cards, PII
- Enable Development Modules
- Post to Slack
- On Deploy
- Programmatic Builds
Programmatic builds are paramount to producing a consistently high-quality site experience. Many developers may touch a site over the course of its lifetime. The more we can distill a particular site's build process and maintenance processes into scripts, the more we can describe (via code) how the site operates and is maintained.
- Appropriate usage of
custom
andcontrib
folders withinsites/all/modules
andsites/all/themes
- We must separate the
custom
andcontrib
modules so that our build operations can delete contrib module and theme directories and download them fresh—ensuring an idempotent rebuild of the site from open source repositories.
- We must separate the
- Drush Make
-
./build/site.make.yml
- The website has a well-documentedsite.make.yml
that allows us to idempotently rebuild the site over and over and get the same result.
- When first building the site from a make file, it is important to successively rebuild it from the
drush make
command, modifying thesite.make.yml
file iteratively and ensuring the site artifact is generated to perfectly match the current site. That means running themake.sh
should generate NOgit diff
.
- We typically need to specify to the following granularity when building a
site.make.yml
for the first time.
- Apply Drupal issue queue patches.
- Perform a specific SHA1 commit checkout of a Drupal contrib module when the site was previously built with a
-dev
release. This commit represents the specific point in time that this site was built with the respective dev release.
- Document the remaining
git diff
locally scoped to each Drupal contrib module, and save that diff as a custom patch to be applied during this build operation.
- The end result of this iterative build process is a well-documented, fully patched Drupal site that can idempotently be rebuilt over and over.
- When first building the site from a make file, it is important to successively rebuild it from the
-
./build/make.sh
- The website has a well-defined build process that detailsdrush make
, removal of Drupal contrib and library projects along with other build steps for the site's code scaffolding. Our goal is to ensure idempotent rebuilding for the site at any given time.
- If there are any further customizations to be made to the build process besides our standard
drush make
from thesite.make.yml
detail them in this script.
- After fully documenting the site's code scaffolding, we move on into applying updates to said code to bring Drupal core, contrib, and libraries up to current standards.
- If there are any further customizations to be made to the build process besides our standard
-
./build/
updatemake.sh
- Updates the make.yml whenever we want to run updates. After successfully documenting the site's open source code and patches, run thisupdatemake.sh
script to ensure the site is fully up to date.
- If there are any customizations to the Drupal security update or library update process, make those customizations here.
- For instance, sometimes repositories for old libraries are taken offline, and you must use another source.
-
- Composer
- Identify how composer is currently used to maintain the site's code.
- If composer is used in combination with
drush make
ensure that the build steps for composer are also included in./build/make.sh
- Unused Modules
- Are there modules that should be uninstalled?
- To uninstall these modules, first make sure that the appropriate uninstall hooks are fired in the
prod
environment before removing these modules from the repository.
- Typically, we would bundle our uninstall commands into our
PROJECT_release_mgmt.install
file, which controls the order and sequencing of updates as we deploy updates.
- Appropriate usage of
- Git
-
.gitignore
at root supports programmatic builds, log files, and folder structure without unnecessarily committing odd artifacts or hidden files.
-
.gitsubmodules
remove all git submodules.
- Check that there are no nested
.git
repositories other than the main root.
- If we're deploying to Pantheon, we can't checkout any vendor projects with
.git
folders in the path as they aren't allowed to be committed.
- When building from composer, we may need to ensure a custom script is executed to rename such
.git
folders and append an underscore →.git_
- If we're deploying to Pantheon, we can't checkout any vendor projects with
-
- Drupal Site Audit Report
- Pantheon provides this on the site dashboard as
Status
. This catches many things we also list here, but sometimes it's good to run the report and review it visually and highlight the items below which you know will need attention.
- You can alternatively run this with
drush sa
- Pantheon provides this on the site dashboard as
- System Variables -
settings.php
Many of the following best practices are already pre-configured in our
facet-d7-upstream
example settings.php file: https://gitlab.com/facet-interactive/drupal-ops/facet-d7-upstream/-/blob/master/web/sites/default/settings.phpWhen copying and setting up this file for the first time, or comparing the current settings.php against our best practices, consider the following checklist:
- Environment Switches for System Variables
- HTTP/HTTPS Redirects
- Make sure we have appropriate redirection from HTTP to HTTPS browsing experience across the site.
- Cache Settings
- Cache setting for
prod/live
andtest
should be set in settings.php with an environment switch.
- Lower environments should remain uncached to ensure speedy development.
- Cache setting for
- Email API / SMTP Connection Credentials
- Devel Config Defaults
$conf['dev_timer'] = 1; $conf['devel_memory'] = 1; $conf['devel_query_sort'] = 1; $conf['devel_query_display'] = 0;
- Reroute Email in non-live environments
- Use the
reroute_email
module
- Use the
- HTTP/HTTPS Redirects
- Coding Standards
- Configure Strict Reporting of PHP Notices in lower environments (NOT in
live
ortest
)
// Turn on All Error Reporting error_reporting(E_ALL); ini_set('display_errors', TRUE); ini_set('display_startup_errors', TRUE); $conf['error_level'] = 2;
- Check for count of similar logs in the
watchdog
table
select message, count(*) as count from watchdog group by message order by count DESC;
- Count PHP Notice Warnings in the Database
select -- message, -- variables, substring_index(substring_index(substring_index(variables, ';',2),'"',-2),'"',1) as type, substring_index(substring_index(substring_index(variables, ';',4),'"',-2),'"',1) as message, substring_index(substring_index(substring_index(variables, ';',6),'"',-2),'"',1) as function, substring_index(substring_index(substring_index(variables, ';',8),'"',-2),'"',1) as file, substring_index(substring_index(variables, ';',10),':',-1) as line, count(*) as count from watchdog where variables like 'a:6:{s:5:\"%type\";s:6:\"Notice\"%' group by type, message, function, file;
- Configure Strict Reporting of PHP Notices in lower environments (NOT in
- Environment Switches for System Variables
- Deployments
- Release Management Module
- Install the
PROJECT_release_mgmt
module from thefacet-d7-upstream
: https://github.com/FacetInteractive/facet-d7-upstream/tree/master/web/sites/all/modules/custom/release_mgmt
- The release management module is responsible for ensuring sequential database update commands are all logged. This allows us to ensure new modules get enabled, and then once enabled those modules respective
.install
files can manage their own independent updates.
- When first installing the release management module, we want to ensure it starts at version
7000
when deployed to live for the first time.
- After deploying the module to live, you may have to reset the version with the following command:
drush ev "include 'includes/install.inc'; drupal_set_installed_schema_version('PROJECT_release_mgmt', '7000')"
- Install the
- Config Management
- Features (D7)
- Features are cleanly separated and not interdependent.
- Features should be grouped based a single deployable site feature.
- It is ideal if a module contains a single content type, or a set of content types that compose a single page, e.g. a
homepage
may have aslide
content type, and some panels or other site building elements.
- Features defining roles & permissions do not declare dependencies on modules.
- Sometimes we group sitewide functionality, such as roles and permissions, into a single feature to make it easier to update permissions without creating the need to export multiple features when performing permissions updates.
- Features are cleanly separated and not interdependent.
- Config Sync (D8)
- Config Split (D8) - Dev/Test/Live
- Features (D7)
- Release Management Module
- Caching Configuration
Caching configuration is largely preconfigured for Pantheon with our
facet-d7-upstream
settings.php
: https://gitlab.com/facet-interactive/drupal-ops/facet-d7-upstream/-/blob/master/web/sites/default/settings.phpBut if applying these changes manually, ensure the appropriate context of the following across multi-dev environments:
- Page Caching
-
$conf['cache_lifetime'] = 0;
-
$[conf'page_compression'] = 0;
-
$conf['page_cache_maximum_age'] = 900;
-
- Preprocess CSS / JS
-
$conf['preprocess_css'] = 1;
-
$conf['preprocess_js'] = 1;
- AdvAgg Configured
- Compress CSS
- Compress JS
- Move render-blocking JS to the footer
-
- Asset Caching
- JS, CSS, Images - Long cache lifetimes so CDN can continue to serve them.
- Recommended a minimum of 1 week, e.g.
604800
- Recommended a minimum of 1 week, e.g.
- JS, CSS, Images - Long cache lifetimes so CDN can continue to serve them.
- Views
- Views Query Caching
- Views Render Caching
- Layouts
- Blocks Caching
- Panels Caching
- Entity Caching -
entitycache
- Object Caching -
redis
,memcache
- Proxy Caching - Varnish, CDN
- Page Caching
- Custom Modules
- Module Organization
- Application-specific, or entity-specific?
- Are implementations coupled to other modules, or are the modules clear and discrete?
- Drupal Standards / Coder
- Best Practices (found via search)
-
['und']
→ replace with →[LANGUAGE_NONE]
- Use of
eval()
- SQL queries that run many lines instead of EFQ / DQ
-
- Module Organization
- Roles & Permissions
- Admin uid=1 username and password is randomly hashed
- Subadministrator is maximum granted level to Client Userse
- Custom Permissions
- Custom Permissions should be setup and used instead of role access checks in Views, menu hook callbacks, and custom modules.
- Search for use of
user_has_roles
or$user->roles
for any custom checks for sets of roles.
- Drupal Modules
- Gray List - It's easy to implement something that can be quite harmful to the system, meaning load, stability, maintainability. Review the following modules and the full extent of their configuration to ensure no damaging configurations are in place.
- Rules
- Computed Fields
- Black List - Security concerns, abandoned projects, etc.
- PHP Filter
- Drupal Statistics
- Issue Queue Count - Programmatically query the issue queue and count RTBC and other issues for 7.x or 8.x version as needed.
- GROOM Patch Dev Work: Identify any issues which should be considered for applying a patch or our own development work.
- Create a ticket in JIRA per-effort to resolve these items.
- Group up the RTBC patches, but consider breaking them apart if the level of testing is going to significantly impact the size of the estimate (or total number of items to apply patches).
- Any tickets which require additional development work / don't have a patch applied should be raised as a dev task / risk for consideration.
- GROOM Patch Dev Work: Identify any issues which should be considered for applying a patch or our own development work.
- Issue Queue Count - Programmatically query the issue queue and count RTBC and other issues for 7.x or 8.x version as needed.
- Gray List - It's easy to implement something that can be quite harmful to the system, meaning load, stability, maintainability. Review the following modules and the full extent of their configuration to ensure no damaging configurations are in place.
Database
- Evaluate Cardinality of Indexes
- Evaluate MySQL Slow Query Log for low-hanging fruit.
Local Development Workflow
Drupal 7
-
.lando.yml
- Configured and setup with our best practices.
- Preconfigured reference: https://gitlab.com/facet-interactive/drupal-ops/facet-d7-upstream/-/blob/master/.lando.yml
- PHP Version
- Webroot
- Xdebug Configured
- Solr Configured
- Appserver Build Commands
- Download registry_rebuild
- PHPCS Config
- Nodeserver (if gulp) Build Commands
-
npm install
-
gulp
-
- Tooling
- Events
- post-db-import
-
local_db.sh
- Programmatic cleanup of local development environment.
-
- post-db-import
-
example.settings.local.php
- Keep a copy of the recommended local development settings committed to git history.
- When first standing up the environment locally, copy this to
settings.local.php
- When first standing up the environment locally, copy this to
-
local_db.sh
- Standard shell script for sanitizing the database on local db import and ensuring development can be executed safely.
- Sanitize users
- Environment Switch (D7) / Config Sync (D8)
- Enable Development Modules
- Disable Production Modules (e.g. caching)
- Development Modules
-
stage_file_proxy
-
reroute_email
-
environment
-
environment_status
-
- Linter
- Drupal Coder
- PHPCS
-
.git/pre-commit/hook
- to fire Drupal Coder PHPCS standards before each commit
It's a bit extreme for some projects, but to enforce linting standard for each commit you can setup lando to copy the following script into
.git/pre-commit/hook
to prevent git commits unless the files in the commit pass linting:https://gist.github.com/skwashd/b353d59f460d5d77a2f7d99f5799fb96
- GrumPHP?
- PHPLint
Quality Assurance
Manual QA
- Testing Plan
- Are all critical functions clearly defined?
- Can we transfer all testing items to a Google Sheet?
- Who is responsible for testing?
- What's their level of expertise?
- Is testing their full-time or part-time responsibility?
- If Facet is going to be responsible for testing, who is going to teach us about the website functionality / document the critical paths?
- Who are the key power users? We can perhaps use these people as product champions and shorten the feedback loop during deployments.
- Are all critical functions clearly defined?
Automated QA
Assess the current levels of automated testing available, and determine if investment into the following technologies is necessary to simplify our ongoing development, maintenance, and support.
- Functional Testing (Interfaces)
- Are there programmatic cron jobs or items which we'll need to rigorously define for testing?
- Functional Testing
- Is there sufficient complexity in the web application to justify automated tests? They require initial investment and upkeep.
- Determine the sophistication of automated testing required. Should we use something like Behat or would a SaaS testing solution such as Ghost Inspector suffice?
- What critical paths should be tested on each deploy as a smoke screen?
- Visual Regression Testing
- Is the site consistently undergoing front-end development?
- Unit Testing
- What code coverage do we currently have?
- Is the code unit testable (e.g. OOP in D8) or is it largely hook-driven (D7).
- Is there a clear need to refactor some code for cleaner separation of interfaces and testability?
- Are there standard services which should be refactored into a Service Class? e.g. email, logging
- Performance Testing / Load Testing
- Are there any performance or load testing scripts in place to benchmark site performance?
- Is the site’s revenue tied to page load speed and performance in a significant way?
- Do we have to deal with a remarkable amount of traffic? Periodically or consistently?
- Consider Recommending Blazemeter / Lokust for load testing
- Consider Benchmarking the front-end page speed performance across the site
- PageSpeed score should be
>85
- PageSpeed score should be
Release Management
- Git Workflow
- Determine if there is a defined git workflow that ties into current development and deployment infrastructure (CD)
- If not defined, establish the branching, code review, and release management process.
- Release Workflow
Establish our best practices including defining the following for the project and communicating it with the client:
- Release Manager, e.g. Facet Team Lead
- Hotfix Approval Process (downtime, disaster recovery [DR] event, etc)
- Release Signoff Process → See standard Facet JIRA Ticket for
DEPLOY
tickets.
- Continuous Integration
Assess and determine the current sophistication or level of need for implementing the following in a CI/CD pipeline:
- Automated Tests
- Programmatic Builds & Deploys
Drupal 7 TCO Optimization
Depending on the long-term goals of the Client, we may want to recommend incremental adoption of third-party systems to slowly decouple data from Drupal.
For example, consider approaches such as:
-
xautoload
to implement OOP in Drupal 7
- Data Lake / Data Warehouse Configuration for Reporting
- Prepare for Drupal 7 LTS
Common Drupal Configurations
The following common Drupal configurations may not always be necessary, but certainly can improve the quality of various website archetypes.
Administrative Tools
- Enable an Administrative Theme and Admin Tools. JQuery Update, Adminimal Theme, Adminimal Admin Menu, Field Permissions Administration (FPA), and Module Filter are some of our favorite modules for easier Drupal administration and configuration.
- Reroute Email in development environments. Make sure email cannot be sent from non-live environments by implementing the Reroute Email module.
Customer Support Tools
- Implement Masquerade for easy login to end-customer accounts to provide better support to internal and external users.
Improve User Experience
- Redirect Unauthorized Pages to User Login. Improve usability for Access Denied (403) pages by redirecting users to the User Login Form.
- Improve Email Deliverability with SMTP email sending. Implement SMTP, Mailsystem, and any third-party modules (e.g. Mandrill, Sendgrid) required for transactional email sending.
Maximize SEO
- Metatag Configuration. Appropriate configuration of Content Types, Taxonomies, and Users, making sure that content that is not meant to be a landing page—such as Taxonomy Terms—are
noindex,follow
ed and other such foundational optimizations.
-
noindex
low quality content / duplicate content
- Metatag Views (
metatag_views
is included inmetatag
module)
-
- Social Metatag Configuration. How do pages look on social platforms which are meant to be shared? Ensure that the appropriate image, header, and description are in the metatag markup for maximum impact on social platforms.
- Open Graph Image Tags
- Twitter Cards
- URL Path Management. Appropriate configuration of Pathauto, Redirect, and Global Redirect modules.
- Pathauto configurations follow a natural hierarchical pattern on the site (breadcrumb of the menu matches the URL structure).
- Redirect Module Configuration Setup with Pathauto updates.
- Redirects should be created when the path updates and the old path should be
301
redirected to the new path.
- No redirects in
.htaccess
file.
- Redirects should be created when the path updates and the old path should be
- XML Sitemap. Appropriate configuration of XML Sitemap for the publicly indexable site content.
- Make sure it is submitted via Google and Bing Webmaster Tools.
- Exclude content which is low value, or could adversely impact the site crawl allowance of the site.
- XML Images Sitemap. Appropriate configuration of XML Sitemap for Images.
- Yoast SEO Module. Fuel constant improvement for Content Editors with immediate feedback on the node edit experience.