How we de-risked our editor upgrade
Any seasoned software engineer will tell you that full rewrites are a bad idea. More often than not, they are abandoned after wasting a significant amount of resources on them. This is especially true if what you're pitching for a rewrite is the backbone for one of the most ubiquitous components of your application. In this case, you have to come up with a very compelling reason as to why the rewrite is necessary — and an even better strategy to actually complete the task.
A problematic rewrite
We found ourselves in this exact situation two years ago. When we started work on our collaborative text editor in 2017, it was built on top of Slate version 0.27. Both Slate and our text editor grew up alongside each other. We even became active members of the Slate community as two of our engineers, Zach Schneider and Justin Weiss, worked on features and bug fixes for the upstream Slate project while building our editor.
Slate underwent an almost full rewrite and deprecated version 0.47, which our implementation depended on, a year after we finished our project. The main driver for the Slate overhaul was removing Immutable.js. It was a core dependency of the library that provided some benefits, yet introduced a lot of complexity and friction to the development process.
The cons outlined in this GitHub discussion align pretty well with our own experience working with Slate. When the new version was released, we decided to maintain our own fork of the library, fixing errors and bugs as they came up. Justin still participated in discussions related to the legacy version — but although the fork was public, we didn't upstream our changes. The areas where we were making these changes had shifted so much in the latest versions that they couldn't be directly applied in the main repository. We revisited the idea of upgrading periodically, always deciding that there were more pressing matters to focus on.
As time progressed, however, two problematic situations came up that stemmed from how we handled this project. First, browser updates would sometimes break the editor in unexpected ways, affecting a considerable percentage of our users and prompting us to go in and perform surgery on our fork. More importantly, the Slate-related code was fairly unapproachable and very hard to debug. This meant just a small number of engineers could make changes to the editor.
Coming to a decision
If I had to boil down the reasons why working on the editor wasn't attractive for us, I'd say it was the programming style it imposed. First, it was the only place in our codebase where we used Immutable.js, requiring a context switch from the paradigms we used on a daily basis just for one task. We had to relearn how to do basic things with immutable data structures, and even simple things (like printing out to the browser's developer console) became a pain to achieve.
When you got past that, you encountered a very opinionated plugin structure that required you to delegate to recursive next()
calls so all plugins in the chain would have a chance to execute. At best, this required developers to think long and hard about the plugin execution order so it would not interfere with other plugins. At worst, it led to impossible-to-debug errors where the stack trace would be composed of endless calls to next()
with very few clues as to what the problem actually was.
Judging by the discussion linked above, we weren't the only ones feeling this pain — the Slate rewrite aimed to reduce complexity by getting rid of Immutable. It was a radical departure from the original programming paradigms used, removing many of the strong opinions the library imposed on its users and allowing us to write the upgrade using patterns familiar to all Aha! engineers.
Lastly, legacy browser support required a lot of code to handle edge cases. With the passing of years and the official end-of-life of these browsers, some of the special cases became unnecessary, but we'd end up keeping the code around. When faced with the trade-off between handling complexity and breaking something for our customers, we'd always choose the users over ourselves.
The promises of better stability and a more approachable codebase were good enough reasons to consider upgrading to the latest Slate version — and consequently, rewriting everything related to our text editor. Lowering barriers to entry so any of our engineers could easily contribute features or bug fixes was exactly what we needed to make text editing within Aha! a truly lovable experience.
Looping in the team
Very early on, we decided to share almost every aspect of the project with our colleagues. I gave a number of talks about the upgrade process and the new architecture we were designing during our weekly engineering talks. I also co-hosted a deep dive into Slate at our December 2023 onsite in Hawaiʻi. Involving the whole team at every step of the process helped dispel the air of mystery that existed around the editor, providing everyone with knowledge surrounding the code. It also gave team members the option to weigh in on the different architectural decisions we were making while rebuilding our editor from the ground up. We were fortunate enough to still have Justin on our team, who has participated in the project as an adviser — providing valuable insights and context on some of the architectural decisions for the original editor.
Before embarking on such a challenging project, we needed to do our due diligence to ensure we could complete the upgrade successfully. We started with a proof of concept covering features based on three criteria: importance, complexity, and previously available features that we removed in the latest version.
Here is what we focused on:
Collaboration
Naturally, the most important functionality in a collaborative text editor is the collaborative part. Failing to produce a proof of concept for collaboration using the upgraded framework would have made the project a no-go from the start. An initial implementation that provided bi-directional collaborative editing between two clients — including cursor synchronization so each user could see where others were editing — was enough to tick this box and move on to evaluating the next one.
Tables
Web-based editable table components that feel like the ones you would use on desktop text editing software are very hard to produce. And as a result, the implementation tends to be proportionally complex. Ours wasn't the exception to this rule. But that complexity played in our favor here: We decided that a partial implementation of the table component would serve as a proxy to determine the feasibility of the other features.
Annotations
This one was somewhat specific to our case. Slate removed some functionality we were using when it transitioned to the current version. Specifically, it stopped providing the ability to annotate portions of a document out of the box. This meant we were responsible for providing this functionality ourselves, either through a third-party library or our own implementation. We ended up opting for the latter option for simplicity and better control, but either one would have been acceptable.
At the time of writing this, the project of upgrading our collaborative text editor is well underway. The de-risking process we designed early on has given us confidence that the project can be successfully completed despite the amount of work required. In addition, we've gone to great lengths to communicate the project's status, the patterns we've followed, and how much better the developer experience is in the upgraded editor versus the current one. The last point has been especially effective in piquing our colleagues' curiosity — leading them to express interest in contributing to the project. Even our CTO spent a few weeks hacking away at a not-insignificant number of features (and only stopped doing so because he had to attend to other matters)!
The final result
About two years after I first embarked on this journey — with the contributions of about half of our engineering team — we enabled the v2 version of the Aha! text editor on our internal Aha! accounts. We then performed some final testing and polishing before making it available to customers.
It's easy to miss some things with a project this large. There was a lot of tension as soon as we flipped the switch, and we kept a close watch for any error reports or indications that something had gone terribly wrong. But the editor worked as expected — all the effort we put into making sure everything went smoothly had paid off.
This was in no small measure due to the time we spent analyzing the risks and doing our best to remove them from the equation. However, I would dare say the key to this project's success was involving the whole engineering team from the get-go. By release time, 28 people had made contributions to the project. (That's over 60% of our engineering team!) This aspect was especially crucial in the last few months, when I was starting to run out of gas. Most teammates were already familiar with the project and could jump in and get it across the finish line.
Full-rewrite projects shouldn't be taken lightly. But they shouldn't be dismissed out of the gate, either. This will be our starting point next time we have to undertake such a project, and we think it could be helpful if you find yourself in a similar situation:
- Define the reasons why the rewrite is necessary.
- De-risk the most important parts first.
- Demo your progress constantly and invite discussion.
- Build curiosity and excitement among your colleagues.
To go boldly is one of our core values at Aha! But embodying this confidence doesn't mean being reckless. Ensuring risks are accounted for is a recipe for success while going boldly.
Ready to tackle your own tough projects with confidence? We have open engineering roles.