If you've ever needed to explain how a system works to another developer, a PlantUML sequence diagram is one of the fastest ways to do it. Instead of drawing boxes and arrows by hand, you write a few lines of text and get a clear, professional-looking diagram. The code examples below show you exactly how to build these diagrams from scratch, whether you're mapping a simple login flow or a complex microservices interaction.

What Exactly Is a PlantUML Sequence Diagram?

A PlantUML sequence diagram is a text-based diagram that shows how different participants (objects, users, services) communicate over time. You write simple, readable code, and PlantUML renders it into an image. The diagram reads left to right and top to bottom, showing messages flowing between participants in the order they happen.

PlantUML is an open-source tool that takes plain text descriptions and turns them into UML diagrams. Sequence diagrams are one of the most popular diagram types because they make it easy to visualize workflows, API calls, authentication flows, and system interactions. You can learn more about the full syntax details in our UML sequence diagram syntax reference.

How Do You Write a Basic PlantUML Sequence Diagram?

Every PlantUML sequence diagram starts with @startuml and ends with @enduml. Between those tags, you define participants and the messages they send to each other. Here's the simplest possible example:

@startuml

Alice -> Bob: Hello Bob

Bob --> Alice: Hi Alice

@enduml

This creates two participants, Alice and Bob, with a solid arrow from Alice to Bob and a dashed return arrow back. The -> operator draws a solid arrow, while --> draws a dashed one (typically used for responses).

You can also declare participants explicitly to control their order and give them aliases:

@startuml

actor User

participant "API Gateway" as GW

participant "Auth Service" as Auth

database "User DB" as DB

User -> GW: POST /login

GW -> Auth: validateCredentials()

Auth -> DB: SELECT user WHERE email=?

DB --> Auth: user record

Auth --> GW: JWT token

GW --> User: 200 OK + token

@enduml

This example uses four different participant types: actor (stick figure), participant (rectangle), and database (cylinder). Choosing the right shape helps readers quickly identify what kind of participant they're looking at.

What Are Some Common PlantUML Sequence Diagram Code Examples?

Simple Request-Response Between Two Services

@startuml

participant Client

participant Server

Client -> Server: HTTP GET /users

Server --> Client: 200 OK (JSON)

@enduml

This is the most basic interaction pattern. It's useful when documenting API endpoints or explaining a single service call. Keep it this simple when you only need to show one request and one response.

Adding Notes and Activation Bars

@startuml

participant "Web App" as App

participant "Payment Service" as Pay

participant "Stripe API" as Stripe

App -> Pay: processPayment(orderId)

activate Pay

Pay -> Stripe: POST /v1/charges

activate Stripe

Stripe --> Pay: charge.id

deactivate Stripe

note right of Pay: Store charge ID in order record

Pay --> App: success(orderId)

deactivate Pay

@enduml

The activate/deactivate keywords draw vertical bars showing when a participant is actively processing. Notes attach extra context directly to a participant. These two features make diagrams much easier to read.

Conditional Logic with alt and opt Blocks

@startuml

participant User

participant "Auth Service" as Auth

User -> Auth: login(email, password)

alt credentials are valid

  Auth --> User: 200 OK (token)

else credentials are invalid

  Auth --> User: 401 Unauthorized

end

@enduml

The alt block shows branching logic. If you only have one optional path (no "else" case), use opt instead. For multiple conditions, you can chain them with else if inside the alt block.

Loops and Parallel Execution

@startuml

participant Scheduler

participant "Worker A" as WA

participant "Worker B" as WB

Scheduler -> WA: startBatch()

Scheduler -> WB: startBatch()

par parallel processing

  WA -> WA: process(items[0..499])

  WB -> WB: process(items[500..999])

end

WA --> Scheduler: done

WB --> Scheduler: done

@enduml

The par block shows concurrent operations. Use loop to represent repeated actions, like retrying a request: loop 3 retries.

Grouping Messages with Boxes and Dividers

@startuml

box "Frontend" #LightBlue

  participant Browser

end box

box "Backend" #LightGreen

  participant API

  participant Cache

end box

Browser -> API: GET /products

API -> Cache: get("products")

Cache --> API: null (cache miss)

API -> API: queryDatabase()

API --> Browser: product list

== Cache Refresh ==

API -> Cache: set("products", data, TTL=300)

@enduml

Boxes visually group related participants. The double-equals syntax (==) creates a horizontal divider to separate phases of an interaction.

How Do You Handle Self-Calls and Nested Messages?

When a participant calls itself (like an internal method invocation), you send a message to the same participant name:

@startuml

participant Controller

Controller -> Controller: validate(request)

@enduml

For nested calls, activate the participant again to create stacked activation bars:

@startuml

participant OrderService

participant InventoryService

OrderService -> InventoryService: checkStock(productId)

activate InventoryService

InventoryService -> InventoryService: findProduct(productId)

activate InventoryService

InventoryService --> InventoryService: product

deactivate InventoryService

InventoryService --> OrderService: stockLevel

deactivate InventoryService

@enduml

This shows two levels of activation, making it clear that findProduct runs inside checkStock.

What Are the Most Common Mistakes When Writing PlantUML Sequence Diagrams?

Forgetting @startuml or @enduml. PlantUML won't render without these bookend tags. Double-check that both are present before debugging anything else.

Mixing up arrow types. A solid arrow (->) means a synchronous call or a message. A dashed arrow (-->) usually represents a response. Using the wrong one confuses readers about what's a request vs. a reply.

Using participant names with spaces without aliases. If a participant name has a space, wrap it in quotes and give it an alias. Writing API Gateway -> Auth Service: verify() without quotes or aliases will cause rendering errors. The correct way is: participant "API Gateway" as GW.

Overloading a single diagram. Trying to show an entire system in one sequence diagram makes it unreadable. Break it into multiple smaller diagrams, each covering one specific flow. For comparing how different scripting languages handle sequence diagrams, see our scripting language comparison for sequence diagrams.

Skipping activation blocks. Without activate/deactivate, readers can't tell when a participant is actively doing something. Even simple diagrams become clearer with these markers.

How Can You Render and Share PlantUML Sequence Diagrams?

You have several options for turning your PlantUML code into images:

  • Online editors The official PlantUML server lets you paste code and get a diagram instantly. You can also try our online sequence diagram generator for a quick workflow.
  • IDE plugins VS Code, IntelliJ, and other editors have PlantUML plugins that show live previews as you type.
  • Command line Download the PlantUML JAR file and generate PNG, SVG, or PDF output from the terminal.
  • CI/CD integration Add PlantUML generation to your build pipeline so diagrams always stay in sync with your code.

What Tips Help You Write Better PlantUML Sequence Diagrams?

  • Use aliases for long names. Instead of typing "PaymentProcessingService" every time, define participant "PaymentProcessingService" as PPS and use PPS throughout.
  • Keep participants under 7 per diagram. More than that and the diagram becomes hard to scan. Split into linked diagrams instead.
  • Use colors to distinguish systems. Add color with syntax like participant API #LightBlue or wrap participants in colored boxes.
  • Add a title. Start your diagram with title User Login Flow v2 so anyone viewing the image knows what it covers.
  • Version your diagrams. Store the .puml source files in your repo alongside your code. Diagrams that live outside version control will drift out of date.
  • Use footnotes for assumptions. footnote right of User: Assumes MFA is disabled adds context without cluttering the message flow.

Practical Checklist for Your Next PlantUML Sequence Diagram

  1. Identify the specific flow you want to document (one flow per diagram).
  2. List all participants and choose the right shapes (actor, participant, database, collections, control, entity).
  3. Write the message exchanges in chronological order from top to bottom.
  4. Add activate/deactivate blocks to show processing time for each participant.
  5. Include alt, opt, loop, or par blocks for branching, optional, repeated, or concurrent logic.
  6. Use notes and colors to add context without overcrowding.
  7. Add a title and version number at the top of the diagram.
  8. Render the diagram and check that it reads clearly at the size it will be displayed.
  9. Commit the .puml source file to version control.
  10. Share the diagram link or exported image with your team.

Start by picking one active feature or bug ticket you're working on right now, and write a sequence diagram for the main interaction. It takes about five minutes, and you'll immediately see whether the flow makes sense or has gaps worth fixing.