umldesignarchitecture

UML in Practice: Designing Systems Before Writing a Single Line of Code

UML 1.0 was just published. We adopted it immediately. Here is what actually helped and what turned into ceremony.

·4 min read

UML in Practice: Designing Systems Before Writing a Single Line of Code

UML 1.0 was published in January 1997. By April we had Rational Rose installed and were drawing diagrams. The pitch was compelling: a standard visual language for software design, replacing the ad-hoc boxes-and-arrows each team had been using. Three years later I had a clearer view of what UML was good for and what it was not.

At Motorola we used UML to design the network management system before writing code. Here is what that looked like in practice.

Why Model Before Coding

The argument for upfront modelling is that changing a diagram is cheap, changing running code is expensive. With a team of eight working on interlocking components, it also forces the design conversation to happen explicitly rather than in each developer's head separately.

The conversations around the diagrams were often more valuable than the diagrams themselves.

Class Diagrams: Where We Spent Most Time

A class diagram shows types, their attributes, and the relationships between them. For our NMS core:

┌──────────────────────┐          ┌──────────────────────┐
│   NetworkDevice      │          │   DeviceStatus       │
├──────────────────────┤          ├──────────────────────┤
│ - ip: String         │ 1    1   │ - uptime: long       │
│ - name: String       │◆────────▶│ - interfaces: int    │
│ - type: DeviceType   │          │ - lastPolled: Date   │
├──────────────────────┤          └──────────────────────┘
│ + poll(): void       │
│ + reset(): void      │
└──────────┬───────────┘
           △
    ┌──────┴──────┐
    │             │
┌───┴──┐    ┌────┴───┐
│Router│    │ Switch │
└──────┘    └────────┘

The diamond is composition — a NetworkDevice owns its DeviceStatus. The triangle is inheritance — Router and Switch extend NetworkDevice.

This diagram gave every team member the same mental model of the domain. When someone said "device" in a meeting we all knew exactly what that meant.

Sequence Diagrams: Capturing Interactions

Where class diagrams show structure, sequence diagrams show behaviour over time. We drew a sequence diagram for the polling cycle:

Scheduler    PollerThread    DeviceRegistry    SnmpClient    NetworkDevice
    |               |               |               |               |
    |─pollTick()───▶|               |               |               |
    |               |─getDevices()─▶|               |               |
    |               |◀──[list]──────|               |               |
    |               |               |               |               |
    |               |─────────────────────get()────▶|               |
    |               |               |               |────GET────────▶|
    |               |               |               |◀───response────|
    |               |◀──────────────────────────────|               |
    |               |─update(ip,st)▶|               |               |

This was invaluable for finding race conditions before they were written. Reviewing a sequence diagram, someone spotted that getDevices() and update() could run concurrently without a lock. We added synchronisation to the design, not the code.

Component and Deployment Diagrams

For the deployment architecture we used component and deployment diagrams. Component diagrams showed the major subsystems and their interfaces. Deployment diagrams showed which processes ran on which hosts.

These were the diagrams most useful for operations and the ones developers looked at least. They served their purpose once, at project start, and were rarely updated.

What Did Not Work

Keeping diagrams in sync with code. After the initial design phase, diagrams diverged from reality within weeks. Rational Rose could reverse-engineer Java code into class diagrams but the result was noisy and rarely matched the initial design intent.

Over-specifying behaviour. We spent a week drawing activity diagrams for the trap processing pipeline. The diagrams were accurate but no one ever looked at them again. The code was clearer.

Designing to the diagram. Junior developers occasionally wrote code to match a diagram rather than to solve the problem — adding classes the diagram implied but the implementation did not need.

The Pattern That Worked

Use class diagrams early to align the team on the domain model. Use sequence diagrams to spot concurrency and ordering problems before they are in code. Once development begins, treat diagrams as communication tools, not living documentation. Stop maintaining them when the cost exceeds the value.

The discipline UML imposes — thinking about types, relationships and interactions before writing — is genuinely valuable. The tooling of the era was not.