Convert your FHIR JSON -> XML and back here. The CDA Book is sometimes listed for Kindle here and it is also SHIPPING from Amazon! See here for Errata.

Sunday, August 14, 2016

Well, Shit.

My twitter feed is all abuzz this morning on the death of @jess_jacobs. Jess is a woman with a challenging illness who documented some of the complete failures of our health system to provide her with even barely adequate care  I've met her briefly about four times in my life. Her death saddens me this morning, not because I knew her well, but because she did great things with her life, and because we share at least one thing in common, our jackets. But in some ways I feel the way that others do when their favorite celebrity dies.

Among many of my friends Jess IS a celebrity.  But outside our world, she is not known well enough, nor is her story told often enough.  So I will walk today in honor of Jess, and instead of telling my story, I will tell hers.

   Keith










Friday, August 5, 2016

Offering an Informal FHIR Chat, Whitfield area, Bengaluru, India

I'll be in Bangalore for about 10 days later this month, to work with several of my teams during the week of the 22nd, and to deliver some standards training internally the following week.  One of my architects suggested that we set up an informal FHIR chat on the weekend I'll be staying through, either Saturday, August 27th, or Sunday August 28th for folks in India who want to learn more about FHIR.

Timing is too tight for me to arrange a venue through any sort of official channels, however, others in the region might be able to put something together.

So, here is the offer.  I'm free for the entire day either Saturday or Sunday, and can deliver an overview of HL7 FHIR for developers in India.  If you are interested in helping to pull this together, please either leave a comment for me here, or e-mail me at keith.boone@ge.com.

   Keith

Friday, July 29, 2016

Round tripping identifiers from CDA to FHIR and back

I think I solved this problem for myself last year or so, or else the answer wouldn't be so readily available in my brain.

The challenge is this: You have an identifier in a CDA document.  You need to convert it to FHIR. And then you need to get it back into CDA appropriately.

There are four separate cases to consider if we ignore degenerate identifiers where nullFlavor is non-null.
  1. <id root='OID'/>
  2. <id root='UUID'/>
  3. <id root='OIDorUUID' extension='value'/>
For case 1 and 2, the FHIR identifier will have system =  urn:ietf:rfc:3986, and the value will be urn:oid:OID, or urn:uuid:UUID.
For case 3, it gets a tiny bit messy.  You need a lookup table to map a small set of OIDs to FHIR URLs for FHIR defined identifier systems.  If the OID you have matches one of the FHIR OIDs in that registry, use the specified URL.  Otherwise, convert the OID to its urn:oid form.  If it is a UUID, you simply convert it to its urn:uuid form.

Going backwards:
If system is urn:ietf:rfc:3986, then it must have been in root-only format, and the value is the OID or UUID in urn: format.  Simply convert back to the unprefixed OID or UUID value, and stuff that into the root attribute.

Otherwise, if it is a URL that is not in urn:oid or urn:uuid format, then look up the identifier space in the FHIR identifier system registry, and reverse it to an OID, and put that into root.  Otherwise, you just convert back to the unprefixed OID or UUID value, and stuff that into root.  In that case, the extension attribute should contain whatever is in value.

So now then, you might ask, how do I represent a FHIR identifier that is NOT one of these puppies in HL7 CDA format.  In other words, I have a native FHIR identifier, and CDA had nothing to do with generating it.  So, there's a system and a value, but no real way to tell CDA how to deal with it.  To do that, we need a common convention or a defined standard.

So, pick an OID to define the convention, and a syntax to use in value to represent system and value when system cannot be mapped to an OID or UUID based on the above convention.  In this manner you can represent a FHIR identifier in CDA without loss of fidelity because CDA does not provide any limits on value.  Oh, and modify the algorithm above to handle that special OID in case four.

I'll let HL7 define the standard, select the OID, and specify the syntax.  I have better things to do with $100 than register an OID for this purpose.  But clearly, it could be done.

   Keith





Tuesday, July 26, 2016

