There's always a risk of overdoing either the TDD design or the upfront design. So the answer is that it depends. I prefer starting with a user story/acceptance test which is the base of the requirement that my tests will aid in producing. Only after I've established that, I start writing detailed unit tests TDD-style. If the only design and thinking you do is through TDD, then you risk too much of a bottom up approach, which might give you units and classes that are excellent in isolation, but when you try to integrate them into the user story fulfilling task you might be surprised by having done it all wrong. For more inspiration on this, look att BDD.
A great "debate" about this has been recorded between Robert C. Martin and James Coplien, where the former is a TDD advocate and the latter has stated that it ruins the design of a system. This is what Robert said about TDD and design:
"There has been a feeling in the Agile
community since about '99 that
architecture is irrelevant, we don't
need to do architecture, all we need
to do is write a lots of tests and do
lots of stories and do quick
iterations and the code will assemble
itself magically, and this has always
been horse shit. I even think most of
the original Agile proponents would
agree that was a silliness."
James Coplien states that merely driving your design from TDD has a great risk:
"One of the things we see a lot, in a
lot of projects, is that projects go
south on about their 3rd sprint and
they crash and burn because they
cannot go any further, because they
have cornered themselves
architecturally. And you can't
refactor your way out of this because
the refactoring has to be across class
categories, across class hierarchies,
and you no longer can have any
assurances about having the same
functionality."
Also he gives a great example of how a bank account probably would look if you test drove it as compared to using your upfront knowledge to drive the architecture:
"I remember when I was talking with
Kent once, about in the early days
when he was proposing TDD, and this
was in the sense of YAGNI and doing
the simplest thing that could possibly
work, and he says: 'Ok. Let's make a
bank account, a savings account.'
What's a savings account? It's a
number and you can add to the number
and you can subtract from the number.
So what a saving account is, is a
calculator. Let's make a calculator,
and we can show that you can add to
the balance and subtract from the
balance. That's the simplest thing
that could possibly work, everything
else is an evolution of that.
If you do a real banking system, a
savings account is not even an object
and you are not going to refactor your
way to the right architecture from
that one. What a savings account is,
is a process that does an iteration
over an audit trail of database
transactions, of deposits and interest
gatherings and other shifts of the
money. It's not like the savings
account is some money sitting on the
shelf on a bank somewhere, even though
that is the user perspective, and
you've just got to know that there are
these relatively intricate structures
in the foundations of a banking system
to support the tax people and the
actuaries and all these other folks,
that you can't get to in an
incremental way. Well, you can,
because of course the banking industry
has come to this after 40 years. You
want to give yourself 40 years? It's
not agile."
The interesting thing here is that both the TDD proponent and the TDD antagonist are saying that you need design up front.
If you have the time, watch the video. It's a great discussion between two highly influential experts, and it's only 22 minutes long.