Skip to content

Concepts

Personal lexicon — terms I've defined for myself.

Stateless A system or function that does not depend on or retain any memory between operations — each call is fully self-contained
Programming Web

Statelessness appears in two related contexts. In functional programming, a stateless (pure) function produces output based solely on its inputs — it has no side effects and does not rely on any external mutable state, making it predictable and easy to test. In web and distributed systems, a stateless service treats each request as independent: no session data is stored between requests. REST APIs embody this principle — every HTTP request must carry all the information needed to process it, including authentication credentials.

Examples

  • Pure function: add(a, b) => a + b always returns the same result for the same inputs
  • HTTP GET request: each request is self-contained with URL, headers, and auth token
  • REST API endpoint: no server-side session — auth token is sent with every request
  • Array transformations with map, filter, reduce — the input array is never mutated
Related:
Stateful A system or component that retains information across multiple operations or requests — past interactions influence future behavior
Programming Web

Stateful systems track context over time. In programming, a stateful object or component holds internal state that can be read and modified — its behavior depends not just on the current input but on its history. In web and distributed systems, a stateful service maintains session or connection data between requests, so the server 'remembers' who you are. Statefulness adds power and expressiveness but also introduces complexity: the system becomes harder to test, scale, and reason about, because outcomes depend on when and in what order operations happen.

Examples

  • A counter object that stores and increments its own value
  • React component using useState — re-renders reflect accumulated state changes
  • HTTP session/cookie: server remembers a logged-in user across requests
  • WebSocket connection: server maintains persistent, two-way channel per client
  • Database transaction: multiple operations share state until commit or rollback
Related:
Recursion A function that calls itself to solve smaller instances of the same problem
Programming Mathematics

Recursion is a powerful problem-solving technique where a function breaks down a problem into smaller, similar subproblems. Each recursive call works on a simpler version of the original problem until it reaches a base case—the simplest form that can be solved directly without further recursion. This approach is particularly elegant for problems with self-similar structure, such as traversing trees, calculating factorials, or processing nested data structures.

Examples

  • Calculating factorial: n! = n × (n-1)!
  • Traversing directory trees in a file system
  • Computing Fibonacci numbers: F(n) = F(n-1) + F(n-2)
  • Depth-first search in graphs
Related:
Base Case The simplest form of a recursive problem that can be solved directly
Programming Mathematics

In recursive algorithms, the base case is the condition that stops the recursion. Without a proper base case, a recursive function would call itself indefinitely, leading to a stack overflow. The base case represents the simplest version of the problem where the answer is known or trivial to compute.

Examples

  • In factorial: if n = 0, return 1
  • In Fibonacci: if n ≤ 1, return n
  • In tree traversal: if node is null, return
Related:
Iteration Repeating a process using loops rather than recursive calls
Programming

Iteration is an alternative to recursion that uses explicit loops (for, while) to repeat operations. While recursion uses the call stack to maintain state, iteration uses loop variables. Iteration is often more memory-efficient but can be less elegant for problems with naturally recursive structure.

Examples

  • Using a for loop to calculate factorial
  • While loop to traverse a linked list
  • Iterating through array elements
Related:
Emergence Complex patterns arising from simple interactions between parts of a system
Systems Thinking Complexity

Emergence describes how complex behaviors and patterns can arise from simple rules operating at a local level. The whole becomes more than the sum of its parts. Examples include consciousness emerging from neurons, traffic patterns emerging from individual driving decisions, or ant colonies exhibiting intelligent behavior despite individual ants following simple rules.

Examples

  • Flocking behavior of birds from simple rules
  • Conway's Game of Life creating complex patterns from simple cellular automaton rules
  • Market prices emerging from individual buy/sell decisions
  • Consciousness emerging from neural networks
Related:
Systems Thinking Understanding how parts of a system interact and influence each other over time
Philosophy Problem Solving

Systems thinking is an approach to problem-solving that views problems as parts of an overall system, rather than isolated events. It emphasizes understanding relationships, feedback loops, delays, and unintended consequences. This holistic perspective helps identify leverage points where small changes can produce significant effects.

Examples

  • Understanding climate as interconnected feedback loops
  • Analyzing business problems considering all stakeholders
  • Debugging software by examining component interactions
Related:
Second-Order Thinking Considering not just immediate consequences but also the consequences of those consequences
Decision Making Philosophy

Second-order thinking means thinking beyond the immediate and obvious. It involves asking 'And then what?' repeatedly to understand the full chain of consequences from a decision. While first-order thinking is fast and easy, second-order thinking requires more effort but leads to better long-term decisions by revealing non-obvious implications.

Examples

  • Not just 'Will this make money?' but 'What happens after we make money?'
  • Considering long-term environmental impact of short-term solutions
  • Understanding how solving one problem might create new problems
Related:
Complexity Systems with many interacting components that produce unpredictable, non-linear outcomes
Systems Thinking Philosophy

Complexity arises when a system has many interconnected parts that interact in non-linear ways. Complex systems are different from merely complicated ones: they're unpredictable, adaptive, and exhibit emergent behaviors. Understanding complexity helps us work with uncertainty and avoid simplistic solutions to complex problems.

Examples

  • Weather systems (chaotic and complex)
  • Economic markets
  • Biological ecosystems
  • Social networks
Related:
Mental Model A simplified representation of how something works in the world
Cognition Decision Making

Mental models are frameworks we use to understand and predict how things work. They're internal representations that help us make decisions, solve problems, and navigate complexity. Good mental models accurately reflect reality and help us think clearly. Building a latticework of mental models from different disciplines improves decision-making.

Examples

  • The map is not the territory
  • First principles thinking
  • Supply and demand
  • Compound interest
