Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save carefree-ladka/6b4384df7f0cd19f0c905b7e399c905b to your computer and use it in GitHub Desktop.

Select an option

Save carefree-ladka/6b4384df7f0cd19f0c905b7e399c905b to your computer and use it in GitHub Desktop.
Frontend System Design Guide - RADIO Framework

Frontend System Design Guide - RADIO Framework

A comprehensive guide to frontend system design interviews using the RADIO framework: Requirements, Architecture, Data Model, Interface Definition, Optimizations

Table of Contents

Applications

  1. News Feed (e.g. Facebook)
  2. Autocomplete
  3. Pinterest
  4. Rich Text Editor
  5. Google Docs
  6. Video Streaming (e.g. Netflix)
  7. E-commerce Marketplace (e.g. Amazon)
  8. Travel Booking (e.g. Airbnb)
  9. Chat App (e.g. Messenger)
  10. Photo Sharing (e.g. Instagram)
  11. Email Client (e.g. Microsoft Outlook)

UI Components

  1. Dropdown Menu
  2. Image Carousel
  3. Modal Dialog
  4. Poll Widget

News Feed (e.g. Facebook)

Requirements (R)

Core Features:

  • Display infinite scrolling feed of posts
  • Create new posts (text, images, videos)
  • Interact with posts (like, comment, share)
  • Real-time updates for new posts

Functional Requirements:

  • Users can view posts from friends/followed accounts
  • Users can create text posts with optional media
  • Users can like, comment, and share posts
  • Feed updates in real-time when new posts arrive

Non-Functional Requirements:

  • Performance: Initial load < 2s, smooth 60fps scrolling
  • Support desktop and mobile web
  • Offline: Show cached posts, queue actions
  • Accessibility: Screen reader support, keyboard navigation

Out of Scope:

  • Stories, Groups, Marketplace
  • Video calls, Gaming
  • Ad serving system

Architecture (A)

┌─────────────────────────────────────────┐
│              Server (API)               │
│  - Feed endpoints                       │
│  - Post creation endpoints              │
│  - WebSocket for real-time updates      │
└─────────────────────────────────────────┘
                    ↕
┌─────────────────────────────────────────┐
│            Controller                   │
│  - Manages data flow                    │
│  - Handles API calls                    │
│  - WebSocket connection                 │
└─────────────────────────────────────────┘
                    ↕
┌─────────────────────────────────────────┐
│          Client Store                   │
│  - Feed posts cache                     │
│  - User data                            │
│  - Pagination state                     │
└─────────────────────────────────────────┘
                    ↕
┌─────────────────────────────────────────┐
│            Feed UI                      │
│  ┌─────────────────────────────────┐   │
│  │   Post Composer                 │   │
│  └─────────────────────────────────┘   │
│  ┌─────────────────────────────────┐   │
│  │   Feed Post (repeated)          │   │
│  │   - Author info                 │   │
│  │   - Content                     │   │
│  │   - Interaction buttons         │   │
│  └─────────────────────────────────┘   │
└─────────────────────────────────────────┘

Data Model (D)

Source Entity Fields
Server Post id, created_time, content, media_url, media_type, author (User), reactions (object), comments_count, shares_count
Server User id, name, profile_photo_url, username
Server Feed posts (Post[]), next_cursor, has_more
Server Reaction type (like/love/haha/wow/sad/angry), count, user_reacted (boolean)
Client NewPost content, media_file, is_uploading, upload_progress
Client UIState is_loading, error, active_post_id

Interface Definition (I)

Server APIs:

// GET /feed - Fetch feed posts
GET /api/feed?cursor={cursor}&size={size}
Response: {
  posts: Post[],
  pagination: {
    next_cursor: string,
    has_more: boolean
  }
}

// POST /posts - Create new post
POST /api/posts
Body: {
  content: string,
  media?: File,
  media_type?: 'image' | 'video'
}
Response: {
  post: Post
}

// POST /posts/:id/reactions - React to post
POST /api/posts/:id/reactions
Body: {
  type: 'like' | 'love' | 'haha' | 'wow' | 'sad' | 'angry'
}

// WebSocket events
WS /feed/updates
Events:
  - new_post: { post: Post }
  - post_updated: { post_id: string, changes: Partial<Post> }

Component APIs:

// Feed Component
interface FeedProps {
  userId: string;
  onPostCreated?: (post: Post) => void;
}

// Post Component
interface PostProps {
  post: Post;
  onReact: (type: ReactionType) => Promise<void>;
  onComment: () => void;
  onShare: () => void;
}

// Post Composer Component
interface PostComposerProps {
  onSubmit: (content: string, media?: File) => Promise<void>;
  placeholder?: string;
}

Optimizations (O)

Performance:

  • Virtual Scrolling: Render only visible posts (react-window, react-virtualized)
  • Image Lazy Loading: Load images as they enter viewport (Intersection Observer)
  • Code Splitting: Lazy load video player, comment section
  • Debounced Reactions: Batch reaction updates
  • Optimistic UI: Immediately show reactions/comments before server confirms

Network:

  • Pagination: Cursor-based pagination (better than offset for real-time feeds)
  • Prefetching: Load next page when user is 80% through current page
  • Request Deduplication: Cache identical API calls
  • WebSocket: Real-time updates without polling
  • CDN: Serve media files from CDN with appropriate cache headers

User Experience:

  • Skeleton Screens: Show loading placeholders
  • Pull to Refresh: Mobile gesture support
  • Infinite Scroll: Automatic loading
  • Error Recovery: Retry failed requests, show error boundaries
  • Offline Support: IndexedDB cache, queue actions when offline

Accessibility:

  • ARIA labels for all interactive elements
  • Keyboard navigation (Tab, Enter, Space)
  • Screen reader announcements for new posts
  • Focus management for modals
  • Proper heading hierarchy

Security:

  • XSS Prevention: Sanitize user content
  • CSRF Protection: Token-based
  • Content Security Policy headers
  • Rate limiting on client

E-commerce Marketplace (e.g. Amazon)

Requirements (R)

Core Features:

  • Product listing and search
  • Product detail page
  • Shopping cart
  • Checkout flow
  • Order history

Functional Requirements:

  • Users can browse and search products
  • Users can add/remove items from cart
  • Users can complete purchase
  • Real-time inventory updates
  • Support for multiple payment methods

Non-Functional Requirements:

  • Performance: Page load < 2s, cart updates instant
  • Scalability: Handle traffic spikes (Black Friday)
  • SEO: Server-side rendering for product pages
  • Security: PCI compliance, secure checkout
  • Mobile: Responsive design

Architecture (A)

┌─────────────────────────────────────────┐
│          API Server                     │
│  - Product catalog                      │
│  - Inventory service                    │
│  - Cart service                         │
│  - Order service                        │
│  - Payment gateway                      │
└─────────────────────────────────────────┘
                    ↕
┌─────────────────────────────────────────┐
│          Controller                     │
│  - API calls                            │
│  - State management                     │
│  - Analytics tracking                   │
└─────────────────────────────────────────┘
                    ↕
┌─────────────────────────────────────────┐
│       Client Store                      │
│  - Product catalog                      │
│  - Cart state                           │
│  - User data                            │
│  - Checkout state                       │
└─────────────────────────────────────────┘
                    ↕
┌─────────────────────────────────────────┐
│       UI Components                     │
│  ┌─────────────────────────────────┐   │
│  │   Product Listing               │   │
│  │   - Filters                     │   │
│  │   - Sort options                │   │
│  │   - Pagination                  │   │
│  └─────────────────────────────────┘   │
│  ┌─────────────────────────────────┐   │
│  │   Product Detail Page           │   │
│  │   - Image gallery               │   │
│  │   - Add to cart                 │   │
│  │   - Reviews                     │   │
│  └─────────────────────────────────┘   │
│  ┌─────────────────────────────────┐   │
│  │   Shopping Cart                 │   │
│  │   - Item list                   │   │
│  │   - Quantity controls           │   │
│  │   - Price summary               │   │
│  └─────────────────────────────────┘   │
│  ┌─────────────────────────────────┐   │
│  │   Checkout Flow                 │   │
│  │   - Shipping info               │   │
│  │   - Payment method              │   │
│  │   - Order review                │   │
│  └─────────────────────────────────┘   │
└─────────────────────────────────────────┘

Data Model (D)

Source Entity Fields
Server Product id, name, description, price, images, category, rating, reviews_count, in_stock, variants
Server CartItem product_id, quantity, variant, price_at_add
Server Order id, user_id, items, total, status, shipping_address, payment_method, created_at
Client Cart items (CartItem[]), subtotal, tax, shipping, total
Client CheckoutState step, shipping_info, payment_info, is_processing
Client Filters category, price_range, rating, in_stock_only

Interface Definition (I)

Server APIs:

// GET /products - Search/browse products
GET /api/products?q={query}&category={cat}&page={page}&limit={limit}
Response: {
  products: Product[],
  total: number,
  page: number,
  total_pages: number
}

// GET /products/:id - Get product details
GET /api/products/:id

// POST /cart - Add to cart
POST /api/cart
Body: {
  product_id: string,
  quantity: number,
  variant?: string
}

// GET /cart - Get current cart
GET /api/cart
Response: {
  items: CartItem[],
  subtotal: number,
  tax: number,
  shipping: number,
  total: number
}

// PUT /cart/:itemId - Update cart item
PUT /api/cart/:itemId
Body: { quantity: number }

// DELETE /cart/:itemId - Remove from cart
DELETE /api/cart/:itemId

// POST /orders - Create order
POST /api/orders
Body: {
  items: CartItem[],
  shipping_address: Address,
  payment_method: PaymentMethod,
  payment_token: string
}

Component APIs:

interface ProductListProps {
  products: Product[];
  onFilterChange: (filters: Filters) => void;
  onSortChange: (sort: SortOption) => void;
  onLoadMore: () => void;
}

interface CartProps {
  items: CartItem[];
  onUpdateQuantity: (itemId: string, quantity: number) => void;
  onRemove: (itemId: string) => void;
  onCheckout: () => void;
}

interface CheckoutProps {
  cart: Cart;
  onSubmit: (orderData: OrderData) => Promise<void>;
  onBack: () => void;
}

Optimizations (O)

Performance:

  • SSR/SSG: Server-side render product pages for SEO
  • Image Optimization: WebP, lazy loading, responsive images
  • Code Splitting: Load checkout code only when needed
  • Optimistic Updates: Instant cart updates
  • Memoization: Cache product listings, filters
  • Virtual Scrolling: For long product lists

Cart Management:

// Optimistic cart updates
function addToCart(product, quantity) {
  // 1. Update UI immediately
  dispatch({ type: 'ADD_TO_CART', product, quantity });

  // 2. Sync with server
  api.addToCart(product.id, quantity)
    .catch(error => {
      // Rollback on failure
      dispatch({ type: 'ROLLBACK_CART', product });
      showError('Failed to add to cart');
    });
}

// Persist cart
localStorage.setItem('cart', JSON.stringify(cartState));
// Or sync to server for logged-in users

Search & Filtering:

  • Debounced Search: 300ms delay
  • Client-side Filtering: For small datasets
  • URL State: Sync filters to URL for bookmarking
  • Faceted Search: Show available filters with counts

Checkout:

  • Progress Indicator: Show current step (1/3)
  • Form Validation: Real-time validation
  • Address Autocomplete: Use Google Places API
  • Payment: Stripe/PayPal integration
  • Guest Checkout: Don't force account creation

User Experience:

  • Persistent Cart: Save across sessions
  • Recently Viewed: Track product history
  • Recommendations: "You may also like"
  • Quick View: Modal for quick product preview
  • Stock Notifications: Alert when out-of-stock item available
  • One-click Checkout: Save payment/shipping info

Security:

  • HTTPS: All pages, especially checkout
  • PCI Compliance: Never store card numbers
  • CSRF Tokens: Protect state-changing operations
  • Rate Limiting: Prevent abuse
  • Input Sanitization: Prevent XSS

Analytics:

  • Track product views, cart additions
  • Conversion funnel analysis
  • A/B testing for checkout flow
  • Abandoned cart tracking

