Vibe Coding With Claude Code: A Real Project From Zero to Deployed in 4 Hours

Vibe Coding With Claude Code: A Real Project From Zero to Deployed in 4 Hours

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.