Module 3: Understand an Existing Architecture

Flow Analysis — Request→Response and Data Pipelines

Flow Analysis — Request→Response and Data Pipelines

Capsule description

The dependency maps you learned in the previous capsule show you what's connected to what — the static structure of the codebase. But a codebase isn't just structure; it's behavior. And behavior is understood through flows: how a request travels from the entry point to the response, how the data is transformed at each step, and what side effects happen along the way.

In this capsule you're going to learn to generate flow analysis with Claude Code — complete traces that show the execution sequence of a feature. You're going to trace HTTP flows (request→response), data flows (input→transformations→output), and error flows (exception→propagation→response). Each type of flow reveals different information about the codebase.

The connection with the project is direct: in the Architecture Map you're going to build in capsule 05, flow analysis is one of the three main components (along with dependency maps and pattern analysis). Without flow analysis, your architecture map shows structure but not behavior.


Why Flow Analysis Matters

Structure vs Behavior

# The dependency maps tell you:
# "order_service.py imports payment_service.py"
#
# The flow analysis tells you:
# "When a user checks out, order_service
#  validates the cart, computes the total, calls
#  payment_service.charge(), and if the payment fails,
#  it reverts the inventory changes"

The structure shows you the pieces. The flow shows you how they fit together when they run. To make refactoring decisions, you need both.

The 3 types of flow

TypeWhat it showsExample
Request flowThe execution sequence of an endpointPOST /orders → validate → calculate → charge → save
Data flowHow the data is transformedJSON input → Pydantic model → DB record → JSON response
Error flowHow errors propagateStripeError → PaymentFailed → HTTP 402 + rollback

Request Flow Analysis

What it is and what it reveals

A request flow traces the complete sequence of functions that run when an HTTP request arrives. It reveals:

  • Processing layers: middleware → route → service → repository → DB
  • Business logic: what it validates, what it computes, what it verifies
  • Decision points: if/else that change the flow
  • Side effects: emails, logs, events, cache updates

Generating request flows with Claude Code

Basic level — Linear trace:

# Prompt:
> "Use Explore to trace the complete flow of the endpoint
   POST /api/users/register. For each step, state:
   file, function, what it receives, and what it produces."

# Expected output:
# 1. src/api/routes/auth.py:register()
#    Receives: POST body {name, email, password}
#    Produces: calls auth_service.create_user()
#
# 2. src/middleware/validator.py:validate_request()
#    Receives: raw request
#    Produces: validated RegisterRequest (Pydantic)
#
# 3. src/services/auth_service.py:create_user()
#    Receives: RegisterRequest
#    Produces: User object + tokens
#    Internal steps:
#      - Verifies email doesn't exist
#      - Hashes the password (bcrypt)
#      - Creates User in the DB
#      - Generates JWT tokens
#
# 4. src/models/user.py:User.create()
#    Receives: user_data dict
#    Produces: User row in the DB
#
# 5. src/utils/security.py:generate_tokens()
#    Receives: user_id
#    Produces: {access_token, refresh_token}
#
# 6. src/api/routes/auth.py:register()
#    Receives: User + tokens
#    Produces: HTTP 201 {user: {...}, tokens: {...}}

Intermediate level — Trace with branching:

# Prompt:
> "Use Explore to trace the flow of POST /api/orders,
   including the points where the flow can branch
   (failed validation, rejected payment, out of stock)"

# Expected output includes the happy path AND the branches:
#
# Step 3: validate_order()
#   → If empty cart: return HTTP 400 "Empty cart"
#   → If product out of stock: return HTTP 409 "Out of stock"
#   → If valid: continue to Step 4
#
# Step 5: charge_payment()
#   → If card rejected: rollback inventory → HTTP 402
#   → If timeout: retry 1 time → if it fails: HTTP 503
#   → If successful: continue to Step 6

Advanced level — Trace with side effects:

# Prompt:
> "Use Explore to trace the flow of POST /api/orders
   including ALL the side effects. Not just what the endpoint
   returns, but everything that happens as a consequence:
   emails, events, cache updates, logs."

# Expected output adds:
# Side effects of the happy path:
#   1. inventory_service.reduce_stock() → updates the DB
#   2. email_service.send_confirmation() → email to the user
#   3. event_bus.publish(OrderCreated) → notifies subscribers
#   4. analytics.track("order_created") → metrics
#   5. cache.invalidate("user_orders_{id}") → clears the cache
#   6. audit_log.record() → audit record

Data Flow Analysis

What it is and what it reveals

A data flow traces how data is transformed through the system. It doesn't focus on what functions are called, but on what form the data has at each point.

Generating data flows with Claude Code

Data transformation trace:

# Prompt:
> "Use Explore to trace how the data is transformed
   from when a user sends a JSON to create an order
   to what's saved in the database. At each step,
   show the structure of the data."

# Expected output:
#
# Step 1: Client input (JSON)
# {
#   "product_id": 123,
#   "quantity": 2,
#   "shipping_address": "...",
#   "coupon_code": "SAVE10"
# }
#
# Step 2: After validation (Pydantic model)
# OrderCreateRequest(
#   product_id=123,
#   quantity=2,
#   shipping_address=Address(street="...", city="...", ...),
#   coupon_code="SAVE10"
# )
# + Added: user_id=456 (from the JWT token)
#
# Step 3: After calculations (enriched dict)
# {
#   "user_id": 456,
#   "product_id": 123,
#   "quantity": 2,
#   "unit_price": 24.99,
#   "subtotal": 49.98,
#   "discount": 5.00,  # coupon applied
#   "tax": 4.05,
#   "shipping": 5.99,
#   "total": 55.02
# }
#
# Step 4: What's saved in the DB (Order model)
# Order(
#   id=789,
#   user_id=456,
#   status="pending_payment",
#   subtotal=49.98,
#   discount=5.00,
#   tax=4.05,
#   shipping=5.99,
#   total=55.02,
#   created_at=datetime(2026, 4, 5, ...),
#   updated_at=datetime(2026, 4, 5, ...)
# )
# + OrderItem(order_id=789, product_id=123, quantity=2, unit_price=24.99)
#
# Step 5: What the client receives (JSON response)
# {
#   "order_id": 789,
#   "status": "pending_payment",
#   "total": 55.02,
#   "items": [{"product_id": 123, "quantity": 2, "subtotal": 49.98}],
#   "estimated_delivery": "2026-04-10"
# }

Why does this matter for refactoring? If you need to change how the discount is computed, the data flow tells you exactly at which step the transformation happens and what data you need at that point. Without a data flow, you'd have to read all the code to find where the calculation happens.


Error Flow Analysis

What it is and what it reveals

An error flow traces how exceptions and errors propagate through the system. It reveals:

  • Where errors originate: what function raises the exception
  • How they propagate: what layers catch, transform, or re-raise it
  • What the user receives: the final error message and the HTTP status code
  • What's lost: whether there are errors that are silenced or logged without reporting

Generating error flows with Claude Code

# Prompt:
> "Use Explore to trace what happens when the payment
   fails during order creation. From the error
   in Stripe to the response to the user, including
   any rollback or cleanup."

# Expected output:
#
# 1. stripe.Charge.create() raises stripe.CardError
#    File: (external stripe library)
#    Data: {"code": "card_declined", "message": "..."}
#
# 2. payment_service.py:charge() catches CardError
#    Transforms to: PaymentFailedError(reason="card_declined")
#    Logs: logger.warning("Payment failed", extra={...})
#
# 3. order_service.py:create_order() catches PaymentFailedError
#    Executes rollback:
#      - inventory_service.restore_stock(product_id, quantity)
#      - order_repo.update_status(order_id, "payment_failed")
#    Re-raises: OrderError("Payment failed: card_declined")
#
# 4. api/routes/orders.py:create_order() catches OrderError
#    Returns: HTTP 402 {
#      "error": "payment_failed",
#      "message": "Your card was declined",
#      "order_id": 789,
#      "status": "payment_failed"
#    }
#
# Finding: If the inventory rollback fails, the error
# is logged but the order is left in an inconsistent state
# (status=payment_failed but inventory not restored).
# This is a potential bug.

The value of the error flow: finding how errors propagate frequently reveals bugs. In the previous example, we discovered that a failure in the rollback leaves inconsistent data — a finding worth more than all the analysis put together.


Visualizing Flows with Mermaid

Generating diagrams with Claude Code

