Code Refactoring (Episode 2) - Changes For Biggest Bang for your Buck

Peter Cho, Senior Developer
#Development | Posted

This article is a continuation of Code Refactoring (Episode 1) - An Overview of Logical Approaches to Enhancing Software.

Most will cringe at the thought of refactoring their code; doing extra work that does not yield additional functionality or fixes to existing features.  However, there are hidden benefits to refactoring that might warrant your consideration:

  1. Lower maintenance cost

  2. Improved performance

  3. Updates to existing features for modern technologies

If you anticipate on working with the existing codebase for a long time, periodic refactoring would be a wise investment as it helps consolidate code to something more readable and understandable.  It also helps make future change requests much quicker and predictable to implement.

Reviewing existing code over again also benefits from enhancements that could improve performance overall.  As time goes by, developers continue to gain knowledge on new methods and techniques. New technologies and tools are introduced to the development space, doing away with obsolete and inefficient techniques and enhancing the way people use and implement certain features.  Without code refactoring, none of the existing code would benefit from these future enhancements, causing issues down the road.

And when it comes to updating core toolsets that may require syntax changes and removal of deprecated features, allocating resources to refactoring will help prevent an apocalypse of issues when a vendor announces a dead halt on their services within the next couple days causing the development team to find a workaround before the blackout date, which might add more unwanted hacks and bad practices into the system.

In a sense, code refactoring is a phase in a project that is considered a proactive approach to preventing long term problems in the future.  Just because something works now doesn’t mean that it requires absolutely no maintenance.

Eliminate Boiler-plating; Create your own Library of Helpers

Ever get angered watching a tv show that repeatedly airs previous scenes in the series multiple times and thought “ok, I get the point now”?  Ever read an instructional manual that had a long list of dialogue printed over and over again and thought “they could’ve just said ‘reference this section again’”?

Technically speaking, this is commonly known as boilerplating, the act of repeating a set of instructions in code multiple times with little or no deviation.  It’s undesirable in a sense that it increases the size of the files containing duplicate code, it’s a pain to read in person, and it also takes a long time to process.  When it comes time to update, for example, code that appears across numerous pages, it would take much longer to make the updates on multiple instances rather than one declaration (done via a subroutine, function, object functions, etc…) that affects the entire site.

To illustrate, reflect back on the example from the first post in the series.

Instructions on Building a Burger

  1. Walk down Elm St. from house
  2. Take a left and walk two blocks down Oak St.
  3. Turn right and find George’s Butcher.
  4. Walk up to counter and search for angus meat.  Instruct seller to package 1lb. of meat.
  5. Pay seller using your credit card.
  6. Walk away with meat.
  7. Walk two storefronts to the left of George’s Butcher and walk into Abby’s Produce
  8. Pick up convenient basket at entrance.
  9. Search for lettuce, collect one head of lettuce and deposit into basket.
  10. Search for tomatoes, collect one tomato and deposit into basket.
  11. Seek out cash register, place basket on counter, wait for cashier to process order
  12. Pay cashier using your credit card
  13. Walk away with produce
  14. Walk to intersection of Elm St. and Oak St. and walk back up Elm St.
  15. Enter your house
  16. Walk through hallway, into the dining room, and into the archway in the kitchen, then out the door to the patio.
  17. Place all ingredients in hand on patio table.
  18. Go back into kitchen, open drawer near sink, acquire knife.
  19. Walk back out to patio, take out tomatoes, place on top of table.
  20. Hold tomato upright, then cut tomato in half.
  21. Take one piece of tomato, cut in half
  22. Take one piece of tomato, cut in half
  23. Take one piece of tomato, cut in half
  24. Take one piece of tomato, cut in half
  25. Take one piece of tomato, cut in half
  26. Take one piece of tomato, cut in half
  27. Take one piece of tomato, cut in half
  28. Take all pieces of tomatoes and store in upper right corner of patio table
  29. Take out head of lettuce
  30. Peel outer layers and deposit into compost just outside patio
  31. Walk to grill, lift grate off top, place on ground
  32. Take charcoal nearby, then deposit contents of bag into grill
  33. Take lighter fluid nearby, spray charcoal with contents
  34. Take matches nearby, take out one stick from box.  Swipe red end on rough surface on box
  35. Carefully transfer the match to charcoal.  Jump back when fire starts
  36. Run to front of house
  37. Walk down Elm St. from house
  38. Take a left and walk two blocks down Oak St.
  39. Turn right and find George’s Butcher.
  40. Go to store front to the right of George’s Butcher
  41. Enter and search for ketchup
  42. Acquire ketchup
  43. Search for buns
  44. Acquire buns
  45. Go to counter. Place ketchup and buns on counter and let seller process transaction
  46. Pay seller with credit card
  47. Acquire ketchup and buns, and walk out of store
  48. Find nearby newspaper stand
  49. Find nearby vending machine
  50. Walk to vending machine, and deposit four coins into receptacle
  51. Push button for cola
  52. Wait 5 seconds
  53. Collect cola from vending machine
  54. Walk to intersection of Elm St. and Oak St. and walk back up Elm St.
  55. Enter your house
  56. Walk through hallway, into the dining room, and into the archway in the kitchen, then out the door to the patio.
  57. Find meat on patio, pick it up, and place on grill
  58. Wait 4 minutes
  59. Find spatula, and attempt to flip meat to other side
  60. Wait 4 minutes
  61. Use spatula to pick up meat from grill
  62. Walk to patio table with meat.
  63. Place meat on patio table
  64. Peel one piece of lettuce from head and place on top of meat.
  65. Take one piece of tomato and place on top of meat and lettuce.
  66. Open bottom of ketchup, and pour on top of meat, lettuce, and tomato
  67. Take all contents of the partially created “burger” product and place on top of one side of the bun
  68. Take other side of bun and place on top of all contents of partially created “burger”
  69. Done!

