Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Walking in Their Shoes: User Journeys

Ever watched someone use a product you built? You notice things you never would—confusing buttons, unclear error messages, workflows that don't make sense. Why? Because you built it from your perspective, not theirs.

User journeys (or scenarios) are your way to model the complete user experience—from their perspective. They show what users do, how your system responds, and what happens when things go right (and wrong).

In this lesson, you'll learn to create user journeys that capture the complete user experience. You'll discover how to model happy paths and error paths, document edge cases, and create scenarios that serve as both requirements and test cases.

Let's start by understanding what user journeys actually are.

Learning Goals

By the end of this lesson, you'll be able to:

  • Model user journeys and behavioral scenarios from user's perspective
  • Use BDD-style scenarios to document requirements
  • Model both happy paths and error paths
  • Document edge cases and unusual scenarios
  • Create scenarios that serve as test cases and documentation

What Are User Journeys, Really?

User journeys (also called scenarios or behavioral flows) show how a person interacts with your system to achieve a goal. Unlike data flows that show data moving through a system, user journeys show human behavior and system responses.

Think of it like a story: "As a customer, I want to buy a product so I can use it." The journey shows every step—what the customer does, what the system does, and what the customer experiences.

User journeys capture:

  • User actions (what they click, type, select)
  • System responses (what happens, what they see)
  • Success paths (when everything works)
  • Error paths (when things go wrong)
  • Decision points (where different things happen based on conditions)

What makes them special:

  • User's perspective (not system's)
  • Behavioral (not just data)
  • Complete experience (start to finish)
  • Both happy and sad paths

Why User Journeys Matter

I've learned this the hard way. I once built a feature without modeling user journeys, and when we launched, users were completely confused.

They couldn't find the checkout button. When they did, error messages were cryptic. The "success" page didn't tell them what to do next. Support tickets flooded in. We had to rebuild the entire feature.

If I'd modeled user journeys upfront, we would have seen these issues immediately. The journey would have shown: "User clicks checkout → System shows error 'ERR_500' → User is confused."

User journeys matter because:

1. Requirements Clarity

User journeys turn vague requirements into concrete scenarios:

Vague requirement: "Users should be able to check out"

User journey makes it concrete:

CheckoutJourney = scenario "Customer Checkout" {
  Customer -> WebApp "Clicks checkout button"
  WebApp -> API "Validates cart"
  API -> Database "Checks inventory"
  Database -> API "Inventory available"
  API -> PaymentGateway "Processes payment"
  PaymentGateway -> API "Payment successful"
  API -> Database "Saves order"
  API -> EmailService "Sends confirmation"
  WebApp -> Customer "Shows order confirmation page"
}

Now everyone knows exactly what "checkout" means—every step, every system involved, every user action.

2. Test Case Generation

User journeys become test cases automatically:

HappyPathTest = scenario "Test: Successful Checkout" {
  // From the user journey
  Customer -> WebApp "Clicks checkout"
  WebApp -> API "Validates cart"
  // ... test each step
}

ErrorPathTest = scenario "Test: Checkout with Invalid Payment" {
  Customer -> WebApp "Clicks checkout"
  WebApp -> API "Validates cart"
  API -> PaymentGateway "Processes payment"
  PaymentGateway -> API "Payment declined: invalid card"
  API -> WebApp "Returns error: Invalid card"
  WebApp -> Customer "Shows error: Please check your card details"
}

I've worked on teams where we spent weeks writing test cases manually. With user journeys, we just turn scenarios into tests. Huge time saver.

3. User Experience Visibility

User journeys show the complete experience—not just what works, but how it feels:

RegistrationJourney = scenario "User Registration" {
  Customer -> WebApp "Opens registration form"
  Customer -> WebApp "Enters name and email"
  Customer -> WebApp "Enters password"
  Customer -> WebApp "Clicks 'Create Account'"
  WebApp -> API "Submits registration"
  API -> Database "Creates user account"
  API -> EmailService "Sends welcome email"
  WebApp -> Customer "Shows 'Account created!' message"
  WebApp -> Customer "Redirects to dashboard"
}

See how this shows the full experience? Form entry → submission → success message → welcome email → dashboard. Anyone reading this understands exactly what users experience.

4. Edge Case Discovery

When you model user journeys, you naturally think about edge cases:

RegistrationEdgeCases = scenario "Registration Edge Cases" {
  // Edge case 1: Duplicate email
  Customer -> WebApp "Registers with existing email"
  WebApp -> API "Submits registration"
  API -> Database "Checks email exists"
  Database -> API "Email already exists"
  API -> WebApp "Returns error: Email already registered"
  WebApp -> Customer "Shows error: Email already in use"

  // Edge case 2: Weak password
  Customer -> WebApp "Registers with weak password"
  WebApp -> API "Submits registration"
  API -> ValidationService "Validates password strength"
  ValidationService -> API "Password too weak"
  API -> WebApp "Returns error: Password must be at least 8 characters"
  WebApp -> Customer "Shows error: Password too weak"
}

I once launched a feature without modeling edge cases. Users started using weird inputs, emojis in names, passwords with special characters, and the system broke in creative ways. Model edge cases upfront.

Creating User Journeys in Sruja

Sruja gives you two keywords for user journeys, and they're interchangeable:

Using scenario

Use scenario for behavioral flows—it's the most common and descriptive choice:

CheckoutScenario = scenario "Customer Checkout Experience" {
  Customer -> Shop.WebApp "Clicks checkout button"
  Shop.WebApp -> Shop.API "Validates shopping cart"
  Shop.API -> PaymentGateway "Processes payment"
  Shop.WebApp -> Customer "Shows order confirmation"
}

Using story

Use story when you want to emphasize the narrative or story aspect:

CheckoutStory = story "As a customer, I want to checkout so I can purchase my items" {
  Customer -> Shop.WebApp "Clicks checkout button"
  Shop.WebApp -> Shop.API "Validates shopping cart"
  Shop.API -> PaymentGateway "Processes payment"
  Shop.WebApp -> Customer "Shows order confirmation"}
}

The story keyword is great for BDD (Behavior-Driven Development) style where you write scenarios in "Given-When-Then" format.

BDD (Behavior-Driven Development) Style

BDD is about writing requirements as behavior, not specifications. User journeys in Sruja map perfectly to BDD's "Given-When-Then" structure.

The Given-When-Then Pattern

// GIVEN: Customer has items in shopping cart
// WHEN: Customer clicks checkout and completes payment
// THEN: Order is created and confirmation is shown

CheckoutJourney = scenario "Customer Checkout (BDD Style)" {
  // GIVEN: Customer has items in cart
  Customer -> Shop.WebApp "Views shopping cart with 3 items"
  Shop.WebApp -> Shop.API "Fetches cart contents"
  Shop.API -> Shop.Database "Returns cart data"
  
  // WHEN: Customer completes checkout flow
  Customer -> Shop.WebApp "Clicks checkout button"
  Shop.WebApp -> Shop.API "Validates cart"
  Shop.API -> Shop.Database "Checks inventory"
  Shop.Database -> Shop.API "All items available"
  Shop.API -> PaymentGateway "Processes payment"
  PaymentGateway -> Shop.API "Payment successful"
  
  // THEN: Order is created and confirmation shown
  Shop.API -> Shop.Database "Creates order"
  Shop.API -> Shop.Database "Reserves inventory"
  Shop.API -> EmailService "Sends confirmation email"
  Shop.WebApp -> Customer "Shows order confirmation page with order #12345"
}

Why BDD works:

  • It's in plain language anyone can understand
  • It focuses on behavior, not implementation
  • It serves as both requirements and tests
  • It's unambiguous (unlike "system should work well")

I've worked with product managers who couldn't understand technical specs. But they understood BDD scenarios immediately. It became our common language.

User Journey Patterns

After modeling user journeys for years, I've found patterns that repeat constantly. Let me show you the ones I see most often.

Pattern 1: Happy Path

The ideal scenario where everything works perfectly:

HappyRegistration = scenario "User Registration (Happy Path)" {
  Customer -> WebApp "Opens registration form"
  Customer -> WebApp "Enters name, email, password"
  Customer -> WebApp "Clicks 'Create Account'"
  WebApp -> API "Submits registration"
  API -> Database "Creates user account"
  Database -> API "User created successfully"
  API -> EmailService "Sends welcome email"
  EmailService -> Customer "Receives welcome email"
  API -> WebApp "Returns success"
  WebApp -> Customer "Shows 'Account created! Welcome!' message"
  WebApp -> Customer "Redirects to dashboard"
}

Characteristics:

  • No errors
  • No branches
  • Perfect flow from start to finish
  • User achieves their goal

When to model:

  • First—model the ideal scenario
  • Before optimizing, understand what success looks like
  • Before testing edge cases, have a working baseline

Pattern 2: Error Path

What happens when things go wrong:

ErrorRegistration = scenario "User Registration (Error Path: Duplicate Email)" {
  Customer -> WebApp "Opens registration form"
  Customer -> WebApp "Enters existing email: john@example.com"
  Customer -> WebApp "Enters name and password"
  Customer -> WebApp "Clicks 'Create Account'"
  WebApp -> API "Submits registration"
  API -> Database "Checks if email exists"
  Database -> API "Email already exists: john@example.com"
  API -> WebApp "Returns error: Email already registered"
  WebApp -> Customer "Shows error: 'Email already in use. Try logging in or use a different email.'"
}

Common error paths to model:

  • Duplicate data (email, username)
  • Invalid data (email format, weak password)
  • Missing data (required fields empty)
  • System errors (database down, API timeout)
  • Business rule violations (age restriction, region blocking)

Characteristics:

  • Error occurs at some step
  • System returns meaningful error
  • User sees helpful error message
  • User can correct and retry

When to model:

  • Always model critical error paths
  • Focus on errors users actually encounter
  • Ensure error messages are helpful, not cryptic

I once worked on a system where error messages were like "ERR_500_CHECKOUT_FAILED." Users had no idea what went wrong. We rewrote them to be helpful: "Payment failed. Please check your card details or try a different payment method." Support tickets dropped by 70%.

Pattern 3: Branching Path

Different things happen based on conditions:

BranchingApproval = scenario "Order Approval (Branching Based on Value)" {
  Manager -> WebApp "Submits order for approval"
  WebApp -> API "Processes approval request"
  API -> Database "Fetches order details"
  Database -> API "Returns order: value = $1500"
  
  // Branch 1: Auto-approve for low-value orders
  if value < 1000 {
    API -> Database "Updates order: auto-approved"
    API -> WebApp "Returns: Order auto-approved"
    WebApp -> Manager "Shows 'Order approved! Shipping soon.'"
  }
  
  // Branch 2: Manual review for high-value orders
  if value >= 1000 {
    API -> EmailService "Sends approval request to director"
    EmailService -> Director "Receives approval request"
    Director -> WebApp "Reviews order and approves"
    WebApp -> API "Submits approval decision"
    API -> Database "Updates order: manually approved"
    API -> WebApp "Returns: Order approved by director"
    WebApp -> Manager "Shows 'Order approved by director. Shipping soon.'"
  }
}

Characteristics:

  • Decision point in the flow
  • Multiple possible paths
  • Different actions based on conditions
  • Paths may converge at the end

When to model:

  • Business logic has rules
  • Different user types have different experiences
  • System behavior changes based on context

Pattern 4: Retry Path

System tries multiple times before succeeding or failing:

RetryPayment = scenario "Payment with Automatic Retry" {
  Customer -> WebApp "Clicks 'Place Order'"
  WebApp -> API "Submits order for payment"
  
  // Attempt 1: Fails with timeout
  API -> PaymentGateway "Process payment"
  PaymentGateway -> API "Payment failed: timeout"
  API -> WebApp "Returns: Processing, please wait..."
  WebApp -> Customer "Shows spinner: 'Processing your payment...'"
  
  // Attempt 2: Fails with timeout
  API -> PaymentGateway "Retry payment (attempt 2)"
  PaymentGateway -> API "Payment failed: timeout"
  
  // Attempt 3: Succeeds
  API -> PaymentGateway "Retry payment (attempt 3)"
  PaymentGateway -> API "Payment successful!"
  
  // Continue with success
  API -> Database "Saves order"
  API -> EmailService "Sends confirmation"
  WebApp -> Customer "Shows order confirmation"
}

Characteristics:

  • Same action repeated
  • Eventually succeeds or fails permanently
  • User sees "retrying" state
  • Transparent about what's happening

When to model:

  • External services are flaky
  • Network issues are common
  • You want to handle transient failures gracefully

Complete User Journey Example

Let me show you a complete user journey that brings everything together:

import { * } from 'sruja.ai/stdlib'

// Person
Customer = person "Customer"

// System
Shop = system "Shop" {
  WebApp = container "Web Application"
  API = container "API Service"
  Database = database "PostgreSQL"
}

// External systems
PaymentGateway = system "Payment Gateway" {
  metadata {
    tags ["external", "vendor"]
    sla "99.9% uptime"
  }
}

EmailService = system "Email Service" {
  metadata {
    tags ["external", "vendor"]
  }
}

// Complete user journey from browsing to confirmation
CompleteCheckoutJourney = scenario "Complete Checkout Experience" {
  // Step 1: Browse products
  Customer -> Shop.WebApp "Browses products"
  Shop.WebApp -> Shop.API "Fetches product list"
  Shop.API -> Shop.Database "Queries products"
  Shop.Database -> Shop.API "Returns 50 products"
  Shop.API -> Shop.WebApp "Returns product data"
  Shop.WebApp -> Customer "Displays products grid"

  // Step 2: Add to cart
  Customer -> Shop.WebApp "Clicks 'Add to Cart' on Product #5"
  Shop.WebApp -> Shop.API "Adds item to cart"
  Shop.API -> Shop.Database "Saves cart item"

  // Step 3: Review cart
  Customer -> Shop.WebApp "Clicks 'View Cart'"
  Shop.WebApp -> Shop.API "Fetches cart contents"
  Shop.API -> Shop.Database "Returns cart: 3 items, $75 total"
  Shop.API -> Shop.WebApp "Returns cart data"
  Shop.WebApp -> Customer "Shows cart with total"

  // Step 4: Checkout
  Customer -> Shop.WebApp "Clicks 'Checkout'"
  Shop.WebApp -> Shop.API "Initiates checkout"
  Shop.API -> Shop.Database "Validates cart"
  Shop.Database -> Shop.API "Cart is valid"
  
  // Step 5: Payment processing
  Shop.API -> PaymentGateway "Process payment: $75"
  PaymentGateway -> Shop.API "Payment successful!"
  
  // Step 6: Order creation
  Shop.API -> Shop.Database "Creates order #12345"
  Shop.API -> Shop.Database "Reserves inventory"
  Shop.API -> EmailService "Send order confirmation"
  
  // Step 7: Confirmation page
  Shop.API -> Shop.WebApp "Returns order confirmation"
  Shop.WebApp -> Customer "Shows 'Order #12345 Confirmed!' page"
  
  // Step 8: Email delivery
  EmailService -> Customer "Sends confirmation email"
}

view index {
  include *
}

This journey shows the complete experience from start to finish—every user action, every system response, every step. Anyone reading this understands exactly what a customer experiences when checking out.

Testing with Scenarios

One of the most powerful things about user journeys: they become test cases automatically.

Acceptance Criteria as Scenarios

// Acceptance criteria: As a customer, I want to checkout so that I can purchase products

// AC1: Customer can checkout with valid payment
HappyCheckout = scenario "AC1: Successful Checkout" {
  Customer -> Shop.WebApp "Clicks checkout with valid payment"
  Shop.WebApp -> Shop.API "Validates cart"
  Shop.API -> PaymentGateway "Process payment"
  PaymentGateway -> Shop.API "Payment successful"
  Shop.API -> Shop.Database "Creates order"
  Shop.API -> EmailService "Sends confirmation"
  Shop.WebApp -> Customer "Shows confirmation page"
}

