You don't even try to "evolve" software but periodically rewrite it which "solves" the "Paradoxes of Software Architecture" in one fell swoop. Disposable instead of durable software.
Maybe you make a small change in a logical expression or modify a control structure. Maybe you reimplement an entire function using a new algorithm, or redesign a whole module with different internal data structures. Maybe you rebuild the whole system with the extra N years of insight you have gained from how the previous one worked plus knowing about N more years of evolving requirements than you did last time around.
One way or another, you are always looking for the scale where the benefits of rewriting the whole instead of adapting what is already there — in other words, instead of rewriting one or more smaller parts — outweigh the costs. If a project has reached the stage where the cost of working around an existing architecture is prohibitive, it’s time for a rewrite on an architectural scale.
Yes. c.f. "Write it, throw it away, rewrite it."
However, I'm not sure I agree with the rest of your post. It's certainly true - even tautological - that any change is a change of something. It is possible to change behavior without changing code, though (configuration), and it's possible to change code without changing architecture, and I think each of these things is somewhat different in kind not just degree.
1. think before you do something. i.e. architect towards known knowns. 2. refactor when new information becomes available.
Most people have a problem with 1 and just go blindly into random search, with the thought: "we can just try this; we can always refactor later"
That is fine if you cannot predict the outcome. However, if you never try to become good at predicting outcomes you will get stuck in "lets try" mode.
If you practice predicting outcomes i.e. architecture code/product ( 'think without actual coding'), you will find that more often it guides the way to the highspeed lane and allowing you skip a couple of "iterations/sacrifices" ( but obviously not all... )
Or they leave out the refactor later part in my experience.
If you don't do this, you'll always have to rewrite the whole system at once, which becomes bigger over the years. So you would need more and more time and developers for each rewrite.
In contrast, if you manage to always keep the amount of code you want to rewrite at a reasonable size, this approach may become workable over years.
Looking at biological systems though, that is the opposite of how reliable systems are constructed. If we design from the assumption of failure and runaway complexity, with watchdog and recovery processes, and using emergent behavior from small discrete components to obtain large-scale results, then we can build software components that are much better able to recover from unexpected interaction and produce more reliable systems. It seems to me that this is what the theory should be. I just have no idea how to do that in the real world when building a web app, aside from recycling server processes every now and then.
Here's a quick intro to Elixir (it takes about an hour), where you'll build a distributed app and have it automatically recover from a failure in a process: http://howistart.org/posts/elixir/1
* I had 400,000 processes running on a modest VM without maxing out.
This is a good pattern (one of many), but it is no silver bullet, either. If you overdo this, you end up with overly defensive programming, where all code is littered with catching theoretical error conditions that will never occur. That hides the actual functionality and makes code really hard to read and to change.
The solution pattern for this is to move checks at the boundary of the module, so that the code inside can make safe assumptions on the data it receives, so it can concentrate on the real functionality.
As the article says, whatever you do, you can overdo. Software architecture is always about balancing and judging conflicting appoaches and goals.
I think functional languages have a more healthy appreciation for layering by nature of requiring functions to act on data rather than having mutable data types. I believe if we want to reduce complexity we HAVE to create more layered systems to scope the bounds of our complexity. The problem is many people see this as over-architecting things. With time, a design can evolve such that 10 or 20 layers, as compared to the 3 in MVC we often see, could be manageable with help from our languages and environments (IDE / text editor / etc.).
Design up front for reuse is, in essence, premature optimization.
I also think there is a difference between architecture and features where keeping it simple as possible is more about the features and less about the architecture, i.e. keep the features as simple as possible but have your architecture cater for the future.
And the entire "build one to throw away, then build the real thing" (i.e. prototype first) ideology is also tooting the same horn. Except that one is much harder to explain to non-programmers because they think "if it works, why throw it away?".
I would say that is common sense after writing software for over 12 years. Not specific to extreme programming.
https://cs.uwaterloo.ca/~a78khan/cs446/additional-material/s...