The goals of brownfield and greenfield development projects are different. Greenfield projects develop a new application as quickly as possible to address an ongoing business problem. Brownfield software development projects, however, are concerned with enhancing existing code to solve a business problem, while keeping the legacy application available. It is the second part of that sentence that is the key to the value of brownfield development over greenfield – you can fix the plane while it’s in the air.
These different goals require a different management and technical approach. Let’s assume we are adding a new feature to an existing application. This is an application that was developed a long time ago and has been working well, but now the business requirements have changed so we must add a new feature. Moving our legacy application towards Service Oriented Architecture is our long term goal, so we will be also move the application in that direction while adding our first feature.
Step 1 – Introduce Inversion of Control
If the legacy application does not have some form of Inversion of Control, this should be introduced first. IoC is an common architectural pattern that makes applications much easier to build and enhance by breaking them up into smaller pieces that work together. Each piece can be replaced with something more functional at any time without affecting the rest of the system, as long as the inputs and outputs remain the same.
One type of IoC is called Dependency Injection. This is an excellent approach to use when building applications from scratch, but can require a lot of work when trying to introduce the pattern into an existing system because the pattern must be used everywhere – it cannot be slowly introduced to different parts of the system. An alternative that is easier to use is called Service Location. This type of IoC can be introduced a little bit at a time, and so lends itself very well to reengineering efforts. Though some consider Service Location to be an anti-pattern, when working with brownfield projects it can be very valuable.
The amount of effort to introduce the infrastructure necessary for a Service Locator pattern to work various from application to application, but typically is less than a day for a developer experienced in the use of IoC. The ROI on this effort is huge, so it should be done at the earliest opportunity.
Step 2 – Create the Service
With our IoC in place, we can create the functionality necessary for our feature as a service. Note that there are two types of services – external and internal. An external service is something you might call via WebServices or some other WebMethod, where the logic needed to implement the business logic could potentially live on a different server. In this context we are talking about an internal service which is available only to the application in which it is incorporated. Internal services are built with the same principles in mind. They take a well-defined set of parameters as input and return a result in a specific pattern.
By building our new feature as an internal service we dramatically improve the testability of the service itself, and the application as a whole. In fact, our Inversion of Control combined with a Service Orientation is what unlocks much of the power of automated testing.
Step 3 – Find (or Create) a Hook
Adding a feature to an existing application requires finding a hook – the appropriate place to insert the new code. Applications built with the latest technical approaches (Inversion of control, Service Oriented Architectures, etc.) have hooks built in to the architecture and so enhancing them is fairly easy.
Legacy applications typically do not benefit from these modern patterns, and so the hooks are more difficult to find. Often the application must be refactored a bit in order to add the hooks necessary to implement the new feature. Depending on the state of the application and the technology used, this refactoring can range from a few hours to a few days. The end result is the same, though – an application that (for now) behaves exactly as it did before, but has a new hook available for adding the new feature.
Hooks often take the shape of just a few lines of code strategically placed in the existing application so that an instance of the newly developed service is created using our Service Locator. We then call a specific method on the service, provide the data necessary to accomplish the business goal, and finally process the result.
Step 4 – Implementation
Something to note about this procedure is that it fits very well into an Agile development process. The code in any one of these steps may be pushed into production without any ill effects. This means it’s possible to avoid the complication and effort of a new branch to perform the work necessary for the new feature. All work can be done in the trunk without the risk of effecting the rest of the system (until the feature is turned on, of course).
One advantage of reworking an existing system over the build-and-replace approach is that the application need never go down, nor does feature development need to stop while a new system is being written. The steps outlined above allow for incremental improvements to an existing system, all while maintaining existing feature set deadlines. If these steps are followed, any development manager should be able to keep his/her users happy, while updating the infrastructure to allow faster delivery times.
If you would like more information, I go over the details of this approach in much more detail in my book “Reengineering .Net”.
The goal of this approach to development focuses on keeping an existing system online during the repair process. There is no need to freeze feature development while reworking an existing system, and no reason to disappoint users by not delivering new functionality as promised.