Reviewing Software Design for Growing Applications

January 20th, 2008

Applications that live for a long time tend to grow to the point that they become unmanageable. It is a common problem. Large projects grow by adding new features continually. And when critical bugs are discovered they are fixed as quickly as possible without fully considering the impact on the overall system. It is too bad because even the most basic check on the design is a process that can be as simple as a 10 minute review.

Reviewing the application can be as simple as drawing up a diagram to represent all of the parts of the application with lines drawn between the dependent pieces. This diagram does not have to be a detailed UML diagram but can instead be simple circles and lines. The diagram will quickly reveal some obvious problems. Once you have the diagram you can ask yourself a couple of questions:

  1. Are the dependencies always moving down, or are there circular dependencies?
  2. Are the components too big or too small?

The first question is determined by the lines between the dependent pieces in the application. Place an arrow to represent which direction each dependency goes. If you have lines pointing to and from a dependency then you have a problem. It is time to consider how you can move the piece or pieces in that component that is causing the circular dependency. This may mean moving some functionality out into another component or moving the functionality into the component using the dependency.

Next you want to ensure the dependencies all move in the same direction. If your application is built as a series of layers, such as a 3-tier application, you do not want to allow any layer to skip a layer. Each layer should only service or use the next layer. In applications with a clearly defined Data Access Layer (DAL) that services a User Interface (UI) you do not want to allow any UI element to run any ad hoc SQL against the database, or to reference the database directly in any way. The DAL should be responsible for all database interaction. And when you do need to fix a bug or add a new feature related to the database you will have a much easier job of doing so successfully.

Determining whether components are too big or too small depends on the level of complexity in each component and the amount of interaction between the components. The more complex the components and the level of interaction the more costly it will be to maintain the application. Whenever you can encapsulate a significant amount of functionality into a component that is independently testable and easily reusable you will likely have a reasonably sized component. However, determining an ideal size when you already have a few components can be difficult.

When you draw the diagram for your application for the first time, especially for an application that has grown organically for a few years you will probably just need to draw big circle and label it, "Application." It is a big ball of code with interdependencies going everywhere. I see this happen a lot with web applications. New features are added to pages with lots of code in the code-behind which then cannot be reused in other pages. Placing complex business logic into code-behind files is a sure sign that application needs some refactoring. The best first step is to create a new class library and start using it as your first dependency for the website. Then start moving functionality that you find in the code-behind files to the class library. The best code to move is code that is duplicated across multiple code-behind files.

As your class library starts to grow, do your best to keep it organized using namespaces. I like names like the following:

  • MyApp.Common
  • MyApp.Data
  • MyApp.Business

You can break up code into namespaces as you see fit. And when the class library grows too big you can move a section of the code to another class library with the same namespaces. The code is simply moved into an independently managed component that can now be versioned and deployed as needed. If you look around the assemblies that make up the .NET Framework you will see that namespaces span across multiple assemblies. Perhaps all of the code in the MyApp.Data namespace gets to a certain size you can move it to another class library and adjust your project references as necessary. Often the code that uses the classes which were moved to another class library will need no adjustment, yet you now benefit from the modular nature of a new component.

Note: For a great resource on project planning and software design, watch the .NET Project Planning and Tracking video from IDesign.

After a few review sessions and refactoring your application to account for what you discover in each review you will start to see the big circles shrink and new medium sized circles appear. You should also start seeing lines that flow nicely up down the dependency tree. And as you make adjustments you should start to recognize personal best practices that you will be able to follow to avoid having to make big changes as a result of design reviews.

In the end you will be able to push out releases with more features in a more timely manner.

Comments are closed.