Skip to main content
Development

Your ADRs mean nothing if the code ignores them fitness functions and CI/CD

20 ADRs in /adr, and the code has drifted. ADR-0005 banned coupling between Billing and Catalog there are now 7 cross-imports. Here is how to make the machine enforce your decisions.

ADR Fitness functions Deptrac CI/CD GitHub Actions PHP Architecture

This article concludes the ADR series. If you are new to the concept, start with article 1. Article 2 covers installation and templates.

The silent drift

Your team has 20 ADRs in /adr. Nobody re-reads them. ADR-0005 was clear: no direct coupling between Facturation and Catalogue. There are now 7 cross-imports. Nobody caught it.

Fitness functions address exactly that.

What fitness functions are

A fitness function is an automated test that verifies an architectural decision is still respected in the code. Not a unit test — a unit test verifies that code does what it should. A fitness function verifies that code is what it should be.

PHP/Symfony implementation

PHPUnit + reflection — zero extra dependency

<?php
// tests/Architecture/FacturationArchitectureTest.php
// @enforces ADR-0005

namespace App\Tests\Architecture;

use PHPUnit\Framework\TestCase;

class FacturationArchitectureTest extends TestCase
{
    public function testFacturationDoesNotImportCatalogue(): void
    {
        $facturationPath = __DIR__ . '/../../src/Facturation';
        $violations = [];

        $iterator = new \RecursiveIteratorIterator(
            new \RecursiveDirectoryIterator($facturationPath)
        );

        foreach ($iterator as $file) {
            if ($file->getExtension() !== 'php') {
                continue;
            }
            $content = file_get_contents($file->getPathname());
            if (str_contains($content, 'App\\Catalogue')) {
                $violations[] = $file->getPathname();
            }
        }

        $this->assertEmpty($violations, sprintf(
            "ADR-0005 violation: %d file(s) in App\\Facturation import App\\Catalogue.\n%s",
            count($violations),
            implode("\n", $violations)
        ));
    }
}

Deptrac — the dedicated PHP dependency tool

composer require --dev qossmic/deptrac-shim
./vendor/bin/deptrac analyse --config-file=deptrac.yaml
parameters:
  paths: ['src']
  layers:
    - name: Facturation
      collectors:
        - type: className
          regex: ^App\\Facturation\\
    - name: Catalogue
      collectors:
        - type: className
          regex: ^App\\Catalogue\\
    - name: Shared
      collectors:
        - type: className
          regex: ^App\\Shared\\
  ruleset:
    Facturation:
      - Shared
    Catalogue:
      - Shared

CI/CD integration with GitHub Actions

# .github/workflows/adr-fitness.yml
name: ADR Fitness Functions

on:
  pull_request:
    branches: [main, develop]

jobs:
  architecture-check:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: shivammathur/setup-php@v2
        with:
          php-version: '8.3'
      - run: composer install --no-dev --optimize-autoloader
      - run: |
          composer require --dev qossmic/deptrac-shim
          ./vendor/bin/deptrac analyse --config-file=deptrac.yaml
        continue-on-error: false
      - run: ./vendor/bin/phpunit tests/Architecture/

What this series covers — and what it does not

  • Article 1: understand ADRs and write the first one in 15 minutes
  • Article 2: install, choose a template, integrate into Git
  • Article 3: have the machine enforce the decisions

ADRs are a long-term team investment. Solo, they are an investment in your future self.

The complete GitHub Actions workflow and Deptrac configuration are available in the series repository on GitHub CodexLab.

In the same vein