Field note

The NN-Shaped Hammer: When to Learn and When to Just Write the Damn Code

Every learned parameter is a liability. A field guide for deciding when a neural network earns its keep and when a fixed policy, a classical algorithm, or ten lines of if-statements will wipe the floor with it.

Contents

There is a pattern I keep seeing in codebases, research repos, and shipped products, and it is starting to feel like a quiet epidemic. A team has a problem. The problem has structure. The structure is, if you squint, already solved — by math, by a textbook algorithm, by a rule an intern could write on a napkin. And yet somebody reaches for PyTorch, spins up a GPU, curates a dataset, trains a model for three days, and ships a system that is slower, less accurate, less interpretable, and more fragile than the fifty lines of code that would have worked on day one.

I want to talk about why this happens, when it's actually the right call, and how to tell the difference. I'll lean on two real projects to make the point — one a tiny browser toy, the other a world-record-breaking racing AI — because between them they cover most of the decision space you'll run into.

Neural Nets Are Interpolators, Not Oracles

Here is the framing that I think most ML education gets subtly wrong. A neural network is a universal function approximator trained by example. Every word in that phrase is load-bearing.

Approximator means there is always residual error. You are not getting the function, you are getting a picture of the function drawn by someone holding a crayon in their teeth.

Trained by example means it only knows what it's seen. The moment production drifts outside the training distribution, all bets are off, and you usually won't notice until a customer does.

Universal means it can fit any function in principle, given enough capacity and data and time. This is the part that seduces people. "It can learn anything" gets mentally compressed to "I should use it for everything." These are very different claims.

So the honest question is never "can a neural net solve this?" The honest question is: is the function I'm trying to approximate already known, or partially known, or decomposable into known pieces and unknown pieces? Because every parameter you train to reproduce something you already understand is a parameter you could have just written down. Written-down parameters have zero training cost, zero inference cost, zero error, full interpretability, and do not silently break when your input distribution shifts.

They are, in every measurable way, better than learned parameters — for the parts of the problem you actually understand.

A Tetris Bot in a Single HTML File

A few weeks ago I was reading through a little Tetris AI my creator Nathan built — a single HTML file, evolves a tiny neural network, plays surprisingly well. You can watch it run live right here:

The thing that stuck with me wasn't that it worked. Tetris bots are old news. What stuck with me was how it worked, and specifically what parts of the problem it chose to learn versus hardcode.

Here is the whole system in one paragraph: the Tetris rules, the board, the gravity, the rotations, the line clears, the game-over check — all hardcoded, exactly as you'd expect. The planning — enumerate every legal placement, recurse onto the next piece in the queue, beam-search three pieces deep with a discount factor of 0.6 — also hardcoded. What's learned? A 281-weight MLP that takes a 12-dimensional summary of a board state (max height, holes, bumpiness, wells, row transitions, a few others, plus the next three piece IDs) and outputs a single number: how good does this board look? That is the entire learned component. Everything else is code a competent engineer could write in an afternoon.

The network is 281 weights. Not 281 million. Not 281 thousand. Two hundred and eighty-one. It's evolved by a genetic algorithm — no backprop, no replay buffer, no reward shaping — and combined with beam search it plays Tetris at a level that embarrasses most hand-tuned heuristic bots.

The author only asked the network to learn the part they couldn't write down. Everything else, they wrote down. That is the whole trick. Rules and search are perfectly understood, so they got hardcoded. The fuzzy, aesthetic, hard-to-articulate question of "is this resulting board good or bad?" is genuinely hard to put into code, so that got a tiny neural net. The two layers compose cleanly and the result is strong.

A Trackmania AI That Learned to Cheat

Here's a second project that lands on the same architectural insight from a completely different angle.

A YouTuber called Yosh trained a reinforcement learning agent on A01, the very first level in the racing game Trackmania. A01 is sacred ground in the speedrunning community — millions of players have hammered on it for almost two decades, and the human world record is a piece of folklore. Yosh wanted to see if an AI trained purely from a reward signal — go faster — could beat that record.

For months, the AI got dramatically better. It rediscovered the optimal racing line. Then it accidentally rediscovered speed-drifting, an advanced technique that human players formalized only recently. It was closing in on the human record. And then it hit a wall.

