Skip to content

Instantly share code, notes, and snippets.

@rstrom
Created January 8, 2026 18:54
Show Gist options
  • Select an option

  • Save rstrom/cab075b7164bb64e68646761037434bf to your computer and use it in GitHub Desktop.

Select an option

Save rstrom/cab075b7164bb64e68646761037434bf to your computer and use it in GitHub Desktop.
Ralph Strategy - Iterative PRD-Driven Development with Claude

Ralph Strategy - Iterative PRD-Driven Development with Claude

A battle-tested approach for autonomous, iterative implementation of features using Claude Code. This strategy has been used to build a complete Telegram bot that negotiates static websites, accepts Stripe payments, and uses Claude Agent SDK to build and deliver sites.

Overview

The Ralph strategy uses a Product Requirements Document (PRD) in JSON format combined with a simple shell loop to let Claude autonomously implement features one at a time, maintaining context through a progress log.

Key Principles

  1. One feature at a time - Claude picks the highest-priority feature, implements it fully, then commits
  2. Self-verification - Each iteration runs typechecks and tests before committing
  3. Progress tracking - A markdown log maintains context across sessions
  4. Autonomous prioritization - Claude decides what to work on next based on dependencies
  5. Clean exit condition - Outputs <promise>COMPLETE</promise> when all features are done

Quick Start

1. Create your PRD (plans/prd.json)

[
  {
    "category": "infrastructure",
    "description": "Set up TypeScript monorepo with pnpm",
    "steps": [
      "Create pnpm-workspace.yaml",
      "Configure tsconfig.json with strict mode",
      "Add build and typecheck scripts"
    ],
    "passes": false
  },
  {
    "category": "feature",
    "description": "Implement user authentication",
    "steps": [
      "Create auth service with Context.Tag pattern",
      "Add login/logout endpoints",
      "Write unit tests"
    ],
    "passes": false
  }
]

2. Create progress log (plans/progress.txt)

# Progress Log

<!-- Append your progress notes below this line -->

3. Create the runner script (plans/ralph.sh)

See ralph.sh in this gist.

4. Run it

bash plans/ralph.sh 10  # Run up to 10 iterations

How It Works

┌─────────────────────────────────────────────────────────────┐
│                     ralph.sh loop                           │
├─────────────────────────────────────────────────────────────┤
│  for i in 1..N:                                             │
│    1. Claude reads prd.json + progress.txt                  │
│    2. Picks highest-priority feature (with `passes: false`) │
│    3. Implements the feature                                │
│    4. Runs `pnpm typecheck && pnpm test`                    │
│    5. Updates prd.json (marks `passes: true`)               │
│    6. Appends to progress.txt (context for next iteration)  │
│    7. Git commits the work                                  │
│    8. If all items pass → outputs COMPLETE → exits          │
└─────────────────────────────────────────────────────────────┘

PRD Schema

Each item in your PRD JSON array should have:

Field Type Description
category string Groups related features (infrastructure, feature, bugfix, testing, etc.)
description string Clear, one-line description of what needs to be done
steps string[] Ordered checklist of implementation steps
passes boolean false = not done, true = implemented and verified

Recommended Categories

  • infrastructure - Setup, config, CI/CD
  • feature - New functionality
  • bugfix - Bug fixes
  • testing - Test infrastructure and test cases
  • refactor - Code improvements without behavior change
  • documentation - Docs (use sparingly)
  • integration - Connecting systems together
  • deployment - Deploy and verify in production

Progress Log Format

The progress log serves as context memory across iterations. Each entry should include:

## YYYY-MM-DD: Short description

**Feature:** [Exact description from PRD]

**What was done:**
- Bullet points of changes made
- Files created/modified
- Key implementation decisions

**Notes for next person:**
- Context the next iteration needs
- Known issues or limitations
- What should be done next

Slash Command (Optional)

For Claude Code users, create .claude/commands/ralph.md:

---
allowed-tools: Bash(bash plans/ralph.sh:*)
description: Run the Ralph iterative PRD loop
---

# Ralph - Iterative PRD Implementation

Run: `bash plans/ralph.sh $ARGUMENTS`

Then use /ralph 10 to run 10 iterations.

Tips

Writing Good PRD Items

  1. Be specific - "Add user authentication" is vague; "Add JWT authentication with refresh tokens" is specific
  2. Include verification steps - "Ensure tests pass" or "Verify in production"
  3. Order by dependency - Infrastructure before features, features before integration
  4. Keep steps atomic - Each step should be independently verifiable

Common Patterns

Dependency tracking:

{
  "description": "Connect frontend to auth API (REQUIRES: user authentication)",
  "steps": ["..."]
}

Blocking notes:

{
  "description": "Deploy to production",
  "steps": [
    "NOTE: Requires AWS credentials configured",
    "Build Docker image",
    "Push to ECR"
  ]
}

When to Use This Strategy

  • Greenfield projects with clear requirements
  • Porting existing codebases
  • Feature backlogs with 10+ items
  • Projects requiring consistent progress tracking

When NOT to Use This Strategy

  • Exploratory/research tasks
  • One-off bug fixes
  • Tasks requiring human judgment at each step

Example Output

After running bash plans/ralph.sh 5:

Iteration 1
--------------------------------
[Claude implements "Set up TypeScript monorepo"]
...
git commit -m "feat: Set up TypeScript monorepo with pnpm"

Iteration 2
--------------------------------
[Claude implements "Add user authentication"]
...
git commit -m "feat: Add JWT authentication with refresh tokens"

...

Iteration 4
--------------------------------
[Claude detects all items pass]
<promise>COMPLETE</promise>
PRD complete, exiting.

Files in This Gist

File Description
README.md This documentation
ralph.sh The shell script that runs the loop
prd-example.json Example PRD from the Ralph project
progress-example.txt Example progress log showing the format
ralph-command.md Slash command for Claude Code

License

MIT - Use freely in your projects.


This strategy was developed while building Ralph, a Telegram bot that negotiates and delivers static websites using Claude.

[
{
"category": "infrastructure",
"description": "Initialize pnpm monorepo with Effect-TS and TypeScript",
"steps": [
"Create package.json with pnpm workspaces",
"Create pnpm-workspace.yaml with packages/*",
"Install effect, @effect/platform, @effect/cli",
"Add typecheck and prepare scripts"
],
"passes": true
},
{
"category": "infrastructure",
"description": "Create packages/shared with common types, errors, and constants",
"steps": [
"Create package.json for @myapp/shared",
"Define Schema.TaggedError classes: ValidationError, ApiError",
"Define constants: API_BASE_URL, DEFAULT_TIMEOUT",
"Export all types and constants"
],
"passes": true
},
{
"category": "core",
"description": "Create packages/core with the functional runtime Effects",
"steps": [
"Create package.json for @myapp/core",
"Define service contracts with Context.Tag pattern",
"Implement core business logic as Effect pipelines",
"Ensure runtime is readable as tight functional core"
],
"passes": false
},
{
"category": "service",
"description": "Create UserService using Context.Tag pattern",
"steps": [
"Create packages/core/src/services/UserService.ts",
"Define UserService class extending Context.Tag",
"Implement getUser, createUser, updateUser methods",
"Create Live layer with actual API client",
"Create Test layer for unit testing"
],
"passes": false
},
{
"category": "testing",
"description": "Add test infrastructure and example tests",
"steps": [
"Install vitest as dev dependency",
"Create test setup with Effect test utilities",
"Add example test for UserService",
"Add typecheck to CI workflow"
],
"passes": false
},
{
"category": "feature",
"description": "Implement user authentication flow",
"steps": [
"Add JWT token generation and validation",
"Create login/logout endpoints",
"Add refresh token support",
"Write integration tests"
],
"passes": false
},
{
"category": "integration",
"description": "Connect frontend to API (REQUIRES: user authentication)",
"steps": [
"Create API client service",
"Add authentication header injection",
"Handle token refresh automatically",
"Test end-to-end flow"
],
"passes": false
},
{
"category": "deployment",
"description": "Deploy to production",
"steps": [
"NOTE: Requires AWS credentials configured",
"Build Docker image",
"Push to container registry",
"Deploy via Pulumi/Terraform",
"Verify health checks pass"
],
"passes": false
}
]
# Progress Log
<!-- Append your progress notes below this line -->
## 2026-01-06: Created @myapp/shared package
**Feature:** Create packages/shared with common types, errors, and constants
**What was done:**
- Created `packages/shared/` directory structure
- Created `package.json` for @myapp/shared with Effect dependency
- Created `tsconfig.json` with Effect language service plugin
- Implemented `src/errors.ts` with Schema.TaggedError classes:
- ValidationError
- ApiError
- NetworkError
- Implemented `src/constants.ts` with:
- API_BASE_URL
- DEFAULT_TIMEOUT
- MAX_RETRIES
- Created `src/index.ts` exporting all modules
**Notes for next person:**
- Run `pnpm install` to install dependencies before typechecking
- The shared package is the foundation - core and other packages depend on it
- Error classes use Effect's Schema.TaggedError for type-safe error handling
## 2026-01-06: Initialized pnpm monorepo
**Feature:** Initialize pnpm monorepo with Effect-TS and TypeScript
**What was done:**
- Created root `package.json` with pnpm workspaces configuration
- Created `pnpm-workspace.yaml` pointing to `packages/*`
- Installed core dependencies: effect, @effect/platform
- Configured `tsconfig.json` with strict mode and Effect language service
- Added scripts: `typecheck`, `test`, `build`
**Notes for next person:**
- Use `pnpm --filter @myapp/packagename <command>` to run commands in specific packages
- Never cd into packages - always use filter syntax
- The Effect language service plugin provides better autocomplete
## 2026-01-07: Added test infrastructure
**Feature:** Add test infrastructure and example tests
**What was done:**
- Added vitest and @effect/vitest to dev dependencies
- Created `vitest.config.ts` with test pattern `packages/**/*.test.ts`
- Created `packages/core/src/__tests__/testUtils.ts` with:
- Mock service factories for unit testing
- Test layer composition utilities
- Created example UserService test demonstrating:
- Effect.runPromise for async tests
- Test layer provision pattern
- Error case testing
**Notes for next person:**
- Run `pnpm test` to execute all tests
- Use `createTestLayer()` from testUtils for consistent test setup
- Tests use Layer.succeed pattern for simple mocks
allowed-tools description
Bash(bash plans/ralph.sh:*)
Run the Ralph iterative PRD loop

