Back to Blog
Error Handling Architecture: Graceful Degradation
Failures Are Inevitable
Systems fail. Networks go down, databases crash, third-party services become unavailable. Good architecture assumes failures and designs for graceful degradation.
Circuit Breaker Pattern
class CircuitBreaker
{
private int $failureCount = 0;
private int $threshold = 5;
private ?Carbon $openedAt = null;
public function call(callable $action): mixed
{
if ($this->isOpen()) {
throw new CircuitOpenException();
}
try {
$result = $action();
$this->reset();
return $result;
} catch (Exception $e) {
$this->recordFailure();
throw $e;
}
}
private function isOpen(): bool
{
if ($this->openedAt === null) {
return false;
}
// Half-open after timeout
if ($this->openedAt->addMinutes(1)->isPast()) {
return false;
}
return true;
}
private function recordFailure(): void
{
$this->failureCount++;
if ($this->failureCount >= $this->threshold) {
$this->openedAt = now();
}
}
}
Retry with Backoff
class RetryHandler
{
public function withRetry(
callable $action,
int $maxAttempts = 3,
int $baseDelay = 1000
): mixed {
$attempt = 0;
while (true) {
try {
return $action();
} catch (RetryableException $e) {
$attempt++;
if ($attempt >= $maxAttempts) {
throw $e;
}
$delay = $baseDelay * pow(2, $attempt);
usleep($delay * 1000);
}
}
}
}
Fallback Strategies
class ProductService
{
public function get(int $id): Product
{
try {
return $this->primaryService->get($id);
} catch (ServiceUnavailableException $e) {
// Fallback to cache
if ($cached = Cache::get("product_{$id}")) {
return $cached;
}
// Fallback to degraded response
return Product::placeholder($id);
}
}
}
Bulkhead Pattern
// Isolate failures with separate connection pools
class ServiceConnections
{
public function forPayments(): Client
{
return new Client(['pool' => 'payments', 'max' => 10]);
}
public function forInventory(): Client
{
return new Client(['pool' => 'inventory', 'max' => 5]);
}
// Payment failures don't exhaust inventory connections
}
Conclusion
Design for failure with circuit breakers, retries with backoff, fallbacks, and isolation. The goal isn't to prevent all failures—it's to handle them gracefully and recover automatically.
Related Articles
Need Help With Your Project?
I respond to all inquiries within 24 hours. Let's discuss how I can help build your production-ready system.
Get In Touch