BIRKEY IA

ABOUT  RSS  PROJECTS  ARCHIVE


Posts tagged "coding-agent":

03 Oct 2026

Everything I Build Around the LLM Becomes a Distraction

I've been using LLMs for software development for a bit more than a year now. One thing I can say for sure: the landscape changes very fast.

There are a lot of trendy things that, at the time, seemed like a good idea. Over time they turned out to be more signal than the real entropy underneath. I want to come back to that in a separate post, because it deserves more than a few sentences.

The one thing that did not change

If there's a single takeaway, it's this: you have to actually use it, see it, and understand it. Understand what the benefit is, what the trade-offs are, what you're losing. And then you have to adapt to that.

Methodologies change. Strategies change. Every few months something new comes along and the best practice from last quarter looks naive. But underneath all that churn there is one thing I keep coming back to, again and again: the core of the problem, and the core mental model of understanding it, has always been the same.

Yes, the LLM helps. But if you don't use it carefully, it actually generates more content, more features, more code, more of everything, that you have no understanding whatsoever. Even when it seemingly works. Even when it's sort of tested and whatnot. There's no orthogonal consistency given the complexity of the problem we're solving.

What I want to do here is walk through the journey itself. What I actually did, what worked, what didn't, and where I ended up.

Confined problems

Early on, I mostly gave it a very confined problem. Refactoring a code base. Simplifying a certain module. And they did a pretty good job at it. The reason is simple: the problem is already solved. It's just a matter of refactoring, which is a well suited shape of task for an LLM. That was the early stage of LLM assisted development for me.

The context rabbit hole

Then, as the models got more powerful and started to get into it, I realized how critical the context you give an LLM is. I spent a lot of time refining it, trying to get as much relevant information as possible in there.

The way I did it was by experimenting. Starting with a simple grab search tool, whether it was syntactic or semantic, to pull more relevant information from the code base, or from the web, or from whatever literature is relevant. Getting the right context in front of the model. That's where we started, and the repository was the first step.

It was a mixed result overall. The lesson I took from it is that context engineering is a kind of dark art. Even refining the context engineering, you can get into a deep rabbit hole. The LLM keeps changing, and then a seemingly pretty solid methodology just degrades. The model updates underneath you. So you start a new eval to refine it. And then you keep refining. The more you refine, the more you realize the quality doesn't really improve in proportion to the time you put in.

The amount of time you spend on context engineering versus the benefit you actually get back doesn't justify it. Eventually what you end up doing is starting over. There's no way of prompting, no way of giving more context, that fixes it.

Context management and compaction

The next issue that hits you immediately is context management, meaning compaction and things like that. That also turned out not to be an easy problem to solve. There are many different methodologies you can try.

The easiest would be to just ask an LLM to summarize it and write a handoff. But then you have the risk of: how do we compact it, how do you summarize it? The more you work on it, in terms of summarizing after the context is full versus doing summarization after each successful request and response as a background process running async, the more you end up creating. You get into a rabbit hole of managing different processes and coordinations. At the end of the day, it's more about state continuity. And because of the stochastic nature of LLMs, there's just no way to consistently, deterministically find a better way to do summarization and handoff.

The best way I've actually been using right now is: no automatic handoff, no automatic summarization. You summarize manually. You ask a certain model for the conversation history so far, and then you verify it before handing it off.

Which seems to me that maybe in the future this will just be done automatically by the model provider on the server side. But on the client side, it seems to me that it's a drag on your development productivity. Including so-called agentic coding, coding agents. Because it distracts you from actually solving the problem, versus actually creating a non-deterministic handoff flow that you keep just changing, which is never ending. That's also not a good way of managing the context problem.

Understanding the tool you are using

The third thing is using coding agents that are developed by someone else. Even though you end up customizing it, you end up putting a lot of tools and plugins and things like that in it. Eventually it gets to such a complex state that it's just hard to understand the tool you're using.