Ralph - Iterative PRD Implementation

Run the ralph.sh script to iteratively work through PRD features.

Usage

/ralph <iterations>

Example

/ralph 10

This runs up to 10 iterations, implementing one feature per iteration until the PRD is complete.

What it does

  1. Finds highest-priority feature from plans/prd.json
  2. Implements the feature
  3. Runs pnpm typecheck and pnpm test
  4. Updates PRD and plans/progress.txt
  5. Makes a git commit
  6. Repeats until complete or max iterations reached

Run: bash plans/ralph.sh $ARGUMENTS

#!/bin/bash
# Ralph Strategy - Iterative PRD Implementation Script
#
# Usage: bash ralph.sh <iterations>
# Example: bash ralph.sh 10
#
# Prerequisites:
# - Claude Code CLI installed (`claude` command available)
# - prd.json in plans/ directory
# - progress.txt in plans/ directory
# - pnpm project with `typecheck` and `test` scripts
set -e
if [ -z "$1" ]; then
echo "Usage: $0 <iterations>"
exit 1
fi
for ((i=1; i<=$1; i++)); do
echo "Iteration $i"
echo "--------------------------------"
result=$(claude --permission-mode acceptEdits -p "@plans/prd.json @progress.txt \
1. Find the highest-priority feature to work on and work only on that feature. \
This should be the one YOU decide has the highest priority - not necessarily the first in the list. \
2. Check that the types check via pnpm typecheck and that the tests pass via pnpm test. \
3. Update the PRD with the work that was done. \
4. Append your progress to the progress.txt file. \
Use this to leave a note for the next person working in the codebase. \
5. Make a git commit of that feature. \
ONLY WORK ON A SINGLE FEATURE. \
If, while implementing the feature, you notice the PRD is complete, output <promise>COMPLETE</promise>
")
echo "$result"
if [[ "$result" == *"<promise>COMPLETE</promise>"* ]]; then
echo "PRD complete, exiting."
notify "Ralph PRD complete after $i iterations"
exit 0
fi
done
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment