Hello! Welcome to your next lesson in the "Core Infrastructure Components" module.
In our last lesson on API Gateways, we established that a key function of a gateway or load balancer is to route traffic to healthy service instances. This naturally leads to a critical question: how does any part of the system actually know which instances are healthy and which are not?
Today, we'll answer that question directly. Your learning outcome is to design health checking mechanisms for service reliability and discovery. This is a fundamental concept in building resilient distributed systems and a topic you can expect to discuss in any senior-level system design interview.
We will explore:
- The basic principles and mechanisms of health checking, like heartbeats.
- The critical distinction between different types of checks, such as liveness vs. readiness probes.
- The difference between shallow and deep health checks.
- An advanced concept for senior engineers: treating health as a spectrum rather than a binary state to enable graceful degradation.
1. The Fundamentals: Why and How We Check Health
In any distributed system, individual components will inevitably fail. A robust system isn't one that never fails, but one that can detect failures and recover from them automatically. Health checks are the sensory nervous system that makes this auto-recovery possible.
At its core, a health check is a signal that a service instance provides to prove it's alive and functioning correctly. Let's start with a video that introduces this core concept, including the problem of "zombie" servers that appear alive but are non-functional.
Service discovery and heartbeats in micro-services 👍📈
This video from Gaurav Sen provides an excellent introduction to the necessity of health checks. It covers the basic 'pull' model, where a health service polls other services, and introduces the concept of a 'push' or heartbeat model.
Please watch from 01:02 to 04:30. Pay close attention to: The role of a central 'Health Service'. How it interacts with a load balancer to remove dead servers from rotation. The 'zombie server' problem and how a two-way heartbeat helps mitigate it.
The video introduces two primary communication patterns for health checks:
- Pull Model (Active Polling): A central monitor (like a load balancer or a dedicated health check service) periodically sends a request ("Are you alive?") to each service instance and waits for a response. This is also called an active health check.
- Push Model (Heartbeating): Each service instance periodically sends a "I'm alive" message (a heartbeat) to a central monitor or service registry.
To formalize this and explore the trade-offs, let's look at a quick reading.
Day 45: System Design Concept: Heart-Beats and Health-Checks
This article, 'Day 45: System Design Concept: Heart-Beats and Health-Checks', provides clear definitions and trade-offs for different heartbeat mechanisms.
Read the section 'Heartbeat Mechanisms: The Communication Pattern'. It covers Push-Based, Pull-Based, and Heartbeat with Acknowledgment patterns. Focus on the advantages and challenges of each.
In an interview, being able to articulate these patterns and their trade-offs (e.g., push model's faster detection vs. pull model's lower network overhead) is a good starting point.
2. A Granular View: Liveness, Readiness, and Startup Probes
A simple "is it alive?" check is often insufficient. A service might be alive (the process is running) but temporarily unable to handle traffic because it's still starting up or currently overloaded. Sending traffic to it would result in errors.
Modern container orchestration systems like Kubernetes have popularized a more nuanced approach with three distinct types of checks, or "probes."
Kubernetes Health Checks: Liveness vs. Readiness vs. Startup Probe
This short video from Anton Putra clearly explains the three essential Kubernetes health probes. Understanding these is crucial as they represent industry-standard best practices.
Please watch from 00:13 to 02:09. Focus on the distinct purpose of each probe: Liveness: Is the application running or deadlocked? (Action: Restart the container). Readiness: Is the application ready to accept new traffic? (Action: Remove from service endpoint). Startup: For slow-starting apps, to delay the other probes.
These probes are typically implemented as HTTP endpoints exposed by your service. For example:
GET /health/live: Checks for liveness. Should be a simple, fast check that doesn't depend on external services. A200 OKmeans it's alive.GET /health/ready: Checks for readiness. This might involve checking connections to databases or other critical dependencies. A200 OKmeans it's ready for traffic.
This separation allows an orchestrator to make intelligent decisions:
- If the liveness probe fails, the application is broken and must be restarted.
- If the readiness probe fails, the application is just temporarily busy or not yet initialized. We should stop sending it new traffic but let it continue running to recover.
3. Deeper Insight: Shallow vs. Deep Health Checks
The concept of liveness vs. readiness leads us to another key distinction: shallow vs. deep health checks.
- Shallow Health Check: Verifies only the health of the service instance itself. Is the web server responding? This is typically what a
livenessprobe does. - Deep Health Check: Verifies the health of the service and its critical dependencies. Can it connect to the database? Can it reach the caching layer? This is what a
readinessprobe often does.
The diagram below provides a great overview of the different aspects a deep health check might cover.
Design Trade-off:
While deep health checks provide a more accurate signal, they come with risks. If a shared dependency (like a database) fails, it could cause the readiness probes for all dependent services to fail simultaneously. This could lead to a cascading failure where the load balancer takes all instances out of rotation, causing a complete outage.
A common strategy is to keep liveness probes shallow and use readiness probes for deep checks, while also implementing other patterns like circuit breakers (which we'll cover in the next lesson) to handle dependency failures more gracefully.
4. Integration with Service Discovery and Load Balancing
So, how does this all tie together? Health information is the critical data that fuels service discovery and load balancing.

The flow is as follows:
- A service instance starts up.
- It registers itself with a Service Registry (e.g., Consul, Eureka, or Kubernetes's built-in service discovery).
- The service begins sending heartbeats (push model), or the registry begins polling its health endpoint (pull model).
- The Load Balancer or API Gateway queries the Service Registry to get a list of healthy instances to which it can route traffic.
- If a health check fails, the Service Registry removes the instance from its list of available endpoints. The Load Balancer gets the updated list and stops sending traffic to the failed instance.
5. For Senior Engineers: Health is a Spectrum
Thinking of health as a binary (up/down) is a good start, but for a senior role, you need to demonstrate a more sophisticated understanding. In reality, a service isn't just "up" or "down"; it can be in various states of degradation. It might be "up" but so overloaded that its response time is unacceptably high.
This is where the concept of treating health as a spectrum and enabling graceful degradation comes in.
Health Checks and Graceful Degradation in Distributed Systems
This article from Cindy Sridharan is a classic read for senior engineers. It argues that health is a spectrum and introduces the idea of dynamic feedback loops for load balancing and backpressure.
This is a more conceptual read. Please focus on these two sections: 'Health is a Spectrum, not a Binary Taxonomy': Absorb the core argument that a simple up/down status is insufficient and can be misleading. 'The Need for Feedback Loops when applying Backpressure': Pay close attention to the HAProxy agent-check example. This is a concrete mechanism where a service can dynamically report its current capacity (e.g., 'I am at 75% load, reduce traffic' or 'set my max connections to 30').
The key takeaway is that an advanced health checking mechanism shouldn't just return 200 OK. It could return a rich payload that communicates the service's current state, such as:
- Current request queue depth.
- p99 latency.
- A calculated "capacity" score.
The load balancer can then use this rich information to make much smarter routing decisions, like sending less traffic to a struggling instance instead of cutting it off entirely. Discussing this concept of dynamic feedback and graceful degradation is an excellent way to demonstrate senior-level thinking in an interview.
Conclusion
In this lesson, you've learned how to design the sensory system of a distributed application. You can now articulate the mechanisms that ensure reliability and enable effective service discovery.
Key Takeaways:
- Purpose: Health checks are for automated failure detection, enabling system reliability and recovery.
- Mechanisms: The fundamental patterns are pull (polling) and push (heartbeating).
- Probe Types: For precise control, use liveness (is it alive?), readiness (can it take traffic?), and startup probes.
- Depth: Differentiate between shallow (self-contained) and deep (checks dependencies) health checks, and understand the trade-offs.
- Senior-Level Insight: Health is a spectrum, not a binary. Advanced systems use dynamic feedback loops to allow services to report their capacity, enabling graceful degradation and intelligent load balancing.
Next Lesson Preview:
Detecting an unhealthy service is the first step. The next step is to protect the rest of the system from the failure. When a downstream service is failing, we don't want clients to waste resources by repeatedly calling it. In our next lesson, we will cover a client-side resilience pattern that addresses this exact problem: applying the circuit breaker pattern to prevent cascading failures.