Skip to main content

UML Diagrams

UML (Unified Modeling Language) is a standardized visual language for describing the structure and behavior of a software system. A UML diagram helps you visualize and document complex systems so that design intent survives beyond one person's head — useful for onboarding, code review, and just remembering why you built something a certain way.

Why bother with UML instead of just drawing boxes?

UML gives every shape, line, and arrowhead a precise, agreed-upon meaning. A dashed arrow doesn't mean the same thing as a solid one with a diamond — and once you know the notation, you can read anyone's diagram without a legend.

The Two Families of UML Diagrams​

UML's 14 official diagram types fall into two buckets:

CategoryFocuses onExamples
StructuralThe system's static architecture — what things are and how they relateClass, Object, Component, Deployment, Package
BehavioralHow components interact over time — what things doSequence, Use Case, Activity, State Machine

The most commonly used diagrams in day-to-day engineering work are:

  • Class diagram (structural) — the blueprint of your object model. Covered in depth below.
  • Sequence diagram (behavioral) — shows objects exchanging messages in chronological order (great for API call flows).
  • Use case diagram (behavioral) — shows actors and the actions/goals they can perform against a system.
  • Activity diagram (behavioral) — a flowchart-like view of a process, with actions, decisions, and parallel flows.

Class diagrams are generally considered the most popular/common UML diagram because they map so directly onto object-oriented code (Java, C#, TypeScript classes, etc.) and double as living documentation for a codebase.

A General Process for Creating Any UML Diagram​

  1. Define the purpose. What question should this diagram answer? "What are the entities and their relationships?" vs. "What happens, step by step, when a user checks out?"
  2. Identify the key elements. Sketch the main components (classes, actors, objects) roughly before finalizing them.
  3. Establish relationships. Map out how the elements connect — this is usually where the real design discussion happens.
  4. Refine and review. Revisit with teammates; diagrams are cheap to redraw and expensive to get silently wrong.
  5. Pick the right tool so the diagram stays version-controlled and easy to update (see Tooling below).

Class Diagrams — Syntax & Conventions​

A class diagram describes a system's classes, their attributes, operations (methods), and the relationships between them.

Anatomy of a Class Box​

A class is drawn as a rectangle split into three compartments:

┌─────────────────────────────┐
│ ClassName │ ← Class name (italics if abstract)
├─────────────────────────────┤
│ - attribute : Type │ ← Attributes (fields)
│ + otherAttribute : Type │
├─────────────────────────────┤
│ + operation(param: Type): T │ ← Operations (methods)
│ - helperMethod(): void │
└─────────────────────────────┘

Visibility notation (prefix before each member):

SymbolMeaning
+public
-private
#protected
~package/internal

Formatting conventions:

  • underlined member → static (class-level, not instance-level)
  • italicized class name or method → abstract
  • Constants are written in ALL_CAPS by convention
  • Method signatures follow name(paramName: ParamType): ReturnType
  • Special stereotypes use guillemets: «interface», «abstract», «enumeration»

Relationships Cheat Sheet​

This is the part people forget and re-Google every time — the arrows are not interchangeable.

RelationshipMeaningLine Style
Association"uses / has a reference to" — a general structural linkSolid line, no arrowhead (or open arrowhead if directional)
Aggregation"has a" (whole-part, but the part can exist independently)Solid line, hollow/open diamond at the whole end
Composition"owns a" (whole-part, part's lifecycle depends on the whole)Solid line, filled diamond at the whole end
Inheritance (Generalization)"is a" — subclass extends superclassSolid line, hollow triangle arrowhead pointing to parent
Realization (Implementation)Class implements an interface's contractDashed line, hollow triangle arrowhead
Dependency"depends on temporarily" (e.g., a method parameter or local variable)Dashed line, open arrowhead

Multiplicity (cardinality) is written near each end of a relationship line:

NotationMeaning
1exactly one
0..1zero or one
* or 0..*zero or more
1..*one or more
n..ma specific range

A memory trick: aggregation = shared/loose ("has-a"), composition = exclusive/tight ("owns-a", dies with parent). Think "a Team has Players" (aggregation — a player can exist without the team) vs. "a House is composed of Rooms" (composition — a room doesn't exist without the house).

Example: Mermaid Syntax (renders natively in Docusaurus)​

Docusaurus supports Mermaid diagrams out of the box once you enable the @docusaurus/theme-mermaid plugin (add markdown: { mermaid: true } and the theme to docusaurus.config.js). This means the diagram source lives right in your Markdown and renders as an actual diagram — no external image files to keep in sync.

```mermaid
classDiagram
class Animal {
<<abstract>>
#string name
#int age
+Animal(name: string)
+makeSound()* void
+getName() string
}

class Dog {
-string breed
+makeSound() void
+fetch() void
}

class Cat {
+makeSound() void
}

class Owner {
-string name
+feed(a: Animal) void
}

class Vet {
<<interface>>
+checkup(a: Animal) void
}

class VetClinic {
+checkup(a: Animal) void
}

Animal <|-- Dog : inherits
Animal <|-- Cat : inherits
Owner "1" o-- "0..*" Animal : owns
VetClinic ..|> Vet : implements
Owner ..> Vet : depends on
```

This renders as:

Mermaid relationship arrow syntax reference:

Mermaid SyntaxRelationship
`A --> B`
`A ..> B`
A --* BComposition (A is composed of B)
A --o BAggregation (A aggregates B)
A --> BAssociation (directed)
A -- BAssociation (undirected)
A ..> BDependency (A depends on B)
A .. BLink (dashed, no semantics)

Example: PlantUML Syntax (common in Confluence/Jira via plugins)​

PlantUML is the other dominant text-to-diagram syntax, frequently seen embedded in Confluence pages via macros (e.g. the PlantUML for Confluence marketplace app). It's slightly more terse:

@startuml
abstract class Animal {
# name : string
# age : int
+ Animal(name: string)
+ {abstract} makeSound() : void
+ getName() : string
}

class Dog {
- breed : string
+ makeSound() : void
+ fetch() : void
}

class Cat {
+ makeSound() : void
}

interface Vet {
+ checkup(a: Animal) : void
}

class VetClinic {
+ checkup(a: Animal) : void
}

class Owner {
- name : string
+ feed(a: Animal) : void
}

Animal <|-- Dog
Animal <|-- Cat
Vet <|.. VetClinic
Owner "1" o-- "0..*" Animal
Owner ..> Vet

@enduml

The relationship symbols are nearly identical in spirit to Mermaid's (<|-- inheritance, <|.. realization, o-- aggregation, *-- composition, --> association, ..> dependency) — the two ecosystems converged on very similar ASCII-art-for-arrows conventions since both are modeling the same UML spec.


Other Diagram Types Worth Knowing (Quick Reference)​

DiagramTypeUse it to show...
SequenceBehavioralThe order messages are passed between objects/services over time (e.g., an API request lifecycle)
Use CaseBehavioralActors and the goals/actions they can perform against the system
ActivityBehavioralA workflow or process, with decision branches and parallel paths (flowchart-like)
State MachineBehavioralThe states an object can be in and the events that trigger transitions
ObjectStructuralA snapshot of actual object instances and their links at a point in time
ComponentStructuralHigh-level software components and the interfaces they expose/consume
DeploymentStructuralPhysical/infrastructure nodes (servers, devices) and what's deployed on them
PackageStructuralHow classes/namespaces are grouped into packages/modules

Common Mistakes to Avoid​

  • Mixing up aggregation and composition — if you're not sure whether the "part" can outlive the "whole," default to plain association; don't guess at a diamond.
  • Overloading one diagram with too much detail. A class diagram meant to explain a domain model to a new teammate doesn't need every private helper method — trim to what serves the diagram's stated purpose.
  • Letting the diagram drift from the code. Diagrams-as-code (Mermaid/PlantUML checked into the repo) solve this better than diagrams drawn once in a GUI tool and never updated.
  • Skipping multiplicity. Order to LineItem being "one-to-many" vs. "one-to-one" is often the single most important fact the diagram is supposed to convey — don't leave it off.

Tooling for Docs-as-Code​

Since this page lives in a Docusaurus site, the two most practical options for keeping diagrams versioned alongside the docs are:

  • Mermaid — via @docusaurus/theme-mermaid; syntax lives directly in fenced ```mermaid code blocks, no build step or external service needed.
  • PlantUML — needs a rendering server (self-hosted or plantuml.com) or a build-time plugin (e.g. docusaurus-plugin-plantuml), but has a larger feature set and is the de facto standard for UML inside Confluence/Jira ecosystems, so it's worth knowing if you collaborate with teams using those tools.

Other general-purpose diagram tools that support UML notation (useful outside of docs-as-code, e.g. quick whiteboarding): draw.io / diagrams.net, Lucidchart, and Visual Paradigm.

Further Reading​