diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index deb031969876438a8f21260733f3e0b3553a0ed2..5ffbbce37f61f796457285af82e961cf78e4059e 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -42,6 +42,7 @@ workflow:
 # testing merge requests and commits. Each of these pipelines has to be
 # triggered manually which can only be done by a maintainer of that project.
 
+# KeyCDN 8.x-1.x has 'current', 'previous major' and 'next major' variants.
 # Testing via phpunit binary directly.
 '→ Key CDN':
   extends: .downstream-base
@@ -50,6 +51,7 @@ workflow:
     project: project/keycdn
     branch: 8.x-1.x
 
+# API 2.x has 'current' and 'previous major' variants.
 # Testing via run-tests.sh.
 '→ API D10+':
   extends: .downstream-base
@@ -58,6 +60,7 @@ workflow:
     project: project/api
     branch: 2.x
 
+# Decoupled Pages 8.x-1.x has 'current', 'previous minor', 'previous major', 'next minor' and 'next major' variants.
 # For Nightwatch testing.
 '→ Decoupled Pages':
   extends: .downstream-base
@@ -66,6 +69,7 @@ workflow:
     project: project/decoupled_pages
     branch: 8.x-1.x
 
+# API 7.x-2.x has 'current' variant only.
 # For Drupal 7.
 '→ API D7':
   extends: .downstream-base
diff --git a/assets/phpcs.xml.dist b/assets/phpcs.xml.dist
index cf6407981e9b853dfdd492f46b73336bd8d9a2aa..471c29ae5b86bd86af622a9a2708b8c8a43e631f 100644
--- a/assets/phpcs.xml.dist
+++ b/assets/phpcs.xml.dist
@@ -1,6 +1,14 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<ruleset name="drupal-project">
-  <description>Default PHP CodeSniffer configuration for Drupal project.</description>
-  <rule ref="vendor/drupal/coder/coder_sniffer/Drupal/ruleset.xml"/>
+<ruleset name="drupal-contrib-project">
+  <!-- Change the name and description if you copy this file to your project to make customizations. -->
+  <description>Default PHP CodeSniffer configuration for a Drupal contrib project.</description>
+
+  <!-- Include all rules in the Drupal ruleset. -->
+  <rule ref="Drupal"/>
+
+  <!-- Include all rules in the DrupalPractice ruleset. Uncomment if required -->
+  <!-- <rule ref="DrupalPractice"/> -->
+
   <arg name="extensions" value="php,inc,module,install,info,test,profile,theme"/>
+
 </ruleset>
diff --git a/docs/jobs/phpcs.md b/docs/jobs/phpcs.md
index 179a4dfd12ec413d211678cef0059c753b013dc8..3d835b053a8d18c75b59556711db7ea4ea08b6a1 100644
--- a/docs/jobs/phpcs.md
+++ b/docs/jobs/phpcs.md
@@ -1,9 +1,22 @@
-# PHP_CodeSniffer
+# PHPCS
 
-This job will validate the module's code against Drupal coding standards.
+The `phpcs` job validates the project's source code against the [Drupal PHP coding standards](https://www.drupal.org/docs/develop/standards/php).
 
-It uses [PHP_CodeSniffer](https://github.com/PHPCSStandards/PHP_CodeSniffer/) library and configures the Drupal coding standards.
+[PHP_CodeSniffer](https://github.com/PHPCSStandards/PHP_CodeSniffer/) is used to perform the checks that are configured in the Drupal standards as specified in the [Drupal Coder module](https://www.drupal.org/project/coder). The standards are curated by the [Drupal Coding Standards project](https://www.drupal.org/project/coding_standards).
 
-If the module does not have a default `phpcs.xml` or `phpcs.xml.dist`, it will use [this one](https://git.drupalcode.org/project/gitlab_templates/-/blob/main/assets/phpcs.xml.dist). If it has one, then it will use it.
+## Customized configuration file
+By default the full set of `Drupal` coding standards are checked, using this
+[default phpcs.xml configuration file](https://git.drupalcode.org/project/gitlab_templates/-/blob/main/assets/phpcs.xml.dist). The `DrupalPractice` set of best practices are not enforced by default.
 
-You can also use the variable `_PHPCS_EXTRA` in the `variables` section of the `.gitlab-ci.yml` file to pass additional arguments to the call.
+This job checks files with the following extensions: `.php`, `.inc`, `.module`, `.install`, `.info`, `.test`, `.profile`, `.theme`
+
+A project may have its own `phpcs.xml` or `phpcs.xml.dist` configuration file which will be used instead of the default file.
+
+The set of coding standards should be defined using the short path with just the name:
+```
+  <rule ref="Drupal"/>
+  <rule ref="DrupalPractice"/>
+```
+
+## Custom options
+You can use the pipeline variable `_PHPCS_EXTRA` in the `variables` section of the `.gitlab-ci.yml` file to pass additional arguments to the call. An example is ` -v ` to add verbose output to the log.
diff --git a/includes/include.drupalci.main-d7.yml b/includes/include.drupalci.main-d7.yml
index dc48624226d74a6921bfcbe6a08b3c6d40e416ba..f7aa2012d75f16585db4356c8921ca9597935223 100644
--- a/includes/include.drupalci.main-d7.yml
+++ b/includes/include.drupalci.main-d7.yml
@@ -328,9 +328,9 @@ stages:
     # If composer.json exists then make a backup before it gets modified. Otherwise create an empty file.
     - |
       if [[ -f composer.json ]]; then
-        cp composer.json composer.json.backup
+        cp -v composer.json composer.json.backup
       else
-        echo "{}" > composer.json
+        echo "{}" > composer.json && echo "Project has no composer.json so creating an empty one"
       fi
     # Get the composer version currently installed. Use space or . for delimiters to return the single digit top-level version.
     - COMPOSER_VERSION=$(composer --version  | awk -F'[ .]' "{print \$3}")
@@ -402,13 +402,44 @@ phpcs:
     - when: on_success
   script:
     - cd $CI_PROJECT_DIR && pwd
-    - test -f phpcs.xml.dist || curl -OL https://git.drupalcode.org/$_CURL_TEMPLATES_REPO/-/raw/$_CURL_TEMPLATES_REF/assets/phpcs.xml.dist
-    - vendor/bin/phpcs --version
-    - composer show | awk '$0 ~ /codesniffer|coder|coding-standard|variable-analysis/ {print $1 " " $2}';
-    # Show the installed config file, the paths to the standards and actual coding standards groups being used.
-    - vendor/bin/phpcs --config-show installed_paths
-    - vendor/bin/phpcs -i
-    - vendor/bin/phpcs --basepath=$CI_PROJECT_DIR -s $_WEB_ROOT/sites/all/modules/custom --report-junit=junit.xml --report-full --report-summary --report-source $_PHPCS_EXTRA
+    # Does the project have its own phpcs config file?
+    - PHPCS_CONFIG_FILE=($(ls {.,}phpcs.xml{.dist,} 2>/dev/null | head -1 || true))
+    - |
+      if [[ ! "$PHPCS_CONFIG_FILE" ]]; then
+        echo 'This project has no (.)phpcs.xml(.dist), getting default from assets/phpcs.xml.dist'
+        curl -OL https://git.drupalcode.org/$_CURL_TEMPLATES_REPO/-/raw/$_CURL_TEMPLATES_REF/assets/phpcs.xml.dist
+      else
+        echo "Using the project's existing $PHPCS_CONFIG_FILE config file"
+        # If the file has hard-coded paths to the Drupal and/or DrupalPractice ruleset give a message recommending they are changed.
+        LONG_PATHS=$(grep -inE "vendor\/drupal\/coder\/coder_sniffer\/Drupal(Practice)?\/ruleset.xml" $PHPCS_CONFIG_FILE) || true
+        if [[ $LONG_PATHS ]]; then
+          printf "$DIVIDER\nThe project's $PHPCS_CONFIG_FILE file contains the following long path(s)\n$LONG_PATHS\n \nWe recommend that this is changed to the short syntax\n"
+          # Use only newline, not spaces, to split the grep array for sed.
+          IFS=$'\n'
+          for line in $LONG_PATHS; do
+            echo $line | sed -E 's/rule.*vendor\/drupal\/coder\/coder_sniffer\/(Drupal|DrupalPractice)\/ruleset.xml.*/rule ref="\1"\/>/'
+          done
+          unset IFS
+          printf "\n $DIVIDER\n"
+        fi
+      fi
+      # Add --colors and --report-width=120 if those options are not already in _PHPCS_EXTRA. Use ,, for lower-case matching.
+      [[ ${_PHPCS_EXTRA,,} =~ (--colors|--no-colors) ]] || _PHPCS_EXTRA+=" --colors"
+      [[ ${_PHPCS_EXTRA,,} =~ (--report-width) ]] || _PHPCS_EXTRA+=" --report-width=120"
+    # Show the versions of the applicable elements.
+    - COLUMNS=120 composer show | grep -E '(codesniffer|coder|coding-standard|variable-analysis)'
+    # Show the phpcs version, config file, the paths, the installed coding standards and actual number of sniffs being used in this job.
+    - $CI_PROJECT_DIR/vendor/bin/phpcs --version
+    - $CI_PROJECT_DIR/vendor/bin/phpcs --config-show installed_paths
+    - $CI_PROJECT_DIR/vendor/bin/phpcs -i
+    - $CI_PROJECT_DIR/vendor/bin/phpcs -e | grep -E 'contains.*sniff' || true
+    - echo -e "\e[0Ksection_start:`date +%s`:phpcs_sniffs[collapsed=true]\r\e[0KList of all PHPCS sniffs used in this job"
+    - $CI_PROJECT_DIR/vendor/bin/phpcs -e || true
+    - echo -e "\e[0Ksection_end:`date +%s`:phpcs_sniffs\r\e[0K"
+    - echo "executing $CI_PROJECT_DIR/vendor/bin/phpcs $DRUPAL_PROJECT_FOLDER -s --report-junit=junit.xml --report-full --report-summary --report-source --basepath=$DRUPAL_PROJECT_FOLDER $_PHPCS_EXTRA"
+    - $CI_PROJECT_DIR/vendor/bin/phpcs $DRUPAL_PROJECT_FOLDER -s --report-junit=junit.xml --report-full --report-summary --report-source --basepath=$DRUPAL_PROJECT_FOLDER $_PHPCS_EXTRA || EXIT_CODE=$?
+    - if [[ $EXIT_CODE == "" ]]; then echo "There are no PHPCS coding standards errors or warnings"; fi
+    - echo "Exiting with EXIT_CODE=$EXIT_CODE" && exit $EXIT_CODE
   allow_failure: true
   artifacts:
     expose_as: junit
diff --git a/includes/include.drupalci.main.yml b/includes/include.drupalci.main.yml
index 91fc58259d96aa1ed9b79d6979ee4cdb6edb9fde..930b8330b6bc42a4322c505ab0fa3a690fbbc273 100644
--- a/includes/include.drupalci.main.yml
+++ b/includes/include.drupalci.main.yml
@@ -634,13 +634,44 @@ phpcs:
     - composer
   script:
     - cd $CI_PROJECT_DIR && pwd
-    - test -f phpcs.xml.dist || curl -OL https://git.drupalcode.org/$_CURL_TEMPLATES_REPO/-/raw/$_CURL_TEMPLATES_REF/assets/phpcs.xml.dist
-    - vendor/bin/phpcs --version
-    - composer show | awk '$0 ~ /codesniffer|coder|coding-standard|variable-analysis/ {print $1 " " $2}';
-    # Show the installed config file, the paths to the standards and actual coding standards groups being used.
-    - vendor/bin/phpcs --config-show installed_paths
-    - vendor/bin/phpcs -i
-    - vendor/bin/phpcs -s $_WEB_ROOT/modules/custom --report-junit=junit.xml --report-full --report-summary --report-source $_PHPCS_EXTRA
+    # Does the project have its own phpcs config file?
+    - PHPCS_CONFIG_FILE=($(ls {.,}phpcs.xml{.dist,} 2>/dev/null | head -1 || true))
+    - |
+      if [[ ! "$PHPCS_CONFIG_FILE" ]]; then
+        echo 'This project has no (.)phpcs.xml(.dist), getting default from assets/phpcs.xml.dist'
+        curl -OL https://git.drupalcode.org/$_CURL_TEMPLATES_REPO/-/raw/$_CURL_TEMPLATES_REF/assets/phpcs.xml.dist
+      else
+        echo "Using the project's existing $PHPCS_CONFIG_FILE config file"
+        # If the file has hard-coded paths to the Drupal and/or DrupalPractice ruleset give a message recommending they are changed.
+        LONG_PATHS=$(grep -inE "vendor\/drupal\/coder\/coder_sniffer\/Drupal(Practice)?\/ruleset.xml" $PHPCS_CONFIG_FILE) || true
+        if [[ $LONG_PATHS ]]; then
+          printf "$DIVIDER\nThe project's $PHPCS_CONFIG_FILE file contains the following long path(s)\n$LONG_PATHS\n \nWe recommend that this is changed to the short syntax\n"
+          # Use only newline, not spaces, to split the grep array for sed.
+          IFS=$'\n'
+          for line in $LONG_PATHS; do
+            echo $line | sed -E 's/rule.*vendor\/drupal\/coder\/coder_sniffer\/(Drupal|DrupalPractice)\/ruleset.xml.*/rule ref="\1"\/>/'
+          done
+          unset IFS
+          printf "\n $DIVIDER\n"
+        fi
+      fi
+      # Add --colors and --report-width=120 if those options are not already in _PHPCS_EXTRA. Use ,, for lower-case matching.
+      [[ ${_PHPCS_EXTRA,,} =~ (--colors|--no-colors) ]] || _PHPCS_EXTRA+=" --colors"
+      [[ ${_PHPCS_EXTRA,,} =~ (--report-width) ]] || _PHPCS_EXTRA+=" --report-width=120"
+    # Show the versions of the applicable elements.
+    - COLUMNS=120 composer show | grep -E '(codesniffer|coder|coding-standard|variable-analysis)'
+    # Show the phpcs version, config file, the paths, the installed coding standards and actual number of sniffs being used in this job.
+    - $CI_PROJECT_DIR/vendor/bin/phpcs --version
+    - $CI_PROJECT_DIR/vendor/bin/phpcs --config-show installed_paths
+    - $CI_PROJECT_DIR/vendor/bin/phpcs -i
+    - $CI_PROJECT_DIR/vendor/bin/phpcs -e | grep -E 'contains.*sniff' || true
+    - echo -e "\e[0Ksection_start:`date +%s`:phpcs_sniffs[collapsed=true]\r\e[0KList of all PHPCS sniffs used in this job"
+    - $CI_PROJECT_DIR/vendor/bin/phpcs -e || true
+    - echo -e "\e[0Ksection_end:`date +%s`:phpcs_sniffs\r\e[0K"
+    - echo "executing $CI_PROJECT_DIR/vendor/bin/phpcs $DRUPAL_PROJECT_FOLDER -s --report-junit=junit.xml --report-full --report-summary --report-source --basepath=$DRUPAL_PROJECT_FOLDER $_PHPCS_EXTRA"
+    - $CI_PROJECT_DIR/vendor/bin/phpcs $DRUPAL_PROJECT_FOLDER -s --report-junit=junit.xml --report-full --report-summary --report-source --basepath=$DRUPAL_PROJECT_FOLDER $_PHPCS_EXTRA || EXIT_CODE=$?
+    - if [[ $EXIT_CODE == "" ]]; then echo "There are no PHPCS coding standards errors or warnings"; fi
+    - echo "Exiting with EXIT_CODE=$EXIT_CODE" && exit $EXIT_CODE
   allow_failure: true
   artifacts:
     expose_as: junit