In almost every company (if you’re lucky enough to get there), there is a moment when you start having customers. At first, you are super excited, you remember every one of them and you put your best efforts into keeping them happy.
But after a while, there are just too many of them. If you are like me, at this point you start a spreadsheet, which is the solution to 50% of the world’s problems. And this spreadsheet gets bigger and bigger, with more columns, with more side tabs and the like, and then you realize: you need to use a real CRM (customer relationship management).
Starting to use a CRM
Keeping a CRM updated is kind of weird: you are updating an external system with state changes and events that are happening inside your application.
Everyone in the company has requirements for the CRM: the marketing team, the customer success team, the support team, the sales team, the analysts… they all use it. The only exception is the R&D team. And this is where the pain begins and why I’m writing this post. Because when we started to support a CRM service at Oribi, we thought of it as a favor to the other teams, not as an engineering challenge.
This post is not about choosing the right CRM service or about defining its requirements, it’s about a small change in our state of mind that made a big change in the company.
I will discuss:
Why it’s impossible to perfectly support CRMs.
What we did at Oribi.
How you can do it correctly and give yourself a head start.
Why it’s impossible to perfectly support CRMs
I lied, it is possible, but it requires a lot of resources. A LOT. Oribi’s R&D team has a CRM-related task almost every sprint.
Not all CRM services are born equal, each one has its strengths and weaknesses, so it’s very likely that as the company grows, you will use more than one, and the number of them might escalate quickly.
Because the various CRM services have different features, you can’t reach 100% consistency, neither between the CRM services nor between the CRMs and your system.
Most importantly, requirements will change. Even though there was a lot of thought put into them, as the company evolves so do its needs.
Illustration of the enemy
What we did at Oribi
At Oribi, when we started to send data to our first CRM service, we did it naively. Don’t be us: before you start, you should be aware of all of the above, and you should choose your design and implementation intelligently.
We have a microservices architecture, and one of our microservices is the customers service. It's responsible for account management actions (creating accounts, adding members, etc.), so all of the changes to a customer’s state go through it. It was the obvious place to locate our CRM related code. We created a client there and it all worked well.
As I mentioned earlier, as a system evolves, so do its needs, and so at some point we wanted other microservices to update the CRM as well. We wanted the payments service to update changes in a payment’s state, we wanted the subscriptions service to update whether customers are subscribed to our newsletter or not, we wanted a service to update the number of events sent to us by a customer and so on.
The additional requirements were arriving slowly, so each time we implemented a local solution. The payments service sent update announcements to the customers service with a pub-sub mechanism, a scheduled task with its own CRM client updated the number of events sent to us, and so on.
In addition, we added more CRMs, and eventually it became too messy and too time consuming to handle.
At that point, we realized that our entire approach was wrong, and we finally went for a more holistic solution.
How you can give yourself a head start
We learned that supporting CRM services is not a part of business logic, but it’s something that may be needed at any point of an application’s flow.
Out of respect to clean code principles like “separation of concerns”, “each class has a single responsibility” and “dependencies are bad”, we implemented a new microservice - a CRM connector service.
This service has an API with CRUD operations on the account and user levels, an interface, and a number of specific CRM clients that implement the interface and apply the specific logic of the service. (For example, is it “company” or “account”? Is the payment state property at the account level or user level? What is the property name?)
Today the CRM connector service is the only one that communicates with our 3rd party CRM services.
Oribi’s CRM-related architecture
What are the benefits?
The other microservices don’t need to worry about the various implementations, they just send an HTTP request to the CRM connector service and move on with their lives.
What used to be spaghetti code is now well organized, so it’s both easier to reshape a customer’s flow and more pleasant to the meticulous programmer.
The CRM connector service is doing all of the error handling without interrupting the business logic.
The CRMs and our system are more consistent than ever.
There’s still maintenance work: new properties, new events to send, new property names, etc., but it’s all so much easier.
Debugging, backward updates and monitoring are also much easier.
During the process, we started to use Zapier to update our main CRM service, and now we are saving all of that money.
Once we started to think about it as an engineering challenge rather than an annoying favor we were doing for the other teams, we invested in designing and implementing a good solution, and it has made everyone’s lives easier.
While it’s not a big technological innovation, it has had a massive effect. The change helped the R&D team to support the company’s growth in a way that helped other teams do their work better.
In retrospect, I recommend starting with a similar solution. It’s not even that complicated, it just requires a different state of mind.