Welcome back to the Architecture Corner.
This is part three of our five part series looking at improving
application availability. That's right.
We've talked about redundancy in compute and persistence.
Before and today we're shifting gears a bit, exploring
strategies that are maybe less complex, potentially more cost
effective. Exactly.
We're diving into graceful degradation and asynchronous
processing. The goal is understanding how
these can really boost availability.
A lot of these ideas come from some great work by Mario
Bittencourt. OK, so most people focus on
preventing failures, right? Yeah, throwing more hardware at
the problem maybe. Pretty much, but today it's
about a different angle, almost counterintuitive, how planning
for failure can make things more resilient.
So how do we actually do that? How do we move beyond just
adding more servers and, you know, account for things
potentially going wrong in a smarter way?
Well, graceful degradation is key here.
It's a fundamentally different approach.
You see different hacks. Instead of trying to prevent
every single failure, which is often impossible or just too
expensive, you consciously plan for certain parts to fail.
OK, plan for failure. How does that help availability?
It allows the application to keep running, maybe not
perfectly, but running nonetheless.
It continues operating, perhaps in a less optimal mode, by
prioritizing the really core functions.
OK, can you give an example? Sure.
Think about an ecommerce site. Yeah, let's say it relies on an
external fraud check service. Right retty, Common dependency.
Exactly. Now what if that fraud service
goes down temporarily? The default might be to just
stop the purchase, full stop. Which is a terrible customer
experience. Awful but with graceful
degradation. You might say OK the fraud
service is down. Instead of blocking the sale,
maybe you offer a sort of pre approval.
Ah, like let the purchase go through but flag it for a closer
look later. Precisely.
Or maybe you apply a simpler internal risk rule temporarily.
It's not the ideal flow perhaps, but the customer completes their
purchase. It's way better than a complete
failure and you achieve this without massive over engineering
costs. That makes a lot of sense.
So how would you actually implement that?
It takes a few steps really. First you've got to identify
your applications dependencies. What external or internal
services does it rely on? OK, and crucially, what happens
if they fail? What's the impact on specific
user journeys? You know, tools like BPMN
diagrams or even just good APM data can help map this out.
Visualize the connections and failure points.
Then Step 2, you need to talk to the business side, the product
owners, the stakeholders to define what an acceptable
degraded experience actually is. What's the fall back?
Is it OK to show slightly older data, use a default value, skip
an optional? Step, right?
So it's a business decision as much as a technical one.
Absolutely. And finally you implement that
fall back option and importantly you protect it with something
like a circuit breaker pattern. Ah, the circuit breaker.
So that stops you from constantly hammering a service
that's already down. Exactly.
It detects the failure, trips open and directs traffic to your
fall back logic. Then it periodically checks if
the main service is back. Up.
So what this all means is you're essentially creating a safety
net. Yeah, allowing your application
to stay functional, keep serving users even if some component is
having a bad day. That's a perfect way to put it,
a safety net. OK, that covers graceful
degradation. It gives us that safety net,
keeps things moving. But error?
What if that degraded experience that fall back just isn't good
enough? What if using a default value or
skipping a step is simply too risky?
That's a really important point. Sometimes a default behavior
just isn't acceptable, full stop.
Right? And this is where asynchronous
execution often comes into the picture.
It's a different pattern. Asynchronous, so doing things
later, not right away. Essentially, yes.
It works by removing what we call temporal coupling.
Temporal coupling, Yeah, it's when two parts of a system are
rigidly tied together by time one process has to wait for
another to finish. Right now, asynchronous
processing breaks that dependency, often by using
messaging infrastructure. Things like message queues.
A task or request is placed on a queue and some other process
picks it up and handles it later at its own pace.
OK, let's tie this back to the fraud example.
If the business says Nope, we absolutely cannot pre approve
without a proper check. How does async help?
Well, if a default rule is too risky, you could make that fraud
assessment asynchronous. So the customer clicks buy.
Right. The order is accepted
immediately. May be placed in a pending
review state. The request for the fraud check
is put onto a queue. And the customer gets
confirmation their order is placed blocked.
Exactly. The customer experience is
smooth. Even if the fraud service is
down at that exact moment, the order goes through from their
perspective. The actual fraud check happens
later when the service is back U or a background worker picks it
up. So the high value operation, the
check itself might be delayed internally, but it doesn't stop
the customer in their tracks. Precisely for those dependencies
where you simply can't accept a default or degraded behavior,
making the interaction asynchronous can virtually
eliminate its failure from impacting the immediate user
experience. That's powerful.
It does raise a question though, doesn't it?
For those really critical dependencies, you need to ask,
can we make this asynchronous? Can we defer its execution
without messing up the core business need?
That's the crucial question to ask.
Yes, it requires careful design. So wrapping this up, graceful
degradation and asynchronous processing, they're like these
powerful levers you can pull. They let you maintain
availability, keep that customer experience smooth, essentially
get planning for failure. Not just, you know, crossing
your fingers and hoping it doesn't happen.
You're really underscores that improving availability is a
complex journey, isn't it? These strategies are vital for
resilience, especially when you're dealing with dependencies
you don't fully control. But yeah, it does make you
think, what if your application is truly mission critical?
I mean, where even these minor hiccups or delays we've
discussed are just unacceptable. Ooh, that is a provocative
thought. Something for you all to Mull
over, and it's exactly what we'll be exploring next.
Time indeed. We'll be diving into disaster
recovery strategies and the whole concept of active active
solutions. Looking forward to that one.
For more information on today's topic and to keep up with all
our insights, please do check the description for details.
And don't forget to subscribe for free to the Architecture
Corner newsletter over at architecturecorner.substack.com.
Thanks. For joining us, see you next
time.