Asher Cohen
Back to posts

Engineering Guidelines: Patterns, Principles, and Philosophy

Team engineering standards covering clean code, SOLID, DRY, functional programming, dependency injection, and anti-patterns to avoid

Introduction

Consistent engineering practices are the foundation of maintainable codebases. These guidelines capture the patterns, principles, and philosophy that lead to better software — code that's easier to understand, test, and change.

Engineering Philosophy

Our approach to engineering is guided by a few core beliefs:

  • Code is read far more than it's written — Optimize for readability
  • Simple solutions scale better than clever ones — Prefer boring code
  • Tests are documentation — They describe expected behavior
  • Refactoring is continuous — Improve code every time you touch it
  • Convention over configuration — Consistency reduces cognitive load

Patterns to Follow

Clean Code

Write code that expresses intent clearly:

// ❌ Unclear intent
function process(d) {
  let r = [];
  for (let i = 0; i < d.length; i++) {
    if (d[i].s === 'a') r.push(d[i]);
  }
  return r;
}

// ✅ Clear intent
function getActiveUsers(users) {
  return users.filter(user => user.status === 'active');
}

SOLID Principles

  • S — Single Responsibility: One reason to change
  • O — Open-Closed: Extend without modifying
  • L — Liskov Substitution: Subtypes must be substitutable
  • I — Interface Segregation: Small, focused interfaces
  • D — Dependency Inversion: Depend on abstractions

DRY (Don't Repeat Yourself)

Every piece of knowledge has one authoritative representation:

// ❌ Duplicated validation
function registerUser(data) {
  if (!data.email?.includes('@')) throw new Error('Invalid email');
}
function updateUser(data) {
  if (!data.email?.includes('@')) throw new Error('Invalid email');
}

// ✅ Single source of truth
function validateEmail(email) {
  if (!email?.includes('@')) throw new Error('Invalid email');
}

Functional Programming

Prefer pure functions and immutable data:

// ❌ Impure — mutates input
function addItem(list, item) {
  list.push(item);
  return list;
}

// ✅ Pure — returns new array
function addItem(list, item) {
  return [...list, item];
}

Immutability

Never mutate shared state:

// ❌ Mutation
state.user.name = 'New Name';

// ✅ Immutable update
const newState = { ...state, user: { ...state.user, name: 'New Name' } };

Inversion of Control

Let the framework call your code, not the other way around:

// ❌ Your code controls flow
function main() {
  const data = fetchData();
  processData(data);
  renderUI(data);
}

// ✅ Framework controls flow
app.on('data:received', processData);
app.on('data:received', renderUI);
app.start(); // Framework calls your handlers

Dependency Injection

Pass dependencies in, don't create them internally:

// ❌ Hard-coded dependency
class UserService {
  constructor() {
    this.db = new MySQLDatabase(); // Tightly coupled
  }
}

// ✅ Injected dependency
class UserService {
  constructor(database) {
    this.db = database; // Loosely coupled, testable
  }
}

Anti-Patterns to Avoid

God Objects

// ❌ One class doing everything
class ApplicationManager {
  validateUser() { /* ... */ }
  saveToDatabase() { /* ... */ }
  sendEmails() { /* ... */ }
  generateReports() { /* ... */ }
  handleWebhooks() { /* ... */ }
}

// ✅ Split into focused modules
class UserValidator { /* ... */ }
class UserRepository { /* ... */ }
class EmailService { /* ... */ }

Magic Numbers and Strings

// ❌ Magic values
if (user.role === 1) { /* admin */ }
setTimeout(fn, 86400000); // What time is this?

// ✅ Named constants
const ROLES = { ADMIN: 1, USER: 2 };
const ONE_DAY_MS = 24 * 60 * 60 * 1000;

if (user.role === ROLES.ADMIN) { /* ... */ }
setTimeout(fn, ONE_DAY_MS);

Premature Optimization

// ❌ Optimizing before measuring
const cache = new Map(); // Added complexity without evidence it's needed

// ✅ Simple first, optimize when profiler shows bottleneck
const result = computeValue(input);

Code Review Checklist

Before merging, verify:

  • Code expresses intent clearly
  • Functions are small and focused
  • No duplicated logic
  • Errors are handled appropriately
  • Tests cover happy path and edge cases
  • No commented-out code
  • Naming is descriptive and consistent

Further Reading

  • Basics — JavaScript fundamentals (primitives, scope, functions)
  • Advanced — Closures, destructuring, modules, event loop
  • Patterns — Strategy, Factory, Adapter, Singleton, CQRS
  • Weird JS — Quirks and surprising behaviors to watch for
  • Future JS — Upcoming proposals and language features

Conclusion

These guidelines aren't rigid rules — they're patterns that have proven valuable across projects. Apply them thoughtfully:

  • When in doubt, keep it simple
  • Consistency matters more than perfection
  • Guidelines evolve — improve them as you learn

The goal is code that your teammates can read, understand, and modify with confidence.

#engineering #clean-code #solid #patterns #best-practices #software-development