Quick start, how to construct your SaaS app

Amit Cohen
13 min readMar 2, 2022

Lack of product-market fit is considered by most the main reason new products fail. Once product-market fit is achieved eventually, they start investing into building a scalable and secure product. There is a lot of advice for early stage SaaS startups on finding the right customers and market to serve. 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 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. 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.vAn alternative approach is to be thoughtful about your product architecture right from the start, even before you have the first set of features implemented. 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.

The Database

At the core of the tech stack of any modern SaaS product sits a reliable, secure and scalable database. The DB component is crucial 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. lets explore

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.

Separate Databases

Storing tenant data in separate databases is the simplest approach for data isolation. Computing resources and application code are generally shared between all the tenants on a server, but each tenant has its own set of data that remains logically isolated from data that belongs to all other tenants. Metadata associates each database with the correct tenant, and database security prevents any tenant from accidentally or maliciously accessing other tenants’ data. Giving each tenant its own database makes it easy to extend the application’s data model to meet tenants’ individual needs, and restoring a tenant’s data from backups in the event of a failure is a relatively simple procedure. Unfortunately, this approach tends to lead to higher costs for maintaining equipment and backing up tenant data. Hardware costs are also higher than they are under alternative approaches, as the number of tenants that can be housed on a given database server is limited by the number of databases that the server can support. Separating tenant data into individual databases is the “premium” approach (like AWS reserved instances), and the relatively high hardware and maintenance requirements and costs make it appropriate for customers that are willing to pay extra for added security and customizability. For example, customers in fields such as finance ,medical or even governments records management often have very strong data isolation requirements, and may not even consider an application that does not supply each tenant with its own individual database.

Shared Database, Separate Schemas

Another approach involves housing multiple tenants in the same database, with each tenant having its own set of tables that are grouped into a schema created specifically for the tenant when customer first subscribes to a service, the provisioning subsystem creates a discrete set of tables for the tenant and associates it with the tenant’s own schema. After the schema is created, it is set as the default schema for the tenant account A tenant account can access tables within its default schema by specifying just the table name, instead of using the SchemaName.TableName convention. This way, a single set of SQL statements can be created for all tenants, which each tenant can use to access its own data. Like the isolated approach, the separate‐schema approach is relatively easy to implement, and tenants can extend the data model as easily as with the separate‐database approach. This approach offers a moderate degree of logical data isolation for security‐conscious tenants, though not as much as a completely isolated system would, and can support a larger number of tenants per database server. A significant drawback of the separate‐schema approach is that tenant data is harder to restore in the event of a failure. If each tenant has its own database, restoring a single tenant’s data means simply restoring the database from the most recent backup. With a separate‐schema application, restoring the entire database would mean overwriting the data of every tenant on the same database with backup data, regardless of whether each one has experienced any loss or not. Therefore, to restore a single customer’s data, the database administrator may have to restore the database to a temporary server, and then import the customer’s tables into the production server — a complicated and potentially time‐ consuming task. The separate schema approach is appropriate for applications that use a relatively small number of database tables, on the order of about 100 tables per tenant or fewer. This approach can typically accommodate more tenants per server than the separate‐database approach can, so you can offer the application at a lower cost, as long as your customers will accept having their data co‐located with that of other tenants.

Shared Database, Shared Schema

A third approach involves using the same database and the same set of tables to host multiple tenants’ data. A given table can include records from multiple tenants stored in any order; a Tenant ID column associates every record with the appropriate tenant. Of the three approaches explained here, the shared schema approach has the lowest hardware and backup costs, because it allows you to serve the largest number of tenants per database server. However, because multiple tenants share the same database tables, this approach may incur additional development effort in the area of security, to ensure that tenants can never access other tenants’ data, even in the event of unexpected bugs or attacks. The procedure for restoring data for a tenant is similar to that for the shared‐schema approach, with the additional complication that individual rows in the production database must be deleted and then reinserted from the temporary database. If there are a very large number of rows in the affected tables, this can cause performance to suffer noticeably for all the tenants that the database serves. The shared‐schema approach is appropriate when it is important that the application be capable of serving a large number of tenants with a small number of servers, and prospective customers are willing to surrender data isolation in exchange for the lower costs that this approach makes possible.

User authentication

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. Other good options to consider for your Identity Provider vendor are Okta and FusionAuth. They both have free offerings to get started with and provide similar functionality to Auth0 and Firebase. 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.

User Interface

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. There are several choices you need to make when it comes to picking up the right tech for the UI. First one is whether to use server-side vs client-side for the web application. Server-side rendering works by building and populating HTML files with data dynamically on the server. In client-side rendering, the user’s browser is getting from the server a bare-bones HTML document and a Javascript file responsible for populating the document with content. The content is dynamically generated in the browser, usually using data retrieved from the server through a REST API. 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. If you decide to build the UI as a Single Page Application, you need to choose a Javascript framework that helps with the implementation. Some of the most popular frameworks are React and Angular, while other options include: Vue.js, Ember.js and Meteor. No matter what your final choice is, make sure to pick a framework for which you’ll be able to find skilled developers later on.

RESTful API

As mentioned in the previous section, in a Single Page Application the data needs to be exposed to the client through a secured REST API. The Javascript functions running in the browser will call the API, retrieve the required data and populate the HTML page accordingly.

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

Any of the main server-based programming languages can be used to implement a REST API. Most popular are Python, Javascript/Node.js and Java. Pick the language your team is most familiar with.

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 VM’s. 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.

There are many resources available on each of these options. 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 services, 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:

  • Build automated tests that cover most of the code base. Having both unit tests and integration tests is a great start. Make sure to follow best practices when writing these tests.
  • 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.

Security

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.

--

--

Amit Cohen

A product leader with exceptional skills and strategic acumen, possessing vast expertise in cloud orchestration, cloud security, and networking.