Travel Booking (e.g. Airbnb)

Requirements (R)

Core Features:

  • Search listings (map + list view)
  • Listing detail page
  • Booking flow with calendar
  • Reviews and ratings
  • Host/guest messaging

Functional Requirements:

  • Users can search by location, dates, guests
  • Interactive map showing available listings
  • Users can book with date selection
  • Real-time availability updates
  • Price calculation with fees

Non-Functional Requirements:

  • Performance: Search results < 1s, map smooth at 60fps
  • Mobile responsive (50%+ mobile traffic)
  • Accessibility compliant
  • Support multiple currencies/languages

Architecture (A)

┌─────────────────────────────────────────┐
│          API Server                     │
│  - Search service (Elasticsearch)       │
│  - Listing service                      │
│  - Booking service                      │
│  - Payment service                      │
│  - Messaging service                    │
└─────────────────────────────────────────┘
                    ↕
┌─────────────────────────────────────────┐
│          Controller                     │
│  - Search logic                         │
│  - Map interaction                      │
│  - Booking flow                         │
└─────────────────────────────────────────┘
                    ↕
┌─────────────────────────────────────────┐
│       Client Store                      │
│  - Search results                       │
│  - Map state                            │
│  - Booking state                        │
│  - User data                            │
└─────────────────────────────────────────┘
                    ↕
┌─────────────────────────────────────────┐
│       UI Components                     │
│  ┌─────────────────────────────────┐   │
│  │   Search Bar                    │   │
│  │   - Location autocomplete       │   │
│  │   - Date picker                 │   │
│  │   - Guest selector              │   │
│  └─────────────────────────────────┘   │
│  ┌──────────────┐  ┌──────────────┐   │
│  │  Map View    │  │  List View   │   │
│  │  - Pins      │  │  - Cards     │   │
│  │  - Clusters  │  │  - Filters   │   │
│  └──────────────┘  └──────────────┘   │
│  ┌─────────────────────────────────┐   │
│  │   Listing Detail                │   │
│  │   - Photo gallery               │   │
│  │   - Amenities                   │   │
│  │   - Reviews                     │   │
│  │   - Booking widget              │   │
│  └─────────────────────────────────┘   │
└─────────────────────────────────────────┘

Data Model (D)

Source Entity Fields
Server Listing id, title, description, location (lat/lng), price_per_night, images, amenities, host_id, rating, reviews_count, max_guests
Server Booking id, listing_id, user_id, check_in, check_out, guests, total_price, status
Server Review id, listing_id, user_id, rating, comment, created_at
Client SearchParams location, check_in, check_out, guests, price_range, amenities
Client MapState center, zoom, bounds, visible_listings, selected_listing_id
Client BookingDraft selected_dates, guests, price_breakdown, payment_info

Interface Definition (I)

Server APIs:

// GET /search - Search listings
GET /api/listings/search?
  location={location}&
  check_in={date}&
  check_out={date}&
  guests={num}&
  ne_lat={lat}&ne_lng={lng}&
  sw_lat={lat}&sw_lng={lng}

Response: {
  listings: Listing[],
  total: number
}

// GET /listings/:id - Get listing details
GET /api/listings/:id?check_in={date}&check_out={date}
Response: {
  listing: Listing,
  availability: Date[],
  price_breakdown: {
    base_price: number,
    cleaning_fee: number,
    service_fee: number,
    total: number
  }
}

// POST /bookings - Create booking
POST /api/bookings
Body: {
  listing_id: string,
  check_in: string,
  check_out: string,
  guests: number,
  payment_method: string,
  payment_token: string
}

// GET /listings/:id/reviews - Get reviews
GET /api/listings/:id/reviews?page={page}&limit={limit}

Component APIs:

interface SearchBarProps {
  onSearch: (params: SearchParams) => void;
  initialValues?: SearchParams;
}

interface MapViewProps {
  listings: Listing[];
  center: LatLng;
  zoom: number;
  onBoundsChange: (bounds: Bounds) => void;
  onListingClick: (listingId: string) => void;
  selectedListingId?: string;
}

interface BookingWidgetProps {
  listing: Listing;
  availability: Date[];
  onBook: (bookingData: BookingData) => Promise<void>;
  checkIn?: Date;
  checkOut?: Date;
} string;
}

interface BookingWidgetProps {
  listing: Listing;
  availability: Date[];
  onBook: (bookingData: BookingData) => Promise<void>;
  checkIn?: Date;
  checkOut?: Date;
}

Optimizations (O)

Map Performance:

// Use map clustering for many pins
import { MarkerClusterer } from '@googlemaps/markerclusterer';

const clusterer = new MarkerClusterer({
  map,
  markers,
  algorithm: new SuperClusterAlgorithm({ radius: 100 })
});

// Load listings only in visible bounds
map.addListener('bounds_changed', () => {
  const bounds = map.getBounds();
  fetchListingsInBounds(bounds);
});

// Debounce map movements
const debouncedFetch = debounce(fetchListingsInBounds, 300);

Search:

  • Geospatial Search: Use Elasticsearch with geo queries
  • Autocomplete: Google Places API for location
  • Debounced Search: 300ms delay
  • URL State: Sync search params to URL
  • Caching: Cache search results by parameters

Image Gallery:

  • Progressive Loading: Low-res → high-res
  • Lazy Loading: Load images as user scrolls
  • Preload Next: Preload next image in gallery
  • CDN: Serve from image CDN with transforms

Calendar:

  • Blocked Dates: Show unavailable dates
  • Price Calendar: Show price per night
  • Minimum Stay: Enforce minimum nights
  • Smart Defaults: Suggest popular date ranges

User Experience:

  • Instant Search: Show results as you type
  • Map Sync: Hovering card highlights map pin
  • Saved Searches: Let users save search criteria
  • Wishlists: Save favorite listings
  • Flexible Dates: "±3 days" option
  • Guest Reviews: Show both host and property reviews

Mobile Optimization:

  • Touch Gestures: Pinch zoom on map
  • Bottom Sheet: Listing details slide up
  • Simplified Filters: Mobile-friendly filter UI
  • App-like Experience: PWA with install prompt

Booking Flow:

  • Progressive Disclosure: Show steps gradually
  • Price Transparency: Clear breakdown of fees
  • Calendar Integration: Add to Google/Apple calendar
  • Confirmation: Email + SMS confirmation
  • Cancellation Policy: Clear display

Chat App (e.g. Messenger)

Requirements (R)

Core Features:

  • Real-time messaging
  • Message history
  • Typing indicators
  • Read receipts
  • Media sharing (images, files)

Functional Requirements:

  • Send/receive messages in real-time
  • Support for 1:1 and group chats
  • Messages persist across devices
  • Online/offline status
  • Push notifications

Non-Functional Requirements:

  • Performance: Messages delivered within 100ms
  • Reliability: No message loss
  • Scalability: Support millions of concurrent users
  • End-to-end encryption (optional)
  • Works offline with sync

Architecture (A)

┌─────────────────────────────────────────┐
│          Server                         │
│  - WebSocket server                     │
│  - Message store (DB)                   │
│  - Presence service                     │
│  - Push notification service            │
└─────────────────────────────────────────┘
                    ↕ WebSocket
┌─────────────────────────────────────────┐
│       Connection Manager                │
│  - WebSocket connection                 │
│  - Reconnection logic                   │
│  - Message queue                        │
└─────────────────────────────────────────┘
                    ↕
┌─────────────────────────────────────────┐
│       Client Store                      │
│  - Conversations                        │
│  - Messages (indexed)                   │
│  - User presence                        │
│  - Unread counts                        │
└─────────────────────────────────────────┘
                    ↕
┌─────────────────────────────────────────┐
│       UI Components                     │
│  ┌─────────────────────────────────┐   │
│  │   Conversation List             │   │
│  │   - Recent chats                │   │
│  │   - Unread badges               │   │
│  │   - Last message preview        │   │
│  └─────────────────────────────────┘   │
│  ┌─────────────────────────────────┐   │
│  │   Message Thread                │   │
│  │   - Message bubbles             │   │
│  │   - Timestamps                  │   │
│  │   - Read receipts               │   │
│  │   - Typing indicator            │   │
│  └─────────────────────────────────┘   │
│  ┌─────────────────────────────────┐   │
│  │   Message Input                 │   │
│  │   - Text area                   │   │
│  │   - Media upload                │   │
│  │   - Send button                 │   │
│  └─────────────────────────────────┘   │
└─────────────────────────────────────────┘

Data Model (D)

Source Entity Fields
Server Message id, conversation_id, sender_id, content, type (text/image/file), timestamp, status (sent/delivered/read)
Server Conversation id, participants, type (direct/group), last_message, updated_at
Server User id, name, avatar, status (online/offline/away), last_seen
Client LocalMessage temp_id, is_sending, is_failed, retry_count
Client TypingState conversation_id, users_typing, last_update
Client UnreadCount conversation_id, count, last_read_message_id

Interface Definition (I)

WebSocket Protocol:

// Client -> Server
{
  type: 'message',
  data: {
    conversation_id: string,
    content: string,
    type: 'text' | 'image' | 'file',
    temp_id: string // Client-generated ID
  }
}

{
  type: 'typing',
  data: {
    conversation_id: string,
    is_typing: boolean
  }
}

{
  type: 'read',
  data: {
    conversation_id: string,
    message_id: string
  }
}

// Server -> Client
{
  type: 'message',
  data: {
    id: string, // Server-generated ID
    temp_id: string, // Match with client temp_id
    conversation_id: string,
    sender_id: string,
    content: string,
    timestamp: number
  }
}

{
  type: 'typing',
  data: {
    conversation_id: string,
    user_id: string,
    is_typing: boolean
  }
}

{
  type: 'presence',
  data: {
    user_id: string,
    status: 'online' | 'offline',
    last_seen: number
  }
}

HTTP APIs:

// GET /conversations - Get conversation list
GET /api/conversations

// GET /conversations/:id/messages - Get message history
GET /api/conversations/:id/messages?before={messageId}&limit={limit}

// POST /conversations - Create new conversation
POST /api/conversations
Body: {
  participants: string[],
  type: 'direct' | 'group'
}

// POST /messages/upload - Upload media
POST /api/messages/upload
Body: FormData with file

Optimizations (O)

Real-time Communication:

// WebSocket with reconnection
class ChatWebSocket {
  constructor() {
    this.connect();
    this.messageQueue = [];
  }

  connect() {
    this.ws = new WebSocket('wss://chat.example.com');

    this.ws.onopen = () => {
      this.flushQueue();
    };

    this.ws.onclose = () => {
      // Exponential backoff reconnection
      setTimeout(() => this.connect(), this.getBackoffDelay());
    };

    this.ws.onmessage = (event) => {
      const message = JSON.parse(event.data);
      this.handleMessage(message);
    };
  }

  send(message) {
    if (this.ws.readyState === WebSocket.OPEN) {
      this.ws.send(JSON.stringify(message));
    } else {
      this.messageQueue.push(message);
    }
  }
}

Message Storage:

// Use IndexedDB for local message storage
const db = await openDB('chat-db', 1, {
  upgrade(db) {
    const messageStore = db.createObjectStore('messages', { keyPath: 'id' });
    messageStore.createIndex('conversation_id', 'conversation_id');
    messageStore.createIndex('timestamp', 'timestamp');
  }
});

// Query messages efficiently
const messages = await db.getAllFromIndex(
  'messages',
  'conversation_id',
  conversationId
);

Performance:

  • Virtual Scrolling: Render only visible messages
  • Message Batching: Send multiple messages together
  • Lazy Loading: Load older messages on scroll
  • Image Compression: Compress before upload
  • Debounced Typing: Send typing indicator every 2s max

Optimistic UI:

// Show message immediately, update when confirmed
function sendMessage(content) {
  const tempId = generateTempId();
  const optimisticMessage = {
    temp_id: tempId,
    content,
    sender_id: currentUserId,
    timestamp: Date.now(),
    status: 'sending'
  };

  // Add to UI immediately
  dispatch({ type: 'ADD_MESSAGE', message: optimisticMessage });

  // Send to server
  ws.send({ type: 'message', data: { content, temp_id: tempId } });
}