For example, when I say the tools you're using, it's hard to understand what the current context is, what the current prompt is that the LLM is seeing, what the actual in-memory instruction set is, the in-memory context the coding agent is actually working out of. And yes, there are tools to inspect this and that, but it becomes a job of its own. It distracts you from the problem you're trying to solve.

That dictates that you start either using a very bare bone, very minimal coding agent, which you can then customize to your workflow. Or, in my case, even after experimenting with the Pi coding agent, which had so many different choices and plugins and kept evolving so fast, I eventually decided to write my own. With a particular constraint that I put on it, and the particular workflow I can actually wrap my head around.

Whether you use someone's coding agent or do it your own, the learning is: you really have to understand the tools you're using, and be able to master them, to take the best advantage of the coding models.

Skills

Another thing I've changed, another thing I've learned, is using skills. I'm still very on board with them, because they have this nice property: they furnish the context on demand, only when it's needed. When it's not needed, it's not going to be used. That's one of the really nice things about it. It's dynamically loaded, so it doesn't cover the context the whole time.

The tradeoff is that it's not a deterministic instruction set. It's more of an advisory to the LLM. The LLM more or less follows it, but there's no guarantee.

The nice part, though, is that you can take some ad hoc prompt you've been repeating over and over and turn it into a really well structured skill. And you get better results. You can express a lot, the majority of workflows that you have, with a skills file. I still use them. I don't know what the future holds for this, but so far it gives me more ROI than not. You can describe them in a very example based approach, and then most of the time, almost all of the time, LLMs are pretty good at following those instructions exactly, the way you'd like them to approach solving a problem.

Spec driven development

In terms of the other aspects, it's spec driven development. It starts out well, especially when you have a well-defined spec, which is in and of itself a complicated problem.

But as time goes by you generate too much documentation. Doc, doc, doc, doc. Because of the nature of specs, over time it drifts. It introduces contradictory, non-orthogonal ideas. Because you probably forgot the spec you wrote about a few days ago, let alone a month ago, and then the LLM starts to introduce something else. Eventually the documentation explosion is going to be a big problem.

What I've learned over time is that the spec should be sparingly used. A very high level description of how a specific module, or program, or feature works. But in terms of the detail, I rely more on executable code. That's the direction I landed on.

Scripts instead of prose

One of the other things that works really well for me is this. When there's a certain kind of repetitive thing I always ask it to do, instead of writing skills and whatnot, I ask LLM to create a script. An executable script. And then combine that with a skill that says: hey, this is the script you run to accomplish certain things, and then use the output to go on within the problem we're trying to solve.

Start with an executable prototype

And one last thing. After all of those experiences, one thing I've been having good results with is to always start with an executable prototype of the actual flow. An actual problem.

You can even start from the UI. You don't even have to start with the database. You don't even have to have all of the server running. It's just one executable program. Or, if you're doing more of a web app, a consumer facing app, it's more like starting with one HTML file with everything just embedded in there. That executable, it is code.

That's one thing I believe is helping me way more than anything else right now. It's still yet to be seen in terms of the results, but so far the iteration loop works like this. Say you have a flow, some kind of onboarding flow. You can ask the LLM to generate an onboarding flow based on whatever your requirement is. You can even simulate it. You can even let it test it. And the requirement is just one HTML file, everything is embedded, no framework, nothing.

That gives you a prototype you can actually iterate over until you say that this is a fleshed out flow. And then you can use that as a blueprint for the actual app. That has been paying pretty well for me.

I hope some of this rambling is useful.

Tags: AI engineering coding-agent
11 Jul 2026

Engineered, Not Vibe Coded

People call coding with AI "vibe coding". Sloppy, they say. Not real engineering. I am not interested in having that debate. Whether it is good or bad in the abstract is not an interesting question to me.

