I have been reading "A Philosophy of Software Design" by John Ousterhout. The contents of the book are great - in way that it pushes me to think of more out-of-the-box solutions to the problems being described with complex software design.
One of the aspects discussed in the initial part of the book is about tactical and strategic programming. To be honest, I have never come across those terms, but it made so much sense even before I went into reading the details.
To summarise, tactical programming is a way of building software with quick solutions and patches without giving much thought to the design and long-term sustenance and maintainability of the software. On the other hand, products developed with well-thought design principles and patterns are called as strategic programming.
Tactical programming may enable faster early releases. However, it tends to accumulate technical debt in the long term. This gets much more expensive when the code base enlarges and new team members have no clue about it.
Strategic programming suggests investing time early on to build on patterns and principles which are easy to follow even for a new developer. But this requires a lot of reconsideration and trial and error techniques to make sure the design is clean. It does not encourage the notion of "only working code". One other example of time investment is creating organized documentation about the product itself. Any investment that can minimize any cognitive load in the future, is the core of strategic programming.
This is analogous to the classic story of the hare and tortoise.
I am big on strategic programming. However, I do not think tactical programming is no good. Like everything in life, a hybrid approach provides a much better balance of speed and stability.
One of the main drawbacks of tactical programming is the creation of a spaghetti code base, because of the inconsistent coding done all over the place. But this is a faster way to develop and deliver as compared to strategic programming. Teams that are under high pressure - especially in the startup world - of delivering the working product usually tend to do more tactical programming.
By hybrid approach, I mean incrementing both design and code development cycles parallelly. But, start with some design principles at least before starting to code. If the high-level principles and design are ready, to fill those modules we can still work with tactical programming.
This approach containerizes the spaghetti code in smaller modules as against one giant monolith. Complexity is exponentially related to tactical programming. Bigger the complexity, the time required to refactor the same will be exponentially longer. The smaller the complexity, time will still be required to refactor, but it will be much lesser.
When to refactor the smaller complexities? As soon as the design principles are frozen on a deeper level. I mentioned modules, so when design for each module is finalized in the next iteration of the design stream, conscious efforts should be invested to first refactor those modules so that the good design is not left out.
What about Agile?
Those who have worked with me, know that I do not appreciate Agile methodology. In my opinion, it works well only when good design principles are established. But it just forces developers to adopt tactical programming in the initial phase of product development.
Again, a hybrid approach of having parallel design and programming sprints would go a long way.