Vibing with the AI

It’s probably no surprise to anyone that Tomás the Purrfect Budgeting App is being coded with a good amount of AI, in general I could say is just the way it is now for every development.

In this post I would like to talk about our approach to it, the problems we had, how we solved them, what it allowed us to make, an in general how good the vibe really is.

1.- Ethics of the vibing

It is probably strange to see a programmer that is extensively using AI to code to talk about ethics, but I do believe it’s important.

AI is basically trained in all the corpus of Open Source out there, I say Open Source because I really don’t think AI companies would be able to take a look at the latest code from Adobe, and definitely they wouldn’t be able use it for training.

There is of course this thing where AI advocates say that it is no different that a person learning from such openly available resources, but when LLMs can regurgitate line by line parts of such code, its clear at least for me that yeah, some way or another we are creating our apps out of open source software.

The magic thing here is that, in reality most if not all of OpenSource licenses actually want you to make money out of it, they only differ in the amount of recognition given to the original creator and if you should publish your work back as open source.

I cannot do much about the recognition part as the specific influences are hidden deep inside the neural network, but at least, unattached from financial and legal limitations with investors or employers, I can publish Tomás as Open Source.

Space Troopers (1997)

On the business side, it’s simple: offer SaaS hosting for those who don’t want to self-deploy, plus enterprise support and premium AI-powered services like receipt processing that require server infrastructure.

2.- AI is an over enthusiastic Junior… with ADHD

At the beginning it only had chat capabilities and the VSCode developers patched a way to apply the suggestions from the chat window into code, it was functional but slow.

Now we have Agent mode and it can find its own references, run commands, move files from one place to another, and in general do everything you can do, except maybe pushing to prod…

I have found that AI really is like a regular ADHD dev that makes mistakes, leans into assumptions, keeps repeating old patterns because that’s how it learned, gets easily sidetracked by refactors, and doesn’t know where to cut a PR so it just keeps adding to it.

And the solution to most problems caused by an ADHD dev is just good development habits, the same ones that you should already have but that you forgot to implement because suddenly the AI goes too fast and felt that if you can “skip” coding, you could also skip on everything else.

3.- Good Development Habits

So lets tackle each one of the issues caused by lack of development sanity and the corresponding good practice.

3.1- Commit every time a small goal is achieved

I personally used to commit a lot and don’t care about having a clean log for development branches. Because you can always squash merge on the PR and leave a clean history in main/develop, no code is made without breaking a few builds.

But now with copilot I’m finding myself committing every successful prompt. And that has saved me a lot of times.

AI has a tendency to go in destructive refactors when it can’t make a test pass or it encounters an error. It suddenly decides the solution is to go into full refactor mode, or to totally delete a file and start from scratch.

It’s in these moments when you will want to revert all changes and start again from zero. But you can only do that if you commit regularly.

3.2.- Work incrementally in small iterations

Don’t expect Copilot to solve a full system in one prompt. While it could definitely do it with the proper prompt, you won’t know until it finishes if it was a success, and then you will be left with a lot of code to read, test and carefully sanction… because you do read what the AI creates… right? RIGHT!

The problem is, the more you have to review in one sitting, the more chances there are that you overlook something.

Treat vibing like you would treat actual coding: don’t ask the AI to modify the API, the unit test, the frontend and the UX test all at once. Tackle each individual part of the problem in its own step*, validate and move to the next one. This will allow you to think about every piece.

3.3.- Build persistent context

AI models have a limited context window. This means they often revert to generic solutions instead of following the instruction you gave 20 messages before, simply because it forgets about it… funny enough we humans also have bad memory and forget about previous decisions.

For both AI’s and your own sanity, get in the habit of creating reusable, persistent context in the form comments and documentation files. The AI will work better when it has more permanent context to work with and you and your coworkers will have something to refer to next time someone forgets exactly what shade of white was chosen for the headers.

We will forget anyway TBH

3.3.1.- Build permanent context with copilot-instructions.md

Create a .github/copilot-instruction.md file, and start documenting how you want things done. Each time copilot gets something wrong from your prompt, take your time to include it in the copilot instructions.

