Saavo, My First SaaS Framework: Finally Done, Still Not Quite Happy
My first SaaS framework is finally ready to ship. A few notes on building it, the choices I'm already second-guessing, and why I want to use it for a few projects before starting over.

I finally got my first SaaS framework into a state where I could release it. I picked a short name, Saavo, and then, by the time I'd released the framework, I discovered that someone else had built a website with the same name. Well, damn. That site wasn't there when I bought the domain. Still, I'm pretty happy to have finished it. At least the next time I start a project, I'll have a template I can actually use. Looking back through the code, though, I'm still not all that happy with it, and I've already started thinking about what I'd change in the next version. Apparently I haven't kicked the habit of wanting to rewrite something the moment I finish it...
1. Reinventing Another Wheel
There are things you can hardly avoid when building a SaaS product: authentication, users, payments, email, file storage, multiple languages, and so on. Before you've even started on the actual product, you have to get all of that in place. Setting it up from scratch every time gets old, so this time I put it into a template. When the next idea comes along, I can build on what I've already got.
Besides the template itself, there's now a CLI for creating projects and documentation to go with it. You can download the template from the command line, initialize your local environment, follow the docs to configure the services you need, and deploy it. I won't go through every feature here; that's what the documentation is for. Mostly, I want to write down what I'm thinking now that it's done, before I forget why I made these choices in the first place.
I did another cleanup before the release, and there were quite a few details left to deal with. The content collections, for example, are generated files, so they shouldn't be in the source archive. But once you remove them, initialization has to regenerate them, or the type checker immediately starts complaining. Then there were the images. They didn't seem like a big deal sitting in the project, but the archive turned out to be pretty large. So I went back, removed the unused ones, compressed the rest, and updated the references in both the site and the docs wherever I'd changed an image format.
The docs needed attention too. Change a command in the template, and you have to update the instructions, then check that you haven't missed any. Code, template, documentation: none of these is particularly complicated on its own, but put them together and it's easy to update one while leaving another out of date. Anyway, I went through it all again before the release and got those issues sorted out.
2. A Few Thoughts Now That It's Done
The structure is the first thing. The framework is still a bit of a mess overall. The layers are reasonably clear, but some parts aren't handled very well, especially database transactions, which feel awkward to work with. I haven't quite figured out whether those parts just need a better design or whether this way of organizing the layers isn't a good fit in the first place. Drawing clear boundaries between layers doesn't seem to solve everything. Knowing where the code belongs is one thing; finding it comfortable to write actual business logic is another.
I have a feeling a more modular approach might work better, especially for a framework used with AI coding tools. Add the modules for the features you want. Need payments? Bring in the payments module. Leave out the features you don't need, and try to keep changes to a feature within its own module. Sounds nice enough. Actually making it work is probably a different story.
These features aren't completely independent in a SaaS application. Payments need to know who the user is, and that user may have subscriptions and permissions. How do you organize one module's dependencies on another? Who provides the shared infrastructure? How do you handle configuration and database changes? Without those mechanisms in place, I could easily end up moving the same code into a few different directories and calling them modules, while every change still drags a whole bunch of other code along with it. That wouldn't get me very far. I think the direction is worth exploring, but it would ask a lot more of the framework's foundations than the current design does.
Then there's authentication. I really shouldn't have written that myself. It was a hassle and took time and energy I could have spent elsewhere. Looking back, I should have just used Better Auth. While I was building it, rolling my own seemed manageable enough. Now that it's done, I do wonder whether it was worth the effort, and I regret that choice a bit. I plan to replace it in the next version. I don't need to maintain everything myself.
The rendering engine is much the same. I wrote that myself too, and I'm not particularly happy with it. I should have gone with TanStarter. Unfortunately, as far as I knew when I started this SaaS template, TanStarter hadn't had a stable release yet, so I ended up building my own. Looking back, it feels like I took the long way around in a few places. But it's written now. I can't just tear the whole project down every time another option starts looking better.
I have a rough idea for the next version: possibly Void + TanStarter, together with the features already implemented in this SaaS template. I'd try to separate those features into modules as cleanly as I can, then put them together. How exactly they would fit together, what belongs in the foundations, and what can stand on its own—I haven't worked that out yet. I'm writing it down now before some other idea comes along and pushes this one out of my head.
This time, though, I'm going to try to resist jumping straight into the next version. When I was tinkering with my blog, I went from Ghost to WordPress and then to Next.js. Every switch seemed to have a perfectly good reason behind it. Several systems later, I hadn't written all that many more posts. If I finish this template and immediately start investigating another framework, I'll probably have another rewrite post to publish soon enough. As for projects actually built with it, there might still not be many...
So I'm going to keep using this template to start new projects, put its features to work, fix what comes up, and gradually improve the modules. Which features actually matter? Which needs are worth addressing? Which parts look neatly layered but are a pain to use? I need to build a few real projects to find out. Then, when I eventually migrate things over and build a new framework, I'll know what I'm trying to change. If I go on nothing more than the feeling I have now, I could easily end up with a different structure and the same problems.
I'll use this one first. The next version can wait until I've actually worked with it.