// When server confirms
ws.onmessage = (event) => {
  const { id, temp_id } = event.data;
  // Replace temp message with real one
  dispatch({ type: 'CONFIRM_MESSAGE', temp_id, id });
};

Typing Indicators:

// Debounce typing events
const sendTyping = debounce((conversationId, isTyping) => {
  ws.send({
    type: 'typing',
    data: { conversation_id: conversationId, is_typing: isTyping }
  });
}, 1000);

// Stop showing after 3 seconds of no updates
setTimeout(() => {
  setTypingUsers([]);
}, 3000);no updates
setTimeout(() => {
  setTypingUsers([]);
}, 3000);

User Experience:

  • Message Grouping: Group consecutive messages from same sender
  • Timestamp Formatting: "Just now", "5m ago", "Yesterday"
  • Link Previews: Show rich previews for URLs
  • Emoji Picker: Quick emoji insertion
  • Reply/Quote: Reference previous messages
  • Search: Full-text search across conversations
  • Unread Badges: Clear visual indicators

Offline Support:

  • Queue Messages: Store in IndexedDB when offline
  • Sync on Reconnect: Send queued messages
  • Offline Indicator: Show connection status
  • Message Status: Show sending/sent/delivered/read

Security:

  • End-to-end Encryption: Optional Signal Protocol
  • Message Expiration: Self-destructing messages
  • Authentication: JWT tokens
  • Rate Limiting: Prevent spam

Notifications:

  • Push Notifications: Web Push API
  • Badge Count: Show unread count
  • Sound Alerts: Configurable sounds
  • Desktop Notifications: When tab not focused

Photo Sharing (e.g. Instagram)

Requirements (R)

Core Features:

  • Photo feed with infinite scroll
  • Photo upload with filters
  • Like, comment, share
  • User profiles
  • Stories (ephemeral content)

Functional Requirements:

  • Users can upload photos/videos
  • Apply filters before posting
  • View feed of followed users
  • Like and comment on posts
  • View user profiles with grid layout

Non-Functional Requirements:

  • Performance: Feed loads in < 2s
  • Image quality: Balance quality vs load time
  • Mobile-first design (80%+ mobile usage)
  • Real-time likes/comments
  • Accessibility for screen readers

Architecture (A)

┌─────────────────────────────────────────┐
│          CDN / Media Server             │
│  - Original images                      │
│  - Thumbnails (multiple sizes)          │
│  - Videos (HLS)                         │
└─────────────────────────────────────────┘
                    ↕
┌─────────────────────────────────────────┐
│          API Server                     │
│  - Feed service                         │
│  - Upload service                       │
│  - Social graph                         │
│  - WebSocket (real-time)                │
└─────────────────────────────────────────┘
                    ↕
┌─────────────────────────────────────────┐
│          Controller                     │
│  - Feed management                      │
│  - Image processing                     │
│  - Real-time updates                    │
└─────────────────────────────────────────┘
                    ↕
┌─────────────────────────────────────────┐
│       Client Store                      │
│  - Feed posts                           │
│  - User profiles                        │
│  - Engagement data                      │
└─────────────────────────────────────────┘
                    ↕
┌─────────────────────────────────────────┐
│       UI Components                     │
│  ┌─────────────────────────────────┐   │
│  │   Feed View                     │   │
│  │   - Post cards (infinite)       │   │
│  │   - Stories bar                 │   │
│  └─────────────────────────────────┘   │
│  ┌─────────────────────────────────┐   │
│  │   Post Card                     │   │
│  │   - Image/video                 │   │
│  │   - Like, comment, share        │   │
│  │   - Caption                     │   │
│  └─────────────────────────────────┘   │
│  ┌─────────────────────────────────┐   │
│  │   Upload Flow                   │   │
│  │   - Image selection             │   │
│  │   - Filter application          │   │
│  │   - Caption editor              │   │
│  └─────────────────────────────────┘   │
│  ┌─────────────────────────────────┐   │
│  │   Profile View                  │   │
│  │   - Photo grid                  │   │
│  │   - Stats (followers/following) │   │
│  └─────────────────────────────────┘   │
└─────────────────────────────────────────┘

Data Model (D)

Source Entity Fields
Server Post id, user_id, image_url, caption, filter, likes_count, comments_count, created_at, location
Server User id, username, profile_pic, bio, followers_count, following_count, posts_count
Server Comment id, post_id, user_id, text, created_at
Server Like id, post_id, user_id, created_at
Server Story id, user_id, media_url, created_at, expires_at, views_count
Client UploadDraft image_data, selected_filter, caption, is_uploading, upload_progress
Client FeedState posts, cursor, has_more, is_loading

Interface Definition (I)

Server APIs:

// GET /feed - Get personalized feed
GET /api/feed?cursor={cursor}&limit={limit}
Response: {
  posts: Post[],
  next_cursor: string,
  has_more: boolean
}

// POST /posts - Create new post
POST /api/posts
Body: FormData {
  image: File,
  caption: string,
  filter: string,
  location?: string
}

// POST /posts/:id/like - Like a post
POST /api/posts/:id/like

// DELETE /posts/:id/like - Unlike a post
DELETE /api/posts/:id/like

// POST /posts/:id/comments - Add comment
POST /api/posts/:id/comments
Body: { text: string }

// GET /users/:username - Get user profile
GET /api/users/:username
Response: {
  user: User,
  posts: Post[],
  is_following: boolean
}

// GET /stories - Get stories from following
GET /api/stories
Response: {
  stories: Array<{ user: User, stories: Story[] }>
}

Component APIs:

interface FeedProps {
  onLoadMore: () => void;
  onPostClick: (postId: string) => void;
}

interface PostCardProps {
  post: Post;
  onLike: () => void;
  onComment: () => void;
  onShare: () => void;
}

interface UploadFlowProps {
  onComplete: (post: Post) => void;
  onCancel: () => void;
}

interface ImageFilterProps {
  imageData: string;
  selectedFilter: string;
  onFilterChange: (filter: string) => void;
  filters: Filter[];
}

Optimizations (O)

Image Processing:

// Client-side image compression before upload
async function compressImage(file) {
  return new Promise((resolve) => {
    const reader = new FileReader();
    reader.onload = (e) => {
      const img = new Image();
      img.onload = () => {
        const canvas = document.createElement('canvas');
        const ctx = canvas.getContext('2d');

        // Resize to max 1080px width
        const maxWidth = 1080;
        const scale = Math.min(1, maxWidth / img.width);
        canvas.width = img.width * scale;
        canvas.height = img.height * scale;

        ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
        canvas.toBlob(resolve, 'image/jpeg', 0.85);
      };
      img.src = e.target.result;
    };
    reader.readAsDataURL(file);
  });
}

// Apply filters using CSS or Canvas
const filters = {
  clarendon: 'contrast(1.2) saturate(1.35)',
  gingham: 'brightness(1.05) hue-rotate(-10deg)',
  juno: 'contrast(1.2) saturate(1.4) brightness(1.1)',
  // ... more filters
};

// Or use Canvas for complex filters
function applyCanvasFilter(imageData, filterMatrix) {
  // Apply convolution matrix
  // ... filter logic
}

Feed Performance:

  • Virtual Scrolling: Render only visible posts
  • Progressive Image Loading:
    • Show blur placeholder (< 1KB)
    • Load thumbnail (200px)
    • Load full resolution on interaction
  • Lazy Load: Images load as they approach viewport
  • Prefetch: Load next page before user reaches end
  • Image Formats: WebP with JPEG fallback

Real-time Updates:

// WebSocket for real-time likes/comments
ws.on('post_update', ({ post_id, likes_count, comments_count }) => {
  dispatch({
    type: 'UPDATE_POST',
    payload: { post_id, likes_count, comments_count }
  });
});

// Optimistic UI for likes
function toggleLike(postId) {
  const isLiked = likedPosts.has(postId);

  // Update UI immediately
  dispatch({
    type: 'TOGGLE_LIKE',
    payload: { post_id: postId, increment: isLiked ? -1 : 1 }
  });

  // Sync with server
  api.toggleLike(postId).catch(() => {
    // Revert on failure
    dispatch({
      type: 'TOGGLE_LIKE',
      payload: { post_id: postId, increment: isLiked ? 1 : -1 }
    });
  });
}

Upload Flow:

  • Progressive Upload: Show progress bar
  • Background Upload: Continue in background
  • Retry Logic: Auto-retry failed uploads
  • Draft Saving: Save drafts locally
  • Chunked Upload: For large videos

Profile Grid:

// Responsive grid with aspect ratio
<div className="grid grid-cols-3 gap-1">
  {posts.map(post => (
    <div className="aspect-square">
      <img src={post.thumbnail_url} className="w-full h-full object-cover" />
    </div>
  ))}
</div>

// Lazy load profile posts
const observer = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      loadMorePosts();
    }
  });
});

Stories:

  • Auto-advance: Progress to next story after 5s
  • Tap Navigation: Left tap = previous, right tap = next
  • Preload: Preload next story
  • Temporary Storage: Cache for 24h, then delete

User Experience:

  • Double-tap to Like: Native-app feel
  • Pull to Refresh: Update feed
  • Swipe Gestures: Navigate between posts (mobile)
  • Haptic Feedback: On like (mobile)
  • Activity Feed: Notifications for likes/comments
  • Explore Page: Discover new content

Accessibility:

  • Alt text for images
  • Screen reader support
  • Keyboard navigation
  • High contrast mode
  • Caption support for videos

Analytics:

  • Track engagement rate
  • Monitor upload success rate
  • A/B test feed algorithms
  • Track filter usage

Email Client (e.g. Microsoft Outlook)

Requirements (R)

Core Features:

  • Email list with folders
  • Read/compose emails
  • Rich text editor
  • Attachments
  • Search functionality

Functional Requirements:

  • Display inbox with unread count
  • Compose new emails with formatting
  • Reply/forward emails
  • Organize with folders/labels
  • Search across all emails

Non-Functional Requirements:

  • Performance: Load inbox < 1s, handle 10k+ emails
  • Reliability: No lost emails/drafts
  • Offline: Read cached emails, queue sends
  • Accessibility: Full keyboard navigation
  • Security: Encryption, spam filtering

Architecture (A)

┌─────────────────────────────────────────┐
│          Email Server                   │
│  - IMAP/POP3/Exchange                   │
│  - SMTP (sending)                       │
│  - Search index                         │
└─────────────────────────────────────────┘
                    ↕
┌─────────────────────────────────────────┐
│          API Gateway                    │
│  - Email sync service                   │
│  - Search service                       │
│  - Attachment service                   │
└─────────────────────────────────────────┘
                    ↕
┌─────────────────────────────────────────┐
│          Sync Controller                │
│  - Periodic sync (IMAP IDLE)            │
│  - Conflict resolution                  │
│  - Offline queue                        │
└─────────────────────────────────────────┘
                    ↕
┌─────────────────────────────────────────┐
│       Client Store                      │
│  - Email cache (IndexedDB)              │
│  - Folder structure                     │
│  - Draft emails                         │
│  - Settings                             │
└─────────────────────────────────────────┘
                    ↕
┌─────────────────────────────────────────┐
│       UI Components                     │
│  ┌──────────┐  ┌─────────────────────┐ │
│  │ Sidebar  │  │  Email List         │ │
│  │ - Inbox  │  │  - Subject          │ │
│  │ - Sent   │  │  - Preview          │ │
│  │ - Drafts │  │  - Unread badge     │ │
│  │ - Folders│  └─────────────────────┘ │
│  └──────────┘  ┌─────────────────────┐ │
│                │  Reading Pane       │ │
│                │  - Email content    │ │
│                │  - Attachments      │ │
│                │  - Actions          │ │
│                └─────────────────────┘ │
│  ┌─────────────────────────────────┐   │
│  │   Compose Window                │   │
│  │   - To/Cc/Bcc fields            │   │
│  │   - Subject                     │   │
│  │   - Rich text editor            │   │
│  │   - Attachments                 │   │
│  └─────────────────────────────────┘   │
└─────────────────────────────────────────┘

