š 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:
- Clarify requirements with PRD (What to build)
- Establish execution plan with tasks (How to build)
- Decompose into manageable sub-tasks (One at a time)
- 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:
- Epic sequence odd: CLI tool before WordPress API?
- Dependencies not considered: Translation should come after markdown parsing
- 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:
- AI drafts initial version (80% accuracy)
- 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:
- Completable within 1-2 hours
- Clear inputs/outputs
- 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:
- Request “analyze with ultrathink”
- Review analysis results (validate correctness)
- Decide fix direction (priorities, tradeoffs)
- 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:
- Logical Consistency: “Are edge cases handled?”
- Intent Alignment: “Is this what I wanted?”
- 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:
- Replacing existing code (not adding)
- Omitting parameters
- 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
-
Incomplete Context Understanding
- Only 1 of 10 files needs modification
- AI: “I’ll modify all 10 files” (unnecessary work)
-
Cannot Judge Tradeoffs
- “Performance vs Readability?”
- “Optimize now vs later?”
- ā Developer decides
-
Limited Creative Problem-Solving
- Only combines existing patterns
- Innovative architecture is human territory
Precautions
-
100% Review of AI Code Required
- Security vulnerabilities (SQL Injection, XSS)
- Performance issues (O(n²) algorithms)
- Missing error handling
-
Context Management
- Project documentation like CLAUDE.md
- Explicitly provide PRD, Task Lists
-
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:
- Part 1: AI-Era Developer – 5 Ways to Integrate Claude Code
- Part 2: 2-Week Project Real Story (Current Post)
Leave A Comment