Developer Tools
RAFF
Executable architecture fitness functions for Rust — statement count, module coupling and Git volatility as CI and pre-commit gates, so AI-generated code meets the same deterministic quality boundary as human-written code.
Active · 2025
Rust · CLI

raff is a code-quality and architecture analysis tool for Rust projects.
It turns architectural expectations into executable fitness functions: measurable rules that can run locally, in continuous integration, or as part of a pre-commit hook. Rather than relying entirely on code review to notice structural degradation, raff makes selected qualities of a codebase continuously observable and enforceable.
I originally built it as a practical code-quality tool, but I also designed it with AI coding agents in mind.
As AI agents take on larger implementation tasks, generating code is no longer the difficult part. The harder problem is ensuring that the resulting code remains maintainable, appropriately decomposed, and consistent with the architecture of the repository. raff provides a deterministic quality boundary that sits outside the agent: generated code can be analysed before it is committed, and changes that exceed configured architectural thresholds can be rejected automatically.
Why I built it
Code quality often degrades gradually.
A file gains a few more responsibilities. A module starts depending on several neighbouring components. A supposedly stable area begins changing with every feature. None of these changes necessarily looks severe in isolation, but together they create systems that are harder to understand, test, and modify safely.
Traditional linters are effective at catching local implementation problems, but many architectural concerns exist above the level of individual expressions:
- Is one component becoming disproportionately large?
- Are module boundaries becoming increasingly coupled?
- Which parts of the codebase change most often?
- Are frequently changing components also highly connected?
- Is a proposed change moving the architecture towards or away from its intended shape?
raff is intended to make questions like these measurable.
It is based on the idea of architecture fitness functions: automated checks that continuously evaluate whether a system still exhibits the characteristics its developers want to preserve.
What it analyses
Statement count
raff can count statements across Rust files and directories.
Statement count is not a complete measure of complexity, but it is a useful structural signal. It can reveal files, modules, or components that are accumulating too much responsibility and may need to be decomposed.
raff statement-count --path ./src Rather than treating every large file as inherently wrong, teams can use configurable thresholds to identify significant outliers and architectural drift.
Module coupling
raff analyses dependencies between Rust modules and components.
This makes it easier to see where boundaries are becoming porous, where internal modules have accumulated too many outward dependencies, and where changes may have an unexpectedly broad impact.
raff coupling --path ./src Coupling analysis is particularly useful in layered systems, modular monoliths, and repositories organised around bounded contexts or vertical slices. It gives developers a concrete way to evaluate whether those intended boundaries are reflected in the code.
Code volatility
raff uses Git history to identify which parts of a repository change most frequently.
raff volatility --path . High volatility is not automatically a defect. A component may change frequently because it represents an actively developed part of the product. The value comes from combining volatility with other signals.
A component that changes often and is also highly coupled deserves attention: it may amplify the cost and risk of every change made around it.
Combined architecture checks
The analyses can be run together through a single command:
raff all This provides a broader view of the repository and makes it suitable for automated quality gates.
Designed for AI-assisted development
One of the reasons I built raff was to improve the reliability of AI-generated code.
AI coding agents are effective at completing well-scoped implementation tasks, but they can optimise too heavily for the immediate request. An agent may produce code that compiles and passes tests while still:
- adding responsibility to an already oversized module;
- introducing unnecessary dependencies between components;
- duplicating logic across architectural boundaries;
- placing code in a convenient rather than appropriate location;
- increasing the change surface of a frequently modified component;
- satisfying the feature request while weakening the overall design.
Human review remains important, but it should not be the only defence.
raff gives a repository a deterministic architectural feedback loop. The same rules apply regardless of whether code was written manually, generated by an agent, or produced through a combination of both.
A useful workflow is:
- An AI agent implements a task.
- Tests, formatting, and conventional linting run.
raffanalyses the staged architectural impact.- The commit proceeds only if the configured fitness functions pass.
- If a threshold is exceeded, the agent receives concrete feedback about the affected component and can revise the implementation.
This moves architectural quality from a prompt-level suggestion into an executable repository constraint.
Instead of merely telling an agent to “keep the architecture clean”, the repository can define what selected aspects of clean architecture mean and verify them automatically.
Pre-commit integration
raff includes a pre-commit-oriented profile designed to keep local feedback fast.
repos:
- repo: local
hooks:
- id: raff-architecture-check
name: Architecture fitness functions
entry: raff --profile pre-commit all
language: system
pass_filenames: false
always_run: true The pre-commit profile focuses on staged files and fast analyses, avoiding slower history-based work when appropriate.
This makes it suitable for both human and agent-driven workflows. An AI agent can make a change, stage it, and run the same checks that a developer would encounter before committing.
The result is a tighter correction loop: architectural problems are caught while the implementation context is still active, rather than later during pull-request review.
Configurable quality boundaries
raff can be configured through a repository-level configuration file:
[profile.pre_commit]
fast = true
staged = true
quiet = true
sc_threshold = 25 This allows each repository to define quality boundaries appropriate to its own architecture.
The intent is not to impose a universal definition of good design. A low-level parser, a command-line entry point, and a domain service may have very different acceptable shapes. Fitness functions are most valuable when they encode deliberate local decisions rather than generic style opinions.
Configuration can be used to determine:
- which analyses run;
- whether only staged files are inspected;
- how strict thresholds should be;
- which paths or components are included;
- what output format is produced;
- whether a violation should fail the command.
Multiple output formats
raff can produce results for both humans and automation.
Supported output styles include terminal-oriented reports as well as machine-readable and visual formats such as:
- JSON
- CSV
- HTML
- Graphviz DOT
- JUnit-compatible output
- Static Analysis Results Interchange Format
Human-readable output is useful during local development. Structured formats make the same analyses suitable for continuous integration systems, dashboards, pull-request annotations, and other developer tooling.
Graph output can also make coupling relationships easier to inspect visually than through raw dependency lists.
Architecture as an executable property
The central idea behind raff is that architecture should not exist only in diagrams, documentation, or the memories of senior engineers.
Those things are valuable, but they are passive. They do not automatically react when the code stops matching the intended design.
Fitness functions turn selected architectural decisions into executable properties of the repository.
They cannot prove that an architecture is good, and they should not replace engineering judgement. What they can do is continuously surface measurable drift:
- components growing beyond intended limits;
- dependency direction becoming less controlled;
- volatile areas accumulating broader coupling;
- code changes crossing boundaries unexpectedly.
That is especially valuable as repositories grow and more implementation work is delegated to autonomous tools.
Installation
Install the command-line tool with Cargo:
cargo install raff-cli The installed binary is named raff:
raff --help It can also be installed directly from the repository during development:
cargo install --git https://github.com/liamwh/raff.git Design principles
Architecture rules should be executable
An architectural rule that cannot be checked is easy to ignore accidentally. raff focuses on turning useful structural signals into repeatable commands.
Results should be actionable
A quality tool should identify the affected component, explain the relevant metric, and provide enough context for a developer or coding agent to improve the change.
Local feedback should be fast
The pre-commit path should analyse only what is needed and avoid expensive work that belongs in continuous integration or an explicit full-repository analysis.
Rules should be configurable
Architecture is contextual. Thresholds and boundaries should reflect the repository rather than imposing arbitrary universal limits.
AI-generated code should meet the same standards
The origin of a change should not affect its quality bar. Human-written and AI-generated code should pass the same tests, lint rules, and architectural fitness functions.
Metrics support judgement rather than replacing it
Statement count, coupling, and volatility are signals. They help focus attention and support decisions, but they are not substitutes for understanding the code.
Status
raff is an open-source Rust project and command-line tool.
Its current capabilities centre on statement-count analysis, module coupling, Git-based code volatility, configurable profiles, staged-file analysis, pre-commit integration, and multiple report formats.
The longer-term direction is to expand the set of architecture fitness functions while keeping them practical, configurable, and useful as automated quality gates for both developers and AI coding agents.