Every team has that architecture doc. You know the one — 47 pages, last updated 8 months ago, ignored by everyone shipping features.
The problem isn't discipline. It's that rules written in prose don't fail builds.
Here's a workflow I've been using to convert architecture documentation into executable, failing JUnit tests — in under 10 minutes.
The 3-Step Process
Step 1 — Copy your arch rules from Confluence/Wiki
Just grab the raw text. Doesn't need to be formatted perfectly.
Example rules I copied:
Classes extending a parent must use the parent name as suffix
e.g., CacheDataProvider extends DataProvider ✅No string literals inside methods — use static final class members
e.g., static final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss" ✅Controllers must live under .controller. package only
Domain objects must NOT be instantiated with "new" inside methods
e.g., new CacheHelper() inside a method ❌
Step 2 — Feed them to Claude Code with this prompt
Open Claude Code and paste this:
You are an expert Java architect specializing in ArchUnit — a Java library for checking architecture rules as unit tests.
You will receive architecture rules in plain english format and must convert it into a complete, compilable ArchUnit JUnit5 test class.
Rules:
<architecture_rules>
A. Class must have parent class name as suffix while extending.
Ex: class CacheDataProvider when extends DataProvider
B. No string literals inside a method, always have static final class member.
Ex: String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";
C. Package Naming Conventions like edu.pk.demo.archunit.controller.*
D. Method should not create domain object locally using "new" keyword.
Ex: final CacheHelper cacheHelper = new CacheHelper();
</architecture_rules>
Requirements:
- Use @AnalyzeClasses(packages = "edu.pk.demo.archunit") # Use your project package name
- One @ArchTest per rule, named descriptively
- Use ArchUnit's fluent API (classes(), noClasses(), methods())
- Add a brief comment above each test explaining the rule intent
- Base package: edu.pk.demo.archunit
ArchUnit API Reference
- Class-level setup:
@AnalyzeClasses(packages = "com.example", importOptions = ImportOption.DoNotIncludeTests.class)
public class MyArchTest { ... }
- Rule declaration (always static final):
@ArchTest
static final ArchRule ruleName = ...;
- Fluent API for common patterns:
# CONTAINMENT (CONTAINED WITHIN / CONTAINS):
classes()
.that().resideInAPackage("com.example..")
.should().resideInAnyPackage("com.example.domain..", "com.example.service..")
.as("...").because("...");
# NO DEPENDENCY ON:
noClasses()
.that().resideInAPackage("com.example.web..")
.should().dependOnClassesThat().resideInAPackage("com.example.db..")
.as("...").because("...");
# CYCLE FREE:
slices()
.matching("com.example.(*)..")
.should().beFreeOfCycles();
# MUST USE LIBRARY (CONTAINS library):
classes()
.that().resideInAPackage("com.example.api..")
.should().dependOnClassesThat().resideInAPackage("com.security..")
.as("...").because("...");
Output Rules:
- Output ONLY valid Java source code
- No markdown code fences
- No explanation text before or after the class
- Full import statements required
- Class must be immediately runnable with ArchUnit + JUnit5 on the classpath
Step 3 — Drop the generated tests into your project and run
Claude generates something like this:
@AnalyzeClasses(packages = "edu.pk.demo.archunit")
public class ArchitectureRulesTest {
// Rule: Subclass name must end with the parent class simple name
@ArchTest
static final ArchRule subclassesShouldUseSuperclassNameAsSuffix =
classes()
.that().areAssignableTo(DataProvider.class)
.should().haveSimpleNameEndingWith("DataProvider")
.because("Subclasses must use the parent class name as suffix for discoverability");
...
}
Why this matters
Architecture drift happens because rules live in documents, not in pipelines.
When you encode rules as tests:
✅ Every PR is verified automatically
✅ New developers can't accidentally break conventions
✅ Architecture decisions are self-documenting in code
✅ No more "we agreed not to do this" code review comments
The best part? Claude Code handles the ArchUnit API fluency. You just describe what you want to enforce — it figures out how to write it.
Your architecture doc shouldn't be a suggestion. Make it a compiler error.
Have you automated architecture enforcement in your team? What rules do you wish you'd enforced from day one? Drop them below 👇.
Top comments (0)