With many programs, there are a huge number of areas that can be improved. Since this is a big “whole-project refactoring”, I want to start by creating a list of the things to improve – and then prioritize the changes.
The areas I want to look at are how to:
- Make the code easier to understand/work with
- Make the code less fragile (less likely to break during future changes)
- Improve the project’s automated testing
Identify complex areas of code
The first candidates for refactoring are the areas of code you hate to work in.
There are usually several classes/functions that you try to avoid. Whenever you, or another developer on your team, makes a change to that code, something goes wrong. That’s a big arrow, pointing to code that could use refactoring.
Next, you can use code analysis tools to show you areas of the program that have quality issues.
I use NDepend to analyze my projects. It’s an extremely powerful program, but it is expensive – 399 Euros for a developer license, 799 Euros for a server license. This will look for dozens of quality issues in your source code.
It can also create a heatmap to show which classes have the most quality issues. In this map, green is good, yellow is “warning”, and red is bad quality.
An alternative is SourceMonitor. It isn’t as powerful as NDepend, but it’s free.
In SourceMonitor’s results, I look for the classes with more than 15 or 20 methods, or methods with more than 10-15 lines. Those are often good candidates for refactoring into smaller/separate classes or functions.
Visual Studio can also calculate some code metrics for you. Open your solution and highlight the solution or project in the Solution Explorer. From the menu, click on Analyze -> Calculate Code Metrics -> For Solution or For Selected Project(s).
Look for classes and methods with large numbers for Cyclomatic Complexity, Class Coupling, or Lines of Code. Those may be good candidates for refactoring.
NOTE: Metrics “are a great servant, but a terrible master”. Use them to point you at areas that might need improvement. But, decide if the benefits of a refactoring will be greater than the costs, before making any changes.
When I first started doing formal Agile software development, we thought we needed to have 100% of our code covered by unit tests. We wasted a lot of time writing unit tests we didn’t need – just to meet the arbitrary 100% coverage.
What is cyclomatic complexity?
I’ve mentioned cyclomatic complexity several times. If you aren’t familiar with it, it’s a measurement of how many possible execution paths a function has.
If you have a function with one line of code, that is always run (no “if” statements around it), that function has a cyclomatic complexity of 1.
public int FunctionWithComplexity1(int param) { return 5 + param; }
If you surround that line of code with an “if” statement that checks one variable, the function has a cyclomatic complexity of 2. There are two paths through the code – one where the condition is true (in this example, where param is greater than 0), one where it is false (where param is less than, or equal to, zero).
public int FunctionWithComplexity2(int param) { if(param > 0) { return 5 + param; } return 5; }
As you add more conditional statements to a function, there are more paths through it, and the cyclomatic complexity grows.
A function with a large number of execution paths is often difficult to understand. So, it’s usually a good idea to simplify complex functions – or, at least break them into smaller functions.
Is there duplicate (or almost-duplicate) code?
Copy-pasting code is a huge problem in the applications I’ve seen.
Instead of having one function that’s used everywhere, programmers will copy that code into their new classes. When they need to make a change, they need to find every place they copied that code. If they miss one place, that class won’t behave like the other classes – which is usually a problem.
Finding duplicate code will show you code you might be able to combine into shared functions or classes.
If you have Visual Studio Enterprise (the paid version), you can search for duplicate code by clicking on the menu option Analyze -> Analyze Solution for Code Clones.
ReSharper has a free set of command line tools, including one you can run to check for duplicate code. Run dupfinder.exe against your .sln file (or any list of files you want to check) and it will identify duplicated code. It creates an XML file listing the files that have duplicate code, and the line numbers of the duplicated code fragments.
Is there “business logic” code in the UI?
It is usually very difficult to create automated tests that run through the UI. So, we end up manually testing our programs.
Manual testing takes longer than automated testing. Plus, it’s easy to forget a step, or to mis-type a value.
So, we want to move as much “logic” code to the class library projects. These don’t have a UI, and are easy to create
Refactoring changes I want to make in SuperAdventure
After looking over the existing SuperAdventure source code, these are the main changes I want to make. I’m sure I’ll find new ones as I make the refactoring changes.
- Better folder/namespace structure in Engine project
- Models/ViewModels/Utilities
- Add a ViewModel object
- Move some logic from Views and Models into the ViewModel
- Reduce scope of classes/functions/properties, where possible
- Remove boilerplate code
- PropertyChanged notification
- Improve/simplify large functions
- Move literal strings into resource file (for internationalization)
- Create unit tests
- Mock saved game read/write for XML and SQL
Tools to identify refactoring candidates
Telerik JustCode