In summary, it seems like the instructions guide you to the marketplace a few times to get the necessary ingredients for the burger.  After the second iteration, you might think “why did I read those instructions again?  I got it the first time!”  Some people might benefit from the repetition, others might get annoyed by it.  But this poses a bigger issue in the event that all the businesses in that particular marketplace in the instructions went out of business, and you’re forced to go to the grocery store in a completely different direction.

We will get to acquiring all the ingredients the first time through later, but it would be kind of nice to shorten and consolidate the instructions into something that gets the point across more concisely, something like this:

  1. >> Go to marketplace (refer to How to get to marketplace)
  2. Turn right and find George’s Butcher.
  3. Walk up to counter and search for angus meat.  Instruct seller to package 1lb. of meat.
  4. Pay seller using your credit card.
  5. Walk away with meat.
  6. Walk two storefronts to the left of George’s Butcher and walk into Abby’s Produce
  7. Pick up convenient basket at entrance.
  8. Search for lettuce, collect one head of lettuce and deposit into basket.
  9. Search for tomatoes, collect one tomato and deposit into basket.
  10. Seek out cash register, place basket on counter, wait for cashier to process order
  11. Pay cashier using your credit card
  12. Walk away with produce
  13. >> Go back home from marketplace (refer to How to go home)
  14. Walk through hallway, into the dining room, and into the archway in the kitchen, then out the door to the patio.
  15. Place all ingredients in hand on patio table.
  16. Go back into kitchen, open drawer near sink, acquire knife.
  17. Walk back out to patio, take out tomatoes, place on top of table.
  18. Hold tomato upright, then cut tomato in half.
  19. Take one piece of tomato, cut in half
  20. Take one piece of tomato, cut in half
  21. Take one piece of tomato, cut in half
  22. Take one piece of tomato, cut in half
  23. Take one piece of tomato, cut in half
  24. Take one piece of tomato, cut in half
  25. Take one piece of tomato, cut in half
  26. Take all pieces of tomatoes and store in upper right corner of patio table
  27. Take out head of lettuce
  28. Peel outer layers and deposit into compost just outside patio
  29. Walk to grill, lift grate off top, place on ground
  30. Take charcoal nearby, then deposit contents of bag into grill
  31. Take lighter fluid nearby, spray charcoal with contents
  32. Take matches nearby, take out one stick from box.  Swipe red end on rough surface on box
  33. Carefully transfer the match to charcoal.  Jump back when fire starts
  34. Run to front of house
  35. >> Go to marketplace (refer to How to get to marketplace)
  36. Turn right and find George’s Butcher.
  37. Go to store front to the right of George’s Butcher
  38. Enter and search for ketchup
  39. Acquire ketchup
  40. Search for buns
  41. Acquire buns
  42. Go to counter. Place ketchup and buns on counter and let seller process transaction
  43. Pay seller with credit card
  44. Acquire ketchup and buns, and walk out of store
  45. Find nearby newspaper stand
  46. Find nearby vending machine
  47. Walk to vending machine, and deposit four coins into receptacle
  48. Push button for cola
  49. Wait 5 seconds
  50. Collect cola from vending machine
  51. >> Go back home from marketplace (refer to How to go home)
  52. Walk through hallway, into the dining room, and into the archway in the kitchen, then out the door to the patio.
  53. Find meat on patio, pick it up, and place on grill
  54. Wait 4 minutes
  55. Find spatula, and attempt to flip meat to other side
  56. Wait 4 minutes
  57. Use spatula to pick up meat from grill
  58. Walk to patio table with meat.
  59. Place meat on patio table
  60. Peel one piece of lettuce from head and place on top of meat.
  61. Take one piece of tomato and place on top of meat and lettuce.
  62. Open bottom of ketchup, and pour on top of meat, lettuce, and tomato
  63. Take all contents of the partially created “burger” product and place on top of one side of the bun
  64. Take other side of bun and place on top of all contents of partially created “burger”
  65. Done!

[/php]

 How to get to marketplace

  1. Walk down Elm St. from house
  2. Take a left and walk two blocks down Oak St.

[/php]

 How to go home

  1. Walk to intersection of Elm St. and Oak St. and walk back up Elm St.
  2. Enter your house

[/php]

I basically consolidated the instructions on getting to and from the marketplace from your house.  Of course, there are many other ways to shorten the code to something more readable and still performs the way it should.  To preserve your attention, I will further clean up our set of instructions (and assume everything prepended with a "  >>" references another set of instructions...just in case you forgot, for instance, to pay the merchant for his goods.

  1. Go to marketplace (refer to How to get to marketplace)
  2. >> Buy angus meat from George’s Butcher
  3. >> Buy lettuce and tomatoes from Abby’s Produce
  4. Go back home from marketplace (refer to How to go home)
  5. >> Go to patio
  6. Place all ingredients in hand on patio table.
  7. >> Find a knife
  8. Walk back out to patio, take out tomatoes, place on top of table.
  9. >> Cut tomatoes in half eight times
  10. Take all pieces of tomatoes and store in upper right corner of patio table
  11. Take out head of lettuce
  12. Peel outer layers and deposit into compost just outside patio
  13. >> Start a fire in the grill
  14. Run to front of house
  15. Go to marketplace (refer to How to get to marketplace)
  16. >> Buy ketchup and buns from store right of George’s Butcher
  17. Find nearby newspaper stand
  18. Find nearby vending machine
  19. >> Buy cola from vending machine
  20. Go back home from marketplace (refer to How to go home)
  21. Walk through hallway, into the dining room, and into the archway in the kitchen, then out the door to the patio.
  22. Find meat on patio, pick it up, and place on grill
  23. >> Cook meat
  24. Use spatula to pick up meat from grill
  25. Walk to patio table with meat.
  26. >> Assemble burger
  27. Done!

[/php]

This reads a little more easily even though I might have eliminated a lot of the detail on doing the steps.  It’s a little easier to go through 27 lines of instructions instead of 69, right?

You can imagine the steps I’ve consolidated (denoted by the "  >>") references another set of steps (or another page in the instructions).  This way, if someone were to write instructions on grilling a steak, they would be able to swap it out with another set of instructions easily.

These are often referred to as helper functions or libraries.  It may not make sense for a single script, but if you anticipate on doing something multiple times in the future like going to the store to buy eggs or cutting a vegetable, it might not be a bad idea to architect something that can be used multiple times in that fashion.  Most languages support the use of parameters to allow deviations to occur in the same set of instructions so that you don’t have to cater to every case (like if you want to buy ham instead of eggs or cut a piece of meat).

Code like a Gecko; Write Code to be Dispensable

Geckos are interesting creatures in that they have disposable body parts that they can regenerate.  So if some predator tries to snag them by the tail (for dinner for themselves or for their underlings), they can simply cut it off their system while they escape.  Over time, they can simple regrow the ones they lost to something better and stronger.

Similarly, once we refactor the code into little helper functions like we did in the previous section, it is easier to make adjustments to subsets of instructions without missing a spot.  Let’s look back on how we assemble a burger:

  1. Place meat on patio table
  2. Peel one piece of lettuce from head and place on top of meat.
  3. Take one piece of tomato and place on top of meat and lettuce.
  4. Open bottom of ketchup, and pour on top of meat, lettuce, and tomato
  5. Take all contents of the partially created “burger” product and place on top of one side of the bun
  6. Take other side of bun and place on top of all contents of partially created “burger”

[/php]

We previously mentioned that this seems a bit unhygienic and unsafe.  But looking at this is less intimidating than trying to find it through a large, monolithic script.  Ideally, we would want to change these instructions to something like:

  1. Place all ingredients on a plate
  2. Put one side of the bun facing upwards in the middle of the plate
  3. Place meat patty on top of upward facing bun
  4. Next pour ketchup on top of incomplete burger
  5. Next place lettuce on top of incomplete burger
  6. Next place a slice of tomato on top of incomplete burger
  7. Place other half of bun on top of incomplete burger

[/php]

Additional instructions or order of ingredients can be adjusted on a per user basis, but the good thing is that we know that it’s all contained in one small section of the code.  Therefore, future maintenance should be quick and easy (to an extent).  This not only makes future maintenance quick and easy (to an extent), it also makes it more predictable to estimate, too.

Keep in mind that going with this approach also allows for your codebase to connect with third party services.  In this case, if you feel that ABC chef is much more capable of assembling the burger or if ABC delivery services is better at acquiring the ingredients, you can simply re-implement the same function to reference the third parties and get rid of the old code once and for all (kind of like how a gecko drops his tail when a predator tries to grab him for dinner).

Most modern languages and architectures support such constructs, so there should be no excuse to break your system down into small, bite-sized pieces.  A system built in the 90s (or earlier) might struggle to incorporate this concept since the tools used provided this feature in a cumbersome manner.  If it’s been a long time since you’ve reviewed your code, it might not hurt to spend a little time refactoring it, or even determining whether upgrading your system to a new tool might be worth it.

After all, it does not sound too good spending hours on a task that ideally should’ve taken 10 minutes to take care of.

Designing your Helpers like a Vending Machine

Sometimes, it’s not a good idea to put all your eggs in one basket, because growing a beast could bite you back in later stages of development.  It’ll especially bite you if you anticipate on adding new developers on the project or if you lose the original creators for some reason.

It’s kind of like building a house.  If you’re the original architect, you’re probably the guy everybody should talk for quick answers on how the house was built.  But if you decide to jump ship and go traveling the world for two years, it’ll take some time for other architects to figure out how the house was built no matter how much documentation and blueprints you wrote for the project.

Therefore, it might not be a bad idea to try and modularize the system in smaller bits that fulfill simple tasks.  Instead of thinking of designing one big system that does everything, think of several smaller systems that, when working in unity, will achieve what you want.

Each system can be designed differently as long as it has means of providing its output in a standardized manner, mostly referred to as APIs in this case.  That way, developers are not limited to a specific framework, and have the ability to work with whatever tools they’re comfortable with and, more important, optimally fulfills the task at hand.

And just like the analogy I made in the last section, if the system has too many issues or is too obsolete for what is needed now, it can be easily disbanded and recreated to fit the new requirements, all with very minimal research and assessment on how it might affect the existing system since each component serves a small purpose in the ecosystem.

With that, designing a system in this fashion will allow for easier testing since the data put in and out of the system are more likely documented very well (and even if there’s no such documentation, it’s a lot easier to figure out than trying to make sense of a gigantic system).  In most cases, we refer to this as unit testing.

Initially, it might seem like additional work that adds absolutely no value to the functionality of the project, but spending extra time on writing a few tests to make sure the data put into the system is validated correctly and the system outputs consistent data will save you a ton of trouble in the future (since most issues that happen on production results from unexpected behaviors not caught during development).

 

"If it's not the code, you might want to change your data..."

 

In the old days, most people’s understanding of technology didn’t mature, and much of the data in the system were typically jumbled together or organized badly.  You can have the best designed system in the world with some of the best practices discovered by great computer scientists.  However, if the data in the system is too difficult to interact with, then you might not only have issues trying to work around it now, but also bigger issues later on when, for example, you want to migrate the data to the new system.

 Think of an abnormal sound you hear in your car or your house.  Sure, things might be working fine now, but not diagnosing the problem now might lead to bigger issues later.  If the issue is bad enough, it might cause some of the best experts to recommend that you abandon the place when something bad happens.

I am not saying that your data always has issues.  There is a chance it’s perfectly organized, so nothing needs to be done.  Though not all constructs are designed for every scenario.  Even though everything is modularized, you might have requirements that might force you to structure your data in a different way.

To simplify this, if your system has any of the following issues:

  1. Performance issues

  2. Runs out of space all the time

  3. Many functions that heavily process data (with less than 100% success)

...it might be a good idea to do an audit of your data.

Now this does not mean that you might be able to do something about it now unless you know the system well enough to make the adjustments needed to insure the data is in good condition.  If refactoring your system is not an option, it might not be a bad idea to incorporate fixes to your system to prevent bad data from getting created.  It’s always better to prevent things from happening rather than try to fix it later.  Some good examples of this might include preventing HTML code into a field or converting injected input to prevent the system from reading it as malicious code or trying to separate special elements like photos or video embed code from fields that only should have text.

Another thing to do if you anticipate on moving the data to the new system is to write some external scripts to organize them in a specific fashion, whether you need to move images out of a certain field and into another or delete markup from another module that is not being used anymore.  In most cases, most will do this when they migrate their data to a new system, but doing this can also be done to fix up data in existing systems for more predictable behaviors and better performance.

After all, it is much easier to reference a field in the database and get exactly what you want rather than spending hours trying to figure out every single edge case that exists with little or no certainty that the finished product fixes everything, right?

Stay tuned for Episode 3 - Hacking your Code for Readability for some ideas on making your code understandable and easier to maintain for team members in your project.

 

Peter Cho

Senior Developer