Back to blog
June 15, 2023
4 min read

Architecture Fitness Functions: The Automated Guard Rails for Your Code

How to automatically enforce architectural decisions and prevent code decay using fitness functions

Architecture Fitness Functions: The Automated Guard Rails for Your Code

Ever wondered how to make sure your team doesn’t accidentally break architectural decisions? Or how to prevent that one developer from importing the database layer directly into the presentation layer?

Architecture fitness functions are your solution.

What Are Architecture Fitness Functions?

Think of fitness functions as automated tests for your architecture. Just like unit tests verify that your code behaves correctly, fitness functions verify that your code structure follows your architectural rules.

The term comes from evolutionary computing, where fitness functions determine how well a solution performs. In software architecture, they determine how well your codebase adheres to your architectural vision.

The Problem They Solve

Here’s what typically happens without fitness functions:

  1. You design a beautiful layered architecture
  2. Everyone agrees on the rules during the meeting
  3. Six months later, someone directly calls the database from a controller
  4. Technical debt accumulates
  5. Your architecture becomes a mess

Sound familiar? That’s architectural drift – the gradual erosion of your intended design.

Examples of Architecture Fitness Functions

Let’s look at some practical examples using ArchUnitTS:

1. Layer Dependency Rules

it('presentation layer should not depend on database layer', async () => {
  const rule = projectFiles()
    .inFolder('src/presentation/**')
    .shouldNot()
    .dependOnFiles()
    .inFolder('src/database/**');

  await expect(rule).toPassAsync();
});

This prevents your controllers from directly importing database repositories.

2. Circular Dependency Detection

it('should not have circular dependencies', async () => {
  const rule = projectFiles().inFolder('src/**').should().haveNoCycles();

  await expect(rule).toPassAsync();
});

Circular dependencies are architectural cancer. This catches them automatically.

3. Code Metrics

it('should not contain too large files', async () => {
  const rule = metrics().count().linesOfCode().shouldBeBelow(500);

  await expect(rule).toPassAsync();
});

Large files often indicate poor separation of concerns.

4. Naming Conventions

it('services should follow naming patterns', async () => {
  const rule = projectFiles()
    .inFolder('src/services/**')
    .should()
    .haveName('*Service.ts');

  await expect(rule).toPassAsync();
});

Consistent naming makes your codebase more predictable.

Why Fitness Functions Matter

1. Continuous Governance
Instead of architectural reviews every few months, you get feedback on every commit.

2. Objective Measurements
No more subjective arguments about code quality. The tests either pass or fail.

3. Documentation
Your fitness functions become living documentation of your architectural decisions.

4. Onboarding
New team members immediately understand the rules when they break them.

5. Confidence
You can refactor knowing that breaking architectural rules will be caught immediately.

Implementing Fitness Functions

You can implement fitness functions in various ways:

  • Custom scripts using AST parsers
  • Architecture testing libraries like ArchUnitTS for TypeScript
  • Static analysis tools with custom rules
  • Linting rules for simpler constraints

The key is making them part of your CI/CD pipeline. They should run on every pull request and block merges if they fail.

Real-World Success Stories

Netflix uses fitness functions extensively to manage their microservices architecture. They automatically verify service boundaries, dependency directions, and performance characteristics.

Amazon enforces team size limits (the famous “two-pizza rule”) through organizational fitness functions that monitor team structures.

Many financial institutions use them to enforce regulatory compliance in their codebases.

Getting Started

Start small:

  1. Pick one rule that your team cares about
  2. Implement a simple test for that rule
  3. Add it to your CI pipeline
  4. Gradually expand your fitness function suite

Don’t try to encode every architectural decision at once. Build your fitness functions incrementally as your architecture evolves.

The Future of Architecture

Architecture fitness functions represent a shift from hoping developers follow rules to ensuring they do. They’re part of the broader movement toward evolutionary architecture – systems that adapt and improve automatically.

As codebases grow larger and teams become more distributed, automated architectural governance becomes essential. Fitness functions aren’t just a nice-to-have; they’re becoming a necessity for maintaining architectural integrity at scale.

Your architecture is only as good as your ability to maintain it. Fitness functions ensure that your architectural vision survives contact with reality.