In Working Effectively with Legacy Code Michael Feathers defines Legacy Code as “Code with no tests“, which reflects the perspective of legacy code being difficult to work with. I’ll stick to this definition.
This is a great article on how code evolves in an organization. A quick fix here; a small misstep there can easily accumulate to a difficult to maintain piece of software. Even if the initial code is wonderful; new techniques and unexpected features can cause the code to evolve into unexpected, and unwanted, ways.
While I agree on the conclusion of the article, attempt to leave the code a better place than you found it, there are certainly difficulties in doing so. I know that in my experience you will not always have the availability to refactor the system; sometimes you will have to leave it at the current status quo. What I have found that works is to start to keep track of the more problematic areas and keep estimates on how long a refactor for those areas will take. The next new feature or update to that area, you can include the refactor time (be sure to flag it as non-billable if necessary) and simply perform it during that project. The refactor is included in the schedule (along with the enhancement) and should not cause any unexpected delays (which a surprise refactor could).