Vasu Patel logoVasu Patel
← Clean by Default: Coding Standards
All chapters in this track
  1. 01Chapter 1: Why Coding Standards Matter: Readability, Onboarding, Defects, Consistency at Scale
  2. 02Chapter 2: Naming Conventions
  3. 03Chapter 3: Formatting and Style
  4. 04Chapter 4: Comments and Self-Documenting Code
  5. 05Chapter 5: Error Handling Conventions
  6. 06Chapter 6: Code and Project Organization (Files, Modules, Packages)
  7. 07Chapter 7: Writing Testable Code
  8. 08Chapter 8: Commit Hygiene: Atomic Commits, Messages, PR-Sized Changes
  9. 09Chapter 9: Language-Specific Idioms and Anti-Patterns Across TypeScript and Python
  10. 10Chapter 10: Security-Conscious Basics: Input Validation, Secrets Handling, Injection Risks
  11. 11Chapter 11: Enforcing Standards Automatically: Linters, Formatters, CI Gates
  12. 12Chapter 12: Creating, Documenting, and Evolving a Team Style Guide (Exceptions and Legacy Code)
  13. 13Chapter 13: Worked Example: Refactoring a Messy Function to Standard
  14. 14Chapter 14: Worked Example: Drafting a Style Guide Section From Scratch
  15. 15Chapter 15: Common Interview Questions About Coding Standards and Code Quality

Chapter 14 of 15

Chapter 14: Worked Example: Drafting a Style Guide Section From Scratch

Why this matters

Chapter 12 covered the principles behind building a style guide; this chapter applies them concretely by drafting one real section end to end — "How we handle errors" — the way a strong candidate or a team lead would actually approach the task, showing the reasoning at each step rather than just presenting a finished document.

The prompt

"Your team has no documented convention for error handling, and inconsistency is causing production incidents. Draft a style guide section addressing this."

Step 1: identify the actual problems, before writing any rules

[SENIOR INSIGHT] A strong first move is not writing rules immediately — it's grounding the eventual rules in real, specific problems the team has actually experienced (directly Chapter 12's "document the why" principle, applied at the moment of creating a rule, not just recording it afterward):

Observed problems (gathered from recent incident reviews):
1. Three separate empty catch blocks found in the payment module, discovered
   only after a customer complaint (no logs, no traces) -- INC-4521.
2. Inconsistent error shapes across API endpoints: some return {error: "msg"},
   others return {message: "msg"}, others throw raw strings -- frontend code
   has grown defensive, inconsistent handling for each shape individually.
3. A third-party API's temporary outage crashed the ENTIRE checkout flow,
   including unrelated functionality (order history display) that happened
   to share a request handler with the failing call.

Step 2: draft rules that directly address each observed problem

# Error Handling Standards

## Why this section exists

Recent incidents (INC-4521, and repeated frontend defensive-coding patterns)
traced back to inconsistent, ad hoc error handling. This section establishes
a single, consistent approach to prevent recurrence.

## Rule 1: Never leave a catch block without at least a log statement

Every caught exception must be logged with enough context (relevant IDs,
operation name) to investigate later, even if no other handling is possible
at that point in the code.

BAD:
    try { await processPayment(order); } catch (e) {}

GOOD:
    try {
      await processPayment(order);
    } catch (e) {
      logger.error("Payment processing failed", { orderId: order.id, error: e });
      throw e;
    }

## Rule 2: Use our standard error response shape for all API endpoints

All API error responses must use this exact shape:
    { "error": { "code": string, "message": string } }

This replaces all prior inconsistent shapes ({error: "msg"}, {message: "msg"},
raw strings). New endpoints must use `ApiError` (see `src/errors/ApiError.ts`)
to construct error responses; do not hand-construct error response objects.

## Rule 3: Distinguish core operations from optional enhancements explicitly

Before writing a try/catch, explicitly classify the operation:
- CORE (the request cannot succeed meaningfully without it): let the error
  propagate (fail fast) -- do not catch it locally.
- OPTIONAL (a degraded experience is acceptable): catch, log at WARN level,
  and continue with a sensible fallback/default.

