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:
- Read through each section and mark: Keep as-is / Adjust / Conflicts with our current practice.
- Resolve conflicts explicitly rather than letting two standards coexist silently.
- Once ratified, standards should be enforced primarily through automated tooling (linters, formatters, CI gates), not manual review nagging — see Tooling & Enforcement.
- Revisit quarterly or when a new language/framework is adopted.
Part 1 — Principles That Apply to All Languages
1.1 Core priorities (in order)
- Correctness — it does what it's supposed to do.
- Readability — the next person (often future-you) can understand it in one pass.
- Consistency — it looks like the rest of the codebase, not like a personal side project.
- Simplicity — the simplest solution that meets requirements beats a clever one.
- Performance — optimize only where it's measured to matter.
1.2 Naming
- Names should describe what, not how (
getActiveUsers(), notloopUsersAndFilter()). - Avoid abbreviations unless they're domain-standard (
id,url,httpare fine;usrCntis 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.
TODOcomments 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, nottest1.
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 refreshnotfixed 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
anywithout a comment justifying it — preferunknown+ narrowing, or a proper type. - Strict mode on (
"strict": trueintsconfig.json). - Prefer
constoverlet; nevervar. - 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.tsre-exports) only where they meaningfully simplify imports — they can hide circular dependencies.
2.2 Go
Formatting & linting
gofmt/goimportsis non-negotiable and non-debatable — run in a pre-commit hook and CI.go vetandgolangci-lintin 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, notuser_service). - No package named
utilsorcommon— 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:
finalfields, immutable collections where reasonable. - Use
Optional<T>for return values that may be absent — never returnnullfrom 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/WHEREconditions 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, notdt_created). - Foreign keys:
<singular_table>_id(user_id). - Primary key:
idunless 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 BYat 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
| Language | Formatter | Linter | CI Gate |
|---|---|---|---|
| JS/TS | Prettier | ESLint | fail build on lint/format errors |
| Go | gofmt/goimports | golangci-lint, go vet | fail build on vet/lint errors |
| Java | Spotless/google-java-format | Checkstyle | fail build on style violations |
| SQL | sqlfmt (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)
