Skip to content

Chapter 2: From DBA to Vibe Coder

I do not like the term "vibe coding."

I used it in the chapter title because it is currently the shortest path between the words and the concept. But let me tell you what I actually mean by it, because the term as it circulates on tech Twitter carries a connotation I want to separate from what I am describing.

The popular definition is something like: describe your feature to an AI in plain English, accept whatever it produces, ship it. Do not read the code. Do not understand the code. Just vibe.

That is not what I mean. That is the path to production software that nobody on the team understands, and when it breaks at 2 AM, it stays broken longer because the codebase is alien to everyone touching it.

What I mean by vibe coding, at least in the context of building Bob, is closer to this: focus on intent, not syntax. Know what you want the system to do. Be precise about the requirements and the constraints. Use AI to generate the structural code, the boilerplate, the framework scaffolding, because that work is low-value and time-consuming. But review everything. Understand everything. Treat the AI as a fast junior developer who needs close supervision, not as a senior architect whose output you trust without reading.

That distinction matters because Bob does things with real consequences. It runs T-SQL against production databases. It takes Proxmox snapshots. It sends alerts. An agent that does any of those things based on code nobody reviewed is a liability, not a feature.


The shift from pure DBA to something you could call a systems builder happened over a couple of years and was driven by necessity more than ambition.

The practical reality of working at a digital agency is that the DBA role does not exist in isolation. You are also the person who configures the monitoring server, writes the deployment scripts, sets up the Docker environments, and figures out why the staging database disagrees with production. Over time, the line between "database work" and "systems work" becomes irrelevant. You do what needs doing.

That context meant I already had a Python environment and some exposure to REST APIs before I started thinking about Bob. I was not coming in cold. But I was also not a developer. My code before AI assist was functional and ugly: lots of string concatenation, no type hints, test coverage as an afterthought. It worked. It was not something I would show anyone.

When I started using AI for code generation, two things happened. First, the output was better than my equivalent unassisted output, at least structurally. The AI wrote cleaner function signatures, used the right data structures, caught edge cases I would have missed. Second, and this is the part I did not expect: I started learning faster. Not because the AI explained things, though it did when I asked. Because I was reading more code in a day than I had read in a week before. You learn to code by reading code. If AI can generate twenty good examples of a pattern in the time it takes me to write one bad one, that is a faster feedback loop.

The combination of those two things meant that within about six months, my ability to build systems had expanded meaningfully. Not because I was a better programmer. Because the cost of attempting something had dropped. I would try an approach, read what the AI gave me, modify it, test it, and discard it if it did not work. The discard rate was high. That was fine.


The first Bob prototype was not called Bob. It was called something embarrassing that I am not going to put in this book.

It was a Python script, about 200 lines, that connected to SQL Server via pyodbc, ran a handful of DMV queries, bundled the results into a prompt, and posted them to an Ollama API endpoint. The response came back as plain text. I printed it to the console.

That was it. That was the prototype.

Let me describe one specific session that was representative of how the thing got built, because the retrospective version sounds cleaner than the reality was.

The feature I was trying to add was deadlock detection. I knew what I wanted: the agent should extract deadlock events from the SQL Server system health session, parse the victim chain, and include a summary in the diagnostic context. I knew the T-SQL to query the system health extended event session. What I did not know was how to parse the deadlock XML in Python in a way that handled the multiple schema variations SQL Server uses depending on the version and the deadlock type.

I described the problem to the model. Not "write me a deadlock parser." That gives you generic code that handles none of the specific SQL Server quirks. I described it as: "I need to extract the victim process ID and the blocking resource from SQL Server deadlock XML that looks like this," and I pasted an actual deadlock XML blob from my test environment. The model gave me a parsing function using xml.etree.ElementTree that worked for that specific deadlock shape. I ran it. It worked on the example. I tested it against three other deadlock XML samples from the system health session. Two of them had a different structure for the blockingObject node. The parser broke on those.

I fed the failing examples back and asked it to handle both structures. It revised the function. I tested again. Still failing on one case, a deadlock involving a row lock on a heap table with no clustered index, which has a different XML structure than a deadlock on an indexed table. Third iteration, I described the specific difference. The model produced a function that handled all four cases I had tested. I added a try/except wrapper around the whole thing to handle any fifth case I had not seen yet, logged the raw XML when the parser failed, and shipped it.

That session took about 45 minutes. Writing the same parser from first principles, reading the XML schema documentation, handling all the edge cases myself: call it three hours minimum, and I would have been less confident in the result because my XML handling in Python was not strong. The model's XML handling was better than mine. I reviewed every function it produced. I tested it against real data. I made the judgment calls about what "good enough" looked like. The collaboration produced something I understood and trusted.

That is vibe coding done right. Not accepting the first output. Using the model to close the gap between "I know what I want" and "I know how to build it," then applying your own judgment to decide when you got there.

The response quality was inconsistent. Sometimes the model said useful things. Sometimes it said things that were technically accurate but irrelevant to the actual data. Sometimes it hallucinated column names from DMVs that do not exist. I tuned the prompt, read about temperature settings, tried different models.

The skepticism I had about this process did not go away quickly. Twenty years of building muscle memory around SSMS, query plans, and DMV queries is a real thing. The reflex when something breaks in a database is to open a familiar tool, run a familiar diagnostic, apply a familiar fix. There is speed in that reflex. There is also a ceiling: you can only move as fast as your hands can type the queries you already know.

What shifted was a specific moment, not a gradual conversion. I was investigating a slow query that had suddenly gotten worse after a deployment, standard scenario. The developer who deployed the change swore the stored procedure was identical. The query plan had regressed from a seek to a scan on a key table. I went through my usual process: checked the statistics date, ran the update, compiled new plans, tested. Still scanning. Spent about an hour on it before I was willing to admit I was not seeing something.

I described the situation to the model, pasted the before and after query plans, and asked what changed. It came back in about fifteen seconds with: the parameter sniffing issue is on this specific parameter, the plan was compiled when the parameter value was low-cardinality, and the deployed change probably altered the stored procedure with RECOMPILE removed. Check the procedure definition for the WITH RECOMPILE option.

That was it. The developer had changed the procedure signature as a "minor cleanup" and removed an option that I had added years earlier and that had no comment explaining why it was there. The model spotted the structural difference in the plans that I had been staring at for an hour. Not because it was smarter than me about SQL Server. Because it was looking at the data fresh without the hour of sunk cost that made me keep reaching for solutions I had already tried.

That was the conversion. Not "this is better than me." Just: sometimes the fresh eyes are the tool you need, and if the fresh eyes can do it in fifteen seconds at any hour, that changes what you build around them.

Over the course of several weeks, the consistency improved. Not because the model got better, though I was switching between versions and sizes. Because the prompt got better. I learned what information the model needed in context to reason well about SQL Server state. A raw wait stats snapshot with no baseline context produced worse output than a snapshot plus a rolling average plus a note about what the application load was doing. The model was not magic. It needed good inputs.

That prompt-engineering process was the first real vibe-coding lesson. You are not writing code when you write a system prompt. You are writing a contract. You are specifying what the agent knows, what it is supposed to do, and what constraints it must respect. Clarity in the contract produces clarity in the output; ambiguity produces creative interpretations you did not want.


The prototype validated the direction but exposed the limits of a 200-line script.

The first limit was state. A single prompt-and-response exchange has no memory. If the model identified a suspicious query at 9 AM and I wanted to check whether it was still suspicious at 2 PM, I had to start from scratch. There was no continuity, no ability to say "earlier you flagged this query as a candidate for index optimization; has the wait pattern changed?"

The second limit was action. The prototype could diagnose. It could not do anything about what it diagnosed. The output was text. Someone, me, had to read that text and decide whether to act. For after-hours monitoring, that defeats the purpose.

The third limit was integration. The prototype knew about SQL Server. It knew nothing else. If the cause of a query performance problem was a VM on the hypervisor starving for RAM, the prototype could not see that. It only saw SQL Server state.

Addressing all three limits was the transition from "script that wraps an LLM" to "agent architecture." That architecture has a name now: three layers, two inference hosts, one Model Context Protocol server tying it together. Part 2 documents it in full.

But the shift in thinking that made the architecture possible was simpler than the architecture itself. I stopped asking "how do I make the AI smarter" and started asking "what does the AI need to be useful?"

It needed memory. It needed tools. It needed a scope it could reason about completely, not just a fragment of the system state. Once I framed the problem as "give the model what it needs to reason well and give it hands to act with," the architecture followed from those requirements pretty directly.


The word "vibe" in "vibe coding" captures something real, even if the full phrase is a little glib. When you work this way, there is a mode shift compared to traditional programming. You are not sitting down with a blank file and building upward from first principles. You are having a conversation with the system about what you want. The conversation has a feel to it. You know when you are getting close. You know when the output is technically correct but missing the point.

That is not a replacement for knowing your domain. It is the opposite. The better you know SQL Server, the better your prompts get. The better you understand agent architectures, the better you can evaluate what the AI generates. Vibe coding, done well, amplifies domain expertise. It does not substitute for it.

The DBA background was not a liability in this process. It was the whole point. I knew what questions to ask. I knew what a good diagnostic read like. I knew which signals to weight and which were noise. The AI handled the code generation. I handled the judgment.

The next chapter talks about why "something similar" matters at the organizational and community level. But before we go there: in Chapter 4, the hardware is real, the IPs are production, and the machine learning is running in your basement, not someone else's data center.