I agree with most of what people said here, but I think a more elaborate description is needed:
@public static: Your statement is spot on regarding the problems of designing up-ahead... Even if you're one of those amazingly smart people - the creme de la creme of geniuses - you can't design a whole system up front. You might think you can, but you're fooling yourself. The source of this problem is that, like they say, "God is in the details". The idea of nailing an appropriate design up front is pure hubris. But... That doesn't mean we shouldn't put anytime into designing up-front.
Lets see what's our purpose in "designing" a system is anyway:
First of all, we need a framework to think in. Have you ever tried to start a new project? The hardest part is writing the first line. Why? Because you have a writer's block - you have no idea where you're getting at. For example, lets say you want to write a computer game, lets call it "NotSoFarShout", which will be the next super-cool first-person-shooter that will conquer the world and make you rich for all eternity. Let's design by coding...
We'll start with our program's "main" function:
void main(){
// Um, what the h*** should I put in "main"?!
}
So maybe designing by coding wasn't the right decision. Let's try a different way:
We are planning to write a first-person-shooter. What is a first person shooter anyway? OK, roughly speaking, it's a 3-D world where there's a player character, NPCs (Non-player-characters, like bad people, good people, monsters, gerbils etc), objects (trees, buildings, cars). The world is viewed from the player character's point of view. The player character can obtain all sorts of objects like weapons, ammo, health-packs etc. There are a gazillion other things that describe a first person shooter, but lets move on for a moment.
I know a first person shooter takes a long time to develop - definitely more than a week or two. How would I even begin? I start little, and incrementally write and design the system. So I choose a small, attainable, short-term goal that I can grasp conceptually and actually think on how I will implement - for example, I'll start with the visualization of a 3-D world. I know there are a few good 3-D engines out there, I'll do a little research, pick one, and make it show my world, which is currently made out of a single sphere floating in a void. This is something that shouldn't take more than a week or two.
OK, done. Didn't take me two weeks - it took me three instead, but now I have something to build on. Next thing I might decide I want to build a single scene - static for now, nothing moves, no AI, no special meaning objects, people etc. Now comes the design part... As I start to think about depicting a scene, different aspects of the 3-D world would come into my mind:
- My scene depicts two people standing in the desert, one of them is completely sun-burnt, the other is wearing a suit and a hat and holding a gun in his hand.
- Question 1: How do I describe the content of the scene? The 3-D engine needs a specific representation in order to render the scene. Is it intuitive and easy to work with? If I want to change the scene and move the gun to the other hand of the character, how hard is it? What if I want to move it to the other character? What if I want to add a third person - how hard is it? What if I want to add 100 people in random locations in the scene, each of them randomly possessing a gun and/or a hat?
- Question 2: What if I want the skin color of the character to change over time? For example, one of the characters is sun-burnt. If the other one stays in this desert scene long enough, I want her to gradually get sun-burnt as well?
- Question 3: The sun is shining in the scene, but I want that to change over time - let's say each minute is real time is equivalent to 1 hour in the game, so I want the sun to move over the sky and for the scene to gradually turn into night, with or without a moon. Maybe I want the moon to go through its phases as days and nights go by. How would I represent that?
The idea of this lengthy example is to outline these issues with design:
There are multiple levels of design. At the beginning of writing a product/subsystem/very-large-module you can usually only perform very High-level design. That is, you can define basic large entities like in our example: 3-D engine, storage, Networking, etc. This is important so you can choose where you want to start, where you think the biggest risks will be and suchlike. Note that later on in the process, that initial high level design will not only be broken into smaller, finer details, but most chances are you'll find it has flaws and it will change to accommodate the needs of the product.
Once you've chosen a manageable piece you want to work on, you might have to create a detailed design of that piece. This is where designing by coding usually comes into play. You know you want to render a 3-D world, but you have no idea of what it takes. So you write a prototype to render a single 3-D scene. Then you create a single static scene. Then you look at what you have and think - "OK, what next? What do I need to make this useful for a FPS game? I'll need to render a lot of different scenes. OK, it took a lot of "describe" the current scene in code, how could I make it easier to make variations on the current scene? To create a whole new scene? How would I store scene descriptions? Graphic files? Sound files?...". This is the manifestation of what someone here said before (quoting the XP way of programming) - you write what you have to instead of writing a generic and complex framework up front, but once you have to write the same code again with some variation (rendering a second scene, making variations on the current scene etc.), that's when you generalize and enhance the framework.
Don't be afraid to write throw-away prototypes that will highlight the different aspects you need to consider in your design. That way you have a concrete implementation on which to base your thinking.
Again, quoting some one here - don't freeze your design! It's just plain stupid - if you realize you made a mistake in your design, refactor to change it. Just make sure not to make the mistake of throwing away everything and starting from scratch. Making incremental changes through refactoring usually works much better.
Last but most important - I'm not presenting anything special or revolutionary here - all these ideas have been outlined by other people in the industry, much smarter and more experienced than me. I recommend you read a little about "Extreme programming", Scrum and other agile methodologies, as well as general books on the subject like "The pragmatic programmer", "Code Complete", "Refactoring", the "Gang Of Four" book (a.k.a. "Design Patterns: Elements of Reusable Object-Oriented Software")...
Hope all this text was helpful :)