As time goes on in the life of a software project, the codebase grows and complexity increases. This along with hacky bug fixes, tacked on functionality and quick improvements can turn a project into a large multi-functional, inter-meshed mess.
Needles to say this makes feature tracking, versioning, bug isolation and maintenance difficult and limits code reuse potential. While it is best if planning and good design practices protect a project from growing into such a state, the need for rapid development is sometimes more pressing than the need to get design details perfect.
Despite the perfectionist in me making me shudder at the thought of imperfect design, I am slowly coming around to the idea that a product with a flawed design that functions and which customers are happy with, is better than a product that is internally perfect but never delivered. Iterative development and a quick release cycle really are the way to go. Learnings gleaned during one iteration can quickly go into the next.
Just don’t forget to track the areas of code that need improvement so that they *do* get reviewed in future releases. Keep chipping away, keep improving. With time your codebase will make you happy again.