The 12-factor app methodology states that we should treat backing services as attached resources. This enables us to swap out faulty services quickly and swap them out easily as we move from one environment to the next.
This is the final video in our 12-factor Application Modernisation series. In this video, Marc Firth (Managing Director at Firney) explains software engineering best practices for building scalable, reliable web services that are efficient to work with.
See our Application Modernisation with 12-Factor App series and the full App Modernisation playlist on YouTube.
A transcript of the video above is included below.
Why treat backing services as attached resources?
Marc: Today, we’re talking about why we should treat backing services as attached resources. This allows us to swap out our backing services easily, such as when we move our release from one environment to another.
Great to have you here to discuss “loosely coupled backing services”.
We use this methodology across nearly all of our applications. For example, we use a dockerised MySQL locally, when we’re doing our local development, and then when we get to production, we’re using a Google Cloud SQL or Amazon RDS database.
Application modernisation
This is our 12-Factor Application modernisation series where we’re talking about how to make applications more reliable, scalable and efficient to work with.
Databases
Now, 12-Factor tells us that we want to keep our environments as similar as possible but, because we’re trying to keep our local development costs low, we use a Dockerised MySQL locally, and we try to match that as closely as possible to the version of Cloud SQL that we’re using in production.
We’re willing to accept that there may be some quirks, but we know that 99.9% of the time it’ll work flawlessly.
We also know that if we needed to, we could easily swap out one of those instances with a Cloud SQL instance just by making a config change.
Queue & caching
We use a similar approach for our queue and caching services with Redis. We have a dockerised Redis instance that we work with locally, and we have a cloud-based Redis instance that we use in production.
What do we mean when we say “Backing Services”?
So what do we mean when we say backing services?
Well, a backing service is any service the app consumes over the network as a part of its normal operation.
So, to give you a few examples, these could be databases; so MySQL, Postgres, MongoDB; email or SMTP services, memcached, Redis, queues, file systems and any other external API.
Should I use internal or external (managed) services?
The code for a 12-Factor app makes no distinction between any internal or external services. To the app, both of those are attached resources which are accessed via some form of credentials or URL within the config.
Storing that “locator”, or the URL within the config, means that we can swap out those services very quickly.
An example of this might be using MailTrap for local development where you might want to capture any email coming from system so they don’t actually get sent to the users.
You might also use MailTrap in your Testing, Stage and UAT environments before swapping it out with a service such as SendGrid in Production, which will actually send the emails to the users.
A key thing to note here is that you want all of those services to be as similar as possible. So try and make sure you’re interacting with them the same way. For example, by using SMTP for our mail service.
The Benefit: Swapping out services quickly
One of the benefits of attaching resources like this is that we can swap them out quickly if there’s an error.
For example, if a database instance was to go down, we could quickly spin up another instance of a database, restore the database from a backup, and just make a change in the config in order to access that new database.
It’s the ability to resolve issues quickly like this, which enables us to stick to our service level objectives and service level agreements.
Tips for working with backing services
A few tips for working with backing services:
Tip 1: Store config in the environment
First of all, make sure you store the config in the environment and not in the code. I made a whole video on how you should work with storing config, and I’ll link to that down in the comments.
Tip 2: Monitor backing services
You should also monitor those backing services within your observability stack. So using a service like New Relic for instance. That’ll enable you to quickly tell if it’s the third-party service that’s gone down.
Tip 3: Vendorise and isolate dependencies
If you’re using libraries to interact with those services, then make sure your vendorise and isolate those dependencies. I’ll leave a link down to my video on dependencies in the comments as well.
Tip 4: Keep backing services consistent
Keep those backing services as similar as possible between environments because then you won’t run into any issues as you promote a release through those environments.
Although, admittedly depending on the SLO, we don’t always do that with our databases and file systems. It does depend on the service objective that you’re trying to meet.
For everything else, we always make sure to use the same container images, or the same third-party services, between our environments.
Series Finale
Now, this brings us to the end of the official 12-Factors. We have more tips that we’ll be releasing in future videos which are just as important as these 12.
But don’t forget to like, share, follow and subscribe… and any other verb you can think of, and let us know what you thought of this video down in the comments, and I’ll see you in the next one.