Comment your classification at the catch site if it's not obvious from
context, e.g.: `// OPTIONAL: recommendations are a nice-to-have, checkout
must proceed without them if this fails`

Step 3: address enforcement — how much of this can be automated?

[IN PRACTICE] Directly applying Chapter 11's automation principle: after drafting the rules, explicitly identify which are mechanically enforceable and add tooling, rather than relying purely on code review to catch violations:

## Enforcement

- Rule 1 (no empty catch blocks) is enforced automatically via ESLint's
  `no-empty` rule, configured as a blocking CI error.
- Rule 2 (standard error shape) is enforced via a shared `ApiError` type;
  a custom lint rule flags any hand-constructed error response object
  bypassing it.
- Rule 3 (fail-fast vs. fail-gracefully classification) requires human
  judgment and is a standard code review checklist item, not automatable.

Step 4: anticipate and document known exceptions upfront

[SENIOR INSIGHT] A mature style guide section anticipates that some existing code won't fit the new rule immediately, and states the legacy-code policy explicitly (Chapter 12) rather than leaving it ambiguous and inviting a wave of "does this apply to me?" questions:

## Legacy code

These rules apply in full to all new code. Existing code is not required
to be retroactively updated, EXCEPT:
- Any code being otherwise modified should have its error handling brought
  into compliance as part of that change.
- The payment module specifically (source of INC-4521) is being audited
  and updated as a dedicated priority, tracked in PROJ-891, given its
  demonstrated incident history.

Common mistakes

  1. Writing style guide rules in the abstract, disconnected from real, observed problems — a rule with no grounding in an actual incident or pain point is harder to justify and easier to dismiss as arbitrary.
  2. Not specifying enforcement mechanism per rule — leaving every rule to "please follow this in code review" wastes the automation opportunity Chapter 11 covers, for the subset of rules that are actually mechanically checkable.
  3. Forgetting to address legacy code explicitly, leaving the rule's scope ambiguous and inviting inconsistent, case-by-case interpretation across the team.
  4. Drafting overly broad or vague rules ("handle errors appropriately") that don't give an engineer a clear, actionable rule to follow — every rule should be specific enough that a reader can tell, unambiguously, whether a given piece of code complies.

Discussion / interview questions

  1. "Walk through how you would draft a new style guide section for a problem your team is actually experiencing." Strong answer: Starts by grounding the rules in specific, observed problems (real incidents or pain points) rather than writing abstract rules first, then drafts rules that directly address each identified problem, then specifies enforcement mechanisms and a legacy-code policy explicitly.

  2. "How would you decide which parts of a new style guide rule to automate versus enforce via code review?" Strong answer: Identify which parts are mechanically checkable (a specific error shape, an empty catch block) and add linting/tooling for those; reserve code review specifically for the judgment-requiring parts (like classifying an operation as core vs. optional) that automation can't evaluate.

  3. "Why is it important to explicitly address how a new rule applies to existing/legacy code when introducing it?" Strong answer: Without an explicit policy, the rule's scope is ambiguous, inviting inconsistent case-by-case interpretation and either blocking unrelated work (if full retroactive compliance is implied) or being silently ignored (if no guidance is given) — an explicit boundary-based policy (Chapter 12) avoids both failure modes.

  4. "What makes a style guide rule 'actionable' versus vague?" Strong answer: An actionable rule lets a reader unambiguously determine whether a specific piece of code complies (a concrete before/after example, a specific required shape or utility to use); a vague rule ("handle errors appropriately") provides no clear standard to check against, making it useless for both self-review and code review.

Chapter summary / cheat sheet

  • Ground new style guide rules in specific, observed problems (real incidents, real pain points) rather than writing abstract rules disconnected from actual experience.
  • Draft rules with concrete before/after examples specific enough that compliance is unambiguous.
  • Specify an enforcement mechanism per rule — automate what's mechanically checkable, and explicitly designate what remains a human code-review responsibility.
  • Address legacy code scope explicitly (new code fully complies, modified code complies for touched lines, untouched legacy is left alone except where a specific incident history justifies a dedicated remediation effort).