Medical code systems for developers
Breaking down the jargon
Picture this. You’re a lead developer in 2023 and your organization, a GP clinic, has tasked you with creating a dashboard that allows GPs to select a diagnosis from a dropdown menu. Simple, right?
“Well, okay, first I need a list of diagnoses,” I hear you say. After that, you build a UI and an application. Bam, mission accomplished!
The question is: How will you populate that diagnoses list? You can’t just go around creating your own lists. Remember, we’re entering the world of interoperability. Another application will not be able to understand your bespoke diabetes diagnosis code:
So, is there an authoritative list?
The answer: Yes, but there isn’t just one. There are many to choose from. In fact, there are at least 20 official international medical code systems, each with tens of thousands of specific, fine-grained medical codes within them. To make matters worse, many diagnoses out in the wild are defined by multiple code systems simultaneously.
So which code system do you choose? Are you supposed to bet on a winning horse? What if the code system you choose goes out of fashion by 2028? Or what if the code system you choose ends up missing some important diagnosis codes that your GPs urgently need? Surely there’s a safer way to build your dropdown list, right?
Enter HL7’s FHIR standard. HL7 is pioneering the effort to standardize the previously unstandardizable. The good news is FHIR has a solution to the above problem. The bad news is that we’re now going to have to learn a complex, nuanced, and academic data model.
Oftentimes trying to make sense of the FHIR standard can sometimes feel like you’re cramming for an exam and nothing is going in. But fear not! This article will teach you the basic components of how FHIR references medical code systems. You will also learn how to practically use FHIR within your application to improve code system interoperability.
So get ready to go back to school as we explore how FHIR aims to tackle the problem of code systems in healthcare. For our first class, let’s introduce some important building blocks in FHIR: CodeSystems, ValueSets, Coding, and CodableConcept.
What are CodeSystems?
The CodeSystem FHIR resource is a data model element that is used to record real-world medical code systems. When referring to CodeSystems from this point onwards, I am referring to the FHIR resource itself.
CodeSystems are collections of codes that represent different types of medical information such as conditions, procedures, or medications. These codes are used to standardize how medical information is shared between different healthcare systems. Each CodeSystem in FHIR may include metadata about its authors, the current version, description, and must include a publication status (draft / active / retired / unknown).
The official definition of the CodeSystem resource can be found at: https://www.hl7.org/fhir/codesystem.html
What are ValueSets?
ValueSets in FHIR are collections of codes that are drawn from one or more CodeSystems and are designed to be grouped around a specific clinical purpose. The codes in a ValueSet:
May be a subset of a CodeSystem,
May represent an entire CodeSystem by including all of the codes in that CodeSystem
May pick a subset of codes from multiple CodeSystems which are grouped together by a shared clinical purpose
May even be drawn from multiple versions of the same code system
ValueSets can be used to describe different aspects of patient care, such as diagnoses, medications, procedures, and other types of clinical data. Similar to CodeSystems, ValueSets in FHIR may include metadata about its authors, the current version, description, and must include a publication status (draft / active / retired / unknown). ValueSets are designed to be used in conjunction with CodeSystems to provide more specific and targeted clinical information.
The official definition of the ValueSet resource can be found at: https://www.hl7.org/fhir/valueset.html
The codes in a ValueSet can be found within the ValueSet’s Expansion section. Please note that a ValueSet Expansion is intended to be dynamic in nature and change over time. It is not recommended to store expansions since stored data may become stale.
An expansion is ideally meant to be queried from a FHIR Terminology server, such as https://tx.fhir.org/, at the time that it’s needed. FHIR exposes an operation called $expand which can be used to query the codes in a ValueSet. An $expand operation will return a list of the codes in a ValueSet and the $expand input parameters can be used to filter the server response.
Differences between CodeSystems and ValueSets
CodeSystems and ValueSets are essentially lists of clinical codes which could, in theory, be used to solve the developer problem described earlier. This is because CodeSystems and ValueSets are published by authoritative organizations. If you’re anything like me, however, you might have found yourself scratching your head at one point in time wondering:
“If both CodeSystems and ValueSets contain lists of clinical codes, why in the world do we need both!?”
While CodeSystems and ValueSets are both collections of codes that are used to standardize healthcare data exchange, there are some important differences between these two resources.
One of the main differences between CodeSystems and ValueSets is their level of granularity. CodeSystems are more general and provide a broad range of codes that represent different types of medical information. On the other hand, ValueSets are more specific and are designed to represent a smaller set of clinical concepts related to a specific patient care scenario.
Another difference between CodeSystems and ValueSets is their intended use. CodeSystems are designed to define clinical concepts and then assign codes to those concepts. CodeSystems provide a basic set of codes that can be used as a starting point for creating more targeted subsets. This is where ValueSets come into the picture. ValueSets are subsets of codes that share a common clinical scenario or context. ValueSets are intended to group concepts from CodeSystems for a particular purpose to provide more targeted and specific clinical information.
Finally, CodeSystems and ValueSets differ in their level of complexity. CodeSystems are relatively simple and straightforward, with each code representing a single medical concept. ValueSets, on the other hand, can be more complex and may include multiple codes representing a single clinical concept. This allows ValueSets to provide more detailed and nuanced clinical information.
By using ValueSets in conjunction with CodeSystems, healthcare systems can provide more targeted and specific clinical information, leading to better patient care and more efficient data exchange.
What are some examples of CodeSystems and ValueSets?
To better understand the differences between CodeSystems and ValueSets, let's take a look at some examples.
Example of a CodeSystem
An example of a widely used clinical CodeSystem is SNOMED-CT (http://snomed.info/sct). SNOMED-CT organizes concepts hierarchically and uses relationships to capture clinical knowledge. It includes a broad set of codes that represent different types of medical conditions, such as infectious diseases, cancer, injuries, and more. These codes are used to standardize the way that medical information related to these conditions is shared between different healthcare systems.
Example of a ValueSet
A ValueSet can be created using a subset of codes from a CodeSystem to represent a specific clinical concept.
An example of a ValueSet is https://www.hl7.org/fhir/valueset-interactant.html. This ValueSet derives its codes from the SNOMED-CT CodeSystem, yet is more specific because it only includes codes where the concept is a 105590001 (Substance). This means that the ValueSet includes only the codes that represent substances, making it easier for healthcare systems to exchange information about specific substances.
Another example of a ValueSet is an implicit ValueSet of a CodeSystem. In FHIR, “every CodeSystem has an implicit ValueSet that is all the codes defined in the code system”. In the case of SNOMED-CT, the implicit ValueSet also has a URI of http://snomed.info/sct?fhir_vs. Whilst implicit ValueSets are no more specific than the CodeSystems that define them, they benefit from being expandable. Why is this important? Because you can’t technically $expand a CodeSystem but you can $expand its implicit ValueSet.
How can we use CodeSystems?
Two datatypes that we’ll discuss, Coding and CodableConcept, can help us read and write clinical data in a standardized way by referring to a CodeSystem.
What is a Coding?
An object that is typed as Coding is used to represent a clinical code defined by a code system. An object of type Coding may include a code, the CodeSystem defining the code, and display text describing the code. Whilst all of the attributes in a Coding object are optional in FHIR, including them will generally increase the quality of the data. It is also worth noting that FHIR recommends against including a display without a code in a Coding object since computation on Coding.display alone is considered unsafe.
system: ‘<URI that represents a CodeSystem>’
For example, diabetes mellitus is defined as code 73211009 in the SNOMED-ST CodeSystem. Its Coding object to can be represented as:
display: ‘Diabetes mellitus’,
That’s a great win for interoperability. The code can now be listed in context of the CodeSystem which defined it. Put more simply, it means that no matter what CodeSystem you choose (SNOMED-CT in the example above), an application that is trying to make sense of your data can then translate the code into their own medical lexicon by way of a lookup.
Not only does this mean that your application can now be CodeSystem agnostic, but you are also now at liberty to use multiple different CodeSystems within the same application!
The official datatype definition of Coding is found at: https://build.fhir.org/datatypes.html#Coding
What is a CodableConcept?
An object that is typed as a CodableConcept is used to represent a clinical concept that can be described by one or multiple Coding objects. This datatype acknowledges the reality that a single medical concept may be defined by multiple CodeSystems. In the same way that multiple human languages (English, French etc) can encode the word for “dog”, multiple CodeSystems (SNOMED-CT, LOINC etc) can encode the concept of “Hypertension”. Therefore, a CodableConcept can implement an array of Coding objects that contain alternative encodings (translations) of the same concept.
Technically, FHIR specifies that the coding array is an optional attribute because it is assumed that some medical concepts may not be codable. In these cases, the best an implementer can do is rely on the text attribute to describe the concept. However if codes are available, they should be used.
This datatype is best described with an example.
In this example, the coding array contains two coding elements, each representing a specific code system and its corresponding code. The first coding element represents a SNOMED-CT code for "Hypertension" with the code "38341003".
The second coding element represents a LOINC code for "Hypertension" with the code "45643-4". The text field provides a human-readable display of the overall concept, which in this case is "Hypertension".
The coding array in the example code snippet above allows for the representation of multiple coding systems (SNOMED-CT and LOINC) and links two codes that are associated with the same medical concept, enabling interoperability and standardization.
The official datatype definition of CodableConcept is found at: https://build.fhir.org/datatypes.html#CodeableConcept
How can we use ValueSets?
At this point you might be wondering how to use ValueSets in web applications. You might instinctively want to refer to ValueSet URIs within Coding and CodableConcept objects like so:
// An invalid implementation that uses a ValueSystem URI for the system attribute
Is this valid FHIR? The specification is pretty clear about this one: No. “The URL in a system is always a reference to a CodeSystem, not to a ValueSet.” By the way, implementers often get this wrong. We, at Flexpa, have seen many implementations out in the wild that refer directly to ValueSet URIs from Coding and CodableConcept objects.
This still leaves the question: How are you, an application developer, meant to use ValueSets in your app?
Well instead of referring to ValueSet URIs directly from within FHIR resources, ValueSets can help you choose which list of codes to use in the first place. Specifically, FHIR allows for certain attributes within resources to be ‘bound’ to a ValueSet. This means you ought to:
Refer to the FHIR specification which defines the resource profile whose attribute you’re interested in encoding.
Refer to the Implementation Guide which defines the resource profile whose attribute you’re interested in encoding.
Research whether that resource’s attributes are bound to any ValueSets.
Query a FHIR Terminology service using the $expand operation to retrieve a list of codes in the relevant ValueSet.
Use the response from the Terminology service to populate your application dropdown menu.
For the diagnosis example, you could make use of the ‘code’ attribute in the Condition resource. Condition.code is a CodableConcept and is bound to a ValueSet called ConditionProblemDiagnosisCodes, which contains thousands of codes.
Therefore from the point of view of an application developer who is interested in populating a list of diagnoses, you should only use the codes defined in the expansion section of the ConditionProblemDiagnosisCodes ValueSet.
Or should you?
In FHIR some rules are meant to be, well, bent. The idea of binding strengths highlights this. To clarify, a Coding or a CodableConcept attribute is said to be bound to a ValueSet with one of four different binding strengths: Required, Extensible, Preferred, and Example.
In the diagnoses example above, Condition.code is bound to the ConditionProblemDiagnosisCodes ValueSet with a strength of Example. And a binding strength of Example essentially means you can use whichever codes you’d like. Hmm…
Yes I hear you. “If I can use any code I’d like, then why am I even bothering to learn about ValueSets?” Well the reason is that attributes can have stronger binding strengths. This may occur are when:
Your app complies to an Implementation Guide. For example US Core enforces a stricter binding strength for Condition.code. In US Core, Condition.code is bound to the USCoreConditionCodes ValueSet with an Extensible binding strength, which is stronger than Example. Therefore if your application uses the US Core Implementation Guide, then you should use the USCoreConditionCodes ValueSet to populate your diagnoses list.
The attribute itself has a strong binding strength in the FHIR core specification. For example, ExplanationOfBenefit.status has a Required binding strength. Required is the strongest binding strength.
Both CodeSystems and ValueSets are essential FHIR resources for standardizing healthcare data exchange and promoting interoperability between different healthcare systems. While CodeSystems provide a broad set of codes that represent different types of medical information, ValueSets are more specific and are designed to represent a smaller set of clinical concepts.
Implementers should make use of FHIR’s Coding and CodableConcept datatypes in order to reference CodeSystems and use ValueSets in order to source the codes themselves.
By understanding the differences between CodeSystems, ValueSets, Codings, and CodableConcepts, healthcare professionals and product builders can work together to streamline healthcare data exchange with the shared goal of improving patient care.