What interests me is what the current advances in AI, especially coding agents, actually enable: rigorous engineering. And by rigorous I do not mean lines of code. Lines of code matter, but they are not the point. I mean being very intentional about the feature set you want, starting from a clear spec and a clear direction, and keeping things as simple as possible so you get to the core of the problem you are solving.

I have a concrete case: a few months ago I started writing OneScribe1, and I used Claude Code to write it. It has been my daily driver ever since. Whatever we end up calling this way of working, the result is a fairly robust tool that I rely on every day. I was in the driver's seat the whole time.

Why I built my own

Before writing my own, I used Wispr Flow and then the Willow Voice subscription service. Both work fine. I have nothing against either of them functionally. But the frequent updates kept making them more clever in ways I really did not want, and the feature set never quite met my needs. So I started writing my own.

A daily driver

I use OneScribe for far more than I expected when I started. I compose messages and emails with it. I talk to coding agents with it. I dictate blog post drafts with it.

In fact, this very post started as a dictation. I spoke my raw thoughts into OneScribe, read the transcript, and proofread it over and over. Then I asked Claude Code to clean it up, with strict instructions: make it idiomatic, give it structure, and do not generate anything I did not say. Which is pretty much the same way I drove it to write the code in the first place.

What kind of vibe is this?

I think we have been abusing the word "vibe". It is short for vibration, from the Latin vibrare, to shake. Somewhere along the way it came to mean coding by feel: wave your hands at the model, accept whatever comes back, move on.

That is not what I did. This tool was spec'd and engineered, with me guiding every step of the way. I chose the features. I wrote the spec. I set the direction. I rejected code that did not fit the structure and the idiomatic style I wanted, and I drove the AI until it did. None of it was autopilot. I was intentional about the specs, disciplined about the structure, and disciplined about the tests.

What drives a process like that is not a vibe. It is fundamentals, solid understanding, and long years of experience. The engineer is the one driving: setting the direction and managing the entire process. Call it vibe engineering if you like. Whatever the word, the distinction that matters is who is driving.

So is it sloppy? Is it vibe coded? I do not know what those words mean anymore. What I know is that I used a coding agent to build a robust tool that meets my needs exactly, with just the features that I want and none of the distraction or cleverness that I did not ask for. That, to me, is intentional planning and engineering.

Along the way I also used OneLoop, the small coding agent I built myself2. How I used it here is a separate post, and I will write about it at some point as a way of showing how I use coding agents in real work.

Footnotes:

1

OneScribe: press a hotkey, speak, and the text appears where you are typing. Overview: https://www.birkey.co/OneScribe/, source: https://github.com/oneness/OneScribe.

2

I wrote about building OneLoop and what it taught me in The Agent Is Not the Point.

Tags: AI engineering coding-agent
19 Apr 2026

Why I Love Agent Pi

Pi1 is the first coding agent that really empowered me to morph it to fit the way my brain works, and I am genuinely excited about how malleable it is on top of a handful of primitives.

Most coding agents I have tried in the past have grown in the same direction. More built-in tools. More opinionated prompts. More hidden, opaque decisions with no real visibility or control. More assumptions that I will bend my workflow around theirs - mandatory hooks here, implicit conventions there - instead of letting me configure a few simple things and get on with my work.

The small stuff gives it away. A recent example: Claude Code animates its "thinking" state with a glyph that lives in a font not every machine has. If you happen to use a font that does not have extensive glyph support, it makes eterm2 trigger a redraw on every tick, resulting in the surrounding text shaking visually. Yes, I can switch to a different font, but I did not have the choice to turn it off. It is a small thing. It is also exactly the kind of small thing you cannot fix from the outside, because the decision is buried somewhere you are not invited to touch. You start to succumb to their way of doing things. Small things aside, I cannot tell what the agent is really doing, I cannot tell why it chose a particular path, and I cannot tell how to change its behavior without jumping through the hoops they put in place.

Pi does almost the opposite. Its core is four primitives: read, write, edit, and bash. That is roughly it. Everything else is something you compose on top, or an extension you opt into. That small surface is exactly the thing I keep coming back for.

If you want a fuller sense of the thinking behind Pi, I would point you to a twenty-minute talk by its creator3. I do not often recommend agent-related talks - most of them age badly within a week or so - but the way he frames agents, agentic coding, and the current landscape is the clearest version of that picture I have heard recently. It is worth your time even if you never use Pi.

Small primitives, visible behavior

A coding agent with four primitives is not a limitation in the way it first appears. Most of what a coding agent actually needs to do boils down to reading a file, writing a file, changing part of a file, or running a command. If you can see each of those happen, you can reason about what the agent is doing. If you can script around them, you can make the agent behave the way your project already behaves.

This matters to me because I spend a lot of my time with agents trying to get clarity - building a clearer mental model of a problem before I commit to a direction. That work depends on visibility. What did the agent change? What did it run? What did it see? An agent whose behavior reduces to a handful of visible primitives is much easier to think alongside than one that hides its moves behind a thicker abstraction.

I do not think this is a new insight. Unix made the same bet a long time ago. So did Emacs, in its own way, with text, buffers, processes, and commands. The bet is that a small number of durable primitives plus a programmable substrate will outlast whatever feature-of-the-month the industry is currently excited about. Pi feels like that bet applied to coding agents.

Where Pi fits in the way I already work

I did not come to Pi looking for a new philosophy. I came to it because it fits two things I already depend on.

The first is Nix. I have written before about why I lean on it heavily4. In short, I do not want coding agents mutating my machine. I want them to declare what they need, pull it into an isolated shell, use it, and leave no residue. Pi cooperates with this model very naturally. Because its only way to reach the outside world is the shell primitive, I can point it at nix shell or nix develop and it will happily do real work inside a clean environment. A Rust toolchain, a specific Python, an odd CLI tool for one task - none of it has to touch my base system. When I am done, nothing is left behind except, if I want, a flake.nix that captures what actually worked.

The second is Emacs. I have also written about treating Emacs as a programmable workbench5. A coding agent that respects shell and files, and does not insist on its own fancy UI, drops into that workbench without friction. Pi runs in a buffer. As a simple example, I can make it interact with Emacs buffers to persist the commands it ran, so I can come back to them later if I need to.

Those two fits are not accidents of Pi's implementation. They fall out of the decision to keep the primitives small and the surface inspectable. An agent that tried to be a full IDE would not compose with Nix or Emacs this cleanly, because it would be busy being its own environment.

Extensibility I can actually use

The other thing I like about Pi is that its extension points are honest. When I want the agent to behave differently on a particular project, I do not have to reverse-engineer a hidden system prompt or wait for a vendor to ship a feature. I can add an extension, adjust a skill, or wire in a custom tool, and the change is visible in the same place the rest of the behavior lives.

Here is a concrete example. I wanted a small workflow for myself: whenever I ask one model to brainstorm an idea with me, a second comparable model should immediately review the response and list pros and cons, and optionally a third arbitrator model should weigh in when the two disagree. Useful pattern, but not something any agent I know ships out of the box. I asked Pi to build it for me as a peer-review extension. It did. Now, my brainstorming sessions work exactly as I wanted.

What made that work was not that Pi is clever. It was that Pi ships with documentation and code examples for its own extension points, and those examples are legible enough that the agent could read them and write a correct extension on the first try. I did not reverse-engineer anything. I did not hunt down a third-party tutorial. I just asked Pi to make the extension and it built the thing.

That is the part I keep thinking about. Pi is self-documenting in the same sense that Emacs is self-documenting6 - the system knows how to explain itself, to me and to any agent I put in front of it. That property sounds almost unremarkable until you notice how rare it is in modern tooling, and how much friction its absence quietly adds to every customization you try to make.

The honest trade-offs