Related:
Abstraction Hiding complexity by exposing only essential features while concealing implementation details
Programming Systems Thinking Problem Solving

Abstraction is a fundamental principle in computer science and problem-solving that involves separating the interface from the implementation. It allows us to work at higher levels without worrying about underlying details. Good abstractions reduce cognitive load, enable code reuse, and make systems easier to reason about. However, all abstractions are leaky to some degree—sometimes the underlying complexity seeps through.

Examples

  • Functions: Use a function without knowing its internal implementation
  • APIs: Interact with a service without understanding its backend
  • Programming languages: Write high-level code without managing memory directly
  • Database queries: Use SQL without understanding storage engines
  • Operating systems: Run programs without managing hardware directly
Related:
Monorepo A single repository containing multiple projects or packages that are developed and versioned together
Programming Architecture

A monorepo stores all related projects — frontend apps, backend services, shared libraries, and tooling — in one version-controlled repository. Unlike polyrepo (one repo per project), a monorepo makes it easy to share code, enforce consistent tooling, and make atomic cross-project changes in a single commit. Tools like Nx, Turborepo, and pnpm workspaces are commonly used to manage builds and dependencies efficiently at scale.

Examples

  • Google keeps most of its internal code in a single giant monorepo
  • A Next.js frontend, Express API, and shared types library in one repo with pnpm workspaces
  • React, Jest, and Create React App were developed together in the facebook/react monorepo
Related:
Backend for Frontend (BFF) A dedicated backend layer built specifically to serve the needs of one frontend client
Architecture Programming

The BFF pattern introduces a thin server-side layer between a frontend (web, mobile, etc.) and the downstream microservices or APIs it depends on. Instead of having every client talk directly to every service, each client gets its own BFF that aggregates, transforms, and shapes data for that client's exact needs. This avoids over-fetching, reduces coupling between frontend and backend teams, and lets each BFF evolve independently.

Examples

  • A mobile BFF that merges product and inventory API responses into a single optimized payload
  • A web BFF that handles authentication cookies and server-side rendering data fetching
  • Next.js API routes acting as a BFF between the React frontend and external microservices
Related:
Microservice An architectural style where an application is composed of small, independently deployable services each responsible for a single business capability
Architecture Programming

Microservices decompose a monolithic application into loosely coupled services that communicate over a network (typically HTTP/REST or message queues). Each service owns its data, can be deployed independently, and can be written in the language best suited to its job. This enables teams to scale, deploy, and iterate on individual services without affecting the whole system — at the cost of increased operational complexity around service discovery, distributed tracing, and inter-service communication.

Examples

  • An e-commerce platform split into separate services for orders, payments, inventory, and notifications
  • Netflix running hundreds of microservices each owned by a small team
  • A user-auth service, a notification service, and a billing service each with their own database
Related:
ORM (Object-Relational Mapping) A technique that lets you query and manipulate a database using an object-oriented paradigm instead of raw SQL
Programming Database

An ORM maps database tables to classes and rows to objects, letting developers work with the database using the same language and style as the rest of their application code. It handles query generation, type coercion, and often migrations. The trade-off is that ORMs can obscure what SQL is actually being executed, sometimes generating inefficient queries — making it important to understand the underlying database even when using one.

Examples

  • Prisma and Drizzle in TypeScript/Node.js ecosystems
  • SQLAlchemy in Python
  • ActiveRecord in Ruby on Rails
  • Hibernate in Java
Related:
Reliability A system's ability to continue working correctly even when things go wrong
Architecture Systems Thinking

Kleppmann defines reliability as the system continuing to work correctly — performing the correct function at the desired level of performance — even in the face of adversity. Faults (one component deviating from its spec) are different from failures (the whole system stops). Reliable systems are fault-tolerant: they anticipate faults and prevent them from causing failures. This is achieved through redundancy, graceful degradation, and thorough testing including chaos engineering.

Examples

  • A database that replicates data across multiple nodes so one node dying doesn't cause data loss
  • Netflix Chaos Monkey deliberately killing production instances to test fault tolerance
  • Hardware RAID arrays continuing to serve reads even when one disk fails
Related:
Scalability A system's ability to cope with increased load without degrading performance
Architecture Systems Thinking

Scalability is not a one-dimensional label — it means having strategies for coping with growth. Kleppmann emphasizes describing load with precise load parameters (e.g. requests/sec, cache hit rate, ratio of reads to writes) before reasoning about performance. Two approaches exist: vertical scaling (more powerful machines) and horizontal scaling (distributing load across more machines). Understanding your system's bottlenecks — whether CPU, memory, I/O, or network — determines which approach is appropriate.

Examples

  • Twitter's fan-out challenge: posting a tweet must update millions of followers' timelines
  • Percentile response times (p95, p99) as a better load metric than averages
  • Stateless services that can be horizontally scaled behind a load balancer
  • Read replicas to scale read-heavy workloads without touching the primary database
Related:
Maintainability Designing systems so that people can work on them productively over time
Architecture Programming

Kleppmann breaks maintainability into three principles: Operability (making it easy for ops teams to keep the system running), Simplicity (reducing accidental complexity so new engineers can understand the system), and Evolvability (making it easy to make changes as requirements evolve — also known as extensibility or plasticity). Most software cost is not in initial development but in ongoing maintenance: fixing bugs, adapting to new platforms, adding features, and paying down technical debt.

Examples

  • Good observability — metrics, logs, distributed tracing — makes a system operable
  • Abstracting away accidental complexity behind clean interfaces to achieve simplicity
  • Schema migrations that allow evolving a data model without breaking existing readers
  • Avoiding tight coupling between services so one can change independently
Related: