{"slug": "defending-your-codebase-from-ai-slop-guardrails-that-keep-you-in-control", "title": "Defending your codebase from AI slop: guardrails that keep you in control", "summary": "A developer outlined a guardrail stack for keeping AI coding agents from degrading a codebase, combining ARCHITECTURE.md and AGENTS.md convention files with dependency-cruiser for enforcing allowed import directions between layers and Semgrep for pattern-based rules that flag misplaced helpers and other structural violations. The approach writes standards in a machine-enforceable form so checks fail the build with messages that explain why, rather than relying on human review to catch the volume of agent-generated pull requests.", "body_md": "AI coding agents are fast. They also write code that compiles, looks fine at a glance, and slowly wears down your architecture. A component imports the database layer directly. A function grows to 200 lines with three nested ternaries. A dependency shows up that nobody asked for. A routes file picks up a `formatDate` helper, then a `slugify`, then a `retryWithBackoff`. Tests run every line but never check a result.\n\nNone of this is new. Junior developers in a hurry do the same things. What's new is the volume. If an agent opens ten PRs a day, careful human review alone won't catch everything.\n\nThe answer isn't to stop using AI. It's to write your standards down in a form machines can enforce, so the rules hold no matter who or what wrote the code. Here's the stack I usually build for my projects.\n\nTools can only enforce rules that exist. Before adding any tooling, I decide how I want my code to look, define the rules, and write them in two files:\n\n`ARCHITECTURE.md` covers the layers, which layer may import which, where state lives, and how data flows.`AGENTS.md`, essential nowadays, is usually created automatically and then edited by me. It covers conventions for AI agents: naming, patterns to use, patterns to avoid, and tools that are off-limits (for example: \"we use Biome, don't add ESLint\").\nAgents read these files. So do new hires. And every tool below points back to a section in one of them, so when a check fails, the message explains *why* and not just *what*.\n\nSomething I've discovered quite recently: [dependency-cruiser](https://www.npmjs.com/package/dependency-cruiser) checks which files import which. You describe the allowed directions between layers, and it fails the build when something crosses a line:\n\nThis is the kind of mistake AI makes most, because it takes whatever import path is closest. A rule that always gives the same answer stops it before review.\n\n`dependency-cruiser` tells you *which files talk to each other*. It can't tell you *what the code inside a file is doing*. That's what [Semgrep](https://semgrep.dev) is for.\n\nSemgrep is a pattern matcher that understands code. You write a rule that says \"code shaped like X, in files matching Y, is a violation.\" Semgrep parses each file into a syntax tree and reports every match. It doesn't run your code or try to understand your whole app. It only checks shapes.\n\nA rule is a short YAML entry:\n\n```\nrules:\n  - id: api-routes-no-helpers\n    languages: [python]\n    severity: ERROR\n    message: >\n      routes.py is for route handlers only.\n      Move helpers to their own module (see ARCHITECTURE.md → API).\n    paths:\n      include: [packages/api/src/api/routes.py]\n    patterns:\n      - pattern: |\n          def $FUNC(...):\n              ...\n      - pattern-not-inside: |\n          @$ROUTER.$METHOD(...)\n          def $F(...):\n              ...\n```\n\n`pattern` is the code to match. `$FUNC` matches any name, and `...` matches anything.`pattern-not-inside` is the exception. Here, functions with a route decorator are allowed.`paths` limits the rule to certain files.`message` is what the developer (or the AI agent) sees when the rule fails. Write it as an instruction.\nRunning `semgrep --config .semgrep/rules/` checks the whole repo against every rule in that folder. We keep one YAML file per area (app, API, mq) so rules stay easy to find, and each rule gets a small test fixture with code that should match and code that shouldn't, so we know the rule catches what we meant.\n\nRead [their docs](https://docs.semgrep.dev/writing-rules/generic-pattern-matching) (or ask your agent to) on what's possible and get creative.\n\nThis was the rule we wanted most. Some files exist for one job, and AI agents love to drop \"just one small helper\" into them. After a few months, your routes file is half utilities.\n\nSo we made it impossible. Each of these files may only contain its one kind of thing:\n\n`routes.py`): route handlers only` services/``functions/`\nHelpers go in their own module, where they can be named, tested and reused. The rule message tells the agent exactly that, so it usually fixes itself on the next try.\n\nOnce you think in shapes, a lot of review comments turn into rules:\n\n`fetch` in routes and server functions`db/queries`\n`tx: DbClient = db` (this hides transaction bugs)\nSemgrep works on [many languages](https://docs.semgrep.dev/supported-languages), so it covers anything you want.\n\n**Rule of thumb:** if a review comment can be written as a code pattern, make it a Semgrep rule. Then it gets checked the same way on every PR.\n\nYou probably have violations already. Don't let that stop you. In CI, Semgrep can compare against a baseline:\n\n`main`\nNew code is held to the standard from day one, and nobody has to fix everything first.\n\nArchitecture rules keep the code tidy. They don't tell you whether it's safe. AI agents write the same security bugs humans do, just faster: SQL built from strings, an endpoint that forgets to check who's calling it, a token pasted into a config file, user input rendered as HTML.\n\nSecurity tools fall into three groups, and each sees something the others can't:\n\n| Kind | Looks at | Finds | Misses | \n|---|---|---|---|\n| **SAST** (static application security) | Your source code, not running | Injection, unsafe APIs, hardcoded secrets, tainted data flows | Runtime config, auth bugs that depend on real data | \n| **SCA** (software composition analysis) | Your dependencies and lockfiles | Known CVEs, malicious packages, licenses | Bugs in your own code | \n| **DAST** (dynamic application security) | The running app, from outside | Missing auth, exposed endpoints, bad headers, misconfigured servers | Where in the code the bug is; code paths it never reaches | \n\nSAST and SCA run on every PR in seconds or minutes. DAST needs a deployed app, so it runs against a preview environment or staging. Most of the tools below cover more than one group:\n\n| Tool | SAST | SCA | DAST | Other | \n|---|---|---|---|---|\n| Semgrep | ✅ | ✅ |  | Secrets | \n| Trivy |  | ✅ |  | Containers, IaC, secrets | \n| Snyk | ✅ | ✅ | ✅ | Containers, IaC | \n| Checkmarx One | ✅ | ✅ | ✅ | Containers, IaC, secrets, API | \n| GitHub CodeQL | ✅ |  |  | Dependabot covers SCA | \n| ZAP |  |  | ✅ | Free, open source | \n\nI am an OSS supporter, so I usually go with Semgrep and Trivy. Companies I've worked for went with whatever suited them best. Choose any; no judgement there.\n\nGood SAST tools track data flow: they follow user input from a request handler and flag it if it reaches SQL, a shell or HTML unescaped. Ask an agent to \"add search\" and you may get:\n\n``` python\n@router.get(\"/search\")\ndef search(q: str, db: Session = Depends(get_db)):\n    return db.execute(text(f\"SELECT * FROM products WHERE name LIKE '%{q}%'\"))\n```\n\nIt works, and it's SQL injection. [Semgrep Code](https://semgrep.dev/products/semgrep-code) catches it with the engine you already run for architecture (add `--config p/owasp-top-ten`). [CodeQL](https://codeql.github.com) is free for public repos and shows results in the PR. [Snyk Code](https://snyk.io/product/snyk-code/) and [Checkmarx](https://checkmarx.com) cover the same ground in their platforms. Fail the build on high-confidence rules only. A noisy check gets switched off.\n\nAgents add packages freely, sometimes outdated, sometimes made up and squatted (\"slopsquatting\"). [Trivy](https://trivy.dev) is the free baseline for vulnerable packages, secrets, container images and IaC:\n\n```\ntrivy fs . --scanners vuln,secret,misconfig --severity HIGH,CRITICAL --exit-code 1\n```\n\nTrivy only matches versions, so it flags vulnerable packages whether or not you use the vulnerable part. [Semgrep Supply Chain](https://semgrep.dev/products/semgrep-supply-chain) checks reachability: does your code actually call the vulnerable function? [Snyk](https://snyk.io) opens fix PRs and alerts when a new CVE hits code you already shipped. [Checkmarx One](https://checkmarx.com) also detects malicious packages such as typosquats.\n\nRequire CODEOWNERS approval on `package.json` and lockfiles, so every new dependency is a human decision.\n\nDAST sends real requests to a deployed app, so it catches what code scanning can't: an endpoint the agent left without an auth check, one user reading another's data by changing an ID, missing security headers, exposed debug pages. Run it against a preview deploy or staging:\n\n```\ndocker run -t ghcr.io/zaproxy/zaproxy:stable zap-api-scan.py \\\n  -t https://preview.example.com/openapi.json -f openapi\n```\n\n[ZAP](https://www.zaproxy.org) is free. [Nuclei](https://github.com/projectdiscovery/nuclei) checks for known exposures. [StackHawk](https://www.stackhawk.com), [Snyk API & Web](https://snyk.io/product/snyk-api-web/) and [Checkmarx DAST](https://checkmarx.com/product/dast/) are paid options. Give the scanner credentials and an API spec, or it will scan your login page and little else.\n\nDAST, I'd say, is a nice-to-have, not a must-have. [Folks at Semgrep think so too](https://semgrep.dev/blog/2023/dast-devsecops/). Occasional manual testing or pentesting is more than enough.\n\nSomething I've learned about from [Uncle Bob](https://x.com/unclebobmartin/status/2047661738456121506?s=20). I never thought about it until AI became a thing.\n\nCRAP stands for Change Risk Anti-Patterns, a metric used to identify risky, complex, and poorly tested code. The CRAP Index measures the maintenance risk of a specific function or method.\n\nRule of thumb: a score above 30 indicates a high-risk, \"CRAPpy\" method that needs attention or refactoring.\n\nI found two tools I could integrate into my pipelines that would help to keep AI-generated code under control:\n\nSome rules can't be written as patterns: \"does this name match what the code does?\" or \"is this abstraction premature?\" For those, we can use [CodeRabbit](https://docs.coderabbit.ai/triage/rules) (or one of many other AI code review tools) with a strict config file, like `.coderabbit.yaml`:\n\nThe important part is that **CodeRabbit is the last layer, not the first.** AI review isn't consistent, so anything that *can* be checked by a tool that always gives the same answer should be. CodeRabbit handles judgement calls.\n\nWhat we've set up covers most of it. Here are a few more tools that can help you keep even tighter control over your repo:\n\nEvery layer follows the same idea: **move each rule to the most reliable tool that can enforce it.**\n\nAI can write as much code as it likes. It just has to pass the same checks as everyone else. That's how you stay in control.", "url": "https://wpnews.pro/news/defending-your-codebase-from-ai-slop-guardrails-that-keep-you-in-control", "canonical_source": "https://dev.to/snikidev/defending-your-codebase-from-ai-slop-guardrails-that-keep-you-in-control-1b73", "published_at": "2026-10-08 19:28:53+00:00", "updated_at": "2026-10-08 19:49:19.360570+00:00", "lang": "en", "topics": ["ai-agents", "developer-tools", "ai-tools", "mlops"], "entities": ["dependency-cruiser", "Semgrep", "Biome", "ESLint"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/defending-your-codebase-from-ai-slop-guardrails-that-keep-you-in-control", "markdown": "https://wpnews.pro/news/defending-your-codebase-from-ai-slop-guardrails-that-keep-you-in-control.md", "text": "https://wpnews.pro/news/defending-your-codebase-from-ai-slop-guardrails-that-keep-you-in-control.txt", "jsonld": "https://wpnews.pro/news/defending-your-codebase-from-ai-slop-guardrails-that-keep-you-in-control.jsonld"}}