← writing

Agentic Coding, the Old Fashioned Way


Are we in the loop?
Are we in the loop?

As a lifelong software developer, I have mixed feelings about using LLM for coding. I can do soooo much more now that I’m AI-enabled, but keeping up with the development in this space is relentless. With new frameworks coming out every day it's easy to get lost down the rabbit hole, chasing the latest shiny/expensive toy. You lose the advantage that Agentic Coding claims to offer: productivity.

Unfortunately, it seems like AI is mostly good at making more code, not necessarily better code, and the constant churn of different Agent frameworks in the marketplace is burning out the people they are supposed to be helping.

I believe that getting high quality maintainable code with AI is definitely doable but it requires a lot of human experience. Instead of trying to adopt each and every framework, I’ve been working on refining the experience using the out-of-box coding experience from Claude code and similar tools. It turns out that you can do quite a lot without needing to pay for additional tools.

Why LLMs Struggle with Code

There is lots of promise in coding agents but they often fail, even on relatively simple projects. The market is flooded with tools that try to work around context limits and the like, but there’s a fundamental issue with most coding agents. They know a lot about code, but not much about coding leading to reams of slop that human developers are then tasked with maintaining.

A big pile of Slop
A big pile of Slop

The key to getting great output is understanding that LLMs are great at mimicking people but really bad at acting like a computer. The more you try and coral an LLM into machine-like predictable outcomes, the worse it’s going to perform. My AI coding improved tremendously once I started treating the LLM as a person (actually persons) rather than a calculator.

To illustrate, consider the frustrating tendency of LLMs that keep solving problems by barfing out fully implemented solutions in one long stream. This would be the equivalent of a programmer writing an entire system from scratch with only a text editor and their stream of consciousness. It's crazy when you think about it.

The fact that LLMs can stream out fully implemented solutions for some very simple (and/or common) use cases is truly impressive. Try typing “make a tetris game” into your favorite AI Chat tool, and you will get an impressively functional result. About as functional as the hundreds of implementations you can find by searching for “tetris source code”. But, try asking it to do something a little more novel and you’ll spend the rest of your day getting angry at software.

The reason why is that, humans do not write code that way, so it makes sense that an LLM which is built around human language and interactions would perform poorly trying to just “think it out”. In fact, I’ve spent my entire career learning and teaching how to write code in a clear and concise way, optimized for maintainability, because we know by now, writing the code is the easy part.

It's not just code, it's a practice

A Vibe Coding Dojo
A Vibe Coding Dojo

Keeping software alive and healthy is a never ending task that requires strong coding practices like requirements gathering, Behavior-Driven Design (BDD), Sprint planning and sequencing, and test-driven development (TDD). Practices like these have evolved to keep humans in the programming biz despite the ever larger and more complex “systems of systems” that we build and maintain today.

Over the course of the last 25 years of software development, practitioners like myself have transformed the practice of software development from an Art to a Science. It turns out that the rigor of a thoroughly implemented SDLC works just as well with LLM code agents as it does with human developers. These highly developed software development practices provide structure needed to achieve predictability and sustainability with complex software systems.

We've learned that hero developers are an anti-pattern and what is really needed are teams with specialized roles that make modern software development work. So what we need from LLM to create great code, is not one LLM but a team. And that team needs to have a philosophy of professionalism and high standards. Standards inspired by the great Bob "We don't ship Shit" Martin (aka Uncle Bob) and others, like Martin Fowler and Kent Beck (of the Agile Manifesto), who demanded better practices in the field of software development.

Uncle Bob Martin
Uncle Bob Martin

In his many talks, Uncle Bob repeatedly raised the bar for the software development industry. He gave in the weeds engineers permission to demand more from themselves and from their (sometimes impatient) colleagues. It is in that spirit of professionalism that I built:

The 'Bob Protocol'

A simple approach to agentic coding that tries to reach the level of technique, excellence, and professionalism that Uncle Bob demanded and that the best engineers aspire to.

But first, It all started with an agent named Bob.

Bob is the name I gave my original Prompt Engineering agent way back in the ChatGPT-3 days. He has skills for defining other agents and I used Bob as a tool to assemble a complete and professional engineering team. Each member is crafted from an actual role in software development and is specialized to perform that function as if they were a part of a highly productive engineering team at a tech company.

While rare in the wild, an ideal engineering team consists of (at least) 5 members who support each other throughout the development process. Each member has the specialized expertise to perform their specific role. For humans this specialization helps teams manage the overall cognitive load of a complex project but it works for LLMs too.

Let me introduce:

The Crew

The Crew
The Crew

Cypher


Role: [Product Manager]

Defining the “why” and “what” of a product and guiding a product's lifecycle from conception to launch and beyond. Acting as the intersection between business strategy, user needs, and technology.

Artifacts:
- Product Requirements Document (PRD)
- User Stories
- Business Requirements Document (BRD)
- Measure of success
- Backlog

Morpheus


Role: [Technical Architect]

Designing, implementing, and maintaining robust, scalable, and secure technology systems that align with business goals.

Artifacts:
- Architecture Blueprints
- Class Diagrams
- Technology selection
- System Design

Neo


Role [Software Engineer]

Designing, developing, testing, and maintaining software systems by writing clean code, analyzing user needs, and resolving technical issues with Test Driven Development (TDD).
Artifacts:
- Functional source code
- Unit Tests
- Implementation plans
- Refactoring plans

Mouse


Role: [Project Manager / Scrum Master]

Planning, executing, monitoring, and closing projects, ensuring they are completed on time, within budget, and to stakeholder standards.
Artifacts:
- Project Plans
- Sprints
- Tasks
- Status

Trinity


Role: [Software Engineer in Test]

Ensuring software quality by designing, developing, and executing automated and manual tests to identify bugs and performance issues.

Artifacts:
- Hermetic Tests
- Integration tests
- UX tests
- User Acceptance Report

The Oracle


Role: [Knowledge Officer]

Managing the team’s intellectual capital by capturing, organizing, and disseminating both tacit (expert knowledge) and explicit (documented) information.

Artifacts:
- Documentation / TL;DRs
- User Guide
- README.md
- Knowledge Base
- Lessons Learned
- Decision log.

Each Character on the team is designed as an Agentic Skill, using frontmatter syntax, and exposed so that the LLM can invoke and then access additional sub-skills that are relevant to the responsibilities of the given role. This is similar to some other agentic coding frameworks, except there is no framework here. It’s running with standard AI coding tools like Claude Code, Gemini Code, or agentic coding IDEs like Cursor and VSCode and work out of the box.

One key innovation that ensures that the team is working together is a chat tool that the agents use to send messages to each other by appending to a CHAT.md file in the project. This file is critical for context persistence and state management across multiple LLM invocations and when switching between different LLMs. Here's a sample:

The Crew Communications
The Crew Communications

Each CHAT.md message serves as a “step” in the development process with messages provided by the agents and the user to help manage the overall flow of the Software Development Life Cycle (SDLC) and provide feedback and support to when the team needs help.


The Agentic Invocation Cycle

Feedback loop
Feedback loop

Through what I call “Bob Protocol” the LLM follows an interactive and iterative cycle. I’ll usually start by describing the project in a prompt and asking Cypher for formal product requirements by invoking Cypher’s product manager skills using the chat tool like this:

chat @cypher pm create PRD

The sub commands work as a simple vernacular so I don’t need add much to the instruction.

When LLM activates the Cypher skill it loads Cypher’s personal context, todos, plans, etc.. from Cypher's agent specific folder: agent/cypher.docs/. The LLM will iterate through some Q\&A as Cypher the Product Manager, capturing requirements from the user and constructing the relevant user stories and exit criteria. When the Product Requirements Document is written it "Cypher" will "send a message" using the chat tool announcing what was done and, importantly, what to do next..

As the user of the system. I review the generated .md files, which already contain questions that the Cypher needs me to resolve. I’ll add inline feedback, move things out of scope, and refine the criteria if necessary. Then I’ll use the chat tool to send a message like:

chat @cypher pm see feedback

I am the "Human the loop" and I actively engage with the process from the very beginning rather than attempting an impossible and likely performative review at the end. I can use very high level instructions because the agent is specialized enough to not get confused and when needed, adding additional sub commands is trivial.

After I approve the requirements I’ll notify Morpheus the Team Lead and Architect, again via the chat tool.

chat morpheus ta arch all

That triggers the LLM to switch personas, first offloading the Cypher context documents, and then load context and skills belonging to Morpheus. The simple ‘arch all’ sub command expands to the expected actions that a senior technical architect is expected to perform.

Morpheus is mostly responsible for the technical planning documentation for the project and after a few rounds of back and forth we have a solid architectural plan and can hand it over to Mouse for planning the first Sprint.


Engagement is Necessary for Command and Control

Asleep at the wheel
Asleep at the wheel

It’s important to spend a lot of time reviewing and commenting on the documentation and refining the artifacts with chat messages, but this is where I can leverage my experience to steer the design and architecture in the right direction. It’s helpful that I’ve worked in and around most of these roles throughout my career so, I know what good looks like.

If you don't know what good looks like, it's not going to work.

When working with LLMs you need to have your "bullshit meter" set to 11 so you can catch misunderstandings or unnecessary requirements that would otherwise doom the project from the start.

It’s not unusual to spend a few hours reading and refining the documentation before generating any code. This is needed to ensure that the system understands my intent and will produce the features I want in the way I want them to be written.

The cycle mirrors an idealized SDLC process. One designed to avoid all the common pitfalls that us humans spent the last 20 odd years learning how to avoid. For some reason, LLMs haven't learned these lessons but rather the opposite - being trained on reams and reams of low quality software (which is the industry norm btw).

But how did people solve this problem? We do what humans always do: make tools.

One tool to rule them all

The Eye of GNU
The Eye of GNU

I have mixed feelings about agent tool use, but I’ve found an old school way to specifically proscribe and limit agent actions by requiring them to be invoked from a Makefile. Gnu Make is used to formalize and manage the invocation of each agent's skills. It is a tool that is typically associated with C/C++ development but it is actually language agnostic. It makes it very simple to define new Makefile targets either manually, or via an agent. It does the heavy lifting of dependency management, and build sequencing and is inherently repeatable. A property that pure agentic systems sorely lack.

Again, Gnu Make is a tool that was invented a long time ago to solve this problem for human developers and it should be no surprise that it works great for my team of fake humans. There are many other tools for humans that come into play and all can be normalized via the simple, old, Makefile.

For example, many human teams use commit gates to ensure that the code they contribute is in a good state. The gates don't rely on developers' assurances about the code, rather they remove the subjective nature of code review with rigid rules that prevent bad code from getting into your team's repository.

These gating tools include test frameworks, formatters, linters and a variety of other language specific static analysis tools. Trinity’s character is a specialist at using tools like these and will set up the Makefile for the various kinds of testing needed for a high quality User Acceptance commit gate. Trinity doesn't need special "training" to judge quality instead, relying on the same dispassionate and metric driven analysis tools that development teams have relied upon for decades.

There's no School like the Old School

Neolithic coder
Neolithic coder

My Bob derived agents (not a framework) work because they are using tools and practices that have already been proven to work with humans. Keeping character specific contexts and the global CHAT.md log. The written artifacts, help the humans catch up and makes it easy to start new chat sessions to continue the work.

It’s mesmerizing to watch the agents communicate through the chat tool and feels very natural as a developer. I can follow the progress, pipe in with a chat message of my own from time to time and be confident that the solution is not regressing over time (hooray for unit tests).

By using this approach, I can consistently get high quality code, with excellent test coverage, that uses abstractions that we humans can understand and contribute to. If Neo is stuck or Morpheus includes unnecessary components, I can easily intervene, and use some of my own coding skills to help the team through a complex part of the project.

You can add the Bob Protocol agents to your own project by cloning the repo and running

git clone https://github.com/drusifer/bob_protocol.git  
cd bob_protocol  
make install TARGET="your project folder"

You can start by prompting something like: "init bob_protocol (play along it's fun)" and then start chatting with the team about your project and they will leap into action on your behalf..

Use Your Wits

For a real example, take a look at my "team's" latest product: A game called The Battle Of Wits, created from scratch using the Bob Protocol. Browse the artifacts in the repo, and take a look at the CHAT.md file to see the history of the agents in action.

Finally, don't forget to play the Battle Of Wits game and see if you can best Vizzini, the world's smartest man, in a Princess Bride inspired composition that runs as a single page web app with no external dependencies.

Agentic coding is here, and with a lot of help from the humans, it actually works.

Happy Vibe-Coding,

Drew