Duncan Leung

All Posts

01

Does Haiku Fan-Out Replace Sonnet? Two Experiments in Code Review and Solution Discovery

aiclaude-codeopenrig

I swapped the five Sonnet hunters in my code review pipeline for Haiku across 14 PRs with known bugs. Haiku found 20 of 26 defects to Sonnet's 21, produced 61% more raw findings, and used 28% more tokens. In a second test, 8 Haiku advocates plus an Opus adjudicator reached the same design family as 3 Sonnet advocates on all 3 decisions. The result: near-parity on recall, but no cost advantage in a validation-heavy pipeline.

02

Why I Built a Compaction System Instead of Handing Off to New Agents

aiclaude-codeopenrigmulti-agent

When a long-running orchestrator hits context limits, the obvious move is to hand off to a fresh agent. I tried that. The startup overhead, lost momentum, and broken continuity made it worse than the problem it solved. So I built a daemon-managed compaction system instead: one that monitors context usage, writes ground truth to disk, runs a four-stage idle-gated restore protocol, and distills session knowledge into a wiki page. The daemon is the sole compaction authority; seats never self-compact. Recent research shows neither approach is categorically better, but for same-model orchestrators that accumulate judgment over time, daemon-managed compaction with external ground truth wins.

03

Surviving the Dumb Zone: A Checkpoint System for Claude Code's /compact

aiclaude-codeopenrig

Claude Code's /compact command compresses your conversation to free context, but it silently drops external state mutations, abandoned approaches, and behavioral constraints. I built a precompact/postcompact checkpoint system that writes ground truth to disk before compaction and cross-checks it afterward — including live reconciliation against git, CI, and external systems. Here's the design, the research behind it, the failure modes it prevents, and how OpenRig's native compaction system eventually superseded these custom skills.

04

Building an Observer That Turns Claude Code Sessions into a Knowledge Base

aiclaude-codeknowledge-management

Every Claude Code session produces decisions, patterns, and context that vanish when the terminal closes. I built a daemon that watches for session endings, extracts the knowledge, distills it with Haiku, and compiles it into an Obsidian wiki with Sonnet — turning every coding session into searchable institutional memory for about $20/month.

05

Claude Code Research Tools: A Decision Guide

aiclaude-code

Claude Code has a dozen ways to research a question — from a single MCP call to a 98-agent adversarial pipeline. This guide maps every research tool by cost, agent count, and use case so you pick the right one the first time. Includes the full source of a custom deep-research-plus workflow that adds a Perplexity search lane to Claude Code's built-in deep-research pipeline.

06

The Attention Mechanism: Context Rot and the Lost-in-the-Middle Problem

ai

