There’s no upper limit on the number of Contacts that customers will want to add to your CRM. Until you can support billions of contacts, there’s always an argument that you should support more.
On the other hand, Relationship and Management options are constrained by the choices you present to your users. You can’t support everything and need to consider which options make sense for your CRM. Every option adds complexity and cost to you; and cognitive load on your users. Adding features often decreases value.
When it comes to contacts, think about what scales. For everything else, remember that people have limits.
Before going into how to migrate, a reminder of my philosophy:
Minimize risk by taking small incremental steps
Focus on providing value to the customer
There are many ways to migrate from one pattern to another. This strategy will minimize risk and maximize customer value.
Step 1 - Extend Your Relational Database
The Relational and NoSql patterns make use of a relational database.
Step 1 is to add a JSON column to your existing contacts column.
Your new schema should look like one of these two models
That’s it - deploy an additive schema update to your database. Since there’s no code to access the new columns, there’s no coordinated deployment. Just the regular, minimal, risk of updating your database.
Step 2 - Query The New Schema
Now that the new schema is in production, it is time to extend your query code.
Add a new code path that checks to see if any data is present in the new schema. If there is data available, execute the query using the new JSON column. When there’s no data, use the original query method.
You will need to develop this code hand-in-hand with the code for migrating the data from your original system to the new schema. The important piece is that you should always be deploying with the READER code on, WRITER code off.
When you deploy this code, there won’t be any data in the JSON column. The new code will be available, but unused.
Since the new code won’t be used, this step is also extremely low risk.
Step 3 - Double Write
At this point your system will use the new schema whenever data is present.
This gives you a single switch to flip - to use the new system, start writing to the new column IN ADDITION to the original method.
Mistakes at this step are the most likely to cause customer impact! It is also the most expensive in time and resources because you are writing the data twice.
However, this also gives you a very quick fallback path. The original writing process is untouched!
If there’s a problem, turn off the double write and delete the data in the new column. Thanks to the work in Step 2 you’ll instantly fall back with NO DATA loss.
Migrations are hard! Preventing data loss minimizes the risk.
Step 4 - Only Hybrid Write
The final step is to stop writing to the original data store. This ends your ability to fall back so make sure to confiscate copies of the data deleter from Step 3!
Ending the double write should be low risk because you were only doing it as a fallback at this point. You should see an immediate bump in performance and drop in costs. This trend will continue as the data migrates from the old system to the new.
Step 5 - Clean Up
At some point you’ll be ready to shut down the old system.
The last step is to decide what to do with the unmigrated data. Depending on how long you’ve waited you’re looking at customer data that hasn’t been accessed in months. Look at your retention promises; maybe you don’t have to migrate the data at all.
Either way, clean it up and shut down the old system at your leisure.
You can migrate User Defined Field code to the latest pattern with very little risk by using the 5 step strategy laid out in this article.
The Hybrid Solution offers excellent scalability and performance for reasonable costs. If your CRM is using one of the earlier patterns, it is time to start migrating.
Take control of the process with small, low risk, steps and never rewrite!
Part 2 covers how NoSQL emerged as an improvement over the classic relational database solution for User Defined Fields. NoSQL delivers speed and scalability by being expensive and fragile. In part 3 I’m going to cover the emerging Hybrid Database solution for User Defined Fields.
Hybrid Databases allow you to combine the best aspects of the relational and NoSQL models, while avoiding most of the downsides.
A hybrid implementation looks like this:
The hybrid model brings the data back to a single server, but without the Contact->Field relation. Instead the field data is stored as a JSON object in the Contact table itself.
No meta programming and no filters, everything is back to SQL. Hybrid databases allow you to directly query JSON fields as if they were regular columnar fields.
You can create indexes on the JSON data. This is an improvement over both the classic and NoSQL models. It can significantly improve performance by allowing the database engine to optimize queries based on usage.
Having a single system makes things simple to set up and easier to maintain.
The database will enforce valid JSON structures, which makes it difficult to poison your data.
There’s no enforced relationship between the JSON data and your User Defined Fields. This means that data can get lost because your system no longer knows to display or delete it.
While Hybrid Databases should scale far beyond the needs of your SaaS, the scaling isn’t quite as open ended as the NoSQL model. If you out-scale the Hybrid model, congratulations, your company’s services are in high demand!
If your SaaS is implementing User Defined Fields from scratch today, go with the Hybrid model. If you already have the classic or NoSQL pattern in place, it’s a good time to start thinking about how to evolve towards a hybrid solution.
I’ll cover how to evolve your existing solution in Part 4.
In part 1 - I covered the classic solution for User Defined Fields; simple but unscalable.
NoSQL emerged as a solution to relational fields in the late 2000s. Instead of having a meta table defining fields in a relational database, the User Defined data would live in NoSQL.
The structure would look like this:
This model eliminates the meta programming and joining the same table against itself. The major new headache that this model creates is difficulty in maintaining the integrity of the field data.
No complicated meta programming. Instead you write a filter/match function to run against the data in the Collection Of Fields.
No more repeated matching against the same table. Adding additional search criteria has minimal cost.
Open ended/internet level scaling. For a CRM or SaaS, the limiting factor will be the cost of storing data, not a hard limit of the technology.
Much more complicated to set up and maintain. Even with managed services supporting two database technologies doubles the difficulty of CRUD. Multiple inserts, multiple deletes, tons of ways for things to go wrong.
Without a relational database enforcing the data structure, poisoned or unreadable data is common. Being able to store arbitrary data collections means you’ll invariably store buggy data. You’ll miss some records during upgrades and have to support multiple deserializers. You will lose customer data in the name of expediency and cost control.
It’s more expensive. You’ll pay for your relational database, NoSQL database, and software to map between the two.
NoSQL systems solve the scaling problems with setting up User Defined Fields in a relational database. The scaling comes with high costs in terms of complexity, fragility and costs.
Reducing the complexity, fragility, and costs leads to the upcoming 3rd shift, covered in part 3.
This series covers a brief history of the 2 historic patterns for implementing User Defined Fields in a CRM, the upcoming hybrid solution that provides the best of both worlds, and how to evolve your existing CRM to the latest pattern. If you care about CRM performance, scaling, or cost, this series is for you!
What are User Defined Field Patterns?
Every CRM provides a basic fields for defining a customer. Every CRM’s basic field set is different depending on the CRM’s focus. So, every user of a CRM needs to expand the basic definition in some way. Birthdays, purchase history, and interests are three very common additions.
The trick is allowing users to define their own fields in ways that don’t break your CRM.
The Three Patterns
At a high level, there have been three major architectures for implementing Custom Fields. Most of the design is driven by the strengths and weaknesses of the underlying database architecture.
Pattern 1, generalized columns in a database, spanned the dawn of time until the rise of NoSQL around 2010.
Pattern 2, NoSQL, began around 2010 and continues to today.
Pattern 3, JSON in a relational database, began in the late 2010s and combines the best of the two approaches
Pattern 1 - All in a Relational Database
Before the rise of NoSql there was pretty much one way to build generic user defined fields.
The setup is simple, just 3 tables. A table of field definitions, a table for contacts, and a relational table with the 2 ids and the value for that contact’s custom field.
This design is extremely simple and can be implemented by a single developer very quickly.
Basic CRUD operations are easy and efficient.
Building search queries requires complicated techniques like metaprogramming.
Every search criteria results in a join against the ContactFields table. This results in an exponential explosion in query times.
The lack of defined table columns handicaps the database’s query optimization strategies.
The classic relational database pattern is easy to set up, but has terrible scaling. This super simple example would bog down by 1,000 contacts and 50 fields.
There are lots of ways to redesign for scale, but this is a SHORT history. Suffice it to say that it takes extremely complex and finicky systems to scale past 100,000 contacts and 1,000 fields.
The solutions to the classic pattern’s scaling led to the NoSQL revolution, covered in part 2.
The panel is fully functional, when you push the button a light turns on and the elevator comes. It is also obviously wrong - the top button is flush with the mount, and the bottom button sticks out.
I found this sad, Beer Belly Elevator Panel, at a high end resort and wondered how it happened.
Certainly whomever installed the mismatched button knew it was wrong. Did the tech not care? Was using the wrong button the only way to get the panel repaired? Was the plan to come back and fix it when the right parts came in?
The hotel maintenance staff had to sign off on it. Did they care about the quality of the repair? Were they only able to give a binary assessment of “working” or “not working”?
Did the hotel manager not care? Were they told to keep costs down? It isn’t broken now, it would be a waste to fix something that wasn’t broken.
Quality vs Letting Your Gut Hang Out
Employees at the hotel see the mismatched panel every day. It is a constant reminder that letting things slide, just a little, is acceptable at this hotel.
When you let consistency and quality slide because something works, you’re creating beer bellies in your codebase.
One small button at a time until everyone sees that this is acceptable here.
So long as a light turns on when you hit the button does it matter if the light is green, red or blue? Does it matter if the light is in the center or on the edge?
But I’m running a SaaS, not a Hotel
Your SaaS may not maintain elevator panels, but your codebase is probably full of beer bellies.
“It works, we’ll clean it up on the next release” bellies.
“This is a hack” bellies.
“This is the legacy version, we’re migrating off of it” bellies.
When you let sad little beer bellies into your codebase, your employees see exactly what you find acceptable.
I recently stepped into an elevator and saw this panel:
The panel was clean, full of high quality materials, and everything worked.
Quality is about more than the functionality, does this look like a quality panel?
Push a button and it lights up!
Sure, it might light up red, green or blue. And the light might be around the edge or in the center. And some of the buttons are flush with the mount, while others extend out; but that doesn't impact the light turning on.
There are 12 possible button implementations, and 5 of them appear randomly.
But when you push the button, the light turns on!
What does that have to do with SaaS Scaling?
No matter how excellent any individual endpoint implementation is, having an API with endpoints that work differently decreases the overall quality of your product.
Having a UI with mismatched widgets and styles increases the user’s cognitive load and decreases quality, even when the differences don’t change any functionality.
Consistency during the scaleup period can be difficult as multiple new teams spin up, but it’s critically important if you want a quality product.
Does your Customer’s experience with your service get better over time?
Does it get worse?
SaaS software often punishes long term clients in subtle and frustrating ways.
Do your CRM customer screens show a decade of buying history?
How many emails can a contact open before you can’t open the contact?
Do marketing campaigns, contact lists, and tags accumulate over the years?
Do database inserts slow down as you write the 10 millionth row into a log table?
There are countless ways to punish customers for staying with you for years. It’s not a startup problem, it sneaks in as you become a scaleup. The flood of new customers blinds you to the slow leak as your most loyal customers churn.
When your longest customers complain about performance more than your largest, chances are your software is punishing them for being loyal.
But how do you get started? How do you shorten your stride from shooting the moon, to one small step?
The next series of posts is going to lay out my scaling iterative delivery framework. This site is about scaling SaaS software, and this framework works best if you want an order of magnitude more of what you already offer your clients. This isn’t a general framework, and it certainly isn’t the only way to get started with iterative delivery.
Work your way through these steps:
Pick a goal - 1 sentence, highly aspirational and self explanatory.
Iterative Delivery is a uniquely powerful method for adding value to a SaaS. Other than iterative, there are no respectably named ways to deliver features, reskins, updates and bug fixes. Big bangs, waterfalls, and Quarterly releases fill your customer’s hearts with dread.
Look at 5 antonyms for Iterative Delivery:
If your customers used these terms to describe updates to your SaaS, would they be wrong?
Iterative Delivery is about delivering small pieces of value to your customers so often that they know you’re improving the Service, but so small that they barely notice the changes.
Don’t be overwhelming, erratic or infrequent - be iterative and delight your customers.