Legacy means any code you'd rather replace than work with. Given the attitude of most programmers toward most existing code, that usually includes nearly everything except what you're actively writing at the moment (and on a large project where you have to code to a frozen design, even what you're writing at the moment can be included as well).
Edit: since some of the comment threads are getting pretty long, I think I'll add a couple of points here.
- The emphasis "rather" was intended to convey that legacy code also means you're stuck working with it even though you'd rather not. I'm not sure the emphasis was sufficient though. Reasons for that can vary. Some really are just too big and complex to replace. Others are interfaces to other systems. Still others are driven by politics and personalities (e.g., the code is horrible, but the booth babe was amazing).
- Another point I may not have emphasized sufficiently is that while code quality can be a factor, it's rarely (if ever) the sole factor -- and often not even a particularly important one.
- I think the people pushing unit tests as the sole determining factor are like the guy looking under the streetlight for the earring his wife dropped a block away. The light may be better, but you're still looking in the wrong place. Think before you drink the Koolaid.
- (a corollary to 3) It seems to me that some people are talking more about how they wish things were than how they really are. If John Conways' game of life for an Apple II+ isn't legacy, it's because it's sufficiently small and simple that it's easy to re-implement from the ground up. The most perfect unit testing ever written won't change that a single iota.
The last point does lead to two other points I think are often true.
First, code is often retained as legacy, even when it really shouldn't be. Higher level managers generally assume re-implementing a system will cost as much or more than the initial implementation did, which is rarely true.
Second, unit tests are a two-edged sword. They make it all too easy to think that localized changes in the implementation are what really matter. To make significant improvements in a larger system, you frequently (usually?) have to change enough of the overall design that many (if not most) of the unit tests become irrelevant. Unfortunately, the presence of unit tests and the attitude they engender can make it all too easy to ignore the changes that are really necessary.
Perhaps an example of that would help: let's assume a program has a UI library with great unit testing, and several programs that use that library. Somebody in upper management becomes convinced that "web enabled" is important (and, just for the sake of argument let's assume that in this case he's actually right about that). After careful review, the middle managers find that their current unit testing is sufficient that they can change from a user interface displayed via the local operating system's windowing capability to being displayed remotely via HTML/CSS/AJAX, while retaining all the original input validation.
That's great isn't it? It shows how useful unit testing can be. We've swapped out the entire implementation of the whole UI, but ensured that the look, feel, and functionality remains virtually consistent, and all user input is validated to ensure data integrity. Unit testing has saved the day!
Or not! That great, highly flexible, carefully unit tested UI library has helped blind everybody involved to the fact that for this program in its market with its users, a web-based UI is entirely the wrong thing to work on at all. What's really needed is a web service with a RESTful interface, and absolute no UI of its own at all.
Now, it's certainly true that unit testing does not, in and of itself remove people's ability to understand their market or realize that's really needed. At the same time, the old line about hammers and nails virtually leaps to mind. It can be even worse when you not only have a hammer, but have a lot of experience with this hammer, and know it's a really a high quality hammer that works incredibly well in many different situations. The very fact that it's so good at so many things in so many situations makes it even harder to recognize when it's entirely the wrong tool for the job at hand.