# Prompt:
> "Generate a mermaid sequence diagram for the flow
   of POST /api/orders (happy path)"

# Expected output:
# ```mermaid
# sequenceDiagram
#     participant C as Client
#     participant R as Router
#     participant V as Validator
#     participant OS as OrderService
#     participant PS as PaymentService
#     participant DB as Database
#     participant ES as EmailService
#
#     C->>R: POST /api/orders
#     R->>V: validate(request)
#     V-->>R: OrderCreateRequest
#     R->>OS: create_order(request)
#     OS->>DB: check_inventory()
#     DB-->>OS: stock available
#     OS->>PS: charge(amount)
#     PS-->>OS: payment_id
#     OS->>DB: save_order()
#     DB-->>OS: order_id
#     OS->>ES: send_confirmation()
#     OS-->>R: Order(id=789)
#     R-->>C: HTTP 201 {order_id: 789}
# ```

Diagrams for error flows

# Prompt:
> "Generate a mermaid sequence diagram for the flow
   of POST /api/orders when the payment fails"

# Expected output will include:
# PS--xOS: CardError
# OS->>DB: rollback_inventory()
# OS--xR: PaymentFailedError
# R-->>C: HTTP 402

Comparison: Manual Analysis vs Claude Code

CriterionManual (reading code)Claude Code (flow analysis)
Time for one flow30-60 min5-10 min
CompletenessDepends on experienceConsistent
Side effectsEasy to forget oneFinds them all
DiagramsRequires an extra toolGenerates mermaid directly
Error pathsRequires tracing each catchTraces the whole chain
UpdatingRe-read everythingRe-run the prompt

Trade-off: Manual analysis gives deep intuition about the code (you read it, you understand it viscerally). Claude Code gives completeness and speed. The ideal is to use Claude Code for the first trace and then manually read the parts that seem critical or suspicious to you.


Connection with the Project

In the Architecture Map you'll build in capsule 05, flow analysis is the second main component:

  • Dependency maps (capsule 02) show the static structure
  • Flow analysis (this capsule) shows the dynamic behavior
  • Pattern analysis (capsule 04) identifies patterns and anti-patterns

For the project, you need at least 2 flow traces of critical flows in the codebase you're analyzing.


Troubleshooting

Problem 1: The trace is too long

Cause: The flow passes through many layers.

Solution: Ask for the trace at different zoom levels:

# High level (overview):
> "Trace of the endpoint at the services level, without detailing
   the internal functions of each service"

# Detailed level (only one section):
> "Now detail only the payment_service.charge() step:
   what it does internally step by step"

Problem 2: I can't find the entry point

Cause: The codebase uses a framework with implicit routing.

Solution: Ask Explore to find the entry point first:

> "Where is the POST /api/orders endpoint defined?
   What file and function handles that request?"

Problem 3: Hidden side effects

Cause: The codebase uses events, signals, or decorators that cause non-obvious side effects.

Solution: Ask specifically:

> "Are there event listeners, Django signals, or decorators
   that run when an order is created? Include
   side effects that aren't called directly."

Problem 4: The error flow doesn't show a rollback

Cause: There's no rollback implemented (possible bug).

Solution: Document it as a finding:

"Finding: When charge() fails after reducing
inventory, there's no automatic inventory rollback.
This can cause data inconsistency."

Exercises

Exercise 1: Identify the type of flow (Easy)

For each question, state which type of flow analysis you'd use (request flow, data flow, or error flow):

  1. "How is a login request processed?"
  2. "What happens to the client's JSON before it's saved in the DB?"
  3. "What happens when the DB is down?"
  4. "How many functions run to generate a report?"
  5. "How does the date format change throughout the system?"
See solution
  1. Request flow — the function sequence of the login endpoint
  2. Data flow — data transformations JSON → DB
  3. Error flow — propagation of a DB connection error
  4. Request flow — the execution sequence of the reports endpoint
  5. Data flow — the transformation of a specific field

Rule: if you ask "what functions run" → request flow. If you ask "how does the data change" → data flow. If you ask "what happens when something fails" → error flow.

Exercise 2: Write flow analysis prompts (Easy)

Write a Claude Code prompt for each type of flow, applied to an e-commerce system:

