The key I've found is not worrying about how beautiful or clever the code is, but how maintainable it is.
If the code is designed properly, the only requirement I have is that I can go back into my code and change it easily to what I now need it to do. It should be malleable like silly putty. If I can do that easily, without requiring massive rewrites, then this means that the code can change as my requirements change, and that to me is a good design, and "good enough" code. So don't worry about optimizing too early, but make sure the code can be optimized easily when you need it to be.
If I want to make code changes that requires massive rewrites when my requirements change a little, then it means I've coded myself into a corner, and I've done a poor job designing the code.
This has to be tempered, though, with some consideration of how likely it is that the requirements will change. Of course we might say "requirements always change" but there are definitely situations where to know with a high degree of certainty that you are writing something that will be used once or a few times and never again, or something small enough and well defined enough that the requirements can't change much. If you worry too much about maintainability on code that will never be maintained, you're wasting time.
I agree completely, this is usually the driving principle when coding, I don't spend a lot of time with premature optimization in terms of making the code run as fast as possible, etc but if I am face with an issue where I realize I haven't thought something through in terms of maintainability or ease in extending the code for similar purposes I know I will need sometime in the near future then I have no problem spending a lot of time thinking through these problems. In my opinion, this is really what pushes one to become a better developer as the time spent thinking about this sort of issue pays off later when a similar issue comes up again and the solution is already second nature.
If the code is designed properly, the only requirement I have is that I can go back into my code and change it easily to what I now need it to do. It should be malleable like silly putty. If I can do that easily, without requiring massive rewrites, then this means that the code can change as my requirements change, and that to me is a good design, and "good enough" code. So don't worry about optimizing too early, but make sure the code can be optimized easily when you need it to be.
If I want to make code changes that requires massive rewrites when my requirements change a little, then it means I've coded myself into a corner, and I've done a poor job designing the code.