It is fair to say that in .Net community refactoring had a slow start. Even today, Visual Studio, a flagship product for development in .Net is hardly crossing the Refactoring Rubicon (http://www.martinfowler.com/articles/refactoringRubicon.html) with C#. With Visual Basic, or more recently with C++, a situation is better, but only if you download and install free refactoring add-in, Refactor! for VB or C++ developed by Developer Express.
After that, all alternatives are products you have to pay for. And while these products are certainly worth the investment, knowing the dynamics of corporate acquisitions when non-essential and esoteric arguments like "code quality" are concerned, they will have a hard time reaching anywhere near the developer mainstream. While you can refactor without a tool, manual process can be arduous and is definitely less attractive. No wonder than that .Net community is lagging behind in refactoring adoption and that there is still a lot of confusion regarding the whole issue of refactoring and its usefulness.
In this article, I have tried to list some of the misconceptions I come across the most often. These, together with some traditional programming preconceptions can often present an important hurdle in a process of adoption of this valuable technique. In continuation, I will list these preconceived obstacles, try to explain their origins and give good arguments in refuting them. I hope this will prove useful for anyone that still has a doubt on true nature of refactoring, to anyone learning to refactor or working on establishing and spreading this practice inside his team.
"If It Ain't Broke, Don't Fix It"
Often portrayed as longstanding engineering wisdom, "If It Ain't Broke, Don't Fix It" posture only promotes complacency. Refactoring teaches against it, and for a good reason.
Early on in your programming life you learn how even a minuscule detail in code can make huge difference, often paying dearly for this knowledge. A small change can provoke software to break in a surprising manner and at the worst of the moments. So once you have burned your hands you can often become reluctant to make any change that is not absolutely necessary. This can work well for a while. Inevitably however situation comes across where bugs have to be resolved and petitions for new features cannot be evaded anymore. You are faced with the same code you tried so hard not to confront.
Those who adopt "if it ain't broke, don't fix it" position look upon refactoring as unnecessary meddling with something that already serves its purpose. Actually, this conformist posture that tries to maintain the "status quo" is the result of intent to rationalize the fear of confronting the code and the fact that you do not have control over it.
Many experienced programmers adopt this posture because of legitimate intent to minimize any effort that is not really necessary. For example, if application is doing fine performance wise, then there is no need to squeeze that last processor cycle out for performance sake. Similar goes for speculative design, often argued with "We might need that feature one day" kind of prophetic programmer talk.
In that sense, refactoring is pretty much on the same wavelength. When you refactor, you should eliminate dead code, avoid speculative design or premature optimization.
However, for refactoring adepts, software can also be broken on the "inside". If the design is flawed, code poorly written and badly structured, then even if application is serving users correctly at the moment, and is not broken visibly on the "outside", it should still be refactored and design "fixed". In that sense, refactoring insists on some less tangible, but decisive characteristics of software like design, simplicity, improved source code comprehension, readability and alike.
Refactoring will help you win back the command over your code. While this is hardly an easy task with code base that has grown out of control, without refactoring the only solution you can look up to is the complete rewrite.
Refactoring Is Nothing New
This misconception could be restated as, "Refactoring is just another word for what we all know already." Which means you have all learned about good code, object-oriented design, style, good practices, and so on, and refactoring is just another buzzword that someone invented to sell some books.
Okay, refactoring does not pretend to be imposing radically new paradigms like object-oriented or aspect oriented programming when they appeared for the first time. What it does do is radically change the way you program: it defines rules that make it possible to apply complex transformations to code at the click of a button. You do not look at your code as some frozen construct that is not susceptible to change. Instead, you see yourself as capable of maintaining the code in optimum shape, responding efficiently to new challenges and changing the code without fear.
Refactoring Is Rocket Science
Programming is hard. It's a complex activity that requires a lot of intellectual effort. Some of the knowledge can be very difficult to grasp. With Visual Basic .NET, VB programmers had to acquire the ability to work in a fully capable object-oriented language. For many, this was baffling at first. The good part is that learning new skills definitely pays off.
The great thing about refactoring is how simple it can be. It equips you with a very small set of simple rules to start off. This, coupled with a good tool, makes first steps in refactoring a breeze. Compared to other techniques an advanced programmer should know nowadays, like UML or design patterns, I'd say refactoring has the easiest learning curve, a lot like VB itself compared to other programming languages.
Very soon, the time spent in learning refactoring will start to reap rewards. Of course, as with any other thing in life, gaining mastery requires a lot of time and effort.
Refactoring Causes Poor Performance
A longer way to state this might be, "Because after refactoring you usually end up with a larger number of more fine-grained elements like methods and classes, new design with so much indirection must incur some performance cost."
If you go back in time a little, you'll discover that this argument sounds curiously similar to the one used to voice initial skepticism toward object-oriented programming.
The truth is that the differences in performance between refactored and unstructured code are, at best, minimal. Except in some very specialized systems, this is not a real concern.
Experience shows that performance flows are generally afflicted by some precise spots in code. Fixing those during an optimization phase will get you the required levels of performance. Being able to easily identify the critical pieces of code can prove to be very valuable. By producing understandable code in which duplication and total size is minimized, where change can be performed in a single place, refactoring greatly aids process of optimization.
These days, when CPU horsepower is constantly incremented, some other aspects of code like maintainability, quality, scalability and reliability are forcing performance out of the top priority code features. Now, don't use this as a pretext to write code that performs lousy, just don't overdo it.
Just in case you would like to see some numbers here, I have prepared a small experiment. I will use two code samples; first is an example of poorly structured code and has single Main method. Second example has a Main method inside a Module and a class Circle with number of fine-grained methods. I have originally used these samples to illustrate unstructured vs. structured code style, so all it lacks is some measurement code.
I will measure the time it takes to execute simple geometrical formula (circle circumference length) and in order not to make out of this code calculation sensitive application, I will add some database query code. In order to even out the measurement, I will repeat execution inside a loop for 10000 times. Since I don't mean to make out of this extremely precise experiment, I will use System.Diagnostics.Stopwatch
class to capture the interval; precision of Stopwatch will suffice in this case.
Code Sample 1: Unstructured Code
Option Explicit On
Option Strict On
Imports System.Diagnostics
Imports System.Data.SqlClient
Namespace RefactoringInVb.Chapter9
Structure Point
Public X As Double
Public Y As Double
End Structure
Module CircleCircumferenceLength
Sub Main()
Dim center As Point
Dim pointOnCircumference As Point
'read center coordinates
Console.WriteLine("Enter X coordinate" + _
"of circle center")
center.X = CDbl(Console.In.ReadLine())
Console.WriteLine("Enter X coordinate" + _
"of circle center")
center.Y = CDbl(Console.In.ReadLine())
'read some point on circumference coordinates
Console.WriteLine("Enter X coordinate" + _
"of some point on circumference")
pointOnCircumference.X = CDbl(Console.In.ReadLine())
Console.WriteLine("Enter X coordinate" + _
"of some point on circumference")
pointOnCircumference.Y = CDbl(Console.In.ReadLine())
'calculate and display the length of circumference
Console.WriteLine("The lenght of circle" + _
"circumference is:")
'calculate the length of circumference
Dim radius As Double
Dim lengthOfCircumference As Double
Dim i As Integer
'use stopWatch to measure transcurred time
Dim stopWatch As New Stopwatch()
stopWatch.Start()
'repeat calculation for more precise measurement
For i = 1 To 10000
'add some IO
Dim connection As IDbConnection = New SqlConnection( _
"Data Source=TESLATEAM;" + _
"Initial Catalog=RENTAWHEELS;" + _
"User ID=RENTAWHEELS_LOGIN;" + _
"Password=RENTAWHEELS_PASSWORD_123")
connection.Open()
Dim command As IDbCommand = New SqlCommand( _
"SELECT GETDATE()")
command.Connection = connection
Dim reader As IDataReader = command.ExecuteReader()
reader.Read()
reader.Close()
connection.Close()
radius = ((pointOnCircumference.X - center.X) ^ 2 + _
(pointOnCircumference.Y - center.Y) ^ 2) ^ (1 / 2)
lengthOfCircumference = 2 * 3.1415 * radius
Next
stopWatch.Stop()
Console.WriteLine(stopWatch.Elapsed)
Console.WriteLine(lengthOfCircumference)
Console.Read()
End Sub
End Module
End Namespace
Code Sample 2: Structured Code
Option Explicit On
Option Strict On
Imports System.Data.SqlClient
Namespace RefactoringInVb.Chapter11
Public Structure Point
Public X As Double
Public Y As Double
End Structure
Module CircleCircumferenceLength
Sub Main()
Dim circle As Circle = New Circle
circle.Center = InputPoint("circle center")
circle.PointOnCircumference = InputPoint( _
"point on circumference")
Console.WriteLine("The length of circle " + _
"circumference is:")
Dim circumference As Double
Dim i As Integer
'use stopWatch to measure transcurred time
Dim stopWatch As New Stopwatch()
stopWatch.Start()
'repeat calculation for more precise measurement
For i = 1 To 10000
circumference = circle.CalculateCircumferenceLength()
Next
stopWatch.Stop()
Console.WriteLine(stopWatch.Elapsed)
Console.WriteLine(circumference)
WaitForUserToClose()
End Sub
Public Function InputPoint(ByVal pointName As String) As Point
Dim point As Point
Console.WriteLine("Enter X coordinate " + _
"of " + pointName)
point.X = CDbl(Console.In.ReadLine())
Console.WriteLine("Enter Y coordinate " + _
"of " + pointName)
point.Y = CDbl(Console.In.ReadLine())
Return point
End Function
Private Sub WaitForUserToClose()
Console.Read()
End Sub
End Module
Public Class Circle
Private centerValue As Point
Private pointOnCircumferenceValue As Point
Public Property Center() As Point
Get
Return centerValue
End Get
Set(ByVal value As Point)
centerValue = value
End Set
End Property
Public Property PointOnCircumference() As Point
Get
Return pointOnCircumferenceValue
End Get
Set(ByVal value As Point)
pointOnCircumferenceValue = value
End Set
End Property
Public Function CalculateCircumferenceLength() As Double
QueryDatabase()
Return 2 * 3.1415 * CalculateRadius()
End Function
Private Function CalculateRadius() As Double
Return ((Me.PointOnCircumference.X - Me.Center.X) ^ 2 + _
(Me.PointOnCircumference.Y - Me.Center.Y) ^ 2) ^ (1 / 2)
End Function
Private Sub QueryDatabase()
Dim connection As IDbConnection = New SqlConnection( _
"Data Source=TESLATEAM;" + _
"Initial Catalog=RENTAWHEELS;" + _
"User ID=RENTAWHEELS_LOGIN;" + _
"Password=RENTAWHEELS_PASSWORD_123")
connection.Open()
Dim command As IDbCommand = New SqlCommand( _
"SELECT GETDATE()")
command.Connection = connection
Dim reader As IDataReader = command.ExecuteReader()
reader.Read()
reader.Close()
connection.Close()
End Sub
End Class
End Namespace
After few executions, you can see that times for both samples are pretty close. On my machine these values are in neighborhood of 2.2 to 2.4 seconds. One small difference I could appreciate is that the best time for unstructured sample was 1.9114800, while for structured it was 2.0398497. Not a huge difference in my opinion.
Refactoring Breaks Good Object-Oriented Design
Well-structured and refactored code can look awkward to an untrained eye. Methods are so short that they often seem without substance. Classes seem without enough weight, consisting of only a few members. It seems as if nothing ever happens in your code.
Having to manage a greater number of elements like classes and methods can imply that there is more complexity to deal with.
This argument is actually misleading. The truth is that the same complexity was always present. In refactored code however, it is expressed in much cleaner, more structured manner.
Refactoring Offers No Short-Term Benefits
There is overwhelming consensus between refactoring converts that refactoring actually makes you program faster. So far, I do not know of any study that I could call upon in order to prove what I just said, but my own experience tells me this is the case. It is only logical that this is so. Because you have a smaller quantity of code overall, less duplication, and a clearer picture, unless you are dealing with some trivial and unrealistically small scale code, refactoring benefits become apparent very soon.
Refactoring Works Only for Agile Teams
Because it's often mentioned as one of the pillar techniques in agile methodologies, refactoring is interpreted as working only for teams adhering to these principles.
Refactoring is indispensable for agile teams.
Even if your team has a different methodology, most of the time you are the one in charge of the way you code, and this is where refactoring comes along. Other team members or management might even be oblivious to the fact that from time to time you reach for "Refactor" option inside your IDE. So there is nothing to prevent you from refactoring your code no matter the methodology your team might subscribe to. Best results in refactoring are achieved if you adopt refactoring in small steps, performing it regularly while you code. Some practices, like strict code ownership or a waterfall process, can play against refactoring. If you can prove that refactoring makes sense from a programming point of view, you can start building your support base, first with your peers and then by spreading the word to the rest of your team.
Refactoring can be applied as a separate stage in development process and performed by separate team
This one is generally favored by managers. Being able to look at refactoring as a separate stage, and placing it somewhere between implementation and testing phase, can give a false sense of control from managerial point of view where tasks and resources are often viewed as bars on some Gantt chart that can be easily squeezed or moved around.
The truth is that in order to perform refactoring successfully, you should have full understanding of the problem domain, be aware of requirements, the design and even implementation details. If you do not form the part of the team from the beginning, you did not spent time in working with clients, analyzing requirements and thinking about the design, you will have hard time improving something constructed by the original team.
Following the model where code can be refined a posteriori akin to some substance in some industrial process will generally bring few benefits. Without good understanding of what the code is actually doing, refactoring you can really perform with confidence will bring only small improvements. If you try to make any substantial change in such circumstances, results will most probably be quite opposite of what is supposed to be achieved. Instead of making code relate better to problem domain, you will probably make things worse and end up introducing bugs into your application.
Refactoring can work just as well without unit tests
I imagine some simple refactorings can be performed even without unit testing in place. Refactoring tools and compiler itself can bring limited safety net that you can count on to catch some simple human mistakes. You can also test the code in traditional ways, using debugger or performing functional tests. These manual testing methods tend to be tedious and unreliable. And with refactoring, your code is more susceptible to change than ever. Avoid unnecessary problems, add some NUnit tests to your project and you will be able to test for errors with each small step you make.
Not relying on comments can't be right
Guaranteed to produce those funny looks of confusion and disbelief. I'm sure you have been told a number of times that you should write comments in your code. This is thought as a good programming practice that will help others understand your code. It is often interpreted as a sign of good, systematic and professional approach to coding. So if now someone dares to tell you that comments are not such a good idea, then that must produce certain amount of surprise.
The motivation for writing comments is often same as for refactoring your code. You should write code that is readable by humans. In early days, your tool often had a limit on identifier character length that could be used so the comments were the only choice for delivering some meaning to other human beings. The difference is that form refactoring standpoint, you should use code itself to carry the meaning, by choosing correct method, class, variable and other identifiers. Also, you should avoid using comments for that same purpose because comments do not get executed so they can easily get obsolete. In a hurry of typical day-to-day toil, it is so easy to change the code and miss to update the comments, documentation, diagrams and other secondary artifacts.
What's wrong with Hungarian notation?
It must be that those who coined the term Hungarian notation didn't have only Charles Simonyi's native tongue as inspiration. I wouldn't be surprised if names like a_crszkvc30LastNameCol and lpszFile sounded like Hungarian to them. These days, compiler will take care of type safety and will track type information. In modern IDEs, all necessary type information can be found at the tip of your mouse pointer. In languages like C# and VB.Net, Hungarian notation has outlived its usefulness.
Yet again, changing some old habits can be painstaking. Hungarian notation is often viewed as useful programming practice, one that is not implemented without some effort. No wonder then that not everyone will be happy to find that his traditional naming scheme is largely obsolete.
Using easily readable name that resemble natural, human language will greatly improved clarity of your code. Give up those funny prefixes and use words that other humans can easily understand.
Conclusion
While it would be probably too much to say that refactoring is revolutionalizing the programming, it is changing some old habits ground-up. This is bound to provoke some resistance and a decent amount of confusion among the uninitiated.
I hope you will find this article helpful as you embark on learning refactoring. You should not be taken aback with some issues that might put you off at first reading. And if you are promoting refactoring, or trying to educate others about it, then you should be ready to be questioned. In that case, I hope I have provided you with enough arguments that you might use to justify the true reasoning behind refactoring and necessity for its adoption.
This article has been adopted from the book "Professional Refactoring in Visual Basic" written by Danijel Arsenovski and published by Wrox.
About the Author
Danijel Arsenovski is an author of Professional Refactoring in Visual Basic published by Wrox. Currently, he works as Product and Solutions Architect at Excelsys S.A, designing Internet banking solutions for numerous clients in the region. He started experimenting with refactoring while overhauling a huge banking system, and he hasn't lost interest in refactoring ever since. He pioneered the use of refactoring as a vehicle for a VB 6 code upgrade to VB .NET. Arsenovski is a contributing author for different major publications, holds a Microsoft Certified Solution Developer (MCSD) certification, and was named Visual Basic MVP in 2005. You can reach him at danijel.arsenovski@empoweragile.com, and you can take a look at his blog at http://blog.vbrefactoring.com.