As a first post, I'll open with a favorite topic of mine...dealing with change. When developing, I'm often torn between handling change by either planning and coding for it or by reacting to it when it comes up. To deal with this, I try to: avoid premature generalization, stick to the rule of "fool me once," code to interface when applicable, and architect judiciously. To elaborate...
Often, I hear people talk about how they're writing their code to be reusable and configurable. Eight times out of ten, when I find myself saying these very words, I find that either 1) it never ends up being reused or 2) the configurable parts I coded never needed reconfiguration. Unless you're building an application or framework specifically for a niche of reusability (such as an ORM), a better philosophy to stick to, to combat premature generalization while still preparing for change, is to keep your code as simple as possible and have no duplicated code.
Fool Me Once
Along with premature generalization, there are developers that fall into the bane of pattern-itus. This is the developer that throws a design pattern at every opportunity in the name of preparing for change. But if that area of the application never gets changed, then the pattern may have only introduced needless complexity to the application. On the other hand, patterns are incredibly useful when change does occur; therefore, I (try to) stick to keeping things as simple as possible and introduce patterns only after the first change in code occurs. To illustrate, suppose you have a two-case switch statement. The state pattern, or other polymorphic solution, could be introduced to eliminate the switch statment...but what if the two-case switch statement never gets any bigger or never gets duplicated? In this case, the state pattern only added a lot more complexity to the situation. On the other hand, what if the two-case switch statement begins to grow or is needed in another area of the application. This becomes the perfect opportunity to introduce a design pattern because we now know that this is an area likely to change again in the future. Obviously, some design patterns are useful from the get go...which leads me to me to my next point...
Code to Interface
Although it's difficult to predict exactly where change will occur, coding to interface is a simple way to keep code loosely coupled while still avoiding excessive premature generalization. Communications with a data-tier are perfect examples of good places to code to interface. The data-tier is often mocked during unit testing and, generally, has a stable set of publicly exposed methods. In this case, design patterns, such as the abstract factory pattern, are a perfect fit from the get-go of project development. It could be argued that this itself is premature generalization...but, IMO, there are some patterns which are obviously applicable when a project begins...I'd call this architecting judiciously.
So after bemoaning on about premature generalization and needless complexity, general architecture still needs to be taken into account. Third party tools and general architectural approaches should be carefully considered before beginning any project. For instance, if I know I'll be building a data-driven application, then there are some patterns which, for me, almost become a given and need to be taken into account from day one; an example includes building a data-communications framework with NHibernate using the abstract factory pattern. On the flip side, over-architecture can quickly lead to overly-complex applications that are difficult to understand and maintain.
There's certainly a delicate balance among premature generalization, coding to interface and architecting judiciously. That balance is usually determined in each project by trial-and-error...with the goal being that each type of architectural error is only encountered once. If you're successful at this, then your application is probably adapting well to change and will continue to do so in the future.
For some great resources with respect to...
...fool me once: Martin Fowler's Refactoring and Joshua Kerievsky's Refactoring to Patterns.
...coding to interface: Robert Martin's Agile Software Development.
...architecting judiciously: Ken Pugh's Prefactoring and Martin Fowler's Patterns of Enterprise Application Architecture.
09-20-2006 2:21 PM