Today I Learned

Daily reflections on work and learning—what I did and what I learned.

2026년 10월

2026-10-01

'Run only the related tests' is safe only when there is a rule that falls back to everything when you can't trust the graph

A record of switching the push hook and PR CI, which kept repeating the full unit test suite for every parallel job, to change-based selective runs, covering how to avoid guessing the base point and the rule that sends deletions and global config changes to a full run

  • Change-based test selection is an optimization that relies on the accuracy of the import graph, so it needs a rule that sends changes the graph can't be trusted for (deletions, renames, global config) to a full run
  • Don't guess the base point. CI uses the PR's base commit, the push hook uses the previous remote SHA that Git passes in, and for a new branch a person states it explicitly

2026년 9월

2026-09-25

I pressed two keys together twice and it dashed in only one direction — don't commit on the first event

Why committing the direction on the first key fails to merge the second key, since the browser sends keys pressed at the same time as separate keydown events, and how a 50ms collection window solved it

  • Even for keys pressed at the same time, the browser sends a separate keydown event for each key, in order
  • If you commit the result on the first key event, the second key cannot be merged as soon as a frame or simulation step slips in between the events
2026-09-18

In front of a server that handles one request at a time behind a lock, jittered retries inside the worker come before a queue

A record of splitting a form into a queue and a worker after several simultaneous submissions failed with 'busy', and of confirming that queue redelivery alone does not resolve the contention and that acking an exception silently loses the message

  • If the other side processes one request at a time behind a lock, queue redelivery alone does not resolve the contention. Redeliveries also arrive at the same moment and collide again, so random-jitter retries inside the worker come first, and the queue's backoff is meant for outages
  • Once you have already answered 'received', the worker must not ack an unexpected exception. An ack is a silent loss

2026년 8월

2026-08-23

Locking Down a Reusable Table Component with Types

What I learned deriving a sort-key union from a column array and passing a per-domain row type all the way down into compound component parts — and the variance rabbit hole that kept showing up along the way

  • Deriving a type from a value and the const type parameter are a set. Drop one and the other quietly becomes meaningless, with no error
  • You can't attach type parameters to a const declaration, so a generic context has to be created inside a function closure
2026-08-14

Lock the correctness criteria before the implementation

Writing the judgment criteria down before handing work to a code generation tool, demoting code, screens, and reviewer opinions to evidence, and stopping when confidence runs out

  • Restricting the source of correctness to people and approved specs removes the failure of mistaking the implementation for the spec
  • Moving the definition of done past "tests pass" removes any incentive to loosen assertions
2026-08-09

Tracing request responses and change notifications separately in a browser extension

A note on requesting writes through message-based RPC in separated extension contexts, then using change signals and query invalidation so other screens re-read the single source of truth

  • Chrome's message APIs are transport; request validation, routing, and error responses are defined by the application
  • Command responses and change notifications for other screens have different purposes and should stay separate
2026-08-07

What a middle layer passes through and what it guesses — proxies, input commit, image ratios

An infinite redirect caused by a rewrite passing an upstream 3xx straight through, Korean input committed before composition ended, and image ratios that were being guessed as 16:9 — what I learned auditing what middle layers pass through and what they assume

  • A rewrite is a proxy, not a redirect, so it hands the upstream's 3xx straight to the browser
  • Treat a Korean input bug as a commit-boundary problem first, not a value-normalization problem

2026년 7월

2026-07-31

Legacy Browser Compat Is More Than babel Alone — the Multi-Layer Contract of Syntax, Polyfill, Conditional Load, and Smoke

What I learned fixing a bug where an app dies wholesale on a browser more than five years old — when several features die at once, suspect hydration; separate syntax down-leveling from runtime polyfills; load polyfills only on old browsers via feature-detect; and make the smoke run real interactions

  • When several UI features die at once, first suspect a common root cause like hydration/bundle parsing rather than separate bugs
  • Syntax down-leveling (babel) and runtime APIs (polyfill) are separate layers — a transpiler rewrites syntax but does not create missing functions
2026-07-25

First-Paint Splash and Offline-First — Getting Instant UX While Keeping ISR

The inline-script technique for showing a native-app-like loading splash from the first paint while hiding it from bots, and how to make 'my writes reflect instantly' in offline-first with invalidate and a durable queue rather than staleTime

  • Reveal before first paint + hide from bots is inline blocking script injection — a UA gate breaks ISR into dynamic via headers()
  • In react-query, "my writes reflect instantly" comes not from lowering staleTime but from invalidateQueries after the write (invalidate ignores staleTime)
2026-07-24

When the LCP bottleneck is hydration, not the image — and restrained scroll motion

What I learned improving landing performance — when LCP is slow but the image is small, suspect render delay (hydration) and strip client JS; prefer entrance stagger reveal over flashy scroll motion; structured data must be 1:1 with the screen

  • When LCP is slow but TBT is 0 and the image is small, suspect element render delay (= hydration at the client boundary) and strip the client JS from that surface
  • A short viewport-entrance stagger reveal is often the universal answer over flashy scroll-dependent motion (scroll-jacking)
2026-07-23

Intermittently Broken Images — objectURL Race, WebP Fallback, Service Worker Shell

Three things I learned chasing an image bug that flickered in and out on every refresh — the objectURL lifetime race, preventing test hangs from browser-only libs, and scoping the service worker shell fallback to navigation requests

  • Render images the server provides by URL directly, do not wrap them in an objectURL blob — this eliminates the createObjectURL lifetime race at its source
  • When putting a browser-only (canvas) lib on a test path, block jsdom hangs with a synchronous capability gate before execution
2026-07-22

Moving an infinite-list feature without the code — clean-room contracts and keyset pagination

What I learned moving a virtualized infinite list + scroll-restoration feature to a different stack — the clean-room approach of writing down observable behavior as a contract rather than the implementation code, and offset vs keyset pagination, which decides how stable infinite scroll is

  • A migration doc records the contract of "how it behaves from the outside and what it guarantees on failure", not "which libraries we used"
  • Infinite-scroll stability comes mostly from the pagination method — offset duplicates/skips on insert/delete, keyset is value-based and stable
2026-07-20

What I Learned from Public Surface, Analytics Facade, and Motion Gates

TIL summarizing structured data and visible FAQ parity, auth-resolution-gated fetch, analytics event fan-out facade, and prefers-reduced-motion JS transition gate as general concepts

  • Structured data (FAQPage, etc.) must come from the same source as visible on-screen text
  • Enable protected APIs only after session loading finishes; do not decide anonymous/fetch from !user alone during loading
2026-07-14

General concepts I hit while tidying up my dev environment: shell quoting, monorepo publishing, CI runners, guard design

A TIL distilling shell word splitting, workspace publish rewriting, CI runner mismatches, and fail-open guards into general concepts encountered while maintaining tooling and infrastructure

  • A path held in a shell variable, if it contains spaces, splits into multiple arguments when used unquoted and the command silently breaks
  • Internal monorepo dependencies should be linked with the workspace protocol and published with a manager that rewrites them to real versions
2026-07-13

Starlight graph, SW cache, Playwright triage, ESLint guardrail

A TIL covering Canvas star nodes, Service Worker SWR and generation security, CI flaky classification, and design-system ESLint guardrails in one day

  • Stabilize Canvas node decoration with ID hash and always restore globalAlpha and shadowBlur right after drawing
  • For red Playwright CI, split flaky from deterministic failed using retry results first
2026-07-11

Directing a scroll-driven home-hero narrative

A day implementing a TIL→Knowledge promotion demo and a pipeline section with sticky scrolly, CSS keyframes, and view-timeline — no GSAP

  • sticky scrolly makes a scroll time axis as tall as the wrapper, and if phases derive from progress the JS stays thin
  • A hero live demo can build a loop with just a clip-path steps wipe + animation-delay
2026-07-10

Modal peek stack and a CSS-layer package

A day aligning peek UX and theme customization via document push inside a single dialog and @layer-based editor CSS splitting

  • gallery→peek is more continuous when you just swap documentId in the same shell rather than opening a fresh dialog
  • async resolve must block the (newId, oldContent) intermediate frame with a render-time reset
2026-07-09

Hierarchical document nav and viewer bundle splitting

A day tuning both portfolio-demo depth and bundle size at once, via a page push/peek navigator and a separate read-only viewer entry

  • A document graph is better separated from the flat editor via a page block + push/peek navigator contract
  • peek/embed can share DOM with the editor, but the import graph must differ for the viewer bundle to shrink
2026-07-08

Mobile pointer gestures and a server image proxy

A day spent stabilizing touch drag/resize and, in an unoptimized environment, cutting transferred bytes with an image proxy

  • Reworked the drag behavior so dragging by touch doesn't scroll the page along with it
  • Added a proxy so images ship only the size needed, not the whole original
2026-07-07

Making Nested UI Re-renders O(1) — read-model, external store, and render-count tests

In an immutable-tree editor, editing a single line redrew everything; here's how I first pinned the problem with a test, then cut row and container re-renders with a normalized projection and per-id subscriptions

  • An immutable tree patch gives ancestors a new ref too, so passing props down triggers O(depth) re-renders — a normalized entry plus per-id subscription can cut it to O(1)
  • The entry cache must be stabilized by output value-equality, not input ref, and the external store sync must run synchronously at the choke point right before setState