Data Model (D)

Source Entity Fields
Server Email id, thread_id, from, to, cc, bcc, subject, body_html, body_text, date, is_read, is_starred, folder, attachments, has_attachments
Server Folder id, name, unread_count, total_count, parent_id
Server Attachment id, filename, size, mime_type, url
Client DraftEmail to, cc, bcc, subject, body, attachments, last_saved, is_sending
Client SyncState last_sync_time, is_syncing, sync_error, pending_actions
Client UIState selected_folder, selected_email_id, is_composing, search_query

Interface Definition (I)

Server APIs:

// GET /emails - List emails in folder
GET /api/emails?folder={folder}&limit={limit}&offset={offset}
Response: {
  emails: Email[],
  total: number,
  unread: number
}

// GET /emails/:id - Get email details
GET /api/emails/:id
Response: { email: Email }

// POST /emails/send - Send email
POST /api/emails/send
Body: {
  to: string[],
  cc?: string[],
  bcc?: string[],
  subject: string,
  body: string,
  attachments?: string[] // IDs from upload
}

// PUT /emails/:id - Update email (mark read, star, move)
PUT /api/emails/:id
Body: {
  is_read?: boolean,
  is_starred?: boolean,
  folder?: string
}

// POST /search - Search emails
POST /api/search
Body: {
  query: string,
  folder?: string,
  from?: string,
  has_attachments?: boolean
}

// POST /attachments/upload - Upload attachment
POST /api/attachments/upload
Body: FormData with file
Response: { id: string, url: string }

Component APIs:

interface EmailListProps {
  folder: string;
  onEmailSelect: (emailId: string) => void;
  selectedEmailId?: string;
}

interface EmailViewerProps {
  email: Email;
  onReply: () => void;
  onReplyAll: () => void;
  onForward: () => void;
  onDelete: () => void;
}

interface ComposeWindowProps {
  mode: 'new' | 'reply' | 'forward';
  initialData?: Partial<DraftEmail>;
  onSend: (email: DraftEmail) => Promise<void>;
  onSaveDraft: (email: DraftEmail) => Promise<void>;
  onDiscard: () => void;
}

Optimizations (O)

Email Sync:

// Use IMAP IDLE for push notifications
// Or long-polling for web
async function startSync() {
  while (true) {
    try {
      const response = await fetch('/api/sync', {
        method: 'POST',
        body: JSON.stringify({ last_sync: lastSyncTime })
      });

      const { new_emails, updated_emails, deleted_emails } = await response.json();

      // Update local store
      updateLocalCache(new_emails, updated_emails, deleted_emails);
      lastSyncTime = Date.now();

    } catch (error) {
      // Exponential backoff
      await sleep(getBackoffDelay());
    }
  }
}

// Sync on visibility change
document.addEventListener('visibilitychange', () => {
  if (!document.hidden) {
    syncEmails();
  }
});

Performance:

  • Virtual Scrolling: List with 10k+ emails
  • Email Pagination: Load 50 emails at a time
  • Lazy Load: Email bodies loaded on selection
  • IndexedDB: Store emails locally
  • Service Worker: Offline support

Search:

// Client-side search for cached emails
function searchLocal(query) {
  return db.emails
    .where('subject').startsWithIgnoreCase(query)
    .or('body').startsWithIgnoreCase(query)
    .or('from').startsWithIgnoreCase(query)
    .toArray();
}

// Server-side search for all emails
async function searchServer(query) {
  return fetch('/api/search', {
    method: 'POST',
    body: JSON.stringify({ query })
  });
}

// Combined search
async function search(query) {
  // Show local results immediately
  const localResults = await searchLocal(query);
  setResults(localResults);

  // Fetch server results
  const serverResults = await searchServer(query);
  setResults(serverResults);
}

Draft Saving:

// Auto-save drafts every 30 seconds
const autoSave = debounce(async (draft) => {
  try {
    await saveDraft(draft);
    showNotification('Draft saved');
  } catch (error) {
    showError('Failed to save draft');
  }
}, 30000);

// Save to IndexedDB immediately
function saveDraftLocally(draft) {
  db.drafts.put({
    ...draft,
    last_saved: Date.now()
  });
}

Attachments:

  • Progressive Upload: Show upload progress
  • Chunked Upload: For large files (> 10MB)
  • Drag and Drop: Easy file attachment
  • Preview: Images, PDFs in browser
  • Download All: Zip multiple attachments

User Experience:

  • Keyboard Shortcuts:
    • c - Compose
    • r - Reply
    • f - Forward
    • / - Search
    • j/k - Next/previous email
    • Enter - Open email
  • Split View: List + reading pane
  • Conversation Threading: Group related emails
  • Smart Compose: Auto-complete suggestions
  • Undo Send: 5-second window to cancel
  • Priority Inbox: AI-powered importance

Offline Support:

// Queue actions when offline
const offlineQueue = [];

function sendEmail(email) {
  if (!navigator.onLine) {
    offlineQueue.push({ type: 'send', data: email });
    showNotification('Email will be sent when online');
    return;
  }

  api.sendEmail(email);
}

// Process queue when back online
window.addEventListener('online', async () => {
  for (const action of offlineQueue) {
    await processAction(action);
  }
  offlineQueue.length = 0;
});

Security:

  • Content Security Policy: Prevent XSS
  • Sanitize HTML: Clean email bodies
  • Phishing Detection: Warn on suspicious links
  • SPF/DKIM: Verify sender authenticity
  • Encryption: TLS for transport, optional PGP

Accessibility:

  • Full keyboard navigation
  • Screen reader optimized
  • ARIA labels for all controls
  • Skip links for navigation
  • High contrast mode

Dropdown Menu

Requirements (R)

Core Features:

  • Opens on click/hover
  • Keyboard navigation
  • Nested submenus
  • Positioning (avoid viewport edges)
  • Close on outside click

Functional Requirements:

  • Toggle open/close
  • Navigate with arrow keys
  • Select item with Enter
  • Close with Escape
  • Support icons and dividers

Non-Functional Requirements:

  • Accessible (ARIA)
  • Smooth animations
  • Mobile touch support
  • RTL language support

Architecture (A)

┌─────────────────────────────────────────┐
│       Dropdown Component                │
│  ┌─────────────────────────────────┐   │
│  │   Trigger Button                │   │
│  │   [Menu ▼]                      │   │
│  └─────────────────────────────────┘   │
│  ┌─────────────────────────────────┐   │
│  │   Menu Panel (Portal)           │   │
│  │   ┌─────────────────────────┐   │   │
│  │   │ Menu Item 1             │   │   │
│  │   ├─────────────────────────┤   │   │
│  │   │ Menu Item 2 →           │   │   │
│  │   │   ┌──────────────────┐  │   │   │
│  │   │   │ Submenu Item 1   │  │   │   │
│  │   │   │ Submenu Item 2   │  │   │   │
│  │   │   └──────────────────┘  │   │   │
│  │   ├─────────────────────────┤   │   │
│  │   │ Menu Item 3             │   │   │
│  │   └─────────────────────────┘   │   │
│  └─────────────────────────────────┘   │
└─────────────────────────────────────────┘

Data Model (D)

interface MenuItem {
  id: string;
  label: string;
  icon?: ReactNode;
  onClick?: () => void;
  disabled?: boolean;
  submenu?: MenuItem[];
  divider?: boolean; // Render as divider
}

interface DropdownState {
  isOpen: boolean;
  activeIndex: number;
  openSubmenus: Set<string>;
  triggerRect: DOMRect;
}

Interface Definition (I)

interface DropdownProps {
  // Trigger
  trigger: ReactNode | ((props: { isOpen: boolean }) => ReactNode);

  // Menu items
  items: MenuItem[];

  // Behavior
  openOn?: 'click' | 'hover';
  closeOnSelect?: boolean; // Default: true

  // Positioning
  placement?: 'bottom-start' | 'bottom-end' | 'top-start' | 'top-end' | 'left' | 'right';
  offset?: [number, number]; // [x, y] offset

  // Callbacks
  onOpenChange?: (isOpen: boolean) => void;
  onSelect?: (item: MenuItem) => void;

  // Customization
  className?: string;
  menuClassName?: string;
}

Optimizations (O)

Implementation:

function Dropdown({ trigger, items, placement = 'bottom-start', ...props }) {
  const [isOpen, setIsOpen] = useState(false);
  const [activeIndex, setActiveIndex] = useState(-1);
  const triggerRef = useRef(null);
  const menuRef = useRef(null);

  // Position menu
  const position = useFloating({
    placement,
    middleware: [offset(8), flip(), shift({ padding: 8 })]
  });

  // Keyboard navigation
  const handleKeyDown = (e) => {
    switch (e.key) {
      case 'ArrowDown':
        e.preventDefault();
        setActiveIndex((i) => Math.min(i + 1, items.length - 1));
        break;
      case 'ArrowUp':
        e.preventDefault();
        setActiveIndex((i) => Math.max(i - 1, 0));
        break;
      case 'Enter':
      case ' ':
        e.preventDefault();
        if (activeIndex >= 0) {
          items[activeIndex].onClick?.();
          setIsOpen(false);
        }
        break;
      case 'Escape':
        setIsOpen(false);
        triggerRef.current?.focus();
        break;
    }
  };

  // Close on outside click
  useClickOutside([triggerRef, menuRef], () => setIsOpen(false));

  return (
    <>
      <button
        ref={triggerRef}
        onClick={() => setIsOpen(!isOpen)}
        aria-expanded={isOpen}
        aria-haspopup="true"
      >
        {typeof trigger === 'function' ? trigger({ isOpen }) : trigger}
      </button>

      {isOpen && createPortal(
        <div
          ref={menuRef}
          role="menu"
          style={{
            position: 'absolute',
            top: position.y,
            left: position.x
          }}
          onKeyDown={handleKeyDown}
        >
          {items.map((item, index) => (
            <MenuItem
              key={item.id}
              item={item}
              isActive={index === activeIndex}
              onClick={() => {
                item.onClick?.();
                if (props.closeOnSelect !== false) {
                  setIsOpen(false);
                }
              }}
            />
          ))}
        </div>,
        document.body
      )}
    </>
  );
}

Positioning Library:

// Use Floating UI (Popper.js successor)
import { useFloating, offset, flip, shift } from '@floating-ui/react';

const { x, y, strategy, refs } = useFloating({
  placement: 'bottom-start',
  middleware: [
    offset(8), // Gap between trigger and menu
    flip(), // Flip if doesn't fit
    shift({ padding: 8 }) // Shift to stay in viewport
  ]
});

Accessibility:

  • role="menu" on container
  • role="menuitem" on items
  • aria-expanded on trigger
  • aria-haspopup="true" on trigger
  • aria-activedescendant for active item
  • Focus management
  • Keyboard navigation (arrows, enter, escape)

User Experience:

  • Smooth Animations: Fade in/out, slide down
  • Hover Intent: Delay before opening submenu (300ms)
  • Touch Support: Handle touch events on mobile
  • Visual Feedback: Highlight active item
  • Dividers: Separate item groups
  • Icons: Support for icons alongside labels

Submenu Handling:

// Open submenu on hover (desktop) or click (mobile)
function handleSubmenuTrigger(item) {
  if (isMobile) {
    // Click to open
    setOpenSubmenus(new Set([item.id]));
  } else {
    // Hover with delay
    hoverTimeout = setTimeout(() => {
      setOpenSubmenus(new Set([item.id]));
    }, 300);
  }
}

Image Carousel

Requirements (R)

Core Features:

  • Display multiple images
  • Navigate with arrows/dots
  • Swipe on mobile
  • Auto-play option
  • Thumbnail preview

Functional Requirements:

  • Navigate between images
  • Support touch gestures
  • Keyboard navigation
  • Lazy load images
  • Responsive sizing

Non-Functional Requirements:

  • Performance: Smooth 60fps animations
  • Accessibility: Screen reader support
  • Mobile: Touch-optimized
  • Works with any number of images