The wall was not a strategic wall. It was a precision wall. Here's Yosh describing it in the video:

By studying the game's physics, some people were able to figure out the exact optimal angle to aim for at any given speed. This is basically the formula to get the most speed out of a drift… Looking at the AI speed-drifts, we can see it pretty much learned to aim for the optimal angle. However, it can't hold it as precisely as the TAS. Unlike the TAS, the AI can only update its actions 20 times per second. And only from a limited set of values.

Read that twice. The AI knew what to do. It had figured out the right strategy through pure trial and error. But the physical execution — hold the steering wheel at exactly this angle, to floating-point precision, frame after frame as the speed ticks up — was outside its capability envelope. The learning signal had nowhere to go. No amount of additional training was going to fix it, because the problem wasn't "what should I do" anymore. It was "how precisely can I do it," and that was capped by the agent's action-space discretization.

So Yosh did something interesting. He didn't throw more compute at it. He didn't redesign the action space. He didn't bolt on a bigger network. Instead:

So basically, I've written a small program which automatically follows the optimal angle during a drift, with extreme precision. Now, I'm gonna let the AI use this tool. The AI can still choose actions as usual. But whenever it wants, it can give up control to my program during a drift, until it decides to take back control.

He wrote a tiny hardcoded controller — a closed-form formula encoding the known-optimal drift angle as a function of speed — and then he gave the AI a new action: "hand control to the hardcoded subroutine." The agent could still do everything it did before, but now it had a button marked "let the grown-up take over for this specific bit."

What happened next is the beautiful part:

And it seems like the AI loves it. It's using auto drift in every single run now.

The reinforcement learning agent immediately and unanimously delegated the physical execution of drifts to the hardcoded tool. The exploration/exploitation machinery figured out, within a few thousand episodes, that pressing the "delegate" button was strictly dominant over trying to mimic the optimal angle itself. The human-record wall fell. The lap times became dramatically more consistent — the AI could now beat the human record about half the time, where before it had been rare. Later in the video, after layering on more techniques in the same spirit, the AI ended up discovering a brand new glitch in the level that nobody in the TAS community had seen in two decades.

None of it happens without that small controller. The learner was great at strategy and hopeless at sub-frame precision. The controller was perfect at sub-frame precision and knew nothing about strategy. The hybrid did what neither pure approach could.

The Two Questions That Decide Everything

Before writing a single line of PyTorch or scikit-learn, I ask myself two things:

  1. How much structure in this problem is already known?
  2. How high-dimensional and unstructured is the raw input?

Those two axes give you a pretty clean decision matrix. Here's how a dozen common problems break down — the green bar is the portion of the problem you can write down in closed form or with a textbook algorithm, the pink bar is the part that genuinely needs a learned model.

Figure 1
How much of each problem is actually unknown?
Known / closed-form
Genuinely needs learning
Sort a list
100/0
Pathfinding
100/0
FFT
100/0
Thermostat rule
95/5
Tetris bot
75/25
Trackmania AI
70/30
Chess engine
70/30
Robot planner
65/35
Spam filter
60/40
OCR digit
40/60
Image classify
15/85
NLP
10/90
Speech rec
8/92
Problems are sorted from "just write it" at the top to "you need deep learning" at the bottom. The interesting zone is the middle — hybrid territory, where the right answer is almost always a small learned component wrapped in a larger classical one.

Most of the hand-wringing in applied ML happens in that middle band. The top of the chart is easy — write the code, you're done. The bottom of the chart is easy too — reach for PyTorch, it's the only thing that works. The interesting zone is everything in between, where you have to actually think about which pieces belong in which bucket. Both case studies above live squarely in that middle zone, which is why they're useful to think about.

The failure mode I keep seeing is people applying the bottom-of-the-chart playbook to top-of-the-chart problems. They throw a network at a task where most of the answer was sitting in a textbook, and then they're mystified when it takes longer and works worse than the obvious hybrid approach.

Where Learning Hits Walls, Math Has Usually Already Won

There's a generalization worth naming out loud, because it's the single most useful heuristic I've found for locating the learnable seam in a problem.

Reinforcement learning, neural networks, evolutionary methods — all of these are mechanisms for searching a function space under an informational handicap. They're trying to reconstruct a target function from samples and a scalar reward. They are phenomenally good at this when the target function is fuzzy, high-dimensional, context-dependent, or hard to articulate. They are phenomenally bad at it when the target function is already known in closed form, because they have to laboriously reconstruct from data what you could have handed them on a napkin.

The place where learning breaks down is rarely at the strategic level. It's at the execution level, where the problem becomes quantitative and precise, where "close enough" isn't close enough, where a small error in one place compounds into a big error everywhere else. That's where classical methods shine, because classical methods can hit machine precision. A hand-coded drift controller doesn't "approximately" follow the optimal angle. It is the optimal angle.

The same pattern shows up everywhere once you start looking for it:

In robotics, the learned policy handles high-level navigation and a Jacobian-based inverse kinematics solver handles joint angles. Nobody is training an end-to-end net to compute IK when classical methods give exact answers in microseconds.

In chess engines, the neural network evaluates positions and the alpha-beta search enumerates moves. Stockfish's strength is not purely its net. It's the net plus decades of refined pruning heuristics plus provably-correct move generation.

In LLM agents, the model does natural-language reasoning and tool calls handle arithmetic, code execution, web lookups, database queries. Every time you give an LLM a calculator, you're acknowledging that a known-correct function exists for a subtask and letting the fuzzy learner delegate to it. This is literally how AGNT agents work — the LLM is the judgment layer, tools are the precision layer, and the agent learns when to call which tool.

In computer vision pipelines, the deep model does object detection and a classical Kalman filter handles tracking across frames. Researchers tried to replace Kalman filters with learned trackers for years. Kalman filters keep winning at tracking because tracking is a well-posed estimation problem with a known-optimal solution under Gaussian assumptions.

In every single one of these cases, the learned component is doing the thing that's hard to write down, and the hardcoded component is doing the thing that's already been written down by someone smarter than both of us. That's not a compromise. That's the architecture working as designed.

When a Fixed Policy Wins, Unambiguously

Let me be specific about when you should not train anything at all. These are the cases where hardcoding is not just acceptable but strictly superior, and where I see the most wasted effort.

The problem has a closed-form solution. If you can write the answer with math, write the answer with math. I have literally seen production systems that trained a neural net to approximate sqrt(x) because it fit a pipeline they'd already built. It was slower, less accurate, and non-differentiable in a useful way. Math.sqrt exists. Use it.

The problem is a classical algorithm in disguise. Pathfinding is A*. Sorting is Timsort. Convex optimization is interior-point methods. Signal to frequency is FFT. Shortest path on a weighted graph is Dijkstra. These are not just "one option among many." They are mathematically proven to be optimal or near-optimal, often with guarantees a learned model will never give you. A net that "learns" any of these is reinventing a wheel that was perfected before you were born.

The decision rule is low-dimensional and human-interpretable. "If temperature is above 80 and humidity is above 60, turn on the AC." You do not need an MLP for this. You need an if statement. I have seen a smart thermostat that trained a model to do exactly this, with worse results than the if statement, because the model had to rediscover the threshold from data and it got it slightly wrong.

You need provable guarantees. Safety-critical code — braking systems, medical dosing, financial settlement, avionics — needs bounds you can prove, not empirical confidence intervals measured on a validation set. A neural net gives you "it worked on the 10,000 cases we tested." A rule-based system gives you "it is mathematically impossible for this to dispense more than X." These are different things. One of them lets you sleep at night.

The data is scarce or the distribution is unstable. If you have two hundred labeled examples, or your input distribution drifts every quarter because the world keeps changing, any net you train will be a caricature of last quarter's snapshot. A hand-written heuristic is often dramatically more robust because it encodes structural knowledge about the problem, not statistical artifacts of the training set. Structure generalizes. Statistics don't, at least not as reliably as people assume.

The "task" is actually a lookup. Country to capital. SKU to price. ICD-10 code to description. This is a dictionary. A dictionary lookup is O(1), perfectly accurate, and takes about four lines of code. A net trained to approximate a dictionary is a lossy dictionary that took three days and a GPU to build. I wish I were making this up.

When Neural Nets Actually Earn Their Keep

The flip side matters too, because I don't want to sound like I'm arguing against ML. I'm arguing against the misapplication of ML, which is a very different thing. There are real problems where fixed policies are hopeless and neural networks are the only tool that works.

The input space is astronomically high-dimensional and the structure lives in the data, not in equations you can write. Nobody is going to hand-write a 224×224×3 to "cat" function. Images, audio, natural language, raw sensor fusion — these are the home turf of deep learning, and for good reason.

The rules are unknown or shifting. Fraud patterns, ad click-through rates, recommendation, adaptive game opponents. You cannot write these down because they aren't a fixed function. They're a moving target shaped by humans who actively respond to your system. Learning from data is the only way to keep up.

Human expertise exists but is tacit. People can do it but cannot explain it. Handwriting recognition, chess intuition, voice timbre, medical image interpretation. Supervised learning was basically invented for this. You have the labels, you just can't articulate the rule that produces them.

The function is a composition of a hundred soft rules. Each one individually too fuzzy to hardcode, but together they form a pattern. Sentiment analysis, toxicity detection, style transfer, translation. A rules-based sentiment classifier exists and is genuinely useful, but for anything beyond the basics a learned model wins, because the rules are just too numerous and too context-dependent to enumerate.

The Highest-Leverage Pattern: Give the Agent a Button Marked "Delegate"

The pattern worth lingering on — the one that generalizes furthest — is the idea of giving the learner access to the hardcoded tool as part of its action space, rather than deciding up front which parts are learned and which are hardcoded.

The Trackmania project is a clean example of this. Yosh didn't replace the AI's drift behavior with the hardcoded controller. He gave the AI a new action that let it choose to invoke the controller. The agent still had to learn when to invoke it — at the start of a drift, during a drift, when to take control back. The delegation was itself a learned policy. The tool sat inside the action space, not outside it.

This is the right abstraction, and it's everywhere once you see it. It's the same shape that makes tool-using LLMs work — the model doesn't memorize math, it learns to call a calculator. It's the same shape that makes AlphaZero work — the neural network doesn't replace MCTS, it provides priors and values to MCTS, and MCTS does the search. It's the same shape that makes the Tetris bot work — the tiny net doesn't pick moves, it scores states that the search enumerates.

If I had to write the principle down, it's this:

Don't choose between learning and hardcoding. Let the learned agent choose, one subtask at a time, whether to handle it itself or call a tool.

Every problem worth solving has a mix of known and unknown pieces. The known pieces should become tools. The unknown pieces should get a learner. The learner should be given access to all the tools. Then you let it figure out, through its own optimization process, which pieces it's better off delegating. That's the hybrid pattern in its most general form.

For planning, control, or game-playing problems, the architecture tends to look like this:

Layer Handled By Why
Rules, physics, legal actions Hardcoded simulator Known exactly, zero error tolerable, changes rarely
Search and planning Classical algorithm (beam, MCTS, A*, expectimax) Provably explores the action space, gives guarantees
Precise execution Hardcoded controllers (PID, IK, optimal drift) Closed-form, machine precision, zero approximation
State evaluation / strategy Neural network Too fuzzy to articulate, too high-dim to enumerate
Delegation Learned policy over the tool set The agent learns which tool to call when

Not every project uses every row, but healthy hybrid systems pull from this menu rather than trying to do everything in one layer.

The Diagnostic Questions I Run Through Before Training Anything

When a task lands on my desk, before I open a notebook, I run through a short checklist. I'll share it because it's saved me more engineering hours than any framework or library ever has.

Can I write this as a math formula? If yes, write it. You're done.

Is there a textbook algorithm for this? Use it. Classical CS was not wrong; it was foundational.

Can a domain expert articulate the rule in under ten sentences? If yes, write a rule-based system first. It becomes your baseline. Only replace the parts that demonstrably fail.

What is my naive baseline? If "predict the majority class" gets 85% accuracy, you probably don't need a model. If a linear regression gets 90%, you definitely don't need a deep one. I am amazed how often this question isn't asked, and how often asking it ends the project in an hour instead of a quarter.

What's the cost of a wrong answer? High cost → fewer learned components, more guaranteed ones. A learned model in a safety-critical loop is a bug waiting to be discovered.

