Design drives what we are building, it never “comes into the picture” it is there from the beginning and is constantly iterated upon and changed, just like the code is.
We always do TDD, being experienced practitioners of it, it doesn’t really slow us down for most development. When it does slow down up-front development, its worth the tradeoff in the time it will save later.
You used the word ‘prototype’. We very rarely, if ever build prototypes because they are built with a ‘throwaway’ mentality, and then are very rarely actually thrown away. Instead, we focus on shipping useable software quickly that can be rapidly changed, but that provides the basis of an actual production system.
If you don’t know how something is going to be implemented you might not understand what you are actually supposed to be building, and therefore you should not be coding yet. Instead, go back and do more wire framing or discussing to figure out what you are actually building.
Alternatively, you might not understand how to architect the code of the system. Doing TDD will drive you to good code/good architecture, because bad code is hard to test.
Finally, you really may not know enough about how a particular library is going to work, or just how to write something. In this case, it is ok to write some untested code to figure out the stuff you don’t know. This is called a spike. Once you’ve got it figured out and you understand, throw the spike out (yes, really) and start over doing TDD.
All of the above is where you’ll end up when you are experienced with TDD. You may not be there now. Tests are better than no tests, so if you are getting started with TDD and its hard, and it’s easier right now to write some tests after you write the code, it’s not the end of the world, it’s better than nothing. However, it’s important to understand how you want to be getting better than that, and hopefully the next time you start writing code, you’ll be able to do even more TDD as you are more comfortable with the flow and the tools.