See solution
# Request flow:
> "Use Explore to trace the complete flow of
   POST /api/cart/checkout. From the user's request
   to the HTTP response, list every function that
   runs with its file and purpose."

# Data flow:
> "Use Explore to trace how the shopping cart data
   is transformed from when the user clicks
   'Buy' to what's saved as an Order in the DB.
   At each step, show the structure of the data."

# Error flow:
> "Use Explore to trace what happens when a cart
   item runs out of stock during checkout.
   At what point is it detected, how does the error propagate,
   and what response does the user receive?"

Exercise 3: Multi-level trace (Medium)

Design a sequence of 3 prompts to analyze the "password reset" flow at three zoom levels: overview, service detail, and email sending detail.

See solution
# Level 1 — Overview:
> "Use Explore to trace the password reset flow
   at the services level. Only the main steps:
   request → processing → email → confirmation."

# Level 2 — Service detail:
> "Dig into the auth_service.initiate_reset() step.
   What token does it generate, how does it store it, and how long does it last?
   Does it check rate limiting?"

# Level 3 — Email detail:
> "Dig into the reset email sending. What
   template does it use, what data does it include, and how is
   the reset link built? Is there a retry if the sending fails?"

Pattern: each level uses the previous answer to decide where to dig deeper. Don't try to do everything in a single prompt.

Exercise 4: Find bugs with error flow (Medium)

Write a prompt to find possible bugs in the error handling of a payment system. The prompt should ask Explore to identify scenarios where an error could leave data in an inconsistent state.

See solution
> "Use Explore to analyze the error handling of the
   payment flow. For each step that can fail (validation,
   charge, save_order, send_email), check:
   1. Is the error caught?
   2. Is a rollback of the previous steps done?
   3. Can data be left inconsistent if it fails
      midway?
   4. Does the user receive a clear error message?
   List any scenario where a failure could leave
   the order or the inventory in an inconsistent state."

Why it works: this prompt explicitly asks for a consistency analysis at each failure point, which is where the hardest bugs to find hide.

Exercise 5: Complete flow analysis (Hard)

Choose an endpoint from a real project and produce the 3 types of flow analysis (request, data, error). Document the prompts and results.

See solution

Example with the POST /api/users endpoint of a FastAPI project:

# Request flow:
> "Trace POST /api/users: sequence of functions,
   middleware included"
# Result: 7 steps from request to response

# Data flow:
> "Trace how the data of POST /api/users is transformed:
   JSON input → validation → processing → DB → response"
# Result: 5 data transformations with the structure at each step

# Error flow:
> "Trace what happens in POST /api/users when:
   a) email already exists, b) password too short,
   c) DB connection timeout"
# Result: 3 error paths with HTTP status codes and messages

# Findings:
# - The password is logged in DEBUG mode (security)
# - There's no rate limiting on registration (abuse potential)
# - The DB timeout error returns a generic 500 without retry

The valuable part: the findings are actionable findings you can report to the team.


Summary

In this capsule you learned:

  • Three types of flow analysis: request flow (execution sequence), data flow (data transformation), error flow (error propagation)
  • Request flow reveals the processing layers, business logic, and side effects of an endpoint
  • Data flow shows how the data is transformed at each step — essential to know where to make changes
  • Error flow discovers potential bugs: inconsistent data, silenced errors, missing rollbacks
  • Claude Code generates mermaid diagrams directly, avoiding external tools
  • Flows complement the dependency maps: structure (static) + flows (dynamic) = complete comprehension

Next capsule: Pattern Identification and Anti-Pattern Detection. You're going to learn to recognize architectural patterns (MVC, service layer, repository) and anti-patterns (god objects, circular dependencies) using Claude Code.


Additional Resources

  1. Mermaid Sequence Diagrams - The sequence diagram syntax Claude Code generates
  2. Data Flow Diagrams - Martin Fowler - Fundamentals of data flow analysis
  3. Error Handling Patterns - Patterns of Enterprise Application Architecture (Fowler)
  4. Request Tracing - OpenTelemetry - Tracing tools that complement manual analysis
  5. Python Exception Hierarchy - Python exception reference
  6. Debugging with Data Flow Analysis - Academic fundamentals of data flow analysis

Module 3, Capsule 03 — Refactoring & Legacy Code with Claude Code Guide