For example we have:

  • How the roles work - The AI kept giving superadmins permissions to do everything when we wanted the superadmins to not have access inside the workspaces.
  • Specifics on what versions of software we are using and how it works, so it doesn’t implement old patterns from deprecated libraries.
  • Details on how we like to split responsibilities inside the code.
  • We had developed a few common components for common things, like an input designed to handle currency and basic operations, that input should be used whenever we are handling currency.
  • To put all styles in a main styles.css file instead of adding scoped styles in each component.

Without these instructions copilot kept forgetting to implement things OUR way and reverted back to either the most common way or the easiest/laziest way.

Is our way the best? Maybe not, but at least it’s consistent, and that is more important.

3.3.2.- Add comments and documentation to files

Get in the habit of adding comments and documentation to your files (or ask the AI to do it for you). The more documented your code is, the better the AI will understand what it does and how to work with it.

And once you are there, get in the habit of reading the comments on the code generated by AI so you also know whats is happening behind the prompt.

3.4.- Split long files into smaller ones

Avengers: Age of Ultron(2015)

AI will recreate similar components or functions in multiple places rather than creating reusable, modular code, just because it’s easier or doesn’t remember we already had a component made for that.

This has the unwanted effect of making AI slower as it has to read long files to know what they do and to apply changes. The smaller the function/component the AI is working on, the faster and better it will work.

We are all guilty of this sin of course, I vividly remember my days of copy&pasting as it was yesterday and it probably was TBH.

So every step of the way be alert of files growing too big and find patterns to split them into smaller ones

Examples of this are:

  • Utility functions starting to appear? - move them to its own library.

  • A Section of your UI grows too big? - move it to its own component.

  • Your components folder is getting cluttered? - look for patterns and create subfolder by type (inputs, navbars, layouts, etc…).

  • Too many routes? split them by domain/category.

The benefits of this are:

  • AI will take less time processing.

  • A Clear set of input/outputs simplify troubleshooting.

  • Smaller elements are easier to write unit testing for.

  • You and the AI can forget about what is behind that smaller files and treat them just as an abstraction, freeing context for the current problem.

  • Increased reusability of the individual code pieces.

3.4.1 AHA! Avoid hasty abstractions

About this I’d like to add a concept that really hit home when I read about it a few years ago: AHA programming. I’m pretty sure you’ve heard about DRY (Don’t Repeat Yourself), which this whole section basically advocates.

But there’s another programming catchphrase that says “Premature optimization is the root of all evil”. Don’t start making thousands of subfolders or get ahead of yourself creating different utility libraries for each domain. Wait until the patterns emerge, or risk cluttering your code even more.

In that AHA article, the author introduces the concept of WET (Write Everything Twice), and goes with the following rule:

You can ask yourself “Haven’t I written this before?” two times, but never three.

In general, what you want is to allow yourself some flexibility - observe before you act.

“You must see with eyes unclouded by hate. See the good in that which is evil, and the evil in that which is good. Pledge yourself to neither side, but vow instead to preserve the balance that exists between the two.”
Princess Mononoke (1997)

3.5.- Teach the AI modern patterns


AI suggests outdated patterns because:

  • It’s trained on older code that’s more prevalent in open source repositories
  • Their information cutoff is always in the past

Of course, we are also subject to such outdated knowledge. The cynics in the room will say that “All code becomes legacy the moment it is released to production” (that’s me, I’m the cynic in the room). But the optimist will say that yes, but you can be one month legacy or 5 years legacy (that’s my partner who is also coding this project).

So it’s always good practice to use the latest fully supported version of your dependencies. In our case we had these 3 problems:

Vue Options API vs Composition API
Copilot created all the first components using the Options API instead of the newer Composition API. I didn’t know the difference at the beginning because the last time I used Vue was with Vue 2. Once I realized this, I had to research the differences, correct the AI, and migrate everything to the Composition API.

Storybook 9 struggles
Copilot really struggles with Storybook 9. I don’t know if the AI’s knowledge cutoff is before Storybook 9’s release date, but man did I have to fight that battle uphill. Once I got everything working and left a couple of fully functioning Stories with unit testing, we instructed the AI to follow those examples and it worked just fine.

