Lines and routing
These rules apply to component, package and state diagrams, which share the layered layout and the router. Sequence and activity diagrams have their own layouts; see Notes per diagram kind.
Draw lines orthogonally
Rule. Every connector is a chain of horizontal and vertical runs with small rounded corners (6px radius, less on very short runs). There are no diagonals.
Why. Orthogonal lines match the grid and the boxes, cross at right angles, which readers barely notice (Huang), and ranked second in UML diagram preference (Purchase et al. 2002).
Source. Purchase, Allder and Carrington 2002; Huang 2008 (via the research report);
DECISIONS.md, “Format, look and layout control”. UML gives segments no meaning (“The individual
segments of the line itself have no semantic significance”, 11.5.4, p. 201), so the style is free.
Status: enforced. Routing searches only orthogonal moves, and
glyphs::rounded draws the corners.
Keep lines out of shapes and names
Rule. A line never passes through a box, a symbol, a name, a reserved label or an interface stub. It may cross a container’s border only to reach something inside it.
Why. A line through a shape reads as a connection to that shape. A line through text makes both unreadable.
Source. Castelló, Mili and Tollis 2000 (their motivating figure shows edges through states as the failure to avoid).
Status: enforced. Each connection carries its own obstacles in Routing, and Nudging never moves a segment through one.
Never lay lines on top of each other
Rule. Two different connectors never share a run. Lines may join only where the notation joins them: the branches of a containment tree from one circle-plus, or several connectors meeting at one port.
Why. Two lines on one lane cannot be told apart, and a shared start reads as one connector that forks. That is worse than a crossing, which is still readable.
Bad: two connectors leave A from one point and share a run, so they read as one line that forks.
Source. Wybrow, Marriott and Stuckey 2009, section 5; DECISIONS.md, “Nudging and sliding ends”.
Status: enforced. Nudging ranks overlaps above crossings, and the quality test’s overlap budget is 0. Ends that slide may not come closer than 16px. The study found that an 8px gap lets two ends meet, so the gap stays. The quality test also counts merged ends, two connectors leaving one point together, which the overlap count excuses; its budget is the two intended fan-outs from one port, and box growth is never kept if it merges two connectors.
Avoid crossings
Rule. Lines should not cross. When a crossing cannot be avoided, it is a clean right angle away from any corner, label or arrowhead.
Why. Crossings have the largest effect of any aesthetic on reading graphs (Purchase 1997), and 93% of subjects preferred the UML diagram with fewer (Purchase et al. 2002).
Bad: the lines to C and D cross, although swapping the order of their ends would separate them.
Source. Purchase 1997; Purchase, Allder and Carrington 2002.
Status: partial. Ordering (Crossing reduction), the router’s crossing cost and nudging remove most crossings. Three remain in the 103 golden files, all in component diagrams. Two (Order Platform) need ordering across containers, which the recursive layout cannot see. One (Hexagonal service) comes from attachment points ordered by the position of the other end rather than by the direction the line arrives from. Box growth removed the two Compiler pipeline crossings. See the five crossings.
Bend only when you must
Rule. A line turns only when its ends are not aligned and cannot be. A Z between two ranks is acceptable when the ends cannot share a coordinate; a jog that a slightly different layout would remove is a defect.
Why. Bends are second only to crossings in the preference data (91%), and a continuous path is traced faster (Ware et al. 2002).
Source. Purchase et al. 2002; Ware, Purchase, Colpoys and McGill 2002.
Status: partial. Straight long edges (Placement), slots that slide toward their peers (Slots) and jog folding (Nudging) keep most lines straight, and Box growth, the 8px corner inset and ends on package tabs remove 100 more. Since stage 10, a Z becomes an L where the L is clear, and state labels may follow their lines. 193 bends remain in the golden files, down from 341. About a quarter are necessary, at fans. The rest have causes that Layout levers lists one by one: labels, port policy, state names, layout order, and boxes that would need more than two grid steps.
Leave from the facing side
Rule. A connector leaves its box on the side that faces the other end. When the boxes are apart on both axes, the side along the flow wins.
Why. The line then takes the short way, and the flow direction stays visible at both ends.
Source. Purchase et al. 2002 (information flow ranked third for UML).
Status: enforced, by router::facing, used by every adapter.
Prefer an L to a Z when the L is clear
Rule. When two boxes are apart on both axes and an L-shaped line between a side and a top (or bottom) is clear of everything, use the L, with one bend, instead of a Z with two.
Why. It saves a bend without hiding the flow, as long as the last run is long enough for its arrowhead.
Source. Purchase et al. 2002 (bends).
Status: enforced for plain boxes. State diagrams do this for final states, choices, junctions and terminate symbols when choosing sides (side arrivals). After routing, L over Z moves one end of a Z to the side of its box that faces the line, when the L then meets no box, name, reserved label or boundary, keeps 16px from other ends, gains no crossing, overlap or close run, and merges no ends. It applies to leaf components without ports or stubs, leaf packages and classes (not a package’s top, which holds its tab), and simple states (not the sides of a state with internal behaviours, which its compartment line meets). In the golden files it removed 41 bends.
Spread and order attachment points
Rule. Ends on one side are spread at least 24px apart, in the order that keeps their lines from crossing, and each slides toward the line that makes its connector straight.
Why. Evenly spaced ends read as separate connectors. Their order decides whether lines cross just outside the box.
Source. Schulze, Spönemann and von Hanxleden 2014 (port constraints in layered layout, via the research report).
Status: partial. Slots spreads and orders ends by the position of the other end. Ordering by the side the route arrives from is not done, and this causes three of the five remaining crossings.
Keep ends off corners
Rule. An end stays at least 8px from a square box corner, and 16px from the curve of a rounded state corner. Ends are first spread 16px from corners; nudging may slide them to 8px on square boxes.
Why. An arrowhead at a corner is ambiguous about which side it meets, and on a rounded corner it meets a curve.
Bad: the arrowhead meets B in its rounded corner.
Source. Drafting practice; Layout levers (images of an 8px inset).
Status: enforced: 8px on square component and package boxes (CORNER_INSET in their routing),
16px on states. The 8px inset saved 12 bends and merges nothing. At 8px on states, whose corners have a
10px radius, the arrowhead would land on the curve. The 16px gap between ends stays.
Keep arrowheads apart
Rule. Two ends on one side are at least 16px apart after nudging, and 24px when slots are first spread.
Why. Two arrowheads closer than their own width merge into one mark.
Source. DECISIONS.md, “Nudging and sliding ends” (SLIDE_GAP).
Status: enforced, by router::SLIDE_GAP and the slot spacing in each adapter.
End with a straight run
Rule. Every line leaves and enters a box with a straight run of at least 16px, so the arrowhead sits on a straight piece and points squarely at the side.
Why. An arrowhead on a corner points in two directions at once.
Source. Drafting practice.
Status: enforced, by the 16px stems of router::Terminal and, at state pseudostates, the 16px
ARRIVAL_RUN.
Space parallel lines evenly
Rule. Lines that share a channel are spread evenly across it, at least 8px apart, and a lone jog sits in the middle of its gap.
Why. Wybrow et al. report that this nudging step has “a significant impact on the perception of layout quality”. Even spacing looks deliberate. Lines bunched on one side look accidental.
Source. Wybrow, Marriott and Stuckey 2009, section 5.
Status: enforced. The spread pass of Nudging does this.
Do not run along a boundary
Rule. A line never travels along a container’s border. It crosses the border at a right angle.
Why. A line along a border merges with it, and reads as part of the container.
Source. Drafting practice.
Status: enforced. Container borders are soft strips in Routing, and Nudging never moves a segment onto one.
Join lines only where the notation does
Rule. Containment drawn with branching lines has one trunk from the owner’s circle-plus. Other relations each get their own line, even when they share an end.
Why. A shared trunk says “these belong together”, which is true for containment (UML 12.2.4) and false for, say, two dependencies.
Source. UML 2.5.1, 12.2.4, p. 248; Purchase et al. 2002 (76% preferred joined inheritance arcs, where the notation joins them).
Status: enforced. Package containment draws a tree when every member sits in the next rank, and otherwise routes each branch from the shared anchor.
Put arrowheads where the notation says
Rule. Arrowheads, triangles, circles and sockets follow UML 2.5.1 for each relation: an open head for a dependency, a hollow triangle for generalization, a filled head for a synchronous message, the circle-plus on the owner of a containment.
Why. The notation is the meaning, so it is never traded for looks. See When rules conflict.
Source. UML 2.5.1; docs/research/uml-audit.md in the repository.
Status: enforced, by the notation audit of stage 6a and the glyphs in core::glyphs.