---
name: coding-standards
description: "Use when reviewing code for quality, naming a class or function, choosing a design pattern, refactoring a code smell, or establishing coding conventions for a new project."
---

# Coding Standards

A comprehensive reference for writing clean, consistent, maintainable code across Python, TypeScript, and Go.

## When to Activate

- Reviewing a pull request for code quality
- Refactoring legacy or unclear code
- Naming a class, function, variable, or file
- Deciding which design pattern to apply
- Setting up coding standards for a new project
- Identifying code smells and their root causes

---

## Naming Conventions

| Construct | Python | TypeScript | Go |
|---|---|---|---|
| Variables | `snake_case` | `camelCase` | `camelCase` |
| Functions / methods | `snake_case` | `camelCase` | `PascalCase` (exported), `camelCase` (unexported) |
| Classes / types | `PascalCase` | `PascalCase` | `PascalCase` |
| Constants | `UPPER_SNAKE_CASE` | `UPPER_SNAKE_CASE` or `camelCase` | `PascalCase` (exported), `camelCase` (unexported) |
| Interfaces | N/A | `IFoo` (avoid) or just `Foo` (preferred) | `Foo` (no `I` prefix) |
| Files | `snake_case.py` | `kebab-case.ts` or `camelCase.ts` | `snake_case.go` |
| Test files | `test_*.py` | `*.test.ts` / `*.spec.ts` | `*_test.go` |

### Naming Heuristics

- Be specific: `user_registration_date` not `date`; `payment_amount_cents` not `amount`
- Avoid meaningless suffixes: `UserManager`, `DataHelper`, `Utils` — what does it manage/help/do?
- Boolean names: use `is_`, `has_`, `can_`, `should_` prefix: `is_active`, `has_permission`
- Functions: verb phrases — `calculate_tax()`, `send_email()`, `validate_schema()`
- Avoid abbreviations except universally understood ones (`id`, `url`, `http`, `db`)
- Length proportional to scope: loop variable `i` is fine; module-level variable needs a full name

```python
# BAD
def proc(d, f):
    temp = d * f
    return temp

# GOOD
def calculate_discounted_price(base_price: float, discount_factor: float) -> float:
    return base_price * discount_factor
```

---

## SOLID Principles

### S — Single Responsibility

A class should have one reason to change.

```typescript
// BAD — UserService does everything
class UserService {
  createUser(data: CreateUserDto) { /* ... */ }
  sendWelcomeEmail(user: User) { /* ... */ }  // email is a separate concern
  generateReport(users: User[]) { /* ... */ } // reporting is a separate concern
}

// GOOD — separated concerns
class UserService { createUser(data: CreateUserDto) { /* ... */ } }
class EmailService { sendWelcomeEmail(user: User) { /* ... */ } }
class UserReportService { generateReport(users: User[]) { /* ... */ } }
```

### O — Open/Closed

Open for extension, closed for modification.

```typescript
// BAD — must modify to add new discount type
function calculateDiscount(type: string, price: number): number {
  if (type === 'student') return price * 0.8;
  if (type === 'senior') return price * 0.75;
  // must add else-if here for each new type
}

// GOOD — extend by adding a new class
interface DiscountStrategy { apply(price: number): number; }
class StudentDiscount implements DiscountStrategy { apply(p: number) { return p * 0.8; } }
class SeniorDiscount implements DiscountStrategy { apply(p: number) { return p * 0.75; } }
```

### L — Liskov Substitution

Subtypes must be substitutable for their base type.

```typescript
// BAD — Square overrides setWidth/setHeight inconsistently, breaking Rectangle contract
class Rectangle {
  constructor(protected width: number, protected height: number) {}
  setWidth(w: number) { this.width = w; }
  setHeight(h: number) { this.height = h; }
  area() { return this.width * this.height; }
}
class Square extends Rectangle {
  setWidth(w: number) { this.width = this.height = w; }   // violates LSP
  setHeight(h: number) { this.width = this.height = h; }  // caller expects independent dims
}

// GOOD — model separately; share via interface if needed
interface Shape { area(): number; }
class Rectangle implements Shape {
  constructor(private w: number, private h: number) {}
  area() { return this.w * this.h; }
}
class Square implements Shape {
  constructor(private side: number) {}
  area() { return this.side * this.side; }
}
```

### I — Interface Segregation

Many specific interfaces are better than one general interface.

```typescript
// BAD — not all workers can eat or sleep
interface Worker { work(): void; eat(): void; sleep(): void; }

// GOOD — split by capability
interface Workable { work(): void; }
interface Feedable { eat(): void; }
interface Restable { sleep(): void; }

class HumanWorker implements Workable, Feedable, Restable {
  work() { /* ... */ }
  eat() { /* ... */ }
  sleep() { /* ... */ }
}
class RobotWorker implements Workable {
  work() { /* ... */ }
}
```

### D — Dependency Inversion

Depend on abstractions, not concretions.

```typescript
// BAD — tightly coupled to PostgresDB
class UserRepository {
  private db = new PostgresDB();  // concrete dependency
  find(id: string) { return this.db.query(/* ... */); }
}

// GOOD — depends on interface, injected
interface Database { query(sql: string, params: unknown[]): Promise<unknown[]>; }
class UserRepository {
  constructor(private db: Database) {}
  find(id: string) { return this.db.query('SELECT * FROM users WHERE id = $1', [id]); }
}
```

---

## Design Patterns Quick Reference

| Pattern | Category | One-line use case | When to avoid |
|---|---|---|---|
| Factory | Creational | Create objects without specifying concrete class | When you only have one type |
| Abstract Factory | Creational | Create families of related objects | Overkill for simple factories |
| Singleton | Creational | One shared instance (config, logger) | When it becomes global mutable state |
| Builder | Creational | Step-by-step construction of complex objects | Simple objects with few fields |
| Strategy | Behavioral | Swap algorithms at runtime | When you only have one algorithm |
| Observer | Behavioral | Notify dependents when state changes | When observers outlive the subject |
| Command | Behavioral | Encapsulate a request as an object (undo/redo) | Simple one-off calls |
| Template Method | Behavioral | Define skeleton, let subclasses fill steps | Deep inheritance hierarchies |
| Repository | Structural | Isolate data access from domain logic | When ORM already provides this |
| Adapter | Structural | Wrap incompatible interface | When both interfaces are yours (just fix one) |
| Decorator | Structural | Add behavior to objects dynamically | When subclassing is simpler |
| Facade | Structural | Simplified interface to complex subsystem | Over-used to hide bad design |

### Full Code Examples

#### Factory

```typescript
interface Logger { log(msg: string): void; }
class ConsoleLogger implements Logger { log(msg: string) { console.log(msg); } }
class FileLogger implements Logger { log(msg: string) { fs.appendFileSync('app.log', msg); } }

function createLogger(type: 'console' | 'file'): Logger {
  if (type === 'console') return new ConsoleLogger();
  return new FileLogger();
}

// Usage
const logger = createLogger('console');
logger.log('Server started');
```

#### Strategy

```typescript
interface SortStrategy { sort(data: number[]): number[]; }
class QuickSort implements SortStrategy { sort(d: number[]) { /* quicksort impl */ return d; } }
class MergeSort implements SortStrategy { sort(d: number[]) { /* mergesort impl */ return d; } }

class DataSorter {
  constructor(private strategy: SortStrategy) {}
  sort(data: number[]) { return this.strategy.sort(data); }
  setStrategy(strategy: SortStrategy) { this.strategy = strategy; }
}

// Swap strategy at runtime
const sorter = new DataSorter(new QuickSort());
sorter.setStrategy(new MergeSort());
sorter.sort([5, 3, 1, 4, 2]);
```

#### Observer

```typescript
type Listener<T> = (event: T) => void;

class EventEmitter<T> {
  private listeners: Listener<T>[] = [];
  subscribe(fn: Listener<T>) { this.listeners.push(fn); }
  unsubscribe(fn: Listener<T>) { this.listeners = this.listeners.filter(l => l !== fn); }
  emit(event: T) { this.listeners.forEach(fn => fn(event)); }
}

const orderEvents = new EventEmitter<{ orderId: string; status: string }>();
orderEvents.subscribe(({ orderId }) => sendConfirmationEmail(orderId));
orderEvents.subscribe(({ orderId }) => updateInventory(orderId));
orderEvents.emit({ orderId: '123', status: 'paid' });
```

