IMO legacy code is code that has lots of if statements and special cases sprinkled in over the years to enable new features quickly and get out immediate hotfixes for bugs without doing full refactors and/or data migrations to address the root causes. Even with 100% test coverage it’s a pain to build new features in because there are so many paths to think through, and instead of single sources of truth each part of the app assumes every other part is working in very specific ways.
An alternative definition, legacy code is any code where there’s no one left on the team who has been working on it for years and intuitively knows the pitfalls. Then everyone’s scared to touch it or make big refactors, which actually leads to those small special cases being added instead.