The engineering leadership here at Symantec has a strong commitment to TDD - Test Driven Development. I have been enjoying practicing this approach and experiencing its benefits.
Through this process I have also seen a similarity between TDD and an approach to design that I have applied, which I shall now moniker "Scenario-Based Design" or SDD.
One approach to design is to get a sense of the requirements from a functional spec, and then put together a design that looks like a good fit. You come up with a set of initial abstractions and then go from there, using TDD to refactor as needed.
Although that approach works, you can commit yourself to a lot of rework if your initial design is off the mark. You can still get to a good design, but it takes time, and who has time these days?
So I think it behooves to apply TDD principles to design.
First of all, identify the scenarios you want to support. Your functional spec should describe the scenarios as use cases. Even if you're designing an API for a service rather than a user-visible feature, your functional spec should still describe use cases.
At the first draft, just write out all the use cases as one or two sentences, following the principle of "actor- initial condition - goal" For example "A developer is using the IncidentManager and wants to find out how many incidents exist for a given resource" or "A system administrator has a system which is CPU bound and wants to find out which processes have the highest CPU usage."
Identify which higher-priority scenarios you think will have the biggest impact on design. Normally this is fairly easy to determine.
Start with the first scenario, and come up with some abstractions that seem reasonable for that scenario. You can do this with CRC cards or just on a whiteboard or a piece of paper. Exercise the abstractions with the scenario, refine as needed. You're looking for a high-level design, don't define the signature of each method; save that for your TDD.
Then go to the next scenario, and repeat. Normally you'll find that you have to rejigger things a bit to address the new issue.
I recommend thinking about some of the more common error conditions, work through them too.
The value of this approach is similar to TDD - you design only what you need, and you do it based on real-world scenarios, not on your own concepts of what makes sense. Personally it's quite fun to watch the design evolve on its own.
This approach reminds me of stories of two great artists/designers in other fields.
When I was in Florence, I went to the museum that had the statue of Michelangelo's David, which is wonderful, but in the same building were a series of unfinished sculptures by Michelangelo. It was immediately clear that the sculpture was "born" out of the stone - he wasn't forcing his own ideas onto the stone, but was letting the image evolve out of the nature of the stone itself . When I read his biography later, my impressions were confirmed. He says "Every block of stone has a statue inside it and it is the task of the sculptor to discover it."
The other story (perhaps apocryphal)  is of a woman who was the architect for a huge college in England. She had the entire campus built, but without any pathways. When asked about this, she said, "wait until the first snow." Then when the snow came, she took pictures of all the paths carved out in the snow by the students, and placed the pathways there. Genius.
 Steward Brand, How Buildings Learn