Stop forbidden dependencies before review.
Define who may depend on whom and fail on the exact source edge that crosses the line.
Guard boundaries, detect cycles, measure design health, and keep architecture honest as code changes, inside the test suite your team already runs.
Open source · Test-runner native · CI ready
Presentation
24 filesBusiness rules
37 filesAdapters
19 filesThe library family
Start with the mature TypeScript and Python libraries, evaluate the active previews, or help shape the next ecosystem.
Architecture tests for TypeScript and JavaScript, from dependency rules to metrics and diagrams.
Zero-runtime-dependency architecture tests for Python projects and any test framework.
Architecture rules and reports for C# solutions, with adapters for major .NET test frameworks.
Executable architecture rules for Ruby and Rails, with RSpec and Minitest-friendly assertions.
Cargo-native architecture tests with immutable builders and structured, source-backed violations.
Allocator-aware architecture checks for Zig projects, compiled as ordinary zig tests.
Architecture policies as plain Go test values, backed by the Go toolchain and x/tools.
A Java-native member of the family, planned around files, packages, types, modules, and JUnit 5.
A planned architecture-testing library for Composer projects, PHPUnit, and Pest.
One mental model
Different language conventions, one recognizable flow: select code, state the expectation, then let the existing test runner enforce it.
import { projectFiles } from 'archunit';
it('keeps the domain independent', async () => {
const rule = projectFiles()
.inFolder('src/domain/**')
.shouldNot()
.dependOnFiles()
.inFolder('src/infrastructure/**');
await expect(rule).toPassAsync();
});from archunitpython import project_files, assert_passes
def test_domain_stays_independent():
rule = (
project_files('src/')
.in_folder('**/domain/**')
.should_not()
.depend_on_files()
.in_folder('**/infrastructure/**')
)
assert_passes(rule)using ArchUnitNet;
using ArchUnitNet.Testing.XUnit;
[Fact]
public async Task DomainStaysIndependent()
{
var rule = ArchUnit.ProjectFiles("./App.csproj")
.InPath("src/Domain/**")
.ShouldNot()
.DependOnFiles()
.InPath("src/Infrastructure/**");
await rule.PassesAsync();
}require 'archunit'
RSpec.describe 'architecture' do
it 'keeps API away from persistence' do
rule = ArchUnit.project_files
.in_folder('app/api/**')
.should_not.depend_on_files
.in_folder('app/database/**')
expect(rule).to pass
end
enduse archunit::{assert_passes, project_files};
#[test]
fn api_does_not_reach_database() {
let rule = project_files()
.in_path("src/api/**")
.should_not()
.depend_on_files()
.in_path("src/database/**");
assert_passes!(rule);
}const std = @import("std");
const archunit = @import("archunit");
test "production files have no cycles" {
var files = try archunit.files(std.testing.allocator, .{});
defer files.deinit();
var scope = try files.inPath(&.{.{ .glob = "src/**/*.zig" }});
defer scope.deinit();
var should = try scope.should();
defer should.deinit();
var rule = try should.haveNoCycles();
defer rule.deinit(std.testing.allocator);
try archunit.expectPasses(&rule, .init(.init(std.testing.allocator, std.testing.io)));
}package architecture_test
import (
"testing"
archunit "github.com/LukasNiessen/ArchUnitGo"
)
func TestApiDoesNotTouchDatabase(t *testing.T) {
rule := archunit.ProjectFiles(nil).
InFolder("internal/api/**").
ShouldNot().DependOnFiles().
InFolder("internal/db/**")
archunit.AssertPasses(t, rule, nil)
}var rule = ArchUnit.projectFiles()
.inFolder("src/main/java/**/api/**")
.shouldNot()
.dependOnFiles()
.inFolder("src/main/java/**/infrastructure/**");
ArchAssert.passes(rule);$rule = ArchUnit::projectFiles()
->inFolder('src/Api/**')
->shouldNot()
->dependOnFiles()
->inFolder('src/Infrastructure/**');
ArchAssert::passes($rule);Beyond import rules
Architecture tests turn diagrams and conventions into fast, repeatable evidence for people, pull requests, and coding agents.
Define who may depend on whom and fail on the exact source edge that crosses the line.
Detect circular dependencies while the loop is still small enough to remove.
Validate slices against PlantUML or export the architecture that actually exists.
Track coupling, cohesion, file size, abstractness, and distance from the main sequence.
Produce test failures, CI artifacts, structured JSON, dependency diagrams, and standalone HTML reports from the same model.
Built in the open
Maintainers and first-time contributors work across languages, test frameworks, graph algorithms, documentation, and real-world examples.
From the blog
Project stories and practical guides for turning architecture decisions into useful feedback.
A hands-on comparison of maintenance, correctness, project resolution, features, and performance for TypeScript architecture testing.
14 min read · Read ↗The practical need that started one TypeScript library and grew into an architecture-testing family.
12 min read · Read ↗A practical guide to choosing editor-fast import rules, graph-aware architecture tests, or both.
11 min read · Read ↗Pick the implementation for your stack and add the first boundary rule to the test suite you already trust.
Find your library