I use UML a lot in my practice. Not the UML, but a UML – eclectic and variable notation, sketched on whiteboards and cards, and scrubbed out immediately. But recently I’ve actually had to make a UML model to go in a document, so I thought I would share a few lessons I’ve learned about UML over the years…
- It’s okay to use UML to document code, as a navigational aid or to give a 30,000-feet summary for folks who can’t read the code.
- But only use it to describe stable aspects of the code. Don’t try to hit a moving target, or to describe code that doesn’t exist.
- Divide the model into pieces, each of which tells a single story. It can be very tempting to try to get everything onto one huge static model, but when the aim is to communicate smaller focused models work better.
- Prefer dynamic models, particularly collaboration diagrams and state transition models. Where multiple threads or processes interact, use activity models (with swim-lanes) to show timing and synchronisation. Static models (particularly class diagrams) often give the (false) impression of “structure” – whereas a running system is all about behaviour. So focus the models on the system’s behaviour.
- I use only two kinds of relationship on a class diagram: «implements» and «uses». No containment or aggregation (even when describing C++ code), no bi-directional relationships, no roles or multiplicities, and no navigability.
- Follow each static model with a textual traversal of the diagram: Write a single paragraph for each class, consisting of a 1-sentence description of the class’ role in the model (not the system), followed by a 1-sentence description of the job done by each of the dependencies leaving that class. The result will be a simplified narrative that walks through the system’s static structure in a dynamic way.
- When deciding what to include on a class diagram, begin with the classes that are essential to telling the story of this particular part of the system. Then add in the “hard” structure, ie. the «inherits» relationships and those «uses» relationships that are really «creates». Then try to remove classes until the model is as simple as possible; only add further dependencies if they carry delegation that’s essential to telling the story.
- Don’t be afraid to mix diagram types. I’m particularly prone to putting messages on my class diagrams, for example to show system start-up or incoming asynchronous stimuli.
- Lay out each model, whether static or dynamic, so that it reads naturally from left to right and from top to bottom. This will usually mean that «inherits» relationships point up the page (abstract above concrete) and «uses» relationships tend to point to the right. In fact I find that left-pointing dependencies usually only arise to represent the “call-backs” in some variant of the Observer pattern.
- Try to arrange for one of the models to describe the entire system using 4-6 classes.
These aren’t “rules” as such, and I don’t consciously use them as a check-list or style guide. But when I’ve made a model I’m happy with, I find it always has all of the above properties. Does a similar style work for you? What, if anything, do you do differently?
I’ve now posted a worked example of this style of UML modelling.