the paradox of expertise
there is a specific kind of dumb move i only seem to make once i know a domain well.
i recently spent forty-five minutes struggling to solve a problem. it gets much easier if you describe it to someone who doesn't know any algorithm names.
start with [1, 3, 5, ..., 2n-1].
in one operation, pick two indices and move 1 from one pile to the other.
what's the minimum number of operations to make all elements equal?
i threw everything at it. i sketched state transitions. i built a dynamic programming table. i implemented a greedy recursion with memoization. i spent forty-five minutes debugging my own sophisticated machinery.
each pass felt like progress. the dp table wasn't confused; it was doing exactly what dp tables do. the memoization wasn't misapplied; it was correctly optimizing the recursion i'd built. the machinery was sound. i had just assembled it around the wrong problem.
then i gave up and looked at the solution:
def min_operations(n):
return n * n // 4one line of logic.
forty-five minutes.
intuition: the array is symmetric around its average (n).
each operation can move 1 unit from an above-average pile to a below-average one, so the work is the total deficit of the lower half, which sums to n * n // 4.
the mistake happened the second i read the word "minimize."
my brain saw that word and immediately retrieved the "dynamic programming" folder. it loaded all the heavy machinery—state transitions, memoization, recursion.
kahneman calls it system 1 — fast, automatic, heuristic. usually it saves time. here, it blinded me. the pattern-match ("minimize" = "dp") happened before i even understood the problem — and it came with no flag, no asterisk, no felt difference from the times it's right, which is most of the time, which is why you trust it.
in 1942, a psychologist named luchins sat people down with water jug puzzles and watched this happen under controlled conditions. once they learned a complex method, they couldn't see the simple one — even when it was obvious to newcomers. he called it einstellung: the mechanization of thought. once you pick up the hammer, you physically cannot see the screws. the wrong tool captures your attention so completely that the right one never even loads.
suddenly, my working memory was full. there was no room left for simple arithmetic. i wasn't solving the problem in front of me. i was solving the problem i expected to see — it felt exactly like the real one.
which is the thing that's hard to keep straight afterward. not that the error happened — errors are obvious in retrospect. but that there is no felt difference, from inside the skull, between solving the right problem and solving a plausible substitute. the sensation of engagement is identical. the experience of progress is identical.
if you asked that same person, they would just look at the numbers and say:
"wait, you're just moving piles around. the total amount of stuff implies the answer, right?"
they win because they are traveling light. i lose because i'm carrying too much luggage.
and that mistake has a particular flavor: hot, obvious, humiliating.
but i get stuck in quieter ways too. sometimes i'm "writing" for three days and the doc still doesn't have the next paragraph. sometimes i don't even notice it's happening.
this essay is for the person who is stuck because they're already carrying a lot.
stuckness isn't a monolith. it has a geometry. and the shape suggests what to try next.
friction vs. incoherence
from the inside, it just feels like "hard." but that single coordinate hides the geometry.
one cut through that space: friction vs incoherence.
climbing a steep hill.
every step hurts, but you are higher than you were.
the geometry: direction is stable. each attempt refines the last.
what tends to help: grind. this is one of the few times "try harder" is the right move.
orbiting a contradiction.
you fix A, which breaks B. you fix B, which breaks A. you feel busy, but you're drawing circles.
the geometry: direction flips. each attempt undoes the last.
what tends to help: stop. effort here often accelerates the loop.
we are trained to expect friction. in school, sports, and early work, effort effectively correlates with progress. if you aren't moving forward, the answer is almost always to push harder.
but usually, when you are truly stuck, the problem is incoherence.
incoherence is dangerous because it triggers the same "try harder" reflex, but the geometry is different. pushing harder in a loop just spins you faster. and you cannot distinguish the two from the inside by feel alone — which is the whole problem, because the thing you'd need to tell them apart (an honest map of where you are) is precisely the thing being compromised.
the common shapes
livelock (fast incoherence)
the geometry: a tight loop. you are cycling through the same three ideas in working memory. your approach flips every few minutes. "maybe it's the cache? no, the logic. maybe the cache?"
the ideas don't feel recycled. each pass feels like the first time — which is the thing. there is no signal from inside the loop that you've been here before.
the signature: high effort, near-zero net progress. specific flavor: frustration, panic, heat.
why it's hard to escape:
your working memory is a scratchpad with roughly four slots — cowan measured this in 2001, and the number is stubbornly small. simple arithmetic takes one slot. but "dynamic programming" isn't one slot — it's a heavy folder. once you load it, it crowds out the rest. you physically can't see the simple solution because the complex one is taking up all the space.
your inner monologue — what baddeley called the phonological loop — lasts about two seconds. if you are muttering "maybe cache... no logic... maybe cache," you are actively maintaining that state in your head. you can't have a new thought because you are too busy repeating the old one to keep it from fading. which means — and this took me a while to really absorb — you are actively maintaining the constraint. the loop doesn't sustain itself. you are the one refreshing it, every two seconds, to keep it from decaying.
worse, concepts prime each other — collins and loftus called it spreading activation. when you say "optimization," your brain involuntarily lights up related nodes like "memoization" and "caching." the wrong tools aren't just sitting there; they are actively reloading themselves. even noticing that you're stuck on "optimization" re-activates the node, which re-primes its neighbors.
put them together and you get a closed loop:
when you ask "what am i missing?", the answer comes from the same biased circuit that missed it.
what helps: interrupt. break the loop before trying to move.
drift (latent incoherence)
the geometry: slow incoherence. you are moving steadily, but in a direction the world didn't ask for. it looks like work. you are optimizing the query planner for a database with ten rows. you are refactoring the auth system for a userbase of one.
the signature: output that doesn't cash out. specific flavor: satisfaction, "flow", cool. the mechanism: local optimization masking global incoherence. doing the sub-task feels good (dopamine), so you keep doing it, even though it doesn't move the main metric. what helps: collide with reality. a hard stop from outside.
drift is the most dangerous shape because it feels like work. you are producing high-quality artifacts. the code compiles. the diagrams are beautiful. and the artifacts are genuinely good — that's the trap. it's good code solving a problem nobody has. a gyroscope spinning in a vacuum: stable, precise, and touching nothing.
traction (the baseline)
traction is what friction feels like when you're pointed the right way. you are running through mud toward the goal — messy, but each attempt builds on the last. specific flavor: ugly, messy, real. crossed-out hypotheses. a growing table of test cases. a diagram that keeps expanding. the search space is shrinking: .
the risk with traction isn't the direction — it's premature flush. your working memory holds an expensive, fragile map of dead ends. if you context-switch or start over, you lose it.
traction is where you want to be. livelock and drift are the two ways you miss it.
the destruction of value
mistaking one shape for another destroys value.
traction and drift can feel identical from the inside. in traction, the search space shrinks. in drift, you generate output that doesn't constrain anything.
i force a check: did the last hour reduce the branching factor? fewer plausible moves, sharper test cases, a clearer statement of what i'm trying to do. if yes, traction. if no, stop and look. this sounds simple, but the hard part is answering honestly — because what you're really asking is "was the last hour of my life wasted?" and nobody volunteers for that verdict.
what makes it harder: the question is recursive. you're using your judgment to audit your judgment. the instrument and the error may be the same instrument.
livelock loops in minutes. drift loops in weeks.
if you only read one thing:
- write the problem naked (inputs/outputs/constraints; no mechanism words).
- if incoherence:
- livelock (tight loop) → interrupt
- drift (slow, feels smooth) → collide
- else: traction → protect
- ask: is the constraint real?
seeing the shape
livelock screams (frustration). drift whispers (satisfaction).
introspection is a compromised sensor for progress. it measures intensity (heat) and coherence (smoothness), but it's bad at measuring velocity (progress toward reality).
- livelock: high heat, low smoothness. (you feel stuck.)
- drift: low heat, high smoothness. (you feel productive.)
write the problem naked. sometimes even that is hard for boring reasons: you're missing an input, you can't yet say what "done" means, or you're just tired. treat that as a constraint too.
e.g. "i don't even know the input limits" / "i can't say what done looks like" / "i'm running on fumes." the move is boring: fetch the missing fact, write the done sentence, or step away and come back.
because drift often feels good, you can't reliably wait for a "symptom."
looking at your scratchpad
artifacts are harder to rationalize than vibes.
paper has no working memory limit. it doesn't forget the bottom of the list when you add to the top. more importantly, by dumping state to the page, you break the phonological loop — risko and gilbert call this cognitive offloading. you don't have to keep chanting the problem to hold it, which frees up your brain to actually solve it. the implication is mildly humiliating: your inner monologue — the voice you identify as you thinking — is less a reasoning engine than a tape loop with a two-second buffer. the reasoning happens when you get it out of the way.
livelock: the same diagram drawn three times. code that circles back to where it started. you've written "dp" in three different places with arrows between them.
traction: crossed-out hypotheses. a growing table of test cases. a diagram that has expanded since you started.
drift: artifacts exist, but they're orthogonal. a polished component. a refactor. a new base class. none of it touches the problem statement.
stripping the mechanism words
mechanism words are just assumption-preservers. to break them, we need an engineering equivalent of cartesian doubt.
in 1619, a twenty-three-year-old soldier locked himself in a stove-heated room in germany and tried to take apart everything he knew. descartes had studied optics, geometry, the mechanics of falling bodies — and could no longer tell which parts of what he knew were real and which were just familiar. so he cut. senses, memory, the external world — removable until proven otherwise. he kept going until he hit the atom that wouldn't split: the bare act of thinking itself.
for the stuck engineer, the move is the same. choose to forget your tools. not because they're wrong — because you can't tell which parts are solving the problem and which are just solving themselves.
the naked problem is that foundation for engineers. we layer mechanism words (cache, optimization, microservice) on top of the actual requirement until we can't see the floor. strip the mechanism until you hit what you can't remove: inputs, outputs, constraints.
| domain | contaminated (mechanism words) | naked (physics only) |
|---|---|---|
| algo | "minimize operations by finding optimal dp state transitions that redistribute values" | "array of odd numbers. one operation moves value. make all equal." |
| systems | "we need a microservice architecture with event sourcing and CQRS to handle user signups" | "user signs up. we store their info. they log in later." |
| writing | "i need to find the right narrative vehicle, earn the turn with enough setup, and make sure the kicker recontextualizes the lede" | "i'm trying to explain one idea. the reader doesn't know it yet." |
| decision | "i need to evaluate whether this opportunity aligns with my long-term career goals given the risk profile and my current runway" | "do i want to do this?" (four words. sometimes the analysis is avoidance.) |
you're describing your approach, not the problem.
if you cannot strip the mechanism words, that's a strong signal — livelock. "dp" is the loop itself: saying it reloads the whole folder back into working memory.
if you can state it naked, you might have traction or drift. the next question is whether the story cashes out in reality.
the judgy duck
explain your current solution — not the problem, the solution you're building — to your most critical coworker. or a rubber duck that judges.
does the thought make you wince?
drift: you feel a flash of shame or defensiveness. "well, i have to get this component right first because..." traction: you feel boring. "i'm just writing the test cases."
you can convince yourself of anything — you share all your own assumptions. the judgy duck doesn't. when you imagine their questions ("why this? why now?"), you're forced to justify premises you'd normally skip over. if you wince, dig there.
shrinking to n=1
run the smallest possible version of the problem. one array element. one request. one user.
scale hides complexity. you can theorize about n=1000 using vague terms, but at n=1, the "dp state" evaporates and you're left with raw arithmetic. if the problem is still hard at n=1, that's signal. if it becomes trivial, your complexity was probably invented.
(trap: if n=1 works but n=1000 fails, the bug may live in interactions, timing, or gaps between units.)
is the constraint real?
sometimes you're not stuck on the problem. you're stuck on a constraint you invented.
i once spent hours on a packing problem — fitting circular covers onto a surface — before realizing the hole was square. the user said "cover the opening." somewhere between hearing that and starting work, i decided "circle." i don't know when. it felt like part of the problem, not a choice i'd made. the constraint enters as a decision and immediately disguises itself as a fact. by the time you notice it, it's load-bearing.
when i finally asked "wait, why circular?" the problem evaporated — not shrank. evaporated. a square packs at 100%. there was no packing problem. there never was.
same thing with algorithms. i had a graph problem once. shortest path. each directed edge has weight w: going with the arrow costs w, going against it costs 2w. and my brain quietly added a word the prompt never said: one. as if the task were: you get one reversal — pick the best edge.
so i started optimizing around that. dijkstra from start, dijkstra from end on the reversed graph, score each candidate reversal: dist(start, b) + 2w + dist(a, end). scan edges once, take the minimum. it felt clean.
then i reread the statement: there is no "pick one edge" step. the backwards edges just exist. you add them and run dijkstra once. the extra complexity was mine.
the move is always the same: list your constraints. for each one, ask — is this given (the user needs it, or physics requires it), or did i assume it? if you can't remember where a constraint came from, it's probably invented.
one trap: some constraints are invented at n=1 but real at n=1000. thread safety is optional for a script, mandatory for a server. the move is: delete the constraint to find the solution, re-introduce it to ship. if the solution breaks when you add it back, you've found where the real difficulty lives.
the watchdog timer
drift disables your internal alarm system. so i use a dumb external one: a watchdog timer.
- set a timer for ~3 hours of deep work.
- when it goes off: pause. strip the mechanism words.
- ask: "did the last ~3 hours touch the naked problem, or did i build something adjacent?"
if you rely on pain to stop you, drift can run for weeks. you have to rely on the clock.
reading the shape
| scratchpad | mechanism words | judgy duck | constraints | likely shape |
|---|---|---|---|---|
| looping, same ideas repeated | can't strip them | — | — | livelock |
| growing, expanding | can strip | feels boring | all given | traction |
| elaborate but disconnected | can strip | defensive | some invented | drift |
what the shape suggests
the taxonomy recommends different moves.
livelock → interrupt. when your working memory is captured by a bad frame, "try harder" mostly just reloads it. what helps me: stop, physically walk away, let the cache cool, then come back and restate the problem without mechanism words. the goal is not to think harder — it's to think with different furniture.
drift → collide. flushing doesn't fix wrong direction. it usually takes external contact. write the desired outcome in one sentence (no mechanism words). build the cheapest artifact that tests it. put it in front of the source of truth. for a blog post, that can be as small as sending the rough paragraph to one reader and asking what they think you're claiming. you need your story to hit something real — before you've invested months in it.
traction → protect. when you are solving the right problem and the space is shrinking, the fragile thing is context. it helps to externalize what you've ruled out, block interruptions, and not context-switch. traction is the only shape where "push through" is the right answer. so push.
the word "stuck" erases the geometry. it makes a dp loop and a slow drift and a real climb feel like the same kind of hard — and it tempts you into the same move every time: push harder. try again.
but a tight loop needs an interrupt, not more force. a slow drift needs a collision, not more hours. and a real climb needs protection, not a reboot.
descartes locked himself in a stove-heated room and stripped until he hit the atom that wouldn't split. for the engineer, that atom is the naked problem.
next time you're stuck, don't push. name the shape first. check whether the constraint is real. then move.
further reading:
- descartes, rené. discourse on the method (1637). the original.
- kahneman, daniel. thinking, fast and slow (2011). why system 1 hijacks the wheel.
- cowan, nelson. "the magical number 4 in short-term memory" (2001). the hardware constraint.
- collins, allan m. and loftus, elizabeth f. "a spreading-activation theory of semantic processing" (1975). why concepts prime each other.
- baddeley, alan. "working memory" (1992). the phonological loop — why speaking hijacks internal monologue.
- risko, evan f. and gilbert, sam j. "cognitive offloading" (2016). why writing things down frees working memory.
- luchins, abraham. "mechanization in problem solving" (1942). the einstellung experiments.