Architecture (A)

┌─────────────────────────────────────────┐
│       Carousel Component                │
│  ┌─────────────────────────────────┐   │
│  │  [←]                        [→] │   │
│  │   ┌─────────────────────────┐   │   │
│  │   │                         │   │   │
│  │   │    Current Image        │   │   │
│  │   │                         │   │   │
│  │   └─────────────────────────┘   │   │
│  │      ● ○ ○ ○ ○                  │   │
│  └─────────────────────────────────┘   │
│  ┌─────────────────────────────────┐   │
│  │   Thumbnail Strip (optional)    │   │
│  │   [▫] [▫] [▪] [▫] [▫]           │   │
│  └─────────────────────────────────┘   │
└─────────────────────────────────────────┘

Data Model (D)

interface CarouselImage {
  id: string;
  url: string;
  thumbnail?: string;
  alt: string;
  caption?: string;
}

interface CarouselState {
  currentIndex: number;
  isTransitioning: boolean;
  direction: 'left' | 'right';
  touchStart: number;
  touchCurrent: number;
}

Interface Definition (I)

interface CarouselProps {
  images: CarouselImage[];

  // Configuration
  initialIndex?: number;
  autoplay?: boolean;
  autoplayInterval?: number; // milliseconds
  loop?: boolean; // Allow wrapping

  // Navigation
  showArrows?: boolean;
  showDots?: boolean;
  showThumbnails?: boolean;

  // Behavior
  swipeThreshold?: number; // pixels
  transitionDuration?: number; // milliseconds

  // Callbacks
  onSlideChange?: (index: number) => void;
  onImageClick?: (image: CarouselImage) => void;

  // Customization
  className?: string;
  arrowRenderer?: (direction: 'left' | 'right') => ReactNode;
}

Optimizations (O)

Implementation:

function Carousel({ images, autoplay, loop = true }) {
  const [currentIndex, setCurrentIndex] = useState(0);
  const [touchStart, setTouchStart] = useState(0);
  const [touchEnd, setTouchEnd] = useState(0);

  // Auto-play
  useEffect(() => {
    if (!autoplay) return;

    const interval = setInterval(() => {
      goToNext();
    }, autoplayInterval || 3000);

    return () => clearInterval(interval);
  }, [currentIndex, autoplay]);

  // Navigation functions
  const goToNext = () => {
    if (currentIndex === images.length - 1) {
      if (loop) setCurrentIndex(0);
    } else {
      setCurrentIndex(currentIndex + 1);
    }
  };

  const goToPrev = () => {
    if (currentIndex === 0) {
      if (loop) setCurrentIndex(images.length - 1);
    } else {
      setCurrentIndex(currentIndex - 1);
    }
  };

  // Touch handlers
  const handleTouchStart = (e) => {
    setTouchStart(e.touches[0].clientX);
  };

  const handleTouchMove = (e) => {
    setTouchEnd(e.touches[0].clientX);
  };

  const handleTouchEnd = () => {
    if (!touchStart || !touchEnd) return;

    const distance = touchStart - touchEnd;
    const threshold = 50;

    if (distance > threshold) {
      goToNext();
    } else if (distance < -threshold) {
      goToPrev();
    }

    setTouchStart(0);
    setTouchEnd(0);
  };

  // Keyboard navigation
  const handleKeyDown = (e) => {
    if (e.key === 'ArrowLeft') goToPrev();
    if (e.key === 'ArrowRight') goToNext();
  };

  return (
    <div
      className="carousel"
      onTouchStart={handleTouchStart}
      onTouchMove={handleTouchMove}
      onTouchEnd={handleTouchEnd}
      onKeyDown={handleKeyDown}
      tabIndex={0}
    >
      <div className="carousel-track" style={{
        transform: `translateX(-${currentIndex * 100}%)`,
        transition: 'transform 300ms ease'
      }}>
        {images.map((image) => (
          <img key={image.id} src={image.url} alt={image.alt} />
        ))}
      </div>

      <button onClick={goToPrev} aria-label="Previous"></button>
      <button onClick={goToNext} aria-label="Next"></button>

      <div className="dots">
        {images.map((_, index) => (
          <button
            key={index}
            onClick={() => setCurrentIndex(index)}
            aria-label={`Go to slide ${index + 1}`}
            aria-current={index === currentIndex}
          />
        ))}
      </div>
    </div>
  );
}

Performance:

  • Lazy Loading: Only load current, previous, and next images
  • Transform: Use transform: translateX() instead of left for better performance (GPU-accelerated)
  • Will-change: Add will-change: transform for smooth animations
  • Preload: Preload next/previous images
  • Image Optimization: Responsive images with srcset

Touch Gestures:

// Smooth swipe with momentum
const useSwipe = (onSwipeLeft, onSwipeRight) => {
  const [touchStart, setTouchStart] = useState(0);
  const [touchEnd, setTouchEnd] = useState(0);
  const [velocity, setVelocity] = useState(0);

  const minSwipeDistance = 50;

  const onTouchStart = (e) => {
    setTouchEnd(0);
    setTouchStart(e.targetTouches[0].clientX);
  };

  const onTouchMove = (e) => {
    setTouchEnd(e.targetTouches[0].clientX);
  };

  const onTouchEnd = () => {
    if (!touchStart || !touchEnd) return;

    const distance = touchStart - touchEnd;
    const isLeftSwipe = distance > minSwipeDistance;
    const isRightSwipe = distance < -minSwipeDistance;

    if (isLeftSwipe) onSwipeLeft();
    if (isRightSwipe) onSwipeRight();
  };

  return { onTouchStart, onTouchMove, onTouchEnd };
};

Accessibility:

  • ARIA labels on navigation buttons
  • aria-current on active dot
  • Keyboard navigation (arrow keys)
  • aria-live region for screen readers
  • Pause on focus for auto-play

User Experience:

  • Zoom: Pinch to zoom on mobile
  • Thumbnails: Click thumbnail to jump to image
  • Captions: Show image caption
  • Fullscreen: Expand to fullscreen mode
  • Counter: "3 / 10" indicator

Modal Dialog

Requirements (R)

Core Features:

  • Opens on trigger
  • Closes on overlay click or X button
  • Traps focus inside
  • Prevents body scroll
  • Keyboard support (Escape)

Functional Requirements:

  • Display content above page
  • Block interaction with background
  • Animate in/out
  • Support various sizes
  • Accessible to screen readers

Non-Functional Requirements:

  • Performance: Smooth animations
  • Accessibility: WCAG 2.1 AA compliant
  • Mobile: Full-screen on small devices
  • Works with nested modals

Architecture (A)

┌─────────────────────────────────────────┐
│       Modal Component                   │
│  ┌─────────────────────────────────┐   │
│  │   Overlay (backdrop)            │   │
│  │                                 │   │
│  │    ┌─────────────────────┐     │   │
│  │    │  Modal Content  [X] │     │   │
│  │    │                     │     │   │
│  │    │  Title              │     │   │
│  │    │  Body content       │     │   │
│  │    │                     │     │   │
│  │    │  [Cancel] [Confirm] │     │   │
│  │    └─────────────────────┘     │   │
│  │                                 │   │
│  └─────────────────────────────────┘   │
└─────────────────────────────────────────┘

Data Model (D)

interface ModalState {
  isOpen: boolean;
  previousFocusElement: HTMLElement | null;
  animationState: 'entering' | 'entered' | 'exiting' | 'exited';
}

Interface Definition (I)

interface ModalProps {
  // Visibility
  isOpen: boolean;
  onClose: () => void;

  // Content
  title?: string;
  children: ReactNode;
  footer?: ReactNode;

  // Behavior
  closeOnOverlayClick?: boolean; // Default: true
  closeOnEscape?: boolean; // Default: true
  preventScroll?: boolean; // Default: true

  // Styling
  size?: 'sm' | 'md' | 'lg' | 'xl' | 'full';
  centered?: boolean;
  className?: string;

  // Callbacks
  onOpen?: () => void;
  onAfterClose?: () => void;
}

Optimizations (O)

Implementation:

function Modal({ isOpen, onClose, title, children, closeOnEscape = true }) {
  const overlayRef = useRef(null);
  const contentRef = useRef(null);
  const previousFocusRef = useRef(null);

  // Store focus before opening
  useEffect(() => {
    if (isOpen) {
      previousFocusRef.current = document.activeElement;
      // Focus first focusable element
      const firstFocusable = contentRef.current?.querySelector(
        'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
      );
      firstFocusable?.focus();
    }

    return () => {
      // Restore focus when closing
      previousFocusRef.current?.focus();
    };
  }, [isOpen]);

  // Trap focus inside modal
  const handleKeyDown = (e) => {
    if (!isOpen) return;

    if (e.key === 'Escape' && closeOnEscape) {
      onClose();
      return;
    }

    if (e.key === 'Tab') {
      const focusableElements = contentRef.current?.querySelectorAll(
        'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
      );
      const firstElement = focusableElements[0];
      const lastElement = focusableElements[focusableElements.length - 1];

      if (e.shiftKey && document.activeElement === firstElement) {
        e.preventDefault();
        lastElement.focus();
      } else if (!e.shiftKey && document.activeElement === lastElement) {
        e.preventDefault();
        firstElement.focus();
      }
    }
  };

  // Prevent body scroll
  useEffect(() => {
    if (isOpen) {
      const scrollBarWidth = window.innerWidth - document.documentElement.clientWidth;
      document.body.style.overflow = 'hidden';
      document.body.style.paddingRight = `${scrollBarWidth}px`;

      return () => {
        document.body.style.overflow = '';
        document.body.style.paddingRight = '';
      };
    }
  }, [isOpen]);

  if (!isOpen) return null;

  return createPortal(
    <div
      ref={overlayRef}
      className="modal-overlay"
      onClick={(e) => {
        if (e.target === overlayRef.current) {
          onClose();
        }
      }}
      onKeyDown={handleKeyDown}
    >
      <div
        ref={contentRef}
        className="modal-content"
        role="dialog"
        aria-modal="true"
        aria-labelledby="modal-title"
      >
        <div className="modal-header">
          <h2 id="modal-title">{title}</h2>
          <button
            onClick={onClose}
            aria-label="Close modal"
          >
            ×
          </button>
        </div>
        <div className="modal-body">
          {children}
        </div>
      </div>
    </div>,
    document.body
  );
}

Animations:

/* Smooth fade + scale animation */
@keyframes modal-enter {
  from {
    opacity: 0;
    transform: scale(0.95) translateY(-20px);
  }
  to {
    opacity: 1;
    transform: scale(1) translateY(0);
  }
}

.modal-content {
  animation: modal-enter 200ms ease-out;
}

.modal-overlay {
  animation: fade-in 200ms ease-out;
}

@keyframes fade-in {
  from { opacity: 0; }
  to { opacity: 1; }
}

Focus Management:

  • Store focus before opening
  • Move focus to modal on open
  • Trap focus inside modal (Tab cycles through)
  • Restore focus on close
  • Focus first focusable element

Prevent Body Scroll:

// Account for scrollbar width to prevent layout shift
const scrollBarWidth = window.innerWidth - document.documentElement.clientWidth;
document.body.style.overflow = 'hidden';
document.body.style.paddingRight = `${scrollBarWidth}px`;

Accessibility:

  • role="dialog" on modal
  • aria-modal="true"
  • aria-labelledby pointing to title
  • aria-describedby for description
  • Focus trap
  • Escape key closes
  • Overlay click closes

User Experience:

  • Stacking: Support multiple modals with z-index
  • Confirmation: Confirm before closing if unsaved changes
  • Loading States: Show spinner in modal
  • Responsive: Full-screen on mobile
  • Smooth Animations: Fade + scale effect

Mobile Optimization:

@media (max-width: 768px) {
  .modal-content {
    width: 100%;
    height: 100vh;
    margin: 0;
    border-radius: 0;
  }
}

Poll Widget

Requirements (R)

Core Features:

  • Display poll question
  • Multiple choice options
  • Vote functionality
  • Show results (bar chart)
  • Real-time updates

