
Cancellation Patterns in TypeScript: Taming AbortController
Why most async TypeScript code ignores cancellation entirely, and how to build composable, leak-free cancellation with AbortController.
Categories
Software craft & practices · 54 articles

Why most async TypeScript code ignores cancellation entirely, and how to build composable, leak-free cancellation with AbortController.

Use TypeScript template literal types to encode event names, route patterns, and permission sets directly into your type system — no runtime checks, no string drift.

TypeScript's satisfies operator validates that a value matches a type while preserving literal inference — here's why that distinction matters and where to apply it.

Eliminate a whole class of runtime bugs by teaching TypeScript's type system the difference between a UserId and an OrderId — even when both are strings under the hood.

TypeScript's type system stops at compile time — Zod closes the gap by validating data at the runtime boundaries where it actually matters.

Move beyond try-catch soup: discriminated unions, Result types, and error boundaries that make TypeScript codebases more reliable and maintainable.

A practical guide to building a testing strategy that catches real bugs — unit, integration, and end-to-end tests with Vitest, Testing Library, and Playwright, plus the tradeoffs of each layer.

Advanced TypeScript patterns I rely on daily — discriminated unions, branded types, the builder pattern, and utility types that make large codebases safer and more maintainable.

Implement dependency injection in TypeScript using constructor injection, factory functions, DI containers, and module-level composition patterns that improve testability and decouple components without framework lock-in.

A battle-tested framework for diagnosing and resolving production incidents quickly — from first alert to root cause analysis and prevention.

Adopt trunk-based development in monorepos with strategies for code ownership, selective CI triggers, merge queues, and branch protection that keep hundreds of developers shipping to a single repository without stepping on each other.

Implement a robust feature flag system that supports percentage rollouts, user segmentation, A/B testing, and kill switches, enabling you to decouple deployment from release and ship with confidence at any scale.

Practical testing strategies for distributed architectures, covering contract testing between services, chaos engineering for resilience validation, integration test design, and building confidence in systems where end-to-end testing is impractical.

A framework for making sound technical decisions when requirements are incomplete, timelines are tight, and the stakes are high, covering reversibility analysis, decision records, and systematic approaches to navigating ambiguity in software engineering.

How to shift observability left by designing systems around structured logs, distributed traces, and meaningful metrics from the start, enabling faster debugging and reducing incident response time in complex distributed architectures.

A systematic approach to incident management covering alerting design, severity classification, on-call response workflows, communication protocols, and blameless post-mortem practices.

An exploration of fault tolerance engineering covering circuit breakers, bulkheads, fallback chains, and degradation strategies that keep systems useful when components fail.

A practical framework for measuring technical debt in concrete terms that resonate with stakeholders, using code metrics, developer velocity data, and incident correlations to justify refactoring investments.

Practice chaos engineering by injecting controlled failures into production systems to discover weaknesses before they cause outages, with practical experiments covering network partitions, resource exhaustion, and dependency failures.

Build a robust feature flag system that supports gradual rollouts, user targeting, A/B testing, and kill switches, with practical patterns for flag lifecycle management that prevent technical debt accumulation.

Implement distributed tracing across microservices using OpenTelemetry, with practical examples of context propagation, span creation, sampling strategies, and trace analysis that turn opaque distributed systems into debuggable ones.

Design and execute effective load tests using k6 with practical patterns for ramping strategies, realistic user simulation, threshold-based pass/fail criteria, and interpreting results to find bottlenecks before production traffic exposes them.

Build production-grade distributed tracing infrastructure with OpenTelemetry, covering context propagation, sampling strategies, trace storage backends, correlation with logs and metrics, and cardinality management at scale.

Learn how property-based testing generates hundreds of random inputs to verify code invariants, catching edge cases that hand-written example tests overlook with practical patterns in TypeScript using fast-check.

Master distributed debugging techniques using correlation IDs, distributed tracing context propagation, log aggregation patterns, and causal ordering to trace requests across services and identify root causes.

Apply systematic refactoring strategies to legacy codebases using characterization tests, the strangler pattern, and incremental extraction to improve code quality without introducing regressions.

Build robust error boundary strategies that catch failures gracefully, provide meaningful fallbacks, report actionable diagnostics, and prevent one broken component from crashing the entire application.

How to build custom ESLint rules for your team's conventions — covering AST selectors, rule testing, auto-fixers, and publishing a shared plugin that enforces patterns your default linter cannot catch.

A practical guide to git rebase and interactive rebase — covering when to rebase vs merge, squashing commits, rewriting history safely, fixing mistakes, and team workflows for clean commit histories.

A developer-friendly introduction to compiler fundamentals — covering lexing, parsing, ASTs, and code generation with practical examples that help you write better code, build tooling, and understand language features.

How to implement feature toggles that enable trunk-based development, gradual rollouts, and instant rollbacks — covering toggle types, testing strategies, and cleanup practices that prevent toggle debt.

How to use Git hooks to automate code quality checks, commit message validation, pre-push tests, and branch naming enforcement — with practical examples using Husky and custom scripts.

How to write Request for Comments documents that drive clear technical decisions — covering structure, audience calibration, alternatives analysis, and building consensus asynchronously.

How to implement structured logging that makes production debugging fast — covering JSON log formats, context propagation, log levels, sensitive data masking, and integration with log aggregation tools.

A deep dive into async iterators and generators in JavaScript — covering for-await-of loops, async generator functions, backpressure handling, and real-world streaming patterns.

How property-based testing finds edge cases your unit tests miss — a practical guide using fast-check to generate thousands of test inputs and verify invariants automatically.

A practical guide to load testing APIs — covering tool selection, realistic traffic patterns, performance baselines, bottleneck identification, and common mistakes.

How to write technical documentation that engineers actually read — covering ADRs, runbooks, API docs, and documentation-as-code workflows.

Techniques for diagnosing memory leaks, excessive allocation, and OOM kills in production — covering heap snapshots, profiling tools, and common leak patterns.

How trunk-based development with short-lived branches reduces merge conflicts, speeds up delivery, and keeps the main branch always deployable.

Patterns for integration tests that are reliable, fast, and catch real bugs — covering database setup, API testing, and avoiding flaky test syndrome.

Practical functional programming patterns for TypeScript that improve code clarity without requiring a complete paradigm shift.

A practical guide to TypeScript generics that takes you from basic type parameters to advanced conditional types and utility patterns.

How to give code reviews that catch real bugs, share knowledge, and improve team velocity instead of creating bottlenecks.

When a request touches five services, logs from each one are useless in isolation — distributed tracing ties them together with trace IDs, spans, and context propagation.

Chaos engineering is the practice of deliberately injecting failures into production systems to discover weaknesses before they cause real outages.

Closures, event listeners, and uncleared timers — the most common memory leak patterns in Node.js and how to find them before they crash production.

Feature flags decouple deployment from release — here's how to implement them without creating a tangled mess of conditional logic across your codebase.

The monorepo debate isn't about tools — it's about how your team communicates, deploys, and shares code across project boundaries.

Structured logging, log levels, and correlation IDs — the patterns that turn noisy log output into a searchable, actionable observability layer.

Dependency injection doesn't require a framework or decorators — here's how to apply it in TypeScript with simple patterns that make code testable and flexible.

The event loop is the engine behind every async operation in JavaScript — here's how it actually works, beyond the hand-wavy explanations.

Trunk-based, GitFlow, or something in between — how to pick a Git workflow that scales with your team without drowning in merge conflicts.

Not all clean code advice is created equal — these are the principles that genuinely reduce bugs, speed up onboarding, and survive real-world deadlines.