🌐 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: