The Conversational Compiler

The Conversational Compiler

Exploring structure, constraint, and the limits of AI-assisted coding

It started with frustration and fascination. AI coding tools can be brilliant and disappointing, sometimes within the same prompt. Even with large context windows and carefully structured inputs, working with language models still feels like walking a tightrope. One wrong input and the model might drift, ignore constraints, or hallucinate solutions I didn’t ask for.

At first, I wondered if I was just doing it wrong. But the more time I spent trying to use LLMs in structured ways, the more I realized the problem might not be with the prompts or even with me. It might be the way these systems are designed. These models were not built to maintain flow, follow constraints, or stay grounded in large project contexts. They were designed to predict the next word.

And yet we keep asking them to do much more. We want them to build software, follow plans, and collaborate in structured, goal-oriented work. That disconnect is where most of the friction comes from.

Andrej Karpathy once said that “LLMs are programmed in English” (source). That framing stuck with me. But if prompts are programs, what actually serves as the interface? What keeps things on track? How do we get consistent outcomes from a tool that is inherently probabilistic?

This journal entry is a reflection on my attempt to answer those questions and the limitations I’ve run into.

Building a Flow Blueprint

I began experimenting with what I call a flow blueprint. It is a kind of natural language state machine that describes application behavior step by step. Each state includes entry points, transition rules, and validation steps. I use it to guide the model during implementation and to validate whether it has adhered to the desired behavior.

The blueprint gives both me and the model something to align on. When adding new features, I work with the LLM to update the blueprint first. Then I use it to create a project plan that outlines how to move from the current state to the desired one. From there, I work with the LLM step by step, getting it to generate code or write tests as needed.

At first, this process helped. The LLM stayed on track longer and I could spot when it drifted or skipped a step. But the results were inconsistent. Sometimes the model would jump ahead, repeat work, or decide it had completed the entire task on its own. In one session, it ignored the blueprint and tried to force its way to the end. Another time, it responded more compliantly after I told it its behavior was making me sad. That moment stood out. I asked myself, “Did I just give the model a guilt trip?”

So I kept going and added more structure.

Adding a Flow Controller

To bring more control to the system, I built a flow controller. It is an MCP server that turns the blueprint into a live state machine. It tracks which step the model is on, enforces transitions, and stores session data so that context can be restored when the window breaks down.

I also added hooks that run before and after tool use. These help validate that each step is being followed correctly and provide guardrails around the model’s actions.

This setup gave me more control, but the process remained fragile. The model would still drift, skip steps, or repeat work. In practice, it still required constant vigilance and prompt engineering to keep it aligned.

I have also been thinking about using slash commands like /start-flow to initiate the implementation phase and signal a shift from planning to build mode. This has not been implemented yet. It is simply an idea to reduce confusion and better separate free-form discussion from structured execution.

Even with these additions, the model struggles to follow rules across multiple prompts. Once the initial context is lost or diluted, it often reverts to unstructured behavior. You can re-ground it using the flow blueprint, but the amount of overhead involved makes the process brittle.

Reevaluating the Compiler Analogy

Originally, I thought the LLM might act like a compiler. I imagined that with the right structure and constraints, it could take intent and reliably produce implementation. That idea was appealing. But it has not held up in practice.

We have heard that AI will replace software engineers. My experience shows the opposite. If you let the model do most of the work, the outcome is usually poor. It generates code that does not scale, loses track of architectural patterns, and fails to stay aligned with a broader system.

To make it useful, I have to create the architecture, define the flow, and build the scaffold. Once that exists, the LLM can fill in small parts. But on larger tasks inside a real codebase, it becomes a source of friction more than acceleration.

LLMs also have a hard time respecting context, even when I give them structure. I have used files like CLAUDE.md, provided detailed flow blueprints, and structured task files. But after a few prompts, that structure is often lost. You end up re-adding context over and over again, and even then there is no guarantee the model will follow it.

The idea of wrapping a probabilistic engine in a deterministic layer sounds good. But building that layer has turned out to be much harder than I expected. My flow controller, the hooks, the project scaffolding—all of it helps, but none of it solves the core problem. The model does not reliably adhere to rules, even when those rules are clear.

Where I Am Now

As Karpathy said, prompts are programs. But that does not mean we have a working system. Flow blueprints might be part of the solution. They provide structure and enable validation. But they are not enough.

The interface I want still does not exist. I am starting to see where it might emerge. I no longer believe the next major advance will come from the models themselves. It will come from what we build around them. That includes scaffolding, memory, execution control, and interfaces that are built for complex workflows.

Until we get that, we are still burning energy trying to force reliability from systems that were never designed for it. But I still believe the work is worth doing. Because if we can shape that layer, we might finally be able to move from prompting to programming.