👋 Hey {{first_name|there}},
Your architecture rules are decaying with every pull request, and nobody notices until something breaks. Here's how to make them self-enforcing.
Why this matters / where it hurts
You've been in this review. Someone opens a pull request, and a senior engineer leaves a comment: "This service shouldn't call the database directly. It needs to go through the repository layer." The author fixes it. Two sprints later, a different developer does the same thing. Nobody catches it. Three months in, your "clean architecture" has seventeen shortcuts baked into production, and the next refactor takes twice as long because nobody knows which violations are intentional and which are accidents.
The real failure mode isn't bad developers. It's that architecture rules live in people's heads, or maybe in a wiki page nobody reads after onboarding week. Code review catches some violations, but reviewers get tired. They get busy. They approve things on Friday afternoon that they'd flag on Tuesday morning. And the rules that seemed obvious to the team that wrote them are invisible to the three engineers who joined last quarter.
In Lesson #34 on the Distributed Monolith, we talked about how service boundaries erode when coupling creeps in unnoticed. Fitness functions are how you stop that creep at the source, before it reaches a pull request comment, before it reaches production.
🧭 The shift
From: Architecture rules are documented conventions enforced by human reviewers during code review.
To: Architecture rules are executable tests that fail the build the moment someone violates them.
The idea comes from evolutionary architecture. A "fitness function" is just a test that evaluates whether your system still meets a specific architectural property. Not a unit test for business logic. A test for structural rules. Does the domain layer import anything from infrastructure? Does a controller class talk directly to the database? Does a new module introduce a circular dependency? These are objective, binary questions. A machine can answer them faster and more consistently than any reviewer.
The term sounds academic, but the implementation is practical. You write rules in code, they run in CI, and they either pass or fail. No opinions. No "well, I think we should..." conversations. The build is green, or it isn't.
If a rule matters enough to put in a design doc, it matters enough to put in a test.
Fitness functions should run on every commit, not quarterly in a review meeting.
Start with the rules your team already argues about in pull requests. Those are the ones that need automation first.
📘 New: The Career Guide got an upgrade
I just finished a major update to the From Developer to Architect career guide. It now includes a self-assessment rubric, a week-by-week 90-day growth plan, architecture artifact templates, and interview prep frameworks. If you're actively working toward a Staff, Tech Lead, or Architect role, this is the structured roadmap.
Free download here: https://www.techarchitectinsights.com/from-developer-to-architect-free-career-guide
🧰 Tool of the week: Architecture Rule Inventory
Architecture Rule Inventory: Turn implicit rules into executable fitness functions.
Rule name - Give it a short, searchable name. "No domain-to-infra dependency" beats "Clean architecture compliance." You'll grep for this in CI logs.
What it protects - One sentence on the architectural property. Example: "Domain logic remains portable across infrastructure changes." If you can't state it clearly, the rule might not be real.
Violation signal - Describe what a violation looks like in code. "A class in
com.app.domainimports fromcom.app.infrastructure." Be specific enough that you could explain it to a new hire in one sentence.Current enforcement - How is this enforced today? Code review? Wiki page? Honor system? Be honest. If the answer is "hoping someone notices," write that down.
Test implementation - The actual fitness function. For JVM projects, this is typically an ArchUnit test. For others, it might be a linting rule, a custom script, or a dependency-check plugin. Write the test, not a description of the test.
Severity - Should this break the build or produce a warning? New rules often start as warnings while the team fixes existing violations. Promote to build-breaking once the baseline is clean.
Baseline exceptions - If you have existing violations you can't fix immediately, list them explicitly. ArchUnit supports
@ArchIgnoreand freezes files. Never let "we have tech debt" be an excuse to skip the rule entirely.Owner - Who reviews changes to this rule? Fitness functions are architecture decisions. They shouldn't be modified in a drive-by commit without the team knowing.
🔍 In practice: The checkout service that quietly became a monolith
Scenario: A checkout service in a mid-size e-commerce platform. Four teams contribute code. The original architecture specified strict layering: controllers call application services, application services call the domain, domain never touches the infrastructure directly. After eighteen months, nobody was sure the layering still held.
Scope: Checkout service only. Not the entire platform. Start small.
Context: Four contributing teams, ~120k lines of Java, Spring Boot, Gradle build. No existing architectural tests.
We ran ArchUnit's dependency checks against the documented layer rules. First run: 47 violations. Not 3. Forty-seven. Mostly domain classes import JPA annotations and repository implementations.
Categorized each violation: 31 were genuine shortcuts that needed fixing, 12 were boundary disagreements where the documented architecture didn't match the team's evolved understanding, and 4 were false positives from test utilities.
Fixed the 31 real violations over two sprints. Updated the architecture docs for the 12 boundary adjustments. Added exclusions for the 4 test utilities.
Wrote seven ArchUnit rules covering layer dependencies, no-cycle checks, and naming conventions. Added them to the Gradle build as a mandatory step.
The tradeoff we accepted: We let two complex violations survive behind
@ArchIgnoreannotations with a "fix by Q3" comment. I'm not fully confident Q3 will happen, if I'm honest. But blocking the entire effort on two hard refactors would have stalled everything.Result: Zero new layer violations in the five months since. PR review comments about "wrong layer" dropped to near zero. Onboarding time for the architecture rules went from "ask someone senior" to "run the tests and read the failure messages."
✅ Do this / ❌ Avoid this
Do this:
Start with three to five rules that match what your team already enforces manually. You're codifying consensus, not inventing new constraints.
Run fitness functions in CI on every commit. A rule that only runs during quarterly reviews isn't a guardrail; it's a suggestion.
Treat fitness function changes like architecture decisions. Review them. Discuss the intent. Don't let someone silently weaken a rule in a large PR.
Avoid this:
Writing thirty rules in week one. You'll spend more time managing exceptions than catching real violations. Start small, prove value, expand.
Using fitness functions to enforce code style preferences. "Classes must be under 200 lines" is a linting opinion, not an architectural invariant. Keep the focus on structural rules.
Skipping the baseline step. If you activate a rule against a codebase with existing violations and no freeze file, you'll break the build for everyone, and the team will vote to delete your tests by lunch.
🎯 This week's move
Pick one architectural rule your team enforces through code review today. Write it down in one sentence. If you can't, clarify it before automating it.
Set up ArchUnit (JVM), Dependency Cruiser (Node/TS), or phpat (PHP) in your project. Run the simplest possible rule: "package A must not depend on package B."
Run it against your current codebase. Count the violations. Don't fix them yet. Just count.
Share the number with your team. That number is the gap between your documented architecture and your actual architecture.
By the end of this week, aim to: Have one automated fitness function running in CI that validates a real architectural rule, even if it's just a warning.
👋 Wrapping up
If a rule isn't tested, it's a suggestion. Suggestions decay.
The build pipeline is the only reviewer that never gets tired, never gets busy, and never approves things on Friday afternoon.
Architecture that enforces itself is architecture that survives contact with a growing team.
Help a friend think like an architect
Know someone making the jump from developer to architect? Forward this email or share your personal link. When they subscribe, you unlock rewards.
🔗 Your referral link: {{rp_refer_url}}
📊 You've referred {{rp_num_referrals}} so far.
Next unlock: {{rp_next_milestone_name}} referrals → {{rp_num_referrals_until_next_milestone}}
View your referral dashboard
P.S. I’m still working on two new rewards. If there’s something you are interested in, let me know 😉
⭐ Good place to start
I just organized all 40 lessons into four learning paths. If you've missed any or want to send a colleague a structured starting point, here's the page.
Thanks for reading.
See you next week,
Bogdan Colța
Tech Architect Insights