The best way to design software is to write it. Now I know that goes against the principles of good design, so let me say this: You need a fairly good design to start with, but one that is essentially an experiment. During this first version of your software you will start to notice that there are flaws in your design. This is what makes writing it so important. Your writing it to throw it away.
"Always be ready to throw (at least) one away" - Fred Brooks in The Mythical Man Month
I read somewhere that Microsoft uses this approach by making competing products.
Here's an interesting discussion on the topic: http://discuss.fogcreek.com/joelonsoftware/default.asp?cmd=show&ixPost=26268
This will teach you important lessons in the next version. I think that version one should always be an attempt at a functional system, and version 2 should be a complete redesign addressing the problems with version 1 head on.
But WHY!!!? Why not perfect the first time?
- The answers are simple. When you solve a problem set the first time you choose an angle of attack after some forethought. The angle of attack is based on assumptions you have made according to specification documents, users and the operating environment. Specifications being what they are(incomplete at best) change as the system evolves. The initial idea of how something should have worked may come under scrutiny only when it is actually being used.
- During the detailed design of the software, including classes, modules, interfaces etc you make decisions on how things will branch out. What will remain static? What will change? These things are assumed, and quite often guessed completely wrong in the first iteration of the software lifecycle.
- Your expertise need to change. When you first take on a new project you may know something about underlying business rules, but you are very likely not an expert. You essentially need to become an expert in the needs that a system has before you can start writing it. The problem is that the best way to become an expert is to write code for the application. You learn that babies cant jump over hedgehogs, and hedgehogs are allergic to babies, but only when they have salted peanuts. Before you know these things your business logic fails multiple times, and this may happen on the design level.
The programmer's aversion to design is natural, and not a failure. Wanting to accomplish the goal or see how the machinery will work is what a programmer can and should do, instead of meaningless speculation. I am by no means saying that no design should be done. There is useful design. A good database design that will work across different versions of your application is essential. Data anomalies and conversions are not easy problems, so it's best to get it right the first time.