I Argue With My AI Pair Programmer, and It Makes My Code Better
Sometimes AI teaches me something and sometimes AI is a better rubber duck. Both are useful
We're often sold the idea that AI coding assistants are just about speed. You give a command, it generates a function, you move on. But I find value in debates and Socratic dialogues where I question and Agent’s choices.
Sometimes AI teaches me something and sometimes AI is just a better rubber duck. Both are useful
This lets me:
Learn new tools, libraries, and approaches. I get smarter!
Think about design principles and reasons behind why I do things
Improve my prompts and context files to get better AI assistance in the future
Here are a few examples while building DevRelifier, an AI-assisted blogging and content creation product I am working on.
Debate 1: Learn to Understand Complexity
The most valuable thing to use AI for is to learn. People often think about learning time as something distinct from building, but you should try to always be in learning mode.
The AI's Position: Make a simple fix by adding greenlet to the dependencies.
My Pushback: I didn’t know what that was so I knew this was a chance to learn. I just knew it felt heavy and I wanted to understand why. Steps I took:
I looked up the greenlet docs to learn roughly what it was for
I then asked about all the possible related dependencies to start with a high level understanding of asked: “Explain to me why we need sqlmodel, alembic, asyncpg, psycopg2-binary, and now greenlet? What are these tools ACTUALLY needed for?".
I kept drilling in, alternating between asking more questions and reading more documentation
I decided I did need it and now had a good understanding of why and how it all fit together.
I looked at a recent app created by the best web developers I know (Danny and Audrey Roy Greenfeld) and saw they were using it too
The Outcome (Knowledge Transfer): I got a lot more clarity of the role of each library in this async Python stack. This was the first time used this stack for db and migrations, and I walked away with a solid understanding and feeling that this was more robust than what I had previously.
Debate 2: Abstractions
I tasked the agent with creating a plan for a code cleanup pass. It came back with some great suggestions. But it also provided suggestions I disagreed with. This exchange and debate led me to improve my CLAUDE.md file to give a rubric to measure refactoring and abstraction tradeoffs with.
Defending a Good Abstraction
AI's Position: It identified helper functions like `style_button()` as "unnecessary abstractions" because they were simple wrappers.
My Pushback: I thought about it and disagreed. style_button() was used five times in the editor toolbar, and it enforced consistency and DRY.
The Outcome: I kept the abstraction. This is an example of how human "taste" for design and maintainability is still valuable. It forced me to think about and defend a design choice that I had not thought of. I added a minimal example about this in my context style guide.
Removing a Bad Abstraction
The opposite also happened. AI created its own abstraction that I immediately flagged as being overly complex for no good reason
AI’s Position: It wanted to create a new component called htmx_form() which wrapped the standard air.Form tag to add HTMX attributes.
My Pushback: I considered and felt that while it seemed possible I would need this in the future, I didn’t need it now. It was similar to the `style_button()` in that it was a simple wrapper, but it was only used once so it didn’t reduce any complexity. It just added a layer of indirection that made the code harder to read by hiding the underlying HTMX attributes.
The Outcome: I opted to have the agent remove the function. I modified my CLAUDE.md to include a simple rubric for evaluating abstractions. This rubric involved weighing code duplication, readability, cost of extra indirection, how thick/thin the abstraction is, and how scannable it would make the code base. Now when I ask for a refactoring plan I get something that’s much faster for me to review and make decisions on.
My Coding Cycle
This is the cycle I go through when I code. The more you learn by questioning things you don’t understand, the more you learn. This lets you create better prompts, debug faster, give better direction to the agents, all of which means you’re faster both with or without AI. The next time I build an async app, I'll be able to debug faster and make better architectural decisions from the start.
That’s the goal, end every coding session a bit more proficient than you were before you started.
The Real Goal of AI-Assisted Coding
The goal is to build better software and become a better developer in the process. By treating your AI assistant as something to be questioned and debated, you force a higher level of intention into your work.
Sometimes it teaches you things. And sometimes it’s just a better rubber duck. Both are useful.