I run into this a lot with clients. They have a lot of code and they want us to do something with it. Clean it up, add features to it, re-implement parts of it, etc. And while I've yet to come across a codebase which was designed to be updated (that is, a codebase which isn't held together by duct tape and bailing wire), I've consistently come across management who clings to that code with all the fervent zeal of an old-timey miner clinging to a nugget of gold.
But the code itself isn't even an asset. It is, in fact, a liability. The business knowledge is an asset. The tests which validate the code are an asset. The people who understand the business are an asset. The intelligence behind the code is an asset.
The code itself is forever suspect. After all, when was the last time you encountered an enterprise software ecosystem which had no bugs? Or wasn't missing any features? It's guaranteed to happen. And the only way to really solve those problems is to have that business intelligence. Sure, you can address various little bugs and problems with various little patches to the code. That is, you can correct code by adding more code. But what are you gaining when you do that? You're just piling on more code. The problem wasn't solved with a business-intelligent design, it was just compounded.
For example, let's say you have a data element in your system which we'll call Widgets. For any given Widgetman, you can have multiple Widgets. So without any further understanding beyond that, the developer has a Widgetman table and a Widget table where the latter has a foreign key to the former. Then the application just has some simple forms over these tables. No business layer, just forms and data. Notice... no business intelligence.
Now there's a problem. Some users are complaining that there are duplicate Widgets. Apparently there's a rule that for any given Widgetman there shouldn't be identical Widgets. They can't discern between the two and, to the business, it doesn't make any sense. So a bug is filed. (Well, there's a pretty significant argument here as to whether or not it's a "bug." After all, it met the requirements. Nobody in the business provided any business intelligence. The "rule" that isn't being followed in the code wasn't known or understood, at least not by anybody involved in writing the code/tests/etc.) The code isn't doing what the business wants.
This is, of course, an overly-simple example. If this was all the system did then it would be a simple fix. But let's assume for a minute it's a very complex system. Let's assume that adding a business logic layer is major surgery, surgery to the point of nearly re-writing the entire system. So what do you do? Well, you're a professional software developer. You know how the system should be designed. The client asks how to fix the problem, and you present options.
One option is to add a business logic layer. Models, services, however it should be architected. The point is, you've identified that there's no business intelligence built into the software and it really needs to be. So you lay out a high-level plan for major surgery. Included in this option is some re-engineering of the data persistence so that this problem can't happen. After all, the business logic is only responsible for the data in motion. The database is responsible for the data at rest. Both of them need to have the business intelligence baked into them.
The other option is to add some code to either the form or the data access layer to check for a "duplicate" (which doesn't actually meet the definition of "duplicate" in the system, which is governed only by the table's auto-generated primary key, but instead is a one-off custom definition defined somewhere different and less intuitive than the primary key). The form would then not allow the user to save a duplicate.
(A third option, and one I've seen more often than I'm comfortable admitting to the universe, is to write a script that a support tech runs against the database to detect duplicates and delete them. So someone would just occasionally run this script and manually delete "duplicate" records. In this case there is no business intelligence anywhere except in the head of the support tech who just knows to do this from time to time.)
That first option is a tough sell. In fact, I don't think anybody's ever bought it. But it's not a tough sell because of the work involved, or the time it would take, or anything immediately concrete like that. Anything can be budgeted if it makes sense, and it certainly makes sense to understand the document the business process and intelligence and codify it in an application.
It's a tough sell because that system that's already in place has already cost the client money. They paid somebody to write that. So somewhere in the business there's a spreadsheet which tracks how much they paid and how much it saves them on operational costs, which feeds into an equation whereby they've calculated how long that code has to continue to be used in order to break even on that past expense.
They've already trained their users not to enter duplicates. (This was an operational cost.) They have a support tech who addresses the issue when it happens. (This is an operational cost.) They hire you to do the second option above. (This is an operational cost.) Later developers will have to find and understand this one-off code and will have to re-learn this lesson in future enhancements, since it's not intuitively designed. (This is an operational cost.) After all, the business knowledge isn't captured anywhere. There's just some random conditional either on the form or in the data access layer. Someday the client may have someone write another app on top of those tables, and that app won't have that logic. Someday the client is going to tell another developer that "in our system a Widgetman can't have multiple similar Widgets" and that developer is going to look at the code and reply with a meek "um... ya they can." (This is an operational cost.) And so on.
Yes, the code they have right now did cost them money. But that doesn't mean the code itself is valuable. The code is lacking in business intelligence. It doesn't represent the business process. The code cost money, but what value does it have to the business? The first option above identifies the business knowledge and codifies it. In documentation, in code, in data, etc. It creates something of value. The second option adds no value to the system. It just continues with the status quo, costing less money in the short term but improving nothing. The third option removes value from the system by doing nothing more than adding an operational cost (to counter the operational cost saved by having the software there in the first place... maybe not entirely but it adds up when combined with other similar support issues).
Perhaps you'll be one of those lucky developers where the client actually agrees to the first option. Maybe that spreadsheet is close enough to break even at this point that they can justify it to themselves. Or maybe the operational cost is bad enough that they just need to do something drastic to show that they're trying. Or maybe you're just a damn good salesman. Either way, it's time to write some new code.
Is this new code going to have business value? Is it going to be an asset to the business? Come on, you've been writing software for a long time. You know as well as anybody that even your own code is crap compared to what you'll be doing a year from now. Someday someone is going to try to sell this same thing to the same client to replace your code. So even when you re-write it and "do it right this time," write it knowing that it will be replaced. Separate those concerns, S.O.L.I.D.-ify those designs, etc. (I should probably write a post about that at some point too, sort of a "Part II" of this post. But that's for another day.)