…design for the specific requirement (singular) that you know is broadly needed, and implement that on top of the system you already have. If that can’t be done, or can’t be done with simple code, then you do re-architecture. Rinse and repeat.
You do re-architecture. There must be some wicked spirit hiding in a dark corner of the collective unconscious of programmers, probably born when re-architecting involved re-punching a card. Fact is, many still try to get a system right the first time, out of fear that they will have to later change it otherwise. Bad news, you will have to anyway; good news, it’s no longer that painful if you accept it from the start and for each line of code you write you don’t just ask yourself “will this run” but also “will it be harder to change things because of this”.
This is how many, many people can be working on an open source project simultaneously without knowing what the others are doing. They design their code so that it doesn’t matter what’s going on anywhere else, the code just works, and can be modified and extended easily by anybody. There are lots of ways to do this—they mostly fall under the rules for good object-oriented design.
That is actually an excellent case for functional programming—and for function without side-effects in particular. If the things that the outcome of a piece of code depends on are clearly spelled out under your eyes in an argument list, instead of floating somewhere in outer space (and that means not just global variables, but also instance variables once the class definition gets taller than a couple of pages), it becomes much easier to reason about that piece of code and change/grow it in ways that do not impair other parts of the system.