List of Figures
Chapter 1. Understanding brownfield applications
Figure 1.1. Brownfield applications have three components that distinguish them from greenfield and legacy applications.
Figure 1.2. The Iron Triangle. Changes in any of the three vertices will affect the project’s overall quality.
Figure 1.3. Generally speaking, your job will be to balance time and resources against code quality and features.
Figure 1.4. There are many challenges to a brownfield application. As developers, we often focus only on the technical ones.
Figure 1.5. Before starting any change, be sure you can measure how successful it will be when you’re done.
Figure 1.6. Proofs of concept and mockups can help convince your boss of the value of a concept and can help you work out
kinks before implementing it large-scale.
Chapter 2. Version control in brownfield applications
Figure 2.1. Many brownfield applications will give reference errors when first downloading them from version control.
Figure 2.2. Often, reference errors are due to missing assemblies. The developer must hunt down all the dependencies for the
project.
Figure 2.3. Editing a file with a VCS that uses file locking. Note that while Dean has the file checked out, no other developer
can edit the file.
Figure 2.4. Editing a file with a VCS that uses version merging. Both Dean and Martin are able to make changes to version
1 of the file. But because Dean checks in his changes first, Martin must merge those changes into his version before checking
in.
Figure 2.5. The branching process. Each node represents a check-in.
Figure 2.6. New developers should be able to follow these simple steps to start being productive on a brownfield application.
Anything else will result in friction and lost productivity.
Figure 2.7. A sample folder structure to facilitate getting developers up and running quickly
Figure 2.8. Example of maintaining the same physical file structure in the VCS as you have on a local working folder
Figure 2.9. TortoiseSVN, like many VCS tools, allows for ignoring patterns and files.
Figure 2.10. The steps of the check-in dance for a file-locking VCS
Figure 2.11. The steps of the check-in dance for a version-merging VCS
Chapter 3. Continuous integration
Figure 3.1. Adding a component to version control that requires developers to install something first is a source of friction.
Figure 3.2. As a problem or bug makes its way through each stage in a project’s life cycle, the effort and cost required to
resolve the bug increase by an order of magnitude.
Figure 3.3. After adding continuous integration, you need to add a step to the end of the check-in dance to allow the CI server
to do its work.
Figure 3.4. During the time between when you get the latest version and check in, someone else may have checked in code.
Figure 3.5. The three basic steps of an automated build. Each of the incremental steps builds and relies on the previous.
Figure 3.6. Keep the build components separate from your code and third-party libraries.
Figure 3.7. Build components can be stored in Visual Studio in solution folders or even separate projects for ease of use.
Note that solution folders don’t map to physical folders.
Figure 3.8. Example of creating a release archive in your folder structure
Chapter 4. Automated testing
Figure 4.1. An example of branching logic within your code. Each path through the code represents a potential point of failure
and should be tested.
Figure 4.2. State-based testing doesn’t require any knowledge of the code under test.
Figure 4.3. Steps to integrate an existing test project back into your process
Figure 4.4. There are many reasons a test may be invalid.
Figure 4.5. Physically separating unit and integration tests in the folder structure allows for build scripts to easily create
separate testing assemblies.
Figure 4.6. Many considerations are involved when you’re testing a web interface, not least of which is the fact that you
must test on all major web browsers.
Figure 4.7. The process for running integrations tests against a known set of data
Chapter 5. Software metrics and code analysis
Figure 5.1. Code coverage utilities are useful when attached to your automated test process. Here, the code coverage tool
monitors the test utility to see how much of your application code is exercised by the tests.
Figure 5.2. Efferent coupling (left) tells you how many classes your class depends on. Afferent coupling (right) shows how
many classes depend on your class.
Figure 5.3. A dependency matrix (shown here from NDepend) can help assess cohesion. Squares around the diagonal suggest higher
cohesion.
Figure 5.4. A graph of the distance from the main sequence taken from NDepend. Notice the Common assembly nestled in the Zone
of Pain indicating that it contains a high number of concrete assemblies and that many assemblies depend on it.
Chapter 6. Defect management
Figure 6.1. Your policy toward defects will affect both your project team’s confidence and the overall quality.
Figure 6.2. Defects come in many flavors: languishing, incomplete, spurious, and feature requests.
Figure 6.3. In the initial defect review, the goal is either to close the defect or assign it to the development or test team.
Figure 6.4. New feature development and defect resolution will almost always occur in parallel.
Figure 6.5. Swapping team members regularly from defect resolution to new features helps keep morale from flagging.
Figure 6.6. The question about whether something is a defect or a feature is moot. The work still ends up in the backlog either
way.
Figure 6.7. Concise, clear, complete, constructive, and, ultimately, closed are the traits of a good defect entry.
Figure 6.8. Remember that the development team is only one part of the overall project team.
Figure 6.9. A sample graph showing number of defects per module over time. Note that the increasing defect count in the A/P
module indicates it could use some attention.
Chapter 7. Bringing better OO practices to the project
Figure 7.1. Five “-abilities” that help lead to a maintainable application
Figure 7.2. Extreme inheritance isn’t always a good thing. A change in the Item class will have far-reaching ripple effects.
Chapter 8. Relayering your application
Figure 8.1. Traditional three-tier architecture. The user accesses the presentation tier, which talks to the business logic
tier, which talks to the data access tier.
Figure 8.2. Very often, a physical tier will contain several logical layers.
Figure 8.3. A seam is the interface that one logical layer uses to communicate with another.
Figure 8.4. A vertical layer (along the left) will span several, sometimes all, horizontal layers. In this example, the physical
tiers are grouped by color.
Figure 8.5. Traditional n-tier representation of a data request. Although we show it here, the domain often doesn’t exist
as a separate layer.
Figure 8.6. With a domain-centric approach, the domain encapsulates both the business logic and the domain objects.
Figure 8.7. The domain-centric architecture we’re working toward. It highlights the importance of the domain as the center
of the application.
Figure 8.8. An overview of the steps involved when you refactor your application to use layers
Figure 8.9. When refactoring to layers, the key is incremental changes—refactoring only a small portion of each screen at
a time.
Figure 8.10. Further refining the layers
Figure 8.11. The final refactorings
Chapter 9. Loosen up: Taming your dependencies
Figure 9.1. Dependencies for our sample classes. Each gray arrow represents a dependency on another class. The dashed arrows
indicate that the dependency is based on inheritance.
Figure 9.2. After introducing an interface, we’ve reduced the number of dependencies and changed the direction of one. More
importantly, SpellChecker now relies on only one interface.
Chapter 10. Cleaning up the user interface
Figure 10.1. The Passive View. The presenter mediates between the model and the view, which have no knowledge of each other.
Figure 10.2. The Supervising Controller. Unlike the Passive View, the view has direct access to the model.
Figure 10.3. In the Presentation Model, the view keeps itself synchronized with a screen-specific Presentation Model object.
Figure 10.4. The high-level steps to refactoring to an implementation of MVP. The first step, writing a test, may not be possible
depending on your current architecture.
Figure 10.5. A Passive View is more testable than the Supervising Controller but at the risk of being more chatty.
Figure 10.6. Model-View-Controller. Like the Supervising Controller, the view has access to the model.
Figure 10.7. Some differences between MVP and MVC. MVP is usually better suited to brownfield applications.
Chapter 11. Refactoring data access
Figure 11.1. Initial approach to layering your data access code. As in chapter 8, we refactor in increments.
Figure 11.2. Interfaces and implementations for a generic data access scheme. The IRepository interface and BaseRepository
implementation provide most of the basic operations.
Figure 11.3. Features of an effective data access layer. Some are easier to implement than others.
Figure 11.4. Database calls are queued in the Unit of Work and executed in a single transaction.
Figure 11.5. Hand-rolled DALs can be application specific or corporate wide. Each has advantages and disadvantages.
Figure 11.6. ORMs can provide a lot of benefit, but be prepared for possible resistance and a learning curve.
Chapter 12. Managing external system dependencies
Figure 12.1. Example of four screens calling the same third-party component. What happens when something changes in the third-party
component?
Figure 12.2. The same drawing with an anticorruption layer added. Now the screens call out to your own service, which passes
the calls on to the third-party component. If the component changes, only the anticorruption layer needs to be changed.
Figure 12.3. Ticket Chooser, a sample application that reads a web service
Figure 12.4. A class diagram of a possible structure you’d like to use instead of DataSets
Figure 12.5. This sample application provides a simple search function against a music library. It uses two different third-party
libraries to extract the metadata from each file.
Figure 12.6. Useful patterns for dealing with external dependencies. All deal with the same theme of hiding parts or all of
the access to the dependency.
Figure 12.7. When connecting to a web service, Visual Studio generates a proxy class to control access to it.
Chapter 13. Keeping the momentum
Figure 13.1. Without a consistent and conscientious effort to improve, a brownfield application reverts back to its old self
very quickly.
Figure 13.2. Once a large project has built up momentum, it’s hard to make drastic changes. The beast will crash right through
most roadblocks you put up.
Figure 13.3. Nudging a project to change its course slowly and steadily over time provides a smoother transition than an all-at-once,
or “big bang,” approach.
Figure 13.4. Each topic lays a foundation for the one above it. Make sure you have a good grasp of each one before moving
on.
Figure 13.5. No one likes spring cleaning. So don’t do it.
Figure 13.6. Pair programming is an excellent way to introduce new concepts to a team.