My Solution for Designing a Simple URL Shortening Service: A TinyURL Approach with Score: 8/10

by voyager_nebulous785

System requirements


Functional:

• Accept long URLs and return shortened URLs

• Redirect users from short URLs to original URLs

• Handle custom aliases (e.g., /my-link)

• Track number of visits (analytics, optional)

• Support expiration date for links (optional)

• Prevent spam or blacklisted URLs



Non-Functional:

• High availability (99.9% uptime)

• Low latency for redirects (<50ms ideally)

• Scalable to billions of URLs

• Secure (avoid malicious redirects, sanitize input)

• Consistent hash or alias generation




Capacity estimation

Assume:

1 billion short URLs total

100 million active users per month

• Each short URL is around 7 characters


Estimates:

• Storage per URL (original + metadata) ~500 bytes

• 1 billion × 500B ≈ 500 GB of storage

Read-heavy workload: 90% redirects, 10% new URL creation

• Peak RPS (requests per second): ~100k





API design

POST /shorten

Body:

{

"original_url": "https://example.com/page",

"custom_alias": "my-link", // optional

"expires_in_days": 30 // optional

}

Response:

{

"short_url": "https://sho.rt/my-link"

}


GET /{alias}

→ Redirects to original_url (HTTP 302)



GET /analytics/{alias}

Response:

{

"clicks": 1534,

"created_at": "...",

"last_visited": "..."

}



Database design



Table: urls

Field Name Type Description

id UUID / INT Unique ID (auto-generated primary key)

short_code VARCHAR(10) Unique short string (e.g., "abc123")

original_url TEXT Full original URL

created_at TIMESTAMP When the short URL was created

expiry_date TIMESTAMP Optional, used for setting expiry on links

clicks INTEGER How many times the short URL was used


Index: short_code should be indexed for fast lookup.






High-level design

Components:


Client (UI or API consumer)


Web Server / API Layer (Node.js, Python, etc.)


Database (PostgreSQL, MongoDB, Redis for cache)


Short Code Generator


Cache (optional, e.g., Redis)


Analytics Logger (optional)


How It Works:


POST /shorten


User sends a long URL.


System checks for valid URL.


A unique short code is generated (random string or via hashing + base62).


It stores this mapping in the database.


Returns the short URL: https://short.ly/abc123


GET /:short_code


System receives the short code from the path.


It checks cache (optional) or DB for the original URL.


Logs the access (for analytics).


Redirects to the long/original URL.






Request flows


1. Shortening a URL


Client → Server (POST /shorten)

→ Validate + generate short code

→ Save to Database

→ Return shortened URL to client


2. Expanding a Short URL


Client (GET /abc123)

→ Server → Look up `abc123` in cache → if not found, check DB

→ If found, redirect to original URL

→ Optionally update click count





Detailed component design


API Gateway / Web Server


1. Handles incoming HTTP requests (/shorten, /:code)


Framework: Express (Node.js) / Flask (Python) / FastAPI


Responsibilities:


Validate user input


Communicate with service layer


Handle responses and redirections


2. URL Shortener Service (Core Logic)


Generate Short Code


Methods:


Random string (Base62 of 6-10 chars)


Hashing (md5/sha256(longURL) + truncate)


Incremental ID → Base62


Ensure uniqueness (check collision in DB)


Store Mapping


Write short_code + original_url to DB


Handle Redirects


Look up short_code


Return original URL


Optionally increment click count


3. Database Layer


SQL DB (PostgreSQL / MySQL) or NoSQL (MongoDB)


Schema as described earlier


Indexes on short_code


4. Cache (Optional but recommended)


Redis to store frequently accessed short_code → long_url pairs


Fast lookup to reduce DB hits


5. Analytics & Logging


Optional background service


Logs every access (IP, timestamp, user agent)


Stores click count in DB or analytics engine




Trade offs/Tech choices

Decision Area Option A Option B Chosen Why

Short Code Generation Random string Base62(ID) Random + uniqueness check Simple, less predictable

Storage SQL NoSQL SQL (PostgreSQL) ACID compliance, structured

Caching Redis Memcached Redis Key expiration, persistence

Hosting Monolith Microservices Monolith (initially) Easier to deploy & test

Code Lookup Cache + DB fallback DB only Cache-first Faster response





Failure scenarios/bottlenecks



Scenario Impact Mitigation

Database failure Cannot shorten or redirect DB replication, failover

Cache miss or crash Slower redirects Fallback to DB, lazy cache reload

Code collision Existing short_code gets overwritten Ensure uniqueness check before save

Bot abuse (mass URL shortening) Service degraded Rate limiting, CAPTCHA

Hot short URL (millions of redirects) DB overload Use Redis/LRU caching, CDN edge cache

Server crash Downtime Container orchestration (Docker + Kubernetes), load balancer



Future improvements


Custom Alias Support


Allow users to create /customName short URLs.


User Authentication & Dashboards


Logged-in users can manage their short links, track clicks.


Expiration & One-time URLs


Auto-expiring links or links that can only be used once.


Preview Page for Safety


Like bit.ly/preview?url=abc to prevent malicious redirection.


Analytics Dashboard


Visual charts of click trends, geolocation, devices.


Multi-region DB + CDN


For global access and low latency.


QR Code Generator


Generate QR code per short URL.


A/B Testing Framework


Randomized redirect destinations to test landing pages.