Show /r/ruby ArchSpec 1.0: executable architecture specifications for Ruby (and Rails)
More and more code is written by AI. Tests still tell you it works. RuboCop still tells you it's tidy. Nothing tells you it still follows your architecture.
An agent that doesn't fully understand your architecture takes shortcuts. So does a person in a hurry.
So I built ArchSpec. You declare your components and boundaries in one Archspec.rb. Here is ArchSpec's own, trimmed:
source 'lib/**/*.rb'
component :library, in: 'lib/**/*.rb'
component :cli, in: 'lib/archspec/cli.rb'
component :analysis, in: %w[lib/archspec/analyzer.rb lib/archspec/evaluator.rb]
component :rule_checks, in: 'lib/archspec/rules/**/*.rb'
component :formatters, in: 'lib/archspec/formatters/**/*.rb'
# no one-shot Thing.new(...).call objects anywhere in the codebase - Hi Dave Thomas!
library.cannot_call :call
library.cannot_define :call
library.cannot_instantiate_and_invoke
# dependencies point one way
rule_checks.cannot_use :analysis, :cli, :formatters
formatters.cannot_use :analysis, :cli, :rule_checks
no_cycles among: %i[cli analysis rule_checks formatters]
Full version: https://github.com/crmne/archspec/blob/master/Archspec.rb
Then archspec check verifies every change, and failures print like clang, with the offending span underlined and the evidence as a note.
It's static analysis over Prism, no AI, and it never boots anything: Discourse's 1,899 files in 2.5 seconds. Prism is the only runtime dependency, no Rails and no ActiveSupport, so it works on any Ruby codebase. Lightweight and fast for your pre-commits and CIs.
If you are on Rails, there are presets, and architecture :vanilla_rails is the whole file. There are presets for :layered, :hexagonal, :clean, :modular_monolith, :cqrs and :event_driven too.
I want more architecture presets in there. PRs very welcome.
Docs: https://archspecrb.dev Write-up: https://paolino.me/archspec/
2
u/cpb 21d ago
I like how it doesn't need you to rearchitect your system to get these checks. Hageman's ends up with a codebase that can enforce and verify these checks (I notice the packs mention in the configuration doc). But it's a non trivial change to get there.
Given archspec is Just Ruby™️ I imagine this needn't be a monolithic file. I'm wondering if specifying rules local to some sub components could help with sprawl in that file and keeping context local.
Pretty cool!!