Skip to content
graceful-degradation logo

Graceful Degradation

graceful-degradation

Build resilient systems that degrade gracefully under failure. Implement fallbacks, feature flags, and partial responses when dependencies fail.

SKILL.md

Full skill instructions

Graceful Degradation

Keep your app running even when things break.

When to Use This Skill

  • External API dependencies
  • Non-critical features
  • High-availability requirements
  • Microservices architecture
  • Third-party integrations

Degradation Strategies

┌─────────────────────────────────────────────────────┐
│                  User Request                        │
└─────────────────────────────────────────────────────┘
                          │
                          ▼
┌─────────────────────────────────────────────────────┐
│              Primary Service                         │
│                                                     │
│  Try primary implementation                         │
│  ├─ Success → Return result                         │
│  └─ Failure → Try fallback                          │
└─────────────────────────────────────────────────────┘
                          │
                          ▼
┌─────────────────────────────────────────────────────┐
│              Fallback Options                        │
│                                                     │
│  1. Cached data (stale but available)               │
│  2. Default values                                  │
│  3. Reduced functionality                           │
│  4. Queue for later                                 │
│  5. Graceful error message                          │
└─────────────────────────────────────────────────────┘

TypeScript Implementation

Fallback Service

// fallback-service.ts
interface FallbackOptions<T> {
  primary: () => Promise<T>;
  fallback: () => Promise<T> | T;
  shouldFallback?: (error: Error) => boolean;
  onFallback?: (error: Error) => void;
  timeout?: number;
}

async function withFallback<T>(options: FallbackOptions<T>): Promise<T> {
  const { primary, fallback, shouldFallback, onFallback, timeout = 5000 } = options;

  try {
    // Add timeout to primary
    const result = await Promise.race([
      primary(),
      new Promise<never>((_, reject) =>
        setTimeout(() => reject(new Error('Timeout')), timeout)
      ),
    ]);
    return result;
  } catch (error) {
    // Check if we should use fallback
    if (shouldFallback && !shouldFallback(error as Error)) {
      throw error;
    }

    // Log fallback usage
    onFallback?.(error as Error);
    console.warn('Using fallback due to:', (error as Error).message);

    // Return fallback value
    const fallbackResult = fallback();
    return fallbackResult instanceof Promise ? await fallbackResult : fallbackResult;
  }
}

export { withFallback, FallbackOptions };

Practical Examples

// product-service.ts
class ProductService {
  private cache: Cache;
  private searchClient: SearchClient;

  // Example 1: Cache fallback
  async getProduct(id: string): Promise<Product> {
    return withFallback({
      primary: () => this.fetchFromDatabase(id),
      fallback: () => this.cache.get(`product:${id}`),
      onFallback: (err) => metrics.increment('product.cache_fallback'),
    });
  }

  // Example 2: Search with degraded results
  async searchProducts(query: string): Promise<SearchResult> {
    return withFallback({
      primary: async () => {
        // Full-featured Elasticsearch search
        return this.searchClient.search({
          query,
          facets: true,
          suggestions: true,
          personalization: true,
        });
      },
      fallback: async () => {
        // Degraded: Simple database LIKE query
        const products = await db.products.findMany({
          where: { name: { contains: query } },
          take: 20,
        });
        return {
          results: products,
          facets: null,        // Not available
          suggestions: null,   // Not available
          degraded: true,      // Signal to frontend
        };
      },
      timeout: 2000,
    });
  }

  // Example 3: Recommendations with default fallback
  async getRecommendations(userId: string): Promise<Product[]> {
    return withFallback({
      primary: () => this.mlService.getPersonalizedRecommendations(userId),
      fallback: () => this.getPopularProducts(), // Generic fallback
      shouldFallback: (err) => err.message !== 'User not found',
    });
  }
}

Partial Response Pattern

// dashboard-service.ts
interface DashboardData {
  user: User;
  stats: Stats | null;
  notifications: Notification[] | null;
  recommendations: Product[] | null;
  errors: string[];
}

async function getDashboard(userId: string): Promise<DashboardData> {
  const errors: string[] = [];

  // User is required - fail if unavailable
  const user = await userService.getUser(userId);

  // Stats are nice to have
  const stats = await withFallback({
    primary: () => statsService.getUserStats(userId),
    fallback: () => null,
    onFallback: () => errors.push('Stats temporarily unavailable'),
  });

  // Notifications are nice to have
  const notifications = await withFallback({
    primary: () => notificationService.getUnread(userId),
    fallback: () => null,
    onFallback: () => errors.push('Notifications temporarily unavailable'),
  });

  // Recommendations are nice to have
  const recommendations = await withFallback({
    primary: () => recommendationService.getForUser(userId),
    fallback: () => null,
    onFallback: () => errors.push('Recommendations temporarily unavailable'),
  });

  return { user, stats, notifications, recommendations, errors };
}

Feature Degradation with Flags

// feature-degradation.ts
class FeatureDegradation {
  private degradedFeatures = new Set<string>();

  async execute<T>(
    feature: string,
    primary: () => Promise<T>,
    fallback: () => T | Promise<T>
  ): Promise<T> {
    // Check if feature is already degraded
    if (this.degradedFeatures.has(feature)) {
      return fallback instanceof Function ? fallback() : fallback;
    }

    try {
      return await primary();
    } catch (error) {
      // Auto-degrade feature after failures
      this.degradedFeatures.add(feature);
      
      // Schedule recovery check
      setTimeout(() => this.checkRecovery(feature, primary), 30000);
      
      return fallback instanceof Function ? fallback() : fallback;
    }
  }

  private async checkRecovery(feature: string, healthCheck: () => Promise<unknown>) {
    try {
      await healthCheck();
      this.degradedFeatures.delete(feature);
      console.log(`Feature ${feature} recovered`);
    } catch {
      // Still failing, check again later
      setTimeout(() => this.checkRecovery(feature, healthCheck), 60000);
    }
  }
}

const degradation = new FeatureDegradation();

// Usage
const searchResults = await degradation.execute(
  'elasticsearch',
  () => elasticSearch.query(term),
  () => sqlSearch.query(term)
);

Python Implementation

# fallback.py
from typing import TypeVar, Callable, Optional
import asyncio

T = TypeVar('T')

async def with_fallback(
    primary: Callable[[], T],
    fallback: Callable[[], T],
    timeout: float = 5.0,
    on_fallback: Optional[Callable[[Exception], None]] = None,
) -> T:
    try:
        result = await asyncio.wait_for(primary(), timeout=timeout)
        return result
    except Exception as e:
        if on_fallback:
            on_fallback(e)
        return await fallback() if asyncio.iscoroutinefunction(fallback) else fallback()

# Usage
async def get_product(product_id: str) -> Product:
    return await with_fallback(
        primary=lambda: fetch_from_database(product_id),
        fallback=lambda: cache.get(f"product:{product_id}"),
        timeout=2.0,
        on_fallback=lambda e: logger.warning(f"Using cache fallback: {e}"),
    )

Decorator Pattern

# degradable.py
from functools import wraps

def degradable(fallback_value=None, fallback_func=None, timeout=5.0):
    def decorator(func):
        @wraps(func)
        async def wrapper(*args, **kwargs):
            try:
                return await asyncio.wait_for(
                    func(*args, **kwargs),
                    timeout=timeout
                )
            except Exception as e:
                logger.warning(f"{func.__name__} degraded: {e}")
                if fallback_func:
                    return await fallback_func(*args, **kwargs)
                return fallback_value
        return wrapper
    return decorator

# Usage
@degradable(fallback_value=[], timeout=2.0)
async def get_recommendations(user_id: str) -> list[Product]:
    return await ml_service.get_recommendations(user_id)

Frontend Handling

// React component handling degraded responses
function Dashboard({ data }: { data: DashboardData }) {
  return (
    <div>
      <UserProfile user={data.user} />
      
      {data.stats ? (
        <StatsPanel stats={data.stats} />
      ) : (
        <DegradedNotice message="Stats temporarily unavailable" />
      )}
      
      {data.recommendations ? (
        <Recommendations items={data.recommendations} />
      ) : (
        <PopularProducts /> // Fallback UI
      )}
      
      {data.errors.length > 0 && (
        <SystemNotice errors={data.errors} />
      )}
    </​div>
  );
}

Best Practices

  1. Identify critical vs non-critical - Know what can fail
  2. Always have a fallback - Even if it's just an error message
  3. Signal degradation to users - Don't hide failures
  4. Monitor fallback usage - Track when degradation happens
  5. Test failure scenarios - Chaos engineering

Common Mistakes

  • No fallback for external dependencies
  • Hiding degradation from users
  • Fallback that's also likely to fail
  • Not monitoring fallback frequency
  • Cascading failures from one service

More skills from majiayu000

xiaohongshu logo
majiayu000/claude-arsenal

xiaohongshu

xiaohongshu

286 148
View
agent-task-conductor logo
majiayu000/claude-skill-registry

agent-task-conductor

Conduct multi-agent task orchestration and workflow coordination.

663 1
View
conductor-setup logo
majiayu000/claude-skill-registry

conductor-setup

Initialize project with Conductor artifacts (product definition,

663 1
View
animation-designer logo
majiayu000/claude-skill-registry

animation-designer

Expert in web animations, transitions, and motion design using Framer Motion and CSS

663 1
View
diagramming logo
majiayu000/claude-skill-registry

diagramming

Creates Mermaid and ASCII diagrams for flowcharts, architecture, ERDs, state machines, mindmaps, and more. Use when user mentions diagram, flowchart, mermaid, ASCII diagram, text diagram, terminal diagram, visualize, C4, mindmap, architecture diagram, sequence diagram, ERD, or needs visual docume...

663 1
View
h3-pg logo
majiayu000/claude-skill-registry-data

h3-pg

PostgreSQL bindings for H3 hexagonal grid system. Use when working with H3 cells in Postgres, including spatial indexing, geometry/geography integration, and raster analysis.

23 1
View
conductor-development logo
majiayu000/claude-skill-registry

conductor-development

Context-Driven Development skill for projects using Conductor. Use this skill when you detect a `conductor/` directory in the project, when working on tasks defined in a `plan.md` file, or when the user asks about tracks, specs, or plans. Automatically applies TDD workflow, tracks task completion...

663 1
View
conductor-status logo
majiayu000/claude-skill-registry

conductor-status

Display project status, active tracks, and next actions

663 1
View
dockerization logo
majiayu000/claude-skill-registry

dockerization

Official Stakpak application containerization standard operating procedure, a step-by-step guidline to properly dockerize applications. This is a rule book curated by the Stakpak Team.

663 1
View

Popular AI tools

Kaiber logo
Video

Kaiber

Generate, edit, and beat-sync AI video with leading models in one workspace.

Paid
View
Vimcal logo
Productivity

Vimcal

The world's fastest calendar for remote work

Free
View

Transform Your Design with AI Designer by ImgCreator.ai

Freemium
View
Akool AI logo
Content & writing

Akool AI

Revolutionizing Video Production with AI-Powered Creativity

Paid
View

Extend an image past the frame and let AI fill the new aspect ratio.

Freemium
View
StarByFace logo
Security

StarByFace

Discover your celebrity doppelgänger with StarByFace!

Free
View
C

ChainClarity explains 700+ crypto whitepapers in plain English, with layered summaries, comparisons, research tools, alerts, and a $4.99 Pro plan.

Freemium
View
Opus Clip logo
Coding & apps

Opus Clip

Opus.ai: Revolutionize Your Web Experience

Free
View