Rung 7 β€” The Production URL Shortener ServiceΒΆ

Target month: Shipped by end of M11 = end of May 2027 (started in M10) Calendar deadline: May 31, 2027 Phase alignment: Phase 07 β€” Enterprise Spring & Data Signal level: Full-stack Spring β†’ proves you can ship a service, not just a class


What It IsΒΆ

A fully-functional URL shortener, built with Spring Boot 3.x + Java 21, deployed to a real, publicly-reachable URL, with observability, tests, CI/CD, and documentation that meet a production bar.

The URL shortener is chosen deliberately: it is the classic system-design study problem, so shipping a real one is directly study-relevant. It has just enough moving pieces (persistence, caching, rate limiting, redirects, analytics) to demand a real architecture without ballooning in scope.

Feature scope (freeze this list; do not expand)ΒΆ

  • POST /api/v1/shorten β€” accepts a long URL, returns a short code (7 chars, base62)

  • GET /{code} β€” 302-redirect to the long URL

  • GET /api/v1/stats/{code} β€” returns hit count, first-hit, last-hit timestamps

  • DELETE /api/v1/links/{code} β€” auth-required, removes a link

  • Auth: minimal API-key auth on write endpoints (POST, DELETE). API key stored hashed.

  • Rate limiting: IP-based on POST, using the Rung 5 rate limiter. This is the payoff of Rung 5.

  • Persistence: PostgreSQL via Spring Data JPA (managed schema via Flyway)

  • Caching: Redis for hot short-code lookups (write-through cache on redirect)

  • Observability: Micrometer metrics, /actuator/health, /actuator/metrics behind auth, structured JSON logs

  • Docs: OpenAPI 3 spec auto-generated via springdoc, hosted at /swagger-ui

DeploymentΒΆ

Deploy to a real host that the public internet can hit. Options in order of preference:

  1. Fly.io β€” free tier still workable for a small JVM app; global anycast is nice for a URL shortener

  2. Railway β€” slightly more expensive but simpler; good Postgres+Redis integration

  3. Render β€” free web service tier with limitations; acceptable

  4. AWS EC2 t3.small + RDS + ElastiCache β€” expensive, but the strongest resume signal because it’s what enterprises use

  5. Zoho Catalyst / AppSail β€” dogfooding-friendly if you’re a Zoho employee; strong signal in-house

The deployed URL goes in the repo README and on your portfolio landing page as a live β€œtry it now” link.

Where To PublishΒΆ

  • Repo: github.com/RaghulR2003/shortlnk (or a name you like) β€” public, pinned

  • Live URL: e.g., shortlnk.raghulr2003.dev or shortlnk.fly.dev, linked from the repo README hero section

  • API docs: <live-url>/swagger-ui.html β€” accessible without auth for the docs themselves

  • Blog: Hashnode. Title: β€œBuilding a production URL shortener in Spring Boot 3 + Java 21 β€” what I skipped, what I couldn’t skip, and why.” Focus on decisions and tradeoffs, not tutorials.

  • LinkedIn: Long-form. Include the live URL. This is the first rung where a recruiter can literally play with your work.

  • Portfolio landing: β€œTry it now” tile with a live short-URL form embedded

Acceptance CriteriaΒΆ

FunctionalΒΆ

  • All five API endpoints implemented and returning correct status codes

  • Short codes are 7-char base62, collision-checked before insert

  • API-key auth working on POST and DELETE, correctly returning 401/403

  • Rate limiting on POST (e.g., 60 requests / minute / IP) via the Rung 5 library

  • Redirects work in a browser with correct 302 and Location header

  • Hit count and timestamp analytics increment correctly and survive Redis restarts (Postgres is source of truth)

QualityΒΆ

  • Unit test coverage above 70% (measured with JaCoCo, badge in README)

  • Integration tests using Testcontainers for real Postgres + Redis (no mocks for these)

  • Load test with k6 or [Gatling] included in load-tests/, results in README

  • OpenAPI spec auto-published, Swagger UI reachable

  • Structured JSON logging, log correlation via MDC + request ID

  • Micrometer metrics exposed and viewable

DeploymentΒΆ

  • Live URL reachable from the public internet

  • CI pipeline builds, tests, and deploys on every push to main (GitHub Actions + host-specific deploy action)

  • Dockerfile is multi-stage, produces an image under 200 MB using eclipse-temurin:21-jre-alpine or equivalent

  • Health check endpoint used by the host’s load balancer

  • Environment secrets managed via host secrets store, not committed

