About this article
This article is the third deep dive in the “Application Architecture” category of the Architecture Crash Course for the Generative-AI Era series, covering naming and code conventions.
Reading time overwhelmingly exceeds writing time on code; readability is team productivity itself. The article covers naming principles, case conventions, linter/formatter, directory layout, PR review, CODEOWNERS, Git commit conventions — the whole picture of “end debates with automation.”
What are naming and code conventions in the first place
Naming and code conventions are, in a nutshell, “a rulebook for unifying how a team writes code.”
Think of traffic rules. Because signal colors, lane rules, and sign meanings are standardized, you can drive on an unfamiliar road without getting lost. If rules varied by region, just passing another car would cause an accident. Code works the same way: when variable naming, indentation, and file structure are unified, anyone can immediately read anyone else’s code.
Why naming and code conventions matter
What happens if a team develops without conventions? Every review devolves into non-essential debates: “camelCase or snake_case for variable names?”, “indentation,” “brace position.” I’ve seen teams where every PR sparked 30+ minutes of comment rallies — the week after introducing Prettier, those debates vanished. Conventions alone raise productivity.
Reading time overwhelmingly exceeds writing time on code; readability is team productivity itself.
Naming basics
Good naming shares principles. Robert C. Martin’s Clean Code organizes them: just observing “intent clear, doesn’t lie, pronounceable, searchable” changes readability sharply.
flowchart TB
NAME([Naming check])
INTENT[Express intent<br/>d → daysUntilExpiration]
NOLIE[Don't lie<br/>List<T> holding Set<T> = NG]
NOAMBI[Avoid ambiguity<br/>process() is vague]
SAY[Pronounceable<br/>genymdhms → generationTimestamp]
SEARCH[Searchable<br/>1-letter vars only locally]
GOOD([Readability up<br/>Comments mostly unneeded])
NAME --> INTENT --> GOOD
NAME --> NOLIE --> GOOD
NAME --> NOAMBI --> GOOD
NAME --> SAY --> GOOD
NAME --> SEARCH --> GOOD
classDef root fill:#fef3c7,stroke:#d97706;
classDef rule fill:#dbeafe,stroke:#2563eb;
classDef goal fill:#dcfce7,stroke:#16a34a;
class NAME root;
class INTENT,NOLIE,NOAMBI,SAY,SEARCH rule;
class GOOD goal;
| Principle | Substance |
|---|---|
| Express intent | daysUntilExpiration, not d |
| Don’t lie | List<T> whose actual content is Set<T> is wrong |
| Avoid misunderstanding | Avoid vague names like process() |
| Pronounceable | generationTimestamp, not genymdhms |
| Searchable | Limit 1-letter variables to loop counters |
Naming is the largest documentation in code. With good names, comments become mostly unnecessary.
Naming conventions (case usage)
Whether to write variables, functions, classes, and file names in which case has de facto standards per language. Writing user_name for a JavaScript variable or user_profile for a Python class — even when grammatically valid — produces unease and cognitive load. Language conventions are accumulated community agreement; respecting them maps directly to readability.
Case unification works because consistency lets you judge “what is this” instantly from form. PascalCase = type, camelCase = variable or function, SCREAMING_SNAKE_CASE = constant, recognizable at a glance — speeding read time itself.
| Case | Use |
|---|---|
| camelCase | Variables / functions in JS / TS / Java / Kotlin |
| PascalCase | Classes / types |
| snake_case | Python / Ruby / DB columns |
| kebab-case | File names / URLs |
| SCREAMING_SNAKE_CASE | Constants |
Match the language convention. Absolutely avoid mixing within the same language.
Per-target naming patterns
Each target (variable, function, class) has gold-standard naming patterns. Following them lets readers instantly judge “what is this.”
| Target | Pattern | Example |
|---|---|---|
| Function | Verb + object | createUser, validateEmail |
| boolean | is / has / can prefix | isActive, hasPermission |
| Class | Noun | OrderService, UserRepository |
| Interface | Noun | UserRepository or IUserRepository (preference) |
| Event | Past tense | UserRegistered, OrderPlaced |
| Constant | All caps, intent-expressing | MAX_RETRY_COUNT |
Events are past tense as the rule. UserRegistered instead of Register makes “what happened” clear.
Names to avoid
The following naming drops codebase quality; flag in review without exception:
❌ data, info, util ← Conveys nothing
❌ temp, tmp ← Trap of being persisted as-is
❌ mgr, mng, svc ← Excessive abbreviation
❌ foo, bar ← Don't use outside test code
❌ getUserList vs getUsers ← Synonym mixing
❌ newUser, oldUser ← Meaningless once time passes
When using abbreviations, build a project-specific abbreviation dictionary (user = u forbidden, identifier = id OK, etc.) and standardize. Sharing a forbidden list in the team makes review-time pointing easier.
”User / Member / Account / Customer problem” (industry case)
A large e-commerce project: same customer info handled across User / Member / Account / Customer — four names mixed. Search APIs were findUser / getMember / searchAccount, three lineages “hitting the same table.” New engineers had to ask seniors “which one to fix for this requirement” every time.
Even mid-career 3rd-year engineers asking “how do User and Customer differ?” and nobody being able to answer clearly is common. The cause is simple: Ubiquitous Language (the team-wide common vocabulary used with one meaning) wasn’t defined initially. Each feature team wrote separately, slamming domain terms together; later joiners spent more “time building the dictionary in their heads.”
Conventions are ultimately “the work of building this dictionary upfront and sharing.” One name per concept. Build the dictionary before writing code.
Linter and Formatter
A linter detects quality issues; a formatter auto-fixes appearance. They forcibly end debates about “tabs vs spaces” or “semicolons or not.”
| Language | Linter | Formatter |
|---|---|---|
| JS / TS | ESLint / Biome | Prettier / Biome |
| Python | Ruff / Pylint | Black / Ruff |
| Go | golangci-lint | gofmt |
| Rust | Clippy | rustfmt |
| Java | Checkstyle / SpotBugs | google-java-format |
The integrated, fast Biome / Ruff are the favorites. Enforce in CI and end the debates.
Formatter philosophy
Modern formatters like Prettier and gofmt design “deliberately few” configurable items. The empirical rule: “the more configurable items, the more new debates.” The design crushes the room for argument.
❌ Tab faction vs space faction debates every time
✅ Decided by Prettier / gofmt — debate ended
The moment the team agrees “follow this formatter,” style debates almost disappear. The goal is zero debate cost; visual preference is secondary.
The tool decision is enough at “nobody is 100% satisfied but everyone tolerates.”
Directory layout
Directory layout splits broadly into layer-type and feature-type. Suitability changes with scale and team workflow.
[Layer] [Feature]
src/ src/
├─ controllers/ ├─ users/
├─ services/ │ ├─ controller.ts
├─ repositories/ │ ├─ service.ts
├─ models/ │ └─ repository.ts
└─ orders/
├─ ...
- Small / many beginners -> Layer (clear layer roles)
- Mid-large / multi-team parallel dev -> Feature (code grouped per feature, easier parallel)
Feature-type works as “a precursor to modular monolith or microservicing.”
Comment principles
Comments are for “WHY”, not “WHAT.” Writing what names already convey just creates confusion when code and comment diverge.
| Write | Don’t |
|---|---|
| WHY (reasons, background) | WHAT (read the code) |
| Non-obvious constraints / workarounds | Names already convey |
| External-spec dependencies | Work history, person names |
| TODO (with deadline) | Old code (let Git handle) |
❌ // increment i by 1
✅ // place a sentinel at the end, so +1 (legacy API constraint)
Comments rot. Comments left without updates become lies. “The best comment is the one you didn’t need to write.”
PR review principles
PR review is the place to protect quality and the team’s learning ground. “Review culture decides team technical strength” — that strong an effect.
- Run as a learning place, not a blame place.
- Comment on code (don’t criticize the person).
- Small PRs produce fast reviews (target ≤400 lines).
- Quick response (within 24 hours) prevents stagnation.
- Make Nit / Suggestion / Blocker explicit.
Labeling comments as “Must / Should / Nit” clearly conveys reviewer intent.
Guardrails via CODEOWNERS
Critical files (payment, infrastructure, security) require team-owner review before merge. GitHub’s CODEOWNERS automatically routes review requests when specific files change.
# .github/CODEOWNERS
/src/payment/ @payment-team
/infra/ @sre-team
/.github/ @admin-team
/src/auth/ @security-team
You can prevent unknown people from merging sensitive or high-difficulty code on their own. Combined with merge-protection rules, ensures critical-area reviews happen.
For large projects, no CODEOWNERS is a hotbed of accidents. Set up early.
Git commit-message convention
Unifying commit-message format across the team makes history-search and changelog generation easy. Conventional Commits is the de facto standard.
feat: add user-search API
fix: fix session-fixation vulnerability at login
docs: add env-var description to README
refactor: split UserService (responsibility overload)
chore: update dependencies
Adding prefixes (feat / fix / docs / refactor / chore / test / perf, etc.) makes “what kind of commit” identifiable at a glance. Adopting Conventional Commits enables auto-versioning via semantic-release. The 2018 Angular-team-published convention is the de facto standard as of 2026.
Convention-rollout phased roadmap
“Conventions written in README are obeyed” is mostly fantasy. Mechanical CI enforcement is the staged rule.
| Phase | Tools | Check timing | Target time |
|---|---|---|---|
| 1 pre-commit | Biome / Ruff / gofmt (Formatter) | At commit (local) | < 5s |
| 2 pre-push | ESLint / Clippy (Lint) + type check | Before push | < 30s |
| 3 PR | All Lint + tests + coverage | At GitHub push | < 10 min |
| 4 CODEOWNERS | Force team review on critical-file change | At PR creation | Auto |
| 5 Pre-release | semantic-release (Conventional Commits) for auto-version | At merge | Auto |
Tools like Prettier / gofmt / Biome that “deliberately narrow settings” are preferred because their design philosophy prevents tab-vs-space debates from arising. Ruff is rapidly replacing Black + Flake8 + isort in the Python ecosystem with ruff format + ruff check.
Enforce conventions mechanically in CI. Verbal, README, “I’ll be careful” doesn’t work.
Naming / convention traps
Common naming and style failure patterns. All erode whole-team productivity.
| Forbidden move | Why |
|---|---|
| Same concept under multiple names (User / Member / Account / Customer) | New people end up asking “which to fix?” |
Vague terms like data / info / util / temp / mgr | Zero intent. temp getting persisted to production is a classic accident |
| Different linter configs across the team | PR diffs drown in formatting; substantive changes invisible |
| Fix linter warnings later in bulk | Past 100 warnings, nobody looks. Zero-maintenance is the realistic answer |
Decide abbreviations individually (usr / custmr) | Without project-specific dictionary, abbreviations become unreadable |
| Free-form commit messages | History search, changelog, semantic-release all break |
| Merge payment / auth / infra without CODEOWNERS | Unknown people change sensitive code freely. Hotbed of accidents |
| 4+ deep directory hierarchy | Long import paths, IDE completion accuracy drops |
| Comments saying WHAT | Becomes a lie when code drifts. Write WHY |
| PR size 1000+ lines | Reviews become formality. Target ≤400, ideally 100 |
| Treating naming as a minor problem | Naming drift exponentially erodes team productivity over time; decide on day one |
| Starting debates about Formatter config | Formatters exist to prevent debate; sufficient if everyone tolerates |
Conventions aren’t for arguing — leave them to tools. The Prettier vs tab-faction debate is technically over.
AI decision axes
| AI-era favorable | AI-era unfavorable |
|---|---|
| ESLint / Ruff enforced in CI | Conventions only in README |
| Integrated tools like Biome / Ruff | Dispersed config files |
| Machine-readable conventions like Conventional Commits | Free-form commit messages |
| CODEOWNERS protecting critical areas | Anyone can merge |
- Names express intent (name > comment > documentation).
- Linter / Formatter enforced in CI (automation over debate).
- Make conventions machine-readable (CODEOWNERS / Conventional Commits).
- Small PRs, fast reviews (≤400 lines, 24-hour SLA).
What you must decide — what’s your project’s answer?
Articulate your project’s answer in 1-2 sentences for each:
- Naming convention (case, prefix, forbidden terms)
- Linter / Formatter selection and configuration
- Directory layout (Layer / Feature)
- Comment policy
- PR-size guideline / review SLA
- CODEOWNERS operation
- Git commit-message convention (Conventional Commits, etc.)
Summary
This article covered naming and code conventions — naming principles, linter/formatter, PR review, CODEOWNERS, Conventional Commits.
End debates with automation; make conventions machine-readable. The 2026 realistic answer for code-convention operation including AI era.
The next article is the Application Architecture category’s final installment, error handling (Result type, Circuit Breaker, idempotency).
Back to series TOC -> ‘Architecture Crash Course for the Generative-AI Era’: How to Read This Book
I hope you’ll read the next article as well.
📚 Series: Architecture Crash Course for the Generative-AI Era (28/89)