Last month we shipped an update to our website that didn’t add any features, but was a major refactoring for us. We moved the thousands upon thousands of user accounts in our system out of our old file “database” if you will, into a document database called RavenDB. But more importantly, we also completely redesigned the class representing the user account itself from the ground up using Domain Driven Design (DDD) techniques: entities, immutable values, repositories, etc.
The update was scripted and went off very smoothly (we were only down for about 20 minutes during the conversion, which also included an upgrade of the Raven database itself). But what’s more interesting to me is the approach we took in getting there. Instead of having a team go dark on a branch, do the redesign all at once, then do a massive merge and ship, we took an incremental approach, what Paul Hammant coined, Branch by Abstraction. Many shops do this without knowing about Paul’s article, which I found when we were in the middle of the process. Paul’s words helped give me courage that our approach was sound, and I wanted to share our success with our readers.
If you look at the object that represents a user account at Pluralsight, you’d see many facets. There’s the obvious id, first/last name, email, and such, but then there’s also important details like the user’s subscription level, expiration date, user preferences, a password verifier, and so on.
One of the first things we did in the new design is factor each of these bits of state into classes (value objects in DDD terminology) that grouped them together into cohesive units. Then we slowly started surfacing them via an abstract interface representing the account. We built integration tests that ensured we weren’t breaking anything, and that we could still properly serialize the account to and from the existing file system database, even as we were changing the in-memory representation to use these new value objects.
Incrementally over a series of many website releases (and several months), we would replace little bits of functionality with our new value objects. The clients of the abstract interface never knew this was happening, but we were slowly getting bits of our new design under test and into production, as we continued to release new features on the website. Then, once we were satisfied the new value object was functioning as it should on the live website, we’d go back in and surface the value object and change the client code to use the value object directly and push the website live yet again.
We finally got to the point where the only thing left to do was replace the usage of the abstract interface with the new UserAccount entity, and actually flip the switch and start serializing our new object in a more natural way and storing it in Raven. That was a pretty major undertaking and our team went dark for a couple weeks while we did that work, which also involved writing code to convert from the old file system database into the new format we were going to use in Raven. We also switched serializers – our file system database used XML serialization, while Raven uses JSON. All of this work, even the conversion code, was written test first. And we made sure to test the conversion on our staging server with a copy of our live data so that when we arrived at work on Mar 22, while we all had butterflies in our stomachs, we were very confident that we’d be successful.
How do you approach major refactorings like this? Leave a comment and let us know!