All notable changes to this project will be documented in this file.

The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.

[0.3.0] - 2026-05-21

Added

Changed

  • Empty assertion subjects now fail by default. Use allow_empty: true when an empty subject is intentional.
  • should_have_module_count/2 accepts min: and max: aliases for at_least: and at_most:.
  • should_be_free_of_cycles/2 now supports both ModuleSet checks and Modulith slice-cycle checks, so documented modulith pipelines work.
  • ArchTest.Collector filters now keep dependency graphs closed by removing excluded modules from both graph keys and dependency edges.
  • ArchTest.Collector.calls/2 and calls_from_path/2 now filter callers and callees, ignore generated protocol/compiler noise, and report static remote captures plus static apply/3 targets.
  • Layer, onion, modulith, and convention helpers now consistently forward graph/scope options to the collector.
  • ArchTest.Metrics.martin/2 now computes each module against itself rather than treating the whole matched set as one package.
  • ArchTest.Metrics.coupling/2 now treats a binary namespace root and its descendants as one package, and counts distinct external dependencies.
  • ArchTest.Modulith.define_slices_by/2 now discovers slices from descendant modules and resolves concrete roots when wildcard segments precede (*).
  • ArchTest.PlantUML.parse_edges/1 now ignores line comments and PlantUML block comments.

Fixed

  • use ArchTest, app: ... now correctly respects per-rule scope overrides for should_have_module_count/2.
  • use ArchTest, freeze: true now baselines should_have_module_count/2 failures and includes semantic scope options (app, apps, paths, include, exclude) in generated freeze IDs.
  • Freeze update mode no longer baselines empty-subject control failures.
  • should_only_be_called_by/3 now fails on an empty protected object set by default, matching other subject-based assertions.
  • should_have_module_count/3 no longer treats assertion options such as rule_id: and allow_empty: as count constraints.
  • should_have_module_count/3 no longer treats collector scope options such as apps:, paths:, include:, exclude:, and force: as count constraints.
  • Auto-freeze rule IDs for should_have_module_count/3 no longer include non-semantic graph: data.
  • Repeated ArchTest.Rule.ignore/2 calls now accumulate filters instead of replacing previous filters for the same field.
  • use ArchTest now exposes option-aware define_layers/2, define_onion/2, and define_slices/2 helpers.
  • Documentation examples were corrected for freeze usage, layer exceptions, collector options, metric helpers, and non-overlapping layer patterns.
  • Added a 0.2 to 0.3 migration guide with upgrade steps and before/after code examples for app scoping, conventions, empty subjects, freeze baselines, layer patterns, modulith slices, and reusable rules.
  • Igniter generators now emit use ArchTest, app: ..., use safer non-overlapping layer defaults, pass app scope to generated convention checks, and mark optional schema-placement checks with allow_empty: true.

[0.2.1] - 2026-04-10

Fixed

  • Compilation without Igniter — mix task files no longer fail with module Igniter.Mix.Task is not loaded when Igniter is not a dependency. All task modules are now wrapped in Code.ensure_loaded?(Igniter.Mix.Task) guards so they are only defined when Igniter is available.

[0.2.0] - 2026-03-08

Added

[0.1.2] - 2026-03-07

Added

  • Modulith.all_modules_covered_by/3 — asserts that every module under a namespace pattern belongs to a declared slice. Modules that escape slice coverage cause an explicit test failure instead of being silently ignored. Supports :except (list of glob patterns) and :graph (for testability). Also delegated from ArchTest as all_modules_covered_by/2,3.

[0.1.1] - 2026-03-07

Fixed

  • OTP version compatibility: tests now use stable fixture modules instead of OTP-version-sensitive stdlib internals, fixing failures on OTP 26/27
  • All credo --strict issues resolved (implicit try, Enum.map_join, negated if/else, nesting depth, cyclomatic complexity)

[0.1.0] - 2026-03-06

Initial release.

Added

  • Dependency assertionsshould_not_depend_on/2, should_only_depend_on/2, should_not_be_called_by/2, should_only_be_called_by/2, should_not_transitively_depend_on/2, should_be_free_of_cycles/1, should_not_exist/1
  • Naming assertionsshould_reside_under/2, should_have_name_matching/2, should_have_module_count/2
  • Behaviour / protocol assertionsshould_implement_behaviour/2, should_not_implement_behaviour/2, should_implement_protocol/2, should_not_implement_protocol/2
  • Module attribute assertionsshould_have_attribute/2, should_not_have_attribute/2, should_have_attribute_value/3, should_not_have_attribute_value/3
  • Function assertionsshould_export/3, should_not_export/3, should_have_public_functions_matching/2, should_not_have_public_functions_matching/2, should_use/2, should_not_use/2
  • Layered architecturedefine_layers/1 + enforce_direction/1, define_onion/1 + enforce_onion_rules/1
  • Modulith / bounded-context isolationdefine_slices/1, allow_dependency/3, enforce_isolation/1, should_not_depend_on_each_other/1
  • ArchTest.Conventions — pre-built checks: no_io_puts_in/2, no_process_sleep_in/2, no_application_get_env_in/2, no_dbg_in/2, no_raise_string_in/2, no_plug_in/2, all_public_functions_documented/2
  • ArchTest.Metrics — afferent/efferent coupling, instability, abstractness, distance from main sequence (Martin metrics)
  • ArchTest.Freeze — violation baseline freezing for gradual adoption; ARCH_TEST_UPDATE_FREEZE=true mode
  • ArchTest.ModuleSet — fluent module selection DSL: glob patterns, excluding/2, union/2, intersection/2, satisfying/1
  • ArchTest.Pattern — glob pattern compiler with * (single segment) and ** (multi-segment) wildcards
  • ArchTest.Collector — BEAM-native dependency graph builder via OTP :xref; works on compiled bytecode, no source parsing
  • Conventions support for both legacy and modern BEAM debug formats — handles both :abstract_code (OTP < 24) and :debug_info_v1 with :elixir_erl backend (Elixir 1.14+)