thecodex.expert · The Codex Family of Knowledge
Tier 2 · Intermediate · Python Project

URL Shortener

Turn long URLs into short codes and back again, saved to a file. Learn two-way lookups and generating unique keys.

🧠 Teaches how to think spoonfed, every age Last verified:

1 The Problem

We want a URL shortener: give it a long link, it returns a short code; give back the code, it returns the original link. It teaches two-way lookups (code↔URL), generating unique keys, and persisting a small store — the core of any link service.

Where this shows up: bit.ly and every link shortener, QR-code targets, affiliate links, any system that maps a short key to a longer value — which includes caches, session stores, and lookup services generally.

2 How to Think About It

Think about the mapping, before any code:

The plan — in plain English
1. Keep a store mapping short code → long URL. → 2. To shorten: make a new random short code, save the mapping, return the code. → 3. To expand: look the code up and return the URL. → 4. Save the store to a file so links survive.

Long URL comes in

Generate a short code

Save code to URL mapping

Return the short code

Short code comes in

Look up the URL

Return the long URL

3 The Build — explained part by part

Here is the complete shortener. Each part is explained below.

Pythonshortener.py
import json
import os
import secrets
import string

FILE = "links.json"

def load():
    if os.path.exists(FILE):
        with open(FILE) as f:
            return json.load(f)
    return {}

def save(links):
    with open(FILE, "w") as f:
        json.dump(links, f)

def make_code(length=6):
    # A random short code from letters and digits.
    chars = string.ascii_letters + string.digits
    return "".join(secrets.choice(chars) for _ in range(length))

def shorten(links, url):
    code = make_code()
    while code in links:        # avoid a clash with an existing code
        code = make_code()
    links[code] = url
    save(links)
    return code

def expand(links, code):
    return links.get(code)      # None if the code is unknown

if __name__ == "__main__":
    links = load()
    code = shorten(links, "https://example.com/a/very/long/link")
    print(f"Short code: {code}")
    print(f"Expands to: {expand(links, code)}")
What each part does — in plain words
links — a dictionary mapping each short code to its long URL. The dictionary is the database here.

make_code(length=6) — build a random 6-character code from letters and digits using secrets (secure, unguessable).

while code in links: code = make_code() — the safety step: if our random code happens to already exist, generate another. This guarantees every code is unique.

links[code] = url — store the mapping, then save it to the file.

links.get(code) — look up a code. Using .get returns None for an unknown code instead of crashing, so we can handle “not found” gracefully.
Common mistakes — and how to avoid them
✗ Not checking for code clashes — a repeated random code would overwrite an existing link.
✓ Loop with while code in links until you get a fresh one.
✗ Using links[code] to expand — an unknown code crashes with KeyError.
✓ Use links.get(code) so missing codes return None.
✗ Using random instead of secrets — predictable codes can be guessed.
✓ Use secrets.choice for unguessable codes.

4 Test & Prove Each Part

We test the round-trip (shorten then expand) and that codes are unique and the right length.

A shortened URL expands back to the original
Each short code is the expected length
An unknown code returns nothing (None)
Pythontest_shortener.py
import secrets
import string


def make_code(length=6):
    chars = string.ascii_letters + string.digits
    return "".join(secrets.choice(chars) for _ in range(length))


def shorten(links, url):
    code = make_code()
    while code in links:
        code = make_code()
    links[code] = url
    return code


def expand(links, code):
    return links.get(code)


def test_round_trip():
    """Shorten then expand returns the original URL."""
    links = {}
    code = shorten(links, "https://example.com")
    assert expand(links, code) == "https://example.com"


def test_code_length():
    """Codes are 6 characters by default."""
    assert len(make_code()) == 6


def test_unknown_code():
    """An unknown code returns None."""
    assert expand({}, "missing") is None

Run with pytest -v. The round-trip test is the most important: it proves the two halves (shorten and expand) fit together. Testing a there-and-back journey is a powerful way to verify a mapping.

5 The Interface

SHORTENlong urlreturns a short code
What it expects
shorten("https://example.com/very/long")
EXPANDshort codereturns the original url
What it returns
Short code: aB3xK9
Expands to: https://example.com/very/long

6 Run It & Automate It

Run it locally
python3 shortener.py
Shorten a link and expand it back. Links are saved in links.json.

Jenkins runs the tests automatically — each line explained below.

What you should see when it works
Terminala real run
Short code: aB3xK9
Expands to: https://example.com/a/very/long/link
If it breaks — how to fix it
🚨 KeyError when expanding.
Use links.get(code) instead of links[code].
🚨 Links lost between runs.
You did not save. Call save(links) after shortening.
GroovyJenkinsfile
// Jenkinsfile — automated tests on every change.
pipeline {
    agent any
    stages {
        stage('Get the code') { steps { checkout scm } }       // download the code
        stage('Install') { steps { sh 'python3 -m venv venv && . venv/bin/activate && pip install pytest' } }  // test tool
        stage('Test') { steps { sh '. venv/bin/activate && pytest -v' } }   // run every test
    }
    post {
        success { echo 'All tests passed.' }
        failure { echo 'A test failed — look above.' }
    }
}
🎯 Try this next — make it yours
  1. Custom codes. Let users pick their own code if it is free. (Teaches: checking availability.)
  2. Click counts. Track how many times each code is expanded. (Teaches: richer values.)
  3. Make it a web service. Combine with the Simple Web Server project. (Teaches: connecting projects.)
What you learned
You learned two-way lookups with a dictionary, generating unique keys safely (with a clash check), and graceful not-found handling with .get. This key-to-value mapping is the basis of caches, stores, and link services. Related: secrets, json.