Thirumanthiram, translated as Holy Incantation, has a famous hymn: மரத்தை மறைத்தது மாமத யானை, மரத்தின் மறைந்தது மாமத யானை. A partial explanation of the hymn is “For a skilled wood carver, a log of wood manifests as an elephant. The artisan reveals the hidden elephant, from the piece of wood and entertains the audience to appreciate the beauty”.
Einstein praised Mozart’s work as “was so pure that it seemed to have been ever-present in the universe, waiting to be discovered by the master.”
I’m neither a skilled crafts man nor a genius. Hence I need to resort to a different technique to compensate for this inability. The technique I use is the Test Driven Development.
Let us see one such situation today.
Context
An organization commissioned a new team to harvest certain details; from a corpus of information. The new team leverages various machine learning techniques to produce a set of result from the Medical Claim Records.
The results are CPT Codes harvested from Claim records.
The organization then decided to evaluate the accuracy of the new technique; by comparing the harvested CPT Codes with a known set of CPT Codes produced through manual operation.
The requirement is, as represented in the scenario table, we need to report the situations of matches and no matches between the Manual and Automated tasks.
The table represents four scenarios with colors for your reference. It appears that we have two kinds of matches; a perfect match and a soft match.
- A perfect match is when the automated value of a CPT Code matches exactly what that of the manual
- A soft match is when just the first segment of the automated CPT Code, meaning the 99214, matched with the first segment of manual CPT Code
- A conflicting case is when the first segment of the automated CPT Code matches with more than one first segment of the manual CPT Code
The First Test
Let us start with a simple test to observe, how this test influences the structure of a Reconciliation class. I represented the first scenario as a unit test here:
[gist https://gist.github.com/krsmanian1972/8df97eb42936acd23530acc3013e7857 /]
[gist https://gist.github.com/krsmanian1972/87810735cae2c7d217ff390bcb0a4624 /]
Observations – 1
- The test influences the way we wish to report the result of matching; We report the result as a Map
- The test helped us to discover the CPTCode class and helped us to devise a custom equal method to compare two CPTCodes
- The refactor cycle helped us to structure the reconcile method into two responsibilities; the perfect match discovery method and the result builder method
The Second Test
Let us introduce the second test and watch for the influences it makes in the fabric of our thought process.
[gist https://gist.github.com/krsmanian1972/1b77564ac9c0fc260b0eadc935b44f58 /]
[gist https://gist.github.com/krsmanian1972/48781d5e7dbf389feb908d343b3f5b75/]
Observations – 2
Sky is the canvas of the nature. It moves the cloud to form various earthly objects. When I failed to observe a formation my daughter points me about that.
Application of TDD, to evolve the design and transform a design is akin to this. A conscious programmer picks the hint and exposes the design that better suits the requirement.
The method difference offers the hint.
- First we look for perfect matches and filter them out. Refer the method difference that prunes the perfectly matched codes
- Then we look for soft matches on the filtered codes
Does this sound very familiar?
The Aha moment:
- We could sense the presence of the behavior pattern – chain of responsibility
- Similar to the authentication filters
- We could sense the presence of a hidden design of pipes and filters as well
- Similar to the shell command
- cat abc.txt | grep “java” | sort > xyz.txt
- If we extend this idea to our context, the flow of the resultant code, may look like:
- Reconciler | Perfect matcher | Soft matcher > ReconciliationResult
We can plumb these matchers as chains and finally inspect into the reconciliation result for matches and kind of the match.
The reconciliation result is the container, a connecting chord, that carries the information from one Matcher to the next Matcher in the chain.
The two test-cases (iteration) of the Reconciliation gave us the big-picture about the design. A typical advantage of the Outside-in. Now we design the individual matchers and chain them together from the Reconciliation class. Which means I preferred an Inside-Out for this context.
[gist https://gist.github.com/krsmanian1972/e048cfac05f7bccd4efd353b560dfb8b /]
[gist https://gist.github.com/krsmanian1972/40d2d2d8e8d5b1eaf8d124491e856a49 /]
[gist https://gist.github.com/krsmanian1972/27458366b30d1440bc5ceab926fa2d0c /]
Art of picking the Hints
- Keeping the methods with single indentation is the key to recognize the hidden design
- The methods with nested ifs and loops tend to fog the emerging design
- Method that does more than one thing shall be factored to throw light on the design
- Preferring the return value of the method under test as containers over primitives and collections
- During the later part of the code we decided to use ReconciliationResult over Map because we can offer more insights into the matching results and the kind of matches
Finally, as told by Robert.C.Martin, “as the tests become more specific; your code becomes more generic”
You can access the complete code for the situation from our git repository https://github.com/krsmanian1972/reconciliation
A simple challenge: I purposefully did not complete the chaining of matchers and leave this to the readers.
Write to you later about more situations.
Thanks for reading.