Agent Skills: System Design Patterns

System design patterns for scalability, reliability, and performance. Use when: (1) designing distributed systems, (2) planning for scale, (3) making architecture decisions, (4) evaluating trade-offs.

UncategorizedID: thapaliyabikendra/ai-artifacts/system-design-patterns

Install this agent skill to your local

pnpm dlx add-skill https://github.com/thapaliyabikendra/ai-artifacts/tree/HEAD/.claude/skills/system-design-patterns

Skill Files

Browse the full folder contents for system-design-patterns.

Download Skill

Loading file tree…

.claude/skills/system-design-patterns/SKILL.md

Skill Metadata

Name
system-design-patterns
Description
"System design patterns for scalability, reliability, and performance. Use when: (1) designing distributed systems, (2) planning for scale, (3) making architecture decisions, (4) evaluating trade-offs."

System Design Patterns

Design scalable, reliable, and performant systems with proven patterns.

When to Use

  • Designing new systems or features
  • Evaluating architecture trade-offs
  • Planning for scale
  • Improving system reliability
  • Making infrastructure decisions

Core Principles

CAP Theorem

| Property | Meaning | Trade-off | |----------|---------|-----------| | Consistency | All nodes see the same data | Higher latency | | Availability | System responds to every request | May return stale data | | Partition Tolerance | System works despite network failures | Must sacrifice C or A |

Choose 2:

  • CP: Banking, inventory (consistency critical)
  • AP: Social media, caching (availability critical)
  • CA: Single-node systems only (no network partitions)

ACID vs BASE

| ACID (Traditional RDBMS) | BASE (Distributed) | |--------------------------|-------------------| | Atomicity | Basically Available | | Consistency | Soft state | | Isolation | Eventually consistent | | Durability | |

Scalability Patterns

Horizontal vs Vertical Scaling

Vertical Scaling (Scale Up)          Horizontal Scaling (Scale Out)
┌─────────────────────────┐         ┌──────┐ ┌──────┐ ┌──────┐
│                         │         │      │ │      │ │      │
│     Bigger Server       │    vs   │Server│ │Server│ │Server│
│                         │         │      │ │      │ │      │
│ More CPU, RAM, Storage  │         │      │ │      │ │      │
└─────────────────────────┘         └──────┘ └──────┘ └──────┘

Pros:                               Pros:
- Simple to implement               - Near-infinite scale
- No code changes                   - Fault tolerant
- Lower operational complexity      - Cost effective at scale

Cons:                               Cons:
- Hardware limits                   - Distributed complexity
- Single point of failure           - Data consistency challenges
- Expensive at scale                - More operational overhead

Load Balancing Strategies

// Strategy selection based on use case

public enum LoadBalancingStrategy
{
    // Simple, stateless services
    RoundRobin,

    // Varying server capacities
    WeightedRoundRobin,

    // Session affinity needed
    IpHash,

    // Optimal resource utilization
    LeastConnections,

    // Latency-sensitive applications
    LeastResponseTime,

    // Geographic distribution
    GeographicBased
}

| Strategy | Use Case | Trade-off | |----------|----------|-----------| | Round Robin | Stateless, homogeneous | No health awareness | | Weighted | Different server sizes | Manual configuration | | IP Hash | Session stickiness | Uneven distribution | | Least Connections | Long-lived connections | Overhead tracking | | Geographic | Global users | Complexity |

Database Scaling

Read Replicas

┌─────────────────────────────────────────────────────┐
│                    Application                       │
└──────────────────────┬──────────────────────────────┘
                       │
        ┌──────────────┴──────────────┐
        │                             │
        ▼                             ▼
┌───────────────┐           ┌─────────────────┐
│  Primary DB   │──────────►│  Read Replica 1 │
│  (Writes)     │    Async  ├─────────────────┤
│               │──────────►│  Read Replica 2 │
└───────────────┘    Repl   └─────────────────┘
                                    ▲
                                    │
                              Read Queries
// Read/Write splitting in ABP
public class PatientAppService : ApplicationService
{
    private readonly IReadOnlyRepository<Patient, Guid> _readRepository;
    private readonly IRepository<Patient, Guid> _writeRepository;

    // Reads go to replicas
    public async Task<PatientDto> GetAsync(Guid id)
    {
        var patient = await _readRepository.GetAsync(id);
        return ObjectMapper.Map<Patient, PatientDto>(patient);
    }

    // Writes go to primary
    public async Task<PatientDto> CreateAsync(CreatePatientDto input)
    {
        var patient = new Patient(GuidGenerator.Create(), input.Name);
        await _writeRepository.InsertAsync(patient);
        return ObjectMapper.Map<Patient, PatientDto>(patient);
    }
}

Database Sharding

┌─────────────────────────────────────────────────────────────┐
│                     Shard Router                            │
│         (Routes queries based on shard key)                 │
└────────────┬──────────────┬──────────────┬─────────────────┘
             │              │              │
             ▼              ▼              ▼
      ┌───────────┐  ┌───────────┐  ┌───────────┐
      │  Shard 1  │  │  Shard 2  │  │  Shard 3  │
      │  A - H    │  │  I - P    │  │  Q - Z    │
      │ (Users)   │  │ (Users)   │  │ (Users)   │
      └───────────┘  └───────────┘  └───────────┘

| Sharding Strategy | Pros | Cons | |-------------------|------|------| | Range-based | Simple, range queries work | Hotspots possible | | Hash-based | Even distribution | Range queries need scatter-gather | | Directory-based | Flexible | Lookup overhead, SPOF | | Geographic | Data locality | Cross-region queries slow |

Caching Patterns

Cache-Aside (Lazy Loading)

public class PatientService
{
    private readonly IDistributedCache _cache;
    private readonly IPatientRepository _repository;

    public async Task<PatientDto> GetAsync(Guid id)
    {
        var cacheKey = $"patient:{id}";

        // 1. Check cache
        var cached = await _cache.GetStringAsync(cacheKey);
        if (cached != null)
        {
            return JsonSerializer.Deserialize<PatientDto>(cached);
        }

        // 2. Cache miss - load from DB
        var patient = await _repository.GetAsync(id);
        var dto = ObjectMapper.Map<Patient, PatientDto>(patient);

        // 3. Populate cache
        await _cache.SetStringAsync(
            cacheKey,
            JsonSerializer.Serialize(dto),
            new DistributedCacheEntryOptions
            {
                AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(10)
            });

        return dto;
    }

    public async Task UpdateAsync(Guid id, UpdatePatientDto input)
    {
        // Update database
        var patient = await _repository.GetAsync(id);
        patient.Update(input.Name, input.Email);
        await _repository.UpdateAsync(patient);

        // Invalidate cache
        await _cache.RemoveAsync($"patient:{id}");
    }
}

Write-Through Cache

public async Task<PatientDto> CreateAsync(CreatePatientDto input)
{
    // 1. Write to database
    var patient = new Patient(GuidGenerator.Create(), input.Name);
    await _repository.InsertAsync(patient);

    // 2. Write to cache synchronously
    var dto = ObjectMapper.Map<Patient, PatientDto>(patient);
    await _cache.SetStringAsync(
        $"patient:{patient.Id}",
        JsonSerializer.Serialize(dto),
        new DistributedCacheEntryOptions
        {
            AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(10)
        });

    return dto;
}

Cache Strategies Comparison

| Pattern | Consistency | Performance | Use Case | |---------|-------------|-------------|----------| | Cache-Aside | Eventual | Read-heavy | User profiles | | Write-Through | Strong | Write + Read | Financial data | | Write-Behind | Eventual | Write-heavy | Analytics, logs | | Read-Through | Eventual | Read-heavy | Reference data |

Reliability Patterns

Circuit Breaker

// Using Polly
public class ExternalServiceClient
{
    private readonly HttpClient _client;
    private readonly AsyncCircuitBreakerPolicy _circuitBreaker;

    public ExternalServiceClient(HttpClient client)
    {
        _client = client;
        _circuitBreaker = Policy
            .Handle<HttpRequestException>()
            .CircuitBreakerAsync(
                exceptionsAllowedBeforeBreaking: 5,
                durationOfBreak: TimeSpan.FromSeconds(30),
                onBreak: (ex, duration) =>
                    Log.Warning("Circuit opened for {Duration}s", duration.TotalSeconds),
                onReset: () =>
                    Log.Information("Circuit closed"),
                onHalfOpen: () =>
                    Log.Information("Circuit half-open, testing...")
            );
    }

    public async Task<T> GetAsync<T>(string endpoint)
    {
        return await _circuitBreaker.ExecuteAsync(async () =>
        {
            var response = await _client.GetAsync(endpoint);
            response.EnsureSuccessStatusCode();
            return await response.Content.ReadFromJsonAsync<T>();
        });
    }
}

Retry with Exponential Backoff

var retryPolicy = Policy
    .Handle<HttpRequestException>()
    .WaitAndRetryAsync(
        retryCount: 3,
        sleepDurationProvider: attempt =>
            TimeSpan.FromSeconds(Math.Pow(2, attempt)), // 2, 4, 8 seconds
        onRetry: (ex, delay, attempt, context) =>
            Log.Warning("Retry {Attempt} after {Delay}s: {Error}",
                attempt, delay.TotalSeconds, ex.Message)
    );

Bulkhead Pattern

// Isolate failures to prevent cascade
var bulkhead = Policy.BulkheadAsync(
    maxParallelization: 10,      // Max concurrent executions
    maxQueuingActions: 20,       // Max queued requests
    onBulkheadRejectedAsync: context =>
    {
        Log.Warning("Bulkhead rejected request");
        return Task.CompletedTask;
    }
);

Event-Driven Architecture

Message Queue Pattern

┌─────────┐    ┌─────────────┐    ┌─────────────┐
│ Service │───►│   Message   │───►│  Consumer   │
│    A    │    │    Queue    │    │  Service B  │
└─────────┘    │             │    └─────────────┘
               │  (RabbitMQ, │
               │   Kafka,    │    ┌─────────────┐
               │   Azure SB) │───►│  Consumer   │
               └─────────────┘    │  Service C  │
                                  └─────────────┘

Event Sourcing

// Store events, not state
public class PatientAggregate
{
    private readonly List<IDomainEvent> _events = new();

    public Guid Id { get; private set; }
    public string Name { get; private set; }
    public PatientStatus Status { get; private set; }

    public void Apply(PatientCreated @event)
    {
        Id = @event.PatientId;
        Name = @event.Name;
        Status = PatientStatus.Active;
        _events.Add(@event);
    }

    public void Apply(PatientNameChanged @event)
    {
        Name = @event.NewName;
        _events.Add(@event);
    }

    // Rebuild state from events
    public static PatientAggregate FromEvents(IEnumerable<IDomainEvent> events)
    {
        var patient = new PatientAggregate();
        foreach (var @event in events)
        {
            patient.Apply((dynamic)@event);
        }
        return patient;
    }
}

Quick Reference: Design Trade-offs

| Decision | Option A | Option B | Consider | |----------|----------|----------|----------| | Storage | SQL | NoSQL | Data structure, consistency needs | | Caching | Redis | In-memory | Distributed needs, size | | Communication | Sync (HTTP) | Async (Queue) | Coupling, latency tolerance | | Consistency | Strong | Eventual | Business requirements | | Scaling | Vertical | Horizontal | Cost, complexity, limits |

System Design Checklist

  • [ ] Requirements: Functional + Non-functional defined
  • [ ] Scale: Expected users, requests/sec, data volume
  • [ ] Availability: Uptime target (99.9% = 8.76h downtime/year)
  • [ ] Latency: P50, P95, P99 targets
  • [ ] Data: Storage type, retention, backup strategy
  • [ ] Caching: What to cache, invalidation strategy
  • [ ] Security: Auth, encryption, compliance
  • [ ] Monitoring: Metrics, logging, alerting
  • [ ] Failure modes: What happens when X fails?
  • [ ] Cost: Infrastructure, operational overhead

Related Skills

  • technical-design-patterns - Document designs
  • api-design-principles - API architecture
  • distributed-events-advanced - Event patterns