SYSTEM Cited by 1 source
ArchUnit¶
ArchUnit (archunit.org) is a popular OSS Java library for enforcing architectural code rules as part of a JUnit suite. It analyzes compiled JVM bytecode via ASM and exposes a fluent type-safe builder API for rule authoring. As of the 2026-05-08 Netflix TechBlog post that triggered this wiki page, ArchUnit has "3.5k stars, 84 contributors" and is "used internally by Gradle, Spring, and is provided as part of the Spring Modulith platform."
This page is the canonical wiki home for ArchUnit, primarily documenting its role as the primitive on top of which Netflix's Nebula ArchRules is built.
Three distinctive features (per Netflix post)¶
"The rules engine, which is built directly on top of ASM, can be used for a wide variety of use cases. It is powerful enough to be a general purpose static analysis tool with the following distinctive features:
1. Works cross-language (JVM), because it uses ASM/bytecode, not AST parsing.
2. Exposes a builder API pattern that makes it easy to write rules
3. Also has a lower level API ideal for writing more complex custom rules." — sources/2026-05-08-netflix-scaling-archunit-with-nebula-archrules
These three properties — language-agnostic via bytecode, fluent builder API, classpath-graph navigation — are the load-bearing design choices distinguishing ArchUnit from AST-based JVM static analyzers like PMD or Spotbugs.
Builder API rule example¶
The Netflix post's verbatim rule example for "DateTimes are not instantiated without an explicit zone":
ArchRuleDefinition.priority(Priority.MEDIUM)
.noClasses()
.should()
.callConstructorWhere(
// constructor does not have a zone arguement
target(doesNot(have(rawParameterTypes(DateTimeZone.class))))
// constructor is for DateTime
.and(targetOwner(assignableTo(DateTime.class)))
)
Properties of this style:
- Type-safe — Java compiler validates the rule shape.
- IDE-supported — autocomplete on
target(...),targetOwner(...),doesNot(...), etc. - Unit-testable in-process — "ArchUnit has a method to pass a rule object and class references to evaluate the rule against those classes" — no separate analyzer process needs to spin up.
- Composable — predicates compose with
.and(...)/.or(...)/.negate().
Bytecode (ASM) over AST¶
ArchUnit's choice of ASM for class-graph construction (vs source-AST tools like PMD) is the architectural move that produces three downstream properties (per the Netflix post):
- Language-agnostic across JVM languages — "Rules that need to support multiple JVM languages, such as Kotlin or Scala, often need to be rewritten for each language. … ArchUnit uses ASM to analyze actual compiled bytecode, which means it doesn't matter how that code was produced. What is analyzed is the actual code that will be run."
- Syntactic-sugar-immune — "It also allows code which should be found to be hidden under syntactic sugar not anticipated by the rule author." Kotlin extension functions, Scala implicits, Java records, lombok-generated code all desugar to bytecode the rule sees.
- Code-generator-aware — annotation processors, Kotlin Symbol Processing (KSP), kapt, Lombok, etc. all generate bytecode that ArchUnit analyzes — so generated code is checked, not just hand-authored source.
See concepts/bytecode-vs-ast-static-analysis for the canonical wiki framing.
Class-graph retention¶
"Because ArchUnit processes the entire classpath with ASM, it retains a graph of the class data, allowing rules to easily traverse class relationships and call sites. This allows rules to have much more context about the code it is evaluating." — sources/2026-05-08-netflix-scaling-archunit-with-nebula-archrules
This enables cross-class rules that per-file AST tools can't express:
- "No class outside this package may depend on a
@Deprecatedclass inside this package." - "All implementations of
Servicemust reside incom.example.impl." - "No layer-N class may call into a layer-(N+1) class."
- "Every
@Entitymust be referenced from exactly oneRepository."
These rules require call-site enumeration + class-hierarchy walking — operations that are natural on a built classpath graph and awkward at best on a per-file AST.
Standard usage shape: JUnit suite¶
ArchUnit is "designed to be used as part of a JUnit suite in a
single repository" (per the Netflix post — this is named as the
limitation that motivates Nebula ArchRules). A typical
project includes ArchUnit as a test dependency, writes rule
definitions in src/test/java, and JUnit asserts the rules
during normal test execution.
This works for a single repo but doesn't address the polyrepo problem of sharing rules across thousands of repos — which is where Nebula ArchRules picks up.
Lower-level API¶
The Netflix post mentions "Also has a lower level API ideal for
writing more complex custom rules" without expanding. ArchUnit
exposes its JavaClasses model directly so rule authors can
walk classes, methods, fields, dependencies, and annotations
imperatively when the fluent DSL doesn't fit.
Seen in¶
- sources/2026-05-08-netflix-scaling-archunit-with-nebula-archrules — canonical wiki citation; framed as the OSS primitive on top of which Netflix builds systems/nebula-archrules.
Related¶
- systems/nebula-archrules — Netflix's organisation-wide rule-distribution system on top of ArchUnit.
- systems/asm-bytecode-toolkit — the bytecode framework ArchUnit is "built directly on top of".
- systems/spring-modulith — Spring's modular-monolith-enforcement platform that uses ArchUnit internally.
- systems/pmd-static-analyzer — the comparison foil (XPath-AST vs ArchUnit-bytecode-builder).
- concepts/architectural-fitness-function — the foundational framing of architecture as test.
- concepts/bytecode-vs-ast-static-analysis — the structural choice ArchUnit codifies.
What is not documented¶
- The post does not deeply document ArchUnit's lower-level API beyond mentioning it exists.
- ArchUnit's caching / incremental-build behavior, performance characteristics, and memory footprint at large class-graph scale are not discussed.
- The OSS contributor / governance model is not discussed beyond the "3.5k stars, 84 contributors" number as of early 2026.