This is an honest account of building a production-deployed URL shortener API using Claude Code in 4 hours. Not a highlight reel — the actual prompts, the failures, the corrections, and what the workflow looks like when it goes wrong. The goal is to give you a realistic picture of what AI-assisted development looks like today, not what the marketing says it looks like.
⚡ TL;DR: Claude Code handled: project scaffolding, Express routes, DynamoDB schema, Redis caching, error middleware, and deployment config. It needed correction on: authentication middleware (security issue), rate limiting logic (wrong algorithm), and test coverage (missed edge cases). 4 hours total. Estimated 12 hours to build manually.
Hour 1: Project scaffold (25 minutes actual time)
# Prompt 1: Project structure
$ claude "Create a production Node.js URL shortener API with:
- TypeScript 5.4, Express 4, Node 20
- DynamoDB for storage (single-table design)
- Redis for caching (short code → long URL)
- Zod for input validation
- Pino for structured logging
- Jest for testing
Generate:
1. Directory structure
2. package.json with all dependencies
3. tsconfig.json
4. Environment variable schema
5. Main application entry point
DO NOT implement routes yet — just the skeleton."
# Claude Code result: ✅ Correct
# Generated clean structure in 3 minutes
# Reviewed design (5 min): directory structure was right, tsconfig was too permissive
# Correction: "Tighten tsconfig — enable strict, noUncheckedIndexedAccess, exactOptionalPropertyTypes"
# Claude corrected in 1 minute ✅
Hour 1.5: Core routes — where it got interesting
# Prompt 2: URL shortening endpoint
$ claude "Implement POST /shorten endpoint:
- Accept { url: string, customAlias?: string, expiresIn?: number }
- Validate URL (must be http/https, max 2000 chars)
- Generate base62 short code from auto-increment DynamoDB counter
- Store in DynamoDB: { PK: 'URL#shortCode', longUrl, createdAt, expiresAt, clickCount }
- Cache mapping in Redis with 24hr TTL
- Return { shortCode, shortUrl, expiresAt }
Use the existing AppError class for errors.
Do NOT use any as a type — fix types correctly."
# Claude Code result: ✅ Mostly correct
# Good: DynamoDB schema, base62 generation, Redis caching
# Issue found: used parseFloat for URL length check instead of .length
# Issue found: expiresAt stored as ISO string but compared with Date in one place
# Correction: "Fix line 47 — url.length not parseFloat(url). Fix type inconsistency
# in expiresAt — use Unix timestamps throughout (number), not ISO strings"
# Both fixed in 2 minutes ✅
The authentication failure — what went wrong
# Prompt 3: API key authentication
$ claude "Add API key authentication middleware.
Protected routes: POST /shorten, DELETE /urls/:code
Public routes: GET /:code (redirect)
Store API keys in DynamoDB. Validate timing-safe."
# Claude Code result: ❌ Security issue
# Generated code:
if (apiKey === storedKey) { // WRONG: timing-vulnerable string comparison
next();
}
# Claude used regular === instead of crypto.timingSafeEqual
# This is a timing attack vulnerability on auth-critical code
# EXACTLY the kind of AI mistake that matters
# Manual correction required:
import { timingSafeEqual } from 'crypto';
const inputKeyBuffer = Buffer.from(apiKey, 'utf-8');
const storedKeyBuffer = Buffer.from(storedKey, 'utf-8');
if (inputKeyBuffer.length !== storedKeyBuffer.length ||
!timingSafeEqual(inputKeyBuffer, storedKeyBuffer)) {
throw new AppError('UNAUTHORIZED', 401);
}
# Lesson: never accept AI auth code without explicit security review
# Always check: timingSafeEqual, bcrypt/argon2, JWT algorithm verification
Hour 3: Tests — what Claude does well and badly
# Prompt: "Write Jest integration tests for POST /shorten.
# Use supertest. Mock DynamoDB and Redis.
# Cover: valid URL, invalid URL, duplicate alias, rate limit exceeded,
# missing API key, expired API key."
# Claude Code result: ✅ Mostly good
# Good: covered all 6 named scenarios
# Issue: mock setup leaked between tests (missing afterEach cleanup)
# Issue: did not test that Redis was actually called (just tested response)
# Issue: did not test the base62 generation edge case (counter overflow)
# Pattern: Claude writes tests that test the obvious paths
# Missing: cross-cutting concerns (mock cleanup), infrastructure contracts (Redis called?)
# Fix: Add your own tests for infrastructure interactions
# Fix: Always add afterEach(() => jest.clearAllMocks())
# Coverage result after corrections: 91% (target was 90%)
Hour 4: Deployment — Claude was excellent here
# Prompt: "Generate AWS SAM template to deploy this as Lambda + API Gateway.
# DynamoDB table (on-demand billing), ElastiCache Redis (t3.micro),
# VPC with private subnets, IAM roles with least privilege,
# environment-specific configs (dev/prod)."
# Claude Code result: ✅ Excellent
# Generated complete SAM template in 4 minutes
# IAM roles were actually least-privilege (common AI failure mode avoided)
# VPC config was correct (2 subnets, different AZs)
# One issue: used ARM64 architecture by default ✅ (correct — we want this)
# One issue: didn't add DynamoDB table deletion policy = RETAIN
# Correction: 30 seconds ✅
# Total time breakdown:
# Project scaffold: 25 min (15 min Claude, 10 min review)
# Core routes: 45 min (30 min Claude, 15 min review + corrections)
# Auth middleware: 35 min (15 min Claude, 20 min security review + manual fix)
# Tests: 50 min (25 min Claude, 25 min review + additional tests)
# Deployment: 25 min (20 min Claude, 5 min review)
# Total: ~4 hours vs estimated 12 hours manual
- ✅ Claude Code excels at: scaffolding, boilerplate, deployment config, route structure
- ✅ Claude Code is good at: test generation for obvious paths, Redis/DynamoDB patterns
- ✅ Claude Code needs review on: auth code, crypto, type safety edge cases
- ✅ Always add your own tests for: infrastructure contracts, cross-cutting concerns
- ❌ Never accept AI auth middleware without explicit security audit
- ❌ Never skip the design review step — wrong interfaces cost more to fix than to prevent
The URL shortener built in this walkthrough is covered architecturally in the URL shortener system design guide — comparing AI-generated code against design principles is a great way to catch issues. The deployment template generated by Claude Code leverages the ARM64 Graviton3 optimization automatically. External reference: Claude Code documentation.
Level Up: AI-Assisted Project Building
→ Python Bootcamp on Udemy — Build real AI agents and automation tools with Python from scratch.
→ Designing Data-Intensive Applications — The infrastructure foundation every AI engineer needs.
Sponsored links. We may earn a commission at no extra cost to you.
Discover more from CheatCoders
Subscribe to get the latest posts sent to your email.
