
Reliability Engineering
- 55 installs
- 19 repo stars
- Updated January 20, 2026
- miles990/claude-software-skills
Helps with ai & agent building tasks.
About
reliability-engineering is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted development.
- reliability-engineering
- AI & Agent Building
- AI-coding skill
Reliability Engineering by the numbers
- 55 all-time installs (skills.sh)
- +1 installs in the week ending Aug 4, 2026 (Skillselion tracking)
- Ranked #6,846 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 4, 2026 (Skillselion catalog sync)
npx skills add https://github.com/miles990/claude-software-skills --skill reliability-engineeringAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 55 |
|---|---|
| repo stars | ★ 19 |
| Last updated | January 20, 2026 |
| Repository | miles990/claude-software-skills ↗ |
What it does
Helps with ai & agent building tasks.
Files
Reliability Engineering
Overview
Site Reliability Engineering (SRE) practices for building and maintaining reliable systems.
---
SLI / SLO / SLA
Definitions
| Term | Definition | Example |
|---|---|---|
| SLI | Service Level Indicator (metric) | Request latency, error rate |
| SLO | Service Level Objective (target) | 99.9% availability |
| SLA | Service Level Agreement (contract) | Refund if < 99.5% |
Common SLIs
# Availability SLI
availability:
definition: "Successful requests / Total requests"
good_events: "HTTP status < 500"
total_events: "All HTTP requests"
# Latency SLI
latency:
definition: "Requests faster than threshold / Total requests"
thresholds:
- p50: 100ms
- p95: 500ms
- p99: 1000ms
# Error Rate SLI
error_rate:
definition: "Failed requests / Total requests"
bad_events: "HTTP 5xx responses"
# Throughput SLI
throughput:
definition: "Requests processed per second"
target: "> 1000 RPS"Error Budget
// Error budget calculation
const SLO = 0.999; // 99.9% availability
const PERIOD = 30; // 30 days
const totalMinutes = PERIOD * 24 * 60; // 43,200 minutes
const errorBudgetMinutes = totalMinutes * (1 - SLO); // 43.2 minutes
// Track error budget consumption
class ErrorBudget {
private consumedMinutes = 0;
private readonly budgetMinutes: number;
constructor(slo: number, periodDays: number) {
const totalMinutes = periodDays * 24 * 60;
this.budgetMinutes = totalMinutes * (1 - slo);
}
recordOutage(durationMinutes: number) {
this.consumedMinutes += durationMinutes;
}
get remaining(): number {
return this.budgetMinutes - this.consumedMinutes;
}
get percentConsumed(): number {
return (this.consumedMinutes / this.budgetMinutes) * 100;
}
get isExhausted(): boolean {
return this.remaining <= 0;
}
}---
Observability
Three Pillars
┌─────────────────────────────────────────────────────────────┐
│ Observability │
├───────────────────┬───────────────────┬────────────────────┤
│ Metrics │ Logs │ Traces │
├───────────────────┼───────────────────┼────────────────────┤
│ - Counters │ - Structured │ - Distributed │
│ - Gauges │ - Contextual │ - Request flow │
│ - Histograms │ - Searchable │ - Latency breakdown│
│ - Aggregated │ - High volume │ - Service deps │
└───────────────────┴───────────────────┴────────────────────┘Metrics with Prometheus
import { Counter, Histogram, Gauge, register } from 'prom-client';
// Counter - monotonically increasing
const httpRequestsTotal = new Counter({
name: 'http_requests_total',
help: 'Total HTTP requests',
labelNames: ['method', 'path', 'status']
});
// Histogram - distribution of values
const httpRequestDuration = new Histogram({
name: 'http_request_duration_seconds',
help: 'HTTP request duration',
labelNames: ['method', 'path'],
buckets: [0.01, 0.05, 0.1, 0.5, 1, 5]
});
// Gauge - can go up or down
const activeConnections = new Gauge({
name: 'active_connections',
help: 'Number of active connections'
});
// Middleware
app.use((req, res, next) => {
const start = Date.now();
res.on('finish', () => {
const duration = (Date.now() - start) / 1000;
httpRequestsTotal
.labels(req.method, req.path, res.statusCode.toString())
.inc();
httpRequestDuration
.labels(req.method, req.path)
.observe(duration);
});
next();
});
// Expose metrics endpoint
app.get('/metrics', async (req, res) => {
res.set('Content-Type', register.contentType);
res.end(await register.metrics());
});Structured Logging
import pino from 'pino';
const logger = pino({
level: process.env.LOG_LEVEL || 'info',
formatters: {
level: (label) => ({ level: label })
}
});
// Add request context
function createRequestLogger(req: Request) {
return logger.child({
requestId: req.headers['x-request-id'] || crypto.randomUUID(),
userId: req.user?.id,
path: req.path,
method: req.method
});
}
// Usage
app.use((req, res, next) => {
req.log = createRequestLogger(req);
next();
});
app.get('/api/users/:id', async (req, res) => {
req.log.info({ userId: req.params.id }, 'Fetching user');
try {
const user = await getUser(req.params.id);
req.log.info({ user: user.id }, 'User found');
res.json(user);
} catch (error) {
req.log.error({ error }, 'Failed to fetch user');
res.status(500).json({ error: 'Internal error' });
}
});Distributed Tracing
import { trace, SpanStatusCode } from '@opentelemetry/api';
const tracer = trace.getTracer('my-service');
async function processOrder(orderId: string) {
return tracer.startActiveSpan('processOrder', async (span) => {
span.setAttribute('order.id', orderId);
try {
// Child span for database
await tracer.startActiveSpan('db.getOrder', async (dbSpan) => {
const order = await db.orders.findById(orderId);
dbSpan.setAttribute('order.items', order.items.length);
dbSpan.end();
return order;
});
// Child span for external API
await tracer.startActiveSpan('payment.process', async (paymentSpan) => {
paymentSpan.setAttribute('payment.provider', 'stripe');
await paymentService.charge(order);
paymentSpan.end();
});
span.setStatus({ code: SpanStatusCode.OK });
} catch (error) {
span.setStatus({
code: SpanStatusCode.ERROR,
message: error.message
});
span.recordException(error);
throw error;
} finally {
span.end();
}
});
}---
Incident Management
Incident Response Process
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ Detect │ → │ Triage │ → │ Mitigate │ → │ Resolve │ → │ Review │
└──────────┘ └──────────┘ └──────────┘ └──────────┘ └──────────┘
│ │ │ │ │
Alerts Severity Stop bleeding Root cause Postmortem
Monitors On-call Rollback Fix issue Learnings
Reports Escalate Communicate Deploy fix Action itemsIncident Severity Levels
| Severity | Impact | Response Time | Example |
|---|---|---|---|
| SEV1 | Complete outage | Immediate | Site down |
| SEV2 | Major degradation | < 15 min | Payment failures |
| SEV3 | Minor impact | < 1 hour | Slow performance |
| SEV4 | Minimal impact | Next business day | Minor bug |
Runbook Template
# Runbook: Database Connection Failures
## Symptoms
- Error logs: "ECONNREFUSED" or "Connection timeout"
- Metric: `db_connection_errors` > 10/min
- Alert: "Database connectivity degraded"
## Impact
- User requests failing
- Data writes not persisting
## Diagnosis Steps
1. Check database statuskubectl get pods -l app=postgres psql -h $DB_HOST -U $DB_USER -c "SELECT 1"
2. Check connection poolcurl localhost:8080/metrics | grep db_pool
3. Check network connectivitync -zv $DB_HOST 5432
## Mitigation
### If database is down:
1. Check pod logs: `kubectl logs -l app=postgres`
2. Restart if necessary: `kubectl rollout restart deployment/postgres`
### If connection pool exhausted:
1. Scale up application: `kubectl scale deployment/api --replicas=5`
2. Increase pool size in config
## Escalation
- Primary: @database-team
- Secondary: @platform-team
- After hours: PagerDutyPostmortem Template
# Postmortem: Payment Service Outage
**Date**: 2024-01-15
**Duration**: 45 minutes (14:30 - 15:15 UTC)
**Severity**: SEV1
**Author**: Jane Smith
## Summary
Payment processing was unavailable for 45 minutes due to
database connection pool exhaustion.
## Impact
- 2,500 failed transactions
- $150,000 in delayed revenue
- 500 customer support tickets
## Timeline
- 14:30 - Alert fired: "Payment error rate > 5%"
- 14:32 - On-call engineer acknowledged
- 14:35 - Identified DB connection errors in logs
- 14:45 - Root cause identified: connection leak
- 15:00 - Deployed hotfix
- 15:15 - Service fully recovered
## Root Cause
A code change introduced a connection leak where connections
were not returned to the pool after timeout errors.
## What Went Well
- Alerts fired promptly
- Team mobilized quickly
- Clear escalation path
## What Went Poorly
- Took 10 minutes to identify root cause
- No runbook for this specific scenario
- Connection pool metrics not in dashboard
## Action Items
- [ ] Add connection pool metrics to dashboard (Owner: Bob, Due: 2024-01-22)
- [ ] Create runbook for DB connection issues (Owner: Jane, Due: 2024-01-25)
- [ ] Add integration test for connection handling (Owner: Alice, Due: 2024-01-29)
- [ ] Review all DB connection code paths (Owner: Team, Due: 2024-02-01)
## Lessons Learned
Connection pool exhaustion can cascade quickly. Need better
visibility into pool utilization and earlier alerting.---
Chaos Engineering
Principles
1. Start with a hypothesis about steady state
2. Introduce realistic failures
3. Run experiments in production (carefully)
4. Minimize blast radius
5. Learn and improveChaos Experiments
// Chaos Monkey - Random instance termination
class ChaosMonkey {
async run() {
const instances = await getRunningInstances();
const victim = instances[Math.floor(Math.random() * instances.length)];
console.log(`Terminating instance: ${victim.id}`);
await terminateInstance(victim.id);
}
}
// Latency injection
function withLatencyChaos(fn: Function, config: ChaosConfig) {
return async (...args: any[]) => {
if (config.enabled && Math.random() < config.probability) {
const delay = config.minLatency +
Math.random() * (config.maxLatency - config.minLatency);
await sleep(delay);
}
return fn(...args);
};
}
// Error injection
function withErrorChaos(fn: Function, config: ChaosConfig) {
return async (...args: any[]) => {
if (config.enabled && Math.random() < config.probability) {
throw new Error('Chaos: Injected failure');
}
return fn(...args);
};
}---
Disaster Recovery
Recovery Objectives
| Metric | Definition | Example |
|---|---|---|
| RTO | Recovery Time Objective | 4 hours |
| RPO | Recovery Point Objective | 1 hour (max data loss) |
Backup Strategy
# 3-2-1 Backup Rule
# 3 copies of data
# 2 different storage types
# 1 offsite location
backup_strategy:
primary:
type: "continuous replication"
location: "us-east-1"
retention: "7 days"
secondary:
type: "daily snapshots"
location: "us-west-2"
retention: "30 days"
tertiary:
type: "weekly archives"
location: "S3 Glacier"
retention: "1 year"
testing:
frequency: "monthly"
procedure: "restore to staging"---
Related Skills
- [[monitoring-observability]] - Detailed monitoring
- [[devops-cicd]] - Deployment reliability
- [[system-design]] - Designing for reliability
/**
* Circuit Breaker Template
* Usage: Protect services from cascading failures
*/
// ===========================================
// Types
// ===========================================
export type CircuitState = 'closed' | 'open' | 'half-open';
export interface CircuitBreakerOptions {
/** Failure threshold before opening (default: 5) */
failureThreshold: number;
/** Success threshold to close from half-open (default: 2) */
successThreshold: number;
/** Time before attempting recovery in ms (default: 30000) */
resetTimeout: number;
/** Request timeout in ms (default: 10000) */
timeout: number;
/** Monitor window in ms (default: 60000) */
monitorWindow: number;
/** Failure rate threshold percentage (default: 50) */
failureRateThreshold: number;
/** Minimum requests before evaluating failure rate */
minimumRequests: number;
}
export interface CircuitBreakerStats {
state: CircuitState;
failures: number;
successes: number;
totalRequests: number;
failureRate: number;
lastFailure?: Date;
lastSuccess?: Date;
lastStateChange: Date;
}
// ===========================================
// Circuit Breaker Implementation
// ===========================================
export class CircuitBreaker {
private state: CircuitState = 'closed';
private failures: number = 0;
private successes: number = 0;
private lastFailureTime?: number;
private lastStateChange: number = Date.now();
private requestLog: Array<{ timestamp: number; success: boolean }> = [];
private options: CircuitBreakerOptions;
constructor(options: Partial<CircuitBreakerOptions> = {}) {
this.options = {
failureThreshold: 5,
successThreshold: 2,
resetTimeout: 30000,
timeout: 10000,
monitorWindow: 60000,
failureRateThreshold: 50,
minimumRequests: 10,
...options,
};
}
/**
* Execute function through circuit breaker
*/
async execute<T>(fn: () => Promise<T>): Promise<T> {
if (this.state === 'open') {
if (this.shouldAttemptReset()) {
this.transitionTo('half-open');
} else {
throw new CircuitOpenError('Circuit breaker is open');
}
}
try {
const result = await this.executeWithTimeout(fn);
this.onSuccess();
return result;
} catch (error) {
this.onFailure();
throw error;
}
}
/**
* Get current stats
*/
getStats(): CircuitBreakerStats {
this.pruneRequestLog();
const totalRequests = this.requestLog.length;
const failures = this.requestLog.filter(r => !r.success).length;
return {
state: this.state,
failures: this.failures,
successes: this.successes,
totalRequests,
failureRate: totalRequests > 0 ? (failures / totalRequests) * 100 : 0,
lastFailure: this.lastFailureTime ? new Date(this.lastFailureTime) : undefined,
lastSuccess: this.successes > 0 ? new Date() : undefined,
lastStateChange: new Date(this.lastStateChange),
};
}
/**
* Manually reset circuit
*/
reset(): void {
this.state = 'closed';
this.failures = 0;
this.successes = 0;
this.lastFailureTime = undefined;
this.requestLog = [];
this.lastStateChange = Date.now();
}
/**
* Get current state
*/
getState(): CircuitState {
return this.state;
}
// ===========================================
// Private Methods
// ===========================================
private async executeWithTimeout<T>(fn: () => Promise<T>): Promise<T> {
return Promise.race([
fn(),
new Promise<never>((_, reject) => {
setTimeout(
() => reject(new TimeoutError('Request timed out')),
this.options.timeout
);
}),
]);
}
private onSuccess(): void {
this.recordRequest(true);
if (this.state === 'half-open') {
this.successes++;
if (this.successes >= this.options.successThreshold) {
this.transitionTo('closed');
}
} else {
this.failures = 0;
}
}
private onFailure(): void {
this.recordRequest(false);
this.lastFailureTime = Date.now();
this.failures++;
if (this.state === 'half-open') {
this.transitionTo('open');
} else if (this.state === 'closed') {
if (this.shouldOpen()) {
this.transitionTo('open');
}
}
}
private shouldOpen(): boolean {
// Check absolute failure threshold
if (this.failures >= this.options.failureThreshold) {
return true;
}
// Check failure rate
this.pruneRequestLog();
if (this.requestLog.length >= this.options.minimumRequests) {
const failures = this.requestLog.filter(r => !r.success).length;
const failureRate = (failures / this.requestLog.length) * 100;
if (failureRate >= this.options.failureRateThreshold) {
return true;
}
}
return false;
}
private shouldAttemptReset(): boolean {
return Date.now() - this.lastStateChange >= this.options.resetTimeout;
}
private transitionTo(newState: CircuitState): void {
console.log(`Circuit breaker: ${this.state} -> ${newState}`);
this.state = newState;
this.lastStateChange = Date.now();
if (newState === 'half-open') {
this.successes = 0;
} else if (newState === 'closed') {
this.failures = 0;
this.successes = 0;
}
}
private recordRequest(success: boolean): void {
this.requestLog.push({ timestamp: Date.now(), success });
this.pruneRequestLog();
}
private pruneRequestLog(): void {
const cutoff = Date.now() - this.options.monitorWindow;
this.requestLog = this.requestLog.filter(r => r.timestamp > cutoff);
}
}
// ===========================================
// Error Classes
// ===========================================
export class CircuitOpenError extends Error {
constructor(message: string) {
super(message);
this.name = 'CircuitOpenError';
}
}
export class TimeoutError extends Error {
constructor(message: string) {
super(message);
this.name = 'TimeoutError';
}
}
// ===========================================
// Circuit Breaker Registry
// ===========================================
export class CircuitBreakerRegistry {
private breakers: Map<string, CircuitBreaker> = new Map();
get(name: string, options?: Partial<CircuitBreakerOptions>): CircuitBreaker {
if (!this.breakers.has(name)) {
this.breakers.set(name, new CircuitBreaker(options));
}
return this.breakers.get(name)!;
}
getAll(): Map<string, CircuitBreaker> {
return new Map(this.breakers);
}
getAllStats(): Record<string, CircuitBreakerStats> {
const stats: Record<string, CircuitBreakerStats> = {};
for (const [name, breaker] of this.breakers) {
stats[name] = breaker.getStats();
}
return stats;
}
reset(name: string): void {
this.breakers.get(name)?.reset();
}
resetAll(): void {
for (const breaker of this.breakers.values()) {
breaker.reset();
}
}
}
// ===========================================
// Usage Example
// ===========================================
/*
import { CircuitBreaker, CircuitBreakerRegistry, CircuitOpenError } from './circuit-breaker';
// Single circuit breaker
const circuitBreaker = new CircuitBreaker({
failureThreshold: 5,
resetTimeout: 30000,
timeout: 5000,
});
async function callExternalService() {
try {
const result = await circuitBreaker.execute(async () => {
const response = await fetch('https://api.example.com/data');
if (!response.ok) throw new Error('API error');
return response.json();
});
return result;
} catch (error) {
if (error instanceof CircuitOpenError) {
// Return cached/fallback data
return getCachedData();
}
throw error;
}
}
// Multiple circuit breakers with registry
const registry = new CircuitBreakerRegistry();
const userServiceBreaker = registry.get('user-service', { failureThreshold: 3 });
const paymentServiceBreaker = registry.get('payment-service', { failureThreshold: 2 });
// Get all stats
console.log(registry.getAllStats());
*/
export default CircuitBreaker;
/**
* Health Check Template
* Usage: Implement health endpoints for your service
*/
// ===========================================
// Types
// ===========================================
export type HealthStatus = 'healthy' | 'degraded' | 'unhealthy';
export interface HealthCheckResult {
name: string;
status: HealthStatus;
latency?: number;
message?: string;
metadata?: Record<string, unknown>;
}
export interface HealthReport {
status: HealthStatus;
timestamp: string;
version: string;
uptime: number;
checks: HealthCheckResult[];
}
export type HealthCheck = () => Promise<HealthCheckResult>;
// ===========================================
// Health Check Manager
// ===========================================
export class HealthCheckManager {
private checks: Map<string, HealthCheck> = new Map();
private startTime: number = Date.now();
private version: string;
constructor(version: string = '1.0.0') {
this.version = version;
}
/**
* Register a health check
*/
register(name: string, check: HealthCheck): void {
this.checks.set(name, check);
}
/**
* Run all health checks
*/
async runChecks(): Promise<HealthReport> {
const results: HealthCheckResult[] = [];
for (const [name, check] of this.checks) {
const start = Date.now();
try {
const result = await Promise.race([
check(),
this.timeout(5000, name),
]);
result.latency = Date.now() - start;
results.push(result);
} catch (error) {
results.push({
name,
status: 'unhealthy',
latency: Date.now() - start,
message: error instanceof Error ? error.message : 'Check failed',
});
}
}
const overallStatus = this.calculateOverallStatus(results);
return {
status: overallStatus,
timestamp: new Date().toISOString(),
version: this.version,
uptime: Math.floor((Date.now() - this.startTime) / 1000),
checks: results,
};
}
/**
* Run liveness check (basic alive check)
*/
async livenessCheck(): Promise<{ status: 'ok' | 'error' }> {
return { status: 'ok' };
}
/**
* Run readiness check (ready to serve traffic)
*/
async readinessCheck(): Promise<HealthReport> {
return this.runChecks();
}
private async timeout(ms: number, name: string): Promise<HealthCheckResult> {
return new Promise((_, reject) => {
setTimeout(() => reject(new Error(`${name} timed out after ${ms}ms`)), ms);
});
}
private calculateOverallStatus(results: HealthCheckResult[]): HealthStatus {
if (results.some(r => r.status === 'unhealthy')) {
return 'unhealthy';
}
if (results.some(r => r.status === 'degraded')) {
return 'degraded';
}
return 'healthy';
}
}
// ===========================================
// Common Health Checks
// ===========================================
/**
* Database health check
*/
export function createDatabaseCheck(
name: string,
queryFn: () => Promise<unknown>
): HealthCheck {
return async () => {
try {
await queryFn();
return { name, status: 'healthy' };
} catch (error) {
return {
name,
status: 'unhealthy',
message: error instanceof Error ? error.message : 'Database error',
};
}
};
}
/**
* Redis health check
*/
export function createRedisCheck(
name: string,
pingFn: () => Promise<string>
): HealthCheck {
return async () => {
try {
const result = await pingFn();
if (result === 'PONG') {
return { name, status: 'healthy' };
}
return { name, status: 'degraded', message: `Unexpected response: ${result}` };
} catch (error) {
return {
name,
status: 'unhealthy',
message: error instanceof Error ? error.message : 'Redis error',
};
}
};
}
/**
* HTTP dependency health check
*/
export function createHttpCheck(
name: string,
url: string,
timeoutMs: number = 5000
): HealthCheck {
return async () => {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
try {
const response = await fetch(url, {
method: 'GET',
signal: controller.signal,
});
clearTimeout(timeoutId);
if (response.ok) {
return { name, status: 'healthy', metadata: { statusCode: response.status } };
}
return {
name,
status: 'degraded',
message: `HTTP ${response.status}`,
metadata: { statusCode: response.status },
};
} catch (error) {
clearTimeout(timeoutId);
return {
name,
status: 'unhealthy',
message: error instanceof Error ? error.message : 'HTTP error',
};
}
};
}
/**
* Memory usage check
*/
export function createMemoryCheck(
name: string = 'memory',
thresholdPercent: number = 90
): HealthCheck {
return async () => {
const used = process.memoryUsage();
const heapUsedPercent = (used.heapUsed / used.heapTotal) * 100;
if (heapUsedPercent < thresholdPercent) {
return {
name,
status: 'healthy',
metadata: {
heapUsed: Math.round(used.heapUsed / 1024 / 1024),
heapTotal: Math.round(used.heapTotal / 1024 / 1024),
percent: Math.round(heapUsedPercent),
},
};
}
return {
name,
status: 'degraded',
message: `Memory usage at ${Math.round(heapUsedPercent)}%`,
metadata: { percent: Math.round(heapUsedPercent) },
};
};
}
/**
* Disk space check (requires external command)
*/
export function createDiskCheck(
name: string = 'disk',
path: string = '/',
thresholdPercent: number = 90
): HealthCheck {
return async () => {
// This is a placeholder - implement based on your OS
// For Node.js, use 'diskusage' package or execute df command
return { name, status: 'healthy', message: 'Implement disk check' };
};
}
// ===========================================
// Express Integration
// ===========================================
/*
import express from 'express';
const app = express();
const health = new HealthCheckManager(process.env.VERSION || '1.0.0');
// Register checks
health.register('database', createDatabaseCheck('database', () => db.query('SELECT 1')));
health.register('redis', createRedisCheck('redis', () => redis.ping()));
health.register('memory', createMemoryCheck());
// Kubernetes-style endpoints
app.get('/healthz', async (req, res) => {
const result = await health.livenessCheck();
res.json(result);
});
app.get('/readyz', async (req, res) => {
const report = await health.readinessCheck();
const statusCode = report.status === 'healthy' ? 200 : 503;
res.status(statusCode).json(report);
});
// Detailed health endpoint
app.get('/health', async (req, res) => {
const report = await health.runChecks();
const statusCode = report.status === 'healthy' ? 200 :
report.status === 'degraded' ? 200 : 503;
res.status(statusCode).json(report);
});
*/
export { HealthCheckManager as default };
Reliability Engineering Templates
Templates for building resilient services.
Files
| Template | Purpose |
|---|---|
health-check.ts | Health check endpoints |
circuit-breaker.ts | Circuit breaker pattern |
Usage
Health Checks
import {
HealthCheckManager,
createDatabaseCheck,
createRedisCheck,
createMemoryCheck,
} from './health-check';
const health = new HealthCheckManager('1.0.0');
// Register checks
health.register('database', createDatabaseCheck('db', () => db.query('SELECT 1')));
health.register('redis', createRedisCheck('redis', () => redis.ping()));
health.register('memory', createMemoryCheck());
// Express endpoints
app.get('/healthz', (req, res) => res.json({ status: 'ok' }));
app.get('/readyz', async (req, res) => {
const report = await health.readinessCheck();
res.status(report.status === 'healthy' ? 200 : 503).json(report);
});Circuit Breaker
import { CircuitBreaker, CircuitOpenError } from './circuit-breaker';
const breaker = new CircuitBreaker({
failureThreshold: 5,
resetTimeout: 30000,
timeout: 5000,
});
async function callService() {
try {
return await breaker.execute(async () => {
return fetch('https://api.example.com/data');
});
} catch (error) {
if (error instanceof CircuitOpenError) {
return fallbackData;
}
throw error;
}
}Kubernetes Endpoints
| Endpoint | Purpose | Response |
|---|---|---|
/healthz | Liveness | { status: 'ok' } |
/readyz | Readiness | Full health report |
/health | Detailed | All check results |
# Kubernetes deployment
livenessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 10
periodSeconds: 10
readinessProbe:
httpGet:
path: /readyz
port: 8080
initialDelaySeconds: 5
periodSeconds: 5Circuit Breaker States
┌─────────┐ failure threshold ┌──────────┐
│ CLOSED │ ───────────────────────▶│ OPEN │
│ │ │ │
│ Normal │ │ Blocking │
│ traffic │ │ requests │
└────┬────┘ └────┬─────┘
▲ │
│ ┌───────────┐ │
│ │ HALF-OPEN │ │
│◀────────│ │◀────────────┘
success │ Testing │ timeout
threshold │ recovery │ expired
└───────────┘Configuration Options
Health Check
| Option | Default | Description |
|---|---|---|
| timeout | 5000ms | Check timeout |
Circuit Breaker
| Option | Default | Description |
|---|---|---|
| failureThreshold | 5 | Failures to open |
| successThreshold | 2 | Successes to close |
| resetTimeout | 30000ms | Open duration |
| timeout | 10000ms | Request timeout |
| failureRateThreshold | 50% | Rate to open |
| minimumRequests | 10 | Min for rate calc |
Best Practices
Health Checks
- Keep checks fast (<5s)
- Check critical dependencies only
- Don't check external services in liveness
- Use readiness for dependencies
Circuit Breakers
- Use per-service/endpoint
- Configure thresholds based on SLAs
- Implement fallback strategies
- Monitor circuit state
Fallback Strategies
// Cached data
const fallback = await cache.get(key);
// Default value
const fallback = { data: [] };
// Degraded response
const fallback = { partial: true, items: cachedItems };
// Queue for retry
await queue.add({ request, retryAt: Date.now() + 60000 });Monitoring
// Expose metrics
app.get('/metrics', (req, res) => {
const stats = registry.getAllStats();
res.json({
circuit_breakers: stats,
health: health.getStats(),
});
});Key metrics to track:
- Circuit state changes
- Failure rates
- Response latencies
- Health check results
Related skills
AI & Agent Buildingagents