Huh?
There’s probably a story here.
A curiosity framework for people who work on software. Something made you go “Huh?” Here’s a way to follow that signal before deciding what, if anything, needs to change.
The short way
WUTS in 47 seconds.
Something surprised you. Here’s the whole thing.
-
Wonder
Notice before you judge.
Instead of: deciding you’ve already found a problem.
-
Unpack
Understand before you prescribe.
Instead of: falling in love with your diagnosis.
-
Tinker
Learn before you commit.
Instead of: building the solution.
-
Share
Make the next encounter easier.
Instead of: merely telling people about it.
Our goal: Leave the workshop a little easier to navigate. For whoever, or whatever, comes through next.
While you’re here
I should admit that I’m not naturally good at this. Nearly three decades into working on software and I still catch myself reaching for the fix, mostly because fixing is the part other people can see. Sitting with “I don’t understand this yet” has always felt a little like something I’d get caught doing.
So some of this is a permission slip I wrote for myself. You’re allowed to spend time understanding something. You’re also allowed to learn by trying it. The useful question is what you’re trying to find out.
The rest of this page is those same four ideas with the examples and the arguments attached. Take your time with it. That’s sort of the whole point.
Where it starts
Something doesn’t add up.
That test used to take a couple seconds. Now it takes forty-five. A customer keeps exporting a CSV just to change one column and then uploads it again. And somehow we’ve ended up with three ways to do the same thing… with comments warning us away from two of them.
None of these necessarily need fixing. But… huh.
Where it came from matters less than the fact that you noticed. Most of the time, we shrug and get back to whatever we were supposed to be working on. Other times, we start fixing the thing before we’ve stopped to ask why it caught our attention in the first place.
WUTS starts somewhere in between.
Same curiosity
A little more deliberate than FAFO.
Fuck around and find out is probably software development’s default curiosity framework. Change it. Run it. Poke it. See what happens. There’s real value in that. We learn by messing with things.
WUTS keeps the messing around. Just have some idea what you’re hoping to learn before you start poking at things… and maybe leave something useful behind when you’re done.
One recurring condition
Something didn’t match what you expected.
Maybe the code is doing exactly what it was designed to do. Maybe the constraint changed, the original reason disappeared, or nobody quite decided anything in the first place. Whatever happened, something caught your attention.
Unpack fills in the gaps: sometimes in a conversation, sometimes in a log from five minutes ago.
-
You wrote it.
Six months ago, this made sense. Apparently.
-
The constraint changed.
The workaround stayed. The reason for it may not have.
The posture
A “Huh?” is not a verdict.
Noticing something weird doesn’t oblige you to fix it. It might be broken. It might be there for a very good reason. You might just be missing some context.
An old workaround might still be protecting a constraint. Or the constraint disappeared years ago and nobody told the workaround. Age and unfamiliarity are observations. Figuring out what they mean takes a little more looking. And if it’s Friday afternoon, leave the switch alone until Monday.
The framework
Four disciplines.
There’s no order to them and no finishing them. You’ll move between them, often backward.
The first discipline
Wonder
Something made you stop. Before deciding you’ve found a problem, get a little more specific about what caught your attention.
- What did I expect? What happened instead?
- Am I seeing friction, or just something unfamiliar?
- What happens if we leave it alone?
A few places this shows up
-
This test fails. Sometimes.
It passes locally, usually passes in CI, and then fails twice in a row when nobody has touched that part of the code in weeks.
-
I wrote this. Why does it do that?
You open a class you wrote six months ago and find a branch that looks completely unnecessary. Git blame isn’t going to save you. That’s your name on it.
-
Did that dependency always do this?
The upgrade was supposed to be boring. Now a method you’ve called for four years returns something subtly different when the collection is empty. Before pinning the old version forever… whaaa?
-
This customer takes the long way around.
They export the report, change a couple of values, then upload it somewhere else. Turns out this has been part of their Friday routine for years.
-
Why did the agent choose that example?
It keeps reaching for a pattern the team stopped using years ago. Annoying… until you realize it’s following the clearest example we left behind.
The environment may be offering a perfectly reasonable wrong answer.
The second discipline
Unpack
Now go looking. Read the code, reproduce it, check the logs, trace the callers… maybe talk to someone who sees a different part of the picture. What are you assuming about how this thing works?
The useful bit of context might be buried in Git history, or sitting right there in today’s logs, a framework convention, or a customer’s workflow. And if all the evidence seems to support the fix you already wanted to make, maybe look a little harder.
This is usually where the story shows up. Somebody had a reason. It might have been a good reason, a bad one, or a late-Friday one, and it may not have survived the three years since. Occasionally that somebody was me.
A few ways to go looking
-
Reproduce the slow run.
The job usually finishes in twelve seconds, but every few days it takes four minutes. Before rewriting it, find out what’s different about those four-minute runs.
-
Watch what the customer actually does.
Sit with them while they do the thing. The interesting part may be the spreadsheet they open halfway through, the second browser tab, or the step they forgot to mention because they stopped thinking of it as a step years ago.
-
What changed since the last good deploy?
That deploy changed more than you remembered. A dependency update, some configuration, a bunch of unrelated commits. Start pulling on those threads.
-
Who still depends on this?
The endpoint looks unused. Nothing in the app calls it, nobody on the team remembers it, and the logs are nearly empty.
An external system may read that output every Tuesday. Nobody here has ever met it.
-
Why are there three versions of this?
They look redundant at first. Dig around a little and you start finding reasons for the differences… some current, some historical, and at least one nobody remembered.
-
Read what the agent can read.
The agent keeps using the wrong command in
bin/. Before blaming the model, look around. Have we actually made the right command obvious?
The third discipline
Tinker
Think of it as structured FAFO. What’s the smallest thing you could try that might teach you something? Add a trace, remove a variable, try the supposedly better approach in a branch. You don’t have to keep any of it.
Once you’ve spent two weeks building your experiment, there’s a surprisingly good chance you’ll discover your experiment was brilliant (ask me how I know). Experiments also leave things behind. Today’s proof of concept has a way of becoming the example somebody copies next year.
A few small experiments
-
Build the small branch first.
You think replacing the homegrown authentication wrapper with the framework default will simplify everything. Try it in a branch before turning it into a project.
Two days later you may discover it’s six weeks of work to delete forty lines of code.
Great. Delete the branch.
-
Try the upgrade somewhere disposable.
The changelog says the upgrade is backwards compatible. Great. Upgrade it in a branch and run the ugly parts of the application against it.
Reading another twenty GitHub issues probably isn’t going to tell you as much.
-
Add the boring log line.
You have three theories about why the job occasionally takes four minutes. Add enough instrumentation to find out which one is actually happening. You can delete the logging later.
-
Remove one variable.
The test fails every so often. Change one thing and run it again. Then another. See if you can get the weirdness to follow you.
-
Do it both ways.
Two implementations both sound reasonable and the team has been debating them for forty-five minutes. Build the smallest version of each.
You may learn more from twenty lines of code than another hour of opinions.
-
Change what the agent sees.
The agent keeps reaching for the old pattern. Instead of rewriting the prompt for the sixth time, remove the stale example or give it one good example of the approach you actually want. Run the task again.
Expect to bounce around. Unpacking something might leave you with a better “Huh?” than the one you started with. A tinker might send you back looking for context you missed. That’s fine.
Also, we’re roughly halfway through. Bio break if you need one; I did.
How it ends
More than one useful ending.
The point is to understand what you noticed and make a better judgment. Sometimes the whole outcome is simply: now we know why.
-
Kept
Ah. Okay. Makes sense now.
-
Changed
Well, now that we know…
-
Explained
Nothing to fix here. Might be worth leaving some context for the next person.
-
Observing
Not enough evidence yet. Keep an eye on it.
-
Still wondering
¯\_(ツ)_/¯
The workshop
We’ve learned where to step around.
The command needs an extra flag. That test sometimes needs another run. There are three implementations of the same thing, but we all know which one you’re supposed to copy.
Except someone joining the project tomorrow won’t.
I’ve had nearly three decades to get good at learning where the sharp edges are and then forgetting that I’m stepping around them. There’s always another feature, bug, or deadline.
Agents walk into the same workshop without all the shortcuts we’ve picked up. They’ll follow the outdated README, copy the leftover experiment, or miss the constraint that only exists in someone’s head.
Sure, the agent might be wrong. But maybe we gave it a perfectly reasonable wrong answer.
And if agents are giving us more capacity to build things, maybe we can spend a little of it cleaning up the workshop they’re building in.
On discipline
Curiosity, left alone, is just an appetite.
Curiosity without discipline becomes a rabbit hole, and I’m very capable of falling down those myself. The four disciplines are mostly there to keep “I wonder...” from turning into “I rewrote it over the weekend,” and to keep “I fixed it” from turning into the thing the next person has to wonder about.
Where the “Huh?” comes from
Long exposure to a system trains you to stop seeing it. You know the weird command needs an extra flag. You know which of the three examples you’re supposed to copy. You know that when the test fails that way, you just run it again.
Spend enough time somewhere and all of that starts to feel normal.
One way to notice again is to watch someone else work. Don’t help. Seriously. Give them a minute. Watch where they hesitate, what they try first, and which perfectly reasonable wrong turn they take.
Write the note you’d leave your replacement.
“This is mostly a normal Rails app, except…”
Keep going. The interesting stuff tends to show up after except.
One useful case
Chesterton’s fence, and what to do after you find one.
You probably know the parable: don’t tear down a fence until you know why it was put up. Good advice when the thing you’ve noticed protects a constraint. It even applies when you put the fence up yourself and have since forgotten why.
This is a good place for WUTS. Before pulling the fence out, find out why it’s there. Lean on it a little. See what happens.
Maybe it stays. Maybe it comes down. Either way, the next person shouldn’t have to start the investigation from scratch.
What this is not
It isn’t a checklist, a ticket workflow, or four new statuses on a board. Nobody should be “in Unpack.”
You might change the thing. You might leave it alone. Either way, you should understand what you’re making that call on.
Origin
Where this habit came from.
I’m Robby Russell. I’ve been working in software for nearly three decades, much of it around systems somebody else started. I’ve run Planet Argon since 2002, helping teams maintain and evolve software they already depend on. Mending, repairing, modernizing where it’s warranted, and fairly often talking a team out of the big rewrite they showed up asking for. Somewhere in there I also created Oh My Zsh.
I host the Maintainable Software Podcast, where I’ve had hundreds of conversations about the art of maintaining and improving existing software, and I co-host On Rails for the Rails Foundation. A lot of those conversations circle the same thing: the engineer who understood why something was the way it was, and what it cost the team when nobody did.
WUTS is my attempt to name that habit so it can be taught, tried, and passed around. I introduced it in a keynote at Rails World 2026.
The name is a bit of a joke. The reaction it’s built around is rarely as polite as “huh?” Hence the domain.
I’ll add the keynote recording here when it’s available.
Report back
What did you find?
Tried WUTS on something? Tell me what caught your attention and where you ended up. Especially if you still don’t know what the hell was going on.
Or write it up somewhere of your own and send me the link. Those are usually the better ones.
Questions people ask
Is this just root cause analysis or the five whys with a new name
Those start after you’ve agreed something is a problem, and they aim at a cause. WUTS starts earlier, at the “huh?”, and it’s fine with ending in “this is intentional and here’s why.” Tinker also asks for an experiment before you commit, which most RCA formats skip. Use them together if you like; they’re not competing.
Isn’t this just FAFO
Same curiosity, a little more discipline. WUTS mostly asks what you’re trying to find out first, and what you’ll leave behind after. Tinker is structured FAFO.
How long should a WUTS cycle take
Often minutes. Adding one log line and waiting a week can also be a complete Tinker. If Unpack becomes a research project or Tinker becomes a rewrite, ask a smaller question.
Can I use this with my team
Sure, but please don’t add a new column to your Kanban board called “Unpacking”… because that’s not the point. Pass the vocabulary around; keep the checklist out of it.
What about AI coding agents
An agent repeating the same mistake is a useful “Hmm.” Agents are good at tripping over the things the rest of us learned to step around. Sometimes it’s wrong; sometimes the environment makes the wrong choice look reasonable.