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.

Comments

No comments yet. Why don’t you start the discussion?

Leave a Reply