That's not entirely true though: There is such a thing as objectively bad code. Bad code takes forever to run, has security holes a mile wide, is extremely verbose and repetitive, makes unwarranted and undocumented assumptions about how the universe interacts with the code, and of course has no automated tests whatsoever.
Now, when you discover this, the right approach is not to rewrite the whole thing, but instead to isolate and fix one small part of the problem. Fixing that one small part of the problem usually goes like this:
1. Create a complete (with 100% case coverage) and passing unit test suite for the one small piece. If there's a bug in the program that makes correct unit tests fail, first verify that it is in fact a bug in the program and not your tests, then make the smallest possible change to make the tests pass.
2. Write the new and better version that satisfies all those tests.
3. Put the new version in place in a test environment, and manually poke at it for a while to ensure that everything that relies on it works properly.
4. Then, and only then, make it live.