Don't ask them to tell me what I should already know

This particular issue shows up quite a bit in standards based exchange, and frankly drives me a bit crazy.  Somewhere in the message someone asks for several correlated pieces of information to be communicated.  A perfect example of this is in the HITSP C32 Medication template.  We had to provide an RxNORM code for the medication, a code for the class of medication, and a code for the dose form within the template.  We also had to provide places to put the medication brand and generic names. Folks insisted that all of this information was useful, and therefore must be communicated in the exchange.

However, we used RxNORM codes to describe the medication.  Have you ever looked at the content in the RxNORM Vocabulary?  If I gave you the RxCUI (code) of 993837 in a communication, here's what RxNORM will tell you about it.


Within the terminology model, I can give you the precise medication ingredients and doses within each tablet, tell you that it is in tablet form intended for oral use, identify the brand name, and generic form.  Now, what where you going to do with all of that other information you asked me to code?

Having redundant information is helpful as it helps you spot errors.  If the code is 993837 and the reported medication is something other than Tylenol #3 or Acetaminophen 300 mg/ Codeine 30mg, then there is a problem.  So, it is helpful to have SOME redundancy.  But when all those other codes are also present, the system needs as much knowledge as is already in RxNORM to produce that information, and we just lost some (if not most) of the benefits of using a vocabulary in exchanging the information.

There's so much redundancy in the coded and fielded information in the HITSP C32 Medication template as to be ridiculous (and while I argued against it, I did not succeed).  The RxNORM code is all you need, and just to be sure that the sender knows what it is talking about, one of either the brand name, or clinical name of the drug. Everything else after than is redundancy UNLESS you can identify a specific use case for it.

In an information exchange, you should pay attention to exchanges that duplicate already existing knowledge about real things in the world, especially when knowledge bases such as RxNorm exist. The need to exchange world knowledge between systems exists when the receiver of the communication cannot be expected to be readily aware of all of that world knowledge.  If I ask you to get rid of the dandelions in my yard, it doesn't really help a whole lot to tell you to get rid of the yellow dandelions, unless I have some very specialized varieties of dandelions or I've been watering them with food die.

If you are expecting someone to transmit information that can be inferred from world knowledge, ask yourself if that is truly necessary.  You should always include enough redundancy to enable a receiver to ensure that the sender knows what it is talking about, but don't include so much that a receiver would be overwhelmed, or the sender would basically be duplicating the content of a knowledge source.  After all, we have reference works and reference vocabularies so that we can look things up.

   Keith

Tuesday, July 19, 2016

Do you have the vision to use HealthIT standards?

One of the challenges of meaningful use is in how it has diverted attention from other uses of standards that could have value.  Use of the Unstructured Document template provides one such example.  Use of unstructured document specifications, either from IHE Scanned Documents (XDS-SD), or CCD-A Unstructured Document supports exchange of electronic text that is accessible to the human reader (CDA Level 1), but not in structured form (CDA Level 3).  A common use case for this kind of text is in dictated notes, something we still see an awful lot of, even in this nearly post-Meaningful Use era.

Some even incorrectly believe that one cannot uses these specifications because they are "forbidden" by meaningful use.  While users of these specifications cannot claim that use towards qualification for meaningful use, that program is NOT specifying what they can or cannot do elsewhere to improve care.  And again, while use of these specifications do not count towards Certification of EHR systems under the 2015 Certification Edition criteria, again, certification is the lower bar.  You can always do more.

There are a number of benefits for using unstructured documents in exchanges where structured detail is not present.  One of these is simply to make the text available to systems that can apply Natural Language Processing technology to the text to produce structured information.  I've worked on two different systems that were capable of doing that with high reliability, and there are commercial systems available to do this today.  This sort of use can be applied to clinical care, or to so-called secondary uses (often related to population-based care or research).

Provider organizations won't ask for this specification by name, rather, they will ask for the capabilities that it supports.  This has always been the problem of standards.  Meaningful Use, MIPS and MACRA eliminate that problem by making systems directly responsible for implementing standards, and making providers care about that by affecting their pocket book when they use systems that do not.

