The Hard Part of AI Coding Isn’t Using AI. It’s Knowing When Not to Trust It. An engineer argues that the main challenge in AI-assisted coding is not access to AI tools but knowing when not to trust their output. The post highlights that AI can generate plausible but incorrect code, and emphasizes the need for human verification, especially for high-risk changes like authentication and database migrations. The author advises treating AI-generated code with the same scrutiny as human code, and warns that tests written by the same model may share its misunderstandings. There are now enough AI coding tools to build an entire workflow out of assistants talking to assistants. The problem is no longer access to AI. The problem is deciding where it belongs in the development process without quietly handing over your judgment. AI is great at reducing friction. It can trace unfamiliar code, generate tedious boilerplate, explain an API, propose refactors, write tests, hunt through logs, and suggest five possible causes for a bug before you finish your coffee. Used well, it can feel like a super fast technical collaborator. But speed creates its own trap. A plausible answer can feel finished long before it has actually been verified. AI coding tools are remarkably good at producing answers that look right. The code is clean. The explanation sounds confident. The function names make sense. There may even be comments describing exactly why the solution works. And sometimes it does work. Other times, the model invents an API method, uses an outdated configuration format, misunderstands a library version, ignores an edge case, weakens a security check, or fixes a symptom while leaving the real bug untouched. The most dangerous failures are not always the ones that immediately crash the application. Those are usually easy to catch. The more dangerous failures are the ones that appear to work. An authentication change might allow valid users to sign in while accidentally bypassing an authorization check. A database migration might succeed against an empty development database but fail on real production data. A generated test might pass because it reproduces the same incorrect assumption as the implementation. The app starts. The tests pass. The agent announces success. That still does not mean the work is correct. If an agent says a library supports something, check the documentation. If it changes authentication code, inspect the security implications. If it writes a migration, read the SQL. Run the tests. Check the diff. Understand the dependencies it added. Ask why the proposed fix works instead of just checking whether the application starts. This does not mean treating every AI-generated line as suspicious. Generated code deserves the same scrutiny as code submitted by anyone else, and occasionally more, because the model cannot be held responsible for the result. A human teammate can explain the assumptions behind a decision, remember a conversation that changed the requirements, and recognize when a technically valid implementation would create a terrible user experience. A model can explain its output too, but that explanation is generated after the fact. It may be useful, but it is not proof that the model started from the correct assumptions. Verification should also match the risk of the change. A developer does not need to conduct a security audit because an agent changed the padding on a button. Changes involving authentication, authorization, payments, destructive operations, personal data, infrastructure, database schemas, or production configuration deserve substantially more attention. The higher the cost of being wrong, the less appropriate it is to accept “looks good” as evidence. Tests are among the best tools available for verifying AI-generated work, but even they require judgment. An agent can write tests that confirm the implementation behaves exactly as the agent expects. That is useful, but it does not prove that its expectation matches the product requirement. If the same model writes the implementation and the tests, both may share the same misunderstanding. Suppose a requirement says that only account owners can delete a project. An agent might interpret “account owner” as any authenticated user associated with the account. It can then write the authorization logic and a complete test suite around that interpretation. Every test passes, but the implementation is still wrong. Good verification asks more than whether the generated tests are green: Tests are evidence, not absolution. Not every failure is a bug. Sometimes an AI-generated solution is technically valid but wrong for the project. It may introduce a state management library into an application that did not need one. It may replace a straightforward function with an elaborate abstraction. It may use a dependency that is incompatible with the deployment environment. It may solve a small performance problem by making the code much harder to understand. Models are generally inclined to produce something rather than conclude that nothing needs to change. If you ask for a refactor, you will probably receive one, even when the original code was perfectly fine. Good engineering is not about choosing the most sophisticated solution available. It is about choosing a solution whose complexity is justified by the problem. AI can propose the options. It cannot own the tradeoff. The same principle applies when choosing among AI tools. Developers do not need to chase every coding assistant that launches. Every week seems to bring another agent with a cinematic demo, an impressive benchmark, and a promise to transform software development forever. Some of those tools are genuinely useful. Some are excellent in one context and mediocre in another. Some will disappear six months after developers have reorganized their workflows around them. One model may be great at exploring a large codebase but unnecessarily verbose when implementing a small change. Another may generate polished interfaces while struggling to diagnose backend behavior. A specialized review agent may notice issues the implementation agent missed, but it may also produce a pile of low-value warnings. The best workflow may involve several tools: one for exploration, another for implementation, and another for review. Or it may simply involve one tool used deliberately. The goal is not to assemble the largest AI stack possible. It is to remove enough friction that more attention can go toward decisions that require engineering judgment. Adding another agent is not automatically an improvement. Sometimes it only adds another layer of output for a human to verify. A subtle skill is developing alongside AI-assisted programming: knowing what to delegate. Some tasks are perfect candidates. Boilerplate? Delegate aggressively. Exploring an unfamiliar codebase? AI is great at identifying likely entry points and tracing relationships between files. Generating alternative implementations? Useful. Seeing multiple approaches can expose tradeoffs you might not have considered. Writing the first version of repetitive tests? Absolutely, as long as someone verifies that the tests represent the intended behavior. Turning an error message into a list of hypotheses? Great. That can accelerate debugging considerably. Other decisions should remain firmly under human ownership. Is a security assumption acceptable? Does the architecture make sense for the expected scale and lifetime of the project? Does the implementation solve the user’s actual problem? Is collecting this data necessary? Is the new dependency worth maintaining? What happens if this operation fails halfway through? Does the code satisfy the requirement, or merely the prompt given to the agent? AI can contribute information to these decisions, but the responsibility still belongs to the developer. A useful rule is to delegate execution more freely than judgment. Let the model perform mechanical work, gather context, generate possibilities, and handle repetition. Be more cautious about delegating decisions involving intent, risk, tradeoffs, or consequences. AI tools are getting better at gathering context automatically, but no coding agent has perfect access to everything that matters. It may see the repository but not the conversation where a feature was deliberately excluded. It may read the issue but not understand the support burden created by a particular implementation. It may know what the code does without knowing why the team chose to do it that way. Even large context windows do not solve this completely. More context is not the same as the right context. Developers still need to identify and communicate the constraints that matter. That includes the technical environment, product requirements, deployment limitations, compatibility expectations, security boundaries, and anything that must not change. When an AI tool produces a poor result, the model is not always incapable. Sometimes it was given an underspecified task and filled the gaps with assumptions. Those assumptions may be perfectly reasonable. They may also be completely wrong. AI agents frequently summarize their work with reassuring declarations: Treat those statements as reports to verify, not conclusions to inherit. Review the actual diff. Check which files changed. Look for unrelated modifications. Confirm that existing behavior was not removed. Inspect configuration changes. Check whether tests were weakened. Search for hardcoded values, silent error handling, broad permissions, unnecessary dependencies, and placeholder logic disguised as a complete implementation. The confidence of the summary has no relationship to the correctness of the code. A beautifully explained mistake is still a mistake. AI-assisted coding does not make developers irrelevant. It changes where their attention is most valuable. Less time may be spent typing predictable code from scratch. More time may be spent defining behavior, supplying context, evaluating alternatives, reviewing changes, designing verification, and deciding what should be built in the first place. That is not a lesser form of development. Typing code has never been the entire job. The real work has always included understanding systems, identifying constraints, managing tradeoffs, predicting failure modes, and taking responsibility for what reaches users. AI can accelerate the production of code. It cannot take responsibility for the consequences of deploying it. That responsibility remains stubbornly human. AI-assisted coding works best when the model handles mechanical effort while the human maintains ownership of intent, verification, and consequences. That does not require hovering nervously over every generated semicolon. It requires calibrating trust based on the task. Use AI freely where mistakes are cheap and obvious. Increase scrutiny where errors can remain hidden or cause real damage. Use tests, linters, type checking, documentation, code review, and isolated environments as parts of a verification system, not ceremonial steps after generation. Most importantly, stay capable of disagreeing with the tool. The interesting question is no longer whether developers should use AI. That debate has largely been overtaken by reality. AI is already embedded in editors, terminals, documentation tools, code review systems, and development platforms. The more important question is whether we can become good enough at using it that faster coding does not become faster mistakes. AI can help us move quickly. Engineering judgment tells us when to slow down.