Playbook Fundamentals

Coding Practices Playbook

Core development principles for consistent, maintainable code. Give this to your AI agent to establish coding standards.

# agent_prompt

Copy this prompt and give it to your AI agent to establish these practices:

"Follow these coding practices for all code you write:

1. DRY - Don't repeat yourself. Extract common functionality.

2. KISS - Keep it simple. Prefer clarity over cleverness.

3. Single Responsibility - Each function/class does one thing well.

4. Fail fast - Validate inputs early, throw clear errors.

5. No magic numbers - Use named constants.

6. Guard clauses - Handle edge cases early and return.

7. Descriptive names - Variables and functions should be self-documenting."

# dry_principle

Don't Repeat Yourself. Every piece of knowledge should have a single, unambiguous representation in the system.

❌ Anti-pattern

function validateEmail(email) {
  const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
  return regex.test(email);
}

function checkUserEmail(email) {
  const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
  return regex.test(email);
}

✅ Better

const EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;

function isValidEmail(email) {
  return EMAIL_REGEX.test(email);
}

// Reuse isValidEmail wherever needed

# kiss_principle

Keep It Simple, Stupid. The simplest solution that works is usually the best. Avoid over-engineering.

❌ Over-engineered

class UserNameFormatter {
  constructor(strategy) {
    this.strategy = strategy;
  }
  format(user) {
    return this.strategy.format(user);
  }
}
// Plus 3 strategy classes...

✅ Simple

function formatUserName(user) {
  return `${user.firstName} ${user.lastName}`;
}

// Add complexity only when needed

# solid_principles

S — Single Responsibility

A class/function should have only one reason to change.

O — Open/Closed

Open for extension, closed for modification. Use composition.

L — Liskov Substitution

Subtypes must be substitutable for their base types.

I — Interface Segregation

Many specific interfaces beat one general-purpose interface.

D — Dependency Inversion

Depend on abstractions, not concretions. Inject dependencies.

# error_handling

Fail fast, fail explicitly. Validate early, provide context in errors, and never swallow exceptions silently.

async function processOrder(orderId) {
  // Validate early (guard clause)
  if (!orderId) {
    throw new Error('Order ID is required');
  }

  const order = await getOrder(orderId);

  if (!order) {
    throw new Error(`Order not found: ${orderId}`);
  }

  // Happy path continues...
  return processValidOrder(order);
}
  • Guard clauses first: Handle invalid states at the top
  • Include context: What failed? What were the inputs?
  • Use custom errors: Create specific error types when needed
  • Log at boundaries: Log errors at API/system boundaries

# naming_conventions

Good names eliminate the need for comments. Code should read like prose.

❌ Unclear

const d = new Date();
const arr = users.filter(u => u.a);
function proc(x) { ... }

✅ Clear

const createdAt = new Date();
const activeUsers = users.filter(u => u.isActive);
function processPayment(order) { ... }

Booleans: Use is/has/can prefix (isActive, hasPermission)

Functions: Use verbs (get, set, create, delete, validate)

Arrays: Use plural nouns (users, orders, items)

Constants: SCREAMING_SNAKE_CASE for true constants

# code_organization

File Structure

  • • One component/class per file
  • • Group related files in directories
  • • Keep files under 300 lines (split if larger)
  • • Co-locate tests with implementation

Function Guidelines

  • • Functions under 30 lines (extract if larger)
  • • Maximum 3-4 parameters (use objects for more)
  • • Single level of abstraction per function
  • • Pure functions where possible (no side effects)

Import Order

// 1. Built-in modules
import fs from 'fs';

// 2. External packages
import express from 'express';

// 3. Internal modules (absolute paths)
import { db } from '@/lib/database';

// 4. Relative imports
import { helper } from './helper';

# testing_philosophy

Tests are documentation. They show how code is meant to be used.

Testing Pyramid

70% Unit tests → 20% Integration tests → 10% E2E tests

  • Test behavior, not implementation: Tests should survive refactoring
  • Arrange-Act-Assert: Clear test structure
  • One assertion per test: Tests should fail for one reason
  • Descriptive test names: "should return empty array when no users exist"
  • Test edge cases: Empty inputs, null values, boundaries