Turn Architecture Principles into Executable Architecture Fitness Functions

Convert architecture principles into measurable, versioned fitness functions across code dependencies, policy, runtime reliability, cost, and operability.

Edilec Research Updated 2026-07-13 Software Engineering

Architecture fitness functions turn important architecture properties into repeatable observations with explicit pass, warn, or fail decisions. They do not automate architecture itself. They automate the evidence that a chosen property still holds: domain code does not import infrastructure, public endpoints meet an authentication policy, a service stays inside its latency objective, or a workload’s idle cost remains within an approved envelope.

A useful function connects a principle to a measurable condition, a trusted data source, an execution point, an owner, and an exception path. Without those elements, a slogan such as services must be loosely coupled invites inconsistent interpretation. With them, a team can see a dependency cycle in a pull request, understand why it matters, and either correct it or obtain a time-bounded exception.

Select architecture properties worth continuously testing

Start from decisions with meaningful failure cost and a machine-observable signal. Dependency direction, forbidden libraries, API compatibility, encryption settings, ownership metadata, error budgets, recovery tests, and unit-cost thresholds are candidates. Principles that depend on context or design quality may remain review prompts. Good user experience and use appropriate technology are too ambiguous to become binary gates without first defining a narrower outcome.

Write each function as a small contract: because a quality attribute matters in a stated scope, measure a named property from a named source at a stated cadence; compare it with a threshold; route failure to an owner; and define evidence for closure. This format exposes missing telemetry and prevents a tool from becoming the de facto author of policy.

Classify functions by signal and response time

Function typeExampleExecution pointTypical response
Static codeNo adapter package is imported by domain packagesLocal test and pull requestBlock new violation
Configuration policyPublic storage must deny anonymous accessInfrastructure plan and admissionBlock or require approved exception
ContractProvider remains compatible with supported consumersProvider build and deployment gateStop incompatible release
RuntimeCheckout latency and error rate stay inside objectiveContinuous telemetry windowAlert, limit exposure, or roll back
ResilienceRestore completes within recovery objective with valid dataScheduled exerciseCreate corrective action and retest
EconomicCost per successful transaction remains within envelopeDaily or weekly analytical jobInvestigate drift before budget breach

Fast deterministic checks belong close to the developer. Slow, noisy, or statistical checks belong later and should rarely block a commit. A single green pipeline cannot prove runtime resilience; a single transient latency breach should not reject source code. Place every function where its evidence is mature enough to support the consequence.

Design the measurable contract and data quality guard

Specify scope precisely. Instead of no cycles, state no cycles among production packages matching the module namespace, excluding generated clients, with an empty match treated as failure. ArchUnit’s user guide supports package and class dependency checks, layered and onion architecture rules, slice cycle detection, and configurable failure when no classes match. The empty-match guard matters because a renamed package can otherwise make a rule pass without evaluating anything.

Six-stage Edilec architecture fitness function diagram from property selection through measurable contract, implementation, validation, enforcement, and portfolio review.
A fitness function supports architecture only when its signal is trustworthy, its consequence matches evidence quality, and its owner can remediate or revise it.

For policy as code, version the policy and its test inputs together. Open Policy Agent separates policy decision-making from enforcement and evaluates structured input. Its policy testing guide shows that tests are discovered by naming convention and documents --fail-on-empty, a valuable CI safeguard. Test allowed, denied, malformed, missing, and boundary inputs; do not validate only the happy path.

Implement a thin vertical slice

Choose one static property and one runtime property for a single service. For example, prevent controllers from calling database adapters directly and monitor the percentage of payment requests that finish within the service objective. Add rule identifiers, rationale, owner, introduced date, severity, and remediation link. Emit a machine-readable result and a concise human explanation containing the violating dependency or time series, not merely architecture check failed.

undefined

Run the static rule locally and in CI. Evaluate the runtime rule over a window that reflects the objective, and make deployment automation consume a stable status rather than a raw metric point. AWS Prescriptive Guidance describes automated static analysis as early detection integrated into delivery; that benefit depends on actionable output and a rule set small enough for teams to trust.

Govern severity, rollout, and exceptions

