A design is the structure of a program. That program exists to satisfy a certain need. So first, you will want to understand the requirements. Typically, you will have different kinds of users doing even more different things with software.
From that, requirements can be prioritized. It doesn't matter much if there is a bug (with simple work-around) in a feature that is used by one person once a year, but a similar bug in a feature that is used every two minutes by 10'000 users is really annoying.
Also, you will have some ideas on how requirements might evolve. However, keep in mind that your understanding of the stakeholders is incomplete, and that they might change their goals as a result of future events you could not have predicted. Chances are, therefore, that the new requirements will actually be quite different from what you expected. Designing for anticipated requirements therefore is a two-edged sword: If the anticipated requirements become actual ones, you have saved yourself work - but if they don't, you have expended additional effort for zero benefit. In fact, not just you, but also those that inherit your code that got needlessly complicated because it was designed to do more than it had to.
Next, I briefly think about the feasibility of the requirements, the rationale being that the earlier I communicate that something is not possible, the more time I give decision makers to come up with alternatives. If something is obviously impossible I give feedback right away, if it might be impossible I try (briefly) to determine whether it actually is. If that remains inconclusive, informing the decision maker about that is prudent. You might then implement that functionality first, so that if the project must fail, it at least fails early.
Then, I try to identify similarities among requirements. These often result in similarities in the code, and are often an opportunity for code reuse. I then group requirements according to similarity into units. Then I identify dependencies among units, and finally implement unit by unit in dependency order.
To implement a unit, I first design the external interface. To get a good interface, it must conform to the expectations of the caller. So I put myself in the mindset of the caller and write the interface signature, before my mind becomes "tainted" with the implementation details the interface is supposed to encapsulate. Some people recommend to actually code against this interface at this time by writing a unit test. (I rarely do that, because I my domain units often require a lot of mocking to test, and when the testing code gets much longer and more complicated than the code under test, and most bugs are integration bugs anyway, integration tests are more efficient.)
Then, I implement it.
Then, I test it, and fix bugs (Because I construct the units in dependency order, I always have a runnable program I can test against)
And then, on to the next feature (or the week end, as the case may be :-)