Skip to content

Instantly share code, notes, and snippets.

@decagondev
Last active October 13, 2025 21:59
Show Gist options
  • Save decagondev/99baa6189d535750c37f9ce34e668255 to your computer and use it in GitHub Desktop.
Save decagondev/99baa6189d535750c37f9ce34e668255 to your computer and use it in GitHub Desktop.

URL Shortener PRD

Synopsis

This Product Requirements Document (PRD) outlines the requirements for building a URL Shortener microservice. The application allows users to shorten long URLs into compact, shareable links, redirect to the original URL when accessed, and track basic analytics like click counts. Designed as a backend-focused project using Java Spring Boot, it serves as a portfolio piece. The goal is to create a simple, deployable API that demonstrates core backend skills, with instructions clear enough for a novice developer, LLM, or AI assistant to implement from scratch to production.

Executive Summary

The URL Shortener is a lightweight web service inspired by tools like Bitly or TinyURL. It provides RESTful endpoints for creating short URLs, redirecting to originals, and viewing analytics. Key benefits include ease of implementation, scalability potential, and relevance in real-world applications (e.g., marketing, social media). Target users are developers testing APIs or end-users needing quick link shortening.

  • Project Goals: Build a functional MVP in 1-2 weeks, deployable to a cloud platform like Heroku or Render.
  • Assumptions: Users have basic Java knowledge; no frontend is required (use tools like Postman for testing).
  • Success Metrics: 100% uptime for redirects, <1s response time, accurate click tracking.
  • Timeline: Setup (1 day), Core Features (3-5 days), Testing/Deployment (2-3 days).

Introduction and Purpose

This PRD serves as a blueprint for developing the URL Shortener. It is structured to guide a new developer or AI assistant step-by-step, from environment setup to production deployment. The project emphasizes best practices in API design, database management, and containerization with Docker, making it educational and production-ready.

  • Problem Statement: Long URLs are cumbersome; shortening them improves usability while allowing analytics.
  • Target Audience: API consumers (e.g., via curl or web apps), developers learning backend Java.
  • Out-of-Scope: Advanced features like custom domains, user accounts (beyond basic auth if added), or rate limiting (consider as extensions).

Scope

  • In-Scope: Core shortening, redirection, click tracking with PostgreSQL storage; Docker for local dev; Basic API security.
  • Out-of-Scope: Frontend UI, payment integration, advanced analytics (e.g., geo-tracking).
  • Dependencies: Internet access for external APIs if extended; Free tiers of cloud services for deployment.

Technical Stack

To keep it simple and modern, use the following stack. Instructions assume a Windows/Mac/Linux setup with Java installed.

  • Backend Framework: Spring Boot 3.x (for REST APIs, easy setup with Spring Initializr).
  • Language: Java 17+ (LTS version for stability).
  • Database: PostgreSQL (for persistence of URLs and analytics; use H2 for in-memory testing).
  • Containerization: Docker (for local dev and deployment; include Dockerfile and docker-compose.yml).
  • Build Tool: Gradle (preferred over Maven for faster builds; use build.gradle.kts for Kotlin DSL).
  • Testing: JUnit 5 (unit/integration tests), Postman or curl for API testing.
  • Other Tools:
    • Git for version control.
    • IDE: IntelliJ IDEA Community Edition (free) or VS Code with Java extensions.
    • Deployment: Heroku (free tier) or Render.com; Optionally, AWS/EC2 for prod.
  • Dependencies (add to build.gradle.kts):
    plugins {
        id("org.springframework.boot") version "3.2.0"
        id("io.spring.dependency-management") version "1.1.6"
        kotlin("jvm") version "1.9.20"
    }
    
    dependencies {
        implementation("org.springframework.boot:spring-boot-starter-web")
        implementation("org.springframework.boot:spring-boot-starter-data-jpa")
        implementation("org.postgresql:postgresql:42.7.3")
        implementation("org.springframework.boot:spring-boot-starter-test") { forTest() }
        implementation("org.projectlombok:lombok")
        annotationProcessor("org.projectlombok:lombok")
        // Optional: for Swagger/OpenAPI
        implementation("org.springdoc:springdoc-openapi-starter-webmvc-ui:2.6.0")
        // Optional: for basic auth on analytics
        implementation("org.springframework.boot:spring-boot-starter-security")
    }

