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 9 of 15

Chapter 9: Language-Specific Idioms and Anti-Patterns Across TypeScript and Python

Why this matters

Every principle covered so far in this book is language-agnostic, but how each principle is idiomatically expressed differs meaningfully between TypeScript and Python — and writing "translated" code (Python that reads like transliterated Java, or TypeScript that ignores its type system's guarantees) is a real, common code smell that experienced reviewers notice immediately. This chapter covers the idioms specific to each language, and the anti-patterns that come from not using them.

TypeScript idioms and anti-patterns

[BEGINNER] Idiom: let the type system do validation work.

// Anti-pattern: using `any`, discarding the entire point of a static type system
function processOrder(order: any): any {
  return order.total * 1.08;
}

// Idiomatic: precise types let the compiler catch entire categories of bugs before runtime
interface Order {
  total: number;
  taxRate: number;
}
function processOrder(order: Order): number {
  return order.total * (1 + order.taxRate);
}

Idiom: prefer union types and discriminated unions over loose booleans/strings for representing state.

// Anti-pattern: a string that could be any value, with no compile-time safety
function setStatus(status: string) { ... }
setStatus("pnding"); // typo -- compiles fine, fails silently at runtime

// Idiomatic: a union type makes invalid values a COMPILE-TIME error
type OrderStatus = "pending" | "shipped" | "delivered" | "cancelled";
function setStatus(status: OrderStatus) { ... }
setStatus("pnding"); // COMPILE ERROR -- caught before the code ever runs

Anti-pattern: overusing non-null assertions (!) to silence the compiler instead of handling null/undefined properly:

// Anti-pattern: `!` tells the compiler "trust me," discarding its safety check entirely
function getFirstItem(items: Item[] | null): Item {
  return items![0]; // if items is actually null, this throws at RUNTIME --
                      // exactly the bug the type system was trying to prevent
}

// Idiomatic: handle the null case explicitly, keeping the compiler's guarantee intact
function getFirstItem(items: Item[] | null): Item | undefined {
  return items?.[0];
}

Python idioms and anti-patterns

[BEGINNER] Idiom: use context managers (with) for resource management, not manual acquire/release.

# Anti-pattern: manual resource management -- easy to forget the close() on an exception path
f = open("data.txt")
data = f.read()
f.close()  # skipped entirely if f.read() raises an exception above it

# Idiomatic: `with` guarantees cleanup, even if an exception occurs inside the block
with open("data.txt") as f:
    data = f.read()
# f.close() is called automatically, unconditionally, when the block exits

Idiom: use list/dict comprehensions for simple transformations, but not at the expense of readability for complex logic.

# Idiomatic for SIMPLE transformations:
active_user_emails = [user.email for user in users if user.is_active]

# ANTI-PATTERN: cramming complex, multi-condition logic into a comprehension
# specifically because it's "more Pythonic" -- this is LESS readable, not more:
result = [transform(x) for x in items if condition_a(x) and condition_b(x) and not condition_c(x) if x.value > threshold]

# Idiomatic for COMPLEX logic: a regular loop is clearer, and just as "Pythonic"
result = []
for x in items:
    if condition_a(x) and condition_b(x) and not condition_c(x) and x.value > threshold:
        result.append(transform(x))

Anti-pattern: using a mutable default argument — one of Python's most infamous, specific footguns:

# ANTI-PATTERN: the default list is created ONCE, at function definition time,
# and SHARED across every call that doesn't pass its own list explicitly:
def add_item(item, items=[]):
    items.append(item)
    return items

add_item("a")  # returns ["a"]
add_item("b")  # returns ["a", "b"] -- the SAME list object, silently accumulating!

# Idiomatic: use None as the default, creating a fresh list inside the function
def add_item(item, items=None):
    if items is None:
        items = []
    items.append(item)
    return items

The meta-lesson: idioms exist because they encode hard-won, language-specific knowledge

[SENIOR INSIGHT] Every idiom in this chapter exists because engineers were repeatedly bitten by the anti-pattern it replaces — TypeScript's discriminated unions exist because loose string/boolean state representations caused real, recurring bugs; Python's mutable-default-argument warning exists because that specific footgun has surprised nearly every Python engineer at least once. This is exactly this book's opening chapter's point (standards encode accumulated, hard-won lessons) applied at the language-idiom level specifically — a code reviewer flagging "don't use a mutable default argument" isn't enforcing arbitrary taste, they're preventing a bug class with a well-documented history.

When NOT to force an idiom

[IN PRACTICE] Idioms should serve readability and correctness, not be applied reflexively as a badge of language fluency. A list comprehension crammed with four chained conditions "because comprehensions are Pythonic" is a worse outcome than a clear loop — the idiom's value (conciseness for simple cases) doesn't transfer to a case where conciseness comes at the cost of clarity. The same discipline from the OOP book's Chapter 14 (recognizing when a general principle stops applying) is relevant here: know why an idiom is usually better, so you can recognize the specific cases where it isn't.

Common mistakes

  1. Using any in TypeScript to avoid dealing with a typing challenge, discarding the compiler's entire safety net for that piece of code and reintroducing exactly the runtime error categories static typing exists to prevent.
  2. Using Python's mutable default argument pattern, unaware of the shared-state footgun — one of the most common, most surprising bugs new (and experienced) Python engineers encounter.
  3. Forcing a comprehension/idiom onto genuinely complex logic "to be more idiomatic," when a plain loop or explicit structure would be more readable — idioms serve readability, not the reverse.
  4. Not recognizing a language-specific anti-pattern in code review because it "runs fine" — many of these anti-patterns (mutable defaults, non-null assertions) work correctly in the common case and fail only under specific, sometimes rare conditions, exactly like the concurrency bugs from the companion Concurrency book.

Discussion / interview questions

  1. "Why is Python's mutable default argument considered a common footgun, and how do you avoid it?" Strong answer: A mutable default argument (like items=[]) is created once, at function definition time, and shared across every call that doesn't explicitly pass its own value — leading to silent, cross-call state accumulation; the fix is defaulting to None and creating a fresh mutable object inside the function body.

  2. "What's the risk of overusing TypeScript's any type or non-null assertion operator (!)?" Strong answer: Both discard the compiler's safety checks for that specific code — any disables type checking entirely, and ! asserts a value isn't null/undefined without verification, reintroducing exactly the runtime error categories (type mismatches, null reference errors) that TypeScript's type system exists to catch at compile time instead.

  3. "Is a list comprehension always more 'Pythonic' or preferable to a regular loop?" Strong answer: No — comprehensions are idiomatic for simple, single-condition transformations; cramming complex, multi-condition logic into a comprehension for its own sake often makes code less readable than an equivalent explicit loop, and readability should take priority over reflexively applying an idiom.

  4. "Why do language-specific idioms matter as a coding standard, beyond just 'writing more X-like code'?" Strong answer: Idioms encode accumulated, hard-won knowledge about specific bug classes and pitfalls particular to that language (discriminated unions preventing invalid-state bugs, with statements preventing resource leaks) — following them isn't about style conformity, it's about avoiding well-documented, recurring failure modes specific to that language's semantics.

Chapter summary / cheat sheet

  • TypeScript: let the type system validate for you (avoid any), use discriminated unions over loose strings/booleans for state, handle null/undefined explicitly rather than silencing the compiler with !.
  • Python: use with for resource management, use comprehensions for simple transformations only, never use a mutable default argument.
  • Every idiom in this chapter exists because its corresponding anti-pattern caused real, recurring, well-documented bugs — treating idioms as arbitrary style preference misses their actual purpose.
  • Idioms should serve readability and correctness — apply them where they genuinely help, and recognize when forcing one (a crammed comprehension) hurts more than it helps.