Skip to main content

Java

Designing a URL Shortener

Introduction # As part of this post, we’ll be covering the design of a modern, highly available, and scalable URL shortening service like TinyURL or Bit.ly. 1. Requirements # Functional Requirements # Shorten - Given a long URL, generate a unique, short alias (e.g. https://tny.li/abc123). Redirect - Given a short URL, redirect the user to the original long URL with minimal latency. Custom Aliases - Allow users to pick a custom human-readable alias (e.g. tny.li/my-resume). Link Expiration - Support optional TTL (Time to Live) on links. Analytics - Track per-link click counts, device types, referrers, and geographic distribution. Non-Functional Requirements # Low Latency - Redirects must be served in < 10ms at the P99 level. Reads far outnumber writes. High Availability - The redirect path is the core business operation. Any downtime directly impacts every shortened link on the internet that points to us. Target 99.99% availability. Scalability - The system should sustain 100K+ redirects/s and 1K new URLs/s with headroom to scale horizontally. Durability - Once a short URL is created, the mapping must never be lost. Uniqueness Guarantee - Two different long URLs must never produce the same short URL. Back-of-the-Envelope Calculation # These numbers allow us to make concrete decisions about storage, caching and partitioning in later sections.

Mastering Concurrency In Java - Part 3: Execution Models

In Part 1 and Part 2, we covered the fundamentals and building blocks of concurrency. In this part, we will discuss the execution models of concurrency - classic thread pools, task queues, Future/Callable, CompletableFuture, and, from Java 21+, virtual threads and virtual-thread-per-task executors. Choosing among them is ultimately about latency, throughput, and operational simplicity, not about syntax.

Mastering Concurrency In Java - Part 2: The Fundamentals

In Part 1, we discussed the core concurrency hazards and control concepts in Java: race conditions, visibility, atomicity, deadlocks, starvation, livelock, contention, backpressure, interruption, and cancellation. In this part, we will discuss the coordination primitives - synchronized, volatile, Atomics, Locks, Semaphores, Blocking Queues, and Concurrent Collections.

Mastering Concurrency In Java - Part 1: The Fundamentals

Introduction # Here, we will discuss the core concurrency hazards and control concepts in Java: race conditions, visibility, atomicity, deadlocks, starvation, livelock, contention, backpressure, interruption, and cancellation. Race Conditions # A race condition occurs when the correctness of a task depends on the relative timing or interleaving of threads accessing shared mutable state. The bug is not “thread A ran before B”, it is “if they interleave in this specific way, invariants break.”

How to Cache?

Modern APIs frequently access databases, or complex business logic that introduce significant latency and consume CPU and I/O resources. Without caching, every request pays the full cost of database queries, network calls, and computation. This can lead to slow response times and poor scalability as traffic increases.