DEV Community

Prabhat Kumar
Prabhat Kumar

Posted on

🔴 Your architecture rules are dying in Confluence. Here's how to make them bite back.

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
Enter fullscreen mode Exit fullscreen mode

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");
...
} 
Enter fullscreen mode Exit fullscreen mode

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)