The solution to making it work first is to make a prototype with the correct design. You do not have to implement it at once.
(On C2 wiki, a proper SpikeSolution)
If you hit a case your design cannot cleanly handle, it is time for redesign, but often a tiny amount of design constrained by use cases will expose a good design quickly.
My rule is simpler: never write instant legacy code. If it feels like something you won't understand a month later, it likely is legacy code.
If it reads badly (and I'm not talking about this or that brace or whitespace choice), then it also provably is in that category.