Microservices are not new nor are they the hottest technique on the block anymore and that’s why now is a great time to learn about them. They’re tried and true. They’ve been tested and perfected over time by developers far smarter than me or you. Let’s talk about microservices. What they are, when they’re useful, how you can use them, and maybe some pitfalls you might want to avoid.
What is a microservice?
A microservice is a small piece of software that works in concert with one or more other, separate pieces of software. Often times you have a monolithic web application, like a standard MVC web app, and there’s a particular piece of the application that for whatever reason makes more sense on its own. The reasons for that are:
- You’re better off using a different language for one piece of the app because it does certain things better. For example, single threaded vs. multithreaded code or functional vs object oriented code
- Maybe you have a web app and a mobile app and both need to use a common API to share data. Boom – microservices to the rescue. Sure, you could build an API into the main app but if you make drastic changes to the app in the future your dependent apps will need serious refactoring. Why not have a common API, a set of rules, that all satellite apps will use as a reference?
- You’re in danger of exceeding the max connections on your server. So you have Nginx proxying requests from port 80 to a local Rails or Node app. Nginx can handle a ridiculous amount of concurrent connections but a Rails app can’t handle as many requests as Nginx can throw at it so they get throttled or put in a queue. The response times are awful and it’s because of one external API request or long running process. Or maybe there’s a Node app which can handle a ton of traffic fast but there’s one endpoint that’s synchronous and it ends up slowing everything down.
When are microservices useful?
Any time you have processes that don’t need to be run right now, an async language calls a slow synchronous method, you have a large feature that could be part of your monolithic app but really makes sense as a separate application, or there’s a large feature that could be made more generic so that multiple separate internal and user facing applications can consume the output and display it however they want. This isn’t about making your life easier by writing less logic (though it is a side effect), it’s simply about allowing the consumers of your microservice to do run whatever logic and apply any formatting they like to your output as each will have different requirements. For example, some applications will want to output things like internal resource IDs while others would be purely focused on browser UI.
It’s often difficult to decide whether or not you need or want a microservice. A good rule of thumb is if you have a feature which encompasses multiple tasks, resources, and use cases – basically looks like an application by itself when looked at in isolation, then you have a good candidate for a microservice.
Real world example
Once upon a time I was the CTO of a company in the healthcare industry. Healthcare is a massively complex topic to start with especially when you think of the ACA (Obamacare), Medicaid, insurance company APIs, rules, laws, and government regulations surrounding it. Even before you start coding you have to take all of those things into account.
Now imagine getting access to a government API. These APIs are built not only with security in mind but also built for large enterprises using technologies that a small startup wouldn’t choose to work with.
So we built our user facing application using Node.js and React. It was a beautiful and fast application from the codebase to the UI. I personally built 90% of it with the other 10% being built out by our junior developers. Everything went great until we hit some roadblocks:
- Authentication was via SOAP and SAML (XML based formats)
- There were few if any actively developed background job libraries for Node at the time
- Each year we had to populate 2 databases with information the US government provided in the form of 7 different CSV or Excel format files that we had to populate our databases with
- We were not provided zip code information – a critical component of our app – so we had to find it and update our database every 3 months
Our Node application was the right tool for the job. It provided us with all the functionality we needed for our unique value proposition. The issue was with the services that interacted with other APIs or had to run in the background. This is where micro apps came into play. Here are the services we broke out of the app and their purpose:
- We created a Ruby executable script that handled SOAP and SAML tasks. Node is not the language to parse XML with. To this day there are plenty of XML parsers but none of them handle SOAP and SAML. Ruby with the help of Nokogiri can easily parse XML and has great libraries for SOAP and SAML support. So each time we needed to deal with that we made a system call to our Ruby script and passed the response back to our Node app. This was a microservice that was only used on the main app servers.
- We lacked any good background job libraries for Node so we used Redis to store data that didn’t need to be dealt with immediately and let the Node app provide a response immediately for the important information. In the meantime we had a daemon built in Go that would use one of Redis’ blocking methods and once data was inserted into a Redis list the Go daemon would pluck it out and do things like sign a user up for a mailing list, look up their insurance agent information from a special API then insert that data into the main app’s database.
- For when we needed to update our database with government provided Excel/CSV files we had a cron job that would run a Ruby script to download the data, format it all, update the database and invalidate old data
- We had a separate API that would respond to users with available insurance plans as well as calculate subsidies based on user input. It also allowed us to look up insurance agent information like where they were licensed and such. This API used the methods above to update its database. It ran on a separate server and was used not only by our main Node app but also by an iOS app that never got to be released as well as for internal use whenever we had to do any face to face interactions with potential clients.
This was the perfect use case for microservices. We had three main applications running. Our user facing Node app, a server daemon, and a Ruby API. Our main product was able to run faster because the burden of long running or slow tasks was handled by another service that could do it better or faster than Node. The other upside was the ability to get dual use from our external API which ended up being used by 3 separate services both internally and externally.
Once you start down the road of microservices it’s easy to just break everything out into microservices. I’ve seen people turn every endpoint into a microservice. In the end you end up with 15 different repositories that could all be running in one app. Not everything needs to be a microservice. There has to be a balance between performance and utility.
Remember, if something is exceptionally slow then think about making it a microservice. If there’s an API endpoint that could serve your main application but also be used as a service all on its own, then it’s a good candidate for a microservice. Don’t take microservices lightly. They may be easy to create but you’re likely not going to be the only person to use this application. The relationship to your main app and the value it brings to your company needs to be considered. The last thing you want is to onboard a new developer and have it take 3 months to show them each codebase, its value proposition, and how it relates to other apps in your ecosystem. It’s best to keep things as centralized as possible.
Micro apps have been trending and like all trends they tend to peak before they come back down to reality and start being used in a sane way.