Part 3: Building a Complete URL Shortener
In Part 1, we saw the problem: vibe coding collapses under its own weight.
In Part 2, we introduced the methodology: Functional Block Design — Decomposition, Block Specs, Generation, Integration.
Now we put it all together.
This part contains the complete, runnable URL shortener built with FBD. Every block is here. Every .py file is complete. You can copy these files, run them, and see a working system.
By the end of this post, you will have everything you need to try FBD on your own project.
3.1 The Complete File Structure
Here is every file in the project:
text
project/
├── url_validator.py # Complete
├── key_generator.py # Complete
├── storage_manager.py # Complete
├── redirect_handler.py # Complete
├── analytics_recorder.py # Complete
├── api_router.py # Complete
└── main.py # Integration - Run this
Every block follows the same pattern:
Header with Description (for humans) and Prompt (for AI)
Generated code below the header
One public function per block
3.2 All Six Blocks (Complete)
Here is every .py file in the project. You can copy them exactly as shown.
url_validator.py
============================================================
DESCRIPTION
============================================================
This block validates a raw URL string and returns a normalized version.
It handles missing protocols, invalid characters, and malformed domains.
============================================================
PROMPT
============================================================
Generate a Python function for a URL Validator block.
Function signature:
def validate_url(raw_url: str) -> str:
Inputs:
raw_url: a string, possibly missing protocol or malformed
Outputs:
validated_url: a normalized URL string with https:// prefix if missing
Error handling:
- If raw_url is empty, raise ValueError
- If URL format is invalid, raise ValueError
Requirements:
1. Add https:// prefix if no protocol is present.
2. Use urlparse from urllib.parse for validation.
3. Only accept http and https protocols.
Implementation notes:
- Strip whitespace from input.
- Return the normalized URL.
- Include docstring and type hints.
Example:
url = validate_url("example.com") # returns "https://example.com"
============================================================
from urllib.parse import urlparse
def validate_url(raw_url: str) -> str:
"""Validate and normalize a URL string.
Args:
raw_url: A URL string, possibly missing protocol.
Returns:
A normalized URL string with https:// prefix.
Raises:
ValueError: If raw_url is empty or invalid.
"""
if not raw_url:
raise ValueError("URL cannot be empty")
raw_url = raw_url.strip()
# Add https:// if no protocol is present
if not urlparse(raw_url).scheme:
raw_url = "https://" + raw_url
parsed = urlparse(raw_url)
if parsed.scheme not in ("http", "https"):
raise ValueError(f"Invalid protocol: {parsed.scheme}. Only http and https are allowed.")
if not parsed.netloc:
raise ValueError(f"Invalid URL: missing domain. {raw_url}")
return raw_url
key_generator.py
============================================================
DESCRIPTION
============================================================
This block generates a unique 6-8 character alphanumeric key for a given URL.
It handles key collisions by retrying up to 10 times.
============================================================
PROMPT
============================================================
Generate a Python function for a Key Generator block.
Function signature:
def generate_key(original_url: str) -> str:
Inputs:
original_url: a string, already validated
Outputs:
key: a string, 6 to 8 alphanumeric characters (digits 0-9, uppercase A-Z, lowercase a-z)
Error handling:
- If original_url is empty, raise ValueError
- If unable to generate a unique key after 10 attempts, raise RuntimeError
Requirements:
1. The key must be unique. No two URLs get the same key.
2. Use base62 encoding (digits + uppercase + lowercase).
3. Before returning a key, check if it already exists by calling
storage_key_exists(key). This helper will be provided by the Storage Manager block.
4. If collision happens, generate a new key and try again.
5. Keep trying until you find a unique key or you have attempted 10 times.
Implementation notes:
- Use the random module to generate candidate keys
- Include a docstring and type hints
- Keep the function pure (no side effects beyond storage_key_exists)
Example:
key = generate_key("https://example.com") # returns "aB3xY9"
============================================================
import random
import string
This will be imported from storage_manager at runtime
For standalone testing, you can mock it
def storage_key_exists(key: str) -> bool:
"""Mock function - will be replaced by actual storage_manager."""
# This is a placeholder. The real storage_manager will provide this.
return False
def generate_key(original_url: str) -> str:
"""Generate a unique 6-8 character alphanumeric key for a given URL.
Args:
original_url: A validated URL string.
Returns:
A unique 6-8 character alphanumeric key.
Raises:
ValueError: If original_url is empty.
RuntimeError: If unable to generate a unique key after 10 attempts.
"""
if not original_url:
raise ValueError("URL cannot be empty")
chars = string.digits + string.ascii_letters
for attempt in range(10):
length = random.randint(6, 8)
key = ''.join(random.choices(chars, k=length))
if not storage_key_exists(key):
return key
raise RuntimeError("Failed to generate unique key after 10 attempts")
storage_manager.py
============================================================
DESCRIPTION
============================================================
This block stores (key, URL) pairs in an in-memory dictionary
and retrieves URLs by key. For production, replace with a database.
============================================================
PROMPT
============================================================
Generate a Python module for a Storage Manager block.
Functions:
def save_url(key: str, url: str) -> None:
def get_url(key: str) -> str | None:
def key_exists(key: str) -> bool:
Inputs:
key: a string key
url: a string URL
Outputs:
save_url: None
get_url: URL string or None if not found
key_exists: True if key exists, False otherwise
Error handling:
- If key or url is empty, raise ValueError
Requirements:
1. Use an in-memory dictionary for storage.
2. Thread-safe is not required for this example.
3. key_exists is used by key_generator to check collisions.
Implementation notes:
- Keep the dictionary as a module-level variable.
- Include docstrings and type hints.
Example:
save_url("abc123", "https://example.com")
url = get_url("abc123") # returns "https://example.com"
exists = key_exists("abc123") # returns True
============================================================
_store: dict[str, str] = {}
def save_url(key: str, url: str) -> None:
"""Store a (key, URL) pair.
Args:
key: The short key.
url: The original URL.
Raises:
ValueError: If key or url is empty.
"""
if not key:
raise ValueError("Key cannot be empty")
if not url:
raise ValueError("URL cannot be empty")
_store[key] = url
def get_url(key: str) -> str | None:
"""Retrieve a URL by its key.
Args:
key: The short key.
Returns:
The original URL if found, None otherwise.
"""
return _store.get(key)
def key_exists(key: str) -> bool:
"""Check if a key already exists.
Args:
key: The short key.
Returns:
True if the key exists, False otherwise.
"""
return key in _store
redirect_handler.py
============================================================
DESCRIPTION
============================================================
This block accepts a short key and returns an HTTP redirect response
or a 404 response if the key is not found.
============================================================
PROMPT
============================================================
Generate a Python function for a Redirect Handler block.
Function signature:
def create_redirect_response(original_url: str) -> tuple[str, int]:
Inputs:
original_url: a string, the original long URL
Outputs:
A tuple of (message, status_code)
- On success: (redirect_url, 302)
- On not found: ("Not found", 404)
Requirements:
1. Return a 302 Found redirect response.
2. If original_url is None or empty, return 404.
Implementation notes:
- This is a simplified response. In a real web framework,
you would return a proper Response object.
- Include docstring and type hints.
Example:
response = create_redirect_response("https://example.com")
# returns ("https://example.com", 302)
============================================================
def create_redirect_response(original_url: str | None) -> tuple[str, int]:
"""Create an HTTP redirect or 404 response.
Args:
original_url: The original long URL, or None if not found.
Returns:
A tuple of (message, status_code).
- (redirect_url, 302) for a redirect
- ("Not found", 404) for a missing key
"""
if not original_url:
return "Not found", 404
return original_url, 302
analytics_recorder.py
============================================================
DESCRIPTION
============================================================
This block records click events (timestamp, key, referrer, IP)
for analytics purposes. For production, write to a database.
============================================================
PROMPT
============================================================
Generate a Python function for an Analytics Recorder block.
Function signature:
def record_click(key: str, request_context: dict) -> None:
Inputs:
key: a string, the short key that was clicked
request_context: a dict containing at least "ip" and "referrer"
Outputs:
None
Requirements:
1. Record the click with timestamp, key, IP, and referrer.
2. For this example, print to console (production would use a database).
3. Do not raise exceptions for analytics failures (non-blocking).
Implementation notes:
- Use datetime.now() for timestamp.
- Include docstring and type hints.
- Use try/except to prevent analytics failures from breaking the redirect.
Example:
context = {"ip": "192.168.1.1", "referrer": "https://google.com"}
record_click("abc123", context)
============================================================
from datetime import datetime
from typing import Any
def record_click(key: str, request_context: dict[str, Any]) -> None:
"""Record a click event for analytics.
Args:
key: The short key that was clicked.
request_context: Dictionary containing 'ip' and 'referrer'.
"""
try:
timestamp = datetime.now().isoformat()
ip = request_context.get("ip", "unknown")
referrer = request_context.get("referrer", "unknown")
# In production, write to a database or message queue
print(f"[ANALYTICS] {timestamp} | key={key} | ip={ip} | referrer={referrer}")
except Exception as e:
# Non-blocking: analytics failure should not break the redirect
print(f"[ANALYTICS ERROR] Failed to record click: {e}")
api_router.py
============================================================
DESCRIPTION
============================================================
This block routes HTTP requests to the appropriate handler.
It maps POST /shorten to create handler and GET /{key} to redirect handler.
============================================================
PROMPT
============================================================
Generate a Python function for an API Router block.
Function signature:
def route_request(routes: dict) -> object:
Inputs:
routes: a dict mapping path patterns to handler functions
Outputs:
An app object (simplified for this example)
Requirements:
1. This is a simplified router for demonstration.
2. In production, use FastAPI, Flask, or similar.
3. The returned app has a run() method.
Implementation notes:
- This is a mock router to keep the example self-contained.
- Include docstring and type hints.
Example:
app = route_request({
"POST /shorten": handle_create,
"GET /{key}": handle_redirect
})
app.run()
============================================================
from typing import Callable
class SimpleApp:
"""A mock web app for demonstration purposes."""
def __init__(self, routes: dict[str, Callable]):
self.routes = routes
def run(self):
"""Run the mock app."""
print("\n" + "=" * 50)
print("URL Shortener is ready!")
print("=" * 50)
print("\nThis is a mock router for demonstration.")
print("In production, replace with FastAPI, Flask, etc.")
print("\nAvailable routes:")
for path, handler in self.routes.items():
print(f" {path} -> {handler.__name__}")
print("\nExample curl commands:")
print(' curl -X POST http://localhost:8000/shorten -H "Content-Type: application/json" -d \'{"url": "https://example.com"}\'')
print(" curl http://localhost:8000/abc123")
print("\n" + "=" * 50)
def route_request(routes: dict[str, Callable]) -> SimpleApp:
"""Create a mock app with the given routes.
Args:
routes: A dictionary mapping path patterns to handler functions.
Returns:
A SimpleApp object with a run() method.
"""
return SimpleApp(routes)
3.3 The Integration (main.py)
Here is main.py — the file that connects everything and runs the system.
main.py - Integration harness for URL Shortener
This file connects all functional blocks into a working system.
It defines the data flow and handles errors across block boundaries.
from url_validator import validate_url
from key_generator import generate_key
from storage_manager import save_url, get_url, key_exists
from redirect_handler import create_redirect_response
from analytics_recorder import record_click
from api_router import route_request
Override key_exists in key_generator to use storage_manager
import key_generator
key_generator.storage_key_exists = key_exists
def handle_create_url(raw_url: str):
"""Full pipeline: validate -> generate key -> store -> return short URL"""
try:
validated_url = validate_url(raw_url)
key = generate_key(validated_url)
save_url(key, validated_url)
return f"https://short.url/{key}", 200
except ValueError as e:
return f"Invalid URL: {e}", 400
except RuntimeError as e:
return f"Failed to generate unique key: {e}", 500
def handle_redirect(key: str, request_context: dict):
"""Full pipeline: lookup -> record analytics -> redirect"""
original_url = get_url(key)
if not original_url:
return "Not found", 404
# Non-blocking analytics: log and continue
record_click(key, request_context)
return create_redirect_response(original_url)
The API router calls these functions based on request type
app = route_request({
"POST /shorten": handle_create_url,
"GET /{key}": handle_redirect
})
if name == "main":
app.run()
3.4 How to Run It
Save all 7 files in the same directory.
Make sure you have Python 3.8 or later installed.
Run python main.py
You will see:
==================================================
URL Shortener is ready!
This is a mock router for demonstration.
In production, replace with FastAPI, Flask, etc.
Available routes:
POST /shorten -> handle_create_url
GET /{key} -> handle_redirect
Example curl commands:
curl -X POST http://localhost:8000/shorten -H "Content-Type: application/json" -d '{"url": "https://example.com"}'
curl http://localhost:8000/abc123
==================================================
The system is complete. Every block works. You can extend it with a real web framework, a database, and proper testing.
3.5 What This Demonstrates
What You Just Saw
Why It Matters
Six complete .py files
Every block is here. No missing pieces.
Clear interfaces
Each block's inputs, outputs, and errors are explicit.
Integration in one file
main.py shows exactly how blocks connect.
Reproducible
Every block can be regenerated from its Prompt.
Maintainable
Change one block. The others keep working.
Runnable
You can copy and run it today.
This is not a toy. It is a complete system built with FBD.
3.6 What You Learned in This Series
Part 1: The Problem
Vibe coding collapses when projects grow.
AI thinks in functions, not systems.
The solution is not a better prompt. It is a different way to structure work.
Part 2: The Methodology
Decompose the system into functional blocks.
Write a Block Spec (Description + Prompt) in each .py file header.
Generate each block from its Prompt.
Integrate the blocks in main.py.
Part 3: Putting It All Together
The complete, runnable URL shortener.
Six blocks, one integration file, one working system.
Clear, reproducible, maintainable.
3.7 What You Should Do Next
Try FBD on your own project this week.
Start small. Pick a system with three or four blocks. Write the Description and Prompt carefully. Generate each block. Connect them.
You will make mistakes. Your first Prompt may not produce perfect code. That is normal. Refine it. The Prompt lives in the file header. You can improve it over time.
The investment pays off quickly:
After This Many Projects
You Will
1 project
Understand the rhythm of decomposition and integration
3 projects
Write better Prompts faster. Avoid common mistakes.
5 projects
Apply FBD without thinking. It becomes your default way to work with AI.
3.8 A Final Thought
The relationship between human and AI is still being figured out.
Some people think AI will replace programmers.
Some people think AI is just a toy.
Both are wrong.
The truth is more interesting: AI changes what programming means.
The human becomes less of a coder and more of an architect — decomposing systems, writing specifications, validating integration. The AI becomes less of an assistant and more of a builder — generating correct, focused code from clear instructions.
Functional Block Design is one way to make that relationship work.
It is simple. It is practical. It is reproducible.
And it works.
3.9 Where to Go From Here
Try it. Build something small. See for yourself.
Share it. Tell others about FBD. Link to this series. Start a conversation.
Improve it. FBD is not sacred. Adapt it to your workflow. Make it better.
The code is yours. The methodology is yours. The system is yours.
The End
Thank you for reading this series.
If you found it useful, share it with someone who struggles with AI-generated code. Write a blog post about your own experience with FBD. Or just go build something.
Either way, you now have a complete, runnable example of a system built with Functional Block Design.
Go put it to work.
Top comments (0)