UML Sequence Diagrams

runtime message flow, illustrated with TextFinder

1.  What a Sequence Diagram Shows

A sequence diagram shows which participants exchange messages, in what order, and what each message carries or returns. Time flows downward; participants appear as vertical lifelines. An arrow crossing from one lifeline to another is a message - a call, a signal, or a return value. Sequence diagrams answer questions that structural diagrams cannot. A package diagram shows that EntryPoint depends on DirNav; a sequence diagram shows that EntryPoint calls search() once, after which DirNav drives all subsequent interaction by firing callbacks. That distinction - who calls whom, and who drives the loop - is invisible in any structural view. One sequence diagram captures one scenario: a single path through the system for a specific set of inputs. It does not try to show all possible paths simultaneously. That constraint is a feature, not a limitation - it forces you to think clearly about one scenario at a time, and to name it explicitly.

2.  Notation

The core elements are participants, lifelines, messages, and activation boxes. Combined fragments - loop, alt, opt, par - extend the diagram to express repetition, branching, and concurrency.
ElementMeaning
Participant box A named actor, object, or component. Appears at the top; its lifeline extends downward as a dashed vertical line.
Solid arrow ->> A synchronous message. The sender blocks until the callee returns. The arrowhead points to the receiver.
Dashed arrow -->> A return message. Carries the response back to the caller. Often labeled with the value or variable name returned.
Async arrow -)+ or -) An asynchronous message. The sender does not block - it continues immediately after firing the message.
Activation box A narrow rectangle on a lifeline marking the period during which that participant is actively executing. Drawn with + / - suffixes on message arrows in Mermaid.
loop [condition] A combined fragment enclosing messages that repeat. The condition describes the iteration - "each file", "until queue empty".
alt [condition] A combined fragment with two or more branches separated by else. Exactly one branch executes per occurrence.
opt [condition] A combined fragment whose body executes zero or one times, depending on the condition.
par A combined fragment whose sections execute concurrently. Used when threads, tasks, or coroutines run in parallel.
Note over A, B An annotation spanning one or more lifelines. Adds explanatory text without adding a message.

3.  Notation in Practice

The diagram below illustrates the core elements in isolation before applying them to a real example.
sequenceDiagram
    participant Caller
    participant Callee
    participant Worker

    Caller->>+Callee: synchronous call (Caller blocks)
    Callee-->>-Caller: return value

    Caller-)Worker: async message (Caller does not block)
    Note over Worker: executes independently

    loop each item in collection
        Caller->>Callee: process(item)
        Callee-->>Caller: result
    end

    alt condition is true
        Caller->>Callee: path A
    else condition is false
        Caller->>Callee: path B
    end
      

Figure 1. Core notation.

Synchronous call with activation box, asynchronous fire-and-forget, loop fragment, and alt fragment with two branches.

4.  TextFinder as a Worked Example

TextFinder's runtime flow divides cleanly into two phases. In the first phase EntryPoint parses options and configures the other components. In the second phase EntryPoint calls search() and hands control to DirNav for the rest of the run. DirNav drives every subsequent message; EntryPoint becomes a passive callback host. In the Rust implementation TfAppl - the type that implements the DirEvent trait - acts as the callback receiver. DirNav calls do_dir and do_file through the trait object; TfAppl.do_file delegates the regex match to TextFinder.
sequenceDiagram
    participant EP as EntryPoint
    participant CL as CommandLine
    participant DN as DirNav
    participant TF as TfAppl
    participant OUT as TextFinder / Output

    EP->>+CL: parse(argv)
    CL-->>-EP: options (path, regex, ext, flags)

    EP->>OUT: set_regex(pattern)
    EP->>DN: add_pattern(ext)

    EP->>+DN: search(root_path, &mut TfAppl)

    loop each directory in tree
        DN->>TF: do_dir(dir_name)

        loop each file in directory
            DN->>TF: do_file(file_name)
            TF->>+OUT: find(fully_qualified_path)
            OUT-->>-TF: matched: bool

            alt matched == true
                TF->>TF: print path to console
            end
        end
    end

    DN-->>-EP: search complete
      

Figure 2. TextFinder runtime sequence.

After search() is called, DirNav drives the entire traversal loop. EntryPoint never sends another message - it waits for DirNav to return.

5.  Reading the Diagram

Three observations stand out when reading Figure 2 as a design document. Control inversion after search(). The activation box on DN's lifeline spans the entire traversal. EntryPoint is active for the initial configuration phase, then suspended while DirNav runs. This is the inversion of control that the callback design produces: the library (DirNav) drives the application (TfAppl), not the other way around. TfAppl is the integration point. All callback messages land on TF. It receives directory and file notifications from DN, delegates the match decision to OUT, and writes to the console. Nothing in DN knows about OUT, and nothing in OUT knows about DN. TfAppl exists precisely to connect them without creating a direct dependency. CmdLine is consulted only once. The activation on CL's lifeline is short and early. After parse() returns, CL is not called again. This tells a reader that CL is stateless after initialization - its data is consumed at startup and does not need to persist across the traversal.

6.  Combined Fragments in Depth

The two nested loop fragments in Figure 2 express that directory traversal is a nested iteration: for each directory, DirNav visits each file. The alt fragment inside the file loop expresses that output is conditional - it fires only when the regex matches. These three fragments together describe the complete runtime behavior of the traversal in a form that is more precise than prose and more readable than code. A common mistake is overloading a single sequence diagram with too many fragments. When a diagram has more than two levels of nesting, consider splitting it: one diagram for the startup phase, a second for the traversal loop with callbacks. Label each diagram with a scenario name so a reader knows which path it covers. The par fragment appears when the design uses threads or async tasks. TextFinder is single-threaded, so it does not appear here. A multi-threaded variant that scanned directories concurrently would add a par wrapping the per-directory loop body, with one branch per worker thread.

7.  What Sequence Diagrams Reveal

Structural diagrams show dependencies; sequence diagrams show consequences. The same package diagram is consistent with designs that differ significantly at runtime. A sequence diagram forces those differences into the open.
QuestionOnly visible in a sequence diagram
Who calls whom first? The topmost message establishes the initiator. In TextFinder it is always EntryPoint.
Who drives the main loop? The participant with the longest activation box drives. Here it is DirNav, not EntryPoint - a consequence of the callback design.
Is the call synchronous or async? Solid vs. open arrowhead. A synchronous call blocks the caller's lifeline; an async message does not.
What is returned, and when? Dashed return arrows carry the value and show the moment control transfers back. Structural diagrams only show that a dependency exists.
Where does branching occur? alt fragments make conditional paths explicit with their guard conditions labeled.

8.  When to Draw One

Draw a sequence diagram when the question is about runtime interaction between components, not about type structure or compilation boundaries. Specific triggers: Keep each diagram focused on one scenario. A sequence diagram that tries to show every possible path becomes a flowchart, which is better expressed as an activity diagram. When branching dominates, reach for that diagram type instead.

9.  References

ResourceDescription
Project Story: TextFinder Architecture, CLI, performance, and code metrics for all five implementations.
UML Package Diagrams Structural view showing the same TextFinder architecture at the compilation-boundary level.
UML Class Diagrams Structural view showing the types, members, and relationships that the sequence diagram orchestrates.
Mermaid sequence diagram syntax Full syntax reference including autonumber, actor aliases, background highlighting, and all fragment types.
UML Sequence Diagrams Overview Formal UML notation reference covering all message types, combined fragments, and interaction uses.