The challenge for good systems developers and product managers is in applying standards like these to system design, without having their customers asking for it directly.  That takes vision.  Do you have the vision to use standards that you aren't required to?  

Friday, July 15, 2016

A FHIR Resistant Guantlet

Someone asked me for my opinion on adopting STU3 resources in an environment that presently supports STU2.  At first this seems to be quite challenging.  But then, as I thought about it, the following idea occurred to me:

It would be a simple matter of engineering to take an STU3 StructureDefinition, and re-write it as an STU2 StructureDefinition that is a profile on an STU2 Basic resource. Such a structure definition would be ideally suited for transfer to an STU3 environment when it is available, but would work in an existing STU2 environment today.

It eliminates one of my objections to pre-adoption of new resources, uses the Basic resource in a way it is intended to be used (to prototype new stuff), and provides a useful way to test new stuff in existing environments.

I don't have the time personally to write such a tool, but would love to see someone take up this gauntlet I just threw down.

-- Keith

P.S.  Such a tool could also support changed resources, if the tooling was smart enough to understand certain kinds of changes.  It could create extensions for new fields added, restrict fields that are removed, and ignore those for which there were simple name changes (detectable perhaps through a combination of w5, V2 and V3 mappings).

Monday, July 11, 2016

Building software that builds software enforces quality

There's an interesting discussion over on the AMIA Implementation list server about software quality. As is often the case in many of my posts, it intersected with something I'm currently working on, which is a code generator to automate FHIR Mapping.  Basically I'm building software that builds software.

You find out a lot of things when you do that.  First of all, it is difficult to do well.  Secondly, it's very hard to create subtle bugs.  When you break something, you break it big time, and its usually quite obvious.  The most common result is that the produced software doesn't even compile.  The reason for this has to do with the volume of code produced.  A software generator often creates hundreds or thousands of times more software than went into it.  And even though it is difficult to do well, when you do it that way, you can produce 10, 20 or even 100 times the code that you would otherwise, with extremely high quality.

Software generators are like factories, only better, in this way.  If a component on the assembly line is bad (a nut, a bolt, et cetera), that results in a minor defect.  But ALL of the materials produced by a software generator with a very small exception are created by automation.  And a single failure anywhere in the production line halts assembly.  You get not one failure, but thousands.  The rare one-offs that you do get can almost always be attributed to bad inputs and a rare cosmic radiation event. Most of  the time we are dealing with electrons and copies of electrons; we never have just one bad bolt.  We have thousands of copies of that bad bolt.

Another interesting thing happens when you use code generators, especially if you are as anal retentive as I am about eliminating unnecessary code.  You will often find that the code you are generating is exactly the same with the exception of a few parameters which can often be determined at generation time.  When this happens, what you should do is base the class you are generating on a common base class, and move that code to the base class, with the template parameters that you specify.  This is a great way to reduce code bloat.  Rather than having fifteen methods that all do the same thing, the superclass method can do the common stuff, and the specialized stuff can be moved to specialization methods in the derived class.  Template parameters can help here as well.

In the example I'm working on, my individual resource providers all implement the basic getResourceById() method of a HAPI IResourceProvider in the same way, with specialized bits delegated to the derived class.  That's 35 lines of code that I don't have to duplicate across some 48 different FHIR resources.  I really only have to test it once to verify that it does what it is supposed to do in 48 different places.  If I was writing that same code 48 times, I guarantee that I'd do it differently at least once (if I didn't go crazy first).  No sane engineer would ever write the same code 48 times, so nobody using code generation should make the software do it that way either.

I once worked with a developer that generated a half million lines of code in this fashion.  In over two years of testing, implementation and deployment, his code generated a grand total of 5 defects.  For him, we could translate the traditional defects/KLOC metric to defects/MLOC, and he still still outperformed the next best developer by an order of magnitude.

That, my friends, is software engineering.

   Keith