The question of how to deal with a brown field project came up in the ALT.Net mailing list. Here is the description of the project:
You've inherited a software project that has deeply rooted dependencies, business logic embedded in the presentation layer (and elsewhere), 'unit' testing that tests databases with hard-coded strings for primary keys, no actual unit testing but contrived integration/functional tests...
Sounds like one of my projects, from several years back, as a matter of fact. Oh, the things we did to System.Web.UI.GridView...
Oh, I was supposing to talk about how to deal with such a project, not reminisce about old projects.
My approach for such projects is called: Obsolete in Isolation.
Basically, I say that the existing software is there, and presumably it is valuable to the business. However, trying to continue development using the existing approach would cost too much time and effort. Typically, maintaining such a project is a big pain, and cost more than keeping my old car running (it was older than me, so take a guess).
Trying to restructure it is possible, but again, costly. As such, I declare the whole thing obsolete and decide that this is a green field project. Now, after having successfully escape a lynching by the customer for suggesting such a thing as throwing away "Working Code" because "I am being uppity", let me try to explain what I mean.
The code is here and it is working. It is not written in a way that make maintainability and extending it easy. We need to maintain and extend the code. We would like to avoid the pain during the process. Throwing the code away is a bad decision in most scenarios. So we will keep it, but we will not continue developing on it.
Instead, we will do all development on a new project, which is built to be maintainable and easily extendible. The old project will stand as it is. Any new features will go to the new project, any minor bug fixes will be done in the old project. With the old "tested with F5" if no other approach is viable. Major bug fixes means porting the code to the new approach.
Over time, what will happen is that we move all the parts of the application that are unstable and buggy to the new approach, which will allow us to test it properly. Any new features will be in the new application as well. The stable parts of the old application are going to remain there, and they are going to keep on working. We aren't going to lose the existing investment in them.
There are issues with this approach, of course. For example, there is a cost of integrating the systems, but it is generally a minor one. And you need to introduce new developers to both systems, the old and the new.
Even with those issues, I find that this is a good way to deal with legacy projects without too much pain.