Ruff vs Traditional Bandit - A Performance Comparison

When Ruff announced it had reimplemented Bandit’s security rules in Rust, my first thought was skepticism. Another tool trying to be everything to everyone, I figured. But after spending a week benchmarking Ruff against traditional Bandit on a real codebase: it was 25 times faster.

That performance difference fundamentally changes how you can use security scanning in your development workflow. When security scans that used to take five minutes now complete in twelve seconds, you can afford to run them on every commit, integrate them into your editor, and provide real-time feedback to developers.

The Great Rust Rewrite

Ruff represents a broader trend in Python tooling: reimplementing Python development tools in systems languages for dramatic performance improvements. Black got Rust competition with ruff format, flake8 got replaced by ruff check, and now Bandit has Rust-powered competition in ruff’s security rules.

The engineering behind this is impressive. The Ruff team didn’t just port Bandit’s rules to Rust, they rebuilt the entire static analysis engine from scratch, optimizing for speed while maintaining compatibility with Bandit’s rule definitions and findings.

Performance: The Numbers Don’t Lie

The performance difference between Ruff and traditional Bandit is staggering across virtually every metric that matters. Startup time, scan speed, memory usage, and CPU efficiency all favor Ruff by significant margins.

# Traditional Bandit
$ time bandit -r large_codebase/
# Result: ~5 minutes, 1000 lines/second

# Ruff security scanning
$ time ruff check --select=S large_codebase/
# Result: ~12 seconds, 25000 lines/second

Traditional Bandit processes about 1,000 lines of Python code per second on typical hardware. Ruff processes the same code at roughly 25,000 lines per second. Not just incrementally faster, but fundamentally different performance characteristics that change what’s possible.

This isn’t just academic. When your security scanning completes in seconds rather than minutes, developers stop seeing it as a roadblock and start treating it as helpful feedback. The psychological impact of instant results cannot be overstated.

Rule Coverage: Close but Not Identical

Ruff implements most of Bandit’s security rules, but not all of them. The coverage is extensive enough for most teams, but there are gaps that matter for specific use cases. Some of Bandit’s more exotic rules, like certain cryptographic vulnerability detections, haven’t been ported to Ruff yet.

More importantly, Ruff’s rule implementations sometimes differ subtly from Bandit’s originals. The core logic is the same, but edge case handling, confidence levels, and severity classifications can vary. In practice, this means you might see slightly different results when scanning the same codebase with both tools.

The Integration Story

Where Ruff really shines is integration simplicity. Because it’s designed as a unified Python development tool, adding security scanning to an existing Ruff configuration is often just a matter of enabling the appropriate rule categories.

# pyproject.toml - Add security rules to existing Ruff config
[tool.ruff]
select = ["E", "F", "S"]  # Add "S" for security rules
ignore = ["S101"]         # Skip assert usage warnings

[tool.ruff.per-file-ignores]
"tests/*" = ["S105", "S106"]  # Allow hardcoded passwords in tests

Teams already using Ruff for linting and formatting can add security scanning without introducing another tool into their development workflow. This consolidation reduces cognitive overhead and simplifies CI/CD configuration.

Traditional Bandit, being a specialized security tool, offers more granular configuration options for security-specific use cases. Ruff’s security features are powerful but less specialized, reflecting its role as a general-purpose Python tooling platform.

When Speed Changes Everything

The performance difference between Ruff and traditional Bandit enables entirely new workflows that weren’t practical before. Real-time security feedback in editors becomes viable when scans complete in milliseconds. Running comprehensive security checks on every keystroke in large codebases becomes possible.

Some teams have implemented “security-first” development workflows where security scanning happens before any other code quality checks, providing immediate feedback about security issues. This is only practical when security scanning is fast enough to feel instantaneous.

Pre-commit hooks that include security scanning become much more palatable when they don’t add significant delay to the commit process. Developers are more likely to embrace security scanning that feels invisible than scanning that makes them wait.

Migration Strategies

For teams considering a switch from traditional Bandit to Ruff, gradual migration often works better than wholesale replacement. Start by running both tools in parallel, comparing their outputs and understanding the differences.

Focus initially on high-confidence, high-severity findings where both tools agree. These represent the most important security issues regardless of which tool you ultimately choose. Gradually expand to include medium-severity findings and eventually develop confidence in Ruff’s coverage.

Some teams maintain traditional Bandit for specialized security analysis while using Ruff for day-to-day development workflow integration. This hybrid approach provides the best of both worlds: comprehensive security coverage and fast developer feedback.

Decision Framework

Choosing between Ruff and traditional Bandit depends on your team’s priorities and constraints. If developer experience and workflow integration are paramount, Ruff’s performance advantages usually outweigh its limitations. If comprehensive security coverage and specialized configuration options matter more, traditional Bandit remains the better choice.

Many teams find that their decision comes down to existing toolchain integration. Teams already committed to Ruff for other purposes naturally gravitate toward its security features. Teams with sophisticated Bandit configurations and security processes often stick with traditional Bandit.

Looking Forward

The competition between Ruff and traditional Bandit is driving improvements in both tools. Traditional Bandit is exploring performance optimizations, while Ruff continues expanding its security rule coverage.

This competition ultimately benefits Python developers by providing better tools and pushing the entire ecosystem toward higher performance standards. Whether you choose Ruff or traditional Bandit, you’re getting better security scanning than was available just a few years ago.

The future likely involves continued convergence, with performance gaps narrowing while feature compatibility improves. For now, both tools serve important roles in the Python security ecosystem, and the choice between them depends more on your specific needs than on any fundamental superiority of one approach over the other.