Concepts & practices
Once by the book.
Once in the choices you make.
Understand an idea once, then recognize it wherever it matters. Explore how languages run, how we model and store data, how we handle failure and coordinate work, and how we check that it all behaves as intended.
Moving in. 74 of 74 topics have lessons; they're moving over from the old site.
Moving in 74 of 74 topics have lessons, which are moving over from the old site. Planned topics show where the catalog is going.
Design principles and language mechanisms
The ideas underneath the patterns and the tools each language gives you.
Composition over inheritance
Follow an invoicing API from a class tree to routes built from parts you can list
TypeScriptGoCoupling & cohesion
What belongs together and what should be able to change independently
TypeScriptGoInversion of control
Who owns the flow of execution; distinguish it from dependency injection
TypeScriptGoDependency injection
Supply collaborators from outside; compare manual wiring and containers
TypeScriptGoDependency direction
Choose which code depends on which, and where an interface should live
TypeScriptGoCurrying & partial application
Build functions in stages or supply some of their arguments ahead of time
TypeScriptGoMemoization
Remember a result so the same inputs do not repeat the work
TypeScriptGoIdempotence
Repeat an operation without adding effects beyond its first successful application
TypeScriptGoModule
Give a piece of code a small public surface and a private implementation
TypeScriptGoMixin
Compose reusable behavior across otherwise unrelated types
TypeScriptGoExtension
Add behavior using the extension mechanisms a language permits
TypeScriptGoDelegation
Pass a responsibility to another object or function
TypeScriptGoPolymorphism
One interface, different implementations behind it
TypeScriptGoLanguage and runtime models
What a value means, where state lives, and what happens when the code runs.
Values and references
Follow assignment and function arguments to see which state is shared
TypeScriptGoCopying, identity, and equality
Follow a delivery-address draft to separate object identity, shared state, and equal fields
TypeScriptGoAllocation and memory locality
Connect memory layout, allocation, and access order to the work a program does
TypeScriptGoOwnership, aliasing, and lifetimes
Reason about who can change a value and who is responsible for releasing a resource
TypeScriptGoRustClosures and captured state
See what a function keeps from its surrounding scope and how that state changes
TypeScriptGoThe call stack and execution
Trace function calls, stack frames, returns, and the state each call keeps
TypeScriptGoStatic types and runtime validation
Separate what TypeScript checks before execution from what must be checked at runtime
TypeScriptGoData modeling and type design
Make the shape of the data carry part of the argument.
Discriminated unions
Give each possible case a name and the data it needs, and let the compiler find every reader when a case is added
TypeScriptGoMaking illegal states unrepresentable
Use the shape of data to rule out invalid combinations
TypeScriptGoParse, don't validate
Turn untrusted input into a value with guarantees the rest of the code can use
TypeScriptGoBranded / opaque types
Keep values with different meanings apart even when their representation is the same
TypeScriptGoAbsence: null vs undefined vs missing
Distinguish an empty value, an absent value, and a field that was never provided
TypeScriptGoWide constructors
Options, positional arguments, builders, or factories — choose how callers supply data
TypeScriptGoImmutability
Compare read-only views, frozen values, copying, and structural sharing
TypeScriptGoWhen a generic earns its place
Use a type parameter to preserve a useful relationship between values
TypeScriptGoDomain model vs. DTO
Choose whether your application and its external contracts should share a shape
TypeScriptGoValue objects
Represent a domain value with its meaning, equality, and rules intact
TypeScriptGoErrors, results, and recovery
What failure looks like, how it travels, and what a caller can do next.
How a function reports failure
Choose a reporting channel that preserves the caller’s distinct next actions
TypeScriptGoExpected vs. unrecoverable
Separate actionable corrections from broken assumptions and name the containing boundary
TypeScriptGoKinds and sentinels
Choose how callers recognize failures, carry recovery data, and cross boundaries
TypeScriptGoClass-based error hierarchies
Recognize a local capture error family, then validate recovery data across constructor and JSON boundaries
TypeScriptGoDiscriminated union results
Return a specimen receipt or the first actionable refusal in a tagged result
TypeScriptGoGo's error protocol
Preserve archive error categories and typed details, then validate a client reply
TypeScriptGoResult types & combinators
Follow dependent reading-card lookups and decide when shared result composition earns its cost
TypeScriptGoErrors across a boundary
Preserve useful failure information as it crosses an application boundary
TypeScriptGoAggregate & partial failure
Fail fast, accumulate failures, or return partial success
TypeScriptGoError boundaries in UI
Recover a part of the interface and decide which state should survive
TypeScriptConcurrency, scheduling, and delivery
Work happening over time, and the bugs each execution model lets through.
Promises vs. goroutines & channels
Compare ways to start, coordinate, and communicate between concurrent tasks
TypeScriptGoEvent loop vs. scheduler
Understand what runs next and how an execution model divides the work
TypeScriptGoCancellation propagation
Stop work that is no longer needed and carry that decision to its child tasks
TypeScriptGoStructured concurrency
Keep child tasks connected to the lifetime and outcome of their parent
TypeScriptGoBounded parallelism
Limit work in flight with concurrency bounds and worker pools
TypeScriptGoTimeouts, deadlines & races
Timeouts, end-to-end deadlines, and competing outcomes
TypeScriptGoBackpressure & queues
Follow excess work through bounded queues that make the producer wait or drop and count what doesn’t fit
TypeScriptGoAsync iteration & streams
Work with values as they arrive, using pull or push to coordinate the flow
TypeScriptGoRace conditions in UI
Stale results, request identity, and which operation still owns the screen
TypeScriptGoIdempotency & at-least-once delivery
Follow duplicate deliveries through lost acknowledgments, identity, and atomic effects
TypeScriptGoActor model
Give each actor its own state and reach it through messages
TypeScriptGoBoundaries and contracts
What crosses a line, in what shape, and who checks it.
Validation at the edge
Check incoming data before trusting it inside the system
TypeScriptGoAPI contracts
Make the agreement between a caller and a service explicit
TypeScriptGoSerialization hazards
See which distinctions survive a round trip through an external format
TypeScriptGoVersioning & compatibility
Compatibility over time and how consumers observe a change
TypeScriptGoModule boundaries
Decide what is public and which parts of the application may depend on it
TypeScriptGoConfiguration as a boundary
Turn external configuration into a validated value the application can trust
TypeScriptGoThe mapping layer
Translate between external shapes and domain values, and decide where that work belongs
TypeScriptGoFeature flags & kill switches
Change behavior at runtime and manage the lifetime of that decision
TypeScriptGoTesting, debugging, and measurement
Make a claim about behavior, check it, and investigate what the evidence shows.
Invariants and example tests
Name what must stay true, then choose examples and edge cases that challenge it
TypeScriptGoProperty-based testing
Generate many inputs and shrink failures to a small example of a broken property
TypeScriptGoFakes, stubs, and mocks
Replace a collaborator deliberately and understand what the test stops exercising
TypeScriptGoContract and integration testing
Check the agreements between components and exercise their behavior together
TypeScriptGoDebugging state and control flow
Reproduce a failure and follow the state changes that explain how it happened
TypeScriptGoDiagnosing concurrency bugs
Investigate races, deadlocks, and timing-dependent failures with controlled schedules
TypeScriptGoDesigning useful benchmarks
Choose a workload, account for warmup and variability, and compare like with like
TypeScriptGoCPU and memory profiling
Find where time and allocations go, then measure the effect of a change
TypeScriptGoData and persistence
Keep stored state useful and correct as reads, writes, and failures overlap.
Transactions and atomicity
Group related changes and reason about what remains after a failure
TypeScriptGoIsolation and concurrent reads
Explore what a transaction can observe while other transactions change the data
TypeScriptGoIndexes and query plans
Connect lookup structures to database access paths and the cost of maintaining an index
TypeScriptGoCaching and invalidation
Choose what to cache, how long to trust it, and how to handle stale or missing entries
TypeScriptGoOptimistic concurrency
Detect conflicting updates and decide whether to retry, merge, or ask the caller
TypeScriptGo