×

A SaaS integration strategy for custom software

Build great systems.

We have at our disposal a suite of powerful tools and documentation allowing us to create things inconceivable just a decade ago. Shouldn’t the state of our bespoke software keep pace with the rapidly improving building blocks?

Custom software consistency

Most nontrivial software products will combine:

  • Custom code
  • Libraries and frameworks
  • 3rd party software-as-a-service (SaaS) products

From a business perspective, the build-vs-buy decision really depends on the problem. At K-Optional, we rate each feature on a spectrum from “ancillary” to “core”; those on the ancillary side qualify for solutions “off the shelf”, but only if a suitable one exists— and that’s a big if. For industry-perfected features like SMS notifications, email delivery, and payment processing, we use existing services to avoid reinventing the wheel.

Let’s assume that you are releasing a product that employs at least one SaaS product: your integration strategy will have a huge effect on scalability and support needs. It might even make or break the product.

Knowing that, we follow a 3-step approach in the design of our systems:

  1. Visualize the potential system, highlighting 3rd-party integrations.
  2. Isolate all components not built in-house.
  3. Substitute weaker 3rd-party integrations for custom-built software as the product scales.

Visualizing SaaS integration

As a rule of thumb, always approach system design top-down; if you find yourself making substantial architecture decisions during the implementation phase, that’s a signal you should plan more.

A reliable way to enforce high-level thinking is to create diagrams that represent your system (”system-viz” as we call it). Please rest assured: basic diagrams can take little effort, demand no expensive software, require no expertise, and be quite informal. Some clients express aversion to this practice, deeming system-viz foreign or more suited to a professional. As those professionals, allow us to tell you that every product stakeholder can and should weigh-in to a system blueprint. It’s a simple process that can help you better envision your product and avoid costly backtracks in the future.

We’ll quickly run through the low-hanging fruit of system-viz. If you find yourself reluctant to put something down, consider at least hiring a custom software team to deliver a blueprint; these hours tend to be the cheapest and highest-yielding in the product development phase.

Example platform

To paint a better picture while diagramming and beyond, we’ll adopt the following system example:

You are building a platform for lawyers to manage clients:

  • The app enables end-to-end encrypted messaging with these clients.
  • You can send and track invoices right from a screen on the application.
  • Prospective clients can schedule consultations with a law firm from a companion website; appointments flow into the application.

Your product includes a custom encryption chat, and uses Stripe to send invoices and Calendly to book appointments; the core system ties together these external services seamlessly.

SaaS integration diagramming

For the diagrams below, we use Mermaid. Mermaid embeds into Github and Notion which we love. We also recommend:

  • Just using pen & paper. Rather than spend time learning a new system, just draw flow charts and sequence diagrams and share photos. Try simply drawing the diagrams below on printer paper.
  • Excalidraw, a free service for white-boarding across teams and devices. Excalidraw provides less structure than Mermaid which can be good or bad depending on your constraints.
  • Diagram as Code, a library for generating cloud diagrams that exhibit how, say, AWS services work together.

SaaS integration sequence diagram

Estimated time to create: 10 minutes

A sequence diagram depicts interactions among subsystems in order. For our example, we can say that “we use Stripe for billing”, but the devil is in the details! Will we use Stripe Invoicing or Stripe Payments? When exactly should invoices be sent? Will the app reflect when a client pays? Will it disable messaging for clients who fail to pay?

A sequence diagram zooms into a process like “billing” and outlines what it entails. Expounding on such details ahead of time will help you avoid dead-ends or costly miscommunications.

Without further ado, the invoicing sequence could look like this:

Sequence Diagram Example

If you want to play around with the actual diagram, you can do so here.

SaaS integration flowchart

Estimated time to create: 7 minutes

The flowchart also visualizes a process, often from the perspective of a user. If a sequence diagram is a list of directions, think of a flowchart a bit like a map. Intuitive and broad, flowcharts are a great starting point for any application.

Flowchart Example

You can find the diagram playground here.

Having now visualized our system we’re ready to isolate 3rd-party dependencies.

Isolating SaaS integrations

In an ideal world, as you’re deciding which features to build or buy, you should aim to minimize costs and maximize utility. But be aware of two common pitfalls:

  1. Don’t invest too many resources into an unnecessary in-house build. For example, avoid the temptation to custom-build your own content management system (CMS) when your product would function just as well with an off-the-shelf solution such as WordPress. Instead, save your budget to custom-build unique, core features.
  2. Don’t leave your product too dependent on a SaaS product that performs poorly or holds your product hostage. Like say, if your eSign provider raises your prices and axes a feature on which you depend.

Luckily, there’s one strategy that can help you defend against both of these risks: strategically isolate your SaaS dependencies. In doing so, you can be more liberal with existing solutions while not being beholden to them.

How can you isolate the SaaS features you use in your product? We’ve had success with a simple strategy.

Start with your system-viz prints: You should be able to draw a circle around outsourced features and providers.

For each dependency you identify, answer the following questions:

  1. What would it mean for my product if this dependency started failing?
  2. How difficult would it be to build a replacement for this dependency?
  3. How does this dependency send information to my product. Using a calendar-booking system as an example, the booking provider could send data to your product with a webhook.
  4. How does this dependency receive information from my product? The reverse of the above, your product could redirect users to this booking page when they click a “schedule” button.

The first question should indicate how a SaaS product exposes your product to issues. The second three can indicate the risk of relying on this product. More specifically yet, questions (3) and (4) portray what we refer to as “integration surface area” – in other words, how hard you have to work to tie in your product with an external system. The greater the effort, the harder it will be to unwed them. Picture a tangled bundle of wires — hard to maintain and hard to undo — compared to the tidiness of a single outlet plug.

Where possible, we recommend a low integration surface area. And as you’re designing, be sure to visualize how you’d substitute a third-party feature, if necessary. It just might come in handy.

Evolving products & migrations

We derive much of our philosophy on migrations from Will Larson’s An Elegant Puzzle, and highly recommend this read.

You may think of a software product as a fixed asset, something to be built, and then continues to exist. Static diagrams and blueprints give that impression. But consider the dimension of time and how a system or product might progress from kick-off through release and beyond: You will find a dynamic entity that lives and breathes, or at least evolves.

As it relates to your SaaS integration strategy, build-vs-buy decisions aren’t permanent. In fact, we gain an edge by accounting for passing time and system flux. You might phase out a 3rd-party provider, or even phase in a provider that better supports an app function.

Transitions like these are called migrations. Migrations can be painful and disruptive— stick around in the industry long enough, and you’ll learn of companies that flipped a “go live” switch on a new system and buckled irreparably. But they can also drastically improve platforms, products and apps when done right.

Here’s the premises we hold when it comes to making migrations:

  • Some fraction of new code contains bugs.
  • A systematic approach to releasing code can catch and dispose of bugs— automated tests, thoughtful quality-control, intelligent design.
  • The more disorganized a system, the harder it is to be systematic, and as a result, the higher propensity for missing bugs.
  • Migrations that fundamentally alter a system blueprint tend to be less organized.
  • Migrations that only alter isolated features and integrations tend to be more organized.
  • We can avoid bugs by deliberately isolating SaaS integrations and supporting migrations that leave system designs alone.

You can use the evolution of your system to your advantage? (i cant tell how meaningful this clause is — you could potentially drop it.) system evolution and protect against bugs with this method: either change the blueprint or the dependency, never both at once. Evolution moves one step at a time.

Example: K-Optional built an enterprise e-learning app that allows instructors to build courses. Instead of writing a course builder from scratch, we tapped a content management system called Flamelink. We knew that the second version would include a custom-designed feature to build courses, and so planned to replace Flamelink when we could give this corner our full attention. But in doing so, our custom course builder mimicked Flamelink’s database structure. Thus the system blueprint never changed, only an implementation detail. Only after we launched our version without incident did we refactor the database structure, better aligning with our own code conventions.

One of the many reasons for this approach, we could have reverted to Flamelink at any point during the transition. Remember: achieve parity between replacement and replaced before making the swap.