
SURAJ NANDANI use AI tools a lot. I'm not going to pretend otherwise. My Whiteboard.ai project, for example, was...
I use AI tools a lot. I'm not going to pretend otherwise. My Whiteboard.ai project, for example, was built with Claude Code under written engineering rules and a plan-then-approve loop, and that's stated plainly in its case study.
But I've had to set rules for myself, because I noticed a specific failure mode: I can ship something I don't understand. That's the real risk of AI tools, and it isn't about quality of output. It's about what happens to me if the tool does all the thinking.
Here are the rules, where AI helps, where it quietly hurts, and one lesson from a different angle: building products on top of models.
Without AI, if I don't understand something, I can't write it. The gap between "I need this" and "I have this" forces learning.
With AI, that gap can vanish. The tool writes the code; it compiles; it passes a quick manual check; I move on. The code is in my repo, but the understanding isn't in my head. Weeks later something breaks and I'm debugging code that's effectively someone else's, in a codebase I'm supposed to own.
So I set rules.
If I can't explain a block of generated code in my own words, I don't commit it. "It works" isn't enough. I need to be able to say why it works and what would break it.
The assistant fills in; it doesn't decide where logic lives. The decision to put CardBridge's rules in Postgres (Day 2) was mine, with reasons I could defend. That's the kind of decision I don't hand over, because the consequences last the whole life of the project.
Generated changes can touch files I didn't expect. Reading the diff before accepting is the cheapest quality control there is. If a change surprises me, that's a signal to stop and ask why.
"It compiles" isn't "it works". Type checks and unit tests are necessary, not sufficient. For anything user-facing, I run the actual app and use the feature.
For Whiteboard.ai, I wrote engineering rules in a file and had the assistant plan first, with me approving the plan before code. A written rule survives a long session in a way that a mental intention doesn't.
AI coding tools are one side. The other is building products that call models. My Knot.ai project, a spoken interview-practice app, taught me something I now think of as a rule of thumb:
Instruction is not control.
In early testing, I handed the whole interview to an LLM and asked it nicely to follow rules: adjust difficulty by one level, cover three topic areas. Against a simulated candidate, the interviewer model tunnel-visioned on one topic and escalated difficulty freely. It ignored "adjust by one level" and "cover three areas" the moment they competed with its own instincts.
The fix was to split the job. The model does what only a model can: understand an answer, judge it, phrase the next question. Everything countable became a state machine on the server: difficulty is a streak-based ladder clamped to 1–5, topic coverage rotates on a counter. The model supplies judgement and phrasing; the server decides what happens.
That's the same principle as my AI-tool rules, from the opposite direction. Don't rely on a prompt to enforce what code can enforce. And don't rely on an assistant to enforce good judgement that only you can supply.
Before I commit anything an AI wrote, I ask:
Could I rebuild this from scratch if the tool vanished tomorrow?
If yes, I'm using it as a power tool. If no, I'm renting understanding, and the rent comes due at the worst time.
I don't have this solved. A few open questions I wrestle with:
If you have answers, I genuinely want them.
Run through this before accepting a diff.
How you ask changes what you learn.
Notice the pattern: these ask the tool to expose reasoning, which keeps me in the loop.
Weak request: "Add rate limiting to my API."
Better request: "I have a Vercel function at api/chat.ts. I want to limit each IP to 20 requests per minute. List two approaches that fit a serverless host, explain the trade-offs, and wait for me to choose before writing code."
The second one keeps the architecture decision with me and gives me something to learn from.
For bigger tasks, I use a loop with written rules:
Written engineering rules (naming, testing, "don't change files outside the plan") make the loop reliable, because rules written once apply to the whole session.
Pick a function you wrote with AI help last month. Without looking at the code, write down what it does, what inputs it can't handle and how you'd test it. Then compare with the code. The gaps you find are exactly the "understanding debt" this post is about.
If two or more are true, I slow down for a few days and write something from scratch.
How do you draw the line between using AI as a tool and letting it replace understanding?