Breaking up monoliths using CRC cards

Class Responsibility Collaborator (CRC) cards can provide a lightweight and Agile tool for refactoring and developing software design hypotheses. In this post, we explore how one of our product delivery teams used this object-oriented design technique to refactor more productively.

A rationale for CRC cards

Scenario

The Alchemists, one of the product delivery teams at Simply Business, recently completed a major project to refactor part of our monolithic codebase for our insurance brokerage service. The project involved determining which quotes and leads should be transferred to a third-party and when. Part of the development work to implement this scenario had already been carried out on our core Rails application. However, as this no longer aligned with our technology strategy of breaking up our large codebase into components, the team was faced with how best to refactor the code.

Migration and microsteps

We began by working on an architectural decision record (ADR) to define how to restructure the code and migrate it to one of our core gems – ‘Opportunity Knocks’ (hereafter referred to as ‘OK’). The refactoring work to move this code from the core application to the component was not trivial so we used the idea of ‘microsteps’ (World’s Best Intro to Test Driven Development – Joe Rainsberger) to move the code out gradually and eventually converge on the architecture envisioned in the ADR.

Visualising the big picture

One thing lacking, though, was a sense of the bigger picture – something the team was quick to point out. So when Tim Mackinnon visited our London office, we were inspired to try out a technique used to teach object-oriented design in the 1980s – Class Responsibility Collaborator (CRC) cards.

Developed by Ward Cunningham and Kent Beck, CRC cards are what may now be considered old-fashioned index cards, used to represent objects or classes. At the top of the card, the developer writes the name of the class that will solve a particular problem. On the left is an itemised list of responsibilities the class has, and on the right is a list of collaborators (other classes) that will help fulfil some of those responsibilities. Ward’s Wiki has more information on the CRC technique.

Learning and experimenting

Getting started

We began by writing CRC cards for our design as it currently stood and realised many immediate benefits; for example, difficulty in describing the responsibilities of some classes clearly and concisely.

We posted each card on our whiteboard, grouping classes and their collaborators together, which gave us the lay of the land fairly quickly. We then experimented with moving cards from the core application (Chopin) section of the whiteboard to the component (OK), to see if they made sense there.

This was great, because the cost of hypothesising and reasoning was almost zero. It enhanced the team’s agility, and uncovered potential difficulties in moving a particular class. All of this led to critical conversations with teammates, and was possible without us writing a single line of code!

Identifying refactoring opportunities

By using the CRC technique, we discovered some refactoring opportunities too: class naming, lots of collaborators and extracting classes, which we’ll discuss further.

Class naming

One class stood out in particular: TransferService. Its responsibilities were to:

  • transfer a customer to a broker;
  • increment a tally when the customer consented to be transferred to that particular broker; and
  • generate a reference that is passed on to that broker by our call centre consultant.

At the CRC card level, the class name wasn’t as accurate as it could be. This raised two questions: ‘Who/What was being transferred?’ (presumably the customer) and ‘To whom?’.

As a first pass, we drew up a new card with the same responsibilities and collaborators but with an improved class name – TransferToBrokerService, and created a pull request to refactor the TransferService. This stimulated some great naming suggestions from teammates and led us to identify a possible feature envy smell, which was tagged in code using SMELL comments. We could then use, say, Rubymine, to filter for SMELLs and address them easily!

Lots of collaborators

Look at the card representing the RequestFactory class below. What do you notice about it?

crc-request-factory

One thing that stood out was the number of collaborators. Any more and they wouldn’t fit on the card! That felt like a smell, and on looking at the tests, sure enough, there was a lot of complicated setup. The test was difficult to follow and resulted in another pull request to clean it up. Many collaborators were parsing objects from a form object, which prompted a hypothesis to extract a class that would be purely responsible for that.

Extracting classes

Have a look at the card representing the TransferToBroker class below:

crc-transfer-to-broker

It’s the one we renamed earlier. The trouble with this class is that it references low level detail (Daily Tally) in the component (OK). The Daily Tally object is used to keep count of how many customers have been transferred to a specified broker today. If the tally exceeds the maximum number of customers the broker can handle, our rules dictate that we transfer customers to BIBA, the British Insurance Brokers’ Association. Now, is it really the business of our core application (Chopin) to know this? Isn’t this a little bit leaky? This led to another refactor, to move the incrementing logic over to the component (OK).

sb-tech-site-technology

How do we do this in microsteps?

The first step was to group the logic to transfer the customer and increment the tally to another class, which is what the TransferToBroker card represents.

crc-transfer-to-broker-ok

Transfer To Broker (OK) is the new class that handles the transfer and tally update process. This microstep makes moving code to the component (OK) easier, as Kent Beck advises. Looking at the responsibilities now, they make much more sense in the core application. The final card looks like this. The responsibilities were easier to articulate and there weren’t too many collaborators required to fulfil them.

crc-transfer-to-broker-refactored

Lessons learned

One thing that can be difficult is collaborating on CRC cards with remote colleagues. A colleague in another of our product delivery teams has been experimenting with Miro to overcome this impediment.

Conclusion

This is our design in its entirety.

crc-result

In a follow-up post, we’ll look at addressing the feature envy smell identified in the TransferToBroker card and how to refactor in microsteps.

Ready to start your career at Simply Business?

Want to know more about what it’s like to work in tech at Simply Business? Read about our approach to tech, then check out our current vacancies.

Hemal Varambhia

This block is configured using JavaScript. A preview is not available in the editor.