// AC2: Customer sees helpful error with invalid payment
InvalidPaymentCheckout = scenario "AC2: Checkout with Invalid Payment" {
  Customer -> Shop.WebApp "Clicks checkout with expired card"
  Shop.WebApp -> Shop.API "Validates cart"
  Shop.API -> PaymentGateway "Process payment"
  PaymentGateway -> Shop.API "Payment declined: card expired"
  Shop.API -> Shop.WebApp "Returns error"
  Shop.WebApp -> Customer "Shows 'Card expired. Please update your payment method.'"
}

// AC3: Customer receives confirmation email
EmailConfirmation = scenario "AC3: Confirmation Email Sent" {
  Shop.API -> Shop.Database "Creates order"
  Shop.API -> EmailService "Send confirmation"
  EmailService -> Customer "Receives confirmation email"}

I've worked on teams where we spent months debating requirements. When we turned them into BDD-style scenarios, everyone agreed. No ambiguity, no confusion, no "I thought you meant X."

Documenting Edge Cases

Don't just model happy paths. Edge cases are where systems break.

Common Edge Cases to Model

// Edge case 1: Checkout with expired card
ExpiredCardCheckout = scenario "Checkout with Expired Card" {
  Customer -> Shop.WebApp "Attempts checkout with expired card"
  Shop.WebApp -> Shop.API "Submits order"
  Shop.API -> PaymentGateway "Process payment"
  PaymentGateway -> Shop.API "Payment failed: card expired"
  Shop.API -> Shop.WebApp "Returns error"
  Shop.WebApp -> Customer "Shows 'Your card has expired. Please update your payment method.'"
}

// Edge case 2: Checkout with insufficient inventory
InsufficientInventoryCheckout = scenario "Checkout with Insufficient Inventory" {
  Customer -> Shop.WebApp "Attempts checkout"
  Shop.WebApp -> Shop.API "Submits order"
  Shop.API -> Shop.Database "Checks inventory"
  Shop.Database -> Shop.API "Insufficient stock: 5 requested, 2 available"
  Shop.API -> Shop.WebApp "Returns error"
  Shop.WebApp -> Customer "Shows 'Sorry, only 2 items available. Would you like to proceed with 2?'"
}

// Edge case 3: Checkout during payment gateway outage
GatewayOutageCheckout = scenario "Checkout During Payment Outage" {
  Customer -> Shop.WebApp "Attempts checkout"
  Shop.WebApp -> Shop.API "Submits order"
  Shop.API -> PaymentGateway "Process payment"
  PaymentGateway -> Shop.API "Service unavailable: outage in progress"
  Shop.API -> Shop.WebApp "Returns error"
  Shop.WebApp -> Customer "Shows 'Payment service temporarily unavailable. Please try again in a few minutes. We've saved your cart.'"
}

Why model edge cases:

  • They expose gaps in your design
  • They become test cases automatically
  • They help teams discuss "what if" scenarios
  • They prevent surprises in production

I once launched a feature without modeling edge cases. Users immediately found scenarios we hadn't considered—checking out with gift cards during sales, checking out from different countries with different currencies, checking out with addresses that don't validate. We spent months fixing edge cases we could have caught upfront.

What to Remember

User journeys tell the story of how users interact with your system—from their perspective. When you create user journeys:

  • Focus on user's experience — Not just what works, but how it feels
  • Model both paths — Happy paths AND error paths
  • Use BDD style — "Given-When-Then" for clarity
  • Document edge cases — Unusual but important scenarios
  • Make them testable — Each scenario becomes a test case
  • Write helpful errors — Users should understand what went wrong

If you take away one thing, let it be this: user journeys are your best tool for ensuring your system actually works for real humans, not just in theory. They bridge the gap between requirements, testing, and documentation.


Check Your Understanding

Let's see if you've got this. Here are a couple of questions to test your understanding.

Question 1

You're modeling a user registration flow for a social media app. Which scenario best represents a BDD-style "Given-When-Then" structure?

A)

RegistrationFlow = scenario "User Registration" {
  Customer -> WebApp "Registers"
  WebApp -> API "Saves user"
  API -> Database "Persists"
}

B)

RegistrationFlow = scenario "As a new user, I want to register so I can use the app" {
  // GIVEN: User is on registration page
  Customer -> WebApp "Views registration form"
  
  // WHEN: User submits valid registration
  Customer -> WebApp "Enters email and password"
  Customer -> WebApp "Clicks 'Sign Up'"
  WebApp -> API "Submits registration"
  
  // THEN: Account is created and user is logged in
  API -> Database "Creates user account"
  API -> WebApp "Returns success with session token"
  WebApp -> Customer "Shows 'Welcome! Redirecting to dashboard...'"
}

C)

RegistrationFlow = scenario "Registration Process" {
  Start -> Database "Create user"
  Database -> Email "Send welcome"
}

D)

RegistrationFlow = story "User Registration Story" {
  WebApp -> API "Register"
  API -> Database "Save"
}
Click to see the answer

Answer: B) BDD-style "Given-When-Then" scenario

Let's analyze each option:

A) Incorrect. This scenario is too abstract. It shows the technical steps (registers, saves, persists) but doesn't capture the user's perspective or experience. There's no "Given-When-Then" structure—it's just a sequence of technical actions. This reads more like a data flow than a user journey.

B) Correct! This is a perfect BDD-style user journey because:

  • Title: "As a new user, I want to register so I can use the app" follows the user story format
  • GIVEN: "User is on registration page" — sets up the starting state
  • WHEN: "User submits valid registration" — describes the user action
  • THEN: "Account is created and user is logged in" — describes the expected outcome
  • User's perspective: Shows what the user sees (form, button, welcome message, redirect)
  • Complete experience: From viewing the form to being logged in

This scenario serves multiple purposes:

  • Requirements: It's unambiguous what "register" means
  • Tests: It can be turned directly into a test case
  • Documentation: Anyone reading understands the user experience
  • Communication: Product managers, developers, and testers all agree on what "register" means

C) Incorrect. This is far too abstract. "Start → Database → Email" tells you nothing about the user. What does the user do? What do they see? What happens from their perspective? This looks more like a data flow (and a poor one at that) than a user journey. It's missing the most important part—the human user.

D) Incorrect. While this uses the story keyword (which is fine), it's far too simple to be useful. "Register → Save" doesn't tell you:

  • What the user actually does (enters email? clicks button?)
  • What the system shows (success page? error message?)
  • What the experience is (how long does it take? what do they see?)
  • What happens if something goes wrong

This scenario is so vague it provides no real value. A BDD scenario should be detailed enough that anyone—developer, tester, product manager—understands exactly what happens.

Key insight: BDD-style scenarios focus on the user's experience, not just technical steps. They follow a clear "Given-When-Then" structure that makes requirements unambiguous. A good scenario should be detailed enough that it can serve as both requirements documentation and a test case.


Question 2

You're modeling a login flow and want to document an error path. Which scenario best models a meaningful error handling experience?

A)

LoginError = scenario "Login Error" {
  User -> WebApp "Login with wrong password"
  WebApp -> API "Authenticate"
  API -> Database "Check password"
  Database -> API "Password doesn't match"
  API -> WebApp "Return error"
  WebApp -> User "Shows error"}

B)

LoginError = scenario "Login Error" {
  User -> WebApp "Login with wrong password"
  WebApp -> API "Authenticate"
  API -> Database "Check password"
  Database -> API "Password doesn't match"
  API -> WebApp "Return error: AUTH_FAILED"
  WebApp -> User "Shows error: 'Authentication failed'"}

C)

LoginError = scenario "Login with Incorrect Password" {
  User -> WebApp "Enters email: user@example.com and wrong password"
  User -> WebApp "Clicks 'Log In'"
  WebApp -> API "Submits login"
  API -> Database "Verifies credentials"
  Database -> API "Password doesn't match"
  API -> WebApp "Returns error: Invalid credentials"
  WebApp -> User "Shows 'Password incorrect. Please try again or reset your password if you've forgotten it.' with link to password reset"}

D)

LoginError = scenario "Login Failed" {
  API -> Database "Check"
  Database -> API "No match"
  API -> WebApp "Error"
  WebApp -> User "Can't login"}
Click to see the answer

Answer: C) Shows user action, system response, and helpful error message

Let's analyze each option:

