We've all been there. Your client will pay you to develop new features, but they won't pay you to refactor. It makes you nuts.
Here's the worst-case scenario.
This isn't just a worst-case scenario because the client fires the developer. It isn't just the worst-case scenario because the developer suffers a nervous breakdown and decides to work on the app for free. It's the worst-case scenario because the developer utters the words "overhaul the entire codebase." Once you go there, you're fucked.
How do you avoid becoming this guy? One way is to simply have clients and managers who trust you when you say the dreaded word "overhaul." But that's hard to predict. At best it'll vary on an overhaul-by-overhaul basis, and overhauls cause terrible stress and boredom at the same time. There has to be a better way.
I read something interesting on the Web which said that Toyota introduced the model of regularly scheduled maintenance which all auto manufacturers now use, and Ford engineers resisted adopting it. The common modern maintenance schedule is based on easy-to-remember milestones, rather than an exact calculation of the exact point at which each and every individual part in the car will experience its peak statistical likelihood of needing service or replacement. That's what Ford was using.
Toyota's schedule was based on regularly scheduled intervals that were easy to remember. The Ford marketing department discovered this and copied it. That drove the Ford engineers nuts, and for many years people remembered Toyota as the cars that didn't break, partly because of superior manufacturing, but partly because Ford's maintenance schedules exceeded the complexity threshold of the average human being, while most Toyota owners followed the standard, simple, easy-to-remember Toyota schedule.
Two issues make the broken-hearted mechanic: first, your clients may only want to pay for development, not maintenance. Second, your clients may not anticipate maintenance costs. If your client doesn't care about the quality of the motor oil, you can't badger them for it or steal their app and go on the run with it. It's up to them how smoothly they want their app to run. But if your client fails to anticipate maintenance costs, that's your fault, because as the expert, it's your job to warn them.
How do you warn them? The exact point at which each and every individual line of code in the app will experience its peak statistical likelihood of needing service or replacement is difficult to calculate. It might be fun to shave that epic, infinitely hairy yak, but it would cost you years of your life. A simpler course of action is to provide a predictable maintenance schedule.
A predictable maintenance schedule for application development should center around regular, easy-to-remember intervals: every three months, every six months, every year. For example, don't upgrade to the latest version of your framework the day they release it. Upgrade on a regularly-scheduled interval that makes sense for your framework, and the pace of development in its community. That makes the upgrade easy to plan and invoice - both for you and for your client. Neither your shop or your client needs to care whether a given version is early or late; just upgrade the framework every six months (for example) and planning becomes easier, whether you're planning the cost or the work.
Consider Rails. It moves quickly. You may want to implement a new feature as soon as you upgrade your app to the latest version, because the latest version makes the feature easy. But then it falls on someone to do the upgrade, and that may require overhauling a few things. Then you end up balancing the pain-in-the-ass factor of the upgrade against the pain-in-the-ass factor of not being able to use whatever new hotness the latest version gives you. Balancing one pain in the ass against another encourages frustration and burnout. But if you know for a fact that the next upgrade is scheduled for a particular date, you have nothing to worry about. You know exactly when it'll happen and you can plan for it in a relaxed way.
One important caveat: I don't actually know if this works. I thought it up in the shower. I think it's a good idea, though.