I don’t rate myself as much of an expert in IDEs, refactoring, history of programming, or in my ability to observe trends, but I still find these topics fascinating and I have had a bit of experience evaluating different products (mainly relating to C development) and will make an attempt at presenting my viewpoint. This is really just based on my own worm’s eye view of what is going during my few short years of observations, hindsight, past fallacies and many shortcomings.
Hopefully, it will provide a bit of fun reading to those who share these interests, and with a bit of luck it will raise some awareness of the value refactoring can bring to an IDE and how it can enhance one’s productivity.
IntroductionEmbed from Getty Images
One complaint I very often hear from new developers is that legacy code is really hard to work with. And they are right. When it was still new, using C to write firmware was a radical new step, and it needed to compete with some of the assembly code it was replacing. It was an order of magnitude more efficient to code in (fewer bugs and one could produce functionality faster, with fewer lines of code). However RAM and processing power were still very limited, so coding and getting the functionality to fit was more of an art than a skill, and whoever produced it smaller and faster was the hero. And whoever did this fastest got extra bonus points (as is the case today). People competed to see who could get the most functionality out of the least resources. Writing a convoluted procedure no-one else could understand was almost a badge of honour. Readability tended to go out the window.
An interesting and amusing side note is that complaints about legacy code is not a new phenomenon, it is something that has and will be always with us. Yesterday’s coders often complained about even older code, often written in Assembly, or written in an even less readable way. No doubt it is human nature to dislike old styles and designs – future programmers will doubtlessly have similar feelings of dread.
Why is Refactoring Needed?Embed from Getty Images
The reality is that a majority of the world still runs on generally unmaintainable legacy code and many of the really successful systems of today are built on code that was developed over a period of decades and the consequence is often a mixed bag of old and new code.
Product rewrites have been often been attempted, but seldom are they successful without a huge amount of pain and suffering, often resulting in products that take many years to mature and stabilize. From a user perspective, they are often inferior to the products they are meant to replace, so for better or for worse, we are often left to make the most of what we already have. Growing and replacing these products from the inside out is often the most successful strategy and this is where having a powerful IDE with good refactoring support really shines.
I have worked with a lot of legacy code and having battled with the readability and structure aspects thereof (or lack thereof), there is one activity I find my fingers itching to do over and over again. That is to make the code readable and understandable.
To be more specific, the types of issues usually include:
- badly named variables and functions,
- functions that are way too long,
- too many global variables,
- Hungarian notation,
- modules that include a mishmash of functions which share very little relationships,
- redundant blocks of code (dead code),
- functions with unexpected side effects,
- spaghetti code,
- copied and pasted code,
- variables with names such as “temp” used over and over again for different things,
I am sure you get the picture…
Something else I find myself wanting to do is to retrofit unit tests to legacy code. This is actually an incredibly difficult task with legacy code. This is because with this code not having been written in logically abstracted layers of manageable bite-size pieces, or in a way that is testable, it can be extremely difficult to simulate conditions that will exercise the functionality you want to test. It can also often be very difficult to monitor the outcome of tests when the intermediate states are not stored.
Michael Feather wrote an excellent book: Working Effectively with Legacy Code where he explains how to breath new life into old code by adding unit tests and thereby improving the structure, readability and maintainability. One feature he stresses as being important in an IDE (Integrated Development Environment) is having the ability to safely refactor.
IDEs to the Rescue:Embed from Getty Images
There are many IDEs, some of which I have evaluated. Over the past few years, I have seen a trend for the leading ones to introduce refactoring features, which has made me really excited. This has the obvious benefit of enabling one to safely and quickly improve code as one writes it, but it also gives new hope to the future of legacy systems. Unfortunately, this feature has been maturing at what feels like a snail’s pace, but it is still happening, and that is the important part.
From my experience, I would say the languages where IDE’s seem to have had the most traction in adding refactoring support (where the best results can be found) are Java and C#. The main benefits of these languages are that you can polish up and neaten your code as you write it. You can obviously also safely improve any badly written code (including legacy).
Focusing on C
Unfortunately, there are two really important languages where refactoring support has been lagging – C and C++ (probably because they are difficult languages to parse). However, there are a couple of examples of IDEs where reasonably good and usable attempts have been made. This is important since there are so much legacy C and C++ code (being much older than Java and C#) out there to be maintained and refactored.
To me there are a few types of refactoring features that add real value and distinguish good products from the rest:
- Entity renaming (Header Files, Functions, Parameters, Global variables, Local Variables, and Macros),
- Function Extraction,
- Changing the Order of Parameters in function calls,
Many IDEs claim to support refactoring, but I have found most of these either don’t work accurately (especially when it comes to function extraction), or only offer types of refactoring that are of little value.
In my opinion, the two leading IDEs for C and C++ development are Visual Studio and Eclipse CDT.
Out of the box, Visual Studio (VS) has almost no refactoring support for C and C++. However, I have found a plugin which I have been rather impressed by. This plugin is called Visual Assist. I have found it to be very good in all respects – easy to set up, usable, feature rich, many shortcuts, and most importantly accurate and reliable refactoring. The company that wrote it The Whole Tomato (I know, a weird name), has constantly improved their product and I would say that it is possibly one of the best in its class. I also like the fact that it is relatively affordable.
Out of the box, recent versions of Eclipse CDT (Luna 2 in this case) offers fairly good refactoring support, which has historically been marred by some quirks and rough edges as is common with Eclipse CDT. The latest versions have got a lot better, but there are a few things to be leery of:
- When renaming entities, occasionally not all instances get changed – some instances are mysteriously ignored,
- My guess is that the cause of this is Eclipse CDT’s slightly flaky indexing mechanism (which appears to occasionally miss things and also sometimes gets corrupt somehow – this can often be resolved by rebuilding the index).
- Eclipse renames most entities, then suddenly back-peddles and undoes most of its changes – weird and annoying,
- It very occasional corrupts one of the files and doesn’t let you know about it.
- fortunately, this can be easily resolved using the local Eclipse history. As long as you discover the corruption in time – the corrupted code will almost never compile, so compile often when refactoring.
- When extracting a function with pointers to variables, it sometimes gets the dereferencing, usually leaving the code in an uncompilable state.
- you need to manually fix whatever was not done correctly – usually a simple task.
- It doesn’t offer an automated way of changing the parameter order in a function.
- It is strange they haven’t bothered adding this feature since it seems like one of the more simple ones to implement.
As mentioned in a previous blog, I have found one plugin that has improved Eclipse CDT’s native refactoring support (based on past experience). This plugin is called CUTE, mainly known as a unit testing framework. However, in my experience, its main benefit is its enhanced refactoring support. Although it doesn’t add parameter reordering, this plugin has in the past helped to make Eclipse CDT work somewhat smoother and provide a more refined experience when it comes to refactoring. It is probably not too surprising, since from what I can see it is written by the same group that implemented the refactoring support that comes natively with Eclipse CDT.
So far, with possibly one exception, I haven’t found another IDE/Plugin combination that supports C/C++ refactoring to anywhere near the levels of sophistication and accuracy that Eclipse CDT (even with its rough edges) and VS offers when used in combination with the above-mentioned plugins.
The one exception I have found is Xrefactory for Emacs. I have tried this tool briefly some time ago and it gave reasonably good results. It is probably best suited to those more comfortable working with Emacs. I, however, did find that function extraction required a bit of extra rework compared to the other tools mentioned.
Conclusion:Embed from Getty Images
As every year goes by and with each new versions of a particular IDE, I have noticed a steady improvement in refactoring support. With most of my time being spent enhancing and growing products written predominantly in C, many of which are now many years old, there is always a need to constantly breath new life into them to keep them modern and competitive. I find the continued enhancements in refactoring support to be the most important trend in modern IDEs.
If anyone knows of other good refactoring tools that support C/C++ which I haven’t mentioned, I would be very curious to know about them.