Code Hygiene in the Age of AI Pair-Programming: Lessons from a Real Codebase Review
- Get link
- X
- Other Apps
Why "hygiene" and not "quality"
Code quality is a big, fuzzy word. Code hygiene is the practical subset of it — the small, mechanical habits that keep a codebase healthy over time: consistent naming, sane file organization, dependency discipline, and the humble but critical CI gate that catches mistakes before they ship. None of it requires deep architectural genius. All of it requires consistency.
This matters more than ever now that a growing share of code is scaffolded by AI copilots and written by developers early in their careers. AI-assisted code is often locally excellent — a single function, a single file, will look clean and idiomatic. What it doesn't automatically give you is global consistency across a hundred files written over many sessions. That's still a human job.
I recently reviewed a FastAPI backend built almost entirely this way — a junior engineer, working with GitHub Copilot, building a conversational AI product. The architecture was genuinely impressive: clean layering, abstract interfaces for every external dependency, a security philosophy applied consistently across the codebase ("the server decides trust and state — never the client, never the LLM"). But a handful of hygiene gaps had crept in exactly where you'd expect: at the boundaries where no single file makes the problem visible. Here's what that taught me.
1. Naming conventions are a contract, not a style preference
Every language ecosystem has a default naming dialect, and mixing dialects inside one layer is where confusion starts.
| Convention | Example | Where it belongs |
|---|---|---|
| snake_case | get_current_state |
Python/Ruby functions & variables |
| PascalCase | ConversationState |
Classes, types, React/Vue components |
| UPPER_SNAKE_CASE | MAX_ANONYMOUS_TURNS |
Constants |
| kebab-case | docker-entrypoint.sh |
Shell scripts, URLs, CSS classes, CLI flags |
| camelCase | getUserId |
JS/TS/Java variables and functions |
The rule isn't "pick the best one" — it's pick the ecosystem's default and never deviate within a layer. A Python module full of snake_case functions with one stray getUserData() isn't a style choice, it's a signal that something was pasted in from a different context without review. Naming consistency is one of the cheapest things to enforce with a linter (ruff, eslint) and one of the most expensive things to fix retroactively once a hundred files are already inconsistent.
2. File and folder structure should be boring
The best compliment you can pay a repository's folder structure is that nobody notices it. Boring means:
- Folders: lowercase, no spaces, no mixed case — because Linux is case-sensitive and your CI probably runs on Linux even if your laptop doesn't.
- Python - should follow snake_case
- Anywhere else prefer kebab-case
- Files: match the ecosystem convention (snake_case for Python modules, PascalCase for one-component-per-file frontend code).
- Root-level meta files (
README.md,LICENSE,.env.example) follow their own long-standing convention — don't "improve" on it.
None of this is exciting, which is exactly the point. Structure should never be a place where a reviewer has to stop and think.
3. Automate what a human will forget to check by hand
This is the single highest-leverage hygiene investment a young codebase can make: a CI pipeline that runs, on every merge request:
- A linter/formatter (
ruff,black,eslint) — catches naming drift, unused imports, dead code. - A type checker (
mypy,pyright) — catches the "this function secretly returnsNonesometimes" class of bug. - A dependency vulnerability scan (
pip-audit,npm audit, Snyk/Dependabot) — catches known CVEs in third-party packages before they ship. - A secret scanner — catches an accidentally committed API key before it's public forever.
- A test coverage gate — not to chase 100%, but to stop the "good test suite" you have today from quietly eroding as the project grows past its first contributor.
The point isn't that any single one of these would have caught everything above — it's that a human reviewing code by hand will always miss the boundary cases, and a machine running the same check every single time never gets tired.
4. Consistency is a team memory problem, not an intelligence problem
The most telling detail in this review wasn't a bug — it was a pattern. The same well-reasoned architectural principle ("the server decides, never the client or the model") was written out, in slightly different words, as an inline comment in six different files. It was the right principle, applied correctly, every single time. It just wasn't written down once, in one place, for the next person (or the next AI session) to discover before reinventing it.
That's the real lesson for AI-assisted development: the model is very good at applying a pattern once it's shown one. It's not good at remembering, across sessions and files, that the pattern needs to be applied everywhere, or that it was already written down somewhere else. That's still the job of an architecture doc, a linter rule, or a human doing exactly the kind of side-by-side review this article is describing.
The takeaway
Good code hygiene isn't about writing clever code. It's about making the boring things — naming, structure, dependency pinning, CI gates — so consistent that they stop being interesting, which frees up review time for the things that actually deserve scrutiny: architecture, security boundaries, and business logic. That's true whether the code was written by a ten-year veteran or by a first-year engineer pairing with an AI assistant — the difference is just where the gaps tend to show up.
- Get link
- X
- Other Apps
Comments
Post a Comment