Setup Instructions:

  1. Install Java JDK 17+ (via sdkman or official site: https://adoptium.net/).
  2. Install Gradle (or use wrapper: ./gradlew).
  3. Install Docker Desktop (https://www.docker.com/products/docker-desktop).
  4. Use Spring Initializr (https://start.spring.io/) to generate project: Select Gradle (Kotlin DSL), Java, Spring Boot 3.x, add dependencies above.
  5. Clone repo or create new: git init.
  6. Set up PostgreSQL: Use Docker to run a local instance (docker run -p 5432:5432 -e POSTGRES_PASSWORD=password postgres).
  7. Configure application.properties:
    spring.datasource.url=jdbc:postgresql://localhost:5432/postgres
    spring.datasource.username=postgres
    spring.datasource.password=password
    spring.jpa.hibernate.ddl-auto=update
    server.port=8080

Architecture Overview

  • High-Level Design: Monolithic microservice with layers: Controller (API endpoints), Service (business logic), Repository (DB access), Entity (DB models).
  • Data Flow:
    • User POSTs long URL → Generate unique short code → Store in DB → Return short URL.
    • User GETs short URL → Redirect to original → Increment click count.
  • Database Schema:
    • Table: urls
      • id: BIGINT PRIMARY KEY AUTO_INCREMENT
      • original_url: VARCHAR(2048) NOT NULL
      • short_code: VARCHAR(10) UNIQUE NOT NULL (e.g., base62 encoded)
      • click_count: INT DEFAULT 0
      • created_at: TIMESTAMP DEFAULT CURRENT_TIMESTAMP
  • Error Handling: Use @ControllerAdvice for global exceptions (e.g., 404 for invalid short codes).
  • Security: HTTPS in prod; Optional JWT or basic auth for admin endpoints.

Functional Features

Core features to implement, prioritized for MVP.

  1. URL Shortening:

  2. URL Redirection:

    • Endpoint: GET /{shortCode}
    • Response: HTTP 302 Redirect to original URL; Increment click_count.
  3. Analytics Viewing:

    • Endpoint: GET /api/analytics/{shortCode}
    • Response: JSON { "originalUrl": "...", "clicks": 5, "createdAt": "..." }
  4. Optional: Bulk Shortening (for extension):

    • POST /api/bulk-shorten with array of URLs.

Implementation Tips:

  • Use @RestController for APIs.
  • Entity: @Entity public class Url { ... }
  • Repository: extends JpaRepository<Url, Long>
  • Service: Generate short code: Convert ID to base62 (implement helper method).
  • Example base62 generator:
    private static final String ALPHABET = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
    public String toBase62(long id) {
        StringBuilder sb = new StringBuilder();
        while (id > 0) {
            sb.append(ALPHABET.charAt((int) (id % 62)));
            id /= 62;
        }
        return sb.reverse().toString();
    }

Non-Functional Features

Requirements for quality and operations.

  • Performance: <500ms for shortening/redirect; Handle 100 req/s (scale with caching later).
  • Scalability: Design for horizontal scaling; Use Redis for caching if extended.
  • Security: Validate URLs (prevent open redirects); Sanitize inputs; HTTPS in prod.
  • Reliability: 99.9% uptime; Handle DB failures with retries.
  • Usability: Clear API docs (use Swagger: access at /swagger-ui/index.html).
  • Maintainability: Clean code, comments, 80% test coverage.
  • Accessibility: N/A (API-only).
  • Compliance: GDPR-friendly (no user data stored).

User Stories

Written in Gherkin-style for clarity; Prioritize MVP stories.

  • As a user, I want to shorten a URL so I can share compact links.
    • Given a valid long URL, when I POST to /api/shorten, then I get a unique short URL.
  • As a user, I want to access the original URL via the short link.
    • Given a valid short code, when I GET /{shortCode}, then I'm redirected to the original.
  • As an admin, I want to view click analytics for a short URL.
    • Given a short code, when I GET /api/analytics/{shortCode}, then I see click count and details.
  • As a developer, I want error handling for invalid inputs.
    • Given an invalid URL, when shortening, then return 400 Bad Request with message.
  • As a deployer, I want Docker support to run locally/prod.
    • Given Dockerfile, when I build/run, then app starts with DB.

API Endpoints

Detailed specs for implementation.

  • POST /api/shorten
    • Body: { "originalUrl": string }
    • Response: 201 Created, { "shortUrl": string }
  • GET /{shortCode}
    • Response: 302 Found, Location: originalUrl
  • GET /api/analytics/{shortCode}
    • Response: 200 OK, { "originalUrl": string, "clickCount": int, "createdAt": datetime }
  • Error Responses: Standard HTTP codes with JSON { "error": "message" }

Use @GetMapping, @PostMapping in controllers.

Database Schema and Migrations

  • Use JPA/Hibernate for auto-schema creation (spring.jpa.hibernate.ddl-auto=update).
  • For prod, use Flyway for migrations (add dependency: implementation("org.flywaydb:flyway-core")).
  • Initial Data: Seed with sample URLs via CommandLineRunner.
  • Example Flyway migration (src/main/resources/db/migration/V1__init.sql):
    CREATE TABLE urls (
        id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
        original_url VARCHAR(2048) NOT NULL,
        short_code VARCHAR(10) UNIQUE NOT NULL,
        click_count INT DEFAULT 0,
        created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
    );

Testing

  • Unit Tests: Service layer (e.g., short code generation).
  • Integration Tests: API endpoints with @SpringBootTest and MockMvc.
  • E2E Tests: Use Postman collection (create and share JSON).
  • Coverage: Aim for 80%; Use Gradle: ./gradlew jacocoTestReport.
  • Instructions: Run ./gradlew test; Fix failures before deploy.

Deployment Guide

Step-by-step to production.

  1. Local Dev: ./gradlew bootRun or Docker: Build image, run with compose (link to Postgres container).
    • Example Dockerfile:
      FROM openjdk:17-jdk-slim
      WORKDIR /app
      COPY build/libs/*.jar app.jar
      ENTRYPOINT ["java", "-jar", "app.jar"]
    • Example docker-compose.yml:
      version: '3.8'
      services:
        app:
          build: .
          ports:
            - "8080:8080"
          depends_on:
            - db
          environment:
            - SPRING_DATASOURCE_URL=jdbc:postgresql://db:5432/postgres
            - SPRING_DATASOURCE_USERNAME=postgres
            - SPRING_DATASOURCE_PASSWORD=password
        db:
          image: postgres:latest
          environment:
            - POSTGRES_PASSWORD=password
          ports:
            - "5432:5432"
  2. CI/CD: Use GitHub Actions (simple YAML: build, test, deploy to Heroku).
    • Example workflow (.github/workflows/ci.yml):
      name: CI/CD
      on:
        push:
          branches: [ main ]
      jobs:
        build:
          runs-on: ubuntu-latest
          steps:
            - uses: actions/checkout@v3
            - name: Set up JDK 17
              uses: actions/setup-java@v3
              with:
                java-version: '17'
            - name: Build with Gradle
              run: ./gradlew build
            - name: Deploy to Heroku
              env:
                HEROKU_API_KEY: ${{ secrets.HEROKU_API_KEY }}
              run: ./gradlew deployHeroku
  3. Prod Deployment:
    • Heroku: Create app, add Postgres add-on, push via git (heroku git:remote -a app-name).
    • Config: Set environment vars (SPRING_DATASOURCE_URL, etc.).
    • Monitor: Heroku dashboard for logs/metrics.
  4. Domain: Use free subdomain or custom via Cloudflare.
  5. Scaling: Add New Relic or basic monitoring.

Risks and Mitigations

  • Risk: Duplicate short codes → Mitigation: Use unique constraints in DB.
  • Risk: High traffic → Mitigation: Add caching (Redis).
  • Risk: Security vulnerabilities → Mitigation: Regular dependency scans (./gradlew dependencies).

Future Enhancements

  • User authentication for private links.
  • Custom short codes.
  • Geo-analytics.
  • Integration with external APIs (e.g., virus scanning URLs).
  • Frontend (React/Vue) for UI.

Appendices

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment