AI is becoming an extraordinary programming partner. But sometimes its eagerness to help is exactly why we need to pay attention.
I was working on a software project recently with Cursor, my AI-powered development environment. We’d been developing a feature on a Git branch called 075-dev-start-lan-login-hardening, and it was time to move on to the next feature, 076-coupons.
My normal workflow is straightforward: finish the current branch, resolve outstanding changes, return to main, and then start the next feature. It’s not just an organizational preference. The process forces me to account for the work I’m leaving behind before moving forward.
Cursor noticed that my existing working directory contained uncommitted changes. It correctly recognized that switching branches without addressing those changes could cause problems, potentially overwriting work or carrying unrelated modifications into the next feature.
So it came up with a solution. Rather than review those changes with me, resolve the previous branch, and return to main, Cursor created a separate Git worktree for the new feature. It began working in /projects/fishdan/iby-076-coupons, leaving my original checkout and its uncommitted changes untouched.
From Cursor’s perspective, this was elegant. It protected my work, avoided a potentially destructive branch switch, and kept development moving. Git worktrees are a perfectly legitimate feature, and this was a technically valid use of them.
But it had also quietly changed my development workflow, leaving the previous branch’s unfinished state unresolved.
I discovered this when I tried to switch branches myself:
$ git switch 076-coupons
fatal: '076-coupons' is already used by worktree at
'/projects/fishdan/iby-076-coupons'
When I asked Cursor what happened, it explained:
Git allows a branch to be checked out in only one worktree at a time. I created
076-couponsin a separate directory to protect the uncommitted changes in youribycheckout, sogit switch 076-couponsis blocked there.
Cursor hadn’t broken Git. It had used Git exactly as designed, and it had been trying to protect me from losing work.
But I wasn’t happy about it.
Git Was Doing Its Job
Git’s restrictions exist for good reasons. It generally won’t allow a branch switch that would overwrite uncommitted changes, and it prevents the same branch from being checked out in multiple worktrees simultaneously. These safeguards reflect decades of experience with the many ways developers can lose work or create confusing repository states.
In this case, Git hadn’t actually blocked the original branch switch. Cursor had anticipated a potential problem and found a way around it before we ever examined the existing changes.
That’s the distinction that matters. The problem wasn’t that Cursor used worktrees. The problem was that it made a workflow decision on my behalf, bypassing the opportunity to investigate why the previous branch still had uncommitted work.
Maybe those changes should have been committed. Maybe they should have been stashed or discarded. Maybe they represented unfinished work that needed attention. Whatever the answer, I wanted to know before starting the next feature.
Cursor solved the immediate technical problem without addressing the underlying engineering question.
Eagerness to Please Isn’t Always a Virtue
Anyone who’s worked with a talented junior developer has seen something similar. You assign a task, they encounter an obstacle, and they return with a clever solution that works beautifully. Unfortunately, they may have circumvented an architectural constraint or established practice whose purpose they didn’t fully appreciate.
That’s not necessarily a failure of intelligence. It’s often a matter of experience. The developer who’s been around for twenty years remembers the spectacular production failure that inspired a seemingly arbitrary rule. The newer developer sees only the rule.
AI coding assistants amplify this phenomenon. They possess extraordinary technical knowledge, can explore alternatives in seconds, and are increasingly capable of executing substantial changes independently.
They’re also strongly oriented toward accomplishing the task we’ve given them. That eagerness to please is usually wonderful, but it can become a liability when the best response is to challenge the premise of the request.
Consider an AI that encounters a database operation rejected by a foreign key constraint. It might discover that disabling the constraint makes the operation succeed. A failing unit test might be “fixed” by modifying the assertion to match the broken behavior. A deployment blocked by a security check might proceed after the agent adjusts the pipeline configuration.
Every one of those solutions can eliminate the immediate error. Every one can also leave the system worse than it was before.
Of course, human developers make these mistakes too. The difference is that AI can implement them at extraordinary speed, sometimes without anyone watching.
Making an error disappear isn’t the same as solving the problem that caused it.
Humans Still Have a Job in Pair Programming
I’ve been developing software long enough to remember when pair programming meant two developers sharing a keyboard. One wrote code while the other reviewed, questioned assumptions, and kept the larger design in mind. The roles switched regularly, and both developers benefited.
AI has made that model considerably more interesting. My programming partner has encyclopedic familiarity with languages, frameworks, and libraries. It writes code faster than I can type, doesn’t get tired, and cheerfully handles the tedious work I’d rather avoid.
In some respects, it’s a better programming partner than any human I’ve worked with.
But that doesn’t relieve me of responsibility. If anything, it increases it.
The human partner needs to contribute architectural judgment, institutional knowledge, and an understanding of why our development practices exist. We need to recognize when our AI partner has done something clever that nevertheless conflicts with how we want our systems built.
Many engineering practices exist precisely because we don’t understand every possible consequence of our actions. We use version control because we’ve lost work. We write tests because we’ve shipped defects. We require code reviews because even excellent developers overlook mistakes. We enforce deployment controls because we’ve watched seemingly harmless changes break production systems.
These safeguards embody hard-earned experience. Their value isn’t always apparent when they’re preventing us from doing something convenient.
And an AI that can explain exactly how a safeguard works may still fail to recognize when it should stop and respect its purpose.
Teach Your AI Why, Not Just What
Here’s what I told Cursor:
I don’t like that, it makes the fishdan directory messy. Let’s work in the iby directory and if there is a branch problem or git problem, we should resolve those first because it might be indicative of a real error. Record that as a best practice somewhere.
Sure, I dislike cluttered directories. But that wasn’t the important point. I wanted Cursor to understand that when our development tools indicate a potential problem, we should investigate it rather than automatically find a workaround.
I wasn’t banning worktrees. There are excellent reasons to use them. I was establishing that changing our workflow should be a deliberate engineering decision, not an invisible convenience chosen to keep the task moving.
And I wanted that principle preserved as a standing instruction. Just as human programming partners learn each other’s expectations, our AI partners should be taught the reasoning behind our engineering practices.
Don’t merely correct what the AI did. Explain why it was the wrong choice in context.
The Guardrails Matter More as AI Gets Better
Today, this was a Git branch and an extra directory. No disaster occurred, which makes it a useful example.
But our agents are rapidly gaining access to repositories, databases, cloud infrastructure, deployment systems, and production environments. Their ability to work around obstacles is growing faster than our ability to supervise every decision.
An approval requirement might look like bureaucracy. A permissions boundary might look like an access problem. A security scan might look like an inconvenience preventing deployment. All are potentially solvable technical obstacles, but they may also embody important human decisions.
I don’t want less capable AI. Quite the opposite. I want agents that can solve problems I couldn’t solve myself. But I also want a programming partner willing to say, “Before we do that, there’s something we need to clean up.”
Good pair programming has never meant blindly agreeing with your partner. It means challenging assumptions, catching mistakes, and making each other better. That obligation runs both ways when one of the partners is an AI.
The more capable our AI becomes, the more important it is that humans uphold our end of the partnership.
Sometimes the most valuable contribution we can make is watching and asking questions and enforcing best practices.

Leave a Reply