Functional Requirements:

  • Users can select one option (or multiple for multi-select)
  • Submit vote
  • View results after voting
  • See vote count and percentages
  • Prevent duplicate voting

Non-Functional Requirements:

  • Performance: Updates in real-time
  • Accessibility: Screen reader support
  • Mobile: Touch-friendly
  • Works offline (queue votes)

Architecture (A)

┌─────────────────────────────────────────┐
│          Server                         │
│  - Poll data                            │
│  - Vote storage                         │
│  - WebSocket for real-time              │
└─────────────────────────────────────────┘
                    ↕
┌─────────────────────────────────────────┐
│       Poll Component                    │
│  ┌─────────────────────────────────┐   │
│  │   Question                      │   │
│  │   "What's your favorite color?" │   │
│  └─────────────────────────────────┘   │
│  ┌─────────────────────────────────┐   │
│  │   ○ Red        [████░░░░] 45%   │   │
│  │   ● Blue       [██████░░] 55%   │   │
│  │   ○ Green      [░░░░░░░░]  0%   │   │
│  └─────────────────────────────────┘   │
│  ┌─────────────────────────────────┐   │
│  │   [Submit Vote]                 │   │
│  │   Total votes: 100              │   │
│  └─────────────────────────────────┘   │
└─────────────────────────────────────────┘

Data Model (D)

interface Poll {
  id: string;
  question: string;
  options: PollOption[];
  total_votes: number;
  multiple_choice: boolean;
  end_date?: string;
  created_by: string;
}

interface PollOption {
  id: string;
  text: string;
  votes: number;
  percentage: number;
}

interface UserVote {
  poll_id: string;
  user_id: string;
  option_ids: string[];
  timestamp: number;
}

Interface Definition (I)

Server APIs:

// GET /polls/:id - Get poll data
GET /api/polls/:id
Response: {
  poll: Poll,
  user_vote?: UserVote,
  has_voted: boolean
}

// POST /polls/:id/vote - Submit vote
POST /api/polls/:id/vote
Body: {
  option_ids: string[]
}
Response: {
  poll: Poll // Updated with new counts
}

// WebSocket - Real-time updates
WS /polls/:id/subscribe
Events: {
  type: 'vote_update',
  poll: Poll
}

Component API:

interface PollWidgetProps {
  pollId: string;

  // Configuration
  showResults?: 'always' | 'after-vote' | 'after-end';
  allowChangeVote?: boolean;

  // Styling
  colorScheme?: 'blue' | 'green' | 'purple';
  compact?: boolean;

  // Callbacks
  onVote?: (optionIds: string[]) => void;
  onResultsView?: () => void;
}

Optimizations (O)

Implementation:

function PollWidget({ pollId }) {
  const [poll, setPoll] = useState(null);
  const [selectedOptions, setSelectedOptions] = useState([]);
  const [hasVoted, setHasVoted] = useState(false);
  const [isSubmitting, setIsSubmitting] = useState(false);

  // Fetch poll data
  useEffect(() => {
    fetchPoll(pollId).then(data => {
      setPoll(data.poll);
      setHasVoted(data.has_voted);
      if (data.user_vote) {
        setSelectedOptions(data.user_vote.option_ids);
      }
    });
  }, [pollId]);

  // Real-time updates
  useEffect(() => {
    const ws = new WebSocket(`wss://api.example.com/polls/${pollId}/subscribe`);

    ws.onmessage = (event) => {
      const { poll: updatedPoll } = JSON.parse(event.data);
      setPoll(updatedPoll);
    };

    return () => ws.close();
  }, [pollId]);

  // Submit vote
  const handleVote = async () => {
    if (selectedOptions.length === 0) return;

    setIsSubmitting(true);

    try {
      const { poll: updatedPoll } = await api.vote(pollId, selectedOptions);
      setPoll(updatedPoll);
      setHasVoted(true);
    } catch (error) {
      showError('Failed to submit vote');
    } finally {
      setIsSubmitting(false);
    }
  };

  // Calculate percentages
  const getPercentage = (votes) => {
    return poll.total_votes > 0
      ? Math.round((votes / poll.total_votes) * 100)
      : 0;
  };

  return (
    <div className="poll-widget">
      <h3>{poll.question}</h3>

      <div className="poll-options">
        {poll.options.map(option => (
          <div key={option.id} className="poll-option">
            {!hasVoted ? (
              <label>
                <input
                  type={poll.multiple_choice ? 'checkbox' : 'radio'}
                  name="poll-option"
                  value={option.id}
                  checked={selectedOptions.includes(option.id)}
                  onChange={(e) => {
                    if (poll.multiple_choice) {
                      setSelectedOptions(prev =>
                        e.target.checked
                          ? [...prev, option.id]
                          : prev.filter(id => id !== option.id)
                      );
                    } else {
                      setSelectedOptions([option.id]);
                    }
                  }}
                />
                {option.text}
              </label>
            ) : (
              <div className="poll-result">
                <span className="option-text">{option.text}</span>
                <div className="result-bar">
                  <div
                    className="result-fill"
                    style={{ width: `${option.percentage}%` }}
                  />
                </div>
                <span className="percentage">{option.percentage}%</span>
                <span className="votes">({option.votes} votes)</span>
              </div>
            )}
          </div>
        ))}
      </div>

      {!hasVoted && (
        <button
          onClick={handleVote}
          disabled={selectedOptions.length === 0 || isSubmitting}
        >
          {isSubmitting ? 'Submitting...' : 'Submit Vote'}
        </button>
      )}

      <div className="poll-footer">
        Total votes: {poll.total_votes}
      </div>
    </div>
  );
}

Animations:

/* Animate result bars */
.result-fill {
  transition: width 500ms ease-out;
  background: linear-gradient(90deg, #4F46E5, #818CF8);
  height: 100%;
  border-radius: 4px;
}

/* Pulse animation for new votes */
@keyframes pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.7; }
}

.poll-option.new-vote {
  animation: pulse 500ms ease-in-out;
}

Optimistic Updates:

// Update UI immediately, rollback on failure
function submitVoteOptimistically(optionId) {
  const previousPoll = { ...poll };

  // Update local state
  const updatedOptions = poll.options.map(opt =>
    opt.id === optionId
      ? { ...opt, votes: opt.votes + 1 }
      : opt
  );

  setPoll({
    ...poll,
    options: updatedOptions,
    total_votes: poll.total_votes + 1
  });

  // Send to server
  api.vote(pollId, [optionId]).catch(() => {
    // Rollback on failure
    setPoll(previousPoll);
    showError('Failed to submit vote');
  });
}

User Experience:

  • Visual Feedback: Animate bar growth
  • Highlight Choice: Show user's selection
  • Vote Count: Real-time update
  • Expired Polls: Show "Poll ended" message
  • Share Results: Copy link to share

Accessibility:

  • Semantic HTML (<fieldset>, <legend>)
  • ARIA labels for radio/checkbox
  • Keyboard navigation
  • Screen reader announces results
  • Color contrast for bars

Mobile Optimization:

  • Touch-friendly tap targets (min 44px)
  • Responsive layout
  • Swipe to view more options (if scrollable)

Summary

This comprehensive guide covers 15 frontend system design questions using the RADIO framework:

  • Requirements: Define scope and constraints
  • Architecture: Component structure and data flow
  • Data Model: Entities and their fields
  • Interface Definition: APIs between components
  • Optimizations: Performance, UX, accessibility

Each section provides detailed breakdowns of requirements, architecture diagrams, data models, API specifications, and optimization strategies tailored to real-world scenarios.

Key Takeaways:

  • Always clarify requirements before designing
  • Draw architecture diagrams to visualize components
  • Define clear APIs between components
  • Consider performance, accessibility, and UX
  • Scale solutions appropriately to the problem─────────────────────────────────────────┐ │ Controller │ │ - Manages data flow │ │ - Handles API calls │ │ - WebSocket connection │ └─────────────────────────────────────────┘ ↕ ┌─────────────────────────────────────────┐ │ Client Store │ │ - Feed posts cache │ │ - User data │ │ - Pagination state │ └─────────────────────────────────────────┘ ↕ ┌─────────────────────────────────────────┐ │ Feed UI │ │ ┌─────────────────────────────────┐ │ │ │ Post Composer │ │ │ └─────────────────────────────────┘ │ │ ┌─────────────────────────────────┐ │ │ │ Feed Post (repeated) │ │ │ │ - Author info │ │ │ │ - Content │ │ │ │ - Interaction buttons │ │ │ └─────────────────────────────────┘ │ └─────────────────────────────────────────┘

### Data Model (D)

| Source | Entity | Belongs To | Fields |
|--------|--------|------------|--------|
| Server | Post | Feed UI | `id`, `created_time`, `content`, `media_url`, `media_type`, `author` (User), `reactions` (object), `comments_count`, `shares_count` |
| Server | User | Client Store | `id`, `name`, `profile_photo_url`, `username` |
| Server | Feed | Feed UI | `posts` (Post[]), `next_cursor`, `has_more` |
| Server | Reaction | Post | `type` (like/love/haha/wow/sad/angry), `count`, `user_reacted` (boolean) |
| Client | NewPost | Post Composer | `content`, `media_file`, `is_uploading`, `upload_progress` |
| Client | UI State | Feed UI | `is_loading`, `error`, `active_post_id` |

### Interface Definition (I)

**Server APIs:**

```javascript
// GET /feed - Fetch feed posts
GET /api/feed?cursor={cursor}&size={size}

Response: {
  posts: Post[],
  pagination: {
    next_cursor: string,
    has_more: boolean
  }
}

// POST /posts - Create new post
POST /api/posts
Body: {
  content: string,
  media?: File,
  media_type?: 'image' | 'video'
}
Response: {
  post: Post
}

// POST /posts/:id/reactions - React to post
POST /api/posts/:id/reactions
Body: {
  type: 'like' | 'love' | 'haha' | 'wow' | 'sad' | 'angry'
}

// WebSocket events
WS /feed/updates
Events:
  - new_post: { post: Post }
  - post_updated: { post_id: string, changes: Partial<Post> }

Client Component APIs:

// Feed Component
interface FeedProps {
  userId: string;
  onPostCreated?: (post: Post) => void;
}

// Post Component
interface PostProps {
  post: Post;
  onReact: (type: ReactionType) => Promise<void>;
  onComment: () => void;
  onShare: () => void;
}

// Post Composer Component
interface PostComposerProps {
  onSubmit: (content: string, media?: File) => Promise<void>;
  placeholder?: string;
}

Optimizations (O)

Performance:

  • Virtual Scrolling: Render only visible posts (react-window, react-virtualized)
  • Image Lazy Loading: Load images as they enter viewport (Intersection Observer)
  • Code Splitting: Lazy load video player, comment section
  • Debounced Reactions: Batch reaction updates
  • Optimistic UI: Immediately show reactions/comments before server confirms

Network:

  • Pagination: Cursor-based pagination (better than offset for real-time feeds)
  • Prefetching: Load next page when user is 80% through current page
  • Request Deduplication: Cache identical API calls
  • WebSocket: Real-time updates without polling
  • CDN: Serve media files from CDN with appropriate cache headers

User Experience:

  • Skeleton Screens: Show loading placeholders
  • Pull to Refresh: Mobile gesture support
  • Infinite Scroll: Automatic loading
  • Error Recovery: Retry failed requests, show error boundaries
  • Offline Support: IndexedDB cache, queue actions when offline

Accessibility:

  • ARIA labels for all interactive elements
  • Keyboard navigation (Tab, Enter, Space)
  • Screen reader announcements for new posts
  • Focus management for modals
  • Proper heading hierarchy

Security:

  • XSS Prevention: Sanitize user content
  • CSRF Protection: Token-based
  • Content Security Policy headers
  • Rate limiting on client

Autocomplete

Requirements (R)

Core Features:

  • Real-time search suggestions as user types
  • Keyboard navigation (up/down arrows, enter, escape)
  • Highlight matching text
  • Support for different data sources (static, API)

Functional Requirements:

  • Show suggestions within 100ms of typing
  • Support minimum character threshold (e.g., 3 chars)
  • Handle empty results gracefully
  • Clear suggestions on selection or escape

Non-Functional Requirements:

  • Performance: Fast response, no janky UI
  • Accessibility: Screen reader compatible
  • Mobile: Touch-friendly
  • Support for 1000+ items efficiently

Architecture (A)

┌─────────────────────────────────────────┐
│          Data Source                    │
│  - API Server / Local Data              │
└─────────────────────────────────────────┘
                    ↕
┌─────────────────────────────────────────┐
│          Controller                     │
│  - Debounce logic                       │
│  - API call management                  │
│  - Cache management                     │
└─────────────────────────────────────────┘
                    ↕
┌─────────────────────────────────────────┐
│      Autocomplete Component             │
│  ┌─────────────────────────────────┐   │
│  │   Input Field                   │   │
│  └─────────────────────────────────┘   │
│  ┌─────────────────────────────────┐   │
│  │   Suggestions Dropdown          │   │
│  │   - Highlighted match           │   │
│  │   - Loading indicator           │   │
│  │   - Empty state                 │   │
│  └─────────────────────────────────┘   │
└─────────────────────────────────────────┘

Data Model (D)

Source Entity Fields
Server/Local Suggestion id, label, value, metadata?
Client InputState query, is_loading, error, selected_index
Client Cache Map<query, { results: Suggestion[], timestamp }>

Interface Definition (I)

Server API:

GET /api/autocomplete?q={query}&limit={limit}

Response: {
  suggestions: Array<{
    id: string,
    label: string,
    value: string
  }>
}

Component API:

interface AutocompleteProps {
  // Data source
  fetchSuggestions: (query: string) => Promise<Suggestion[]>;
  localData?: Suggestion[]; // For client-side filtering

  // Configuration
  minCharacters?: number; // Default: 3
  debounceMs?: number; // Default: 300
  maxResults?: number; // Default: 10

  // Callbacks
  onSelect: (suggestion: Suggestion) => void;
  onInputChange?: (value: string) => void;

  // Customization
  placeholder?: string;
  renderSuggestion?: (suggestion: Suggestion, query: string) => ReactNode;
}

Optimizations (O)

Performance:

  • Debouncing: 300ms delay before API call
  • Request Cancellation: Cancel previous requests (AbortController)
  • Client-side Caching: Cache results for 5 minutes
  • Trie Data Structure: For local filtering (O(m) lookup)
  • Memoization: Memoize rendered suggestions

Network:

  • Query Minimization: Only send necessary characters
  • Compression: gzip responses
  • HTTP/2: Multiplexing for parallel requests

User Experience:

  • Loading States: Show spinner after 100ms delay
  • Error Handling: Show error message, allow retry
  • Empty State: "No results found" message
  • Recent Searches: Show recent searches when input is empty

Accessibility:

  • role="combobox" on input
  • role="listbox" on dropdown
  • aria-expanded, aria-activedescendant
  • Keyboard navigation fully functional
  • Screen reader announcements

Pinterest

Requirements (R)

Core Features:

  • Masonry grid layout (Pinterest-style)
  • Infinite scroll
  • Pin creation and management
  • Search and filtering
  • Board organization

Functional Requirements:

  • Display images in optimized masonry layout
  • Users can save pins to boards
  • Users can create new pins
  • Search pins by keywords
  • View pin details in modal

Non-Functional Requirements:

  • Performance: Smooth scrolling with 1000+ images
  • Images load progressively
  • Mobile responsive
  • Support for various image aspect ratios

Architecture (A)

┌─────────────────────────────────────────┐
│          Server (API)                   │
│  - Pin endpoints                        │
│  - Board endpoints                      │
│  - Image CDN                            │
└─────────────────────────────────────────┘
                    ↕
┌─────────────────────────────────────────┐
│          Controller                     │
│  - API calls                            │
│  - Layout calculations                  │
└─────────────────────────────────────────┘
                    ↕
┌─────────────────────────────────────────┐
│       Client Store                      │
│  - Pins cache                           │
│  - Boards                               │
│  - User data                            │
└─────────────────────────────────────────┘
                    ↕
┌─────────────────────────────────────────┐
│     Masonry Grid Component              │
│  ┌─────────┐ ┌─────────┐ ┌─────────┐  │
│  │  Pin    │ │  Pin    │ │  Pin    │  │
│  │ Card    │ │ Card    │ │ Card    │  │
│  └─────────┘ └─────────┘ └─────────┘  │
│  ┌─────────┐ ┌─────────┐              │
│  │  Pin    │ │  Pin    │              │
│  │ Card    │ │ Card    │              │
│  └─────────┘ └─────────┘              │
└─────────────────────────────────────────┘
          ↕
┌─────────────────────────────────────────┐
│       Pin Detail Modal                  │
└─────────────────────────────────────────┘

Data Model (D)

Source Entity Fields
Server Pin id, image_url, title, description, author (User), board_id, width, height, created_at
Server Board id, name, description, pin_count, cover_image_url
Server User id, username, profile_image_url
Client LayoutState columns, column_heights, item_positions
Client UIState selected_pin_id, is_modal_open, scroll_position

Interface Definition (I)

Server APIs:

// GET /pins - Fetch pins with pagination
GET /api/pins?cursor={cursor}&limit={limit}

Response: {
  pins: Pin[],
  next_cursor: string,
  has_more: boolean
}

// POST /pins - Create new pin
POST /api/pins
Body: {
  image_url: string,
  title: string,
  description?: string,
  board_id: string
}

// GET /pins/:id - Get pin details
GET /api/pins/:id
Response: { pin: Pin }

Component APIs:

interface MasonryGridProps {
  pins: Pin[];
  columnCount?: number; // Responsive: 2-7 columns
  columnGap?: number; // Default: 16px
  onPinClick: (pin: Pin) => void;
  onLoadMore: () => void;
}

interface PinCardProps {
  pin: Pin;
  width: number;
  onClick: () => void;
  onSave: (boardId: string) => void;
}

Optimizations (O)

Performance:

  • Virtualization: Only render visible pins (react-window-masonry)
  • Image Optimization:
    • Serve multiple sizes (thumbnail, medium, full)
    • Use WebP with JPEG fallback
    • Lazy load images with blur placeholder
  • Layout Calculation: Use Web Workers for heavy calculations
  • Memoization: Cache column calculations
  • Intersection Observer: Load images when near viewport

Layout Algorithm:

// Masonry layout algorithm
function calculateMasonryLayout(pins, columnCount) {
  const columnHeights = new Array(columnCount).fill(0);
  const positions = [];

  pins.forEach(pin => {
    // Find shortest column
    const shortestColumn = columnHeights.indexOf(Math.min(...columnHeights));

    positions.push({
      column: shortestColumn,
      top: columnHeights[shortestColumn]
    });

    // Update column height
    const aspectRatio = pin.height / pin.width;
    const pinHeight = columnWidth * aspectRatio;
    columnHeights[shortestColumn] += pinHeight + gap;
  });

  return positions;
}

Network:

  • Progressive Loading: Load low-res first, then high-res
  • CDN: Serve images from global CDN
  • HTTP/2: Parallel image loading
  • Prefetch: Preload next page of pins

User Experience:

  • Skeleton Screens: Show loading placeholders
  • Smooth Animations: Fade in images as they load
  • Responsive: Adjust columns based on screen width
  • Save on Hover: Show save button on pin hover
  • Quick View: Preview pin details on hover

Accessibility:

  • Alt text for all images
  • Keyboard navigation between pins
  • Focus indicators
  • Screen reader friendly

Rich Text Editor

Requirements (R)

Core Features:

  • Text formatting (bold, italic, underline)
  • Headings, lists (ordered/unordered)
  • Links, images
  • Undo/redo functionality
  • Copy/paste support

Functional Requirements:

  • Users can format text with toolbar buttons
  • Users can use keyboard shortcuts
  • Content is saved in structured format (JSON/HTML)
  • Works on desktop and mobile

Non-Functional Requirements:

  • Performance: Smooth typing with large documents (10k+ words)
  • No lag between keypress and display
  • Maintain selection across operations

Architecture (A)

┌─────────────────────────────────────────┐
│       Editor State Manager              │
│  - Document tree (AST)                  │
│  - Selection state                      │
│  - History stack (undo/redo)            │
└─────────────────────────────────────────┘
                    ↕
┌─────────────────────────────────────────┐
│       Rich Text Editor Component        │
│  ┌─────────────────────────────────┐   │
│  │   Toolbar                       │   │
│  │   [B] [I] [U] [Link] [Image]    │   │
│  └─────────────────────────────────┘   │
│  ┌─────────────────────────────────┐   │
│  │   Editable Content Area         │   │
│  │   (contenteditable div)         │   │
│  └─────────────────────────────────┘   │
└─────────────────────────────────────────┘
                    ↕
┌─────────────────────────────────────────┐
│     Formatting Controller               │
│  - Apply formatting commands            │
│  - Parse pasted content                 │
│  - Serialize to storage format          │
└─────────────────────────────────────────┘

Data Model (D)

Document Structure (JSON):

{
  type: 'doc',
  content: [
    {
      type: 'paragraph',
      content: [
        { type: 'text', text: 'Hello ', marks: [] },
        { type: 'text', text: 'world', marks: [{ type: 'bold' }] }
      ]
    },
    {
      type: 'heading',
      attrs: { level: 2 },
      content: [{ type: 'text', text: 'Heading' }]
    }
  ]
}
Entity Fields
Node type, attrs?, content?, marks?, text?
Mark type, attrs?
Selection anchor, head, type
EditorState doc, selection, history

Interface Definition (I)

Editor API:

interface RichTextEditorProps {
  initialContent?: Doc;
  onChange: (content: Doc) => void;
  placeholder?: string;
  readOnly?: boolean;

  // Customization
  enabledFormats?: FormattingOption[];
  customPlugins?: Plugin[];

  // Callbacks
  onImageUpload?: (file: File) => Promise<string>;
  onLinkCreate?: () => Promise<{ url: string, text?: string }>;
}

// Commands API
interface EditorCommands {
  toggleBold: () => void;
  toggleItalic: () => void;
  toggleUnderline: () => void;
  setHeading: (level: 1 | 2 | 3) => void;
  toggleBulletList: () => void;
  toggleOrderedList: () => void;
  insertLink: (url: string, text?: string) => void;
  insertImage: (url: string, alt?: string) => void;
  undo: () => void;
  redo: () => void;
}

Optimizations (O)

Performance:

  • Efficient Re-rendering: Only update changed nodes
  • Debounced Autosave: Save after 500ms of inactivity
  • Virtual Scrolling: For very long documents
  • Throttled onChange: Limit callback frequency

Core Implementation:

// Use contenteditable with controlled state
<div
  contentEditable
  onInput={handleInput}
  onKeyDown={handleKeyDown}
  onPaste={handlePaste}
/>

// Handle formatting
function applyFormat(format) {
  document.execCommand(format, false, null);
  // OR use custom implementation for better control
}

// Keyboard shortcuts
const shortcuts = {
  'Mod-b': () => commands.toggleBold(),
  'Mod-i': () => commands.toggleItalic(),
  'Mod-z': () => commands.undo(),
  'Mod-Shift-z': () => commands.redo()
};

User Experience:

  • Floating Toolbar: Show on text selection
  • Slash Commands: Type "/" for quick formatting
  • Markdown Shortcuts: "# " for heading, "- " for list
  • Drag and Drop: Images, reordering blocks
  • Smart Paste: Clean formatting from Word/Google Docs

Accessibility:

  • Proper ARIA roles and labels
  • Keyboard navigation in toolbar
  • Screen reader announces formatting changes
  • Focus management

Libraries to Consider:

  • ProseMirror (flexible, powerful)
  • Draft.js (React-specific)
  • Slate (React, customizable)
  • Lexical (Meta's new editor framework)
  • Quill (simpler, good for basic use cases)

Google Docs

Requirements (R)

Core Features:

  • Real-time collaborative editing
  • Rich text formatting
  • Comments and suggestions
  • Version history
  • Offline support

Functional Requirements:

  • Multiple users can edit simultaneously
  • Changes propagate in real-time (<200ms latency)
  • Conflict resolution for concurrent edits
  • User cursors visible to all collaborators
  • Auto-save every few seconds

Non-Functional Requirements:

  • Performance: Handle documents up to 50k words
  • Scalability: Support 50+ concurrent editors
  • Reliability: No data loss, even with network issues
  • Works offline with sync when reconnected

Architecture (A)

┌─────────────────────────────────────────┐
│          Server                         │
│  - WebSocket server                     │
│  - Document store                       │
│  - Operational Transform (OT) engine    │
│  - Version history                      │
└─────────────────────────────────────────┘
                    ↕ WebSocket
┌─────────────────────────────────────────┐
│     Collaboration Controller            │
│  - Send local operations                │
│  - Receive remote operations            │
│  - Transform conflicts                  │
│  - Manage user presence                 │
└─────────────────────────────────────────┘
                    ↕
┌─────────────────────────────────────────┐
│       Editor State                      │
│  - Document content                     │
│  - Local pending operations             │
│  - Remote user cursors                  │
└─────────────────────────────────────────┘
                    ↕
┌─────────────────────────────────────────┐
│       Editor UI                         │
│  ┌─────────────────────────────────┐   │
│  │   Toolbar & Menu                │   │
│  └─────────────────────────────────┘   │
│  ┌─────────────────────────────────┐   │
│  │   Document Canvas               │   │
│  │   - User cursors                │   │
│  │   - Inline comments             │   │
│  └─────────────────────────────────┘   │
│  ┌─────────────────────────────────┐   │
│  │   Comments Sidebar              │   │
│  └─────────────────────────────────┘   │
└─────────────────────────────────────────┘

Data Model (D)

Source Entity Fields
Server Document id, title, content (OT structure), version, owner_id, created_at, updated_at
Server Operation type, position, content, user_id, timestamp, version
Server Comment id, doc_id, position, content, author_id, resolved, thread
Client UserPresence user_id, name, color, cursor_position, selection_range
Client LocalState pending_ops, last_synced_version, offline_queue

Interface Definition (I)

WebSocket Protocol:

// Client -> Server
{
  type: 'operation',
  op: {
    type: 'insert' | 'delete' | 'retain',
    position: number,
    content?: string,
    length?: number
  },
  version: number,
  userId: string
}

// Server -> Client
{
  type: 'operation',
  op: Operation,
  version: number,
  userId: string,
  timestamp: number
}

// Presence updates
{
  type: 'presence',
  userId: string,
  cursor: { position: number },
  selection?: { start: number, end: number }
}

Editor API:

interface CollaborativeEditorProps {
  documentId: string;
  userId: string;
  initialContent?: Doc;

  // Collaboration
  onCollaboratorJoin?: (user: User) => void;
  onCollaboratorLeave?: (userId: string) => void;

  // Comments
  enableComments?: boolean;
  onCommentCreate?: (comment: Comment) => void;

  // Version history
  onVersionRestore?: (version: number) => void;
}

Optimizations (O)

Operational Transformation (OT): The key challenge is handling concurrent edits. Two main approaches:

  1. Operational Transform (OT)
// Transform two concurrent operations
function transform(op1, op2) {
  if (op1.type === 'insert' && op2.type === 'insert') {
    if (op1.position < op2.position) {
      op2.position += op1.content.length;
    } else if (op1.position > op2.position) {
      op1.position += op2.content.length;
    }
  }
  // More cases for delete, etc.
  return [op1, op2];
}
  1. CRDT (Conflict-free Replicated Data Types)
  • Alternative to OT, mathematically guaranteed convergence
  • Examples: Yjs, Automerge
  • Better for peer-to-peer scenarios

Performance:

  • Operation Batching: Send multiple ops together
  • Efficient Diffing: Only send changes, not entire document
  • Lazy Loading: Load comments on demand
  • Debounced Presence: Update cursor position every 100ms
  • Local Echo: Show local changes immediately

Network:

  • WebSocket: Persistent connection for real-time updates
  • Reconnection Logic: Exponential backoff, queue operations
  • Conflict Resolution: OT or CRDT for merging changes
  • Compression: gzip WebSocket messages

Offline Support:

// Queue operations when offline
if (!navigator.onLine) {
  offlineQueue.push(operation);
} else {
  sendOperation(operation);
}

// Sync when back online
window.addEventListener('online', () => {
  syncOfflineQueue();
});

User Experience:

  • User Avatars: Show who's editing
  • Colored Cursors: Each user gets unique color
  • "User is typing" Indicator
  • Smart Auto-save: Save after inactivity
  • Conflict Notifications: Alert user to major conflicts
  • Version History: Browse and restore previous versions

Scalability:

  • Sharding: Distribute documents across servers
  • Read Replicas: For viewing-only users
  • Caching: Redis for recent operations
  • Rate Limiting: Prevent spam operations

Video Streaming (e.g. Netflix)

Requirements (R)

Core Features:

  • Video player with adaptive bitrate streaming
  • Browse video catalog
  • Search and recommendations
  • Continue watching
  • Multiple profiles

Functional Requirements:

  • Users can play videos with controls (play/pause, seek, volume)
  • Videos adapt to network conditions
  • Users can browse and search content
  • Progress is saved and synced across devices
  • Support subtitles and multiple audio tracks

Non-Functional Requirements:

  • Performance: Video starts within 2 seconds
  • Quality: Adaptive bitrate (240p to 4K)
  • Smooth playback without buffering
  • Support all major browsers and devices
  • Offline: Download for offline viewing

Architecture (A)

┌─────────────────────────────────────────┐
│          CDN / Media Server             │
│  - Video segments (HLS/DASH)            │
│  - Multiple bitrates                    │
│  - Subtitles                            │
└─────────────────────────────────────────┘
                    ↕
┌─────────────────────────────────────────┐
│          API Server                     │
│  - Catalog data                         │
│  - User profiles                        │
│  - Watch progress                       │
│  - Recommendations                      │
└─────────────────────────────────────────┘
                    ↕
┌─────────────────────────────────────────┐
│       Application Controller            │
│  - Fetch catalog data                   │
│  - Manage playback state                │
│  - Track watch progress                 │
└─────────────────────────────────────────┘
                    ↕
┌─────────────────────────────────────────┐
│       Client Store                      │
│  - Video catalog                        │
│  - User preferences                     │
│  - Playback state                       │
│  - Downloaded content                   │
└─────────────────────────────────────────┘
                    ↕
┌─────────────────────────────────────────┐
│       Video Player Component            │
│  ┌─────────────────────────────────┐   │
│  │   Video Element                 │   │
│  │   <video> with HLS/DASH         │   │
│  └─────────────────────────────────┘   │
│  ┌─────────────────────────────────┐   │
│  │   Controls Overlay              │   │
│  │   - Play/Pause                  │   │
│  │   - Progress bar                │   │
│  │   - Volume                      │   │
│  │   - Fullscreen                  │   │
│  │   - Quality selector            │   │
│  └─────────────────────────────────┘   │
└─────────────────────────────────────────┘
          ↕
┌─────────────────────────────────────────┐
│       Browse UI                         │
│  - Hero banner                          │
│  - Content rows (carousels)             │
│  - Search interface                     │
└─────────────────────────────────────────┘

Data Model (D)

Source Entity Fields
Server Video id, title, description, thumbnail_url, duration, release_year, rating, genres, manifest_url (HLS/DASH)
Server Episode id, series_id, season, episode_number, title, duration, manifest_url
Server WatchProgress user_id, video_id, position, last_watched, completed
Server UserProfile id, name, avatar, preferences, watch_history
Client PlaybackState current_time, duration, is_playing, volume, playback_rate, quality, buffered_ranges
Client PlayerSettings subtitle_track, audio_track, quality_preference, autoplay

Interface Definition (I)

Server APIs:

// GET /videos - Browse catalog
GET /api/videos?category={category}&limit={limit}
Response: {
  videos: Video[]
}

// GET /videos/:id - Get video details
GET /api/videos/:id
Response: {
  video: Video,
  manifest_url: string,
  subtitles: Array<{ language: string, url: string }>,
  audio_tracks: Array<{ language: string, url: string }>
}

// POST /watch-progress - Update watch progress
POST /api/watch-progress
Body: {
  video_id: string,
  position: number,
  duration: number
}

// GET /recommendations - Get personalized recommendations
GET /api/recommendations?user_id={userId}

Video Player API:

interface VideoPlayerProps {
  videoId: string;
  manifestUrl: string;
  
  // Configuration
  autoplay?: boolean;
  startPosition?: number;
  
  // Callbacks
  onTimeUpdate?: (currentTime: number) => void;
  onEnded?: () => void;
  onError?: (error: Error) => void;
  onQualityChange?: (quality: Quality) => void;
  
  // Customization
  controls?: boolean;
  subtitles?: SubtitleTrack[];
  audioTracks?: AudioTrack[];
}

interface VideoPlayerMethods {
  play: () => Promise<void>;
  pause: () => void;
  seek: (time: number) => void;
  setVolume: (volume: number) => void;
  setPlaybackRate: (rate: number) => void;
  setQuality: (quality: string) => void;
  enableSubtitle: (trackId: string) => void;
}

Optimizations (O)

Adaptive Bitrate Streaming:

// HLS (HTTP Live Streaming) or DASH (Dynamic Adaptive Streaming)
// Libraries: hls.js, shaka-player, video.js

const hls = new Hls({
  startLevel: -1, // Auto quality
  maxBufferLength: 30, // 30 seconds buffer
  maxMaxBufferLength: 60
});

// Monitor network and adjust quality
hls.on(Hls.Events.MANIFEST_PARSED, () => {
  const levels = hls.levels; // Available quality levels
  // Switch based on bandwidth
});

Performance:

  • Adaptive Bitrate: Automatically switch quality based on bandwidth
  • Preloading: Load first few seconds ahead
  • Buffering Strategy: 30-60 seconds ahead buffer
  • Lazy Load Thumbnails: Load preview images on demand
  • Code Splitting: Load player library only when needed

Network:

  • CDN: Serve videos from geographically close servers
  • Multi-CDN: Fallback to backup CDN
  • Segment Size: 2-10 second video segments
  • Prefetch: Download next episode in series
  • P2P Delivery: WebRTC for peer-assisted delivery

User Experience:

  • Instant Play: Start with lower quality, upgrade smoothly
  • Skip Intro: Detect and skip intro sequences
  • Continue Watching: Resume from last position
  • Autoplay Next: Countdown timer before next episode
  • Thumbnail Preview: Show preview on hover over progress bar
  • Keyboard Shortcuts: Space (play/pause), Arrow keys (seek), F (fullscreen)

Offline Support:

// Use Service Worker + IndexedDB for downloads
async function downloadVideo(videoId) {
  const manifest = await fetch(`/api/videos/${videoId}/manifest`);
  const segments = await manifest.json();
  
  // Download all segments
  for (const segment of segments) {
    const response = await fetch(segment.url);
    const blob = await response.blob();
    await saveToIndexedDB(videoId, segment.index, blob);
  }
}

Accessibility:

  • Closed captions/subtitles
  • Audio descriptions
  • Keyboard navigation
  • Screen reader support for controls
  • High contrast mode

Analytics:

  • Track play rate, completion rate
  • Monitor buffering events
  • Quality switching frequency
  • Error tracking

Summary

This comprehensive guide covers 15 frontend system design questions using the RADIO framework:

  • Requirements: Define scope and constraints
  • Architecture: Component structure and data flow
  • Data Model: Entities and their fields
  • Interface Definition: APIs between components
  • Optimizations: Performance, UX, accessibility

Each section provides detailed breakdowns of requirements, architecture diagrams, data models, API specifications, and optimization strategies tailored to real-world scenarios.

Key Takeaways:

  • Always clarify requirements before designing
  • Draw architecture diagrams to visualize components
  • Define clear APIs between components
  • Consider performance, accessibility, and UX
  • Scale solutions appropriately to the problem
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment