practice

Architecture Fitness Function in CI

also called Architecture Test, ArchUnit Test

An executable check in the build that fails when an architectural constraint is violated, turning a design rule into a property the codebase cannot lose.

governanceautomationevolutionary

Architectural intent decays because it is enforced by memory and review. A rule that the domain layer must not depend on infrastructure holds while everyone remembers it, and is violated by a reasonable person under deadline, and then again because there is now precedent.

A fitness function makes the rule executable. Dependency direction between layers or modules. No cross-module access except through published interfaces. Naming and placement conventions that carry meaning. Package cycles. Forbidden imports of a deprecated library. Every public endpoint carrying authentication. p95 latency not regressing beyond a threshold. Bundle size within budget.

The categories worth distinguishing: static checks run on the codebase in CI, and runtime checks evaluate deployed behaviour continuously. Both are fitness functions; only the first blocks a merge.

The practices that make them stick. Introduce them for new violations only at first, with the existing backlog recorded as a baseline, or the first run blocks everyone and the check is deleted. Keep the failure message explanatory — a build failure saying "module X must not import Y; use the published interface Z" teaches, while one saying "architecture test failed" invites a workaround. And review them periodically, because a fitness function encoding a decision the team has since changed is an obstacle rather than a guardrail.

The broader point: this is what makes evolutionary architecture possible. Guided change requires a guide, and an automated one is the only kind that is always present.