Software engineering is an ever-evolving industry with various challenges faced by engineers or programmers. One of such is writing scalable and maintainable code.
One of the approaches taken towards a maintainable codebase is the abstraction and reusability of code to prevent duplications everywhere in our code. This is commonly referred to as DRY (DON'T REPEAT YOURSELF).
Let's consider an example:
This function is a simple one. It takes in an object and an array of keys, and if the object holds any of the keys, remove that object entry.
This function belongs closely to this use case and can be used anywhere in our codebase to filter objects. GREAT!.
What if we need to add another use case where the function needs to filter and return a new object where the obj key is present in the keys array? In the spirit of DRY, we might do something like this (sure can be improved, but you get the gist).
Hurray!!, our function is still DRY and reusable, but is it maintainable?
We have added another layer of complexity to a simple function; hence, it is not doing one thing well. You might say, but it is a simple "if-else statement." The reality is, it is not "a simple if-else statement."
Considering additional use cases still revolving around these functions, we start having conditionals everywhere, and we create a monster function that is a nightmare for everyone.
In the words of Sandi Metz;
Duplication is far cheaper than the wrong abstraction. - Sandi Metz
This leads us to WET (Write Everything Twice).
Considering the above example, the above function, even though they are similar, would be better off if written where they are needed hence removing the unnecessary abstraction and can be easily maintained.
And
This way, we have two functions that can easily be maintained and understood without adding extra complexity.
A good abstraction reveals a pattern without adding so much complexity.
As a Thumb rule for me, once an if statement is present in my abstraction, I "Write Everything Twice."
What about AHA? It simply stands for "Avoid Hasty Abstraction."
If we followed this simple term in the example above, we would have discovered quickly that we were hasty with our abstraction, which would have led to a monster function overtime.
Let's consider an example: (Credit to Kent C. Dodds)
Assuming we have this typical state used everywhere in our codebase or file. Considering we have two other functions that update the state to Pending and Success.
We have written these functions, and as I said above, a good abstraction reveals a pattern.
We could abstract the states returned and have this below:
We avoided any hasty abstraction and allowed the pattern to reveal itself in this use case.
Hopefully, the next time you are writing your reusable function, you would consider DRY, WET, or AHA?
Thanks for Reading.
Top comments (1)
Please do not do external side effect in reduce callback. Use the accumulator
result