I have published a few posts about Ruby backends, AWS EC2 and using SNS/SQS in the past. Lately I have done and pushed some experiments further to use a lot more from AWS compute services rather than just hosting the services there.
I started from the idea that what the end user needs to see is what the frontend needs to serve. “Serve”, not “store” or “compute”. Usually we have kept business logic within the main backend as it provides also the frontend. Single page sites using heavy JS frontends have moved that cursor a bit but there is actually nothing preventing us from doing a similar things with RubyOnRails or Django (or Phoenix) backends.
Here is a short example of how to get less heavy on the frontend side and get work split within multiple micro services and tasks.
The easy target : batch jobs
The first target to get hit is the most obvious one : batch and background jobs. They are the ones triggered when a report needs to be generated from one or multiple sources for example.
When you think of that example, there is probably a lot of business logic involved with rendering a report so I have usually seen that rendering bundled within the main backend.
Yet, the main backend and its frontend only need to know what to expose to the end user (an url) , nothing more. So once the time has come to render the report the work can be done in another blob and the url passed to the frontend/backend.
AWS offering contains a few products and ways to do that. I talked before about using custom made containers running on EC2 instances in an ASG, there are now even easier way with Lambda (short lived, small size pieces of code) and Fargate (longer run time, container packaged code).
Following up : any kind of processing
Once you have tasted the ease of use and development with SNS + Lambda you start to look into more ways to use them. Following the previous idea (“the frontend doesn’t have to do more than just display”) you can start to use SNS to send events into your infrastructure and have one or more Lambda (or Fargate tasks) to treat the event and store the data or its transformed version into the data stores.
Let’s take the example of SSL Certificate checks from a service. The frontend just have to get an url from the user and write it to a SQL database and send an event on a SNS topic about this (“new_url”).
That topic has two subscribers :
- ssl_checker : checks the SSL certificate at the url
- new_customers : add the new url in the marketing database
Those are two Lambdas since it’s pretty fast to do all this work.
new_customers doesn’t have a follow up. The marketing frontend will just pickup the data form the marketing database.
ssl_checker will pass the result of its work on one topic (“ssl_result”). That topic is listened by two listeners :
- ssl_stats : a lambda to store the result of the test within the frontend database or a dedicated stats database (a NoSQL one possibly)
- ssl_failures : if the result are bad we need to notify the owner of the site, so this lambda will send the notification to the user
The frontend will only know about the user’s data, and the url they own. The stats about the SSL test will be pulled by the frontend through an internal API or directly from the NoSQL store.
The rest of the computing (SSL tests, result processing and decision) is handled separately to all this and the frontend doesn’t need to know about it. The great bonus about this is that the code of each block can evolve independently : which means each service or task can be small and easy to monitor, inspect, and maintain.
This also is a great way to work for remote and spread out teams : team members don't have to work too tightly on one single code base but rather work loosely on several small code bases.
Link up to better work ethics and culture
That can be easily linked up with better work culture in teams. By having people working directly on smaller parts of the code base they can be trusted to take care of them and own them.
From experience this is very important. As the team decides how to architect the whole fleet of micro services (and micro tasks) together they solve and own the solutions on their own. This cannot work well with waterfall methods since it involves, de facto a lot of communication, ownership and flexibility on the side of the ones doing the work. Thus, once you go for this and the team is on board, what is left is having a product owner setting a direction and letting the team figure out the instructions to make it happen.
“It doesn't make sense to hire smart people and tell them what to do; we hire smart people so they can tell us what to do.” — Steve Jobs
The places where I was most happy to work were places where the technical lead was just giving nudges to his team members to keep them on the right track while they were entirely responsible to figure the implementation details.
And I know that’s the case for most people in our industry and most others.
As a conclusion I can’t help but point out that giving great infrastructure foundations such as these are a great way to empower the software engineering team in any company. That’s why many recommend adding more devops skills and people in the engineering team and possibly within each feature team.
Interested in this architecture or a similar one ? I am happy to tell you more about this and help you. I am based in France, and can consult remotely and on site. Contact me : firstname.lastname@example.org.