Big Ball of Mud (1999)
laputan.org
laputan.org
I've found the most successful way of dealing with these code bases is definitely not this. It is to surround them with a body end-to-end functional tests and then slowly refactor what is underneath, which mostly just involves doing this:
* De-duplicate wherever there is code duplication.
* Pull the different modules apart so that they are minimally dependent upon one another instead of tightly coupled.
* Replacing re-invented wheels once they are de-coupled with higher quality modules.
An unappreciated facet of dealing with big balls of mud is also that unit tests usually range from unhelpful to downright harmful. If you don't have self-contained, loosely coupled algorithmic modules to test, unit tests aren't helpful. They will almost inevitably mean tightly coupling your tests to architecture you know is bad, a mess of unnecessary mock objects and an inability to write tests for real bug scenarios that users report.
Keep in mind that this was written in 1999. The current philosophy of testing+refactoring old code was mostly developed during 2000-201x.
I've been experimenting with applying code coverage to integration test since it gives testers insight into whether they are testing the code at hand vs internals of other integrated systems (this difficulty is likely what keeps many from trying such testing).
But yes, these methods take a lot of time that we don't have.
The other approach I've been leaning towards is static code-flow analysis, which can also be difficult (esp. in distributed web applications).