Undoubtedly, revamps, rewrites, and refactors (will refer to these terms as RRR in this blog) pose considerable challenges due to various factors such as existing code complexities and information gaps. In this blog, we will delve into the intricacies of RRR without delving into specific definitions and implementation details. Ultimately, the common objective across these approaches is to improve upon the existing state of things.
When considering the possibility of engaging in RRR (revamps, rewrites, and refactors), several situations may arise:
Legacy Code: If you find yourself working with legacy code that has become difficult to maintain.
Framework Transition: When you are transitioning to a new state manager.
Design Changes: If the code requires new design changes while keeping the underlying logic intact.
Outdated Systems: Outdated components or technologies can also warrant the need.
Regardless of the specific reason, embarking on a code rewrite requires careful planning. Before diving into the planning process, it is crucial to identify and document certain aspects.
What do you want to RRR?
When considering RRR, it's essential to determine the scope: whether you want to do it on an entire set of screens or a bunch of components or a specific component. The decision may depend on various factors, such as the complexity of the codebase or the specific goals you want to achieve.
It's important to avoid overzealousness, where enthusiasm leads to unnecessary work. Sometimes, only a specific component or group of components requires attention, and it's crucial to assess the effort-to-reward ratio before proceeding. By evaluating the potential benefits and considering the level of effort involved, you can make informed decisions about the scope of your efforts.
Understanding if there is a real need to RRR?
Let's consider a specific example to illustrate the point. Imagine you want to perform RRR on the left-hand side (LHS) of a UI. However, during the identification process, instead of focusing solely on the green units (as shown in the picture below), you unintentionally spent time and effort on the outer container as well. This proves to be a wasteful endeavour.
Additionally, the outcome of this unnecessary work doesn't add much value since the parent or blue component that holds the green component was already well-written and didn't require any modifications. In the end, you might make only minor changes, improve some formatting, and claim self-praise for the work, even though the readability was already satisfactory.
It's important to avoid such situations where efforts are misdirected, and instead, focus on the specific areas that truly require attention and improvement. This helps optimize time and resources during the process.
Indeed, some may argue that there is nothing inherently wrong with performing touch-ups or minor improvements. While this perspective has its validity, it's crucial to consider the broader implications.
For instance, let's say you spend two full days on that touch-up. In doing so, you inadvertently exhaust your engineering bandwidth that could have been used for more impactful areas. By prioritizing these minor adjustments over other tasks with potentially higher rewards, we risk the overall efficiency and effectiveness of our work.
As engineers, it's essential to strike a balance between our nature and the ultimate goals of the product or project. While we may have an inclination towards perfecting every detail and adhering to best practices, it's important not to excessively "babysit" our code when it doesn't truly require our immediate attention.
By being mindful of this tendency, we can allocate our time and effort more strategically, focusing on areas that will yield the most significant benefits and contribute to the overall success of the project.
Once you have successfully identified the need, regardless of the specific reasons, and made the decision to proceed, it's time to channel that enthusiasm as Chad Engineers ๐ to the next step.
To ensure a smooth and efficient process, it's beneficial to have a checklist in place before.
These crucial steps should never be overlooked before diving into the process. I cannot emphasize their importance enough:
Thorough Testing: Prior to initiating, extensively use the product, features, or a feature to uncover and understand all potential edge cases and scenarios.
Code Review and Documentation: If you're a new engineer assigned to the task, carefully review the codebase and search for existing documentation (yes, documentation at times exists!). If documentation is lacking, check the test suite, nothing wrong in doing all of the mentioned.
Seek Guidance: If you're uncertain about any aspect, reach out to experienced team members, project managers, or individuals with relevant knowledge about the respective part of the project. Gathering their insights and context is crucial to ensure comprehensive coverage, even for rare scenarios that may affect a small percentage of users.
Edge Case Coverage: The primary objective of this exercise is to ensure that all edge cases, even those impacting a small user subset, are considered and addressed. This meticulous approach will save you from rework in a short span of time.
Trust me you won't like to revisit stuff just because you missed some things
๐ฅบ
Things to keep in mind while working:
Having gathered sufficient information about the piece you are about to refactor, the next crucial step is to assess if there are any design changes involved. If design changes are required, it's essential to ensure that these modifications do not compromise the underlying logic of the code. Simultaneously, strive for pixel-perfect implementation. Never forget to take design signoffs in writing.
Yes! In writing
, Thank me later when those sign-offs save you from an unwanted situation ๐ .Don't overlook the importance of existing analytics events. Those events are in place for a reason, so make sure you don't forget about them during the refactoring process. Trust me, explaining a sudden dip in the charts due to missing or incorrectly implemented analytics events doesn't look great when discussing metrics.
Additionally, it's crucial to ensure that proper tests are written and that QA sign-offs are obtained before shipping the new code to production. Even though you may have just touched a piece of code, it's essential to actively monitor the system after deployment. Keeping an eye on your monitoring tool, such as Sentry, is essential. While you may believe that nothing will break, it's always wise to have error handling in place as well, as human errors are common and can occur.
By paying attention to analytics events, conducting thorough testing, obtaining QA sign-offs, and actively monitoring the system, you can minimize the risk of issues and provide a more reliable and robust experience to users.
Consider implementing a thoughtful rollout strategy when introducing the refactored code to users. This can be accomplished through various methods, such as utilizing feature flags, remote configuration with a rollout percentage, or targeting a specific set of whitelisted users. While a 100% rollout strategy might seem tempting, it's important to exercise caution, as unforeseen issues can arise even when everything initially appears to be working fine. However, it's understandable that there may be situations where following a specific rollout strategy is not feasible. In such cases, it becomes imperative to prioritize robust error handling. By implementing proper error-handling mechanisms, you can minimize the impact of potential issues and ensure a smoother user experience.
Talking about error handling, it is also crucial to have a panic button or an easy rollback mechanism in place. This ensures that if something goes wrong during the rollout or after deployment, you can quickly revert back to the previous state with minimal impact on users. Prioritizing rollback capabilities provides a safety net and allows for swift resolution of any unexpected issues.
Moving on, documenting the newly written code is equally important. Just because you personally experienced the challenges of deciphering ๐ unreadable code or struggling to understand certain aspects doesn't mean you should leave it that way. Take the time to document the code and provide clear explanations. This not only helps others who may need to revisit the code in the future but also helps you avoid confusion when you revisit your own work (I have been in places where I always felt why did I write this piece of โฆ.. ๐ you get that right ?).
By prioritizing both the availability of a panic button for easy rollbacks and thorough documentation, you create a more resilient and maintainable codebase. These practices contribute to smoother development processes, improved collaboration, and better long-term code understanding (And surely help you get attention at work ๐).
This guide should provide you with a good foundation to plan and execute in various contexts. It is important to note that while this write-up covers the key aspects of the process, your specific case may require additional considerations and adaptations.