Pi is not the right answer for everyone, and I do not want to oversell it.

Out of the box, it is minimal. If you want an agent that arrives pre-wired with a dozen integrations and a confident opinion about how your project should be structured, Pi will feel underdressed. You are expected to bring the way you think, work, and taste yourself. That takes some getting used to. It is the same kind of work Nix and Emacs ask for, and it pays back in the same way.

It is also a young project. Rough edges exist. Pi is not the only agent in this minimalist neighborhood either - Aider and OpenCode share a lot of the same instincts, and I think that is healthy. The argument I am making is not that Pi has won. It is that the design pressure behind Pi is the one I want more agents to feel.

Putting my money where my mouth is

At some point I realized I had been talking about Pi's design philosophy enough that I should probably just internalize it the hard way: by building something that takes the same bet from scratch. So I did. I wrote a small coding agent in Rust called OneLoop7. Its core is exactly what this post has been describing: four tools (read, write, edit, bash), one agent loop, and a session model that appends linearly to a JSONL file. Nothing else.

I am not bringing this up to announce a project. I am bringing it up because it proved something to me. OneLoop is rough, young, and does almost nothing compared to any "serious" coding agent on the market. And yet I am using it right now - in fact, the flake.nix in the OneLoop repo itself was written by OneLoop. The agent is building its own infrastructure. It works for real work because the model does the heavy lifting, and the agent's only job is to stay out of the way: hand it files, hand it a bash shell, and let it run. When your agent is just primitives and a loop, you do not need much more than that to be productive.

Building it also taught me something I did not fully appreciate from just using Pi. The hard part of a minimalist coding agent is not the agent loop - that is genuinely small. The hard part is everything around it: truncation heuristics so the model context does not explode, sensible output formatting so you can see what happened, session persistence so you can pick up where you left off. Those are real engineering problems. But they are infrastructure problems, not agent design problems, and it is useful to know the difference. Pi absorbs those infrastructure decisions so you do not have to think about them. OneLoop forced me to confront them directly. Both experiences are valuable.

Why this matters beyond Pi

The reason I wanted to write this down is not really about one tool. It is about a pattern I keep noticing in the parts of my setup that have aged well.

The systems I still rely on after many years tend to share a shape. A small number of durable primitives. A programmable substrate. Honest extension points. A willingness to be boring where boring is the right answer. NixOS has that shape at the system level. Emacs has it at the workbench level. Pi showed it to me at the agent level. OneLoop is my way of making sure I actually understand it.

I do not know which specific tools I will be using in five years. I do know that the ones that survive, for me, will look more like this and less like the feature-heavy alternatives that dominate the current moment. That is the real reason I love Pi. Not because it is the final answer, but because it is built in a shape that can keep being useful while the rest of the landscape keeps adding bloated features until it works for everyone - maybe not for you or me.

Footnotes:

1

Pi is a minimal, extensible coding agent by Mario Zechner: https://pi.dev/.

2

eterm is my fork of EAT, a pure Emacs Lisp terminal emulator, modified to fit my workflow.

3

Mario Zechner's talk on agents and the current agentic coding landscape: https://www.youtube.com/watch?v=RjfbvDXpFls.

4

My earlier post on why I rely on Nix, especially in the LLM coding era: https://www.birkey.co/2026-03-22-why-i-love-nixos.html.

5

My earlier post on Emacs as a programmable workbench: https://www.birkey.co/2026-03-28-emacs-as-a-programmable-workbench.html.

6

The GNU Emacs manual describes Emacs as a "self-documenting" editor and explains that this means you can use help commands at any time to find out what your options are and what commands do: https://www.gnu.org/software/emacs/manual/html_node/emacs/Intro.html.

7

OneLoop is a tiny coding agent written in Rust. It is a private repo for now. I will open-source it at some point when I believe it is ready.

Tags: nix emacs coding-agent ai
Other posts