The waterfall model provides a nice clear answer to the question of when you do the design. You do it up front, after you've done your requirements analysis and before you've done your implementation. This answer is simple, clear, and completely and utterly wrong. Project managers tend to like it because it's easy to put on a Gantt chart but the connection between this model and reality is tenuous at best.

There are in my opinion two primary problems with attempting to do all your design up front, and these problems are interlinked. Firstly it ignores that design permeates all elements of software implementation. Software developers don't just need to be aware of the design as they are writing code, they are contributing to it. This goes all the way down the the names of parameters and variables. If your up front design specifies things down to this level then I'd argue it has crossed over into implementation already.

The second problem is that business requirements are both fluid and difficult to gain a correct understanding of a priori. Business changes are almost inevitable during a development process and these changes will need to be reflected by the design. Additionally as the system progresses its development will illuminate areas of the requirements that are incomplete, contradictory, wrong or inadequate. Again the design will need to be altered to adjust to the corrections required in the requirements.

One of the common ways to address these issues is to switch to an iterative development model where there are cycles of design, implementation and test throughout the project lifetime. This allows issues discovered to be addressed in the design cycle of the next iteration. Iterative approaches are in my opinion generally to be favoured as they permit the development direction to be redirected to meet actual business requirements.

Another alternative is to do just in time design as needed throughout the course of development. This kind of approach is common in agile programming methodologies. It's also used an excuse not to do any design by teams that claim to be agile but whose definition of agile appears to be "drop everything we don't like doing and just write code however we feel like". This kind of approach uses Test Driven Development (TDD) and frequent refactoring to allow the design of the application to evolve throughout development. This can be advantageous when the requirements are not fixed or are ill-defined at the commencement of the development process.

The rough analogy I like to use with design relates to economics. Doing significant design up front (often referred to as Big Up Front Design or BFUD) is like trying to centrally manage an economy. It attracts people who like the idea of there being a well understood development plan which proceeds steadily through a number of pre-defined steps before a predictable output drops out the other end. If you believe this is possible then you likely know very little about software development. Like an economy developing software involves dealing with large numbers of variable factors. Predicting all of these up front simply isn't possible and making adequate allowance for all possible contingencies is infeasible. Like centrally managed economies you may get a result out the end but it's likely to be less efficient and make a lot of people unhappy.

Pure TDD with no separate design is much like an unregulated free market. Most of the time it works fairly well. Unfortunately like free markets it may produce undesirable side effects and can cause problems when the system attempts to play with others. Also without any up front planning its easy to lose track over the overall picture and a project may get sidetracked into focusing on only short term concerns at the expense of longer term requirements. Refactoring is a powerful technique when combined with adequate unit testing but it is not all powerful and relying upon it to correct all your inadequate design decisions can be a costly mistake.

In my opinion the appropriate timing for design resembles a regulated free market economy. You start with an high level plan that outlines the broad goals and the constraints in which the architecture sits. This gives you a framework to build in without over committing to low level design decisions. A combination of iterative development and TDD then fills in the design throughout the development process. This gives the flexibility to adapt to changing requirements and improved knowledge throughout the development process. This kind of approach is taken by most modern development methodologies.

I shall now abandon my economics analogy before I overstretch it any further. Next up: Performing initial design for my example application.