Bootstrap dark mode confusion
Bootstrap 5.3 introduces simple dark mode where all you have to do is set up the data-bs-theme attribute on main or body elements and use bg-body, bg-secondary and bg-tertiary for backgrounds, which automatically adapt to the theme.

Well, Copilot kept insisting on a complete rewrite of all the CSS variables, effectively duplicating what Bootstrap already does, and kept using bg-light and bg-dark for all backgrounds.

The trade-off

You have a decision to make: if you go back a few versions, you’ll benefit from much larger training data, but you’ll miss the benefits of newer versions of your favorite tools.

I have no doubt that if I had chosen Storybook 8, Copilot would have made everything perfectly for me, but then again, Storybook 9 is better.
The Office, S03 E07(2006)

Our approach: Create working examples, then teach

  1. Research and implement the modern pattern manually (or with lots of struggle)
  2. Create a few solid working examples
  3. Document the approach in your copilot-instructions.md
  4. Reference these examples when asking AI to create similar functionality

Once AI has a working pattern to follow, it’s much better at replicating modern approaches than trying to figure them out from scratch.

3.6 Build it and they will run them (the unit test).

Field Of Dreams (1989)
There’s no better time to introduce Test Driven Development than right now when working with AI.

In TDD you create your functions and components along with a set of tests that define the expectations for the code, then with that tests already in place you can make your continuous integration run the tests and deny any merge to main/develop that doesn’t pass them.

This is crucial because AI has a tendency to change implementations silently. If you’re not careful, you’ll lose functionality that was already working, or the AI will change the implementation to something you don’t want.

But with TDD, the tests will break and the AI will work to fix them (yeah, in Agent mode it even runs the tests by itself).

This used to be heavy work, but with AI is suddenly achievable for small teams or solo developers, which brings me to the next topic.

4.- Where vibing got us

The current team of Tomás is me (levhita, fullstack web developer) and my partner (Sol/Meme, Frontend). In our time in the industry we have been in big companies that do the whole stack of good practices, and we are used to doing all of that, but of course with enough time, fully paid, and not usually wearing all the hats at the same time.

As much as I like to do everything from devops to frontend, it’s just not realistic to do that in a job. It’s nice to work with someone who understands the whole process, but my employers and teammates don’t expect me to do everything.

But with personal projects, you’re expected to do it all, and in the process you usually don’t do things that are boring or don’t bring direct benefit to the project, simply because you don’t have time (did I mention we have a toddler?).

But with AI, we can suddenly implement all the good practices and actually run a project with all the care it deserves. Things that we were able to implement because we have AI are:

  • Fully separated API and Frontend (we can horizontally scale the servers in no time).
  • Complete Continuous Integration process with actually working unit tests and deploy to our demo server.
  • A proper blog, also with continuous integration.
  • Proper Login, and separated areas for workspaces, home and admin section.
  • Carefully crafted core experience, with equally crafted surrounding experience.
  • Visually appealing design, at least better than most of my previous systems.

We even had time to create a nice looking logo with variants. This was not thanks to the AI, but it was certainly good to have peace of mind about the core development going well.

Everything everywhere all at once (2022)

5.- Conclusion

This project wouldn’t be possible at this scale without AI and think that is just fine, I do foresee that we might be entering in a golden age of Open Source development, and that every single dev out there with an idea is about to embark into this addictive code frenzy to see it become reality.

But in this ocean of new code that is about to hit shore, we should be careful in what we return back to the waters of Github, remember that is going to be used to train the new models, don’t commit shit.

Not because of the damage we could make to our corporate overlords precious models, but because I think we should embrace the opportunity to extended our own capabilities and create better more comprehensive systems.

~ Levhita

PS: Corporations will use AI to create all short of horrible things, and they wont stop just because you might disagree with the model, better take advantage of the same tools to do some good.

I specially like this piece by Cory Doctorow about being careful when taking sides in the AI internet fights: Neither the devil you know nor the devil you don’t . It’s a long read, but is worth it.

Hello World

Hi and welcome to the development blog of Tomás, a recession-inspired finance software for home expense management and forecasting.

