The Holy Java

Building the right thing, building it right, fast

Demonstration: Applying the Parallel Change technique to change code in small, safe steps

Posted by Jakub Holý on February 3, 2017

The Parallel Change technique is intended to make it possible to change code in a small, save steps by first adding the new way of doing things (without breaking the old one; “expand”), then switching over to the new way (“migrate”), and finally removing the old way (“contract”, i.e. make smaller). Here is an example of it applied in practice to refactor code producing a large JSON that contains a dictionary of addresses at one place and refers to them by their keys at other places. The goal is to rename the key. (We can’t use simple search & replace for reasons.)

The tests typically compare the produced and the expected JSON but the expected data isn’t hardcoded but is generated through helper functions, such as:

expect(data).to.deep.equal({
    ...expectAddresses({ billing: { /* .. */ }}) // this already uses the new key
    ...expectNotificationRecipients(),
    ...expectAccounts(),
    ...
});

That makes our job much easier. So here is the full process (I use “->” to indicate the change performed):

  1. (Expand) In prod code where we create the data, keep the old & add the new key:
    addresses["accounts_street_address"] = ...
    -> addresses["billing"] = addresses["accounts_street_address"] = ...
    => tests break
  2. In tests, modify expectAddresses() to create and thus expect both as well:
    addresses["accounts_street_address"] = { "street_address": expectStreetAddress(billing || all) };
    -> addresses["billing"] = addresses["accounts_street_address"] = { "street_address": expectStreetAddress(billing || all) };
    => tests fixed
  3. (Migrate) In prod code, use the new key when referring to the address:
    "address_reference_id": "accounts_street_address"
    -> "address_reference_id": "billing"
    => tests break
  4. In tests, fix expectAccounts() to expect the new key:
    "address_reference_id": "accounts_street_address"
    -> "address_reference_id": "billing"
    => tests fixed
  5. (Contract) In prod code, remove the old address:
    addresses["billing"] = addresses["accounts_street_address"] = ...
    -> addresses["billing"] = ...
    => tests break
  6. In tests, fix expectAddresses() to not expect the old address anymore:
    addresses["billing"] = addresses["accounts_street_address"] = ...
    -> addresses["billing"] = ...
    => tests fixed
  7. Fix two tests that access the address itself through its old name.
  8. DONE!
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

 
%d bloggers like this: