Photo by Kira auf der Heide on Unsplash

SaaStack

A gift for tech startups

Jezz Santos
20 min readDec 10, 2023

--

I’ve made a pretty gritty decision to commit myself to putting a bunch of the expertise I’ve acquired over the last decade in engineering SaaS products into creating a new asset aimed at helping other tech startup founders build their new SaaS platforms.

It is not a sample, it is not a no-code solution, it is not AI, but it will make you go an order of magnitude faster and more reliably.

It is called SaaStack, a “codebase template” of essential software architecture, components, and patterns that you should consider if you are starting a new SaaS business and want a jumpstart on your first day coding it up!

This is what you will get, and can deploy to Azure immediately.

Or, if you prefer AWS as your cloud, it will look like this:

It is squarely directed at start-up founders or founding developers who already understand that under-engineering their software products (at an early stage of the business) will eventually cost them dearly in the medium to long run. They prefer to start with architecture, testing, and a modular monolith that can be split later.

And what they don’t expect is to hire cheap and inexperienced coders (which they will) and then magically end up with this kind of work after a few months. It ain't gonna happen.

“But wait! This is so over-engineered!, we don’t need all this stuff! to get started right away!”

I know. I know. I hear some of you.

Many software developers today (and so-called “leaders” in tech) still foolishly believe in popular mottos like: “move fast and break things”, “build things that don’t scale” and “build, measure, learn”. They are great mantras. But these people interpret them to mean that they should deliberately “under-engineer” the software that they are building their new startup businesses on. They conveniently missed the point because missing the point excused them from doing what is hard in software development.

By under-engineer, I mean these kinds of things:

Don’t bother with any architecture (just start with a sample project template that you downloaded from Microsoft), make sure to use all the trending nuget packages (you ever heard of) so that you can avoid writing any code at all. Then simply bind all your code to your SQL database, use a popular ORM, and don’t bother writing any automated tests. You won’t need to grow your engineering team beyond your own personal capability, so don’t bother writing maintainable code for others to change. Definitely don’t put any usage measurements in your codebase, forget about fault tolerance or reliability, and basically ignore all the fallacies of building connected systems. Turn off HTTPS in local development, and don’t bother with CI or CD just yet - just deploy straight from your desktop! Keep your production secrets in your source code, and don’t worry about your monthly Azure/AWS bills.

As they say in New Zealand, she’ll be alright!

Well, it is kind of understandable to believe in the mottos above if you hadn’t bothered to take the time to understand the nuances of them (or even read the original articles yourself) — they sound so great, after all, and everyone’s heard them a million times, repeated over and over to semantic diffusion. We call that the availability bias (or availability heuristic — look it up).

The big problem is that those “experts” never realized that the creators of those mottos aren’t talking about how you go about “engineering the actual software” that you are deploying into the market. They are talking about what you should have done long before (and after) writing the actual software of the product.

Focus on what is proven to work (in the market) rather than what you think will [obviously] work in the market. Since it probably won’t, for reasons you’ve yet to understand about the market and its needs.

However, most “developer-oriented” professionals fail to care about this distinction and prefer only to be guided by working in their code.

Perhaps it is time for those people to go back and study what they missed the first time they heard those phrases above. There are compounding economics that these folks are conveniently wishing away and kicking down the road!

Photo by Nik Shuliahin 💛💙 on Unsplash

So, what’s the big problem?

After working with so many tech startups over the last ten years (including co-founding my own), it still saddens me to see (and still have to accept) that so many startups are consistently falling into the same technical debt traps that so easily kill them in the long run. It appears that they are being misguided or misled.

To try to understand this, let’s first just ignore all the tech startups that ever failed in their first couple of years because they probably had no business in the first place — for whatever reason.

Those who survived the first couple of years and stayed in business clearly could go some distance further, and many are getting to that point where they are actually making some traction/revenue from their market.

They must have either earned enough revenue (or raised enough capital) to pay people’s salaries. Then, they've likely spent more money on scaling up their organizations — in most cases, by hiring many more designers and many more engineers. Right? Because adding more people means -> more things get done! Right? (Not really, no!).

Now, after a few years down the track of those people making more stuff and adding a ton of features to the pile, many of these product teams start struggling to move in any direction anymore and will be struggling to adapt the product fast enough to their rapidly evolving market, especially at that critical time when things need to scale up.

It's the old tech debt — grinding to halt problem (that is so familiar). Make a small change here, and break three other things over there! Things start breaking, and we have to do a lot of work catching up to fix them, and, in the process, upset customers. Fear starts to set in.

Why is that?

Well, there are many factors, of course, in startups; it is very complicated and nuanced, but one of the most common problems I see when it comes to engineering in almost all of the startups I’ve examined over the last decade is this:

Some companies hired too many people in product and engineering too soon because they wanted to just go faster, and they now have a bottleneck problem.

Let’s forgive them for a minute for using the wrong tool for the job of going faster.

They are now struggling to squeeze too much work down the “assembly line” (that’s the way they’ve chosen to see it) manned by competing “delivery teams” working in sticky mud with complex competing dependencies between them.

Not “product teams” anymore, but groups of individual contributors and heroes who spend most of their time just patching things up and doing so-called necessary “performance work” to make up for the mess.

Those individuals and groups of individuals (anti-teams) do no discovery work of their own, they only focus on one or more parts of the whole picture at a time and thus can’t collaborate on any meaningful outcome. Oh, and don’t get me started on the additional production support work that all this keeps spawning, that keeps them busy patching leaky pipes that never stop springing new leaks.

They have been taught by the process they are in (and that organization) to take almost no responsibility for what is actually delivered, as a whole, nor to cater to the end user’s experience of it all. Since no one bothers to ask them anymore what problems they think end users are having. They’ve stopped looking. See no evil, hear no evil, speak no evil.

Someone else, smarter than them (in the organization), decided to take that responsibility away from them, leaving them the freedom to just focus on making it “work”— whatever that means.

All that there is left to enjoy is the hollow pursuit of technical problem-solving someone else’s wishes, using the best of the latest and greatest technologies that they are permitted to use.

Since care for the health of the overall system (a.k.a “the product”) has been taken away from them, they’ve slowly lost sight of the whole point of the venture, and having to much care for the outcomes they are inadvertently creating in a market they don’t see or feel.

All they do now is pile on more complexity onto the heap of complexity they already have, and no one else is near enough to it, to see the mess it has become.

Probably the worst part about creating complex software systems is that the accidental complexity, that is introduced, is neither anticipated nor can it be seen festering from the outside by those now managing these people and the process. It is the perfect storm rising.

Managing that complexity is now someone else's problem since these people have been told to “just keep going “— doing more and more new stuff. As if it is that simple.

They are now, in reality, predictably, going far too slow for the likes of someone higher up in the organization with ample ambition, over-optimistic detailed plans, strategies, and roadmaps who think that they can second guess the actual market, wish for a future that can just materialize if you just whip the developers harder.

Those more enlightened superior beings are now eternally frustrated with how long it takes to get anything done anymore.

They may even pine after the earlier days “when it was so much easier with fewer people!”

You may recognize this picture as your classic “feature-factory”.

The Feature Factory
https://medium.com/one-to-n/beyond-the-feature-factory-c637c2ad50ca

For many companies (not just startups), one solid reason they can’t move and adapt to their market anymore is that every change (to the code) the developers make randomly breaks three other things in the code somewhere else, unbeknownst to them.

No one is seeing that, and no one cares that this is getting worse. Not Sales, not Marketing, not Finance, and certainly not the CEO.

After every death march to some arbitrary deadline, with a bunch of half-baked untested features in it, followed by a very scary release, the support calls start flooding in the very next day, and the cycle repeats.

The most common solution to which is hire more support people and developers to keep up with it.

Perform this circus act a few times over, and now, those same designers/developers start fearing making any changes that could break more customers who are already using their product.

Driven by a deep hate of getting yelled at by the executives for causing problems that threaten “the business” they no longer want to look for evidence of problems actually happening for fear of retribution from the top. The truth about the mess they are inadvertently making is often too shameful to accept for many conscientious designers/developers. But they persist through he mud, wishing it gets rewritten one day.

They didn’t get into this work to piss other people off or cause conflicts between people. In fact, they don’t want to be dealing with people at all, in a lot of cases! They are in it because they like dealing with thing-problems and solutions, not dealing with people-problems and solutions!

They, of course, also despise doing any “production support” work to fix those three random things that broke since no one gives them the time to do that properly nor the time to look into why it happened in the first place and deal with the root cause of the problem. So they just patch it up and move on.

It is no wonder that they start over-engineering their planning, their reviews and their software implementation criteria in an attempt to try and mitigate the chance of creating negative and random side effects later. But this, unfortunately, is exactly the wrong tool to use for this and unintentionally makes the software even more and more brittle over time.

That then causes more pressure to start mounting, and they will exclaim that they can’t do both production support and deliver new features at the same time, at the same rate, and that’s why it takes ages to get anything done.

Management /executives feel like they have lost control and feel held hostage by the tech people.

It’s all just a classic stalemate situation between management and engineering, and while the finger-pointing is going on, no one is looking at the root causes — the classic “big-ball-of-mud” problem. That kills most software ventures.

Photo by Delano Ramdas on Unsplash

Causes?

These are the real-world reasons why I call bullshit on those that think that your job (as a product engineer in a startup) is to write as much software as you can as quickly as you can, with no architecture nor engineering towards the future, and then trust that it all goes well from there.

That is not what Eric Reis intended you to do when he coined “build, measure, learn”, nor what Paul Graham is saying in “build things that don’t scale”, nor even what Zuckerberg means by “move fast and break things”. Go see for yourself!

They are all talking about you doing the hard work of focusing on finding out what actually works in the market first (cheap experimentation), long before engineering anything into a product that you expect to work durably for lots of customers for the next 10-25 years. Focus on finding what works well for your customers and just build that first.

But the sad truth is that writing code (on your own) to solve problems you think you understand clearly is far easier to do for a developer than spending time strategizing.

Not only does that cavalier, undisciplined attitude to “just write any code as fast as you can” miss the whole point of startups (i.e., to find a new sustainable business model), but who decided that the software they are actually writing is what the market needed/wanted in the first place?

I don’t think anyone did. It was all based on guesses.

Then, months to years from now, who is going to have to move forward with their “cowboy creations” once the money starts rolling in? Them maybe?

What if they can’t afford to exit the company yet? How long do you think it will require them to support the under-engineered mess they created years behind themselves? At what opportunity cost to the growing business?

Don’t you think that re-engineering a core product that hundreds/thousands of customers are using right now is the last thing the business is going to want to spend its capital on, just as the money starts coming in the door to scale up the business (after several lean years down the track)?

I mean, if you are starting your new startup and you don’t have millions of $$$ of funding in your pocket to get going, then years of developing and maintaining the software you created is suddenly your gritty reality for the foreseeable future.

If moving forward in the quagmire of technical debt is expensive to do (in time and money), then you can’t move forward very fast at all, and things very quickly come to a grinding halt, frustrating your business’s scaling and expansion plans, and stymying your growth.

BTW: If you do happen to have that unfair advantage of millions of $$$ funding from day one, or you got lucky, struck gold, and became a $10M ARR company in your first year, then stop reading. This approach is not for you. You can literally afford to rewrite your software every couple of years with armies of unskilled developers forever more. You’re the rare lucky ones, not the vast majority, who are having to do it far harder than you. Just don’t go advising other startups based on your lucky experience.

Photo by Kira auf der Heide on Unsplash

How is SaaStack going to help?

It is a “codebase template” for building SaaS tech products - starting on day one. A GitHub repository.

An open-source codebase that you copy and use yourself to get your tech business off the ground. It has all the bits you need to get your Saas business started (some you didn't think you might have needed), and it allows you to pick and choose (plug and play) the infrastructure components you feel most comfortable with and allows you to select the cloud provider that you want to deploy to.

It is intentionally not intended to be the final product that you will end up building over the next few years. It is intended to be a good starting point.

In fact, it fully expects that you will adapt, evolve, and change it to suit your particular context. The point is that you don’t have to start your context from scratch or from crappy general-purpose samples.

It is designed to save months, if not years, of time and money in getting your product idea to the market ASAP because you don’t have to spend time building all the things it gives you on day one.

All you need to do is deploy it as is, then reskin it, and you have the beginnings of the product already on the internet! that you can then refine and launch your next experiments on.

It has been carefully architected and designed to make your evolving product durable, supportable, and maintainable from day one so that your team will continue to move fast with it over the next few years! And it is designed to be scaled later and in the future when your startup gets to the point where that is necessary for you.

Something that obviously many startups fail to do soon enough, at enormous expense to their businesses, getting time and resources to rewrite and reengineer their codebases.

No longer do you have to build as fast as you can, in an undisciplined way, and then rewrite it all over again so it can scale, just as the business is trying to get off the ground.

This is especially significant for startups that can’t raise enough capital early enough to hire teams of experienced engineers to re-write all their core software for the business.

Photo by Philipp Torres on Unsplash

Why should you care about SaaStack?

Like anything, SaaStack is not a silver bullet. It is simply a better starting point than starting from scratch for a new tech startup.

If your next project is not a startup, then it's probably not for you. (Although you may choose to learn from its patterns nonetheless).

Now, depending on who you have writing the code for your new startup, they may or may not be interested in adopting SaaStack.

(But, ask yourself why they shouldn’t be).

Likely, if you were to start the business with a junior or intermediate developer as your first startup CTO or founding developer, then they will likely start writing code for your startup using only the tools and practices that they already learned to date from previous jobs.

They are already on a steep learning curve of their own, trying to master the job, and they don’t have a lot of experience to draw upon. It is more than likely they will start from scratch, taking a lot of dependencies on 3rd party libraries and frameworks, with little thought about future supportability and maintainability of the product for the business.

That’s because at their level of experience, it’s all that they know how to do, and some of the more advanced things in SaaStack they will think they won't need any time soon. Let’s just chalk that up to inexperience, that they need to build.

If, however, at the other end of the spectrum, you hire a more experienced developer as your CTO or founding engineer, especially one who has experience starting new projects/products from scratch and staying with them for years, AND they’ve done that before over and over again. Then, they are more likely to see the value in starting with something like SaaStack as a better starting point than what they could build in the first few months themselves. And they will feel more comfortable adapting it to their specific needs.

Of course, we all know that there will always be those over-confident developers (somewhere in the middle of the experience spectrum) who will have convinced themselves that they already know it all (from their limited experience) and that their design opinions are the only viable ones in the world, moving forward. Think Dunning-Kruger. They will start building their own “baby” their own way themselves anyway, using only what they know and rediscovering and reinventing fundamental things that have gone long before. They only trust themselves and are skeptical of everyone else. Nothing anyone can do about that. We’ve all been there and done that, and then we all eventually grew out of it.

So, if these CTOs and founding engineers could put their egos aside and take an economic point of view, why should they consider using SaaStack?

Architectural styles

Well, firstly, SaaStack is based upon proven architectural styles and implementation patterns for building long-lived, durable SaaS products that have been learned in the wild across making many SaaS products. And that’s all it has. It is just about as lean as it gets — nothing much more and nothing much less to start with.

Just having some solid durable patterns gets you halfway there!

The optimizations that have been made in SaaStack are geared towards high maintainability in small team environments and decoupling for later distribution rather than worrying about optimizing the performance of individual pieces. That kind of optimization is being deliberately deferred until later once the product is proven in the market and needs to scale up.

Some of these architectural styles (e.g., Modular-Monolith, CQRS, DDD, Event Sourcing, EDA, etc.) you may be familiar with already, and some of them may be new to you. You get to choose which ones you want to use moving forward on your product, and you even get to try some you never got a chance to try before that you want to master yourself.

The beauty of giving you a “codebase template” is that we can demonstrate within it actual working examples of most of the options you could choose from. So that you can see how they actually work and you can learn directly from them, right there in situ in the codebase.

Everything is in the open, and nothing is hidden in someone else libraries and frameworks. It’s all your own code.

Then, it is up to you which patterns and conventions you want to continue to use for your context, and you can refine or define your own.

Architectural decisions and design principles

The patterns contained within the architectural styles within SaaStack should look very familiar to anyone who has done this kind of work before on this specific platform (.NET) and very familiar to anyone who is up to speed on building distributable systems that focus on managing complexity well from the beginning.

SaaStack is based on some pretty tried and tested design principles and practices. Each and every design decision (of significance) is documented in an ADR decision log and then explained in detail in design documentation so that you can see why we’ve chosen what we have and help you understand the thinking behind the decisions.

From that detail, you may learn something new about that aspect of architecture, and you have an understanding of where and why those decisions have been made.

All design decisions are rooted in a set of base assumptions about what audience this codebase is designed and optimized for. And it is not for everyone and every project.

Then, we have another set of detailed documentation containing examples backed by a set of custom developer tooling (i.e., Roslyn Generators, Analyzers and a bunch of automated tests) that guide and enforce how you write components in the codebase. They guide you in the code on what to do and what not to do, to keep things clean and keep that accidental complexity at arm’s length.

This documentation and the tooling that goes with it makes learning the abstractions and the framework faster and how to get productive with it immediately.

BTW: The “enforcement” tooling is there to ensure that you and your team don’t inadvertently violate some of the most fundamental principles of coupling that will impede your future ability to split the modular monolith up into separate deployable units (microservices) at a later date, whichis very hard to do effectively with just documented policy and just code reviews.

Customization

Thirdly, SaaStack will be (when we get to the end) a codebase template of about ~75,000 lines of code, that otherwise someone at some point in every SaaS startup will have to write at some point in time (in some form).

Why bother reinventing it all over again? Wouldn’t you rather just get on with the important stuff about your particular tech idea and take advantage of all this basic plumbing?

Certainly, there will be disagreements on some of the implementation details in SaaStack, there always are (eyes rolling). So change it to be your way. It’s your code, and “it is not concrete”.

We know that every developer on the planet has their own personal opinions on how they would do things and have their own tolerances when it comes to their own preferences about how to solve a particular problem. We know that every one of them can get super passionate about what they think is right or wrong. But we also know that if you want to collaborate in a team environment productively, then compromises have to be made. It is less about right and wrong and more about being consistent and relevant for your context.

The beauty of SaaStack not being someone else's framework (downloaded from nuget.org) is that you can exercise those personal preferences! If you see something you don’t like, then take advantage of the fact that you can refactor, rename, and restructure any aspect of SaaStack because you have and own all the code in it!

You don’t need anyone’s permission to do that, and you can set your own conventions and preferences. (just remember to change the documentation to reflect that as well for the next person coming into your team)

Photo by Maksym Kaharlytskyi on Unsplash

Avoiding the big-ball-of-mud

Lastly, if you’ve ever built medium to large-sized SaaS products before, you already know that managing complexity in the future is going to be a big deal in every case. Under-engineering is the wrong tool for this job.

To do that safely (without breaking stuff) and to avoid the big-ball-of-mud disasters that we described at the top of the article, you are going to need a comprehensive and powerful regression capability built right from day one.

And that's exactly what SaaStack has for you. Overlapping layers of automated tests to verify the functionality of all the code within it.

All you have to do from here is keep writing the same kinds of tests for any new features and capabilities that you add on top of all that, and you have a pretty bombproof codebase to move forward with for years on end. That you or anyone on your team can change safely, no matter how experienced they are at writing code.

Can you imagine living in a world where you may, at worst, have to fix a production bug maybe once every few months?

Of course, your mileage may vary depending on how disciplined you and your team are at keeping the bar high.

Photo by Mark König on Unsplash

Status of SaaStack

SaaStack is currently (December 2023) being built out and it is not ready to be built upon just yet. We still have a few months to go before we recommend anyone actually use it in a startup.

We have a clear target to aim for because we’ve done this a few times before already, and you can see that target depicted in the diagrams above.

We are spending a great deal of time and effort ensuring that we capture and document the design decisions being made so that this documentation is ready when the code is ready to go.

You are welcome to join in, and let us know your thoughts, show us some support, or just watch from afar.

Thanks for your support.

--

--

Jezz Santos

Growing people, building high-performance teams, and discovering tech products. Skydiving in the “big blue” office, long pitches on granite, and wood shavings.