by Leonardo Lima
What I needed to do was quite simple and trivial: get some information from the page and send a request to a third-party tracking service. We were building an event tracker on the client side and tracking some page actions along with it.
The code examples below implement the same task using different code design tactics.
Day 1 — Using ES6 Class syntax (aka Object Prototype Pattern wrapper)
You can notice above that I started smart: isolating the generic tracker
SuccessPlanTracker to be reused in another page besides the Empty Index.
But, wait a minute. If this is an empty index tracker, what on earth is this foreigner
TrackNewPlanAdd doing there?
Day 2 — Getting rid of Class boilerplate code
Okay, now the file name clearly reflects the feature responsibility and, look at that, there is no more
EmptyIndexTracker class giving us less boilerplate code. Learn more here and here. We are using simple functions variables and, man, what a good catch using those shining ES6 Object Spread dots.
I find out that the querySelectorAll method already returns a List, so we are able to remove the
Array.from() function from
Array.from(document.getElementsByClassName('js-empty-index-tracking')) `. Remember that getElementsByClassName method returns an object.
Also, since the central responsibility is to bind HTML elements, the
document.addEventListener('DOMContentLoaded') function invocation doesn’t belongs to the file anymore.
Day 3 — Removing ES5 old practices and isolating responsibilities even more.
If you pay close attention, there is no
SuccessPlanTracker class in the code above — it suffered the same fate as the old
EmptyIndexTracker. The class-killing mindset, once installed, spreads and multiplies itself. But don’t fear, my good lad.
Don’t you think using the ES6 class abstraction was a little bit of overkill?
Did you also notice that I removed the variables instances from the top of the file? This practice remounts to ES5, and we don’t need to worry so much about it now that we have ES6+ syntax.
Finally, the last major change in this third version. Our empty index tracker binder now does only one thing: elements binding.
Following those steps brought the code very close to the Single Responsibility Principle — one of the most important SOLID principles.
Day 4 — Avoiding DOM sloppy manipulation
Hey, there are more lines now, you liar!
The thing is that our third version was a little broken. We were inappropriately mutating DOM Elements datasets in the line
properties.emptyIndexAction = button.dataset.trackingIdentifier;.
The property of one button was being passed to another button, generating messed up tracking events. To solve this situation, we removed the responsibility of assigning the
emptyIndexAction property from the binding loop to a proper function by creating its own scoped method
By adding those extra lines, we improved our code, better encapsulating each action.
Finally, to wrap up and write down
- If you want to design and write marvelous pieces of code, you need to be willing to explore further and go beyond the limits of a proper and modern syntax.
- Even if the first version of your code ended up being simple and readable, it doesn’t necessarily mean that the system has a good design or that it follows at least one of the SOLID principles.
- It’s essential to accept constructive code reviews and let other developers point out what you can do better.
- To keep your code simple, you need to think bigger.
Thank you very much for reading the article. Have another refactoring example or code review lesson to share? Please drop a comment below! Also, you can help me share this message with others by clapping and sharing it.
ProTip to-go: Here’s a very useful ES6 (ES2015+) cheatsheet
*Cheers to @anderson06 for being such a good code ‘pal giving me awesome feedbacks.