Unified Modeling Language is about communication. But in order for communication to work, it must be useful. How do you make sure that you don't sweat over a set of UML diagrams only to discover that no one else can understand them? Fortunately, there are guidelines, discussed in this article, to help prevent this catastrophe. This article is excerpted from chapter three of the book UML Applied: A .NET Perspective, written by Martin L. Shoemaker (Apress, 2004; ISBN: 1590590872).
This is a corollary of the previous guideline and of The Model Rule: you draw those diagrams that answer questions or explain points. Every diagram should have a purpose that you want it to communicate to your audience. For each diagram, you should be able to convert the diagram to a PowerPoint slide, project the slide on a screen, and explain the story of that slide within the recommended 2 minutes per slide. The audience should be able to grasp the point of the story with minimal reference to other material. Although some amount of knowledge of the model behind the diagram will be necessary, each diagram should be complete enough to tell its story on its own. Add notes where necessary to fill in gaps in the explanation.
Conversely, examine the detail on each diagram. If a particular detail doesn’t add to the story, consider removing it; and if some detail detracts from the story, definitely hide it within the current diagram.
Some stories are too large to tell in 2 minutes with one diagram. This is another example of how multiple diagrams at different levels of scope can make your communication more clear. Tell the broad outline of the story in one diagram; then bring the scope down to tell individual stories where they make the design more clear.
The Highway Map Rule
As the number of diagrams and models grows, some people will worry: “How do we keep this up to date as the code evolves?” Those in the Extreme Programming/ Agile Development worlds will answer: “You can’t, so don’t bother trying. Once you’ve got the code, throw away the model.” Their focus is entirely on functioning code, and maintaining the model past the point of coding is considered extraneous effort. And worse, because they’re not maintaining the model consistently with the code, the model will eventually be misleading: the model will lead you to believe the code works one way, when it really works another. In their view, the code is the only true design; and once a model helps you produce code, the model is “wrong” after that, so throw it out.
And then, of course, there are those with less discipline: those who use this mismatch between model and code as a justification for never modeling at all.
Well, I understand these concerns (except for those who never model), but I think they’re wrong-headed. For an example of why, I point to the highway map in the glove compartment of my car. This is a map of Michigan (the state where I live), which I use to navigate to any part of the state that I don’t already know how to find. It’s also a useful way to teach someone basic Michigan geography. Without the map, I could set you down outside my front door, and let you start walking. In a few days, you would probably know a lot about the square mile closest to my house; but with the map, you could learn the major cities and highways in Michigan in less than an hour (and you wouldn’t have to walk anywhere). So the highway map is pretty useful, except for a couple of problems:
The map lacks detail. It covers over 160,000 square miles in around 1,100 square inches, a scale of nearly 1,000,000:1. That means that my entire local village of Hopkins—about half a square mile in area—should take up a square 0.04 inches on a side, and that’s about the size of the dot that represents Hopkins on the map. But there’s just no way to legibly draw the 22 streets of Hopkins within that dot. All you see of Hopkins is a dot. This is true for most Michigan cities: they appear as dots or larger geographic areas, but with only major streets drawn in. Only a few of the largest cities are reproduced at a larger scale so that you can see the streets in more detail. (In this way, the map is consistent with my advice to make your model out of multiple diagrams.) So there are many things in the real world that don’t appear in the map.
The map doesn’t show the inevitable highway construction that takes place on practically every Michigan highway, all summer long. And the map is static, not dynamic, so it also doesn’t show the inevitable traffic jams that occur on certain streets at certain times of day.
The map is wrong, in other words. So if I plan a route from my house to the monthly Ann Arbor Computer Society meeting, it’s very likely that the map will mislead me. For instance, on one recent trip, the map didn’t show the traffic jam that occurred in Jackson during rush hour; and the map didn’t show the road construction between Ann Arbor and Jackson that exacerbated the traffic jam; and the map certainly didn’t show the traffic accident (caused by the traffic jam, perhaps) that brought traffic to a complete standstill; and when I got frustrated with the progress and got off the highway onto local Jackson roads, the map didn’t show enough detail to guide me, so I had to explore by myself.
So I should throw the map away, right? The only real design is the streets themselves, and the pattern of traffic on those streets, right? Yeah, the map could be updated to show all those details; but that would take too long to be worth the bother, right?
Of course not! We don’t throw away models just because they’re inaccurate or incomplete. The problem of keeping models and implementation up to date is not unique to software: building architects have to constantly revise their architectural blueprints to reflect the building as built; car designers have to update designs to reflect last-minute design changes; and map makers must occasionally print new maps to reflect new roads. And in fact, it’s far easier for us to maintain software models than for others to maintain their models: automated tools can reverse engineer most of the structural changes, leaving it to us to model the behavioral changes based on those structural changes. (Building architects can only wish for that kind of power.)
As George Box once said, “All models are wrong; some models are useful.” Even though our models are wrong, they’re useful:
The requirements model documents the intent of the analysts and the users.
The architecture model documents the modular breakdown of the system, and thus probably the breakdown of teams and assignments and responsibilities as well.
The design models are broad-scale maps to what’s in the code. To say, “The code is the design,” is simply impractical: it doesn’t scale well. Any given diagram may reflect hundreds or even thousands of lines of code. If you try to comprehend that code a line at a time, you might never be able to understand the system; but if you try to comprehend a model first and then dig into the code for more specifics, your understanding will grow much more quickly.
So if you don’t throw out the models (except, perhaps, for temporary models: proof-of-concept sketches that you throw together just as you’re playing with ideas), when do you maintain them? It’s not possible to be constantly changing your model, updating every single time you make a change to your code. Instead, you should consider one or more of the following approaches:
Every time you start a new phase of development—a new release, a new iteration, a new set of requirements—start by modeling what is new and what has changed.
• When you inherit code, and nobody ever bothered to create a design for it in the first place
A powerful technique for learning existing code is to reverse engineer the existing structure with an automated tool, and then to reverse engineer the behavior of that code by reading the code and reading the structure. As you learn what the code does, capture that understanding as Use Case, Activity, and Sequence Diagrams. This is one example of The Outline Effect at work (see Chapter 1).
• When you see a problem
In Refactoring, Fowler7 describes this approach when it comes to code: when you see an opportunity to improve code, you make the change immediately (and then thoroughly test to ensure that you didn’t break anything in the process). Essentially, you “polish” the code wherever you see that it’s tarnished. Similarly, if you see that the model is incorrect or incomplete in an area where you’re working, don’t throw the model out, and don’t ignore the problem. Fix the problem now, when you see it. This way, the model naturally improves precisely in those areas where the most work is being done. This is another way to leverage The Outline Effect: by fixing the model, you have to think about the existing code and your new code more thoroughly, and will be thus more likely to make correct changes in both the model and the code.
• When you can’t see a problem
If you’re chasing down a bug and you can’t find it in the model or in the code, make a new model, based on the code. That will be an “as built” model. Then compare the “as built” model to the “as designed” model. The contrast will give you one more way of looking for the bug. Then, when you find it, fix the “as built” model, fix the bug, and replace the “as designed” model with the “as built” model.
• When it hurts
When you find that the model is consistently wrong and consistently misleading you in understanding the code, take time out to bring it up to date.
Some developers advocate an approach whereby they don’t bother updating the model, as long as it still conveys the correct intent of the code, even if the two have become inconsistent in places. But if the model and code have diverged to such an extent that the code is unrecognizable with respect to the model, then the team should have a meeting, to figure out whether the model is wrong or the programmer just decided to do his own thing. The value of models in this kind of situation is in their ability to capture intent, more than implementation. As ever, follow rule number one, and Do What Works.
So in summary, The Highway Map Rule is this: throw away temporary models; keep permanent, official models; update the official models as you get the chance; and don’t get overly concerned when the official model and the code aren’t quite in step.