Skip to content

Engineering Coding Standards

Status: Draft — customize and ratify with your team Scope: JavaScript/TypeScript, Go, Java, SQL Owner: [assign a team/role, e.g., Platform Eng or an Architecture Guild]


How to use this document

This is a starting template, not a finished policy. Since you mentioned your org already has existing conventions, treat this as a structure to reconcile against, not replace wholesale:

  1. Read through each section and mark: Keep as-is / Adjust / Conflicts with our current practice.
  2. Resolve conflicts explicitly rather than letting two standards coexist silently.
  3. Once ratified, standards should be enforced primarily through automated tooling (linters, formatters, CI gates), not manual review nagging — see Tooling & Enforcement.
  4. Revisit quarterly or when a new language/framework is adopted.

Part 1 — Principles That Apply to All Languages

1.1 Core priorities (in order)

  1. Correctness — it does what it's supposed to do.
  2. Readability — the next person (often future-you) can understand it in one pass.
  3. Consistency — it looks like the rest of the codebase, not like a personal side project.
  4. Simplicity — the simplest solution that meets requirements beats a clever one.
  5. Performance — optimize only where it's measured to matter.

1.2 Naming

  • Names should describe what, not how (getActiveUsers(), not loopUsersAndFilter()).
  • Avoid abbreviations unless they're domain-standard (id, url, http are fine; usrCnt is not).
  • Booleans read as questions: isValid, hasPermission, canEdit.
  • No single-letter names except conventional loop indices (i, j) or math contexts.

1.3 Comments & documentation

  • Comments explain why, not what — the code already says what.
  • Every public function/method/API gets a doc comment: purpose, params, return value, errors/exceptions raised.
  • Delete commented-out code before merging; git history is the archive, not the file.
  • TODO comments must include an owner or ticket reference: // TODO(jsmith): handle null case — TICKET-1234.

1.4 Error handling

  • Never silently swallow errors (empty catch blocks are a defect, not a shortcut).
  • Fail loudly in development, gracefully in production.
  • Distinguish between expected failure states (validation errors, not-found) and unexpected ones (bugs, outages) — they should be logged/handled differently.
  • Include enough context in error messages/logs to debug without reproducing locally.

1.5 Testing

  • New code ships with tests; bug fixes ship with a regression test that fails without the fix.
  • Target meaningful coverage of business logic, not a coverage-percentage number for its own sake.
  • Tests should be independent (no shared mutable state) and deterministic (no flaky sleeps/timing dependence).
  • Name tests by behavior: returns_404_when_user_not_found, not test1.

1.6 Security baseline

  • Never commit secrets, keys, or credentials — use a secrets manager / environment injection.
  • Validate and sanitize all external input (user input, API payloads, query params).
  • Use parameterized queries always (see SQL section) — no string-concatenated queries, ever.
  • Log security-relevant events; never log secrets, passwords, or full PII.

1.7 Version control

  • Small, focused commits — one logical change per commit.
  • Commit messages: imperative mood, explain why when not obvious. Fix race condition in session refresh not fixed bug.
  • Branch naming: type/short-description (e.g., feat/user-export, fix/null-session).
  • No direct commits to main/master — all changes via PR, even trivial ones (keeps history and review consistent).

1.8 Code review expectations

  • Author: PRs should be small enough to review in under ~20 minutes; explain context in the description, not just "what."
  • Reviewer: review for correctness and design first, nitpicks (style) second — and let the linter catch style so humans don't have to.
  • Comments should be actionable and kind: suggest, don't just criticize.
  • Nothing merges with failing CI, regardless of who approved it.

Part 2 — Language-Specific Standards

2.1 JavaScript / TypeScript

Formatting & linting

  • Prettier for formatting (no manual style debates), ESLint for correctness/style rules.
  • Base config: Airbnb or typescript-eslint/recommended — pick one, don't mix philosophies.
  • Enforce via pre-commit hook + CI check, not manual review.

Language use

  • Prefer TypeScript over plain JS for anything beyond a small script.
  • No any without a comment justifying it — prefer unknown + narrowing, or a proper type.
  • Strict mode on ("strict": true in tsconfig.json).
  • Prefer const over let; never var.
  • Prefer async/await over raw .then() chains for readability.
  • Avoid default exports for anything other than single-component files (React) — named exports make refactors/searches easier.

Structure

  • One component/class/major concept per file.
  • Group by feature/domain, not by file type (avoid global controllers/, models/, utils/ dumping grounds at scale).
  • Barrel files (index.ts re-exports) only where they meaningfully simplify imports — they can hide circular dependencies.

2.2 Go

Formatting & linting

  • gofmt/goimports is non-negotiable and non-debatable — run in a pre-commit hook and CI.
  • go vet and golangci-lint in CI.

Language use

  • Follow Effective Go idioms: short variable names in small scopes are fine and expected (i, err, ctx).
  • Errors are values: check them immediately, wrap with context (fmt.Errorf("fetching user: %w", err)), don't panic for expected failure paths.
  • Return early on error rather than nesting (guard clauses).
  • Interfaces are defined by the consumer, not the producer — keep them small (1–3 methods).
  • Avoid unnecessary abstraction/generics until a second concrete use case justifies it.

Structure

  • Package names: short, lowercase, no underscores (user, not user_service).
  • No package named utils or common — it becomes a dumping ground; name packages for what they do.
  • internal/ for code not meant to be imported by other modules.

2.3 Java

Formatting & linting

  • Google Java Style Guide (or your existing org standard — flag if different) enforced via Checkstyle/Spotless in CI.

Language use

  • Favor immutability: final fields, immutable collections where reasonable.
  • Use Optional<T> for return values that may be absent — never return null from a public API without strong justification.
  • Prefer composition over inheritance; avoid deep inheritance hierarchies.
  • Checked exceptions only for recoverable conditions the caller should explicitly handle; don't overuse them for internal errors.
  • Use dependency injection (constructor injection preferred) over static singletons.

Structure

  • Package by feature/domain (com.company.billing), not by layer (com.company.controllers) at scale.
  • One public top-level class per file, matching the filename.

2.4 SQL

Formatting

  • Keywords uppercase (SELECT, FROM, WHERE), identifiers lowercase.
  • One clause per line for anything beyond a trivial query; align JOIN/WHERE conditions for readability.
  • Consistent indentation (2 or 4 spaces — pick one org-wide).

Naming

  • Tables: plural, snake_case (user_accounts) — or singular, if that's your existing convention; just be consistent.
  • Columns: snake_case, no type prefixes (created_at, not dt_created).
  • Foreign keys: <singular_table>_id (user_id).
  • Primary key: id unless there's a compound-key reason not to.

Query practices

  • Never SELECT * in application code — name columns explicitly (schema drift safety + readability).
  • Always use parameterized queries/prepared statements — never string-concatenate user input into SQL.
  • Every foreign key and every column used in WHERE/JOIN/ORDER BY at scale should have a considered index — don't add indexes reflexively, but don't skip them either.
  • Migrations are additive and backward-compatible by default (avoid breaking changes that require app + DB to deploy in lockstep) — use expand/contract for renames or type changes.

Part 3 — Tooling & Enforcement

LanguageFormatterLinterCI Gate
JS/TSPrettierESLintfail build on lint/format errors
Gogofmt/goimportsgolangci-lint, go vetfail build on vet/lint errors
JavaSpotless/google-java-formatCheckstylefail build on style violations
SQLsqlfmt (or team convention)sqlfluff (optional)migration review required

Principle: anything a machine can check, a machine should check — humans review for logic, design, and intent, not brace placement.


Part 4 — Governance

  • Proposing changes: open a PR against this document; requires sign-off from [N] senior engineers or the owning guild.
  • Exceptions: documented inline in code with a comment explaining why the standard was deviated from, plus a linked ticket if it's meant to be temporary.
  • Review cadence: revisit quarterly, or when adopting a new major framework/language.

Open items to fill in before ratifying

  • [ ] Confirm which existing org conventions this should defer to (list conflicts found in step 1 of "How to use this document")
  • [ ] Pick specific linter configs (exact ESLint/Checkstyle/golangci-lint rule sets)
  • [ ] Assign document owner and change-approval group
  • [ ] Decide table/column naming convention if not already fixed org-wide (singular vs. plural tables)

Released under the ISC License.