#### Repository

```typescript
interface UserRepository {
  findById(id: string): Promise<User | null>;
  save(user: User): Promise<void>;
  delete(id: string): Promise<void>;
}

class PostgresUserRepository implements UserRepository {
  constructor(private db: Database) {}

  async findById(id: string): Promise<User | null> {
    const [row] = await this.db.query('SELECT * FROM users WHERE id = $1', [id]);
    return row ? User.fromRow(row) : null;
  }

  async save(user: User): Promise<void> {
    await this.db.query(
      'INSERT INTO users (id, email) VALUES ($1, $2) ON CONFLICT (id) DO UPDATE SET email = $2',
      [user.id, user.email]
    );
  }

  async delete(id: string): Promise<void> {
    await this.db.query('DELETE FROM users WHERE id = $1', [id]);
  }
}
```

---

## Code Smells Catalog

| Smell | Symptom | Refactoring |
|---|---|---|
| Long Method | Function > 30 lines | Extract Method |
| Long Class / God Class | Class > 300 lines or too many responsibilities | Extract Class |
| Data Clump | Same 3+ params appear together everywhere | Introduce Parameter Object |
| Primitive Obsession | Using strings/ints for domain concepts (money, email) | Introduce Value Object |
| Feature Envy | Method uses data from another class more than its own | Move Method |
| Shotgun Surgery | One change requires edits in many classes | Move Method/Field, consolidate |
| Divergent Change | Class changes for multiple unrelated reasons | Extract Class |
| Duplicate Code | Same logic in multiple places | Extract Method/Function |
| Magic Numbers | Unexplained literals in code | Replace with Named Constant |
| Switch/Match on Type | Repeated `instanceof` or type checks | Replace with Polymorphism |

### Smell Examples

```typescript
// BAD — Primitive Obsession: money is a raw number, email is a raw string
function chargeUser(userId: string, amount: number, email: string) { /* ... */ }

// GOOD — Value Objects carry their own validation and behaviour
class Money {
  constructor(readonly cents: number, readonly currency: 'USD' | 'EUR') {
    if (cents < 0) throw new Error('Money cannot be negative');
  }
}
class Email {
  constructor(readonly value: string) {
    if (!value.includes('@')) throw new Error('Invalid email');
  }
}
function chargeUser(userId: string, amount: Money, email: Email) { /* ... */ }
```

```typescript
// BAD — Magic Numbers
if (user.age >= 65) applyDiscount(price * 0.25);

// GOOD — Named Constants
const SENIOR_AGE_THRESHOLD = 65;
const SENIOR_DISCOUNT_RATE = 0.25;
if (user.age >= SENIOR_AGE_THRESHOLD) applyDiscount(price * SENIOR_DISCOUNT_RATE);
```

---

## Language-Specific Idioms

### Python

```python
# Dataclass instead of verbose __init__
from dataclasses import dataclass, field

@dataclass
class Order:
    id: str
    items: list[str] = field(default_factory=list)
    total: float = 0.0

# Context manager for resource management
with open('file.txt') as f:
    data = f.read()  # file auto-closed, even on exception

# Generator for memory-efficient processing
def process_large_file(path: str):
    with open(path) as f:
        for line in f:           # reads line-by-line, not all into memory
            yield parse(line)

# Walrus operator for readability
if (match := pattern.search(text)) is not None:
    print(match.group(0))

# BAD — unpacking ignored with throwaway names
result = get_user_and_role()
user = result[0]
role = result[1]

# GOOD — structured unpacking
user, role = get_user_and_role()
```

### TypeScript

```typescript
// Discriminated union — exhaustive type narrowing
type Result<T> =
  | { success: true; data: T }
  | { success: false; error: string };

function handleResult<T>(result: Result<T>) {
  if (result.success) {
    console.log(result.data);    // TypeScript knows this is T
  } else {
    console.error(result.error); // TypeScript knows this is string
  }
}

// satisfies — validate type without widening
const config = {
  host: 'localhost',
  port: 5432,
} satisfies Record<string, string | number>;
// config.host is still `string` (not widened to `string | number`)

// Utility types
type PartialUser         = Partial<User>;                  // all fields optional
type ReadonlyUser        = Readonly<User>;                  // all fields readonly
type UserPreview         = Pick<User, 'id' | 'name'>;       // subset of fields
type UserWithoutPassword = Omit<User, 'password'>;          // exclude field

// BAD — casting away the type
const user = response.data as any;

// GOOD — parse and validate at the boundary
import { z } from 'zod';
const UserSchema = z.object({ id: z.string(), name: z.string() });
const user = UserSchema.parse(response.data);
```

### Go

```go
// Functional options pattern — flexible, forward-compatible constructors
type Server struct {
    host    string
    port    int
    timeout time.Duration
}

type Option func(*Server)

func WithTimeout(d time.Duration) Option {
    return func(s *Server) { s.timeout = d }
}

func NewServer(host string, port int, opts ...Option) *Server {
    s := &Server{host: host, port: port, timeout: 30 * time.Second}
    for _, opt := range opts {
        opt(s)
    }
    return s
}

// Usage
srv := NewServer("localhost", 8080, WithTimeout(5*time.Second))

// Error wrapping — preserve context, enable errors.Is / errors.As
if err := db.Query(); err != nil {
    return fmt.Errorf("fetching user %s: %w", userID, err)
}

// Table-driven tests — idiomatic Go
func TestAdd(t *testing.T) {
    cases := []struct{ a, b, want int }{
        {1, 2, 3},
        {0, 0, 0},
        {-1, 1, 0},
    }
    for _, tc := range cases {
        t.Run(fmt.Sprintf("%d+%d", tc.a, tc.b), func(t *testing.T) {
            got := Add(tc.a, tc.b)
            if got != tc.want {
                t.Errorf("got %d, want %d", got, tc.want)
            }
        })
    }
}
```

---

## Red Flags

- **God class with `Manager`, `Helper`, or `Utils` suffix** — a class named `UserManager` with 15 methods is a single-responsibility violation waiting to be split; name by what it does, not that it manages things
- **Boolean parameter flags controlling fundamentally different code paths** — `send_email(user, urgent=True)` means two functions in disguise; split into `send_urgent_email` and `send_email` with distinct call sites
- **Deep inheritance hierarchies (more than 2 levels)** — subclasses become entangled with grandparent internals; prefer composition and interfaces, using inheritance only for true IS-A relationships
- **Returning `None` on error instead of raising an exception or returning a typed Result** — callers silently ignore `None` and propagate nulls deep into the call stack before crashing; be explicit about failure
- **Catching a broad exception to log-and-swallow** — `except Exception: logger.error(...)` hides failures silently; catch the specific exception type and let unexpected ones propagate
- **`any` type in TypeScript at a module boundary** — `any` disables type checking for every caller downstream; use `unknown` and narrow explicitly, or define a proper interface
- **Mutable default arguments in Python** — `def process(items=[])` shares the same list across all calls; use `def process(items=None)` and initialize inside the function body
- **Long positional argument lists (more than 3 parameters)** — hard to read at call sites and easy to transpose; introduce a parameter object or dataclass

## Checklist

- [ ] Naming follows language convention (`snake_case` / `camelCase` / `PascalCase` per language)
- [ ] Functions have verb-phrase names; booleans use `is_` / `has_` / `can_` prefix
- [ ] Each function has a single, clear responsibility (< 30 lines as a guide)
- [ ] No magic numbers — named constants used for all literal values
- [ ] Dependencies injected (not instantiated inside the class)
- [ ] Design pattern applied only where it genuinely simplifies the code
- [ ] Code smells reviewed: no God Classes, Primitive Obsession, or Data Clumps
- [ ] Language-specific idioms used (dataclasses, discriminated unions, functional options)
- [ ] No duplicate logic — extracted into shared function or module
- [ ] PR reviewer can understand code intent without asking the author