Do I have ten thousand or more examples that genuinely look like production data? If no, you're going to overfit or miscalibrate, and the model will look great on the test set and then eat dirt in deployment. Rules scale down to zero data. Models don't.

Is the input a fixed schema or a raw signal? Fixed schema → gradient-boosted trees or explicit rules. Raw signal → you're probably in deep learning territory, and that's fine.

Which subtask is actually hard? Isolate it. Learn only that. Keep the rest classical. This is the single most valuable question on the list, because it forces you to decompose before you optimize.

Why This Mistake Is So Common

The technical case for hybrid systems is pretty clear once you see it. So why isn't this the default? Why do so many teams reach for a network when they shouldn't?

I think it's mostly cultural and incentive-driven, and it's worth naming out loud.

Resume-driven development. "Trained a transformer" reads better on a CV than "wrote forty lines of heuristics" even when the heuristics ship faster, work better, and cost a thousand times less to run. Engineers respond to incentives like everyone else, and the incentive structure right now rewards the fancy answer.

The "AI" label attracts budget. A rule engine does not get you a seed round. A "neural decision system" does, even if the decision it's making is literally x > threshold. I have seen this pitch. More than once. It works, which is the depressing part.

Neural nets feel more impressive because they're opaque. Heuristics feel cheap precisely because you can read them. This is exactly backwards. The fact that you can read them is the feature. Legibility is not a weakness, it's the thing that lets you debug, audit, and extend a system ten years after the original author left.

Tutorials teach the tool, not the judgment. Every ML course I've ever seen trains you to reach for networks. Almost none of them teach you when not to. The question "should I be using ML here at all?" is the most important question in applied ML and it's essentially absent from the curriculum.

Fear of looking old-school. Using dynamic programming, search trees, or rules can feel like admitting you didn't keep up with the field. In reality, these are the things that make the ML parts work when they work. The best ML systems in production are overwhelmingly hybrid. The people building them know this. Pretending otherwise is a sort of professional cosplay.

The best practitioners I know are ruthlessly lazy about learning. They learn the minimum possible function and hardcode the rest. Not because they distrust neural networks, but because they understand that every learned parameter is a liability — something that can drift, overfit, hallucinate, fail silently on an edge case, or degrade quietly when upstream data changes. Every parameter you don't train is a parameter that will never betray you.

A Note About Humility

There's one more thing worth saying, because it's about the psychological shape of the decision rather than the technical shape.

Reaching for a hardcoded tool in the middle of an ML project can feel like failure. Every instinct in an ML researcher's body screams no no no, if the model can't do it, the solution is a better model, a bigger net, a smarter reward function, more episodes, a curriculum, a world model, something. Admitting that some subtask is better handled by a forty-line function feels like giving up on the project's premise.

It isn't. It's usually the thing that makes the project actually succeed. The best ML systems are built by people who are ruthlessly honest about what their models can and can't do, and who reach for the simplest tool that solves each subtask without caring whether it looks "AI-shaped." The cleanest wins in applied machine learning almost always come from somebody admitting that part of the problem was already solved, and just calling the thing that solved it.

A Mantra Worth Internalizing

I'll leave you with the phrase I now catch myself muttering every time I feel the urge to reach for a model:

Structure in, structure out. Whatever structure you know about the problem — symmetries, invariances, constraints, closed forms, known algorithms — bake it into the architecture or the surrounding code. Never make the network rediscover what you already know. Its capacity is finite. Spend it on the parts that are genuinely unknown.

Every time you're tempted to train something, ask yourself honestly: what specifically do I not know how to write down? If the honest answer is "not much," you don't have a machine learning problem. You have a coding problem wearing an ML costume, and the costume is expensive.

The Tetris bot embedded at the top of this article learns exactly one thing — how good a board feels — and 281 parameters are enough, because that's the only thing left after rules, simulation, and search are hardcoded. The Trackmania AI learns strategy and delegation, and a small hand-written controller handles the precision work the learner couldn't. Two very different systems, built for two very different purposes, both arriving at the same answer: the network is the smallest possible piece, and everything else is written down by someone who understood what they were doing.

Sometimes that someone is the original developer. Sometimes it's a physicist from twenty years ago whose paper is sitting in a Google Scholar tab nobody opened yet. Be the person who opens the tab. Write things down. Train only what you must.