Availability in Software

By Alex Snowgirl — Published on
0 Likes
0 Dislikes
Availability in Software
Availability in Software

Availability is the ability of a software system to remain accessible and usable when users need it.

A system may be fast, scalable, and feature-rich, but if users regularly receive errors or cannot connect to it, availability is poor.

In simple terms:

Availability answers:

"Can the system serve requests right now?"

High availability does not mean that nothing ever fails. Servers crash. Databases restart. Networks break. Deployments go wrong.

A highly available system is designed so that individual failures do not make the entire service unavailable.

Table of Contents

What Is Availability?

Consider a simple API:

User
 │
 ▼
Application
 │
 ▼
Database

If both the application and database are working, requests succeed.

If the application crashes:

User
 │
 ▼
Application ✗

the service becomes unavailable.

If the database crashes:

Application
    │
    ▼
Database ✗

the application may still be running, but users cannot complete requests.

Availability therefore depends on the entire request path, not only one component.

User
  │
  ▼
DNS
  │
  ▼
Load Balancer
  │
  ▼
Application
  │
  ▼
Cache
  │
  ▼
Database
  │
  ▼
External Services

If a critical component in that chain is unavailable and there is no fallback, the user-facing service may also become unavailable.

Availability Percentage

Availability is often expressed as a percentage.

A simplified formula is:

Availability =
successful operating time
-------------------------
total measured time

Common targets are:

Availability Approximate Downtime per Year
99% 3.65 days
99.9% 8.76 hours
99.99% 52.6 minutes
99.999% 5.26 minutes

The difference between 99.9% and 99.99% may look small, but operationally it is significant.

Achieving another "nine" usually requires more redundancy, better failover, safer deployments, stronger monitoring, and more operational discipline.

Availability vs Reliability

Availability and reliability are related but not identical.

Availability asks:

Can the system serve the request?

Reliability asks:

Does the system continue behaving correctly over time?

Consider a payment API that is reachable 100% of the time but occasionally charges customers twice.

Availability: high
Reliability: poor

Now consider another service that experiences a short planned outage once a year but otherwise behaves correctly.

Its availability is slightly lower, but its overall reliability may be better.

Availability is therefore one important dimension of a reliable system.

Single Points of Failure

A single point of failure is a component whose failure can make the entire system unavailable.

Consider:

Users
  │
  ▼
One Application Server
  │
  ▼
One Database

This architecture has at least two obvious single points of failure.

If the application server fails:

Application ✗
     ↓
Entire service unavailable

If the database fails:

Database ✗
    ↓
Entire service unavailable

Single points of failure can be less obvious too:

One Redis instance
One message broker
One network gateway
One DNS provider
One authentication service
One configuration service
One storage volume

High-availability design begins by identifying these dependencies and deciding which ones need redundancy.

Redundancy

Redundancy means having more than one instance of a critical component.

Instead of one application server:

Users
  │
  ▼
App Server

run several:

                    ┌──► App 1
Users ──► Load Balancer
                    ├──► App 2
                    └──► App 3

If App 2 fails:

App 1 ✓
App 2 ✗
App 3 ✓

traffic can continue through App 1 and App 3.

This is one of the simplest ways to improve availability.

Redundancy can exist at many layers:

Multiple app instances
Multiple cache nodes
Multiple database replicas
Multiple queue brokers
Multiple availability zones
Multiple regions

But redundancy alone is not enough.

The system must also detect failures and route traffic around them.

Load Balancing

A load balancer distributes requests across multiple application instances.

                     ┌──────────┐
                ┌───►│  App 1   │
                │    └──────────┘
                │
Users ──► Load Balancer ──► App 2
                │
                │    ┌──────────┐
                └───►│  App 3   │
                     └──────────┘

Suppose App 2 crashes.

A healthy architecture should stop sending requests to it:

Before:

App 1 ✓
App 2 ✓
App 3 ✓


After failure:

App 1 ✓
App 2 ✗
App 3 ✓

The user may never notice the failure.

Load balancing therefore improves both scalability and availability.

For availability, the important behavior is not merely distributing traffic. It is removing unhealthy instances quickly and safely.

Health Checks and Automatic Failover

Systems need a way to determine whether an instance can receive traffic.

A load balancer might periodically call:

GET /health

A healthy server returns:

{
    "status": "ok"
}

An unhealthy server may fail the check.

The load balancer then removes it:

App 1 → healthy
App 2 → unhealthy
App 3 → healthy

Traffic:
App 1 ✓
App 2 skipped
App 3 ✓

Health checks should answer the right question.

A process can be running but unable to serve requests.

Process alive ✓
Database pool exhausted ✗
Ready for traffic? No

This is why systems often distinguish between:

Liveness
"Should this process be restarted?"

Readiness
"Should this instance receive traffic?"

For a deeper explanation, see Health Checks, Readiness, and Liveness Probes.

Database Availability

Application servers are relatively easy to duplicate because they can often remain stateless.

Databases are harder because they contain persistent state.

A single database creates a major availability risk:

Applications
     │
     ▼
 Database
     ✗

If it fails, every application instance may become useless.

Replication

Replication maintains additional copies of data.

Primary Database
       │
       ├──► Replica 1
       └──► Replica 2

The primary handles writes while replicas receive replicated changes.

If one replica fails, another copy still exists.

Some architectures also use replicas for read traffic:

Writes ─────────► Primary
                    │
             replication
              ┌─────┴─────┐
              ▼           ▼
          Replica 1   Replica 2
              ▲           ▲
              │           │
             Reads       Reads

Replication improves availability, but it introduces consistency and failover questions.

For example, a replica may be slightly behind the primary.

Primary:
order #101 exists

Replica:
order #101 not replicated yet

The architecture must decide whether this temporary inconsistency is acceptable for a particular request.

Failover

When the primary database fails, a replica may be promoted.

Before:

Primary ✓
Replica A ✓
Replica B ✓

Then:

Primary ✗
Replica A ✓
Replica B ✓

The system promotes Replica A:

Replica A
   ↓
New Primary

Applications then reconnect to the new primary.

This process is called failover.

Availability depends heavily on how quickly and safely failover happens.

Failure detection:     5 seconds
Leader promotion:      10 seconds
Client reconnect:      5 seconds

Approximate impact:
20 seconds

A poorly configured failover system can take minutes or require manual intervention.

Multi-Zone Deployment

Running multiple servers does not help much if every server is in the same failure domain.

Consider three application servers in one data center:

Data Center A
├── App 1
├── App 2
└── App 3

If the entire data center loses power, all three disappear together.

A better design distributes instances across independent zones:

Zone A
├── App 1
└── DB Replica

Zone B
├── App 2
└── DB Replica

Zone C
├── App 3
└── DB Replica

If Zone B fails:

Zone A ✓
Zone B ✗
Zone C ✓

traffic continues through the remaining zones.

This is an important principle:

Redundant components should not share the same failure domain.

Examples of shared failure domains include:

same physical server
same rack
same power supply
same network switch
same availability zone
same cloud region

Multi-Region Availability

For systems with very high availability requirements, one cloud region may not be enough.

A multi-region architecture may look like:

                Global Traffic Router
                    /           \
                   /             \
                  ▼               ▼
             US Region        EU Region
             ├── Apps         ├── Apps
             ├── Cache        ├── Cache
             └── Database     └── Database

If the US region becomes unavailable:

US Region ✗
EU Region ✓

traffic may be redirected to Europe.

This sounds simple, but multi-region systems are significantly more complicated than multi-zone systems.

Questions immediately appear:

Where do writes go?
How is data replicated?
Can both regions accept writes?
What happens during network partition?
How quickly does DNS or routing change?
How much replication lag is acceptable?

There are two common high-level approaches.

Active-passive:

Region A → active
Region B → standby

If Region A fails, Region B takes over.

Active-active:

Region A → serving traffic
Region B → serving traffic

Both regions are active simultaneously.

Active-active can provide excellent availability and lower geographic latency, but conflict resolution and data consistency become much harder.

For a deeper discussion, see Multi-Region Architecture and Disaster Recovery.

Graceful Degradation

A highly available system does not always require every feature to be available.

Consider a product page:

Product details
Price
Inventory
Reviews
Recommendations
Recently viewed

The recommendation service fails.

One design returns:

500 Internal Server Error

The entire page is unavailable.

A better design returns:

Product details ✓
Price ✓
Inventory ✓
Reviews ✓
Recommendations unavailable
Recently viewed ✓

The important functionality remains available.

This is graceful degradation.

More examples:

Personalization fails
→ show popular content

Search reranker fails
→ return basic search ranking

Analytics fails
→ continue checkout and buffer events

Image optimization fails
→ serve original image

Reviews fail
→ show product without reviews

The key is deciding which features are critical and which are optional.

For more detail, see Designing Graceful Degradation Strategies.

Handling Dependency Failures

A system may be healthy while one dependency is unhealthy.

Consider:

Checkout Service
      │
      ├──► Inventory
      ├──► Payment
      ├──► Tax Service
      └──► Analytics

If Analytics becomes slow and every checkout request waits 30 seconds for it, Analytics can indirectly make Checkout unavailable.

This is why remote calls need protection.

Common techniques include:

Timeouts
Retries
Exponential backoff
Circuit breakers
Bulkheads
Fallbacks
```

For example:


Analytics call
   ↓
timeout after 300 ms
   ↓
store event locally or queue it
   ↓
checkout continues

A non-critical dependency should not normally be allowed to take down a critical path.


For more detail, see Timeouts, Retries, and Exponential Backoff.


Availability During Deployments


Deployments are a common source of downtime.


Consider one server:


Old Version
    │
   stop
    │
    ▼
deploy
    │
    ▼
New Version

During the replacement, the application may be unavailable.


With multiple instances, deployments can happen gradually.


Before:

App 1 → v1
App 2 → v1
App 3 → v1

Update one instance:


App 1 → v2
App 2 → v1
App 3 → v1

If App 1 passes health checks:


App 1 → v2
App 2 → v2
App 3 → v1

Eventually:


App 1 → v2
App 2 → v2
App 3 → v2

This is the basic idea behind a rolling deployment.


Another approach is blue-green deployment:


Blue Environment
Version 1
Serving traffic

Green Environment
Version 2
Ready but idle

After validation:


Traffic
   │
   ▼
Green Environment
Version 2

If Version 2 is broken, traffic can often be moved back quickly.


Availability must therefore include deployment strategy, not only runtime infrastructure.


Capacity and Traffic Spikes


A system can become unavailable even when nothing technically fails.


It may simply receive more traffic than it can handle.


Suppose the application supports:


Maximum:
10,000 requests/sec

Then a promotion creates:


Incoming:
25,000 requests/sec

Possible result:


CPU → 100%
Latency → increases
Timeouts → increase
Retries → increase
Load → increases further
Service → unavailable

This is why high availability requires capacity planning.


One strategy is keeping headroom:


Maximum capacity: 20,000 req/sec

Normal traffic:   10,000 req/sec

Headroom:         10,000 req/sec

Another is autoscaling:


Low traffic:
3 app instances

Traffic rises:
6 app instances

Large spike:
12 app instances

But autoscaling takes time, so it should not be the only protection.


Other mechanisms may include:


Rate limiting
Load shedding
Queues
Caching
Backpressure
Admission control

If a system cannot serve every request, intentionally rejecting some low-priority work may protect critical functionality.


Monitoring Availability


Availability needs to be measured from the user's perspective.


CPU utilization alone does not tell whether users can successfully use the system.


Useful availability indicators include:


Successful request percentage
5xx error rate
Timeout rate
Dependency failure rate
Health-check failures
Failover events
Request latency
Synthetic probe success
```

For example:


Requests: 1,000,000
Successful: 999,500
Failed: 500

The successful-request availability is:


999,500 / 1,000,000
=
99.95%

But the exact definition matters.


Should a 400 validation error count as downtime? Usually not.


Should a request that returns HTTP 200 but contains unusable data count as successful? Maybe not.


A meaningful availability SLI should reflect whether users received a valid service result.


For example:


SLI:
Percentage of valid checkout requests
completed successfully within 2 seconds

An SLO might then be:


99.95% over 30 days

This is more useful than simply measuring whether application processes are alive.


Example: Making an Online Store Highly Available


Consider an online store starting with:


Users
  │
  ▼
Application
  │
  ▼
Database

Stage 1: Remove the application single point of failure.


                    ┌──► App 1
Users ──► Load Balancer
                    ├──► App 2
                    └──► App 3

If one application instance fails, traffic continues.


Stage 2: Distribute instances across zones.


Zone A → App 1
Zone B → App 2
Zone C → App 3

A single-zone outage no longer removes the whole application tier.


Stage 3: Replicate the database.


Primary DB
   │
   ├──► Replica A
   └──► Replica B

If the primary fails, a replica can take over.


Stage 4: Protect dependency calls.


Product Service
     │
     ├──► Inventory
     ├──► Reviews
     └──► Recommendations

Each dependency gets timeouts and failure handling.


Recommendations become optional:


Recommendations fail
        ↓
Product page still available

Stage 5: Move non-critical work off the request path.


Checkout
   │
   ├──► Save Order
   │
   └──► Queue
          │
          ├──► Email
          ├──► Analytics
          └──► CRM

An analytics outage no longer blocks checkout.


Stage 6: Make deployments zero-downtime.


App 1 → v2
App 2 → v1
App 3 → v1

then gradually replace remaining instances

Stage 7: Add capacity protection.


Load Balancer
    │
    ├──► App instances
    │       +
    │    autoscaling
    │
    ├──► Cache
    │
    └──► Rate limiting

Stage 8: Add regional disaster recovery if required.


                   Global Router
                     /       \
                    ▼         ▼
               Region A    Region B
```

The final design may look like:


                         Global DNS / Router
                                 │
                                 ▼
                          Load Balancer
                                 │
                 ┌───────────────┼───────────────┐
                 ▼               ▼               ▼
              Zone A          Zone B          Zone C
              App 1           App 2           App 3
                 \               │               /
                  \              │              /
                   └─────────────┼─────────────┘
                                 │
                        ┌────────┼────────┐
                        ▼        ▼        ▼
                      Cache   Database   Queue
                               │          │
                         ┌─────┴─────┐    ├──► Email
                         ▼           ▼    ├──► Analytics
                      Replica     Replica └──► CRM
```

The important idea is not that every system needs this architecture.


The important idea is that every important failure has a planned response.


App fails
→ another app serves traffic

Zone fails
→ other zones continue

Database primary fails
→ replica takes over

Optional dependency fails
→ feature degrades

Traffic spikes
→ scale or shed load

Bad deployment
→ stop rollout or roll back

Common Availability Mistakes


Assuming Multiple Instances Automatically Mean High Availability


Three application instances in the same failure domain can still fail together.


One zone
├── App 1
├── App 2
└── App 3
```

A zone failure removes all three.


Availability depends on independent failure domains, not simply instance count.


Ignoring the Database


This architecture:


20 Application Servers
         │
         ▼
    One Database
```

still contains an obvious single point of failure.


The full dependency chain must be evaluated.


Using Weak Health Checks


A process returning HTTP 200 from /health does not prove that it can serve real traffic.


Process running ✓
Connection pool exhausted ✗
```

Readiness checks should represent actual serving capability without becoming so complex that the check itself becomes unstable.


Using Aggressive Retries During Outages


Retries can reduce availability when a dependency is overloaded.


10,000 requests/sec
×
3 retries

potentially 40,000 attempts/sec
```

Retries should be bounded and combined with backoff, jitter, timeouts, and circuit breaking.


Running Without Headroom


If normal traffic already consumes 95% of system capacity, one failed instance can overload the remaining instances.


Normal:
3 servers × 80% load

One server fails:
2 servers must absorb everything

Result:
120% required capacity
```

Redundancy without spare capacity may not survive actual failures.


Measuring Process Uptime Instead of Service Availability


A server may be running while users receive errors.


Process uptime: 100%
Successful checkouts: 92%
```

The second number is far more important for the user.


Production Checklist


  • Define availability SLIs and SLOs based on user-visible success.
  • Identify critical single points of failure.
  • Run critical application components redundantly.
  • Distribute replicas across independent failure domains.
  • Use health checks and automatic traffic removal.
  • Design database replication and failover.
  • Use timeouts for remote dependencies.
  • Apply retries carefully with backoff and jitter.
  • Degrade optional functionality instead of failing entire requests.
  • Use queues for non-critical asynchronous work.
  • Deploy gradually with health validation and rollback capability.
  • Keep capacity headroom for traffic spikes and instance failures.
  • Test failover instead of assuming it works.
  • Monitor availability from the user's perspective.

Conclusion


Availability means keeping a software service usable when users need it.


High availability does not require eliminating every failure. That is unrealistic.


Instead, the architecture prevents individual failures from becoming complete outages.


Component fails
      ↓
Detect failure
      ↓
Route around it
      ↓
Degrade if necessary
      ↓
Recover
      ↓
Return to full capacity

Redundancy, load balancing, health checks, replication, failover, timeouts, graceful degradation, safe deployments, and capacity planning all contribute to availability.


Key Takeaway: high availability is achieved when the system can lose components without losing the service.





            

Author

Alex Snowgirl

Alex Snowgirl

Nov 09, 2025 3 25
Enjoyed this article?

Support Alex Snowgirl

Buy me a coffee

This helps Alex Snowgirl continue creating useful content

Related articles

Comments (0)