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.
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.