LLM context rot isn't a bug. It's a mathematical property of how the attention mechanism works. Three interacting failure modes (softmax's sum-to-one constraint, RoPE's distance decay, and causal masking's primacy bias) combine to produce the U-shaped attention curve where models attend well to the beginning and end of context but lose the middle. Understanding the mechanism changes how you structure prompts and manage context.

07

The LLM Dumb Zone: Performance Degradation Before the Context Limit

aiclaude-code

LLM context windows have a marketed size and a useful size, and they're not the same number. Research shows quality degrades gradually based on absolute token count, not fill percentage, and a 1M-token model at 40% full is far more degraded than a 200K model at 40%. Here's the research behind the Smart Zone, the Warn Zone, and the Dumb Zone, plus a Claude Code statusline that tracks which zone you're in and how deep.

08

Benchmarks vs Vibes: Opus 4.6 vs Opus 5 for Daily Coding

aiclaude-code

Opus 5 scores 96.0% on SWE-bench Verified vs Opus 4.6's 80.8%, a 15-point gap. But on blind user-preference coding leaderboards, Opus 4.6 ranks 4th (ELO 1547) while Opus 5 ranks 11th (ELO 1530). Synthetic benchmarks and human preference are telling opposite stories. I ran a two-round adversarial research workflow to figure out which signal to trust and landed on a mixed-model strategy.

09

Near-Miss Retrieval in RAG: Semantic vs Hierarchical Chunking

airagretrieval

The retrieval pipeline returned chunks that mentioned vaccination schedules but didn't contain the actual schedule data. The model saw evidence that was on-topic but empty of the answer - and correctly refused. The root cause wasn't the model, the reranker, or the query. It was the chunking strategy splitting tables and structured data away from the prose that named them.

10

Stochastic Refusal in RAG: Same Evidence, Different Answer

airagretrieval

Same question, same branch, same retrieval, same 10 evidence chunks with identical rerank scores - and one deployment refused while another answered with full clinical detail. The root cause wasn't retrieval. It was the LLM making a borderline judgment call on evidence that covered half the question well and the other half barely at all.

11

Reranking in RAG: Cross-Encoder Scoring for Recall and Precision

airagretrieval

Vector search finds the right chunk but ranks it badly and waves through near-garbage that's vaguely on-topic. Reranking fixes that with a second model - a cross-encoder that reads the query and each chunk together instead of comparing two frozen vectors. The whole trick is affording an expensive judge by only running it on the cheap stage's survivors.

12

Setting Up Python with uv

python

A guide to set up a modern Python development environment using uv, the fast all-in-one tool from Astral that replaces pyenv, virtualenv, and Poetry.

13

Module-Load Env Guards in Next.js: Structural Validation Beyond process.env Checks

nextjstypescriptbackendsecurity

Most env-var validation checks that values exist. That catches missing config but misses the worse case - a value that is present, valid in shape, and silently wrong. Here is the asymmetric pattern for separating loud failures from silent ones in a Next.js server.

14

Producer, Service, ProducerService: Interface Segregation for Shared Singletons in Go

backendgo

When one Go struct legitimately serves multiple consumer roles - producer, service, admin - declare three interfaces and let DI cache the singleton at the wide type while each consumer holds only the methods it can call. Same shape as io.ReadWriteCloser in the stdlib.

15

Idempotent SQS Workers with Postgres UPDATE: The WHERE-Status Pattern

backendgoawspostgres

Most SQS idempotency guides reach for Redis dedupe keys or dedupe tables. If your worker's job ends in a terminal state-machine transition, the row's own status column is already the lock - and one UPDATE statement is the entire safety story.

16

Agentic Engineering at Airvet: Research, Plan, Implement

aiproductivity

A talk I gave to the Airvet engineering org on working with AI coding agents - the mental model, context engineering, and the Research → Plan → Implement loop that ties them together.

17

Setting up Global MCP Configuration for Claude Code

claude-codemcp

Learn how to configure MCP (Model Context Protocol) servers globally for Claude Code, making them available across all your projects.

18

AI "Overview": GPT, RAG, and How They Actually Work

ai

A talk I gave to the Airvet engineering org as a primer on AI - what GPT and RAG actually are, how neural networks, tokens, and embeddings fit together, and why the difference matters.

19

Preparing for ESLint v9: TypeScript Integration Changes

eslinttypescript

A guide to the upcoming changes in ESLint v9 regarding TypeScript integration, including migration notes.

20

Tackling 'on load' Scrolling in Chat Applications

reactDOM

How to solve one of the most frustrating UX issues in chat applications: ensuring automatic scrolling works correctly when images load asynchronously.

21

Why useRef Doesn't Work for Dynamic Element Measurements

reactDOM

Why useRef alone is insufficient for dynamic measurements, and how to combine it with ResizeObserver and useState for responsive UI.

22

Proxy Firebase Auth with Next.js on Vercel

firebaseauthentication

Solving Firebase Auth redirect issues on Vercel by implementing a proxy solution to handle SAML SSO authentication and resolve missing initial state errors.

23

Go Idioms: Accept Interfaces, Return Types

backendgo

Understanding the Go idiom of accepting interfaces and returning concrete types to write flexible, reusable, and maintainable code.

24

Allocating Memory in Go

backendgo

Understanding memory allocation in Go, including stack vs heap allocation, escape analysis, and how Go handles pointer returns from functions.

25

Methods in Go

backendgo

Understanding Go methods, the difference between value and pointer receivers, embedding for composition, and how interfaces work with methods.

26

Slices in Go

backendgo

Understanding Go slices, their underlying array structure, creation and modification mechanics, and the implications when passing slices to functions.

27

JSON Encoding and Decoding in Go

backendgo

A guide to JSON serialization and deserialization in Go, covering type mappings, HTTP responses, and practical examples with the net/http package.

28

Go Function Argument Passing Behavior

backendgo

How Go passes function arguments by value and the implications for different data types: integers, pointers, slices, strings, and structs.

29

Tailwind CSS Unknown at Rule

tailwindcss

Fixing VSCode warnings for Tailwind CSS directives by creating custom CSS data configuration files for proper syntax recognition.

30

NVM Commands Cheatsheet

nodejsnvm

A comprehensive cheatsheet for Node Version Manager (NVM) commands including installation, version switching, aliasing, and management.

31

Understanding Interfaces in Go

backendgo

A guide to understand Go interfaces through a power socket and electrical device analogy, covering duck typing and interface implementation.

32

Recover from a git push --force

git

How to recover from an accidental git push --force by using git reflog to find the previous commit and restore the branch to its original state.

33

Using SVGs with Next.js 11 and TypeScript

typescriptnextjs

Fixing TypeScript ESLint errors when using SVGs with Next.js 11 by creating custom module declarations and configuring TypeScript properly.

34

Basic Data Structures in Go

backendgo

Understanding Go data structures and their memory layout including integers, floats, arrays, pointers, structs, strings, and slices.

35

Find and Kill Process on Port 3000

macos

How to find and kill processes running on specific ports like 3000 on macOS using lsof and kill commands.

36

Indexing Strings and Runes in Go

backendgo

Understanding the difference between indexing strings as bytes versus runes in Go, and when to use each approach for proper Unicode handling.

37

Setting Up Husky, Prettier, and ESLint with precise-commits and lint-staged

eslintprettier

Automatically format and lint code with Git hooks using Husky, Prettier, ESLint, precise-commits and lint-staged for pre-commit and pre-push workflows.

38

Understanding JavaScript Promises: Chaining, Error Handling, and async/await

javascriptasync

A practical guide to working with JavaScript Promises - from basic chaining and resolve/reject behavior to .catch, Promise.all fail-fast handling, and async/await error patterns.

39

The io.Reader and io.Writer Interfaces in Go

backendgo

Understanding the io.Reader and io.Writer interfaces in Go, and how accepting an io.Writer parameter makes your code flexible across files, HTTP responses, stdout, and in-memory buffers.

40

Self-Hosting a Go Binary on AWS EC2

backendgoaws

Deploying a Go service to a single AWS EC2 instance as a hardened systemd service - cross-compiling the binary, copying it across, creating a non-root service user, configuring systemd with the right hardening directives, and handling graceful shutdown.

41

ESLint on TypeScript and JavaScript Files

eslinttypescript

Setting up ESLint to parse and lint a mixed JavaScript and TypeScript codebase using overrides configuration for different file types.

42

Accessing Material UI Theme Object in Emotion JS

reactmaterial-uiemotion

A guide to access and utilize Material-UI theme objects within Emotion styled components and CSS props, with practical implementation examples.

43

Configuring AWS Email Billing Alerts with CloudWatch

awscloudwatchmonitoring

A detailed guide to setting up automated email billing alerts in AWS using CloudWatch metrics and SNS notifications for cost monitoring.

44

Securing AWS API Gateway Endpoints (Cognito, IAM, API Keys)

awsserverlesssecurity

A guide to implement authentication and authorization in AWS API Gateway, covering Cognito User Pools, IAM roles, and API keys with practical examples.

45

Set Breakpoints to Locally Debug AWS Lambda

awsserverlessdebugging

A step-by-step guide to setting up and using breakpoints for local debugging of AWS Lambda functions, improving serverless development workflow.

46

Export Serverless Framework Environment Variables to a Local .env File

serverlessaws-lambdaaws-cloudformation

Bridging CloudFormation-generated runtime values - DynamoDB table names, API Gateway URLs, ARNs that do not exist until deployment - into a local .env file using the serverless-export-env plugin.

47

Reference AWS Lambda API Endpoints in serverless.yml

awsserverless

A detailed guide to using Serverless Framework pseudo parameters for referencing Lambda functions and other AWS resources in your serverless applications.

48

Add Custom AWS YAML Tags in VS Code

vscodeserverless

Learn how to set up VS Code to properly recognize and validate custom YAML tags used in Serverless Framework configuration files.

49

Understanding the Browser Rendering Pipeline: Layout vs Paint vs Composite

performancecssDOM

How the browser turns CSS changes into pixels - the five stages of the render pipeline, why some CSS properties trigger the full pipeline and others skip most of it, and how to avoid jank by understanding which path your changes take.

50

HTTP Cache-Control: ETag, Revalidation, and the Two Caching Strategies

performancehttp

There are two caching strategies on the web: fingerprinted-immutable URLs and server-revalidated mutable content. Pick one per resource. Most caching confusion comes from conflating no-cache, no-store, and must-revalidate.