A) Incorrect. While this scenario shows the error occurring, it's missing crucial details:

  • It shows the user action ("Login with wrong password") but doesn't show the specific form interaction (enters email? clicks button?)
  • It shows "Return error" but doesn't specify what the error is
  • It shows "Shows error" but doesn't tell you what error message the user sees
  • It doesn't help the user understand what to do next

The error message is cryptic—"Shows error" tells the user nothing. What kind of error? What should they do? Try again? Reset password? Contact support?

B) Incorrect. This is better than option A but still has problems:

  • The error code "AUTH_FAILED" is technical and cryptic to users
  • The error message "Authentication failed" is vague and unhelpful
  • It doesn't guide the user on what to do next
  • It doesn't offer alternatives (password reset, contact support)

Users seeing this error will be confused: "Authentication failed? What does that mean? Is my email wrong? My password? My account locked? Should I try again? Reset my password?"

C) Correct! This scenario models an excellent error handling experience because:

  • User action is specific: User enters email AND wrong password AND clicks "Log In" — shows the complete action
  • System response is clear: API returns "Invalid credentials" — specific, not generic
  • User experience is helpful: Error message is "Password incorrect. Please try again or reset your password if you've forgotten it." — tells the user what's wrong and what they can do about it
  • Provides alternatives: Links to password reset if they've forgotten it
  • User's perspective: Shows what the user actually sees and experiences

This error message follows best practices:

  • Specific: Tells you exactly what's wrong (password, not email or account)
  • Actionable: Tells you what you can do (try again or reset password)
  • Helpful: Provides a link to password reset
  • Human: Not "ERR_AUTH_403" or "Authentication failed"

D) Incorrect. This scenario is a mess:

  • It starts with "API" instead of showing the user action — this is from the system's perspective, not the user's
  • "Check" and "No match" are meaningless labels — they don't tell you what's actually happening
  • "Error" is vague — what kind of error?
  • "Can't login" is the user's experience, but it doesn't help them understand why or what to do

This scenario is too abstract to be useful. It doesn't capture the user's perspective, doesn't provide helpful information, and doesn't guide next steps.

Key insight: Error paths should be just as well-designed as happy paths. When modeling errors:

  • Be specific about what went wrong (not just "error occurred")
  • Write helpful error messages (not cryptic codes)
  • Guide the user on next steps (try again? reset password? contact support?)
  • Provide alternatives when appropriate (password reset link, different payment method, etc.)
  • Think from the user's perspective (what do they see? what do they understand? what do they do next?)

A well-designed error path turns a frustrating experience into a helpful one. I once saw error messages drop support tickets by 70% just by making them more helpful and actionable.


What's Next?

Congratulations! You've completed Module 4: Flows. You now understand:

  • What flows are and how they differ from static relationships
  • How to create data flow diagrams that show lineage and transformations
  • How to model user journeys that capture the complete user experience
  • How to document both happy paths and error paths
  • How to use BDD-style scenarios for requirements and testing

You can now create diagrams that tell complete stories—stories about how data moves through your system, and stories about how users experience your system. You can model data lineage, transformations, bottlenecks, and user journeys.

In the next module, you'll learn about feedback loops—how systems regulate themselves through circular cause-and-effect relationships. You'll discover how positive feedback loops amplify change and negative feedback loops stabilize systems. You'll learn to recognize these patterns in real systems and understand their powerful effects.

See you there!


Module 4 Complete!

You've now mastered the art of modeling flows. Here's what you've learned:

Lesson 1: Understanding Flows

  • Flows show sequence and transformation, not just connections
  • Different types: data flows, user journeys, control flows, event flows
  • Use flows when order matters, when you need to see bottlenecks

Lesson 2: Data Flow Diagrams

  • Data flows show lineage: where data comes from and where it goes
  • Document transformations: how data changes shape at each step
  • Common patterns: ETL pipelines, event sourcing, real-time analytics, lambda architecture

Lesson 3: User Journeys

  • User journeys show the complete user experience from their perspective
  • Model both happy paths and error paths
  • Use BDD-style "Given-When-Then" for clarity
  • Document edge cases and unusual scenarios

You're ready to tackle more advanced concepts. Let's continue!