- the user's mental model of how the system works and how its different parts relate
- the model in the code, implied by the objects and their relationships
- the design of the system (hopefully this matches the code, but not always!)
When all the models match, you have an effective system. When the models don't match, that's a code smell. At it's core, a code smell is a warning of future difficulty. And when the user's model doesn't match the code, it's going to be hard to add features the user wants because the code has different ideas.
For example, I'm working on a project that is a basic web application. Users sign up and are put into teams for contests. Everyone on the team that wins the contest gets a prize, and then the whole process starts over with new contests and new prizes. A user made a request to be able to see how much their team had won, total. Sounds fairly simple. It wasn't.
There was a discrepancy in the models. The user's mental model of the system said that "teams win prizes". The underlying system code was that "contests have prizes". This model mismatch meant that instead of simply getting all prizes for a team, the developer had to get all the contests a team had participated in and then get all the prizes awarded for that contest and then filter by team. It was real work!
So the developer tried to refuse to do the request because "the system isn't designed that way." Now we've got a real problem. There are three ways forward:
- Refuse to fix it. This is tempting sometimes, but it's an unacceptable answer in many cases. It's a user request, and assuming it gets approved, you'll need to implement it. Features aren't chosen by how hard they are, but by their value. Get over it - "it's hard" is not an excuse.
- Fix it. It's possible to make it work, even if it's in a roundabout way. Note that this does make your code more closely resemble a mountain of spaghetti.
- Reconcile models. Whether you change the user's model (often implied through the user interface) or change your underlying system design, the right thing to do often is to reconcile the models. This is how you ultimately clean up the code smell - by rearchitecting, refactoring, or modifying the UI and documentation so that what the user understands matches the design and matches the code.
When you get a request or a bug, and your first response is "oh, that's weird" or "oh, that's a lot harder than it sounds", ask yourself what the model is that came to that request. You may have just found a code smell.