Bentley Rules

March 10, 2026 (3mo ago)

Here are some of the rules, which are organized into four categories: data structures, logic, loops, functions.

They are originally from the book Programming Pearls by Jon Bentley. We will be applying them to JS/TS.

Data Structures Rules

  1. Packing and Encoding

verbose object with full property names

const user = { isActive: true, accountType: 'premium' };

better: packed encoding using bit flags

enum UserFlags {
  Active = 1 << 0,
  Premium = 1 << 1,
}
const userFlags = UserFlags.Active | UserFlags.Premium;
  1. Augmentation

O(n) lookup in array for common operations

const users = [{ id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }];
const getUser = (id) => users.find(u => u.id === id);

better: augment with Map for O(1) lookups

const userMap = new Map(users.map(u => [u.id, u]));
const getUserFast = (id) => userMap.get(id);
  1. Precomputation & Caching

recalculating expensive data on each render (react)

function ExpensiveComponent({ data }) {
  const processed = heavyComputation(data);
  return <div>{processed}</div>;
}

better: precompute with useMemo

function ExpensiveComponent({ data }) {
  const processed = useMemo(() => heavyComputation(data), [data]);
  return <div>{processed}</div>;
}
  1. Sparsity

dense array for sparse data (wasted memory/iteration)

const matrix = Array(10000).fill(0);
matrix[0] = 1;
matrix[9999] = 1;

better: sparse representation using Map

const sparseMatrix = new Map();
sparseMatrix.set(0, 1);
sparseMatrix.set(9999, 1);

Logic Rules

  1. Short-Circuiting & Test Ordering

expensive call every time

if (user && user.address && validateAddressWithAPI(user.address)) { }

better: cheap checks first, expensive API call last

if (user?.address && await validateAddressWithAPI(user.address)) { }
  1. Creating a Fast Path

fast path for common case in (nest.js)

@Injectable()
export class AuthGuard {
  canActivate(context: ExecutionContext): boolean {
    const request = context.switchToHttp().getRequest();
    
    //fast path: public routes skip all auth logic
    if (this.isPublicRoute(request)) return true;
    
    //slow path: full authentication check
    return this.authenticate(request);
  }
}

Loops Rules

  1. Hoisting

array length accessed each iteration

for (let i = 0; i < array.length; i++) { }

better: length hoisted

const len = array.length;
for (let i = 0; i < len; i++) { }

also modern for-of hoists internally

for (const item of array) { }
  1. Loop Fusion (react/next.js)

two separate loops over same data

users.forEach(user => updateCache(user));
users.forEach(user => notifySubscribers(user));

better: single loop

users.forEach(user => {
  updateCache(user);
  notifySubscribers(user);
});

Functions Rules

  1. Inlining

tiny function with overhead

const isAdult = (age) => age >= 18;
if (isAdult(user.age)) { }

better: inline simple logic (V8 may inline automatically though, but helps readability)

if (user.age >= 18) { }
  1. Coarsening Recursion

recursion for every small chunk

function processChunk(data, index) {
  if (index >= data.length) return;
  doWork(data[index]);
  processChunk(data, index + 1);
}

better: coarsened: process in batches to reduce call overhead

function processBatch(data, start) {
  const end = Math.min(start + 100, data.length);
  for (let i = start; i < end; i++) doWork(data[i]);
  if (end < data.length) processBatch(data, end);
}

Framework-Specific Notes

  • react: useMemo, useCallback, React.memo implement caching and precomputation. Component tree structure affects pipeline/parallelism via concurrent rendering
  • next.js: static generation (SSG) is compile-time precomputation. Middleware allows request fast-paths. Caching headers implement broader caching rules
  • nest.js: guards and interceptors enable test ordering and fast-path patterns. Dependency injection supports augmentation (caching instances)