ResultMeaningPipeline behaviorRequired record
PassProperty observed within policyContinueEvidence retained with build or window
WarnRisk exists but immediate block is disproportionateContinue with visible owner notificationIssue, due date, and trend
Fail-newChange introduces or worsens a violationBlock merge or deploymentViolation details or approved exception
Fail-systemCurrent production state exceeds a critical boundaryLimit rollout or initiate responseIncident or corrective action
UnknownInput missing, stale, empty, or tool failedFail closed for critical controls; otherwise escalateData-quality event and accountable decision

Roll out new functions in observe-only mode, compare findings with expert review, fix false positives, then enable enforcement for new violations. A baseline can prevent existing debt from blocking all work, but every tolerated violation needs an owner and reduction plan. Exceptions should cite the rule, scope, reason, compensating control, approver, expiry, and test for removal. Permanent ignore lists silently rewrite the architecture.

Review the portfolio quarterly and after incidents or major topology changes. Retire functions whose decisions no longer apply, split functions that combine unrelated properties, and challenge thresholds that never fire. OPA’s CI/CD guidance illustrates policy checks over repository and configuration data; the operating lesson is broader: enforcement must remain connected to current inputs and current authority.

Measure governance effectiveness, not rule volume

Useful measures include violations prevented before merge, median time to remediate, exception age, false-positive rate, unowned rules, unknown results, and incidents linked to an untested property. Count how often a function changes a decision. Hundreds of checks that teams routinely bypass are weaker governance than a focused portfolio covering the system’s most expensive failure modes.

Test the fitness-function infrastructure itself. Seed known violations into representative fixtures, verify that each execution environment loads the expected scope, and alert when result volume unexpectedly drops to zero. Pin parser and policy-engine versions, preserve rule output with the build, and make upgrades run old and new evaluators side by side. A silent change in bytecode analysis, infrastructure-plan JSON, or metric labels can invalidate governance while every displayed result remains green.

Separate control ownership from tool administration. A platform team may run OPA, CI plugins, and telemetry storage; a security or architecture owner defines the policy; service teams supply system context and remediate. Changes to critical thresholds should require the same evidence and review as the architecture decision they enforce. Record policy version in every result so an auditor can reconstruct what was evaluated at release time.

Fitness functions can conflict. A rule minimizing cross-zone calls may oppose a resilience rule requiring zone redundancy; a strict cost ceiling may oppose a latency objective. Do not hide this tension in precedence code. Name the competing quality attributes, define which applies under each workload class, and create a higher-level decision for exceptions. Architecture remains a tradeoff system even when its observations are executable.

Finally, design remediation before enforcement. A dependency violation needs the forbidden edge and an approved alternative; a runtime objective breach needs the affected operation, window, release, and rollback path; a cost breach needs allocation and demand context. A function that only says no pushes architecture work into private chats and encourages bypasses. Good feedback shortens the route from detected drift to a verified correction.

Key takeaways

  • Translate a principle into scope, signal, threshold, cadence, owner, consequence, and exception path.
  • Use static, policy, contract, runtime, resilience, and economic functions at the point where their evidence is reliable.
  • Treat missing or empty input as an explicit result, especially for critical controls.
  • Introduce enforcement gradually and prevent new debt before attempting to eliminate every legacy violation.
  • Evaluate the portfolio by prevented risk, remediation, exceptions, and false positives rather than number of rules.

Frequently asked questions

Are fitness functions just architecture unit tests?

Architecture tests are one type. Fitness functions also observe deployment configuration, API compatibility, live service objectives, recovery exercises, security posture, and cost. The common feature is an explicit architecture property evaluated repeatedly.

Should every failed fitness function block delivery?

No. Blocking is appropriate for deterministic, high-confidence checks where the change causes unacceptable risk. Statistical runtime signals, advisory cost drift, and newly introduced rules often need warning, controlled rollout, or human review. Consequence should match evidence quality and impact.

Who owns an architecture fitness function?

A team that can interpret the property and change either the system or policy must own it. A central platform may provide tooling and templates, but an unowned threshold will become stale. Cross-cutting rules need a named governance owner and service teams responsible for remediation.

Conclusion

Executable architecture governance works when a small number of consequential properties become dependable feedback. Define the decision first, choose a trustworthy signal, test the test, make failures actionable, and preserve a governed route for context. Fitness functions then support decentralized delivery without pretending that architecture can be reduced to a dashboard of green checks.

Continue with related articles