Eighteen months after the boundary clean-up described in Part 2, the fictional Magaliesberg Provincial Supplier Desk had two teams, five modules in the main application (registration, documents, cases, reporting and supplier development), an extracted notifications service, an extracted verification service and a growing feature list. Thabo Molefe ran the dependency analysis again, expecting good news. The architecture tests had held: no module referenced another module's internals. But the Cases module now called the Documents contract 23 times per case screen, the shared kernel had grown from four types to nineteen, and a new reporting query read an internal table through a database view someone had granted "temporarily" nine months earlier.
None of this broke a test, because no test was looking for it. The structural rules were enforced. The rest of the architecture was not.
The Magaliesberg Provincial Supplier Desk is a fictional provincial supplier-support platform. Its people, systems and figures are illustrative. This part turns the enforcement table from Part 2 into a complete set of automated checks, and explains how to keep exceptions from becoming the architecture.
What a fitness function is
The term comes from evolutionary architecture, an idea credited to Neal Ford, Rebecca Parsons and Pat Kua. A Thoughtworks article from January 2019, Fitness function-driven development, defines it briefly: "Fitness functions describe how close an architecture is to achieving an architectural aim." In practice, a fitness function is an automated check, often written as a test, that fails when the system moves away from a property the architects care about.
The point is not the tooling. It is that an architectural intention, such as "Cases must not depend on Verification's internals", becomes something the delivery pipeline checks on every change, rather than something a reviewer must remember. Our article on keeping systems fit in the age of cheap code makes the wider argument for continuous upkeep. This part is the specific set of checks for a modular monolith.
Five families of checks
The Supplier Desk organised its fitness functions into five families. Each protects a different part of the boundary.
1. Structure. Rules about code dependencies. No module references another module's internal namespace. No cycles between modules. Each module depends only on the contracts declared in its manifest. The shared kernel contains no business logic, checked by forbidding references from the kernel to any module and limiting what types it may contain. Libraries such as ArchUnit for Java and ArchUnitNET for C# express these as unit tests, and Spring Modulith offers module verification for Spring applications (all checked on 5 November 2026). These are dated examples, not rankings.
2. Data. Rules about who touches which tables. Each module's database role has access only to its own schema, verified by a test that inspects the database's actual grants, not the intended ones. A nightly job scans query logs for any statement that touches more than one module's schema. Every grant that crosses a schema must appear in the exemption register described below. This family would have caught the "temporary" view.
3. Contract. Rules about compatibility. The public surface of each module's contract is compared with the last released version, and any removal or incompatible change fails the build unless it is marked as a new version. Event schemas are checked the same way. For the extracted services, consumer contract tests run against the provider before each release.
4. Runtime. Rules about behaviour in a running system. A budget for calls between modules per user request: the Cases screen's 23 calls to Documents exceeded a budget of five, which pointed to a contract that was too fine-grained. A maximum lag for the outbox relay from Part 3. A latency budget for each extracted service call. These run nightly against the test environment and continuously against production telemetry.
5. Trend. Rules about direction, not absolute limits. The number of cross-module calls, the size of the shared kernel, the number of open exemptions and the number of types in each contract are recorded on every build. A trend check fails when a measure grows by more than an agreed amount in a month without a recorded decision. A shared kernel that grows from four types to nineteen without anyone deciding it should is exactly what this family is for.
Run each check at the right point
Not every check belongs in the pull-request build. Slow checks there make developers wait, and they start to look for ways around them. The Supplier Desk runs each family where it gives the fastest useful signal.
| Where | What runs | Time budget |
|---|---|---|
| Pull request | Structure rules, contract surface diff, shared kernel rules, trend recording | Under two minutes |
| Nightly | Database grant inspection, query-log scan, consumer contract tests, runtime budgets in the test environment | Under an hour |
| Production | Runtime budgets against live telemetry, outbox lag | Continuous, with alerts |
A failure in the pull request blocks the merge. A nightly failure creates a ticket for the owning module, visible to both teams. A production budget breach alerts the owning team, with the same severity as a performance alert.
Exceptions need owners and expiry dates
Every architecture has legitimate exceptions. The Registration and Verification cross-module transaction in Part 4 was one, until the extraction in Part 7 removed it. The problem is not exceptions. It is exceptions without an owner or an end date, like the view granted "temporarily" nine months earlier.
The Supplier Desk keeps an exemption register in the repository, alongside its decision records, which is the habit The architecture repository is a management system argues for. Each entry records:
- the rule being broken and the exact place it is broken;
- the reason, with a link to the decision record;
- the owner who will remove it;
- an expiry date, after which the fitness function fails again.
The fitness functions read the register. An exemption that matches an entry passes until its expiry date, then fails. That one mechanism changed the conversation. Extending an exemption is now a visible decision someone has to make and defend, not a default nobody notices.
Write rules people can read
Fitness functions are only useful if the team understands why each one exists. The Supplier Desk writes every rule in three parts: the plain-language rule, the reason, and what to do when it fails.
Rule: The Cases module may call the Documents contract no more than five times while serving one user request. Why: More calls suggest the contract is too fine-grained and will become slow and fragile if Documents is ever extracted. When it fails: Add a coarser contract operation in Documents, or record an exemption with an owner and expiry date.
The rules become part of the living architecture documentation: they describe the system and fail when the description stops being true. That format matters more than the test framework. A failing check with no explanation teaches people to disable checks. A failing check that says what to do teaches people the architecture.
Do not turn fitness functions into a second bureaucracy
There are failure modes on the other side too.
- Too many rules. Start with the handful that protect the boundaries you care about most. The Supplier Desk started with eight and now has twenty-two, each added after a real erosion was found.
- Rules nobody owns. Each rule has an owner, usually the architect or the module team it protects.
- Thresholds set by guesswork. Runtime and trend thresholds should come from measured baselines, adjusted as the system changes.
- Checks that duplicate review. If a reviewer must still check the same thing by hand, either the rule is wrong or it is not trusted.
The same thinking runs through our framework of guides, guards, checks and sensors for coding agents. Fitness functions are the checks and sensors for the architecture itself, and they matter even more when some of the code is written by agents, which is the subject of Part 9.
What changed
After the five families were in place, the Supplier Desk found and fixed the three erosions in a month: a coarser GetCaseDocuments contract operation cut the Cases screen from 23 calls to two, eleven types moved out of the shared kernel into the modules that owned them, and the "temporary" view was replaced by a published reporting view. The exemption register now holds four entries, each with an owner and a date. Two of them are expected to close when the supplier development module gets its own reporting views next quarter; the other two are recorded trade-offs the architects have chosen to keep and will review on their expiry dates rather than forget. The figures are illustrative; the habit is the point.
Key points
- Structural tests alone do not protect a modular monolith; data, contract, runtime and trend checks are needed too.
- Run each check where it gives the fastest useful signal: pull request, nightly or production.
- Keep exceptions in a register with an owner, a reason and an expiry date that the checks enforce.
- Write each rule in plain language with its reason and the action to take when it fails.
- Start small, add rules when real erosion is found, and set thresholds from measured baselines.
Previous: Part 7, Extract the module with its data. Next: Part 9, How AI coding agents change the architecture choice. All parts are on the series page.
How CloudNala can help
CloudNala designs architecture fitness functions for modular monoliths and the services extracted from them. We turn your intended boundaries into structural, data, contract, runtime and trend checks, place each where it gives the fastest signal, and set up an exemption register with owners and expiry dates, so that your architecture is checked on every change rather than rediscovered every year.
Work with CloudNala
CloudNala helps organisations move from technology ambition to practical execution across cloud, AI, data, platform engineering and digital services.
Whether you are exploring AI, modernising your cloud environment, building a public-sector digital service, or turning an idea into a working MVP, we can help you shape the roadmap and deliver the next step.
Book an Architecture Complexity Review or write to us at consult@cloudnala.co.za
