You're building a SaaS feature. The user says "we need a way to save articles for later." That's it. No requirements. No edge cases. Just a messy sentence fragment from a customer call.

Most solo developers start coding immediately. They pick a database schema, add some endpoints, maybe sketch a UI. Three days later they realize they forgot to handle duplicates, never defined what "saved" means, and the copy still says "Article Saved successfully" because they haven't gotten to UX writing.

Here's a better way. Turn that messy brief into three structured outputs in 10 seconds flat.

The Three-Role Chain

I showed this in Workshop 2. One feature brief, three AI prompts. Each prompt assigns a different role and produces a different artifact:

  1. PM prompt → structured spec with problem statement, user stories, acceptance criteria, edge cases
  2. UX writer prompt → feature name options, changelog, in-app copy, error messages
  3. Developer prompt → working FastAPI backend with endpoints, models, schemas, tests

Same input. Different prompts for different roles. Swap the brief, keep the prompts. 10 seconds to produce all three outputs.

The Messy Feature Brief

Start with the raw input. No cleanup needed. This came straight from a customer interview:

"We need a way for users to save articles for later. They're finding good content but losing track of it. Should be able to access from anywhere."

That's your input. Now run it through each role prompt.

Role 1: The Product Manager

The PM prompt turns ambiguity into structure. Here's the template:

You are a senior product manager. I will provide a feature brief.
You must produce a structured product specification including:

1. Problem statement (why this exists)
2. Target users and use cases
3. User stories in Given-When-Then format
4. Acceptance criteria with clear pass/fail conditions
5. Edge cases and error scenarios
6. Success metrics (how we know it works)

Format: Clear sections. Bulleted lists. No fluff.

Run your brief through this prompt. The output:

Problem Statement
Users discover valuable content during research sessions but lose
track of it, requiring them to re-find or remember URLs manually.

Target Users
- Researchers collecting reference material
- Professionals curating industry reading
- Students building reading lists

User Stories

Given I am reading an article
When I click the "Save for Later" button
Then the article should be added to my saved collection
And I should see a confirmation message

Given I have saved articles
When I view my saved collection
Then I should see all saved articles in reverse chronological order
And I can filter by tag or date range

Acceptance Criteria
- Users can save any article with one click
- Saved articles persist across sessions and devices
- Maximum 500 saved articles per user
- Duplicate detection prevents saving same article twice
- Articles are retrievable within 500ms

Edge Cases
- Article URL becomes 404: mark as unavailable, don't delete
- User exceeds 500 limit: show upgrade message
- Network failure during save: queue for retry, notify user
- Article already saved: show "already saved" message

Success Metrics
- 70% of users who click save return to view collection
- Average collection size: 20+ articles
- Less than 5% support tickets related to saving

You now have a complete spec. No ambiguity. The developer prompt will reference this directly.

Role 2: The UX Writer

Now generate all the copy. Feature names, buttons, error messages, changelog. One prompt covers it all:

You are a senior UX writer. I will provide a feature brief.
You must produce all user-facing copy including:

1. Feature name options (5 distinct approaches)
2. Primary button/action copy (max 3 words)
3. Success confirmation message
4. Empty state message (when no items exist)
5. Error messages for each failure scenario
6. Changelog entry (what users will see in updates)
7. Help tooltip text (one sentence, clear not clever)

Tone: Direct. Action-oriented. No jargon.
Format: Copy in quotes. Context in parentheses.

The output:

Feature Names
- "Saved Articles" (clear, descriptive)
- "Reading List" (common mental model)
- "Library" (personal, organized)
- "Bookmarks" (familiar browser metaphor)
- "Keep" (minimal, verb-first)

Primary Actions
- "Save Article" (what it is)
- "Save for Later" (when it happens)
- "Add to List" (collection metaphor)

Success Message
"Article saved. View your collection."

Empty State
"No saved articles yet. Click 'Save' on any article to build
your collection."

Error Messages
- Article already saved: "This article is already in your collection."
- Save limit reached: "You've reached 500 saved articles.
  Upgrade to save more."
- Network error: "Couldn't save. Check your connection and try again."
- Article unavailable: "This article can't be found. It may have been removed."

Changelog
"New: Save articles for later. Build your personal reading
collection and access it from any device."

Help Tooltip
"Save articles to read later. Access your collection anytime."

You now have every word your users will see. No writer's block. No inconsistent phrasing across the product.

Role 3: The Developer

Finally, the implementation. The developer prompt takes both the brief and the PM spec as input:

You are a senior backend engineer. I will provide:
1. Feature brief
2. Product specification with acceptance criteria

You must produce a complete FastAPI backend including:
- Pydantic models for request/response
- SQLAlchemy database models with relationships
- API endpoints following REST conventions
- Input validation and error handling
- Basic tests for happy path and edge cases
- Database migration notes

Tech stack: FastAPI, SQLAlchemy, PostgreSQL, Pydantic v2.
Code: Production-ready. Type hints everywhere. Clear docstrings.
Format: Separate code blocks for models, endpoints, tests.

The output is substantial. Here's the structure:

# models.py
from sqlalchemy import Column, Integer, String, DateTime, Boolean, ForeignKey
from sqlalchemy.orm import relationship
from datetime import datetime

class User(Base):
    __tablename__ = "users"

    id = Column(Integer, primary_key=True)
    email = Column(String, unique=True, nullable=False)
    saved_articles = relationship("SavedArticle", back_populates="user")

class SavedArticle(Base):
    __tablename__ = "saved_articles"

    id = Column(Integer, primary_key=True)
    user_id = Column(Integer, ForeignKey("users.id"), nullable=False)
    url = Column(String, nullable=False)
    title = Column(String)
    saved_at = Column(DateTime, default=datetime.utcnow)
    is_available = Column(Boolean, default=True)

    user = relationship("User", back_populates="saved_articles")

# schemas.py
from pydantic import BaseModel, HttpUrl, validator
from datetime import datetime

class SavedArticleCreate(BaseModel):
    url: HttpUrl
    title: str | None = None

    @validator('title')
    def title_length(cls, v):
        if v and len(v) > 200:
            raise ValueError('Title too long')
        return v

class SavedArticleResponse(BaseModel):
    id: int
    url: str
    title: str | None
    saved_at: datetime
    is_available: bool

# endpoints.py
from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy.orm import Session

router = APIRouter(prefix="/api/saved-articles", tags=["saved-articles"])

@router.post("/", response_model=SavedArticleResponse, status_code=201)
async def save_article(
    article: SavedArticleCreate,
    current_user: User = Depends(get_current_user),
    db: Session = Depends(get_db)
):
    # Check for duplicates
    existing = db.query(SavedArticle).filter(
        SavedArticle.user_id == current_user.id,
        SavedArticle.url == article.url
    ).first()

    if existing:
        raise HTTPException(
            status_code=409,
            detail="Article already saved"
        )

    # Check user limit
    count = db.query(SavedArticle).filter(
        SavedArticle.user_id == current_user.id
    ).count()

    if count >= 500:
        raise HTTPException(
            status_code=403,
            detail="Save limit reached"
        )

    # Create saved article
    db_article = SavedArticle(
        user_id=current_user.id,
        url=str(article.url),
        title=article.title
    )
    db.add(db_article)
    db.commit()
    db.refresh(db_article)

    return db_article

@router.get("/", response_model=list[SavedArticleResponse])
async def list_saved_articles(
    current_user: User = Depends(get_current_user),
    db: Session = Depends(get_db),
    skip: int = 0,
    limit: int = 50
):
    articles = db.query(SavedArticle).filter(
        SavedArticle.user_id == current_user.id
    ).order_by(SavedArticle.saved_at.desc()).offset(skip).limit(limit).all()

    return articles

# tests.py
from fastapi.testclient import TestClient
from sqlalchemy.orm import Session

def test_save_article_happy_path(client: TestClient, db: Session):
    response = client.post(
        "/api/saved-articles/",
        json={"url": "https://example.com/article", "title": "Test"},
        headers={"Authorization": f"Bearer {token}"}
    )
    assert response.status_code == 201
    data = response.json()
    assert data["url"] == "https://example.com/article"
    assert data["title"] == "Test"
    assert "id" in data

def test_duplicate_article_prevented(client: TestClient, db: Session):
    # First save
    client.post(
        "/api/saved-articles/",
        json={"url": "https://example.com/dup"},
        headers={"Authorization": f"Bearer {token}"}
    )

    # Duplicate save
    response = client.post(
        "/api/saved-articles/",
        json={"url": "https://example.com/dup"},
        headers={"Authorization": f"Bearer {token}"}
    )

    assert response.status_code == 409
    assert "already saved" in response.json()["detail"]

def test_save_limit_enforced(client: TestClient, db: Session, user):
    # Create 500 existing saves
    for i in range(500):
        article = SavedArticle(user_id=user.id, url=f"url{i}")
        db.add(article)
    db.commit()

    # Try one more
    response = client.post(
        "/api/saved-articles/",
        json={"url": "https://example.com/overflow"},
        headers={"Authorization": f"Bearer {token}"}
    )

    assert response.status_code == 403
    assert "limit reached" in response.json()["detail"]

You now have working code. Models, endpoints, validation, tests. The developer prompt referenced the PM spec directly for acceptance criteria and edge cases. The UX writer's copy flows into the frontend layer. All three outputs are coordinated by design.

Why This Works

The key insight is role assignment. Each prompt tells Claude exactly who to be and what format to produce. Context-task-structure, every time.

Context: "You are a senior product manager" not "help me think about this feature."

Task: Clear deliverables. Not "explore" but "produce user stories in Given-When-Then format."

Structure: Output format specified upfront. Sections, bullets, code blocks. No guessing.

This pattern eliminates the conversation trap. You don't go back and forth clarifying requirements. You don't iterate on copy. You don't debug incomplete code. One prompt, complete output.

Setting Up Your Stack

Make this repeatable. Create three prompt files:

~/prompts/
  ├── pm-spec.md
  ├── ux-writer.md
  └── backend-dev.md

Each file contains the role prompt template. When you get a feature brief:

  1. Paste the brief into each prompt file
  2. Run Claude with each prompt
  3. Save the outputs to your project

Project structure:

project-save-articles/
├── specs/
│   └── pm-spec.md           # PM output
├── copy/
│   └── ux-copy.md           # UX writer output
├── backend/
│   ├── models.py            # Developer output
│   ├── schemas.py
│   ├── endpoints.py
│   └── tests.py
└── feature-brief.md         # Original input

The PM spec lives alongside your code. The UX copy becomes your frontend copy. The developer output becomes your backend. Everything traceable to the original brief.

Prompts, Not Conversations

Most AI workflows fail because they treat the tool like a junior colleague. "Hey, can you help me think about this feature?" Then you're in a chat. Iterating. Explaining context. Fixing misunderstandings.

The solo dev stack treats AI like specialized contractors. You wouldn't ask your PM to "help think about" requirements. You'd give them a brief and expect a spec. Same for UX writers. Same for developers.

Role prompts encode your standards. The output format matches your workflow. The quality is consistent because the prompt is consistent.

One messy brief. Three role prompts. Ten seconds to full spec, copy, and code. That's the solo dev startup stack.