🌐 Translation: Translated from Korean.


title: “Building Blog Automation with Claude Code in 2 Weeks: From PRD to Deployment”
slug: “claude-code-blog-automation-project”
excerpt: “AI didn’t magically build this in 2 weeks. The reality: 3 PRD revisions, 137 sub-tasks executed sequentially, validation at every step – a structured development process leveraging AI as a tool. I openly share AI mistakes encountered, how to address them, and critical checkpoints for developers.”
status: “publish”
categories:

  • “Projects”
  • “Development Productivity”
    tags:
  • “Claude Code”
  • “Development”
  • “AI”
    language: “en”

This post is Part 2 of “AI-Era Developer: 5 Practical Ways to Integrate Claude Code into Your Workflow“.

“Claude Code built it in 2 weeks!”

Not exactly. More accurately:

  • PRD written and revised 3 times (priority realignment)
  • 13 Epics → 47 Tasks → 137 Sub-tasks decomposed
  • Logical validation for each sub-task (intent alignment, side effects)
  • 6 AI mistakes caught and corrected (changing “replace” to “add”)
  • Final 81 tests written and passing

Two weeks of systematic developer management with AI as a tool.

This post honestly reveals the actual development process:

  • How was the PRD written and why was it revised 3 times?
  • How were tasks decomposed and sequenced?
  • What was validated at each step?
  • What AI mistakes were caught and how?

📋 Project Overview



Goal

Write in Markdown → Publish Bilingual Blog with One Command

blog publish post.md
# Auto-translate → Upload images → SEO optimization → Inject ads → Publish → Link languages

Why Automation Was Needed

2.5 hours per post:

  • Korean writing → English translation (1.5 hours)
  • Image upload and path updates (20 minutes)
  • SEO metadata entry (15 minutes)
  • Ad code insertion (10 minutes)
  • WordPress publishing and language linking (10 minutes)

100 posts annually = 250 hours wasted

Expected Impact

  • Post publishing: 2.5 hours → 15 minutes (90% reduction)
  • Annual ROI: $11,700 (250 hours × $50/hour – development investment)

🎯 Development Process Overview

Core Principle: AI is the tool, developer is the manager

5-Step Systematic Process

1. Brainstorming (Free-form conversation with Claude Code)
   ↓
2. PRD Writing (AI draft → Developer review → Iterative revision)
   ↓
3. Task Decomposition (Epic → Task → Sub-task, dependency analysis)
   ↓
4. Sequential Execution (One sub-task at a time, validate each)
   ↓
5. Continuous Validation (Logical consistency, intent alignment, side effects)

Why This Process?

❌ Wrong Approach: “AI, build blog automation for me” → Unclear results, repeated rework

✅ Right Approach:

  1. Clarify requirements with PRD (What to build)
  2. Establish execution plan with tasks (How to build)
  3. Decompose into manageable sub-tasks (One at a time)
  4. Validate each step (Is this correct? Any issues?)

Developer sets direction, AI assists execution


🗓️ Day 1: Brainstorming (2 hours)

Free-Form Conversation with Claude Code

Me: "I want to build a WordPress blog automation CLI.
Markdown writing → Auto Korean-English translation → Image upload → Publishing.
Is this feasible?"

Claude: "Yes, it's feasible. You'll need:

Core Technologies:
- WordPress REST API (post publishing)
- Anthropic API (AI translation)
- wpapi library (API client)
- unified + remark (markdown parsing)

Key Challenges:
1. Translation quality validation (code block preservation, SEO keywords)
2. Preventing duplicate image uploads
3. Polylang language linking (Korean-English posts)

Suggested Architecture:
- pnpm monorepo (cli / core / shared)
- TypeScript (type safety)
- Zod (runtime validation)

Me: "How long will this take?"

Claude: "With a systematic approach:
- MVP (basic publishing): 3-5 days
- Translation system: 3-4 days
- Image automation: 2-3 days
- Validation/testing: 2-3 days
Total: approximately 2 weeks."

Key Learning: This stage doesn’t need structure. Freely explore ideas.


🗓️ Day 1-2: PRD Writing (4 hours)

Initial Draft (Request to Claude Code)

Me: "Write a PRD based on our brainstorming.
Include Epics, Tasks, and priorities."

Claude: "I'll create the PRD..."

PRD v1 (AI Draft)

## Epic 1.0: Project Setup
- Task 1.1: pnpm workspace configuration
- Task 1.2: TypeScript setup
- Task 1.3: Build pipeline (tsup)

## Epic 2.0: WordPress API Integration
- Task 2.1: wpapi client
- Task 2.2: Authentication (Application Password)
- Task 2.3: Post create/update/delete

## Epic 3.0: Markdown Parsing
- Task 3.1: gray-matter integration
- Task 3.2: unified pipeline
- Task 3.3: frontmatter validation (Zod)

## Epic 4.0: CLI Tool
...

## Epic 11.0: AI Translation System
...

## Epic 12.0: Image Auto-Upload
...

First Validation: “Any Logical Issues?”

Problems Found:

  1. Epic sequence odd: CLI tool before WordPress API?
  2. Dependencies not considered: Translation should come after markdown parsing
  3. Priorities unclear: No distinction between MVP vs Nice-to-have

Developer Decision: “This needs reorganization”

PRD v2 (1st Revision – Priorities)

## Phase 1: MVP (1 week)
### Epic 1.0: Project Foundation
### Epic 2.0: WordPress API
### Epic 3.0: Markdown Parsing
### Epic 4.0: CLI Basics (publish command)

## Phase 2: Automation (1 week)
### Epic 11.0: AI Translation
### Epic 12.0: Image Upload

## Phase 3: Optimization (Future)
### Epic 21.0: Auto Ad Insertion
### Epic 22.0: Scheduled Publishing

Improvement: Phases clarify priorities

Second Validation: “Is This My Intent?”

Problems Found:

  • Epic 11.0: Translation only, no quality validation
  • Epic 12.0: Missing duplicate image detection

Developer Decision: “Quality validation is essential. Need separate Epic”

PRD v3 (2nd Revision – Quality Validation Added)

## Phase 2: Automation (1 week)
### Epic 11.0: AI Translation
- Task 11.1: Translation engine (Claude API)
- Task 11.2: Code block preservation
- Task 11.3: SEO keyword preservation
- **Task 11.4: 8-Step Quality Validation** ← NEW

### Epic 12.0: Image Auto-Upload
- Task 12.1: Image path parsing
- Task 12.2: WordPress Media API
- **Task 12.3: Duplicate image detection** ← NEW
- Task 12.4: URL conversion

Third Validation: “Any Side Effects?”

Problem Found:

  • Epic 3.0: SEO data generation during markdown parsing
  • Epic 11.0: SEO data generation during translation too
  • Potential Conflict: What if slug is generated twice?

Developer Decision: “Consolidate SEO generation logic in Epic 3.0, Epic 11.0 handles translation only”

PRD v3 (Final – 3rd Revision)

## Epic 3.0: Markdown Parsing
- Task 3.1: gray-matter integration
- Task 3.2: unified pipeline
- Task 3.3: frontmatter validation (Zod)
- **Task 3.4: SEO data generation** ← MOVED

## Epic 11.0: AI Translation
- Task 11.1: Translation engine (excluding SEO)
- Task 11.2: Code block preservation
- Task 11.3: SEO keyword preservation
- Task 11.4: Quality validation

Final Result: 13 Epics, 47 Tasks

Key Learning

PRDs aren’t perfected in one pass:

  1. AI drafts initial version (80% accuracy)
  2. Developer validates and revises 3 times:
    • Logical consistency (sequence, dependencies)
    • Intent alignment (requirements reflected)
    • Side effects (conflicts, duplicates)

Developer’s Role: Strategic decision-making, AI supports documentation


🗓️ Day 2: Task/Sub-Task Decomposition (3 hours)

Breaking Tasks into Sub-Tasks

Epic 11.0: AI Translation → 22 Sub-Tasks

## Task 11.1: Translation Engine
- Sub-Task 11.1.1: Anthropic API client setup
- Sub-Task 11.1.2: Translation prompt design
- Sub-Task 11.1.3: Timeout handling (60ms/word)
- Sub-Task 11.1.4: Error handling

## Task 11.2: Code Block Preservation
- Sub-Task 11.2.1: Code block extraction (regex)
- Sub-Task 11.2.2: Placeholder substitution
- Sub-Task 11.2.3: Post-translation restoration
- Sub-Task 11.2.4: Exact match validation

## Task 11.3: SEO Keyword Preservation
- Sub-Task 11.3.1: Extract keywords from tags
- Sub-Task 11.3.2: Provide keyword hints during translation
- Sub-Task 11.3.3: Validate keyword inclusion post-translation
- Sub-Task 11.3.4: Calculate keyword density (0.5-2.5%)

## Task 11.4: Quality Validation
- Sub-Task 11.4.1: Basic validation (not empty)
- Sub-Task 11.4.2: Line count validation (50-150%)
- Sub-Task 11.4.3: SEO keyword validation
- Sub-Task 11.4.4: Keyword density validation
- Sub-Task 11.4.5: Title length validation (≤60 chars)
- Sub-Task 11.4.6: Link/heading structure validation
- Sub-Task 11.4.7: Code block validation
- Sub-Task 11.4.8: Metadata completeness validation

Determining Execution Order

Dependency Analysis:

11.1 (Translation engine) → 11.2 (Code blocks) depends on
11.2 → 11.3 (Keywords) depends on
11.3 → 11.4 (Validation) depends on

Execution Sequence: 11.1 → 11.2 → 11.3 → 11.4 (sequential)

Key Learning

Sub-Task Criteria:

  1. Completable within 1-2 hours
  2. Clear inputs/outputs
  3. Independently testable

Why Developer Determines Order:

  • AI doesn’t fully grasp dependencies
  • Wrong order = rework

🗓️ Day 3-13: Sequential Execution & Validation (80 hours → 24 hours)

Execution Principle

One at a Time, Then Validate

1. Select 1 sub-task
2. Request Claude Code implementation
3. Code review (logic, intent, side effects)
4. Write tests
5. If passing, move to next sub-task
6. If failing, rework or revise PRD

Case 1: Epic 11.0 Day 5-7 (AI Translation)

Sub-Task 11.2.1: Code Block Extraction (30 min)

Me: "Write a function to extract code blocks from markdown.
Use regex, type should be { code: string, language: string }[]"

Claude: [Generates code]
```typescript
export function extractCodeBlocks(content: string): CodeBlock[] {
  const regex = /```(\w+)?\n([\s\S]*?)```/g;
  const blocks: CodeBlock[] = [];
  let match;

  while ((match = regex.exec(content)) !== null) {
    blocks.push({
      language: match[1] || 'text',
      code: match[2],
    });
  }

  return blocks;
}

Validation 1 – Logical Consistency:



Me: "What if code block is empty?"
Claude: "Returns empty string."
Me: "What if no language specified?"
Claude: "Defaults to 'text'."

✅ Pass

Validation 2 – Intent Alignment:

Me: "What about nested code blocks? (code blocks within code blocks)"
Claude: "Currently not handled. Do you need it?"
Me: "No, that doesn't happen in markdown. We're good."

✅ Pass

Validation 3 – Side Effects:

Me: "Does this function affect other modules?"
Claude: "It's a pure function, no side effects."

✅ Pass

Sub-Task 11.2.3: Post-Translation Code Block Restoration (1 hour)

Me: "Replace placeholders with original code."

Claude: [Generates code]
```typescript
export function restoreCodeBlocks(
  translated: string,
  blocks: CodeBlock[]
): string {
  let result = translated;

  blocks.forEach((block, index) => {
    const placeholder = `__CODE_BLOCK_${index}__`;
    const codeBlock = "```" + block.language + "\n" + block.code + "```";
    result = result.replace(placeholder, codeBlock);
  });

  return result;
}

Validation 1 – Logical Consistency:

Me: "What if placeholder appears multiple times?"
Claude: "Should use replaceAll."

❌ Fail → Request fix

Claude’s Fix:

result = result.replaceAll(placeholder, codeBlock);

✅ Pass

Validation 2 – Intent Alignment:

Me: "What if code block count doesn't match placeholder count?"
Claude: "Currently no handling. Should I throw an exception?"
Me: "Yes, that's safer."

Final Code:

export function restoreCodeBlocks(
  translated: string,
  blocks: CodeBlock[]
): string {
  // Validate placeholder count
  const placeholderCount = (translated.match(/__CODE_BLOCK_\d+__/g) || []).length;
  if (placeholderCount !== blocks.length) {
    throw new Error(
      `Mismatch: ${placeholderCount} placeholders vs ${blocks.length} blocks`
    );
  }

  let result = translated;
  blocks.forEach((block, index) => {
    const placeholder = `__CODE_BLOCK_${index}__`;
    const codeBlock = "```" + block.language + "\n" + block.code + "```";
    result = result.replaceAll(placeholder, codeBlock);
  });

  return result;
}

✅ Final Pass

Case 2: Epic 12.0 Day 9 (Duplicate Image Detection) – Caught AI Mistake

Sub-Task 12.3.1: WordPress Media Search (2 hours)

Me: "Write a function to search WordPress media library by filename.
Return only exact matches."

Claude: [Generates code]
```typescript
async findMediaByFilename(filename: string): Promise<MediaItem | null> {
  const media = await this.wp.media().search(filename);

  if (!media || media.length === 0) {
    return null;
  }

  // Find exact filename match
  const exactMatch = media.find((item: any) => {
    const mediaFilename = item.media_details?.file?.split('/').pop();
    return mediaFilename === filename;
  });

  return exactMatch ? {
    id: exactMatch.id,
    url: exactMatch.source_url,
  } : null;
}

Validation 1 – Logical Consistency:

Me: "What if media_details is missing?"
Claude: "Handled with optional chaining."

✅ Pass

Validation 2 – Intent Alignment:

Me: "Where will this function be used?"
Claude: "For duplicate checking before uploadMedia()."
Me: "Then shouldn't uploadMedia() also be modified?"
Claude: "Yes, you're right!"

AI Mistake Detected: Missed existing function modification

AI Mistake Pattern 1: Replacement Instead of Addition

Me: "Add duplicate check logic to uploadMedia()."

Claude: [Generates code]
```typescript
async uploadMedia(filePath: string): Promise<{ id: number; url: string }> {
  // 1. Extract filename
  const filename = path.basename(filePath);

  // 2. Check duplicates
  const existing = await this.findMediaByFilename(filename);
  if (existing) {
    return existing;
  }

  // 3. Upload new
  const media = await this.wp.media().file(filePath).create();
  return {
    id: media.id,
    url: media.source_url,
  };
}

Validation 3 – Side Effects:

Me: "The original uploadMedia() had title and alt parameters?"
Claude: "Oops, they were removed. I'll add them back."

Problem: AI “replaced” instead of “added”

Original Code:

async uploadMedia(
  filePath: string,
  title?: string,
  alt?: string
): Promise<{ id: number; url: string }>

AI-generated Code: title, alt parameters removed ❌

Developer Fix:

Me: "Keep existing parameters, just add duplicate check.
Existing code:
```typescript
async uploadMedia(
  filePath: string,
  title?: string,
  alt?: string
): Promise<{ id: number; url: string }> {
  const mediaData: Record<string, unknown> = {};
  if (title) mediaData.title = title;
  if (alt) mediaData.alt_text = alt;

  const media = await this.wp.media().file(filePath).create(mediaData);
  return { id: media.id, url: media.source_url };
}

Add duplicate check BEFORE this logic.”

Claude: “Got it, I’ll add before existing logic.”

async uploadMedia(
  filePath: string,
  title?: string,
  alt?: string
): Promise<{ id: number; url: string }> {
  // NEW: Duplicate check
  const filename = path.basename(filePath);
  const existing = await this.findMediaByFilename(filename);
  if (existing) {
    return existing;
  }

  // EXISTING: Keep original logic
  const mediaData: Record<string, unknown> = {};
  if (title) mediaData.title = title;
  if (alt) mediaData.alt_text = alt;

  const media = await this.wp.media().file(filePath).create(mediaData);
  return { id: media.id, url: media.source_url };
}
✅ Final Pass

### Key Learning: AI Mistake Patterns & Responses

**Common AI Mistakes**:
1. **Replace vs Add Confusion**: Deletes existing code and rewrites
2. **Parameter Omission**: Changes existing function signature
3. **Ignoring Side Effects**: Doesn't check usage in other modules

**Response Strategies**:
1. **Clear Instructions**: Instead of "add this", say "keep existing code and add BEFORE it"
2. **Provide Existing Code**: Explicitly show "add before THIS code"
3. **Ask About Side Effects**: "Search where this function is used"

**Why Validation is Essential**:
- AI doesn't fully understand entire context
- Developer must check "logic, intent, side effects" every time

---

## 🗓️ Day 11-12: Slug Bug - Systematic Debugging

### Problem Discovery

```markdown
---
slug: "ai-translation-bilingual-blog"
---

Actually Published Slug: ai-beonyeogeuro-hanyeong-beullogeu... (Korean romanization!)

Debugging Process

1. Problem Clarification

Me: "Slug is ignoring frontmatter.
Analyze root cause with ultrathink."

2. Claude’s Systematic Analysis (Sequential Thinking 9 Steps)

Thought 1: Check markdown.ts parsing
- parseMarkdownFile() sets metadata.slug = "ai-translation-bilingual-blog" ✅
- Frontmatter parsed correctly

Thought 2: Check seo.ts generation
- generateSeoData() called
- Found! Always regenerates slug from title ❌
- No customSlug parameter

Thought 3: Root Cause
- SeoOptions type missing slug field
- generateSeoData() ignoring metadata.slug

Thought 4: Impact Scope
- Epic 11.0 translation also affected (English slug also wrong)
- 4 files need modification:
  1. packages/shared/src/types.ts
  2. packages/core/src/seo.ts
  3. packages/core/src/markdown.ts
  4. packages/core/src/translator.ts

3. Fix

File 1: types.ts

export interface SeoOptions {
  title: string;
  excerpt: string;
  content: string;
  keywords: string[];
  slug?: string; // NEW
  language: 'ko' | 'en';
}

File 2: seo.ts

export function generateSeoData(options: SeoOptions): SeoData {
  const { slug: customSlug } = options;

  return {
    slug: customSlug || generateSlugFromTitle(options.title), // Priority
    // ...
  };
}

File 3: markdown.ts

const seoData = generateSeoData({
  title: metadata.title,
  excerpt: metadata.excerpt || '',
  content: htmlContent,
  keywords: metadata.tags || [],
  slug: metadata.slug, // Pass through
  language: metadata.language,
});

File 4: translator.ts

const seoData = generateSeoData({
  // ...
  slug: `${originalMetadata.slug}-en`, // English gets "-en" suffix
  // ...
});

4. Validation

pnpm typecheck  # No type errors ✅
pnpm test       # 81 tests passing ✅
blog publish test.md  # Slug correct ✅

Key Learning: Systematic Debugging

Before (Manual Debugging):

  • 4.5 hours spent
  • Repeated “why isn’t this working?”
  • Multiple file edits leading to type errors

After (Claude’s Sequential Thinking):

  • 30 minutes spent
  • 9-step systematic analysis
  • Identified impact scope → Fixed 4 files at once

Developer’s Role:

  1. Request “analyze with ultrathink”
  2. Review analysis results (validate correctness)
  3. Decide fix direction (priorities, tradeoffs)
  4. Final validation (tests, type checking)

📊 Final Results

Time Savings

Task Before After Savings
Project Design 10h 4h 60%
Bootstrapping 10h 1h 90%
API Integration 4h 1h 75%
AI Translation Engine 12h 3h 75%
Quality Validation 8h 2h 75%
Image Processing 14h 4h 71%
Bug Fixes 4.5h 0.5h 89%
CLI Completion 4h 1h 75%
Documentation 6h 1h 83%
Total 72.5h 17.5h 76%

Actual: 80 hours → 24 hours (70% reduction, including misc tasks)

Code Metrics

  • Type Safety: 100% TypeScript
  • Tests: 81 tests (90%+ coverage)
  • Packages: 3 (cli, core, shared)
  • Build Time: <2 seconds (tsup)

Post Publishing Time

  • Before: 2.5 hours/post
  • After: 15 minutes/post
  • Savings: 90%

💡 Key Learnings: Development Process in the AI Era

1. Coding Without PRD = Repeated Rework

❌ Wrong Approach:

Developer: "Build blog automation for me"
AI: [Generates code]
Developer: "No, also translate it"
AI: [Modifies code... breaks existing code]
Developer: "Also upload images"
AI: [Modifies again... type errors]

✅ Right Approach:

Developer: [Writes PRD - Clarifies requirements]
AI: [Provides PRD draft]
Developer: [3 revisions - Validates logic, intent, side effects]
Developer: [Decomposes tasks - Determines execution order]
AI: [Implements sub-tasks one by one]
Developer: [Validates each time]

Difference: Clear plan = Minimal rework

2. Step-by-Step Validation = Quality Assurance

3-Step Validation:

  1. Logical Consistency: “Are edge cases handled?”
  2. Intent Alignment: “Is this what I wanted?”
  3. Side Effects: “Any impact on other code?”

Without validating each sub-task:

  • Bug found after 10 sub-tasks
  • Hard to locate the problem
  • Rework time >>>

3. AI Mistakes Are Inevitable

Common AI Mistakes:

  1. Replacing existing code (not adding)
  2. Omitting parameters
  3. Ignoring side effects

Response:

  • “Add this” → “Keep existing code and add BEFORE it”
  • Explicitly provide existing code
  • Validate every time (logic, intent, side effects)

4. Developer as Manager, AI as Tool

Developer’s Role:

  • Strategic decisions (PRD revisions, task sequencing)
  • Code reviews (3-step validation)
  • Final quality responsibility (tests, type checking)

AI’s Role:

  • Draft creation (PRD, code, tests)
  • Automation of repetitive tasks (boilerplate)
  • Systematic analysis (debugging, Sequential Thinking)

AI doesn’t do everything for you.
Developers manage systematically and leverage AI as a tool.


⚠️ Limitations & Precautions

AI Limitations

  1. Incomplete Context Understanding

    • Only 1 of 10 files needs modification
    • AI: “I’ll modify all 10 files” (unnecessary work)
  2. Cannot Judge Tradeoffs

    • “Performance vs Readability?”
    • “Optimize now vs later?”
    • → Developer decides
  3. Limited Creative Problem-Solving

    • Only combines existing patterns
    • Innovative architecture is human territory

Precautions

  1. 100% Review of AI Code Required

    • Security vulnerabilities (SQL Injection, XSS)
    • Performance issues (O(n²) algorithms)
    • Missing error handling
  2. Context Management

    • Project documentation like CLAUDE.md
    • Explicitly provide PRD, Task Lists
  3. Developer Responsible for Decisions

    • Architecture choices
    • Priority decisions
    • Technical debt management

Conclusion

Over 2 weeks, I built a blog automation system with Claude Code.

Results by Numbers:

  • Development time: 80 hours → 24 hours (70% reduction)
  • Post publishing: 2.5 hours → 15 minutes (90% reduction)
  • Tests: 81 tests (90%+ coverage)

But the biggest gain was learning “how to leverage AI as a tool”.

AI is a tool. Powerful, but requires management:

  • Set direction with PRD
  • Create execution plan with tasks
  • Decompose into manageable sub-tasks
  • Validate at each step

All developer responsibilities.


Have questions or feedback? Leave a comment!

I’d love to hear about your AI collaboration projects too. 🙂


Series: