Cargo-Crap: Finding Untested Complexity in AI-Generated Rust Code

koburan1 pts0 comments

cargo-crap: Finding Untested Complexity in AI-Generated Rust Code

Available

Open to new opportunities — if you're building something ambitious<br>and need hands-on engineering leadership, let's talk.

-->

v0.2.0<br>cargo-crap is open source. Try it, star it, or read the docs.

GitHub

crates.io

docs.rs

Rust makes many bugs impossible.

Memory safety. Thread safety. Ownership. Lifetimes. Exhaustive matching. Strong types.

But Rust cannot answer one essential maintenance question: Is this code safe to change?

A function can compile perfectly and still be risky to touch.

It can have too many branches, too many special cases, too many hidden paths, and not enough tests to give you confidence.

This is why I built cargo-crap is a Rust tool that finds functions that are both complex and poorly tested by calculating the Change Risk Anti-Patterns (CRAP) metric.

Coverage shows which parts of the code were exercised by tests.<br>Complexity shows how many paths may exist through a function.<br>CRAP combines both signals to highlight code that is risky to change.

I built it as one small guardrail for AI-assisted Rust development: agents can move fast,<br>but we still need measurable checks around the complexity they introduce.

The problem with coverage alone

Code coverage is useful, but it can be misleading.

A small helper function with 0% coverage is not great, but it is probably not the biggest risk in your system:

fn is_empty(value: &str) -> bool {<br>value.trim().is_empty()<br>Now compare that with a large function that parses input, validates business rules, handles multiple edge cases, mutates state, and has several nested branches.

If that function has 0% coverage, the risk is completely different.

Coverage alone does not tell you that. It only tells you what was executed during tests. It does not tell you how hard the code is to understand or how many paths exist through it.

The problem with complexity alone

Cyclomatic complexity measures the number of independent paths through a function. Every if, match, loop, and branch adds more possible paths.

That is useful information, but complexity alone is also not enough.

Some code is naturally complex. Parsers, state machines, protocol handlers, validation logic, and compatibility layers often branch because the domain itself branches.

If that code is well tested, the risk is lower.

The real problem is untested complexity , not complexity itself.

The CRAP metric

The CRAP metric was introduced by Alberto Savoia and Bob Evans in 2007 with crap4j,<br>and Savoia later described the idea in more detail on the Google Testing Blog.<br>It combines cyclomatic complexity and test coverage into a single number:

CRAP(m) = comp(m)² × (1 − cov(m)/100)³ + comp(m)<br>Where:

comp(m) is the cyclomatic complexity of a function

cov(m) is the test coverage percentage for that function

You do not need to memorize the formula. The intuition is simple:

Simple and well-tested code gets a low score

Complex but well-tested code gets a moderate score

Simple but untested code is not ideal but usually manageable

Complex and untested code receives a much higher score

That last category is what matters because that is where code changes become dangerous.

There are two direct ways to improve a high CRAP score: reduce the function’s complexity or add meaningful tests around the important paths.<br>The number is not meant to shame a function. It shows where refactoring or test work will reduce the most risk.

Why this matters even more with AI agents

AI agents are becoming part of everyday software development. They can generate code, refactor functions, add tests, update APIs, and<br>move very quickly through a codebase.

That speed cuts both ways. It can also make code more complex or less tested than before.

With each automatically generated branch, exception, or fallback, an AI agent can gradually make a function harder to understand and test.

One pattern I see often is preservation by accumulation: instead of simplifying the model, an agent adds another fallback,<br>another special case, or another compatibility branch to keep the current behavior working.<br>The tests may still pass, but the function has become harder to reason about.<br>The code compiles, yet the system has become harder to change safely.

cargo-crap serves as a boundary for AI-assisted development , alerting you when the score crosses your chosen threshold.

You can change code, but cargo-crap makes silent increases in high-risk, untested complexity visible.

That boundary matters even more when code is changed not only by humans but also by AI agents .

My intended workflow is simple:

Let an agent implement the change.

Run tests and coverage.

Run cargo-crap.

If the score increased, ask the agent to either simplify the function or add meaningful branch coverage.

What cargo-crap does

cargo-crap brings this metric into the Rust ecosystem as a Cargo-style tool.

If you use cargo-binstall, you can install a pre-built binary:

cargo...

code crap complexity function cargo coverage

Related Articles