> you should try to understand the difference between code that should stay and code that should go.
I think that’s exactly what the rule is saying, I think almost everyone would agree. The problem is assuming that code is bad, or even actively looking for “bad”. If it’s in prod, that usually means it worked and was needed. Engineers in my experience do like to assume things are bad and actively look for reasons to re-engineer things without properly accounting for the number of ways things are going right. During my career, I’ve watched two separate large teams at two separate companies decide to rewrite a major codebase because they thought there was too much bad, and they thought there was a tower of abstraction. The result was they both dramatically underestimated the time needed to rewrite, and after having wasted literally tens of millions of dollars, ended up with roughly the same code quality as before and a couple of years of unnecessary down time. It’s more difficult to understand the code that came before and the reasons why it’s there, and it’s easier to judge it bad and decide to rewrite it without completely understanding it, that’s why the default should be to assume the code is there for a good reason, and try harder to understand the reasons.
That rule should, of course, be balanced with this rule, both are true:
> Simplify code, systems, and architectures relentlessly
I personally suspect that writing down and maintaining all the requirements and lifetimes for the codebase separately from the codebase, and having a regression test suite with complete coverage, is the way to safely deprecate features and continue to simply old large complicated codebases. But I’ve never seen that work in practice, yet...
I don't think the OP meant "don't touch old code", but this sentence, to me, implies that "reasons" means good reasons:
> There are reasons for every code and every guard that exists in production.
Sometimes the reasons are not good ones. Maybe that's exactly what the author meant. The rule doesn't read like that to me, though.
And yeah, if you're rewriting something, don't rewrite the whole damn thing! Good code is modular and you should be able to pick it apart and replace components that are causing problems. If it's not causing a problem, don't rewrite it...
However if you're going to be adding features to a section of code (or otherwise working on it a lot), it's a good time to try to understand if you can do a bit of refactoring along the way to clean it up. By default, all code gains code smell over time and also becomes more robust. It's a tight rope to walk.
It is true, sometimes the reasons are not good ones. But, if it is in production, one should assume the reasons are good ones, there's already some evidence. The point is mainly that the burden of proof is on the reader to demonstrate the reasons are not good before modifying it... especially because you're right: code smell is the default product of time. We have to be careful about assuming that code smell indicates something is wrong. My couple of decades of experience is that people assuming reasons aren't good and jumping into modifications is a bigger acute problem than being more conservative than necessary. Accumulating the tower of abstractions is no doubt a problem, but it happens more slowly and is caused by a multitude of bad practices, it rarely breaks production or causes downtime, unlike refactoring too eagerly because of smell or opinion rather than demonstrated necessity. It is indeed a fine line, I completely agree!
I think that’s exactly what the rule is saying, I think almost everyone would agree. The problem is assuming that code is bad, or even actively looking for “bad”. If it’s in prod, that usually means it worked and was needed. Engineers in my experience do like to assume things are bad and actively look for reasons to re-engineer things without properly accounting for the number of ways things are going right. During my career, I’ve watched two separate large teams at two separate companies decide to rewrite a major codebase because they thought there was too much bad, and they thought there was a tower of abstraction. The result was they both dramatically underestimated the time needed to rewrite, and after having wasted literally tens of millions of dollars, ended up with roughly the same code quality as before and a couple of years of unnecessary down time. It’s more difficult to understand the code that came before and the reasons why it’s there, and it’s easier to judge it bad and decide to rewrite it without completely understanding it, that’s why the default should be to assume the code is there for a good reason, and try harder to understand the reasons.
That rule should, of course, be balanced with this rule, both are true:
> Simplify code, systems, and architectures relentlessly
I personally suspect that writing down and maintaining all the requirements and lifetimes for the codebase separately from the codebase, and having a regression test suite with complete coverage, is the way to safely deprecate features and continue to simply old large complicated codebases. But I’ve never seen that work in practice, yet...