How I Use AI for Coding Without Giving Up Control
The AI coding space is moving so fast that it is hard to keep up with what actually matters.
We started with Copilot-style autocomplete in the IDE. Then came ChatGPT and the copy-paste workflow. Then editor chat. Then Cursor. Then Claude-based IDE workflows. Then terminal-based agents. Then tools that can operate directly inside your repo.
And this still does not feel like the final form.
There will be more tools, more models, more orchestration, more agents, and probably completely different interfaces again in a few months. The whole space is evolving so quickly that it is hard to form a stable workflow around it.
That is where I think a lot of developer frustration comes from.
Not because the tools are useless.
Because a lot of people still do not feel in control.
The real problem: losing control of the workflow
When people talk about control with AI tools, they often mean permissions:
- Can it access files?
- Can it run shell commands?
- Can it use credentials?
- Can it touch production?
That stuff matters, but it is not the main type of control I care about.
What matters more to me is:
- Control over context
- Control over direction
- Control over the workflow
- Control over what is actually being built
That is where I think a lot of AI coding workflows break down.
The output may look impressive at first. But after a few iterations, the code starts drifting away from your mental model. The structure stops feeling intentional. The patterns start to feel random. Eventually you end up with code that technically exists, but no longer feels like something you deeply understand.
That is the point where AI-assisted coding quietly turns into, "I hope this still works."
What worked for me: do not let the agent drive
The biggest improvement in my workflow came when I stopped letting the agent own the thinking.
I do not want the agent deciding the whole solution end to end while I sit back and review the output afterward. I do not want it inventing architecture and patterns across the codebase without me staying close to those decisions.
That is how you end up vibe coding.
Fast output, weak ownership, and eventually a codebase you no longer really understand.
What has worked much better for me is treating the agent as a partner, not a replacement.
I stay in control of the design. I guide the work. I explain what I want to build, why I want it that way, how I want the code to evolve, and what constraints matter. Then I let the agent accelerate execution.
Sometimes I know exactly what pattern I want. Sometimes I do not, and I ask for suggestions or tradeoffs. But I stay involved in the decision-making either way.
The agent helps me build.
It does not own the direction.
Cheap code makes judgment more important
One of the weird things about AI coding is that code has become cheap.
That is powerful, but it also means you can generate a lot of the wrong code very quickly.
Yes, you can refactor later. But that only works if you still understand the system well enough to know what needs changing. If you let the tool run too far ahead of your own understanding, you eventually lose the context needed to debug, evolve, and trust the codebase.
For tiny throwaway experiments, that may be fine.
For longer-running projects, it becomes a problem.
The more disconnected you become from the code, the more dependent you become on the tool that generated it.
That is exactly what I wanted to avoid.
Agentic coding changes where the thinking happens
Traditional coding gave us thinking time.
When you wrote everything manually, the implementation itself created space to think. You discovered patterns while building. The pace of typing roughly matched the pace of reasoning.
Agentic coding breaks that.
Now implementation can happen almost instantly. That means the thinking needs to happen earlier. You need to have a stronger idea of the structure before the agent starts generating large chunks of code.
You need to know things like:
- Where boundaries should be
- What patterns you want
- What structure you are aiming for
- What you definitely do not want
The faster the code appears, the more deliberate the human needs to be.
Then I hit another bottleneck: typing prompts is a bad interface
One of the first practical problems I ran into was this:
Typing prompts is a terrible high-context interface.
Typing is slow. It is lossy. It encourages shorter instructions. And shorter instructions usually mean less context.
Less context means more inference.
More inference means more chances for the agent to go in the wrong direction.
Developers already love tools that reduce typing. That is why autocomplete became so popular in the first place. We type because we have to, not because it is the best interface for expressing complex intent.
The same problem shows up with prompting. People naturally under-prompt because typing long context feels like effort. So they write short instructions and hope the model fills in the blanks correctly.
Sometimes it does.
Sometimes it absolutely does not.
Messages are great in distributed systems, but not as the main interface here
One way I started thinking about this was through distributed systems.
Messages are great for the right use case. Asynchronous message passing is incredibly useful when you want decoupling. The sender and receiver do not have to operate at the same time. The receiver can decide when and how to process the work. That is exactly what you want in many systems.
But that does not mean a message-based interface is the best way to work with an AI coding agent.
A typed prompt is basically a message: a compact packet of intent sent to a receiver that has to interpret it.
That works when the task is narrow and the prompt is precise.
But when you are trying to shape implementation, architecture, tradeoffs, and evolving intent, that model becomes a bottleneck. It is too low bandwidth.
I do not want my main interaction with an AI coding assistant to feel like sending isolated Slack messages and hoping the other side interprets them correctly.
Asynchronous messages are excellent for distributed systems.
For high-context programming workflows, I want something tighter.
That is what pushed me toward voice.
Talking to the agent was the unlock
Early on, I realized that talking to the agent worked much better for me than typing to it.
I started using the built-in microphone in the ChatGPT desktop app to explain what I wanted, describe structure, walk through tradeoffs, and dump context much faster than I could type it.
That made a huge difference.
Instead of compressing everything into a short prompt, I could explain:
- What I wanted
- Why I wanted it
- What constraints mattered
- What shape the solution should have
- What tradeoffs I cared about
- What I was unsure about
That gave the agent much more useful context, and the output got noticeably better.
Voice ended up being a much higher-bandwidth interface.
It also helped me express my own thoughts better
This mattered to me for another reason too.
Sometimes I know exactly what I want to say, but my English comes out less polished than the thought in my head. Using voice with AI helped me structure and format my own thinking into something clearer and more grammatically correct than my raw first pass.
That does not mean the thinking was not mine.
It was mine.
The ideas were mine.
The judgment was mine.
The direction was mine.
AI just helped me express those thoughts more clearly.
That became especially useful for technical writing too. I could talk through design ideas, dump all my thoughts, and then shape them into something more readable and structured.
Without voice, I do not think I would have used AI nearly as effectively for that.
That workflow is what led me to build ARAI
Eventually I got tired of stitching this workflow together from multiple awkward pieces.
I wanted something more natural. More power-user friendly. Something that fit how I actually work.
I am pretty terminal-heavy. I like keeping my hands where I work. I do not want to keep bouncing between random windows, temporary dictation flows, pasted prompts, and scattered tooling just to get my thoughts into the system.
While exploring the space, I came across tools like Wispr Flow and SuperWhisper. They clearly solve a real problem, and I can see why people use them. They improved parts of the workflow for me too.
But they still were not quite what I wanted.
So I built ARAI — a prompt manager and dictation tool designed around the workflow I wanted for myself.
Part of that was practical.
Part of it was also a challenge to myself.
I wanted to test my agentic programming workflow on something real, and I wanted to do it in Rust, which I had not previously used to build a production-grade or complex application.
That made it a good test:
- Real product
- New language
- Real workflow pain
- Actual reason to build it
And honestly, it worked out better than I expected.
I ended up using ARAI while it was still in alpha to help build more of itself, which felt like a strong validation of the idea.
The biggest lesson I learned
The biggest lesson from all of this is simple:
Do not outsource your judgment.
Use AI to accelerate execution.
Use it to explore options.
Use it to research patterns.
Use it to draft, refactor, and sharpen.
But stay in control of the direction.
Stay close to the code.
Stay responsible for the design.
The best results I have had with AI came from partnering with it, not surrendering to it.
This might not stay true forever
At the same time, I do not assume this will always be the most important thing.
This whole space is changing too quickly to pretend any of us know what the stable end state looks like.
Right now, I still care a lot about how code is written, how it is structured, and how the lower-level parts fit together. I think that still matters a lot today, especially if you want systems that are understandable, debuggable, and reliable.
But I can also imagine that changing.
As these tools improve, maybe the lower-level implementation details become less central. Maybe what matters more is what the system does, how it behaves, how it composes with other systems, and whether the outcome is correct, reliable, and understandable.
Maybe the craft shifts upward.
Maybe some low-level concerns fade into the background.
Maybe some remain critical.
That could be better in some ways and worse in others.
I do not think we know yet.
What I do know is that, for where we are today, staying close to the system and staying in control of the context has worked far better for me than handing the wheel over completely.
Closing
That is why I wanted to write this.
Partly to share the workflow that has worked for me. Partly to share the journey that got me here. And partly to share ARAI as an open-source project in case it helps someone else too.
Whatever the interface becomes next, I still think the human should stay in the loop, not as a passive reviewer of mysterious output, but as an active partner shaping the result.
And that is why I built ARAI — named after my daughter, Arabella.
Project: https://github.com/mattkoltun/arai

Top comments (0)