Skip to content
A dithered grayscale image of a circuit board.
Development

Breaking out of the LLM cycle

How I stopped using LLM tools for everything and started writing code again.

#ai#llm#development

You may know the feeling: you sit in front of your computer, staring at your terminal or agentic IDE and wonder, “What am I doing now?” LLMs allowed us as developers to do a lot in a short time. I enjoy working with AI and I do not want to stop using it. But is this always a good thing? I personally doubt it.

For me, the problem is not AI writing code. The problem starts when I give away my whole thinking process. When I stop thinking about the solution, the API, where code belongs and how everything connects, I am not really building the thing anymore. I am mostly prompting towards a final goal and hoping the AI gets there.

And it is not that I do not want to do this work. Thinking through a problem, designing a solution and understanding the result are the most interesting parts of the job. But current tools and hyped workflows like vibecoding, and now loop engineering, encourage us to offload more of that work to LLMs and start even more work with them.

This is the LLM cycle I want to get out of: I give the AI a task, it creates a plan, writes a lot of code and then I have to review a large diff across files I did not write. It is not that I cannot understand the code. If I take the time, I do. But when an AI changes 30 to 50 files, moves imports around and changes how things connect, it is hard to reconstruct the bigger picture and understand what it wanted to do. When something is wrong, I ask the AI to explain or fix it. The more I do that, the more I need the AI to work on the same project. That is fast, but it does not feel like good development to me.

From autocomplete to agents

I started working with LLMs on code for the first time when Microsoft introduced GitHub Copilot in 2021. To me, it felt like a game-changer. Suddenly my code editor was able to kind of predict what I wanted to write, even though it was not perfect back then. It made working with boilerplate code and repetitive tasks so much easier and faster.

I was hooked and a big fan of tab completions because they did not really interrupt my flow. I was still writing the code, making the decisions and thinking through the problem. Copilot just helped me move a bit faster. It felt natural to use while writing code.

When Anthropic released the first version of Claude Code, I was not sure what to make of it. I was sceptical because I did not really trust an LLM to go into my project, read its content, send that content to a third-party service and then return with a response that was not just correct but also secure and stable. Because of that I stayed away from Claude for a while, even though I was curious.

I think Cursor was already around at that point, but I did not really pay attention to it. I was happy writing code by hand and using Copilot’s tab completion feature to speed up my workflow. However, I started to notice that people around me were using LLMs to write code for them. I wondered if that was the right way to do it and if I should also give it a try.

So I started using Claude Code and it was an amazing feeling. Suddenly I had a tool that could help me finish things a lot faster than I could on my own.

Feeling the high of LLMs

After I got comfortable with Claude Code, I started exploring different AI tools and providers. I tried OpenCode and loved it because Claude’s CLI tool somehow has a really bad UX given the money that went into it. I set up my own repository with skills, agents and subagents and started automating more and more work I usually did manually.

For example, I had a skill that would break down a GitHub issue into small, actionable chunks and delegate them to a planning agent. It would create an implementation plan for each chunk and I could start working right away. This allowed me to close multiple issues per day.

At first, this felt great. I could plan faster, start faster and finish faster. AI is also really helpful when I do not have a concrete idea of how to implement something yet. Even today, I enjoy using LLMs to brainstorm, discuss an API design or ping-pong ideas back and forth.

But after a while something felt off. I automated so much away, worked on so many things at the same time and jumped between projects and topics. I started to lose focus and could not really own my code anymore. I was delegating tasks to a machine and then reviewing tons of code that I did not really like and would never have written myself.

The part I gave away

I tried to get control over it with instruction files, custom agents, skills and other workflow optimizations, but it did not really help. The problem was not that the AI ignored a specific instruction. The problem was that I was still giving it too much responsibility.

Very often an LLM generates too much. It starts touching too many things at once without making clear why it is doing this, what the reasoning behind it is or what comes next. Either it generates each thing step by step and you do not really see the bigger picture, or it tries to one-shot everything and you end up with 30 changed files and 3k lines of code that you somehow have to review and understand.

At that point, I was not saving time anymore. I was moving the work from writing code to reviewing a large diff and reconstructing the bigger picture across all the changed files. And because I did not make many of the decisions myself, I often spent more time asking the AI to explain individual changes than I saved by letting it make them.

That is the part I do not want to give away. Ownership means being able to explain the architecture, justify the tradeoffs, locate the important behavior and safely change it without asking an agent to reconstruct the system for me. I want to think about the problem, design an API, decide where code belongs and understand how the pieces connect. I do not need to write every repetitive line by hand, but I want to stay responsible for the actual implementation.

AI should be a sidekick, not the developer

I personally think the push towards apps that hide the code editor completely is the wrong direction for professional, maintained software. You give the app a goal, it writes the code, decides where it goes, connects everything and then shows you the final result. This may feel great for a quick prototype or internal utility, but for a real project I think this is a dangerous workflow.

This is what people often call vibecoding. You prompt towards the final result without really thinking about the code behind it. The code editor becomes something you do not need to look at anymore. But the code is still there. Someone has to understand it, debug it, review it and maintain it when the next feature or bug comes along.

If you rely on AI to reconstruct how your own project works, the project becomes hard to maintain without it. You are not just using a tool anymore. You are depending on it to explain your own codebase back to you.

