I've been using AI IDE assistants for a while now (mainly co-pilot and then progressing to cursor), and the results have been really helpful. They have, however, primarily been a sort of supercharged auto-complete. I wanted to experiment with creating a library from scratch, both because I was curious in how well these tools could be used to build something good from scratch, but also because I wanted to see this library built and I have limited time after work and family.
I'm a big fan of event driven architecures, but there's a lot of boilerplate involved in setting up the infrastructure to support it (not to mention cost if its a toy/POC project). I wanted to build a library that would
Postevent is the result. The library publishes and consumes strictly ordered events on Postgres, using Debezium for change data capture and notification.
I'd been looking for a library like this for a while, and reading about the approaches other libraries have taken. Nothing really met the criteria I was looking for (low cost, low setup, distributed messaging, strict ordering), but the process of looking at, and understanding, the alternatives meant I'd spent a great deal of time thinking about the low-level details. I'd also spent time with debezium to understand its capabilities, limitations and configuration. This time spent meant, when I came to the next phase, I had a good picture in my head of how the system should hold together, and the responsibility of the various components.
I worked with the team at sourcery.ai to get the project off the ground (they were working on a feature that created code from github issues at the time). After experimenting with aider.ai, we started using langchain to read the first few github issues and boostrap the project. The biggest hurdle will be familiar to any dev - inconsistent information on the internet meant we hit version differences in gradle setup or configuration style. Frustrating, but it was interesting watching the sourcery team use langchain to parse the build or test failures and send to various LLMs to resolve.
After I had a working project with tests, I brought in the Debezium wrapper I created in my thinking phase, and started vibe coding with Cursor. This was incredible fun - I was working on all the isolated component parts so I was happy to YOLO the suggestions straight into the codebase and fix issues later. The code quality varied but was rarely bad (just sometimes not quite what I was looking for and with inconsistencies like different logging libraries in play). Where cursor really excelled was in writing tests; not only do I not have to write the tests, but I didn't have to specify what should be tested, this was inferred from my original request. Cursor put all classes in the same package, so I refactored into separate packages to make it easier for me to understand - at this point with so much progress I was starting to get lost.
After restructuring the project I asked cursor to complete the last few components, implementing catchup and recovery, and remote communication. Remote communication uses gRPC, and this was the first area I had no experience of the low-level details. I extracted the interfaces I wanted to implement as remote procedures and let cursor do the rest: specification, library import and adding code generation to the build. Now the final components were in place I said goodbye to cursor and took over the reigns. I created high level tests that would show the entire system working together, and refactored everything:
Cursor had done a great job, but I wanted to try a new tool. I'd heard augment was targeted at complex codebases and while this was still a small project it might be time to see what it could do. The first thing I asked for was deterministic simulation testing. This is absolutely not a simple thing to implement and involves the interactions between multiple components. The request/prompt can be seen here and the result here. Of course, the implementation didn't work as required immediately, but it got me 80% of the way there, leaving me to perfect the implementation. I think this is a really valuable use of AI: I had free time and headspace after most of the work was done, and I could focus on the details of the implementation. If I'd done this myself I'd have had less time and energy at the end to really get it right. The other tasks I asked augment to do were:
The last part of the journey was to get the library ready for release. This involved load testing in AWS to find issues in deployment. I didn't use any AI tools for this process; it was mainly about deploying and debugging using terraform and the AWS console. I did, however, find a bug, and asked Augment to fix it. This was probably the occasion I was most impressed with Augment. The prompt I gave was
"The catchupservice works well when messages have been missed and fills in the gaps. However, the server has more messages that haven't been received by the client. When the next message arrives, the catchup service will know to fill in the gap, but at the moment it doesn't know they exist. I want a new system event type (FetchLatest) which will get the latest message if it is greater than the current hwm and inserts it in the messages table"
Augment forgot one part of the implementation and I had to prompt again, then fix an off-by-one error and I was done. Really something.
AI has a lot of hype attached to it; there are plenty of players claiming it completely replaces humans in many fields. My experience in this project tells me that this is as yet unlikely. It can follow instructions and create code, but without the thinking, refactoring and polishing it's not much use in creating software by itself. But what it did do is support me to build this library. I enjoy writing software, but I also enjoy spending time with my family, and that comes first, so if I can use AI to reduce the effort then the library gets written - I simply wouldn't have finished this project without it. I think this sort of human augmentation is increasingly important now we're in a post-zirp world; companies may be more focused on their value and less on contributing to open source.
AI was not used to write this blog post, but it was used to read my git log and summarise it, and create the image :)