All software developers know how to write code, but many developers are not very good at creating the design of an application or a subsystem. We all get training in programming languages, development tools, algorithms, data structures, communications protocols, and database tools, but most of us have never had a formal course in software design. We learn about design "on the job" -- it is a case of learning by doing.
There are a number of standard design principles that ought to be part of every developer's toolkit. The most important principles include:
These principles are useful, but they can be a bit complicated to follow. Many novice designers have difficulty applying them in their design work. A better way to improve software designs is to apply a set of design heuristics in the process of doing design reviews.
The best set of design heuristics is a list of object-oriented design heuristics created a number of years ago by the OO design expert Arthur Riel. Arthur's heuristics are a good starting point for having discussions about good and bad design practices.
This set of heuristics is outlined in Arthur's excellent book Object Oriented Design Heuristics. (The book is available online through the Safari system.)
The heuristics are not absolute rules. Each heuristic describes a single good design or programming practice, but there are times where it is acceptable to violate the heuristic -- when there are design tradeoffs with other rules and practices.
These design heuristics are extremely valuable when developers are involved in design reviews. Many developers don't know how to talk about what they like and what they don't like in a design. In many reviews, no one wants to criticize a design choice because they don't know how to explain why the design choice is inadequate. So many design reviews just focus on superficial errors in the design document: obvious errors in algorithms, missing failure cases, and spelling mistakes.
In the design heuristics, there are several key terms that are used many times: god classes (classes that hoard the majority of the responsibility in a module or subsystem), size of the public interface (the number of public functions in a class interface or module interface), patterns of collaboration, containment relationships, information holder, controller, coordinator, and is-a relationship versus has-a relationship. These terms are very helpful, because a developer can look at an existing design and say, "I don't like this way of designing this subsystem, because the XYZ controller class is a god class. We should split up the responsibilities to make the design easier to understand and easier to maintain."
In conclusion, software developers can benefit from learning and using the design heuristics. The heuristics help developers discuss their designs -- to find design errors that might reduce the flexibility and reusability of the modules, or design flaws that might impact system performance and long-term system mainenance.