I think this is unhealthy for actual knowledge and skills, no matter if you are junior or senior. As a junior, you need to make mistakes, debug them and learn why a solution works. As a senior, you still need to understand the details and tradeoffs of the system you are responsible for. Letting an LLM decide every part of the implementation does not help with either of those things.

The right way to use AI for me is as a sidekick. It can sit in the sidebar while I work. I can ask it questions about the project, ask for an implementation plan, discuss an API design or work through a larger feature with it. It can help me brainstorm when I am stuck and point me towards things I might have missed. But I should still be the person deciding what gets built and how it fits into the project.

Facing the abyss with open source contributions

Since I maintain Tiptap, I can tell when something in our community shifts very quickly. I have noticed for the last few months that more and more people seem to just be “bots”. Of course I cannot prove they are, but a lot of contributions I have received lately look like they were written completely by an LLM.

The problem is not that someone used AI. The problem is that the contributors often do not really look into our contribution guidelines, do not really look into the issues themselves or discuss things first. They pass the issue to an LLM, let it guess what the problem and solution will be and start right away with their PR.

The issue is not necessarily the contributor’s intention. The contributor receives the productivity benefit, while maintainers inherit the review, verification and long-term maintenance cost. The code you contribute is not just a piece of code. It is something someone else needs to understand and maintain.

On top of LLM-generated code, the PR descriptions and a lot of human communication are LLM-generated as well. Then even inside the PR you do not really understand what the contributor wanted to do, what they actually did themselves or if they even understood the root problem of the original issue.

The result can still be disrespectful of my time or that of my co-maintainers, even when that was not the contributor’s intention. Why should we do a human review of code and communication that are entirely LLM-generated? On the other hand, it always feels wrong to close a PR just because AI was used. I do not want to be the “AI Police” or throw away potentially good contributions. I want contributors to understand the problem and solution well enough to explain and maintain their work.

I do not have a good answer for this yet. But I think we need to find a way to make sure that contributors understand the code they are contributing and respect the maintainers’ time.

Losing my profession

“Tryin’ to keep up with you
And I don’t know if I can do it” - R.E.M.

The lyrics of R.E.M.’s song fit perfectly with my recent feelings about LLMs. On the one hand, I really enjoy moving fast and being able to implement or fix things quickly. I also like getting help with things that I do not have a lot of experience with yet. On the other hand, the more I let AI do the actual thinking and work, the less connected I feel to the code I am responsible for.

When I say I am losing my profession, I do not mean that I am losing my job. I mean I am losing the practice of being a developer. Thinking about a problem, designing a solution, writing code, making mistakes, debugging them and understanding why the final solution works. That is the actual work I do not want to lose.

The worst thing is that I sometimes feel like I cannot work without AI anymore. I start a new project or a new task and the first thing I usually do is go straight to an LLM and go through the Design => Plan => Implement => Test => Review cycle. Then something gets too big, I lose the overview of what exactly is going on and the AI has generated a trillion lines of code again. At that point, I start to doubt myself and my own abilities.

I want to be a developer who understands what he is doing. I want to be able to tell what each file and utility is doing and explain the bigger picture of something without having to rely on an LLM to explain it to me.

Some of you may know the feeling of a flow state: you enter deep focus while coding and you are in a zone that is hard to get out of. I cannot remember the last time I had this feeling. In my own workflow, AI made it too easy to jump between agent terminals, IDEs, projects and tasks. While one LLM is generating something, I hop into my browser to reply to comments or send another LLM to triage the next tickets I could pick up. It is a constant ping-pong between tasks, projects and context, and it is harmful to my focus and mental well-being.

A small experiment

Because of that, last weekend I started an experiment. I told myself: “For this weekend I will not use any generative AI tools for coding. I will write each line of code myself.”

For this experiment I set myself the following rules:

  1. No generative AI tools for writing code, except for boilerplate or repetitive tasks like moving files, rewiring imports or other things that do not require any thinking.
  2. When I use AI, I use it for brainstorming and planning only. I have to start drafting a plan myself before I ask it for help.
  3. AI is my pair-programming partner only. I set it to read-only, tell it to never give me finished code and use it to find solutions or discuss things that I want to think through.

And what can I say? This weekend felt absolutely refreshing. I actually felt like I accomplished something by writing line by line myself, designing an API and then discussing it with my pairing partner. I could probably tell you what each function in the code is doing.

Conclusion

I am going to rethink my use of AI. I do not want to continue working with it the way I did before. I will do more things manually again, write more things by hand and take back some tasks so I do not lose skills I have built over the last 10 years.

The thing that kicked all of this off was Nuxt’s “Nuxtathon” I contributed to. It felt refreshing to be around developers who care about what they build and who enjoy talking to each other as humans. It reminded me what I like about this work in the first place.

I care a lot about this hobby and about my work. That is why I am writing this post and starting this experiment. I do not want a workflow that slowly turns me into someone who only writes prompts towards a final result. I want AI to help me think, not think instead of me, while I remain responsible for the code, the decisions and the project itself.

AI should reduce mechanical work, not remove me from the act of developing. A project should still be understandable and maintainable when the AI is not there.