Back to Insights
· 5 min read · By Emanuele Pugliese

Architecture Guardrails: Automating Compliance in Your CI/CD Pipeline

architecture devops compliance

Every engineering team has standards. Use this library, not that one. Call this shared service, don’t roll your own. Follow this API pattern.

The problem is enforcement. Standards documented in a wiki get ignored. Standards mentioned in code reviews get inconsistently applied. And as teams grow, the person who knows the standard isn’t always reviewing the pull request.

Architecture guardrails solve this by turning standards into automated checks that run in CI/CD — the same way you run unit tests and linting.

What Is an Architecture Guardrail?

A guardrail is a rule about your codebase that is:

  1. Derived from an architecture decision (ideally an ADR)
  2. Expressed as an automated test (a fitness test)
  3. Enforced on every pull request (via CI/CD)

The term comes from architecture fitness functions — tests that verify your system’s architecture stays aligned with its design intent.

Examples That Work in Practice

1. Dependency Direction

“Frontend packages must not import backend code.”

import ast
import os

def test_no_backend_imports_in_frontend():
    """Guardrail: Frontend must not import from backend/."""
    frontend_files = find_python_files('apps/frontend/')
    for filepath in frontend_files:
        tree = ast.parse(open(filepath).read())
        for node in ast.walk(tree):
            if isinstance(node, ast.ImportFrom) and node.module:
                assert not node.module.startswith('backend.'), (
                    f"{filepath} imports {node.module} — "
                    f"frontend must not depend on backend"
                )

2. Shared Library Usage

“All error handling must use the shared observability library, not direct Sentry imports.”

def test_no_direct_sentry_imports():
    """Guardrail: Use shared-observability, not @sentry/react directly."""
    for filepath in find_source_files('apps/'):
        content = open(filepath).read()
        assert "from '@sentry/react'" not in content, (
            f"{filepath} imports @sentry/react directly. "
            f"Use captureError() from shared-observability."
        )

3. Authentication Pattern

“All API handlers must use resolve_principal() for identity extraction.”

def test_no_legacy_auth_patterns():
    """Guardrail: Use resolve_principal(), not claims.get('sub')."""
    for filepath in find_handler_files():
        content = open(filepath).read()
        assert "claims.get('sub')" not in content, (
            f"{filepath} uses legacy auth pattern. "
            f"Use resolve_principal() per ADR-007."
        )

4. Infrastructure Compliance

“All DynamoDB tables must have point-in-time recovery enabled.”

import yaml

def test_dynamodb_pitr_enabled():
    """Guardrail: All DynamoDB tables must have PITR enabled."""
    for template_path in find_cloudformation_templates():
        template = yaml.safe_load(open(template_path))
        for name, resource in template.get('Resources', {}).items():
            if resource['Type'] == 'AWS::DynamoDB::Table':
                pitr = resource.get('Properties', {}).get(
                    'PointInTimeRecoverySpecification', {}
                )
                assert pitr.get('PointInTimeRecoveryEnabled') is True, (
                    f"Table {name} in {template_path} "
                    f"must have PointInTimeRecovery enabled"
                )

Setting Up the Pipeline

Guardrail tests are just Python tests (or whatever language your team prefers). Run them in CI alongside your other tests:

# .github/workflows/ci.yml
jobs:
  architecture-tests:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: pip install pyyaml
      - run: python -m pytest tests/architecture/ -v

The key is to run them on every pull request, not on a schedule. When a developer opens a PR that violates a guardrail, they get immediate feedback with a clear error message that references the relevant ADR.

From Reactive to Proactive

Most teams discover architecture violations during code review — if they discover them at all. By the time a reviewer spots the issue, the developer has already invested time in the wrong approach.

Guardrails flip this. The developer gets feedback in minutes, before any reviewer sees the code. The error message explains what standard was violated and why (linking to the ADR). The fix is usually straightforward.

This shifts code review from “catching violations” to “discussing design” — which is where reviewer time is actually valuable.

Scaling Across Teams

Guardrails become essential as you grow beyond a single team. With 5-10 teams working on the same codebase (or across multiple repositories), you can’t rely on any individual knowing all the standards.

A compliance matrix shows which repositories pass which guardrails:

RepoAuth (ADR-007)Observability (ADR-012)PITR (ADR-015)
payment-servicePassPassPass
user-servicePassFailPass
notification-servicePassPassN/A

This gives architects and engineering leaders a real-time view of architecture health across the organisation — without manually auditing each repository.

Common Mistakes

Starting too strict. Don’t write 50 guardrails on day one. Start with 3-5 that address your most painful recurring violations. Add more as the team matures.

No ADR reference. Every guardrail test should reference the ADR it enforces in its docstring. Without this, developers see a failing test but don’t understand why the rule exists.

No escape hatch. Sometimes a guardrail needs to be deliberately bypassed (e.g., a legacy service during migration). Use an explicit allowlist in the test, with a comment explaining the exception and a target date for fixing it.

Getting Started

  1. Identify your top 3 recurring architecture violations from recent code reviews
  2. Write an ADR for each (or find the existing one)
  3. Write a fitness test that checks for the violation pattern
  4. Add to CI/CD so it runs on every PR
  5. Review and expand quarterly

The goal isn’t to test everything. It’s to automate the checks that humans keep forgetting — so your architecture quality ratchets up over time instead of slowly degrading.


SystemDox automates the full guardrail workflow: capture the decision, generate the fitness test, enforce in CI/CD, and track compliance across repositories. See our Architecture Audits & Guardrails service to get started.