If "begin with code and end with crap" is the first way and "think it through and code once" is the second way, I prefer the third way: http://www.ultratechnology.com/method.htm
The section titled 'Avionics' is a good read for any programmer and relevant to Dijkstra's discussion (@ 13:00) of the software developed by IBM and used in the Apollo lunar module.
It isn't until I write that I know it needs a rewrite.
Perhaps I'm not smart enough to model accurately enough in my head, or perhaps not disciplined enough. I'd love to feel what it's like to think as someone else for a short while.
The competent programmer is fully aware of the limited size of his own skull. He therefore approaches his task in full humility and avoids clever tricks like the plague.
I think people vary in how much they can model in their head - but everyone has a limit. For me, the interesting question is how to work around that limitation.
What when you release version 1.0 and then someone invents USB and your kernel needs to support it? Then someone invents a device that doesn't conform to the standards and you need to support it?
What about the world where there is no spec for a finished version, merely different balances between effort, cost, value, marketability, usability, and so on...
I don't know enough about Dijkstra to know whether he considers his approach appropriate for all types of programs. I can understand the value for mission-critical applications (like his Apollo example).
This type of software needs to be correct all the time, every time. Adding a trivial feature, like adding USB support, isn't even a consideration (but if it was valuable it would be included in the next generation of the program).