Software is a model.
We're used to thinking of models as physical models that occupy space, but software represents a different kind of model, not a physical model but rather a behavioral model. Behavioral models take place in time rather than in space. You can think of them as black boxes where some input comes in and then drives an output that goes out. What happens in between can be given a label and abstracted out.
Software is only clay. It's a medium for creating virtual models based on our linguistic constructs and our understanding of the way things work.
Remember the myth of the Tower of Babel? Humankind wanted to build a tower so high that it reached up to Heaven, but God didn't like the idea of associating with riffraff like us so he made everyone speak different languages. Because we couldn't understand each other we couldn't collaborate effectively and the tower fell.
Communication is central to collaboration. We must have a shared vocabulary and a shared set of standards and practices. Far too often, we use the same words but have very different meanings for them. We think in terms of our specific experiences, but our words represent generalizations. We're constantly going back and forth between the abstract and specific, between the generalizations of what the words represent and our specific experiences.
This is where ambiguity and misunderstanding come in. Language is inherently ambiguous. But the kind of rules you need in order to articulate behavior in software have to be very precise. This is one of the reasons that requirements have failed as an effective way of conveying the features to be built in a software system. It's far better to make software development a discovery process between the Product Owner or customer representative and the development team.
It's nearly impossible to visualize an entire software system before it's built. It's far easier to visualize each piece as it's being built with feedback from the development team. This kind of iterative development, where we're constantly getting feedback from our customers and refining what we're building so that we're continually building the most valuable pieces, turns out to be a highly effective way for constructing software systems.
The key is to make the software that we build understandable by using effective metaphors that are centered in the domain language of the domain we're working in. The domain language holds valuable information about the domain so we want to incorporate it into our designs. Our domain model should be understandable to domain experts even if they haven't been exposed to programming languages.
If a class diagram of your system reflects an understanding of your domain that a domain expert would appreciate, you have an understandable design. If not, perhaps it's time to refactor.