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

Sorry, the comment form is closed at this time.

 
%d bloggers like this: