What is code refactoring and How Refactoring Resolves Technical Debt? comment down - refactoring

Code refactoring is defined as the process of restructuring computer code without changing or adding to its external behavior and functionality.
There are many ways to go about refactoring, but it most often comprises applying a series of standardized, basic actions, sometimes known as micro-refactorings. The changes in existing source code preserve the software’s behavior and functionality because the changes are so tiny that they are unlikely to create or introduce any new errors.
Comment down your thoughts.

Related

Advice on working with legacy code

I need some advice on how to work with legacy code.
A while ago, I was given the task to add a few reports to a reporting app. written in Struts 1, back in 2005. No big deal, but the code is quite messy. No usage of Action forms, and basically the code is one huge action, and a lot of if-else statements inside. Also, no one here has functional knowledge on this. We just happened to have it in our contract.
I'm quite unhappy about this, and not sure how to proceed. This application is invisible: Few people (but all very important) use it, so they don't care whether my eyes bleed while reading the code, standards, etc.
However, I feel that a technical debt is to be paid. How should I proceed on this? Continue down the if-else road, or try to do this requirement the right way, ignoring the rest of the project? Starting a huge refactor, risking my deadline?
Legacy code is a big issue, and I'm sure people will not agree!
I would say that starting a big re-factor could be a mistake.
A big re-factor means doing a lot of work to make it function exactly the way that it does now. If you choose to take this on on your own, there won't be a lot of visibility of what you are doing. If it works, no one will know the hours of work you put it. If it does NOT work, and you end up with tidy code, but add some bugs (and who has ever written code without adding some bugs) then you will get 'why did this change' type questions.
I have currently nearly completed a project working on a 10 year old code base. We have done quite a few bits of re-factoring along the way. But for each re-factor we have made we can justify 'this specific change will make the actual task we are doing now easier'. Rather than 'this is now cleaner for future work'. We have found that as we worked on the code, fixing the issues that we actually come up against one at a time, we have cleaned up a lot of it, without breaking it (much).
And I would say before you can re-factor much, you will need automated tests, so you can be fairly happy that you have put it back together right!
Most re-factoring is done to 'make maintenance and future development easier'. Your project sounds like there is not a lot of future development coming. That limits the advantage a re-factor will give the company.
Rule #1: If it ain't broke, don't fix it.
Rule #2: When in doubt, reread rule #1.
Unfortunately, legacy code can very rarely be described as "it ain't broke." Therefor we must tweak the existing code to correct a newly found bug, tweak the existing code to modify behavior that was previously acceptable, or tweak the existing code to add new functionality.
My experience has taught me that any refactoring must be done in 'infinitesimally' small increments. If you must break rule #2, I suggest that you start your search with the inner-most nested loop or IF structure and expand outward until you find a clean, logical separation point and create a new function/method/subroutine that contains only the guts of that loop or structure. This won't make anything more efficient but it should give you a clearer view of the underlying logic and structure. Once you have several new, smaller functions/methods/subroutines you can refactor and consolidate those into something more manageable.
Rule #3: Ignore my previous paragraph and reread the first two rules.
I agree with other comments. If you don't have to, then don't do it. It usually cost far more then it's worth if the code base is more or less dead any way.
On the other hand, if you feel that you cannot get your head around the code then a refactor is probably unavoidable. If this is the case then, since it's a web application, can you create a solid suite of functional tests using selenium? If so this is the fastest and most rewarding test approach for such code and will catch most bugs for the buck.
Second, start with the extract method refactoring to create compose methods of the big difficult methods. Every time you think to your self "This should have a comment to explain what it does" you should extract it to a method with a name that replaces the comment.
Once this has been done, if you still can't add the functionality required, you can go for more advanced refactorings, and perhaps even adding some unit tests. But I usually find that I can add what is required/fix the bug in legacy code by just creating self documenting code.
In a few words: before make any modifications to legacy code its good idea to start from automated unit tests.
This will give developer understanding about key things: dependencies this piece of code has, input data, output results, boundary conditions so on.
When it’s done most likely you will better understand what this code does and how it works.
After that its make sense (but not must) clean code a bit giving more accurate names to local variables, moving some functionality (repetitive code, if any) in to functions with clear human friendly names.
A simple clean up could make code more readable and at the same time save developer from regression issues with unit tests help.
Refactoring – make small changes, step by step, when you have a time and understanding of the requirements and functionality, regularly unit testing the code.
But do not start from refactoring

Refactoring and non-refactoring changes as separate check-ins?

Do you intermingle refactoring changes with feature development/bug fixing changes, or do you keep them separate? Large scale refactorings or reformatting of code that can be performed with a tool like Resharper should be kept separate from feature work or bug fixes because it is difficult to do a diff between revisions and see the real changes to code in amongst the numerous refactoring changes. Is this a good idea?
When I remember, I like to check in after a refactoring in preparation for adding a feature. Generally it leaves the code in a better state but without a change in behaviour. If I decide to back out the feature, I can always keep the better structured code.
Keep it simple.
Every check in should be a distinct, single, incremental change to the codebase.
This makes it much easier to track changes and understand what happened to the code, especially when you discover that an obscure bug appeared somewhere on the 11th of last month. Trying to find a one-line change amidst a 300-file refactoring checkin really, really sucks.
Typically, I check in when I have done some unit of work, and the code is back to compiling/unit tests are green. That may include refactorings. I would say that the best practice would be to try to separate them out. I find that to be difficult to do with my workflow.
I agree with the earlier responses. When in doubt, split your changes up into multiple commits. If you don't want to clutter the change history with lots of little changes (and have your revisions appear as one atomic change), perform these changes in a side branch where you can split them up. It's a lot easier to read the diffs later (and be reassured that nothing was inadvertently broken) if each change is clear and understandable.
Don't change functionality at the same time you are fixing the formatting. If you change the meaning of a conditional so that a whole bunch of code can be outdented, change the logic in one change and perform the outdent in a subsequent change. And be explicit with your commit messages.
If the source code control system allows it..
(this does not work in my current job due to the source code control system not liking a single user checking out a single file to more than one location.)
I have two working folders
Both folders are checkout from the same branch
I use one folder to implement the new feature development/bug fixing changes
In the other folder I do the refactoring,
After each refactoring I check in the refactoring folder
Then update the new feature development folder that merges in my refactorings
Hence each refactoring is in own checkin and other developers get the refactoring quickly, so there are less merge problems.

Removing tightly coupled code

Forgive me if this is a dupe but I couldn't find anything that hit this exact question.
I'm working with a legacy application that is very tightly coupled. We are pulling out some of the major functionality because we will be getting that functionality from an external service.
What is the best way to begin removing the now unused code? Should I just start at the extreme base, remove, and refactor my way up the stack? During lunch I'm going to go take a look at Working Effectively with Legacy Code.
If you can, and it makes sense in your problem domain, I would try to, during development, try and keep the legacy code functioning in parallel with the new API. And use the results from the legacy API to cross check that the new API is working as expected.
I think the most important thing you can do is to refactor/remove/test in very small chunks. It's tedious and time consuming but it will help limit risks and errors later on.
I would also start with code that is "low risk" to change.
My advice is to use findbugs and PMD/CPD (copy-paste-detector) to remove dead code (code that can not or will not be called) unused variables and duplicated code. Getting rid of this junk will make re-factoring easier.
Then learn the key mappings for the common re-factoring in your IDE. Extract method and introduce variable should be committed to muscle memory after an hour.
Use the primary disadvantage of tightly coupled code to... your advantage!
Step 1: Identify the area which provides the redundant functionality which you want to replace. Break it...do a quick smoke test of some of the critical parts of the application. Get the feel.
Step 2: Depending on what language it is find the relevant static-code analysis tools and get the needed refactoring info.
Step 3: Repeat Step 1 in incremental levels of narrowing down to the exact pattern.
All this of course, in a sandbox environment. This may seem a bit haphazard but if you limit yourself to critical functionality testing ... you may get many leads in the process. You will definitely identify the pattern of the legacy code, if nothing else.
You absolutely cannot do with with a live development version [new features being added]. You must start with a feature freeze.
I tend to look at all of the components of the system in an overview and see the biggest places of reuse. From there I would implement the appropriate design pattern to solve it, and make the new component reusable. Write test cases to ensure the new code works as expected, then refactor your code around the new change. Then repeat [overview, etc] till you are satisfied.
I would suggest this for many reasons:
Everyone working with you on refactoring will learn something
People learn how to avoid design mistakes down the road
Everyone working on it will get a better understanding of the code base

Inheriting applications at a new job

When inheriting applications at a new job do you tend to stick to the original developers coding practices or do you start applying your own?
I work in a small shop with no guidelines and always wondered what the rule was here. Some applications are written very well but do not follow the standards I use (variable names etc...) and I do not want to "dirty" them up. I find my self taking a little extra time being consistent.
Others are written very poorly and it looks like the developer was changing his mind every keystroke...
ADDITIONAL THOUGHT
What about when I start my own projects? So now I have introduced a new coding standard to the mix:
The good code - but not my style
The bad code with bad practices and lack of standards
My own standards
If there are standards evident in the code, you should stick to them. If there aren't, start introducing your own.
If there are multiple developers who work on the same module, don't change the style.
If you will hand it off to another developer in the near future (this role is temporary), don't change the style.
If you are taking complete, exclusive, permanent ownership of the module, change it, but follow these rules:
One change at a time.
Fix all indentation to your liking at once, and commit that change.
Fix all brace placement to your liking at once, and commit that change.
Fix all other formatting to your liking at once, and commit that change.
Fix all naming to your liking at once, and commit that change.
Don't spend a lot of time on it.
If it takes more than an hour or two, then cut back.
Make the commit description clear.
So you can quickly ignore these changes when analyzing change history.
Use automated tools
to make sure the result is consistent and complete, so you don't have to mess with it again.
Run your tests
Just because your changes shouldn't affect behavior doesn't mean they won't. (Triple negative, ouch!)
Make sure everyone knows what you're doing
Someone might have a change hanging around that they want to commit now, and it'll be painful to merge with your changes. Also, you don't want anyone to get surprised and go tell your boss before you do.
Don't do it again
This is a one-time thing.
Publish a style guide that follows best practices, and build consensus around following it. Refactor old code as you need to maintain it.
I'm in the same boat as you. Lone developer who inherited some apps from the last guy. I
I've been sticking to what appear to be his standards for existing projects for consistency, and using my own preferences for new stuff.
I've noticed that most people think whoever came before them had no idea how to write code. Then whoever comes after them thinks the same thing. Some things are common sense, but most things are just personal preference.
For major problems, i.e. using comments v.s. not using comments, updating the code will probably make it easier to work with, and easier for anyone else to work with. Even then, your time is probably best-spent updating the code as you come across it, instead of embarking on a huge project to refactor everything (introducing new problems in the process).
For things like indentation, line spacing, variable names, one-line ifs v.s. multi-line ifs, the reality is that your coding style is likely just as bad as you think theirs is.
I think it depends on what you mean by "coding practices". If you mean things like code formatting and naming conventions and things that I would personally consider "cosmetic", then stick with whats already there. If you mean things like coding best prcatices and writing code correctly in the first place, then go back and fix the problems if possible, but at the very least make your new code follow best practices.
Given that most of the applications I've inherited have been hacked together by "cowboy coders" who didn't apply even the most basic of coding practices, my opinion is a little biased.
I say introduce coding standards if there are none or the ones that exist are blatantly wrong and/or stupid (e.g. "All variables must be no more than 4 characters in length", "Every database column is varchar(255) null", etc.). Obviously if you have a team then you'll need to come to an agreement as to what practices to implement, but if you're a solo dev then you have free reign and IMO you should introduce order to the chaos.
If the code works, and seems to have had a clean format. Don't waste time changing the style.
If the code is badly written. By all means change it when you have some down time, or the next time you work on the project.
For new projects do them your way, since there is no standard. As with the other well written programs yours should be easy enough to maintain.
composition is often preferred over inheritance
:-P
If it's just you, go for it. If it's a team, especially if any of the original developers are still around (or likely to be called in for consulting), keep with the existing style and practices as much as you can. Don't follow them down a rat hole - if you think they're doing something stupid, change it, but if it's just a stylistic thing, keep to their style as much as you can.
On several jobs I've been on, we had no rule on coding style other than "if you're making changes to an existing file/class, use the existing style, even for new code."
I follow company standards if there are any.
If there aren't any and the changes are small, I adopt to the used style of coding.
If there are larger changes to be made and I don't like the coder's style, I will use my own.
And if the existing code is bad I will change that too.
Will you ever have a better opportunity to update existing code with a standard style? Probably not. When you are new to the code you are going to have the best chance of taking some extra time to make non-new-feature and non-bug-fix changes. The lack of standards may be discouraging but you are unlikely to have a better chance to standardize than when you first inherit the code.
It sounds like we're talking about a situation with no official style guides / best practices. In that case, as Sean said, I'd take the lead on establishing some. But... if at all possible, pick an existing, widely-used standard. It's more likely to be accepted, all the arguments are done with, and the odds of out-of-the-box tool support (editors, code review tools, etc.) greatly increase.
Getting others to adopt it will often work best from the bottom up -- write new code to the new standards, mention to others that you've done so, ask for feedback. Much easier than trying to get approval and buy-in in advance.
Within the existing, ugly project, avoid wholesale changes to existing modules. For one thing, diffs and version control will get quite confusing if a file is suddenly reindented.
If the chunk you're working on is so bad as to be unreadable, I'd do an initial checkin just to reformat it; follow that up with actual code changes.
I would apply the same refactoring standards to the code as I would if it DID match my style standards. That is, I'd ignore the style and just go on about my business.
If it's not terribly difficult to follow the style that is in the code - with regards to naming conventions, I'd go ahead and use those for new code.
However, I wouldn't bother trying to follow stuff like 'tabs should not be used', 'every line should be indented 2 spaces', etc. There are plenty of editors out there where you can 'pretty' the code whenever you need it these days.
G-Man
I think it depends highly on the specific case.
If you are a consultant on a project for a short time you should stick to the way thing are.
If you are on for a long time. Try to refactor bad code into your own scheme.
If you are on for a short time but you are working on an isolated module, then use your own scheme.
Short answer is, "It depends." Here are a few factors that I'd consider important in determining whether to keep the old style or not:
1) Scope of changes. If it is close to a total re-write of the application, then it may make more sense to put in a new standard if you have one that you feel works well for you.
2) Likelihood of future changes. Will this be changed over and over again? If so, then taking some time early on may well be worth it in the end. This does require a bit of judgement and predicting the future, but it may be easy in some cases to see that there will be changes over and over again for some systems that are fairly complex.
3) How much of the code is a customization on a 3rd party codebase, e.g. a company's specific customizations of Oracle products for their business processes, compared to a completely home grown application. The impact here is that when new versions are relased and an upgrade is requested, how much pain may there be on what breaks since it was customized so much.
When starting your own projects, put in the best standard that you know.
If I inherit code that has obviously never been refactored, I would take that as an opportunity to impose some of my own structure.
If people expect me to make time and cost estimates for adding functionality to the code, I'll need to be intimately familiar it, and make sure it lives up to my standards.
If the code is already well-written, that would be a blessing that I would not mess with. But in my experience, this hasn't happened very often.

What can you do to a legacy codebase that will have the greatest impact on improving the quality?

As you work in a legacy codebase what will have the greatest impact over time that will improve the quality of the codebase?
Remove unused code
Remove duplicated code
Add unit tests to improve test coverage where coverage is low
Create consistent formatting across files
Update 3rd party software
Reduce warnings generated by static analysis tools (i.e.Findbugs)
The codebase has been written by many developers with varying levels of expertise over many years, with a lot of areas untested and some untestable without spending a significant time on writing tests.
Read Michael Feather's book "Working effectively with Legacy Code"
This is a GREAT book.
If you don't like that answer, then the best advice I can give would be:
First, stop making new legacy code[1]
[1]: Legacy code = code without unit tests and therefore an unknown
Changing legacy code without an automated test suite in place is dangerous and irresponsible. Without good unit test coverage, you can't possibly know what affect those changes will have. Feathers recommends a "stranglehold" approach where you isolate areas of code you need to change, write some basic tests to verify basic assumptions, make small changes backed by unit tests, and work out from there.
NOTE: I'm not saying you need to stop everything and spend weeks writing tests for everything. Quite the contrary, just test around the areas you need to test and work out from there.
Jimmy Bogard and Ray Houston did an interesting screen cast on a subject very similar to this:
http://www.lostechies.com/blogs/jimmy_bogard/archive/2008/05/06/pablotv-eliminating-static-dependencies-screencast.aspx
I work with a legacy 1M LOC application written and modified by about 50 programmers.
* Remove unused code
Almost useless... just ignore it. You wont get a big Return On Investment (ROI) from that one.
* Remove duplicated code
Actually, when I fix something I always search for duplicate. If I found some I put a generic function or comment all code occurrence for duplication (sometime, the effort for putting a generic function doesn't worth it). The main idea, is that I hate doing the same action more than once. Another reason is because there's always someone (could be me) that forget to check for other occurrence...
* Add unit tests to improve test coverage where coverage is low
Automated unit tests is wonderful... but if you have a big backlog, the task itself is hard to promote unless you have stability issue. Go with the part you are working on and hope that in a few year you have decent coverage.
* Create consistent formatting across files
IMO the difference in formatting is part of the legacy. It give you an hint about who or when the code was written. This can gave you some clue about how to behave in that part of the code. Doing the job of reformatting, isn't fun and it doesn't give any value for your customer.
* Update 3rd party software
Do it only if there's new really nice feature's or the version you have is not supported by the new operating system.
* Reduce warnings generated by static analysis tools
It can worth it. Sometime warning can hide a potential bug.
I'd say 'remove duplicated code' pretty much means you have to pull code out and abstract it so it can be used in multiple places - this, in theory, makes bugs easier to fix because you only have to fix one piece of code, as opposed to many pieces of code, to fix a bug in it.
Add unit tests to improve test coverage. Having good test coverage will allow you to refactor and improve functionality without fear.
There is a good book on this written by the author of CPPUnit, Working Effectively with Legacy Code.
Adding tests to legacy code is certianly more challenging than creating them from scratch. The most useful concept I've taken away from the book is the notion of "seams", which Feathers defines as
"a place where you can alter behavior in your program without editing in that place."
Sometimes its worth refactoring to create seams that will make future testing easier (or possible in the first place.) The google testing blog has several interesting posts on the subject, mostly revolving around the process of Dependency Injection.
I can relate to this question as I currently have in my lap one of 'those' old school codebase. Its not really legacy but its certainly not followed the trend of the years.
I'll tell you the things I would love to fix in it as they bug me every day:
Document the input and output variables
Refactor the variable names so they actually mean something other and some hungarian notation prefix followed by an acronym of three letters with some obscure meaning. CammelCase is the way to go.
I'm scared to death of changing any code as it will affect hundreds of clients that use the software and someone WILL notice even the most obscure side effect. Any repeatable regression tests would be a blessing since there are zero now.
The rest is really peanuts. These are the main problems with a legacy codebase, they really eat up tons of time.
I'd say it largely depends on what you want to do with the legacy code...
If it will indefinitely remain in maintenance mode and it's working fine, doing nothing at all is your best bet. "If it ain't broke, don't fix it."
If it's not working fine, removing the unused code and refactoring the duplicate code will make debugging a lot easier. However, I would only make these changes on the erring code.
If you plan on version 2.0, add unit tests and clean up the code you will bring forward
Good documentation. As someone who has to maintain and extend legacy code, that is the number one problem. It's difficult, if not downright dangerous to change code you don't understand. Even if you're lucky enough to be handed documented code, how sure are you that the documentation is right? That it covers all of the implicit knowledge of the original author? That it speaks to all of the "tricks" and edge cases?
Good documentation is what allows those other than the original author to understand, fix, and extend even bad code. I'll take hacked yet well-documented code that I can understand over perfect yet inscrutable code any day of the week.
The single biggest thing that I've done to the legacy code that I have to work with is to build a real API around it. It's a 1970's style COBOL API that I've built a .NET object model around, so that all the unsafe code is in one place, all of the translation between the API's native data types and .NET data types is in one place, the primary methods return and accept DataSets, and so on.
This was immensely difficult to do right, and there are still some defects in it that I know about. It's not terrifically efficient either, with all the marshalling that goes on. But on the other hand, I can build a DataGridView that round-trips data to a 15-year-old application which persists its data in Btrieve (!) in about half an hour, and it works. When customers come to me with projects, my estimates are in days and weeks rather than months and years.
As a parallel to what Josh Segall said, I would say comment the hell out of it. I've worked on several very large legacy systems that got dumped in my lap, and I found the biggest problem was keeping track of what I already learned about a particular section of code. Once I started placing notes as I go, including "To Do" notes, I stopped re-figuring out what I already figured out. Then I could focus on how those code segments flow and interact.
I would say just leave it alone for the most part. If it's not broken then don't fix it. If it is broken then go ahead and fix and improve the portion of the code that is broken and its immediately surrounding code. You can use the pain of the bug or sorely missing feature to justify the effort and expense of improving that part.
I would not recommend any wholesale kind of rewrite, refactor, reformat, or putting in of unit tests that is not guided by actual business or end-user need.
If you do get the opportunity to fix something, then do it right (the chance of doing it right the first time might have already passed, but since you are touching that part again might as well do it right time around) and this includes all the items you mentioned.
So in summary, there's no single or just a few things that you should do. You should do it all but in small portions and in an opportunistic manner.
Late to the party, but the following may be worth doing where a function/method is used or referenced often:
Local variables often tend to be poorly named in legacy code (often owing to their scope expanding when a method is modified, and not being updated to reflect this). Renaming these in line with their actual purpose can help clarify legacy code.
Even just laying out the method slightly differently can work wonders - for instance, putting all the clauses of an if on one line.
There might be stale/confusing code comments there already. Remove them if they're not needed, or amend them if you absolutely have to. (Of course, I'm not advocating removal of useful comments, just those that are a hindrance.)
These might not have the massive headline impact you're looking for, but they are low risk, particularly if the code can't be unit tested.

Resources