UVAL: The Protocol I Built to Stop Accepting Code I Don't Understand
Jeremy Twei coined it. Addy Osmani popularized it. Margaret-Anne Storey extended it to teams. Here's what I built to fight all three.
The moment I remember most clearly isn’t dramatic. It was 11pm on a Tuesday. A session management bug in the platform I’m building at Méthode Aristote was blocking a demo the next morning, so I asked Claude Code to fix it. It generated a 40-line solution touching three files, restructuring how tokens were refreshed. I read it quickly, ran the tests, they passed, I shipped it.
Three weeks later, a colleague asked me why we refreshed tokens on every request instead of on expiry. I opened the file. Read it again. And I couldn’t explain it. Not the reasoning, not the trade-offs, not the alternative I’d apparently rejected. I could point at the code and say “that’s what it does,” but not “that’s why we made this choice.”
The code was correct. The understanding was gone.
The problem has a name now
For a while I thought this was a discipline issue. A me problem. Then two researchers gave it actual names.
Jeremy Twei coined the term comprehension debt: code that works, code you’ve shipped, code that lives in your codebase, but code you don’t actually understand. Addy Osmani (Google Chrome team) picked it up in January 2026 and built it into a framework for thinking about AI-assisted development. The framing is precise: comprehension debt is distinct from technical debt, where you know the code is suboptimal and have a plan. Comprehension debt is invisible. You don’t know you have it until someone asks you to explain your own system, and you can’t.
Margaret-Anne Storey (Canada Research Chair, University of Victoria) expanded the idea to teams with cognitive debt: the loss of shared mental models across a codebase and the people maintaining it. Her framing is precise: “Cognitive debt tends not to announce itself through failing builds or subtle bugs after deployment, but rather shows up through a silent loss of shared theory.”
Both are describing the same failure mode at different scales. Osmani is talking about what happens to you individually when AI generates code faster than you can absorb it. Storey is talking about what happens to a team. The signature is identical: no alarm, no crash, no failing test. Just a slow erosion of understanding until nobody can explain why the system is structured the way it is, and everyone is slightly too embarrassed to admit it in a PR review.
I recognized exactly what had happened that Tuesday night. By the time I saw the pattern clearly across multiple sessions, I’d already started building something to address it.
UVAL: Understand, Verify, Apply, Learn
I formalized UVAL in the Claude Code Ultimate Guide, in the learning-with-ai section specifically. The name is an acronym, but the logic isn’t complicated: understanding should come before using, not after shipping.
Understand First
For months, my first move when hitting a problem was to open a new chat and describe it. The results were correct but opaque. I’d paste the solution, it would work, I’d move on. What I wasn’t doing was arriving at the conversation with context that would make the AI’s response useful to me specifically, as someone with a specific codebase, specific constraints, and specific things I already understood.
Now I do four things before asking. First, I write the problem in one sentence. If I can’t, I don’t understand the problem yet. The difference between “the auth doesn’t work” and “the login form doesn’t display inline validation errors when the email field is empty on the first submission” matters. The second version produces a useful response. Then I force myself to name three possible approaches, even rough ones, even wrong ones. Having to name them makes me realize how much of the problem space I actually already understand. After that I identify what specifically I don’t know, not “I don’t know how to do this” but “I know Zod handles schema validation, I don’t know how to surface field-level errors inline in React without triggering a full re-render.” Then I ask.
The 15 minutes isn’t slowness. It’s the difference between delegating a problem and collaborating on one. And the hardest part, honestly, is the first sentence. It sounds trivial. It’s not. When I can’t write it, which happens more often than I’d like to admit, it tells me something important: I’m trying to solve something I haven’t fully defined yet.
Verify (Explain It Back)
When I first tried talking the code out loud — the Rubber Duck approach from Hunt and Thomas’s Pragmatic Programmer — I assumed I’d breeze through it. I’d read the AI-generated code, it made sense, I understood what it was doing. Then I tried to explain not what each line did, but why it was written that way. Why this middleware level and not the route level. Why reduce instead of forEach. Why the retry logic was here and not in the caller.
I got stuck on the third line.
That’s where the actual work happens. The places where I can describe what the code does but not why it’s structured that way are the gaps, and finding them is the whole point. Once I’ve identified them, I ask specifically about those lines. “Why is the token refresh triggered here rather than in the middleware?” produces a concrete answer. I remember it because it’s anchored to something I almost understood but didn’t. It’s the difference between passive reading and active verification. The code makes sense when I read it. That’s not the same as understanding it.
If I can’t explain the code to a colleague, I haven’t understood it. That’s the test. Deceptively simple, consistently revealing.
Apply (Transform, Don’t Copy)
What I do here is blunt: never paste directly. Always change at least one thing.
The modification can be minimal. Rename variables to match your project’s conventions. This forces you to read every variable name deliberately instead of letting your eyes slide past them. Extract a helper if it clarifies intent. Add the error handling the AI didn’t include because it doesn’t know your monitoring setup. Remove the features you don’t need (AI solutions tend to be generic and complete; your context is usually more specific). Sometimes I just restructure the iteration approach because the one in the response isn’t how I think about data in this particular codebase.
The cognitive point isn’t code quality. Copy-paste is zero cognitive load, and zero load means zero retention. The modification takes 90 seconds and forces you to process the solution instead of absorbing it passively. Six weeks later, you can still explain it. This is exactly how comprehension debt builds: not through laziness but through accumulated small acts of bypassing the one moment of engagement that makes understanding stick.
Learn (Capture the Insight)
I have started four learning journals in my career. None survived past week two. Life moves fast, the insight that seemed worth documenting feels obvious by Saturday, and by Monday the context has evaporated along with the motivation to write it down. I don’t think the problem is discipline. I think the problem is friction.
What I’ve done instead: a hook in Claude Code on the Stop event. After each session, Claude asks “What’s one thing you learned today?” The answer goes automatically to a log file. Ten seconds, no friction, it happens before I close the terminal. The insights are rough and sometimes useless, but occasionally there’s one I’d have completely forgotten by the next day, and it’s there in the log.
The other mechanism is CLAUDE.md used as a decision log. Not “we use Zod for validation” but “we use Zod because manual validation missed a nested object edge case in production, and we needed the schema as the single source of truth for field rules.” The code tells you what the system does. The decision log tells you why it was built this way. Without it, the why disappears the moment you close the editor, and three months later nobody can reconstruct the reasoning, including you.
What UVAL covers (and what it doesn’t)
UVAL is the individual layer. It addresses your comprehension — your ability to explain your own code three weeks later, your capacity to onboard someone onto logic you built months ago. The four steps are checkpoints that force engagement precisely at the moments when it’s easiest to skip.
For Storey’s cognitive debt (the team version), UVAL is a starting point, not a solution. A team where everyone uses it individually still needs shared decision logs, architecture records, PRs with “why” sections. That’s the team layer. Neither replaces the other.
The test I apply before shipping: if I can’t explain why the code is structured the way it is, I don’t ship it. Not “if I can’t explain what it does.” Anyone can read code and say what it does. The why is where the real work lives.
The honest limits
UVAL slows you down. That’s the point.
If you’re building a prototype you plan to throw away, or exploring a solution space with no intention of shipping, the protocol doesn’t apply. Exploratory work, throwaway experiments, the first hour of a feature when you’re not sure what the feature even is: vibe coding belongs there. Skip the steps.
The protocol is for code that goes to production. For code that others will maintain, or that you’ll debug in six months with no recollection of the decisions that shaped it. For that code, the 15 minutes on U saves hours of confused debugging later. The 5-minute V step saves the “why is this here” conversation that derails a sprint three months out.
It also doesn’t fully solve the collective version of the problem. A team of five each using UVAL individually still needs explicit shared processes: decision logs, architecture records, PR reviews focused on the why rather than just “does it work.” UVAL is one layer. Not the whole answer.
And it requires discipline. There’s no tooling that forces you through the steps. It’s a habit, not a guardrail. It’ll break when you’re tired, under pressure, or just in a hurry. The goal isn’t perfection, it’s a raised baseline: fewer Tuesday nights where the code ships but the understanding doesn’t.
Where to go from here
If you want to try this without committing to the full protocol, start with V. Next time AI generates code for you, before you paste it, explain it back to yourself line by line. Not what each line does, but why it’s there. Notice where you get stuck. Ask specifically about those places. That single change adds five minutes and produces something concrete every time: either you understood it (good) or you found the gap (better).
The full UVAL implementation, including the Claude Code Stop hook configuration, the CLAUDE.md decision log template, and the complete protocol, is in the learning-with-ai section of the guide. The raw source is also on GitHub if you want to go deeper.
The Understand step is the one I watch people skip first, every time I walk someone through this. The Verify step is where I get impatient myself. Curious which one breaks down for you first.
References: Jeremy Twei, original coinage of “comprehension debt”. Addy Osmani, “The 80% Problem in Agentic Coding” (January 2026). Margaret-Anne Storey, “Cognitive Debt” and “Cognitive Debt Revisited” (February 2026).