Scaling Vibe Coding: Why 10 Developers Need One Scanner
Ten developers using AI assistants produce ten different vulnerability surfaces. One deterministic scanner with fixed rules is the team-level gate that fixes this.
Ten developers using AI assistants don’t produce one coherent codebase. They produce ten coding styles, ten prompt strategies, and ten different vulnerability surfaces — then merge them into one repo. The failures aren’t random. They’re structural: each developer’s AI assistant has its own generation habits, its own blind spots, and its own non-determinism. What catches the gaps isn’t another AI reviewer. It’s a scanner that runs the same rules on every PR, every time, regardless of who wrote the code.
Why Individual Vibe Coding Habits Don’t Aggregate
BrassCoders treats every developer’s code the same way: it runs 12 static-analysis scanners — Bandit, Pylint, Pyre/Pysa, Semgrep, ast-grep, detect-secrets, and six custom detectors — and emits the union of their findings as YAML. The developer’s name doesn’t appear in the output. The AI assistant they used doesn’t appear. What appears is the vulnerability pattern, the file path, and the scanner that caught it.
This matters because vibe coding habits don’t cancel each other out at team scale. One developer prompts their AI assistant to be conservative with dependencies. Another prompts theirs to ship fast. A third uses a different model entirely. Each of these produces code with different structural properties. Some AI assistants tend toward certain SQL construction patterns. Some miss exception-handling edge cases in ways others don’t. None of this averages out when you merge. Each developer contributes their AI assistant’s specific blind spots to the shared codebase.
The aggregate vulnerability surface isn’t the average of ten developers’ risks. It’s the union. Every blind spot any AI assistant has becomes a potential hole in the codebase.
Non-Determinism Compounds at Team Scale
BrassCoders is offline and deterministic: the same scanner version on the same code produces identical YAML output every run. No sampling, no temperature, no prompt variation. This is a documented property of static analysis — the scanner reads the AST and applies fixed rules.
AI assistants work the opposite way. GitHub Copilot, Claude Code, and every other LLM-based coding assistant generate suggestions probabilistically. Per the model documentation for every major provider, LLM outputs vary across calls even with identical inputs — temperature and sampling are how these models work. A developer who accepts a Copilot suggestion on Monday and re-prompts on Tuesday might get meaningfully different code for the same function signature. Security properties can change between those two suggestions.
Scale this to ten developers across a sprint. Each developer is accepting suggestions from a non-deterministic system. Different prompts, different temperatures, different context windows. The security properties of what they ship aren’t predictable from the properties of what they tested. And if your code review is also LLM-based, you’ve added a second non-deterministic layer on top of the first.
The gap between what a developer’s AI assistant generates and what a deterministic scanner sees is where bugs live. An AI reviewer is great at detecting things it’s seen in training data. A static-analysis scanner applies rules that don’t change based on what the model saw last week.
What One Consistent Gate Buys the Team
BrassCoders running in CI means the same rules run on every PR — regardless of which developer pushed, which AI assistant they used, or what their local review habits are. The findings set is the scanner’s findings set. Not one developer’s intuition, not one AI assistant’s opinion. The rules.
This is the property that makes BrassCoders a team-level gate rather than a per-developer preference. A per-developer preference is skippable. A CI gate isn’t. When SQL injection looks the same to Bandit whether it was written by GitHub Copilot or Claude Code — because it matches the same AST pattern either way — the scanner catches it regardless of its origin.
The other thing consistency buys: auditability. When your scanner produces the same YAML for the same code, you can compare runs over time. A finding that appears in PR #47 and is still present in PR #52 is a finding that wasn’t addressed. A finding that disappears between commits was either fixed or suppressed — and BrassCoders tracks which. An LLM reviewer can’t give you that. Two runs of the same LLM reviewer on the same PR produce different outputs. You can’t diff them.
The CI Integration Path
BrassCoders plugs into GitHub Actions as a single workflow file — one scan step, same rules, every PR, no per-developer configuration. Teams that want findings caught before they push can also add a pre-commit hook that runs the local scanner before git commit completes.
The full GitHub Actions setup is documented at add-brasscoders-to-github-actions, and the pre-commit hook setup is at pre-commit-hook-ai-coder-bugs.
The split matters for team dynamics. Pre-commit scanning is optional — a developer who doesn’t install the hook still gets caught at CI. CI scanning is mandatory — every PR hits the scanner before it can merge. Teams with high autonomy and low process overhead can start with CI only. Teams where developers want earlier feedback add the pre-commit layer on top.
Either way, the CI gate is where consistency lives. It’s the one scan that runs on everyone’s code under the same conditions.
One activation covers 3 machines per license. A developer can run BrassCoders on their laptop, a staging box, and a CI runner if they want to count the runner as an activation — or the team can run the CI scan under a shared license. The BrassCoders Paid plan is $12 per developer per month.
What Team-Wide Findings Look Like
When BrassCoders scans a shared codebase after a two-week sprint of AI-assisted development, the findings YAML reflects the aggregate of all 10 developers’ contributions. No single developer’s code is labeled. The finding at line 147 of auth.py is either a hardcoded secret or it isn’t. The SQL construction in db_utils.py either hits Bandit’s B608 injection rule or it doesn’t.
This flat view of findings is a feature, not a limitation. A finding that only shows up in one developer’s code isn’t “their problem” — it’s a gap in the shared codebase. Treating it as theirs individualizes what should be a shared signal. The scanner doesn’t know who wrote the code. The security reviewer — a human or an AI assistant doing triage — shouldn’t have to either.
The determinism argument is covered in depth here. The short version: a gate that produces different outputs for the same inputs isn’t a gate. It’s a suggestion.
For teams running 10 developers through a sprint of AI-assisted development, the question isn’t whether any individual developer will write a vulnerable function. One of them will. The AI assistant will generate something plausible and slightly wrong, and the developer will accept it. The question is whether the team has a gate that catches it before it merges. BrassCoders in CI is that gate.
pip install brasscoders
brasscoders activate
brasscoders scan .
Start with the OSS core, free, Apache 2.0. Add BrassCoders Paid when you want the noise-reduction pass that brings 1500+ findings down to the actionable ones.
Frequently Asked Questions
Should every developer on the team run BrassCoders individually?
CI is the mandatory gate — the deterministic scan runs on every PR regardless of what individual developers do locally. Individual dev-time scans with BrassCoders are useful for catching findings before you push, but the team gate is what counts for consistency. What the CI gate sees is what the team is accountable to.
We use different AI assistants across the team — does that affect findings?
Different AI assistants produce different code, but the scanner rules are the same. SQL injection written by GitHub Copilot and SQL injection written by Claude Code look identical to Bandit and Semgrep — they both match the same AST patterns. BrassCoders doesn't know or care which assistant generated the code. The scanner sees the output, not the author.
How does BrassCoders handle different Python versions or project configs across developers?
BrassCoders reads the project's pyproject.toml and targets the declared Python version for scanner rules that are version-sensitive. The scanner-rule set is fixed by the BrassCoders version, not by who runs it. Two developers on the same codebase at the same BrassCoders version get identical YAML output — that's what determinism buys you.
What does 'deterministic' mean for a security scanner?
Deterministic means same code in, same findings out — every run, every machine, every developer. No sampling, no randomness, no prompt variation. BrassCoders runs 12 static-analysis scanners against your code's AST and produces the same YAML regardless of who triggers the scan. LLM-based reviewers can't make this guarantee because they sample probabilistically.
Does running BrassCoders in CI replace dev-time feedback?
No — they serve different purposes. CI catches what slips through before it merges. Dev-time BrassCoders catches it before you even push. Both are useful. The difference is that CI is mandatory and non-negotiable; dev-time scanning is a courtesy to yourself. The team's security posture depends on the gate, not on whether every developer happens to scan locally.