Application Architecture

Naming and Code Conventions — End Debates with Automation

Naming and Code Conventions — End Debates with Automation

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;
PrincipleSubstance
Express intentdaysUntilExpiration, not d
Don’t lieList<T> whose actual content is Set<T> is wrong
Avoid misunderstandingAvoid vague names like process()
PronounceablegenerationTimestamp, not genymdhms
SearchableLimit 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.

CaseUse
camelCaseVariables / functions in JS / TS / Java / Kotlin
PascalCaseClasses / types
snake_casePython / Ruby / DB columns
kebab-caseFile names / URLs
SCREAMING_SNAKE_CASEConstants

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.”

TargetPatternExample
FunctionVerb + objectcreateUser, validateEmail
booleanis / has / can prefixisActive, hasPermission
ClassNounOrderService, UserRepository
InterfaceNounUserRepository or IUserRepository (preference)
EventPast tenseUserRegistered, OrderPlaced
ConstantAll caps, intent-expressingMAX_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.”

LanguageLinterFormatter
JS / TSESLint / BiomePrettier / Biome
PythonRuff / PylintBlack / Ruff
Gogolangci-lintgofmt
RustClippyrustfmt
JavaCheckstyle / SpotBugsgoogle-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.

WriteDon’t
WHY (reasons, background)WHAT (read the code)
Non-obvious constraints / workaroundsNames already convey
External-spec dependenciesWork 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.

PhaseToolsCheck timingTarget time
1 pre-commitBiome / Ruff / gofmt (Formatter)At commit (local)< 5s
2 pre-pushESLint / Clippy (Lint) + type checkBefore push< 30s
3 PRAll Lint + tests + coverageAt GitHub push< 10 min
4 CODEOWNERSForce team review on critical-file changeAt PR creationAuto
5 Pre-releasesemantic-release (Conventional Commits) for auto-versionAt mergeAuto

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 moveWhy
Same concept under multiple names (User / Member / Account / Customer)New people end up asking “which to fix?”
Vague terms like data / info / util / temp / mgrZero intent. temp getting persisted to production is a classic accident
Different linter configs across the teamPR diffs drown in formatting; substantive changes invisible
Fix linter warnings later in bulkPast 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 messagesHistory search, changelog, semantic-release all break
Merge payment / auth / infra without CODEOWNERSUnknown people change sensitive code freely. Hotbed of accidents
4+ deep directory hierarchyLong import paths, IDE completion accuracy drops
Comments saying WHATBecomes a lie when code drifts. Write WHY
PR size 1000+ linesReviews become formality. Target ≤400, ideally 100
Treating naming as a minor problemNaming drift exponentially erodes team productivity over time; decide on day one
Starting debates about Formatter configFormatters 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 favorableAI-era unfavorable
ESLint / Ruff enforced in CIConventions only in README
Integrated tools like Biome / RuffDispersed config files
Machine-readable conventions like Conventional CommitsFree-form commit messages
CODEOWNERS protecting critical areasAnyone can merge
  1. Names express intent (name > comment > documentation).
  2. Linter / Formatter enforced in CI (automation over debate).
  3. Make conventions machine-readable (CODEOWNERS / Conventional Commits).
  4. 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)