Skip to content

perrystreetsoftware/Rubyzen

Rubyzen

Gem Version CI Docs

Rubyzen is an architectural linter for Ruby that lets you write architectural lint rules as unit tests, inspired by Konsist (for Kotlin) and Harmonize (for Swift).

Architectural linters in the era of AI-generated code

In the era of AI-generated code, architectural flaws and subtle bugs happen faster than ever. AI agents produce code that passes tests and looks reasonable but subtly violates your team's architecture. As more code is produced, faster, it becomes impractical for manual code reviews to catch all these violations.

Architectural lint rules act as deterministic guardrails. They catch the architectural or structural mistakes that AI introduces, such as calling the database from a presenter or performing business logic in a controller, before they get merged. And since they run as unit tests, they provide immediate feedback to the AI agents to fix their own code.

Why Yet Another Linter?

Traditional linters such as RuboCop require dealing with the raw AST, which has a steep learning curve and makes rules hard to write, read, and maintain. Rubyzen abstracts away the AST details, providing a high-level API that allows developers to write lint rules in a more natural way — the same way we write tests.

Advantages

  • Readable, Easy-to-Use API: Rubyzen provides a high-level API to access files, classes, methods, parameters, and more, without having to deal with low-level AST operations.

  • Architectural Enforcement & Documentation: By writing lint rules as tests, you can use the Given-When-Then style to enforce and document your architecture directly within the codebase.

  • Less Manual Reviews: With architectural rules automatically enforced, code reviews can focus on complex issues instead of repeating the same architectural feedback.

  • AI-Friendly Feedback Loop: When lint rules fail, the failure messages tell AI agents exactly what they violated and where, allowing them to self-correct their code.

Setup

Add Rubyzen to your Gemfile:

gem 'rubyzen-lint', group: :test

Then run bundle install.

That's it. Rubyzen auto-discovers your project structure (app/, lib/, src/, spec/) from your project root. If you need to lint other directories (e.g., config/, db/), see Custom Paths below.

Write your first set of lint rules

Create a spec file anywhere in your project (e.g., spec/architecture/sample_spec.rb) and start enforcing your architecture:

require 'rubyzen'

RSpec.describe 'Architecture rules' do
  let(:project) { Rubyzen::Project.new }
  let(:controllers) { project.files.with_paths('app/controllers/').classes }
  let(:presenters) { project.files.with_paths('app/presenters/').classes }

  it 'controllers do not call ActiveRecord directly' do
    expect(controllers.all_methods.call_sites.with_name('where')).to zen_empty
  end

  it 'controllers inherit from ApplicationController' do
    expect(controllers).to zen_true { |c| c.superclass_name == 'ApplicationController' }
  end

  it 'presenters do not depend on repositories' do
    expect(presenters.all_methods.call_sites).to zen_false { |cs|
      cs.receiver&.end_with?('Repository')
    }
  end
end

You can find more sample lint rules in the sample_project/spec/ directory.

Run your lint rules

bundle exec rspec spec/architecture/

Custom Paths

By default, Rubyzen::Project.new scans app/, lib/, src/, and spec/. If you need to lint other directories (e.g., config/, db/), add them explicitly, otherwise those files won't be scanned and queries against them will return empty results.

# In your spec file — scope to specific directories
project = Rubyzen::Project.new(['app/models', 'app/controllers'])

# Or in spec/spec_helper.rb — configure globally for all specs
Rubyzen.configure do |config|
  config.paths = ['app', 'lib', 'config']
end

CI Integration

Add a step to your existing CI workflow to run your lint rules automatically when a PR is opened:

- name: Run architecture lint rules
  run: bundle exec rspec spec/architecture/

AI Agent Skills

Rubyzen includes agent skills in .claude/skills/ (also symlinked at .github/skills/) that work with both Claude Code and GitHub Copilot:

Skill Purpose
write-lint-rule Write an architectural lint rule using the Rubyzen API
run-lint-rules Run sample project lint rules and verify the violations are detected
expand-rubyzen Add a new Rubyzen API (Declaration + Provider + Collection)
add-rubyzen-tests Write unit tests for Rubyzen's own components
run-tests Run Rubyzen's unit test suite

Contributing

Contributions are welcome! See CONTRIBUTING.md for instructions on setting up the project, enhancing the API, and adding tests.

About

Rubyzen is a modern linter for Ruby that allows you to write architectural lint rules as unit tests. In the era of AI-generated code, Rubyzen provides your team with deterministic guardrails to keep your codebase clean, maintainable, and consistent as it grows.

Topics

Resources

License

Code of conduct

Contributing

Stars

Watchers

Forks

Contributors

Languages