DEV Community

Cover image for Clean code didn't get less important in the AI age — it got more important
Ricardas Kauneckas
Ricardas Kauneckas

Posted on

Clean code didn't get less important in the AI age — it got more important

Most of the code in my day to day now flows through an agent before it lands in
a diff. You'd think that makes clean code less relevant: nobody's staring at
this line by line anymore. It's the opposite. The less humans read and write
code by hand, the more the shape of the codebase does the work a reviewer used
to do in their head.

Two ideas explain why, and they lead to a concrete practice.

Guidelines vs. guardrails

When you brief an agent (a CLAUDE.md, an AGENTS.md, a comment in the PR
description) you're writing guidelines. Implicit rules you hope it sticks to.
Keep functions small. No god classes. Extract instead of nesting. They work,
right up until the context fills up, the task gets gnarly, or the model just
decides a fourth level of nesting is fine today. Nothing enforces them. They're
a request, not a constraint.

Guardrails are the other half. Explicit rules a tool enforces, with an exit
code that fails the build. A linter, a type checker, a complexity gate in CI.
The agent doesn't need to remember the rule or agree with it. The pipeline
rejects the output and it gets fixed before anything ships.

Both mattered before LLMs wrote code too. But guidelines used to be backstopped
by a human reviewer who'd actually read the diff and push back. That backstop
is thinner now. Review is faster, diffs are bigger, and the reviewer is often
skimming code they didn't write and won't internalize the way the author would
have. Guardrails pick up that slack. If a rule actually matters, it belongs in
something that fails the build, not only in a paragraph the agent is
statistically likely to follow.

It's still for humans too

The usual case for low complexity is human: less to hold in your head, fewer
ways to misread a branch, easier to test. All of that applies to an agent just
as much. A function with a cognitive complexity of 70 is exactly as easy to
introduce a bug into whether the thing writing it has a brain or a context
window. Deep nesting burns tokens and attention the same way it burns a
reviewer's patience, and the failure mode is the same: an edge case gets missed
because the shape of the code hid it.

The part that's easy to miss is that a codebase also acts as reference material
for the next session. An agent asked to add a feature doesn't invent a style
from nothing, it pattern matches on what's already there, because that's the
most relevant context it has. A clean, consistent codebase means new code tends
to follow the same clean patterns. A messy one means the agent's best available
example of "how we do things here" is the mess, and it faithfully extends it.
Good code compounds into more good code. Bad code compounds into more bad code,
just faster than it used to, because now there's an eager pair of hands copying
whatever pattern is nearest.

That's the actual argument for guardrails over guidelines: a linter that fails
the build doesn't let the mess get far enough to become a template.

The tools, not just one kind

None of this is language specific, or even complexity specific. It applies to
every category of quality tool: formatters, type checkers, static analysis,
security scanners, complexity gates. The common thread is enforced, not
requested.

For cognitive and cyclomatic complexity there's gocognit
for Go and the sonarjs
ESLint rules for JS/TS, and for PHP I maintain a small one myself - phpcognit: a single
static binary written in Rust with no PHP runtime or Composer entry that scans large codebases
in a couple of seconds and supports baselining, so a legacy project can gate on
new violations without fixing everything that predates the tool.

For static analysis there's PHPStan for PHP and
mypy for Python. For formatting,
Prettier and gofmt are the cheapest guardrail there
is, with zero excuse not to run one. For security, Semgrep
covers the same principle at higher stakes.

If you're running agents against a codebase day to day, worth asking: what
guardrails are actually catching things for you, versus what's still living as
a hopeful paragraph in a prompt file?

LINKS:

https://github.com/ryckakas/phpcognit

Top comments (0)