DocumentationΒΆ

  • Repo README has: architecture diagram (draw.io / excalidraw), live URL, curl examples, local-dev quickstart, deployment instructions

  • ARCHITECTURE.md explains the design decisions and their tradeoffs (this is the doc a senior engineer will actually read)

  • Blog post published with the decisions section as the core (why Postgres and not DynamoDB, why Redis and not Caffeine, why API key and not JWT)

Signal It SendsΒΆ

  • You can ship a service. Not a class. Not a library. A service that a stranger can hit from a browser. This is the difference the M13 pitch hinges on.

  • You know Spring Boot 3. Not β€œSpring MVC from a 2018 tutorial.” Modern Spring, with modern Java. This is directly the stack most Indian MNC Java job postings ask for.

  • You use real infra. Postgres, Redis, Testcontainers, Docker, a real host. Every one of these is a resume-parseable keyword.

  • You wire the rungs together. The rate limiter is Rung 5’s output. This rung consumes your own libraries. Nothing signals seniority faster than a portfolio where the rungs feed each other.

  • You care about observability. Micrometer, structured logs, health endpoints. This is what separates a hobby project from a service that’s ready to be operated.

  • You write about tradeoffs. The blog post is not a tutorial. It is a decisions post. Decisions posts are what senior engineers actually read.

Common Failure ModesΒΆ

  • Scope explosion. β€œI’ll add QR codes and custom aliases and OAuth and expiring URLs.” Every one of these turns a 4-week rung into a 12-week rung. Freeze the feature list on day 1.

  • Local-only demos. β€œIt works on my machine.” If it isn’t deployed to a public URL, this rung has not been completed. The live URL is the whole point.

  • Skipping the analytics. Just returning a short URL isn’t enough; the analytics endpoint is what makes it a service rather than a toy.

  • Mocked persistence. Using H2 or an in-memory Map instead of real Postgres. This kills the resume-signal. Use Postgres from day 1, with Testcontainers for tests.

  • No architecture doc. Just having the code isn’t enough. ARCHITECTURE.md is what senior engineers read before looking at the code.

  • Free-tier expiry. Fly.io and Render will scale-to-zero or expire free tier services. Set a calendar reminder for M13 to verify the live URL is still up during job studies.

  • Not using the Rung 5 rate limiter. Just calling bucket4j or another library defeats the whole point of the rungs feeding each other. Use your own.

Time EstimateΒΆ

  • Domain model + REST endpoints: ~10 hours

  • Spring Data JPA + Postgres + Flyway migrations: ~8 hours

  • Redis caching layer: ~5 hours

  • API-key auth + rate limiting integration: ~6 hours

  • Analytics endpoints + hit tracking: ~5 hours

  • OpenAPI + Swagger UI setup: ~2 hours

  • Micrometer + structured logging + actuator: ~4 hours

  • Unit + integration tests (Testcontainers): ~12 hours

  • Load test with k6: ~4 hours

  • Docker multi-stage image + deploy scripts: ~5 hours

  • CI/CD pipeline: ~4 hours

  • Deployment + DNS + TLS + host-specific config: ~5 hours

  • README, ARCHITECTURE.md, diagrams: ~5 hours

  • Blog post: ~10 hours

  • Total: ~85 hours over 8 weeks (~10-11 hours/week)

This is your all-of-M10-and-M11 rung. If you have not touched Spring by end of M10, you are behind.

PrerequisitesΒΆ

  • Rung 5 shipped (the rate limiter this depends on)

  • All files in 07_enterprise_spring_data/ read and worked through

  • Docker installed and working; you’ve built and run at least one image

  • One prior Spring Boot β€œtutorial” project completed as scaffolding practice (this can be done inside 07_enterprise_spring_data/projects/)

  • Basic Postgres + Redis knowledge (schema design, indexing, TTL, eviction policies)

  • Account and credit card on your chosen host (Fly.io, Railway, etc.) even if you stay in free tier β€” verify before M10 to avoid surprises

Stretch Goals (Optional)ΒΆ

  1. Custom domain with TLS. s.raghulr2003.dev or similar. This adds a real DNS + TLS story to your resume. Cloudflare + Let’s Encrypt is the free path.

  2. Grafana + Prometheus dashboard. Scrape Micrometer metrics into Prometheus, view in a hosted Grafana. Screenshot in the README. Direct signal to any SRE-adjacent recruiter.

  3. Chaos test. Kill the Redis container mid-load-test, show that the service continues to serve (from Postgres) but with degraded latency. This is the exact scenario an study partner will ask about.

  4. Multi-region deployment on Fly.io. Fly makes this near-trivial. Adds a distributed-systems story that would otherwise wait for Rung 8.


Return to README.md Β· Next: 08_rung8_ml_capstone.md