There's an old proverb that goes "measure twice, cut once". It's so well-known that it transcends language, which is not a typical trait of proverbs. So we all know what it means. It can be applied in reference to skilled trades like carpentry, where its often figurative wisdom becomes literal: one should double-check their measurements before cutting; otherwise they may have to cut again, wasting time and material.
The carpentry example is not one that I randomly chose. Many developers feel that programming is akin to most skilled trades. People get better the more they do it. Of course there's a fair amount of theoretical knowledge involved in software development but the bottom line still stands: one cannot become a good programmer without a good amount of practical experience. This is why people consider programming to be a skilled trade.
But for all the similarity that I can muster between carpentry and programming, the proverb "measure twice, cut once" just doesn't fit them both the same. In fact, I will spend most of this post arguing that the opposite of the proverb fits programming better.
As previously mentioned, making a mistake in carpentry results in wasting time and material. Since we know that in programming there isn't any actual material to be wasted we'll disregard that one and focus on the precious resource of time.
So how much time do we lose by making a mistake that could've been avoided by investing more time in the design (measuring) phase? One might as well ask how long a string is. It completely depends on the project, the task at hand, the clarity of the requirements, the competence of the developers, the expected delivery date, etc. Designing a good software architecture is hard. And it's not hard for technical reasons, it's hard for people reasons. People, usually clients but sometimes our product owners as well are rarely sure about what they want. And we can't blame them for this. Business is not mathematics. Nothing is cut and dry. Most businesses operate by trying out different things and settling on whatever sticks. And the requirements that end up in our backlog often reflect this uncertainty. This is why Agile and Scrum have become popular. An iterative feedback loop development approach produces a better product than the design-oriented (or waterfall) approach in most cases.
I'm not a cowboy developer. I apply many design principles and patterns that I've learned throughout my career. I believe in structure and I believe in planning ahead. But I am not convinced that the best code that can be produced for the task at hand is a result of sitting and thinking about its requirements. I've worked with a few software architects in my career and at this point I'm certain that I prefer to work with the ones that still code. There are too many people in this industry who are not proficient in programming but title themselves architects because they are knowledgeable of software architecture. I have never met a good software architect who wasn't also a good programmer. In order to be a good architect you have to have some skin in the game. You can't just sit and watch over the others from your safe tower.
Does this mean I'm advocating against spending time to design software? Of course not. I am merely arguing that maybe instead of spending more time in the design phase we should spend more time refactoring. I believe that the best way to achieve a good design is to constantly refactor code until it becomes so stable that further changes could not improve it. This should be a joint effort of developers and architects. Code should be written in a way that's easy to change without breaking it. There are many things we can do to achieve that. Unit testing, small and clear interfaces, dependency injection, pursuing purity and avoiding state and side effects, etc. Architects should promote such practices and developers should constantly give feedback to them about things they might've missed in their design. This feedback loop of design and coding will result in the most maintainable code-base. And then maybe when the inevitable change of requirements happens we won't waste as much time implementing it.
There's no material to be wasted. Cut until you get the perfect product.