Vibe Coding with `reset --hard`
Dolt is the world's first SQL database which supports version control features branching and merging. As a business, we'd be foolish not to pay attention to the new frontier of computing: AI. The promise to make us, or our customers, 10X more productive is too good to ignore. We talked about it before with our Robot Blogger, and Agentic Workflows.
I recently started using Cursor. "The AI Code Editor". I have so much to say about it that I broke it into two posts. Previously, I discussed how using Cursor in my day job is going. Today, I'd like to talk about how you can use Cursor to write a Dolt application. And more generally, how git reset --hard
is your friend.
I'm a Git NERD
I'm a Git NERD. I love Git. I was lucky enough to spend 6 years of my career getting paid to mess with it. Even within Dolt, a version control focussed company, I drive my peers a little batty with my Git etiquette.
Version Control is a common tool in every developer's toolbelt. It's a way to track changes to any set of files. It's a way to collaborate with others. It's a way to go back in time. It's a way to make sure you never lose your work. If you get in the habit of regularly committing your work, you'll be able to use Git to its fullest extent.
Dolt is modeled after Git, and it has all the same advantages of collaboration, time travel, and backup. We fully believe in the power of version control - especially in the context of AI agents and the work they do.
This is all to say, I'm biased. When I look at the possibility of using AI to write code, I believe it's impossible to do so without version control. Let's discuss.
My Coding is Chaos
When I'm working on a new feature, it gets messy. Even without the help of AI, I usually read the code, and leave breadcrumbs everywhere. I will edit a file, usually with just the text NM4?
, as a marker for where I found something of interest related to the feature I'm working on. Sometimes it will be code to just mess with an object, or a test to see how something works. This can end up being dozens of breadcrumbs, and the way I find them is I run git diff
to see what files I changed.
Those breadcrumbs don't ever get shipped as production code, but in order to track them for the duration of working on the feature, I commit them. This allows me to pull changes and rebase to stay up to date. Or push half baked code to the CI pipeline to find out what I broke. All of this required committing frequently and committing code I know I'll never ship. And when I've finally addressed all the breadcrumbs, I clean up the code with a git rebase
to remove any record of the mess.
You can understand why I'm a big proponent of version control. I literally don't know how I'd do my job without it.
One of the benefits of committing frequently is that you can use git reset --hard
to go back to bounce between states. It allows me to jump between states quickly to test before and after behaviors (what did I break?). When doing a code review, I'll often pull the branch down and check out the branch at the commit before the changes were made. This lets me see the changes in IDE context and ever run the code in it's two versions if I really need to know what's changed in detail. git reset --hard
is just a dangerous way to checkout a committed branch. This is a powerful tool, and I use it all the time.
AI is a Chaotic Code Editor
I've been playing with Cursor recently, and the first thing I noticed was that it makes a mess, just like me. You prompt it to do something, and boom you get a new file. The code flows like bull through a china shop with things breaking everywhere. Pretty quickly I'm at the point where I want to commit the code and run it through tests. Or I want to put a pin in something generated and go back to the previous state. This is where Git becomes a godsend for the AI coder. And it's taken to the next level with Agentic Workflows. If we are going to use AI to write code, we need to be able to use Git to its fullest extent.
Let's Build
We are going to go through the steps of building the game Battleship for the terminal. Board state is stored in a Dolt database. The inspiration comes from the picture below. That's my kid more than a decade ago. He wrote a Battleship game in Ruby. I figured if AI is like a Jr. Developer and my pre-teen could write a Battleship implementation, then I should be able to write one with AI.
Caveats, Warnings, and Buyer Beware
If you've been paying attention to this space, there are a lot of examples about people building with AI, only to discover their code is full of security problems and other nasty things. This is doubly true for this example! We'll implement Battleship with a wide open database. If either player wanted to cheat, they have all the data they need with single select
.
The First Prompt
>> Create a structure for a golang project that will print colored text
to the terminal, and store state in a dolt database. In the current
directory, create a src directory which will be the root of the git
directory, then create another directory called data.
This produced a few files, created some directories, and the code did run (basically hello world). Also, it created two Go packages, database
and terminal
. I would show it to you, but alas, that history was nuked by Cursor. But all that is to say it got the party started.
The Second Prompt
>> change your cwd to data, and start a dolt sql-server process.
look at the documentation for setting the port to 9889
direct std output and error to the `data/dolt.log` file.
This took a couple times to get right. The main issue I've noticed is that the agent doesn't seem to keep its current working directory "in mind". Once Cursor got it right, there were two new windows open. One looked like this:
And the other looked like this:
This is pretty cool. This is one of several times when Cursor anticipated my needs. I didn't ask to tail the log file, but it did it anyway.
DB Connection
>> connect to the dolt database with a shell
The agent used mysql
to connect to the database, specifying the correct port and whatnot. I wanted to use the dolt sql
shell, which it had a little trouble with. Understandably, it tried to use a host connection string, but it placed the --host
option in the wrong place. I informed it that just running in the same directory as the server will work.
I hit that little "Pop out terminal" button, and that terminal stayed open for the rest of my coding session. Pretty nice to have all the pieces of the development env right at my fingertips.
Connecting Code to the DB.
>> create a table which represents the board states for the game.
model it with two coordinate columns (x,y), a third column which indicates one of four boards: red_ships, blue_ships, red_shots, blue_shots.
Final column is one of the three values "H", "M", "S".
None can be null.
primary key is a composite of x,y,board.
do this over the db connection in the app.
One of the things I'm getting used to with the tool is continually updating the prompt. Basically each line in the prompt is me trying again with more clarity. This is a good example. As I got more specific with each iteration, I could see the code generated. First it guessed at the table, which I confess was asking it a lot. Then I got more specific until it produced the thing I wanted. Interestingly, it first attempted to create the table using dolt sql -q "create table ...
which would have been fine, but my plan was to create the table on a game branch, so I knew it needed to be in the go code. But the point is that Cursor did modify the database, and I needed to roll it back. You know what that means? dolt reset --hard
.
Anyway, after a few prompts and a couple hard resets, I had code that looked like this:
If you look at the prompt though, every step of the way it was generating SQL and running it, until I said "do this over the db connection in the app". At which point it pivoted and stuck it in the go code. I've tried this in more complicated applications with foreign keys and other constraints, and it works just as well. If there is one thing I do a lot with AI assistants, it's to ask them to generate SQL.
Presentation
In order to see the state of the board, I took a little diversion and added a PrintBoard
function. Culminating in the watch
command, which would basically print both boards and update as the game progressed. When there was nothing on
the board it would print something like this:
One trick you may notice in the code is we used the dolt_hashof_db()
function to determine when the database has changed:
Populating the Board
At this point I have commands which will display the state of the board, and I want to just put ships on the board. I had a couple uncommitted changes to the database that I didn't care about, so I reset --hard
and gave this prompt:
>> I've cleared the boards. Generate the sql to fill both red and blue boards with the full complement of ships for both.
place them in random position and orientation.
Just raw SQL
Again, it took a couple tries. Before I specified "random position and orientation" in the prompt, it gave me sql which put the ships evenly spaced to the left:
That's not right. What do we do, reset --hard
and try again. This time I specified "random position and orientation" in the prompt:
What's most impressive to me about this is that I never once told it about the rules of Battleship. You can see that it knows what kind of ships have which length. That saved me a google search right there. Meanwhile, the code which prints out the board is updating every time the database changes. And now it looks like this:
Now that we have what looks like a good starting board, let's commit it to Dolt.
Time to Play?
At this point, I was actually surprised that the mechanics of the game weren't what I wanted to do. I was planning to have prompts in the terminal and so forth. But I had this AI which apparently knew the rules of the game, and it could write sql statements to make moves, maybe? I wasn't really sure where this adventure was taking me. So I asked it to play.
Which is funny because that's not where my submarine is. That's where blue's submarine is, so friendly fire I guess.
Back to grinding. No fun yet. After a bit, I got the board looking kind of respectable.
Taking Turns
I added another table in this commit. The idea was that each player as they joined threw in a random number as their "coin flip." Whichever was higher went first. Each time they updated the board, they changed the value in the table to indicate it was their opponent's turn.
Once that was in place I added dolt commits for each update of the board.
You can look at each commit individually to see what changes were made to the database at each move:
And finally, I want to move the game back 3 moves, I can do the following:
battleship/game_1> \reset --hard HEAD~3
TODOs
I didn't get as far on this as I hoped. I thought I'd have automatic agent to play with, but I guess that was wishful thinking. I'll have to come back to it.
But really, the bigger picture of having multiple games, each on their own branch I didn't even get to. The goal was to show that the board state table and the coin flip table would only exist on the branch. The result of the game would be dropping the tables and merging into main... with a games table which we didn't even get to model yet. Version Control without merging is just taking snapshots. It's a way to go back in time, but it's not a way to collaborate with others. Maybe next time.
Parting Wisdom
Surprise and awe at a new tool is great. I think there is a reason so many people are excited about AI. It truly makes me think "Huh, I wonder if it can do XYZ" and then it does something novel. Sometimes it's a little aggressive and it rm -rf
s your .git
directory. It really did delete my .git
directory, so maybe I'll amend my earlier advice from "commit frequently" to "commit frequently and push to a remote". Aside from that though, having the ability to use version control to not only see how my code was being changed, but also to see how my database was changing is really useful.
If you don't know SQL well, you can use AI to help and Dolt to make sense of it. Dolt really shines when you need to have a really crisp understanding of what changed when. Ask the AI to do something, dolt diff
to see what changed. Commit frequently and dolt reset --hard
if you need to start over. Having a SQL database with version control at your fingertips is a powerful development tool.
Checkout Dolt and tell us how you are going to use it to build something surprising. We're always happy to chat on our Discord!. Be safe - Commit Often!!