Over the next 12 weeks, I’m focusing on one goal:
Becoming more confident in system design by going beyond theory and applying it in practice.
I’ll be documenting everything I learn — the concepts, the mistakes, and the design decisions.
Each week focuses on a different topic, with a real system to apply it.
Week 1 — Scalability Basics & Designing a URL Shortener
Overview
In Week 1 of my system design learning plan, I focused on foundational scalability concepts and applied them by designing and implementing a URL shortener system.
The goal was not just to design a working system, but to:
- Build intuition for data-intensive systems
- Practice thinking in trade-offs
- Connect
Key Learnings from Designing Data-Intensive Applications — Chapter 1
Data-Intensive vs Compute-Intensive Systems
Most modern applications are data-intensive, meaning the primary challenges are:
- Volume of data
- Speed of change
- Access patterns (reads vs writes)
CPU is rarely the bottleneck.
This shifted my thinking from “what framework should I use?” to:
- What data do I store?
- How often is it accessed?
- What guarantees does it need?
This mindset directly influenced how I designed the URL shortener.
Reliability, Scalability, and Maintainability
Chapter 1 emphasizes that failures are inevitable, but systems should be designed so faults don’t become failures.
Key takeaways:
- Fault tolerance matters even for “simple” systems
- Scalability is about how systems behave as load increases
- Maintainability is critical because most software costs are long-term
These principles guided decisions like stateless services, caching, and asynchronous processing.
Load Balancing Fundamentals
A load balancer solves two core problems:
- Availability — no single server failure brings the system down
- Scalability — traffic can be distributed across multiple instances
Important insights:
- Load balancers require stateless application servers
- Distribution strategies (round-robin, least connections) affect latency
- Health checks are essential to avoid routing traffic to failed instances
URL Shortener — System Design
Functional Requirements
- Accept long URLs
- Generate unique short keys
- Redirect users to the original URL
- Store mappings reliably
Non-Functional Requirements
- Low latency (read-heavy workload)
- High availability
- Scalability
- Fault tolerance
- Optional analytics
High-Level Architecture
Figure 1: High-level request flow for URL creation, redirection, and analytics.
The system consists of:
- Stateless API servers behind a load balancer
- PostgreSQL for persistent storage
- Redis for low-latency reads
- Asynchronous analytics processing
Redirects are optimized for speed, while analytics are handled in the background.
Design Improvement: Asynchronous Analytics
Initial Approach (Synchronous)
Initially, redirect analytics were updated synchronously inside the redirect request:
val mapping = repository.findByShortUrl(shortUrl)
mapping.redirectCount +=1
repository.save(mapping)
return RedirectToOriginalResponse(mapping.originalUrl)
Problem:
The user waits for a database write that provides no immediate value.
Timeline:
UserRequest
→DBRead(10ms)
→DBWrite(10ms)
→Response
Total ≈20ms
Improved Approach (Asynchronous)
After revisiting DDIA’s discussion on latency and performance, I moved analytics updates to run asynchronously.
Now:
- Redirect happens immediately
- Analytics are processed in the background
- Redirects remain fast even if analytics fail
Timeline:
User Request
→ DBRead (10ms)
→ Async event / queue
→ Response
User latency ≈10ms
Why This Matters
This change:
- Reduced redirect latency by ~50%
- Improved resilience
- Accepted eventual consistency where strict accuracy isn’t required
A key system design principle reinforced here:
Not all data needs the same consistency guarantees.
Implementation
The system was implemented using:
- Kotlin + Spring Boot
- PostgreSQL for persistence
- Redis for caching
- Asynchronous analytics processing
📌 Source code:
👉 https://github.com/Majd-sufian/12-week-System-Design-Learning-Plan/tree/main/url-shorten-project
Reflections
This week helped me:
- Shift from component-level thinking to system behavior thinking
- Make explicit design trade-offs
- Connect theory (DDIA) to real implementation decisions
Designing even a “simple” system like a URL shortener revealed how quickly concerns like latency, fault tolerance, and scalability appear.
What’s Next — Week 2
- Deeper dive into databases
- Data modeling & indexing
- Designing read-heavy systems under higher load

Top comments (0)