Deconstructing the technical stack of a modern SaaS product
There is a lot of advice for early stage SaaS startups (or any other business building a new SaaS product) on finding the right customers and market to serve. Lack of product-market fit is considered by most the main reason new products fail. However, for those who survive or live longer, there is little guidance on how to choose and build a scalable technical stack that supports their product and customers in the long run.
Most early stage companies rely heavily on the Minimum Viable Product (MVP) practice, allowing them to iterate quicker, while figuring out the customers needs. In many cases, the MVP is a bunch of scripts and UI components glued together rapidly to try out features and gather customer feedback. Once product-market fit is achieved eventually, they start investing into building a scalable and secure product. The problem with this approach is that it creates a significant gap between the state of the product built as an MVP and the requirements of a great future product. If the business is heavily funded, this gap is relatively easy to fill, although it would take time. However, if it relies on its own revenue with little outside money, this task becomes much harder.
An alternative approach is to be thoughtful about your product architecture right from the start, even before you have the first set of features implemented. You don’t need to spend weeks on it, but keep the tech stack in mind from the beginning and grow it gradually as your product and business grow. Many technical aspects of a SaaS offering are constant and re-usable across different products and business ideas. Get them right at the beginning, and you will worry about them much less later on.
No matter what your approach is, sooner or later you will need to give serious consideration to your product tech stack in order to support future growth. In this article I will describe the main technical components of a SaaS product and best practices around each one of them. This is a light technical guide on what comes into building a SaaS product.
At the core of the tech stack of any modern SaaS product sits a reliable, secure and scalable database. This component is crucial for the future of your product and any mistakes made while selecting and designing your database will bear a significant cost to fix later on.
There are several decisions you need to make when considering a database for your new SaaS product. I am going to walk you through each one of them below.
Self-Managed vs Fully Managed
The self-managed option gives you total control, allowing you to have a database that matches your exact needs, while avoiding cloud vendor lock-in. You will deploy, configure and maintain your database from the very start on cloud servers. This option is usually cheaper, especially at scale. This comes with a price though: you will need the time, resources and skills to do that.
If these are scarce, you might consider a fully-managed database, usually offered as a service by most cloud vendors. It is much easier to set up, deploy and scale, as the vendor will take care of this for you behind the scenes. However, they usually bear a higher cost and limited customization and adjustment to one's needs.
SQL vs NoSQL
The SQL vs NoSQL debate it’s been going on since the dawn of the NoSQL databases over 10 years ago. The truth is, there is no one answer. It depends on your product, the kind of data being handled and how the product interacts with the data.
SQL databases have data normalization and ACID properties at the core. They use resources more efficiently and data integrity is supported by design. Also, as their name suggests, they use a standard query language, making it easier to access the data. Their main disadvantage is the rigid data models and the difficulty to scale horizontally, although some progress has been done on the scaling part lately.
NoSQL databases, on the other end, have native support for flexible data models and can easily scale horizontally, although, in order to do that they need to give up (some of) the ACID restrictions and the standard query language.
Choose a SQL database if your product’s data is well structured, predictable and relational. If the data is highly unstructured (documents, free text, JSON objects) and you need support for a dynamic schema, then NoSQL is a better fit for your needs.
Open source vs. proprietary
Open source databases have been gaining significant traction over the last 10 years. In the meantime the popularity of commercial databases decreased. While the latter is still being used widely in large enterprise settings (i.e. banks, government agencies, insurance companies etc.), most new SaaS products are heavily powered by open-source software. The reasons for this choice include cost, availability, independence and a sense of community.
Assuming you have more than one customer using the product, you will need to design it to support multiple customers or tenants. Each tenant usually includes one or more users interacting with the product from within the same organization. Although you can hack your way around it by deploying and running your software on unique resources for each customer (i.e. one set of VMs per customer), it is highly recommended to build multi-tenancy in your product from the very beginning.
In a multi-tenant SaaS architecture, a single instance of the app is shared between many customers. Benefits include lower running costs, better utilization of resources, easier to maintain and upgrade. There are few patterns one can use to design a database configuration and data model for a multi-tenant architecture.
One database per tenant
In this approach, each tenant has his own database allocated, although they share the same app instance. The database selection is performed dynamically after user authentication. It offers good tenant isolation, can be customized and optimized for each tenant, and, assuming the database server and compute resources are shared between tenants, it comes with a much lower cost than the single-tenant approach. However, because each database instance comes with its own compute and storage overload, it can become ineffective for a large number of tenants.
Use this option if your customers are on the higher end, paying a higher price and having more stringent security and data isolation requirements (i.e. mid-market/enterprise). It can work well with 1000s or even 10,000s of tenants, but will struggle with numbers over 100,000.
Single multi-tenant database
With this model, the data for many tenants is stored together in a single multi-tenant database. This approach comes with a lower cost than the previous one, although it sacrifices tenant isolation. The schema of a multi-tenant database should include tenant identifier columns, so that the data for any given tenant can be retrieved separately and securely. This adds some level of complexity to the database design and the app logic. Also because the database storage and compute resources are being shared, there is a higher chance of performance bottlenecks when multiple tenants are very active at the same time.
Sharded multi-tenant databases
This model combines the two patterns mentioned above. Tenant data is distributed across multiple shards or databases, but all the data for any one tenant is contained in one database only. It is a good alternative to the single multi-tenant database, although it adds complexity both to the database design and app logic. A catalogue is needed to maintain the mapping between tenants and shards/databases (which tenant goes to which database).
The multi-tenant database patterns are recommended when the target market has lots of customers paying a smaller fee (i.e. freelancers, SMEs). In this case you need your data model to support a large number of tenants in a cost-effective way.
The final choice of which pattern to use ultimately depends on your business model, the kind of app you are providing and your customer base. See this article for more technical details on the above models.
Identity Management is a mandatory and complex component of any SaaS offering with significant repercussions on your product’s availability and security. There are two approaches you can use: do it yourself or use a vendor. Although authentication seems simple to implement at start, it is hard to do well. It is much more than maintaining a table with user name and password records. Needs to include solid encryption, anomaly detection, vulnerability management, just to mention a few. A lot can go wrong, and when it does, your reputation is seriously damaged. Also, depending on your customers’ requirements you might need to add multi-factor authentication or support for social and enterprise IdPs.
From my own experience, unless there is a specific and solid reason for DYI, I recommend using an Identity Management vendor. It is quicker, convenient, secure and comes at a reasonable price (or even free!). It allows you to focus on your core value proposition rather than spending time re-inventing the wheel.
There are quite a few Identity Management vendors to choose from, and some have dedicated offerings for developers and small startups. With Auth0 you can start with the free plan that includes up to 7,000 active users, and later on move to a paid plan, which is still reasonably priced. What you get is out of the box authentication with further customization capabilities, including: login and signup pages (hosted by Auth0), API token authentication and user management.
A good, although simpler, alternative is Firebase Authentication, part of the larger Firebase platform provided by Google. Same as Auth0, you can start with a free plan then move to a paid option later on, if needed.
Finally, multi-tenancy is one important thing to consider when implementing your Identity Management component. As a SaaS, you will likely have multiple customers (businesses) and each customer has many users accessing the same data, perhaps with different access rights. No matter what your vendor is, make sure to correctly implement multi-tenancy into your authentication flow.
In most SaaS applications, the User Interface represents the main touchpoint between the company and its customers. Ignore this and the customers you’ve tried so hard to find and convert will fade away soon after starting using your product.
Choosing server vs client side rendering, depends on the pattern of interactions your customers are expected to have with the product. If your app needs to support a high level of interactivity with the user and should feel like a native/desktop app rather than a web site, then client-side rendering (aka SPA or Single Page Application) is more appropriate. Otherwise, for more static applications, the server-side option can be a better fit as it is more SEO-friendly and the initial page load is faster.
An important piece of your product architecture stack, the REST API is responsible for making the data stored in the database available to the outside world. Because of this exposure, it is extremely important to ensure all the API endpoints are fully secured:
Always use HTTPS to encrypt data in motion
Confirm identity and control access with token-based authentication
Always validate and sanitize any input from the outside
This article describes a set of security best practices to consider when developing and deploying your API.
Cloud setup in production
From the dawn of cloud computing, there’s been significant progress on how cloud applications are deployed and run in production. There are a few options to consider now:
Launching and managing your own servers as Virtual Machines. Your app and its components is installed and running on these VMs
Use containers to pre-install the application and its dependencies in a dedicated box that you can launch and run everywhere
Run your application as a set of functions, without worrying about the underlying infrastructure, using serverless computing
Depending on your application requirements, you can take any of the options above or even a combination. For complex apps, a hybrid approach is frequently used. If your app or app component is simple and stateless, like a basic request/response pattern, running it as a standalone function with serverless is recommended. For more complex workflows with many dependencies, use containers or even VMs.
Stateful, performance-critical applications, like databases, are harder to run on containers. They need fine-grained tuning (i.e. network/OS performance) that only a VM can provide. Similarly, complex modules serving machine learning models on GPU or data processing pipelines, are more suitable for VMs.
No matter what approach you choose, design your app components as granular and independent as possible. If you build your application as a collection of microservices, it will be much easier to move between different cloud production models later on, if needed.
Testing and Deployment
Any startup needs to ship quickly and frequently new features and changes to customers. There are two approaches you can take: Constant Hacking vs. Continuous Integration & Continuous Deployment backed by Automated Testing.
At the beginning, while your business is still small and your app is relatively simple, you can certainly hack your way to the next feature as quickly as possible. Make the changes on the fly, apply them directly to production and have your customers do most of the testing. This will not work in the long term, and especially not after you start scaling.
To keep the same velocity while you grow, you need to:
Practice continuous integration (CI) by integrating any code change early and often to the main branch, rather waiting for the release date to merge all changes in bulk. After each commit/push, the automated tests will run, notifying the developers if anything broke. This will avoid things going horribly wrong at the release date.
Deploy to production automatically as early as possible in small batches using continuous deployment (CD). With this approach a new feature can go live in minutes after you've finished working on it.
Although it takes time to adopt them at the start, having all these workflows in place will allow you to ship high quality features at a rapid speed in a scalable way that fuels rather than impedes your business growth.
Most probably you will not invest heavily in security before you have a product that customers need. There are few practices though to be considered that will help you in the long run. You don’t have to buy expensive security tools or penetration tests. Just start by being mindful of your product security aspects and follow security best practices in how you develop and deploy your app.
One of the most important recommendations is the principle of least privilege: whether an employee, user, app or server, only allocate to anybody and anything the bare minimum privileges necessary to perform its function.
Other recommendations include:
If using servers, make sure no server should be reachable directly from the outside. Keep you servers in a private network with a firewalled bastion VM for developer access only. Use reverse proxy or load-balancers .
Use strict firewall rules for each component
Keep your libraries and app dependencies up to date, upgrade them regularly
Use encryption all the time for data at rest (database) and in motion (HTTPS for REST APIs)
If you can afford them, use security tools to monitor your app, such as: Vulnerability Management software, Intrusion Detection and Intrusion Prevention
Build a stack that fuels your business growth
Whether you achieved product-market fit or not, your only chance of survival and growth is to keep listening to what your customers need and constantly deliver value to them. You can do this in an ad hoc manner, throwing feature after feature, or build an integrated system that works for you, instead of against you.
The technical stack of a scalable and secure SaaS product is complex and includes many components that need to work together like an orchestra to provide a good customer experience. Architecting and building a solid stack requires the business to get past the Minimum Viable Product (MVP) practice while keeping the same speed and agility. There is a moment when you need to stop hacking features erratically and start being thoughtful about how you are building your product. Do this as early as possible, get it right at the beginning and it will become one of the engines that will fuel your future growth.