I’d like to use this space to share insights into the development process, the whys and how, and in general to share the journey.

Complex economic times

flowchart LR
        A("Get into economic problems")
        A --> B("...")
        B --> C("Success!")

At the end of 2022, my first child was born. We were extremely prepared for him, but what we weren’t ready for was my father losing almost all of his liver function. This put the entire family into a position of having to fix years of financial neglect, and forced our nuclear family to acquire debt.

As you can imagine, the economic turmoil that followed wasn’t kind to my partner and me. She eventually lost her job, I lost mine, I got another one, she didn’t, and after a few months in this situation, the enormous debt from my father’s issues put us in a position where tracking expenses became crucial—not just for the present, but to forecast our recovery and anticipate future expenses.

Temporary solutions

Me every end of month

Of course, the first instinct was to get an expense tracking app, but those are only good for… well, expense tracking. I’d already tried dozens of them throughout my life, so that wasn’t going to cut it.

Instead, we started making detailed spreadsheets. They were good for one-time analysis but always left unaccounted details that would surprise us months later. Still, I pushed through and created pretty advanced ones capable of predicting months ahead, with rolling balances, recurring expenses, and even projections showing how interest-free purchases would affect our monthly credit balance.

The problem was expense tracking itself—it wasn’t user-friendly. You had to input transactions in a separate sheet, and in general, it went unused except for occasional static analyses.

Enter the Full Calendar

Around this time, I starting playing with the idea that the ideal method for capturing, organizing, and forecasting would be a calendar. You add transactions on the day they will happen, and if you know you need to pay your loan every 1st and 15th of the month, you can get ahead of it and add all future payments along with your food budget and fixed income.

Just like adding events to a calendar, but instead of events, they’re transactions.

Recurring Event as seen in AddEvent.com

I fired up VS Code, got a Copilot subscription, and started vibe coding to see if it actually felt right.

¡It did! I got an implementation of Full Calendar connected to the Web SQL API (basically SQLite) working in a couple of hours. A working form to add transactions came the next hour, and the simplest queries were already giving me the summarized information I expected just minutes later.

I moved all the information that took hours to capture and analyze from my spreadsheet into the system in 15 minutes… And there it was in all its glory: A 12-month forecast.

Yes, the calendar is the perfect entry method for forecasting, transaction entry, and visualizing your finances long term.

Copilot (with Claude behind it) is really good at making UIs, especially if you choose something common like Bootstrap. No shame here. I’m not going for style points; I just wanted to regain control of my finances.

Now What?

The fishes in mind being like, Now What?

That was the question in the back of my mind as I effortlessly added small features whenever something felt wrong or repetitive while managing my finances. This looks cool and useful—surely more people should be able to use it… but how?

My first approach was modest: make it an app you can run on your local server so all relevant family members can help. So I moved it from a browser database to my personal computer, converted all the functions to API calls instead of front-end functions, and called it a day.

But I forgot to keep my computer on, so my partner couldn’t log in when she wanted to help, and I constantly forgot to add expenses.

That was a failure. Let’s move it to the cloud then. I have a server to host my projects and I’m pretty good at writing CI pipelines… but now there’s a problem: I’m not going to leave my finances on a server that anyone could access. At this point, I was so focused on adding new features that I’d forgotten to include a user system.

It was around this time, while chatting with an old friend, that I told him about the project. Not knowing that I only wanted to fix my finances, he congratulated me on creating SaaS software that could really bring money to the table.

Honestly, I’d never thought about that, but it actually made sense. I should turn this into a SaaS.

The big refactor

Moving from personal software to SaaS means tons of changes. I entered a huge refactor phase that took 3 months. I’ll talk about that in the next update—hopefully by then we’ll be ready to start the closed beta and you’ll be able to get a taste of the software.


PS: I didn’t realize we were making recession software until 4 months into development, when Reels and TikToks started talking about recession signs like women going back to their natural hair color or pizza being financed in monthly payments.

But yeah, this is totally recession software.

PPS: My partner, a frontend extraordinaire, still hasn’t found a job. If you know of something, please let us know.

Love,
Levhita