Jackie Stewart wasn't the fastest driver because of his reflexes. He was the fastest because he understood the car. He could feel the tyres losing grip before the skid started. He knew what the engine was doing before the rev counter caught up. He called this mechanical sympathy.
Martin Thompson borrowed the term for software engineering. His argument: the best code isn't written by people who know the language best. It's written by people who understand the hardware. Cache lines, memory layout, how the CPU actually executes instructions. You don't need to be a hardware engineer. You need to understand the layer beneath the one you're working at.
Abstractions all the way down
Every generation of software engineers has needed to understand the layer beneath their abstraction.
Assembly programmers needed to understand hardware registers and instruction sets. C programmers needed to understand memory layout and the stack. Java and Python programmers needed to understand what the runtime was doing: garbage collection, the event loop, how the VM actually executes.
Each layer made you more productive. None freed you from understanding the layer below.
Joel Spolsky wrote about this in 2002: the Law of Leaky Abstractions. All non-trivial abstractions, to some degree, are leaky. His key implication was that abstractions save us time working, but they don't save us time learning.
The pattern holds across the entire history of computing. Every abstraction has eventually forced engineers to understand what's underneath. Not because the abstraction was bad, but because no abstraction is perfect, and when it leaks, you need to know what's down there.
The new abstraction layer
Natural language is becoming the programming interface.
Every previous abstraction layer was deterministic. Same input, same output. Compilers, runtimes, frameworks: you could reason about them precisely. The new layer is fundamentally different. It's probabilistic. Non-deterministic. Context-sensitive in ways that aren't fully predictable.
This isn't just another abstraction. It's a different kind of abstraction. And that changes what "understanding the layer below" actually means.
What mechanical sympathy looks like now
So what's the layer beneath natural-language programming? What does the AI-age engineer need to understand? Well, some of it is technical. Some of it is closer to developing an intuition.
How context windows actually work. Not just "put good stuff in the prompt." The model's attention is finite. Tokens compete with each other. Position matters. Context rot is real: performance degrades as input grows, even on tasks the model handles well with shorter input. I've found that keeping context tight and structured consistently outperforms throwing everything in and hoping the model sorts it out. Jeff Huber at Chroma has done good research on this, and I wrote about it in my context engineering post.
How non-determinism propagates. Same prompt, different output. Same plan, different implementation. This isn't a bug to fix. It's a property of the medium. The engineer who understands this designs for verification, not reproducibility. You build checks around the output rather than expecting the output to be consistent.
Where the model is confident vs. where it's competent. Ethan Mollick's "jagged intelligence" captures this well. LLMs are superhuman at some tasks and fail at seemingly simple ones, and the boundary isn't intuitive. Mechanical sympathy here means developing a feel for where the model will fill gaps with confident guesses rather than flagging uncertainty. It's the quiet failures that catch you, not the obvious ones.
How instructions work differently. An instruction to a compiler is a contract. An instruction to an LLM is a suggestion weighted by context. Most frustration with AI coding comes from treating the second like the first. When you tell a model to "always use error handling," that's not an enforced constraint. It's a nudge that competes with everything else in the context window.
The key move is the same one Thompson described for hardware. Just as his mechanical sympathy meant writing code that works with the hardware rather than against it, AI-era mechanical sympathy means designing workflows that work with the model's actual properties: statelessness, probabilistic output, context-bounded attention. Stop pretending it's a fast, slightly unreliable human. It's a different kind of machine, and it works better when you treat it as one.
Do we need a new language?
Someone recently argued that what we need is a higher-level formal language designed specifically for AI tooling. I understand the instinct. It's the traditional engineering response to ambiguity: formalise it away.
But I think it misses what's changed. Natural language is the interface. The ambiguity isn't a bug in the syntax. It's a property of the medium. A formal DSL on top of natural language would be adding a leaky abstraction over a leaky abstraction.
That said, structure still helps. Specifications benefit from discipline: Given/When/Then, domain language, clear scenarios. But the specification is still natural language, not a new formal syntax.
The real skill isn't removing ambiguity from the language. It's learning to work within it. Writing prompts that constrain without over-specifying. Designing verification that catches what ambiguity lets through. Building workflows that assume non-determinism rather than fighting it.
What falls out of this
If mechanical sympathy is the foundational principle, understanding the layer beneath your abstraction, then several practical conclusions follow naturally.
Operational knowledge is the moat. You need to understand what happens when the code runs, not just what the code says. The system layer sits beneath the code layer. It always has, but it matters more when you didn't write the code yourself. I wrote about this in Build Fast, Learn Slow: the gap between what you can build and what you can operate is where things break.
Context quality bounds output quality. The model operates probabilistically beneath your natural language instruction. If you don't understand how context shapes output, you'll blame the model when the problem is what you fed it.
Never send an LLM to do a linter's job. Understand which layer handles deterministic checks (the toolchain) and which handles judgement calls (the model). Using the wrong layer for the wrong job is a failure of mechanical sympathy. If something can be checked mechanically, it should be. Save the model for the parts that actually need probabilistic reasoning.
Ask rather than assume. When you understand how the model fills gaps, confidently, silently, plausibly, you design workflows that force uncertainty to the surface. You ask the model to flag what it's unsure about. You build review steps that specifically look for confident-sounding guesses. You don't let fluent output substitute for verified correctness.
Watch for coherence drift. Each context window is stateless and local. The model has no memory of what it said three sessions ago, or even what it said at the top of a long conversation. When you understand this, you expect global incoherence and design for it. You verify consistency across sessions rather than assuming it.
These principles all stand on their own as practical guidance. But they're all instances of the same deeper thing: understand the layer beneath the one you're working at, and design your practice around its actual properties.
The constant that keeps changing
Every generation of engineers has faced this. The old abstraction gives way, the new one forms, and the question is what you need to understand now that you didn't before. Assembly programmers needed to understand the machine. C programmers needed to understand the memory model. We need to understand probabilistic inference, context dynamics, and the gap between generation speed and operational knowledge.
The abstraction layer has changed. The principle hasn't. Understand the layer beneath the one you're working at. It's always been how software engineering works. It's still how it works now.
This is thinking in progress, and I'm still working through what it all implies in practice. If you're finding the same things, or different things entirely, I'd love to hear about it. Drop me a line.