<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.3.2">Jekyll</generator><link href="https://www.wardle.org/feed.xml" rel="self" type="application/atom+xml" /><link href="https://www.wardle.org/" rel="alternate" type="text/html" /><updated>2026-05-10T08:49:49+01:00</updated><id>https://www.wardle.org/feed.xml</id><title type="html">Health informatics and information technology</title><subtitle>by Dr Mark Wardle, Consultant Neurologist and Chief Clinical Information Officer (CCIO)
</subtitle><entry><title type="html">FTRM: an open container format for FHIR terminology</title><link href="https://www.wardle.org/terminology/2026/05/09/ftrm.html" rel="alternate" type="text/html" title="FTRM: an open container format for FHIR terminology" /><published>2026-05-09T10:09:09+01:00</published><updated>2026-05-09T10:09:09+01:00</updated><id>https://www.wardle.org/terminology/2026/05/09/ftrm</id><content type="html" xml:base="https://www.wardle.org/terminology/2026/05/09/ftrm.html"><![CDATA[<h1 id="ftrm">FTRM</h1>

<p>I’m pleased to announce the release of an open specification for terminology: <em>FTRM</em></p>

<ul>
  <li>SQLite is a fantastic application file format - SQLite database files are portable between 32-bit and 64-bit machines and between big-endian and little-endian architectures and between any of the various flavors of Windows and Unix-like operating systems.</li>
  <li>SQLite is accessible, portable and reliable</li>
</ul>

<p>If we as a community agree an open specification, we can make it easier to meaningful share terminology data such as code systems, value sets and maps across systems.</p>

<h2 id="introduction">Introduction</h2>

<p>If you build clinical software, whether operational or as part of data analytics, 
sooner or later you have to ship
terminology — e.g. SNOMED CT, a value set for a
research cohort, a concept map between two coding systems. The
FHIR terminology resources (<code class="language-plaintext highlighter-rouge">CodeSystem</code>, <code class="language-plaintext highlighter-rouge">ValueSet</code>, <code class="language-plaintext highlighter-rouge">ConceptMap</code>,
<code class="language-plaintext highlighter-rouge">NamingSystem</code>) are the right abstraction. But the <em>distribution
unit</em> — the file you actually copy around, install on a server, drop
into a CI pipeline, or hand to a colleague — has been a recurring
source of friction.</p>

<p>You can ship raw FHIR JSON. That works for a few hundred kilobytes
of conformance resources, and falls apart at the scale of even a
modest national subset. You can stand up a Postgres instance and
load it from a vendor’s bespoke pipeline. That works for a single
deployment, and breaks the moment you need a second one or want to
hand a snapshot to someone outside your organisation. Or you can
serve everything from a remote terminology service over HTTP — fine
when you have the network, untenable when you do not. Making billions
of server round-trips to a national terminology ‘server’ isn’t going 
to work - most throttle requests.</p>

<p>I’ve long felt being able have the flexibility to run software like this 
on your own machine is important, and it is ideal if that code is <em>open-source</em>.</p>

<h2 id="why-a-file-format">Why a file format?</h2>

<p>What’s been missing is a single file you can email, scp, drop in S3,
or commit to a release artefact, that contains the canonical
terminology resources, is fast enough to serve directly, and is
portable between implementations.</p>

<p>I’d like to introduce: <a href="https://github.com/wardle/hades/blob/main/doc/ftrm.md">FTRM</a>
— a <em>FHIR Terminology Container</em>. It is an open specification for a
single SQLite database that holds FHIR <code class="language-plaintext highlighter-rouge">CodeSystem</code>, <code class="language-plaintext highlighter-rouge">ValueSet</code>,
<code class="language-plaintext highlighter-rouge">ConceptMap</code> and <code class="language-plaintext highlighter-rouge">NamingSystem</code> resources, with enough structure to
serve the canonical terminology operations directly off the file —
<code class="language-plaintext highlighter-rouge">$lookup</code>, <code class="language-plaintext highlighter-rouge">$validate-code</code>, <code class="language-plaintext highlighter-rouge">$expand</code>, <code class="language-plaintext highlighter-rouge">$translate</code>, <code class="language-plaintext highlighter-rouge">$subsumes</code>.</p>

<p>A note on scope: FTRM v1 is FHIR-shaped. It does not define SNOMED
CT’s expression constraint language (ECL) or reference-set
semantics, and it is not where I currently put SNOMED. The
reference implementation, <a href="https://github.com/wardle/hades">hades</a>,
serves SNOMED CT through a separate dedicated store —
<a href="https://github.com/wardle/hermes">hermes</a>, built on LMDB and
Lucene, with native ECL evaluation — and uses FTRM for LOINC and
HL7 FHIR NPM packages. The composite catalogue dispatches by
canonical URL, so from a client’s perspective it is one
terminology server. The separation is intentional:
SNOMED-specific operations benefit from SNOMED-specific data
structures, and FTRM’s job is to be the boring, portable container
for everything that fits the plain FHIR terminology shape.</p>

<h1 id="why-sqlite">Why SQLite</h1>

<p>The choice of SQLite is deliberate. SQLite is the most-deployed
database in the world; it is in every browser, phone and operating
system. The file format is
<a href="https://www.sqlite.org/lts.html">stable for the long term</a> and
has a published, public-domain reference implementation. There is
no daemon, no orchestrator, no clustering layer to install. A FTRM
file is just bytes — you can <code class="language-plaintext highlighter-rouge">sha256sum</code> it, version it, mirror it,
and verify it as you would any other release artefact.</p>

<p>And critically: a FTRM file is <em>queryable</em> with stock tooling. If
you want to know how many concepts a code system has, how its
hierarchy is shaped, or which value sets reference which systems,
you open the file with the <code class="language-plaintext highlighter-rouge">sqlite3</code> CLI and write SQL. There are
no opaque binary blobs, no implementation-defined indexes you have
to reverse-engineer, no proprietary on-disk structures. Everything
is documented, including the foreign keys and the FTS5 indexes.</p>

<h1 id="whats-in-the-file">What’s in the file</h1>

<p>The schema separates two kinds of fields: the ones a server needs
to reason about, and the ones it has to round-trip without
understanding.</p>

<p>The first set — <code class="language-plaintext highlighter-rouge">case_sensitive</code>, <code class="language-plaintext highlighter-rouge">hierarchy_meaning</code>,
<code class="language-plaintext highlighter-rouge">status</code>, <code class="language-plaintext highlighter-rouge">experimental</code>, <code class="language-plaintext highlighter-rouge">publisher</code> and so on — are real columns
that you can index and filter. The second set — copyright, contact
points, language extensions, useContext, the long tail of FHIR
metadata that varies between resources but that a terminology
server doesn’t dispatch on — round-trips through a single JSON
column called <code class="language-plaintext highlighter-rouge">metadata</code>. That keeps the schema honest about what
matters operationally, while preserving fidelity for everything else.</p>

<p>Concept properties follow the same idea. The four well-known
concept properties (<code class="language-plaintext highlighter-rouge">inactive</code>, <code class="language-plaintext highlighter-rouge">abstract</code>, <code class="language-plaintext highlighter-rouge">notSelectable</code>,
<code class="language-plaintext highlighter-rouge">status</code>) are projected onto columns of the <code class="language-plaintext highlighter-rouge">concept</code> table, so the
common filters are cheap. Everything else lives in
<code class="language-plaintext highlighter-rouge">concept_property</code> with FHIR’s <code class="language-plaintext highlighter-rouge">value[x]</code> polymorphism mirrored as
typed columns (<code class="language-plaintext highlighter-rouge">value_str</code>, <code class="language-plaintext highlighter-rouge">value_int</code>, <code class="language-plaintext highlighter-rouge">value_bool</code>, …).</p>

<p>The <code class="language-plaintext highlighter-rouge">concept_ancestor</code> table is the transitive closure of the
parent edges, with the shortest-path depth as a column. Subsumption
queries (<code class="language-plaintext highlighter-rouge">is-a</code>, <code class="language-plaintext highlighter-rouge">descendent-of</code>) become a single indexed lookup
rather than a recursive CTE. Writers rebuild the closure when they
write the file; readers trust it.</p>

<p>Free-text search uses SQLite’s FTS5, with <code class="language-plaintext highlighter-rouge">unicode61
remove_diacritics 2</code> baked into the contract so <code class="language-plaintext highlighter-rouge">cafe</code> matches
<code class="language-plaintext highlighter-rouge">café</code> regardless of where the file was built. That detail matters
— if FTS tokenisation drifts between writers, the same query
returns different results on different files, and the format stops
being portable in any meaningful sense.</p>

<h1 id="versioning">Versioning</h1>

<p>FTRM stores an arbitrary number of versions of any resource. The
identity tuple is <code class="language-plaintext highlighter-rouge">(resource_type, url, version)</code>. Selection between
versions is the caller’s responsibility, driven by the FHIR
<code class="language-plaintext highlighter-rouge">system-version</code> and <code class="language-plaintext highlighter-rouge">force-system-version</code> parameters at request
time. There is no ambient “current version” baked into the file —
which means a single FTRM file can hold this year’s LOINC release
alongside a frozen snapshot from two years ago, or successive
versions of <code class="language-plaintext highlighter-rouge">hl7.terminology.r4</code>, and a research pipeline can
reproduce its inputs by referring to versions explicitly.</p>

<p>Writes are replace-by-tuple: a writer that re-imports a
<code class="language-plaintext highlighter-rouge">(CodeSystem, url, version)</code> triple deletes the dependent rows
keyed by that tuple and re-inserts. There is no patch-in-place
journal. The file is a snapshot.</p>

<h1 id="file-identification-and-conformance">File identification and conformance</h1>

<p>Every FTRM v1 file stamps two values into the SQLite header:
<code class="language-plaintext highlighter-rouge">application_id = 0x4654524D</code> (ASCII <code class="language-plaintext highlighter-rouge">FTRM</code>) and <code class="language-plaintext highlighter-rouge">user_version = 1</code>.
A reader that opens a file without the FTRM stamp must refuse it.
That’s a sanity gate: it stops a generic SQLite file being
mistakenly treated as a terminology container, and it gives <code class="language-plaintext highlighter-rouge">file(1)</code>
something to recognise.</p>

<p>The specification defines what it means to be a <em>conforming
reader</em> and a <em>conforming writer</em>. A reader that enumerates
<code class="language-plaintext highlighter-rouge">tx_resource</code>, registers each row as an addressable resource, and
honours the well-known property columns at lookup time — and the
FTS indexes for text search, and <code class="language-plaintext highlighter-rouge">concept_ancestor</code> for filter
pushdown — is conformant. A writer that produces files passing
<code class="language-plaintext highlighter-rouge">PRAGMA foreign_key_check</code> and rebuilds the ancestor closure and
FTS indexes is conformant. The list is short and operational, not
aspirational.</p>

<h1 id="why-this-matters">Why this matters</h1>

<p>The point of an open specification is that no single vendor owns
the format. The schema, identifiers and prose of FTRM v1 are
released under
<a href="https://creativecommons.org/publicdomain/zero/1.0/">CC0</a> — the
public-domain dedication. Anyone can write a reader or a writer,
in any language, without asking permission and without licensing
friction. The
<a href="https://github.com/wardle/hades">reference implementation</a> is
<a href="https://github.com/wardle/hades">hades</a>, my open-source FHIR
terminology server, and it passes 81.8% of the
<a href="https://github.com/HL7/fhir-tx-ecosystem-ig">HL7 FHIR Terminology Ecosystem IG</a>
conformance suite at the time of writing. But the format does not
depend on hades, and a writer in Python, Java, Rust or Go that
follows the spec produces files that any other conforming reader
can serve.</p>

<p>For the NHS, where I sit, this matters in a very specific way. We
have years of accumulated experience with terminology distribution
that has been awkward at best. SNOMED CT, dm+d, the NHS Data
Dictionary, the Welsh national subsets — each comes with its own
pipeline, its own format, its own assumptions about what a server
will do with it. Layering a FHIR-shaped, SQLite-backed,
vendor-neutral container on top of that does not solve the
underlying provenance and governance questions, but it does mean
that <em>once those questions are answered</em>, the artefact you ship is
boring, ubiquitous and inspectable.</p>

<p>The same applies to research. A reproducible cohort study should
be able to declare its terminology inputs by file hash. Today, that
typically means archiving a tarball of source releases and a
loader script. With FTRM, it means archiving a single <code class="language-plaintext highlighter-rouge">.db</code> file
and a sha256 — and any reader can serve it.</p>

<h1 id="whats-next">What’s next</h1>

<p>FTRM is at v1. It is stable; breaking changes will bump the schema
version and ship migration tooling. The spec lives in the hades
repository at
<a href="https://github.com/wardle/hades/blob/main/doc/ftrm.md"><code class="language-plaintext highlighter-rouge">doc/ftrm.md</code></a>,
and the reference DDL is the SQL file the implementation actually
runs against. There is no separate paper.</p>

<p>If you build, ship, or consume FHIR terminology, please read it,
try it, and tell me where it is wrong. The schema is the contract;
sharpening that contract is how the format becomes useful beyond
my own work.</p>

<p><em>Mark</em></p>]]></content><author><name></name></author><category term="terminology" /><summary type="html"><![CDATA[FTRM]]></summary></entry><entry><title type="html">NHS data: we have a problem</title><link href="https://www.wardle.org/strategy/2023/10/19/nhs-data.html" rel="alternate" type="text/html" title="NHS data: we have a problem" /><published>2023-10-19T20:19:19+01:00</published><updated>2023-10-19T20:19:19+01:00</updated><id>https://www.wardle.org/strategy/2023/10/19/nhs-data</id><content type="html" xml:base="https://www.wardle.org/strategy/2023/10/19/nhs-data.html"><![CDATA[<p>If you’re in clinical informatics, you can learn a lot from the COVID-19 pandemic.</p>

<p>When we needed to send out shielding letters to our patients with motor neurone disease,
I ran a report against our neurology electronic patient record, obtaining the
names and addresses of all of our patients with motor neurone disease in South Wales. 
In generating  that report, each patient’s vital status was automatically checked with multiple 
authoritative sources of information, to ensure correct address information and
to ensure we would not send a letter to a patient who was deceased.</p>

<p>This was straightforward because a) we had coded diagnostic information and b)
we had a sensible and flexible information architecture that minimised manual
effort. It took me about 10 minutes.</p>

<p>When we needed to send out shielding letters to patients with multiple sclerosis
but only those on certain categories of disease modifying drugs, then I ran a
report against our neurology electronic patient record as above, but simply 
added a cross-reference to the active medications we had recorded.</p>

<p>This was also straightforward because a) we had already coded both diagnostic
and treatment information for our entire cohort, and b) we had a sensible and 
flexible information architecture.</p>

<p>For both, the use of standards such as SNOMED CT and dm+d was critical in 
creating meaningful data that could be used for a variety of purposes, such as
direct care, service management, and research.</p>

<p>When patients receive botulinum toxin in our clinics, we record using our
electronic patient record. A document is sent into multiple portals and the 
structured information behind that document is stored, so that we know exactly
what brand, dose and even where has been injected, all using SNOMED CT.</p>

<p>The document is useful, but the document is simply a side-effect of the data.</p>

<h1 id="so-what-about-nhs-wales">So what about NHS Wales?</h1>

<p>But in the wider NHS Wales, there were significant data problems, not least
errors in getting the correct address for patients.</p>

<p><img src="/images/pulse-wrong-shielding-letters.png" alt="pulse-wrong-shielding-letters.png" /></p>

<p>And our national ‘portal’, the ‘Welsh Clinical Portal’ is little more than a
document and results archive. Many aspects are good, such as being able to see
letters and laboratory and radiology results from across Wales, but we should
have much greater ambitions. This is software that has been in development for many
years and yet is a long way from being an electronic patient record.</p>

<p>It’s time for a reset.</p>

<p>NHS Wales needs to become data-driven.</p>

<p>We need to be able to focus our limited resources and manage the backlog of 
patients waiting by the sensible and considered use of clinical data in order to 
prioritise, minimise harm and improve outcomes. We need greater user-centred
design, and clinical leadership, not ‘engagement’ as the last part of a development 
process in which the first time we see a new design it is about to be launched.</p>

<p>The Welsh Audit Office (WAO) highlighted a “lack of independent scrutiny” and
“unbalanced reporting of progress”. We as an informatics community should be 
much more open and transparent on the challenges we face, and turn our expertise
to meeting those challenges.</p>

<p>Mark</p>]]></content><author><name></name></author><category term="strategy" /><summary type="html"><![CDATA[If you’re in clinical informatics, you can learn a lot from the COVID-19 pandemic.]]></summary></entry><entry><title type="html">Pluripotent data: A data strategy in health and care</title><link href="https://www.wardle.org/strategy/2023/10/03/pluripotent-data.html" rel="alternate" type="text/html" title="Pluripotent data: A data strategy in health and care" /><published>2023-10-03T04:03:03+01:00</published><updated>2023-10-03T04:03:03+01:00</updated><id>https://www.wardle.org/strategy/2023/10/03/pluripotent-data</id><content type="html" xml:base="https://www.wardle.org/strategy/2023/10/03/pluripotent-data.html"><![CDATA[<p><img src="/images/pluripotent/pluripotent.001.png" alt="pluripotent.001.png" /></p>

<h1 id="pluripotent-data">Pluripotent data</h1>

<blockquote>
  <p>Pluripotent: capable of giving rise to several different cell types.</p>
</blockquote>

<p>“Pluripotent” is a biological term applied to cells that are undifferentiated
and are capable of differentiating into several different cell types, and 
therefore satisfy a variety of demands</p>

<p>Therefore, I propose the term ‘pluripotent data’ for use in healthcare.</p>

<p>In essence, I use ‘pluripotent data’ as a way of describing the prioritisation 
of data harmonisation and standardisation through common data models that may be
used, directly or indirectly, for multiple purposes to meet a variety of demands.</p>

<p><img src="/images/pluripotent/pluripotent-omop.png" alt="pluripotent-omop.png" /></p>

<h1 id="principles-supporting-a-data-strategy">Principles supporting a data strategy</h1>

<p>The key principles that should underpin a healthcare organisation’s data strategy are:</p>

<ul>
  <li>Vendor neutrality</li>
  <li>Use of standards</li>
  <li>Harmonisation, intermediary structures &amp; multi-purpose analytics; <em>pluripotent data</em></li>
  <li>Specialty extensions, registries and research</li>
  <li>Governance and transparency</li>
  <li>Procurement, compliance and audit</li>
  <li>Training and education</li>
</ul>

<h3 id="vendor-neutrality">Vendor-neutrality</h3>

<p><img src="/images/pluripotent/pluripotent.003.png" alt="pluripotent.003.png" /></p>

<p>The current situation is that healthcare data varies between organisations and across applications and depends on purpose. 
Health and care data can be used for multiple purposes such as direct care, reporting, research, service management and quality improvement.</p>

<p>In the main, the current fragmentation reflects organisational and governance structures - this is Conway’s Law in action.</p>

<blockquote>
  <p>“Any organisation that designs a system (defined broadly) will produce a design whose structure is a copy of the organization’s communication structure.”
Mel Conway</p>
</blockquote>

<p>The consequence of this ‘Law’ if that if direct care, hospital administration, quality 
improvement and research are separate in an organisation, or represented by different
organisations, then the ‘systems’ developed will mirror the pattern.</p>

<p>No wonder then that end-users feel such significant fragmentation. This is not a new issue.</p>

<blockquote>
  <p>The various clinical systems, many of them paper based, differed from one another and had no relationship with the administrative hospital-wide systems. The funding made available in the late 1980s and early 1990s for medical and later clinical audit helped to reinforce this separation by making available to groups of clinicians money for small local computer systems. The lack of any connection between these different systems, one administrative, the others clinical, for collecting data cannot be explained solely on the basis of some technical or technological reason. It was just as strongly a reflection of a mindset that clinical matters were the sole domain of clinicians and non-clinical matters, to do with the management of resources and with the movement of patients into and through the hospital, were the preserve of managers and administrators.</p>

  <p>Bristol Heart Enquiry, 2001.
See <a href="https://wardle.org/clinical-informatics/2017/06/06/using-data-patient-safety.html">Using data for patient safety</a></p>
</blockquote>

<blockquote>
  <p>The key thing to remember about Conways Law is that the modular decomposition of a system and the decomposition of the development organization must be done together.</p>

  <p>Martin Fowler <a href="https://martinfowler.com/bliki/ConwaysLaw.html">Conway’s Law</a></p>
</blockquote>

<p>The term ‘Inverse Conway Maneuvre’ (also known as ‘Reverse Conway Maneuvre’) was coined by Jonhy Leroy and Matt Simons (ThoughtWorks) in December 2010.</p>

<blockquote>
  <p>In what could be termed an “inverse Conway maneuver,” you may want to begin by breaking down silos that constrain the team’s ability to collaborate effectively.</p>

  <p>Martin Fowler</p>
</blockquote>

<p>While there are valid criticisms of the ‘Inverse Conway Manoevre’ relating to organisational change, it can inform our
approach to data and data flow.</p>

<p>In essence, rather than framing data in terms of applications, we instead focus on data. Our data is therefore <em>vendor-neutral</em>.</p>

<p>And an ‘Inverse Conway Manoevre’ looks to set up collaborative efforts than span organisational and domain boundaries
so that we build a common data model that can, either directly or indirectly, be used for multiple purposes.</p>

<p>In Wales, through the Welsh Technical Standards Board (which I chair) and Welsh Government, we have mandated HL7 FHIR as a foundational
standard for operational clinical systems. This is an important step to build interoperability between different clinical
systems and fosters the re-use and composition of data.</p>

<p>However, HL7 FHIR does not solve <em>semantic interoperability</em> unless one builds in mechanisms to ensure that the information
standards - the dictionaries encoding different types of information such as reference data like location, or staff member, 
or patient, or value sets - are aligned.</p>

<p>As such, we need to be explicit in mandating the <em>FAIR</em> principles, so that data are:</p>

<ul>
  <li>Findable (through metadata and linked data technologies)</li>
  <li>Accessible</li>
  <li>Interoperable (through standards and vocabularies and collaboration)</li>
  <li>Re-usable (through provenance and standards)</li>
</ul>

<h2 id="data-harmonisation">Data harmonisation</h2>

<p><img src="/images/pluripotent/pluripotent.004.png" alt="pluripotent.004.png" /></p>

<p>There is sometimes a tension between what we think of as general purpose healthcare data and specific demands. 
For example, we contribute to a national audit on stroke care, but because we have separate clinical and dministrative 
systems and this national audit, we have to have dedicated members of staff keying in information from multiple sources
manually in order to satisfy the specific demands of this (important and useful) national audit.</p>

<p>Instead, we need a mechanism that provides a defined patient-centric common data model (CDM) that acts as a foundation
that can be extended to suit multiple purposes. This means that, for example, two organisations can generate
combined regional datasets simply by composing (merging) their data.</p>

<p>This highlights two important requirements:</p>

<ol>
  <li>We need to think of our extract/transform/load (ETL) operations as first-class and important</li>
  <li>We need to recognise the value of shared tools and knowledge in the process of data harmonisation across health and care.</li>
</ol>

<p>For example, Welsh Government are keen to modernise the dataset in relation to outpatients. Until COVID, an outpatient
encounter was not counted unless the appointment was face-to-face, despite many clinical services offering telephone
consultations for many years. In essence, they want greater detail on scheduled outpatient care and include information
relating to procedures and problems/diagnoses.</p>

<p><img src="/images/pluripotent/pluripotent.005.png" alt="pluripotent.005.png" /></p>

<p>The problem is that, at the time of writing, they cannot cope with such information encoded using SNOMED CT and instead
want categorical data - “just one of the top five categories”. This is fine for central reporting, but the team 
suggested a clinical-facing application that would capture those categories at the point of care.</p>

<p>This is quite wrong.</p>

<p>Do we accept that demands for central data reporting define our clinical systems?</p>

<p>Certainly not.</p>

<p>So we must instead recognise the importance of our <em>data infrastructure</em> and ability to map, process and convert
structured meaningful data for a variety of different purposes.</p>

<p>It also emphasises how important it is that such decisions are made by people with required technical knowledge: we need
to also consider the digital and data capabilities of our health and care workforce.</p>

<h1 id="using-intermediary-pluripotent-data-representations">Using intermediary ‘pluripotent’ data representations</h1>

<p><img src="/images/pluripotent/pluripotent.006.png" alt="pluripotent.006.png" /></p>

<p>In this diagram we have multiple operational clinical systems and a variety
of methods of extract and transform to our intermediary patient-centric 
data representation. The OMOP CDM is a good example of such a representation,
potentially being useful for solving multiple demands, and sufficient flexibility
to be specialised easily.</p>

<p>From such a CDM, we can subsequently build further data pipelines permitting
more bespoke ‘rectangular’ projections of our granular and complex health data
suitable for reporting requirements or demands of centralised registries.</p>

<p>In this, we gain multiple perspectives, or prisms, on the same common data models.</p>

<p>You can read more about OMOP and their common data model <a href="https://www.ohdsi.org/data-standardization/#:~:text=The%20Observational%20Medical%20Outcomes%20Partnership,that%20can%20produce%20reliable%20evidence">here</a>.</p>

<h1 id="requirements">Requirements</h1>

<p><img src="/images/pluripotent/pluripotent.007.png" alt="pluripotent.007.png" /></p>

<p>A data strategy with common data models at its heart has important technical, organisational
and cultural requirements:</p>

<ul>
  <li>Strong internal governance
    <ul>
      <li>Registration and monitoring of data flows (information governance, feral apps, o365)</li>
      <li>Standardisation of data across uses</li>
      <li>Harmonisation</li>
      <li>Understand common foundations</li>
      <li>Maintenance and publication of common data model</li>
      <li>Dependent on appropriate governance e.g., local clinical and technical design authorities (and their links with others)</li>
    </ul>
  </li>
  <li>Standardised vocabularies
    <ul>
      <li>Essential for semantic interoperability</li>
      <li>Match to regional, national and international standards</li>
      <li>First-class mapping and transformation</li>
      <li>To satisfy myriad potential requirements - operational clinical systems, analytics, reporting and research</li>
    </ul>
  </li>
  <li>Openness and transparency
    <ul>
      <li>Radical transparency and visibility; working in the open</li>
      <li>Publication of standards used, for all purposes. Openly available and managed and updated</li>
      <li>Versioning and registration of use</li>
      <li>Avoid breaking changes; additive change: <em>you don’t care if your delivery truck bringing your parcel also has other parcels on it</em>.</li>
    </ul>
  </li>
  <li>Coordination with partners (e.g. WG/HBs/DHCW)
    <ul>
      <li>We must work across region, across Wales and across UK</li>
      <li>Our collaborative efforts must be prioritised and supported</li>
      <li>Some of what we need can and should be delivered by our partners</li>
    </ul>
  </li>
  <li>Investment in our data fabric
    <ul>
      <li>Raise the visibility of our data infrastructure</li>
      <li>Balance short-term delivery goals with longer-term standardisation, harmonisation and documentation</li>
      <li>Avoiding technical debt and ‘big ball of mud’ architectures; Infrastructure on demand and as infrastructure as code. Declarative.</li>
      <li>Focus on interoperability, principled approach based on re-use, understand commonalities/foundational data structures</li>
    </ul>
  </li>
  <li>Re-usable tools
    <ul>
      <li>Investment in tooling, and pipelines, that are ‘invisible’ and yet vital. “Our plumbing”</li>
      <li>Investment in our ability to extract, transform and load data; “internal interoperability”</li>
      <li>Could and should be shared across Wales</li>
      <li>Open standards, and open-source at foundations</li>
    </ul>
  </li>
  <li>Education and training
    <ul>
      <li>Health <em>and</em> social care</li>
      <li>Regional partnerships</li>
      <li>Inculcate art-of-the-possible, data skills, across organisations</li>
      <li>Workshops, investment in training, workforce and digital skills, peer support</li>
    </ul>
  </li>
</ul>

<p>There are common themes across these requirements mainly in relation to prioritising, investing in, collaborating and governing 
in relation to data.</p>

<p>I’d be interested in your feedback. What else have I missed? What should be in the data strategy for
Cardiff and Vale University Health Board, or for any healthcare organisation for that matter?</p>

<p>Working in the open is important. Let me hear your views. Email, or raise an
issue on <a href="https://github.com/cavdigital/digitalstrategy2023/issues">https://github.com/cavdigital/digitalstrategy2023/issues</a>.</p>

<p>Mark</p>]]></content><author><name></name></author><category term="strategy" /><summary type="html"><![CDATA[]]></summary></entry><entry><title type="html">NHS number validation and synthetic generation</title><link href="https://www.wardle.org/open-source/2023/09/29/nhs-number.html" rel="alternate" type="text/html" title="NHS number validation and synthetic generation" /><published>2023-09-29T17:16:16+01:00</published><updated>2023-09-29T17:16:16+01:00</updated><id>https://www.wardle.org/open-source/2023/09/29/nhs-number</id><content type="html" xml:base="https://www.wardle.org/open-source/2023/09/29/nhs-number.html"><![CDATA[<p>I’m pleased to announce the release of <a href="https://github.com/wardle/nhs-number">nhs-number</a>.</p>

<p>This is a very small open source library available for both server-side and client-side
use. It is written in Clojure/Script and so can be used, for example, on the JVM (servers) 
from Java or Clojure, or transpiled to JavaScript to be used in web browsers (clients).</p>

<p>In many domains, software developers make use of a range of foundational data and software 
services. They don’t need to re-invent the wheel; they’re focusing on getting to where they
need to go.</p>

<p>Things are so very different in healthcare.</p>

<p>Not only do you have to overcome fundamental problems with the commercial market failure for 
health and care software, byzantine procurement rules, as well as considering your information 
governance and regulatory compliance obligations, you have very few libraries and frameworks 
on which you can build your point of differentiation.</p>

<p>Of course, we have access to general purpose libraries, such as supporting secure networking
or encryption, or data storage. When we write software, whether for the web, or for iOS, or
Android, or Windows, we think about making use of a general purpose computing platform. It
is rare for us to build software without foundational core tools and services. Sometimes
those platforms are open-source, and sometimes those are proprietary, but they are available
and re-usable and composable so you can focus on the problems you are trying to solve.</p>

<p>But there is a dearth of high quality, well tested and functional libraries in health and care.</p>

<p><code class="language-plaintext highlighter-rouge">nhs-number</code> is a small library that:</p>

<ul>
  <li>is published under a permissive <a href="https://github.com/wardle/nhs-number/blob/main/LICENSE">open-source library</a> you can view and modify the source code</li>
  <li>has <a href="https://github.com/wardle/nhs-number/blob/main/test/com/eldrix/nhsnumber_test.cljc">extensive tests</a>, in including the use of synthetic (generative) tests</li>
  <li>has an <em>automated</em> continuous test/integration/deployment pipeline so that any modifications result in updated
tests and automated reports - e.g., see <a href="https://github.com/wardle/nhs-number/actions/runs/5916417398/job/16043690629">this report</a></li>
  <li>reports on <a href="https://app.codecov.io/gh/wardle/nhs-number">test coverage</a> automatically</li>
</ul>

<p>I already use it server-side for validation of data consumed from other services, and client-side to check user input is valid.
I also use it to generate synthetic data for automated tests in other systems.</p>

<p><em>Mark</em></p>]]></content><author><name></name></author><category term="open-source" /><summary type="html"><![CDATA[I’m pleased to announce the release of nhs-number.]]></summary></entry><entry><title type="html">Codelists</title><link href="https://www.wardle.org/strategy/2023/09/28/codelists.html" rel="alternate" type="text/html" title="Codelists" /><published>2023-09-28T10:09:09+01:00</published><updated>2023-09-28T10:09:09+01:00</updated><id>https://www.wardle.org/strategy/2023/09/28/codelists</id><content type="html" xml:base="https://www.wardle.org/strategy/2023/09/28/codelists.html"><![CDATA[<blockquote>
  <p>If a patient has a type of motor neurone disease, then…</p>
</blockquote>

<blockquote>
  <p>If a patient takes an ACE inhibitor, then …</p>
</blockquote>

<blockquote>
  <p>If a patient has a type of autoimmune disease, then …</p>
</blockquote>

<p>Whether we are writing software to build compelling user-facing applications, building rules or decision support,
or choosing categories from which we will run analyses such as understanding patient outcomes, we need to be able
to process health and care data and make inferences.</p>

<p>My open source software <a href="https://github.com/wardle/codelists">codelists</a> generates versioned codelists 
for reproducible data pipelines and research.</p>

<p>In general, there are two ways to think about codelists and reproducibility.</p>

<p>The first is a explicit human curation of a list of codes. This is the approach adopted by
Ben Goldacre and the <a href="http://opencodelists.org">opencodelists</a> team. You create and share codelists.</p>

<p>The second is to define a codelist using a declarative specification which can be
used to dynamically - but reproducibly - generate the codelist.</p>

<p>Let’s look at an example. <a href="http://opencodelists.org">opencodelists</a> has a codelist
to specify terms that represent a referral to colorectal services under a 2-week wait
(urgent) basis. See <a href="https://www.opencodelists.org/codelist/phc/2ww-referral-colorectal/7eac259d/#full-list">https://www.opencodelists.org/codelist/phc/2ww-referral-colorectal/7eac259d/#full-list</a></p>

<p>At the time of writing, this manually curated list includes one active concept and
two inactive concepts.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>276401000000108	Fast track referral for suspected colorectal cancer
276411000000105	Urgent cancer referral - colorectal
276421000000104	Urgent cancer referral - colorectal
</code></pre></div></div>

<p>While this is a useful set of curated terms, I would argue that it is better to simply 
define this codelist using a SNOMED CT constraint using the syntax of the SNOMED CT 
expression constraint language:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>{
  "ecl": "&lt;&lt;276401000000108"
} 
</code></pre></div></div>
<p>Result:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>=&gt; #{276401000000108 276411000000105 276421000000104}
</code></pre></div></div>

<p>Based on a named versioned distribution of SNOMED, and defined versions of this
tool, this specification can be used to generate a reproducible codelist. If SNOMED CT
changes over time, this specification will continue to work, due to the semantic
relationships within SNOMED CT. <code class="language-plaintext highlighter-rouge">codelists</code> can expand a set of codes to include
now inactive concepts using historical associations.</p>

<p>Certainly for drugs, a declarative rules approach works better than the manually
curated set of opencodelists. If new drugs of a type are added to the UK dictionary
of medicines and devices (dm+d), then <code class="language-plaintext highlighter-rouge">codelists</code> will include those new drugs
without any manual intervention, while manual curation requires continued
monitoring and maintenance of code lists.</p>

<h1 id="how-to-use-codelists">How to use <code class="language-plaintext highlighter-rouge">codelists</code></h1>

<p>You can define codelists using a variety of means, such as</p>

<ul>
  <li>ICD-10 codes for diagnoses</li>
  <li>ATC codes for drugs</li>
  <li>SNOMED CT expressions in the expression constraint language (ECL).</li>
</ul>

<p>You can combine these approaches for high sensitivity, or manually derive codelists using hand-crafted ECL for high
specificity.</p>

<p><code class="language-plaintext highlighter-rouge">codelists</code> is a simple wrapper around two other services - <a href="https://github.com/wardle/hermes">hermes</a>
and <a href="https://github.com/wardle/dmd">dmd</a>. I think it is a nice example of composing discrete, but related services
together to give more advanced functionality.</p>

<p><code class="language-plaintext highlighter-rouge">codelists</code> operates:</p>

<ul>
  <li>as a library and so can be embedded within another software package running on the java virtual machine (JVM), written
in, for example java or clojure.</li>
  <li>as a microservice and so can be used as an API by other software written in any language</li>
</ul>

<p>The substrate for all codelists is SNOMED CT. That coding system is an ontology and terminology, and not simply a
classification. That means we can use the relationships within SNOMED CT to derive more complete codelists.</p>

<p>If you only use the SNOMED CT ECL to define your codelists, then simply use <code class="language-plaintext highlighter-rouge">hermes</code> directly.
You only need the additional functionality provided by <code class="language-plaintext highlighter-rouge">codelists</code> if you are building codelists
from a combination of SNOMED CT ECL, ATC codes and ICD-10.</p>

<p>ATC maps are not provided as part of SNOMED CT, but are provided by the UK
dm+d. ICD-10 maps are provided as part of SNOMED CT.</p>

<h1 id="using-codelists">Using codelists</h1>

<p>You can <em>realise</em> a codelist, expanding it to all of its codes. You can also test membership of a given code against a
codelist.</p>

<p>All codelists, by default, expand to include historic codes. This will become
configurable, but is the default for greater sensitivity at the expense of specificity.
Different trade-offs might apply to your specific project.</p>

<p>Boolean logic is supported, with arbitrary nesting of your codes using a simple DSL.</p>

<p>A codelist is defined as names and values in a map, with the names representing the codesystem
and the values the specification.</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"ecl"</span><span class="p">:</span><span class="w"> </span><span class="s2">"&lt;&lt;24700007"</span><span class="w">
</span><span class="p">}</span><span class="w"> 
</span></code></pre></div></div>

<p>This defines a codelist using the SNOMED expression constraint language (ECL). While ECL v2.0 supports the use of
historic associations within constraints, I usually recommend ignoring that ‘feature’ and instead defining whether and
how historic associations are included as part of the API.</p>

<p>SNOMED CT, in the UK, includes the UK drug extension with a 1:1 map between SNOMED identifiers and drugs in the official
UK drug index - dm+d
(dictionary of medicines and devices). That means you <em>can</em> use a SNOMED expression to choose drugs:</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"ecl"</span><span class="p">:</span><span class="w"> </span><span class="s2">"(&lt;&lt;24056811000001108|Dimethyl fumarate|) OR (&lt;&lt;12086301000001102|Tecfidera|) OR (&lt;10363601000001109|UK Product| :10362801000001104|Has specific active ingredient| =&lt;&lt;724035008|Dimethyl fumarate|)"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>Note how SNOMED ECL includes simple boolean logic.</p>

<p>But `codelists’ supports other namespaced codesystems. For example:</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"atc"</span><span class="p">:</span><span class="w"> </span><span class="s2">"L04AX07"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>Will expand to a list of SNOMED identifiers that are mapped to the exact match ATC code L04AX07 and its descendents
within the
SNOMED hierarchy.</p>

<p>A SNOMED CT expression in the expression constraint language must be a valid expression.
ICD-10 and ATC codes can be specified as an exact match (e.g. “G35”) or as a prefix (e.g. “G3*”). The latter will
match against all codes that begin with “G3”.</p>

<p>Different codesystems can be combined using boolean operators and prefix notation:</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"or"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
    </span><span class="p">{</span><span class="w">
      </span><span class="nl">"atc"</span><span class="p">:</span><span class="w"> </span><span class="s2">"L04AX07"</span><span class="w">
    </span><span class="p">},</span><span class="w">
    </span><span class="p">{</span><span class="w">
      </span><span class="nl">"atc"</span><span class="p">:</span><span class="w"> </span><span class="s2">"L04AX08"</span><span class="w">
    </span><span class="p">},</span><span class="w">
    </span><span class="p">{</span><span class="w">
      </span><span class="nl">"ecl"</span><span class="p">:</span><span class="w"> </span><span class="s2">"(&lt;10363601000001109|UK Product| :10362801000001104|Has specific active ingredient| =&lt;&lt;724035008|Dimethyl fumarate|)"</span><span class="w">
    </span><span class="p">}</span><span class="w">
  </span><span class="p">]</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>This expands the ATC codes L04AX07 L04AX08 and supplements with any other product containing DMF as its active
ingredient.</p>

<p>If multiple expressions are used, the default is to perform a logical OR. That means this is equivalent to the above
expression:</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">[</span><span class="w">
  </span><span class="p">{</span><span class="w">
    </span><span class="nl">"atc"</span><span class="p">:</span><span class="w"> </span><span class="s2">"L04AX07"</span><span class="w">
  </span><span class="p">},</span><span class="w">
  </span><span class="p">{</span><span class="w">
    </span><span class="nl">"atc"</span><span class="p">:</span><span class="w"> </span><span class="s2">"L04AX08"</span><span class="w">
  </span><span class="p">},</span><span class="w">
  </span><span class="p">{</span><span class="w">
    </span><span class="nl">"ecl"</span><span class="p">:</span><span class="w"> </span><span class="s2">"(&lt;10363601000001109|UK Product| :10362801000001104|Has specific active ingredient| =&lt;&lt;724035008|Dimethyl fumarate|)"</span><span class="w">
  </span><span class="p">}</span><span class="w">
</span><span class="p">]</span><span class="w">
</span></code></pre></div></div>

<p>Duplicate keys are <em>not</em> supported, but multiple expressions using different keys are.</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"atc"</span><span class="p">:</span><span class="w"> </span><span class="s2">"L04AX07"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"ecl"</span><span class="p">:</span><span class="w"> </span><span class="s2">"(&lt;10363601000001109|UK Product| :10362801000001104|Has specific active ingredient| =&lt;&lt;724035008|Dimethyl fumarate|)"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>When no operator is explicitly provided, a logical ‘OR’ will be performed.</p>

<p>For concision, all keys can take an array (vector), which will be equivalent to using “or” using the same codesystem.</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"atc"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
    </span><span class="s2">"L04AX07"</span><span class="p">,</span><span class="w">
    </span><span class="s2">"L04AX08"</span><span class="w">
  </span><span class="p">]</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>Boolean operators “and”, “or” and “not” can be nested arbitrarily for complex expressions.</p>

<p><code class="language-plaintext highlighter-rouge">codelists</code> also supports ICD-10.</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"icd10"</span><span class="p">:</span><span class="w"> </span><span class="s2">"G35*"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>will expand to include all terms that map to an ICD-10 code with the prefix “G35”, and its descendents.</p>

<p>The operator “not” must be defined within another term, or set of nested terms. The result will be the realisation of
the first term, or set of nested terms, MINUS the realisation of the second term, or set of nested terms.</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"icd10"</span><span class="p">:</span><span class="w"> </span><span class="s2">"G35"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"not"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"ecl"</span><span class="p">:</span><span class="w"> </span><span class="s2">"&lt;24700007"</span><span class="w">
  </span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>Or, perhaps a more complex expression:</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"or"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
    </span><span class="p">{</span><span class="w">
      </span><span class="nl">"icd10"</span><span class="p">:</span><span class="w"> </span><span class="s2">"G35"</span><span class="w">
    </span><span class="p">},</span><span class="w">
    </span><span class="p">{</span><span class="w">
      </span><span class="nl">"icd10"</span><span class="p">:</span><span class="w"> </span><span class="s2">"G36.*"</span><span class="w">
    </span><span class="p">}</span><span class="w">
  </span><span class="p">],</span><span class="w">
  </span><span class="nl">"not"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"ecl"</span><span class="p">:</span><span class="w"> </span><span class="s2">"&lt;24700007"</span><span class="w">
  </span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>Or, more concisely:</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"icd10"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
    </span><span class="s2">"G35"</span><span class="p">,</span><span class="w">
    </span><span class="s2">"G36.*"</span><span class="w">
  </span><span class="p">],</span><span class="w">
  </span><span class="nl">"not"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"ecl"</span><span class="p">:</span><span class="w"> </span><span class="s2">"&lt;24700007"</span><span class="w">
  </span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>These will generate a set of codes that includes codes “G35” and any with the prefix “G36.” but omit “24700007” (
multiple sclerosis).</p>

<p>You can use wildcards. Here I directly use a running <code class="language-plaintext highlighter-rouge">codelists</code> HTTP server
to expand a codelist defined as</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"atc"</span><span class="p">:</span><span class="w"> </span><span class="s2">"C08*"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>This should give a codelist containing all calcium channel blockers.</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>http <span class="s1">'127.0.0.1:8080/v1/codelists/expand?s={"atc":"C08*"}'</span>
</code></pre></div></div>

<p>Result:</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">[</span><span class="w">
  </span><span class="mi">374049007</span><span class="p">,</span><span class="w">
  </span><span class="mi">13764411000001106</span><span class="p">,</span><span class="w">
  </span><span class="mi">376841009</span><span class="p">,</span><span class="w">
  </span><span class="mi">11160711000001108</span><span class="p">,</span><span class="w">
  </span><span class="mi">893111000001107</span><span class="p">,</span><span class="w">
  </span><span class="mi">29826211000001109</span><span class="p">,</span><span class="w">
  </span><span class="mi">376754006</span><span class="p">,</span><span class="w">
  </span><span class="err">...</span><span class="w">
</span></code></pre></div></div>

<p>You can customise how data are returned.</p>

<p>By default, a list of codes is returned.</p>

<p>To return identifier and name, use ‘as=names’</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>http <span class="s1">'127.0.0.1:8080/v1/codelists/expand?s={"atc":"C08*"}&amp;as=names'</span>
</code></pre></div></div>

<p>Result:</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="w">
</span><span class="p">[</span><span class="w">
  </span><span class="p">{</span><span class="w">
    </span><span class="nl">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">374049007</span><span class="p">,</span><span class="w">
    </span><span class="nl">"term"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Nisoldipine 20mg tablet"</span><span class="w">
  </span><span class="p">},</span><span class="w">
  </span><span class="p">{</span><span class="w">
    </span><span class="nl">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">13764411000001106</span><span class="p">,</span><span class="w">
    </span><span class="nl">"term"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Amlodipine 5mg tablets (Apotex UK Ltd)"</span><span class="w">
  </span><span class="p">},</span><span class="w">
  </span><span class="p">{</span><span class="w">
    </span><span class="nl">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">376841009</span><span class="p">,</span><span class="w">
    </span><span class="nl">"term"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Diltiazem malate 120 mg oral tablet"</span><span class="w">
  </span><span class="p">},</span><span class="w">
  </span><span class="p">{</span><span class="w">
    </span><span class="nl">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">11160711000001108</span><span class="p">,</span><span class="w">
    </span><span class="nl">"term"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Exforge 10mg/160mg tablets (Novartis Pharmaceuticals UK Ltd)"</span><span class="w">
  </span><span class="p">},</span><span class="w">
  </span><span class="p">{</span><span class="w">
    </span><span class="nl">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">893111000001107</span><span class="p">,</span><span class="w">
    </span><span class="nl">"term"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Tildiem LA 300 capsules (Sanofi)"</span><span class="w">
  </span><span class="p">},</span><span class="w">
  </span><span class="err">...</span><span class="w">
</span></code></pre></div></div>

<p>For reproducible research, <code class="language-plaintext highlighter-rouge">codelists</code> will include information about <em>how</em> the codelist was generated, including the
releases of SNOMED CT, dm+d and the different software versions. It should then be possible to reproduce the content of
any codelist. At the moment, only the data versions are returned:</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>http 127.0.0.1:8080/v1/codelists/status
</code></pre></div></div>

<p>The following metadata will be returned:</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="w">
</span><span class="p">{</span><span class="w">
  </span><span class="nl">"dmd"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"releaseDate"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2022-05-05"</span><span class="w">
  </span><span class="p">},</span><span class="w">
  </span><span class="nl">"hermes"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
    </span><span class="s2">"© 2002-2021 International Health Terminology Standards Development Organisation (IHTSDO). All rights reserved. SNOMED CT®, was originally created by The College of American Pathologists. </span><span class="se">\"</span><span class="s2">SNOMED</span><span class="se">\"</span><span class="s2"> and </span><span class="se">\"</span><span class="s2">SNOMED CT</span><span class="se">\"</span><span class="s2"> are registered trademarks of the IHTSDO."</span><span class="p">,</span><span class="w">
    </span><span class="s2">"32.12.0_20220413000001 UK drug extension"</span><span class="p">,</span><span class="w">
    </span><span class="s2">"32.12.0_20220413000001 UK clinical extension"</span><span class="w">
  </span><span class="p">]</span><span class="w">
</span><span class="p">}</span><span class="w">

</span></code></pre></div></div>]]></content><author><name></name></author><category term="strategy" /><summary type="html"><![CDATA[If a patient has a type of motor neurone disease, then…]]></summary></entry><entry><title type="html">Unbundling the electronic health record</title><link href="https://www.wardle.org/strategy/2022/12/02/functional-health-records.html" rel="alternate" type="text/html" title="Unbundling the electronic health record" /><published>2022-12-02T14:14:14+00:00</published><updated>2022-12-02T14:14:14+00:00</updated><id>https://www.wardle.org/strategy/2022/12/02/functional-health-records</id><content type="html" xml:base="https://www.wardle.org/strategy/2022/12/02/functional-health-records.html"><![CDATA[<p>This is a <a href="https://youtu.be/2HQSAtmVbAU?si=97TfdaCc1Wwbq6Dk">talk I gave at the re-Clojure 22 technology conference in December 2022</a>:</p>

<iframe width="560" height="315" src="https://www.youtube.com/embed/2HQSAtmVbAU?si=NWX55fL27rvi5HdD" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen=""></iframe>

<p>Clojure is a programming language that is a dialect of Lisp, but none of the concepts here require the use of Clojure as
a programming language. Rather, this talk is more about learning from some of
the principles behind Clojure, and functional programming languages generally,
and internet-era software architectural design, to re-imagine the software we use in health and care.</p>

<h2 id="introduction">Introduction</h2>

<p><img src="/images/unbundling/unbundling.001.png" alt="title" /></p>

<p>In this blog post, I argue that we must transform the way we perceive and use electronic health records (EHRs).
By employing the principles of Clojure and reimagining the EHR, we have the potential to turn this essential tool inside
out, unlocking its full potential, and support a health and care system designed around the needs of patients and
professionals.</p>

<h2 id="traditional-ehr-vs-inside-out-thinking">Traditional EHR vs inside-out thinking</h2>

<p><img src="/images/unbundling/unbundling.002.png" alt="unbundling" /></p>

<p>To begin, let’s understand the traditional or conventional Electronic Health Record (EHR). Typically, an EHR is
considered an application or system that healthcare providers purchase, deploy, and often integrate with other systems.
These EHRs are organization-centric, designed to meet the needs of the purchasing entity, and are heavily focused on
process and billing. They tend to be monolithic, with proprietary internals and limited interoperability within the
product. Interoperability is considered ‘external’.</p>

<p>In essence, EHRs are like isolated islands in the digital healthcare landscape, making integration complex and resulting
in data concretions within an enterprise. Patient data is contained within these applications, making it challenging to
use the same data for different purposes like population health analytics, service management, and research.
Furthermore, the tooling used for EHRs differs significantly from what is required for analytics and research.
For example, we may use Python or R for research, proprietary tools for analytics in managing services,
and very different tools for applications supporting direct care.</p>

<h2 id="unbundling-the-ehr">Unbundling the EHR</h2>

<p>The concept of unbundling the EHR involves shifting the focus from the organization to the patient and adopting the FAIR
principles: findability, accessibility, interoperability, and reusability. In this model, data becomes the central
element, with the potential for easy composition of data from multiple organizations, regardless of their origin. This
shift allows us to move beyond process measures to more meaningful outcomes in healthcare.</p>

<p>By concentrating on data, the architecture inherently becomes distributed, similar to how we build external systems.
This approach is inspired by Rich Hickey’s concept of using plain data, RPC, and queues in system architecture. The
result is a suite of independent but composable computing and data services, both internally and externally
interoperable. These services provide flexibility, adaptability, and scalability, aligning with the dynamic nature of
healthcare.</p>

<p>We must move to a domain-driven design in health care.</p>

<h2 id="interoperability">Interoperability</h2>

<p><img src="/images/unbundling/unbundling.003.png" alt="interoperability" /></p>

<p>In healthcare, interoperability has been a long-standing challenge. Various approaches, such as convergence through
shared applications or information exchanges, have been attempted. However, these approaches often fall short,
especially in supporting granular analytics and research.</p>

<p>A more effective solution is to approach interoperability through <em>composition and layering of domain-aligned data and
software components</em>. Instead of forcing organizations to adopt a single shared application, the focus shifts to
standardized data exchange. This enables the creation of shared care records that may not contain all the detail but are
sufficient for many purposes.</p>

<h2 id="domain-centric-approach">Domain-Centric Approach</h2>

<p><img src="/images/unbundling/unbundling.004.png" alt="Domain-driven design" /></p>

<p>Breaking down the EHR into its domain-specific components can provide clarity and facilitate meaningful, semantic
interoperability. Components related to radiology, staff, patients, reference data, classifications, and terminology
can be understood and managed individually across organisational boundaries. It is seductive to think that a single
system can do all that we need, but health and care is too complex and needs to be broken up into smaller, manageable
chunks which can be developed and improved independently.</p>

<p><img src="/images/unbundling/unbundling.005.png" alt="Shared semantics" /></p>

<p>An approach predicated upon organisations and not health and care domains creates fragmentation and loss of shared
semantics. Traditional EHRs often adopt the shared single instance approach or attempt to converge various systems.
However, this
can lead to fragmentation and a lack of shared semantics. Unbundling the EHR, on the other hand, enables data to flow
seamlessly and allows for dynamic composition of services, resulting in a more cohesive and flexible healthcare
ecosystem.</p>

<p>For example, an organisation-focus means that a staff member who works across multiple sites may need multiple logins
and find it difficult to generate a report detailing all of the surgical procedures performed across those sites, as
there may not be shared understanding of the identifiers that underpin that staff member. If they are a prescriber, each
organisation must be careful to register their accounts so that they have access to e-prescribing.</p>

<p>Instead, we should be recognising the importance of domain-boundaries, and that a staff index, and associated data
such as scope of practice, and regulatory informaion, is a first-class, important domain within health and care; likely
needing federation and aggregation of a number of disparate sources of information, but presented as a unified, and
simpler service to other domain components.</p>

<h2 id="turning-electronic-health-and-care-records-inside-out">Turning electronic health and care records inside-out</h2>

<p>What can we learn from Clojure?</p>

<p><img src="/images/unbundling/unbundling.006.png" alt="Learning from Clojure" /></p>

<p>While Clojure is an excellent language to implement the software for the next
generation of electronic health record systems, we can learn the most from the principles
that underpin the design of Clojure:</p>

<ul>
  <li>Data-orientated</li>
  <li>First class names</li>
  <li>Working to abstractions</li>
  <li>Functional / pure functions / reproducibility</li>
  <li>Immutability</li>
  <li>Loose coupling</li>
  <li>Dynamic / flexible / adaptive to change</li>
  <li>First class events / event modelling</li>
  <li>Building the internals of our systems as in the large (Internet-era approach)</li>
</ul>

<p>Using these principles, we can look towards interoperability through <strong>composition and layering
of domain-orientated data and software components</strong>.</p>

<h3 id="example--thinking-about-analytics-for-electronic-prescribing">Example : thinking about analytics for electronic prescribing</h3>

<p><img src="/images/unbundling/unbundling.007.png" alt="e-prescribing" /></p>

<p>Imagine a scenario: You’re in a hospital, and you need to closely monitor all the antibiotics being prescribed to
patients. It’s a critical task that can impact patient outcomes, but it’s also one that can be quite complex due to the
diverse nature of healthcare systems.</p>

<p><strong>Option 1: Leveraging In-Built Analytics</strong></p>

<p>The first option is to rely on the built-in analytics tools provided by EPMA (electronic prescribing and medicines
administration)
or Electronic Patient Records (EPR) vendors. These tools are designed to work seamlessly within their respective
systems, offering insights
and analytics specific to that environment. While this option can be effective within the confines of a single system,
it falls short when dealing with the intricate web of healthcare data spread across different platforms.</p>

<p>In the real world, many hospitals use different prescribing systems, each tailored to a specific aspect of patient care.
For instance, chemotherapy prescribing systems differ significantly from those used for general medical purposes.
Moreover, various healthcare enterprises, including community services and general practitioner surgeries, operate on
different systems altogether. This fragmentation poses a significant challenge when aiming for a comprehensive,
population-level analysis of healthcare data.</p>

<p><strong>Option 2: Embracing Health Information Exchanges</strong></p>

<p>The second approach involves aggregating data from various sources into a Health Information
Exchange (HIE). An HIE acts as a bridge between disparate healthcare systems, allowing data to flow between them. While
this approach can provide a degree of standardization and interoperability, it doesn’t fully address the underlying
issues of data granularity and accessibility. In essence, it’s a step in the right direction but may not be the ultimate
solution for comprehensive healthcare data analysis.</p>

<p><strong>Option 3: Unbundling Electronic Health Records (EHRs)</strong></p>

<p>Now, here’s where unbundling the Electronic Patient Record (EPR) or Electronic Health Record (EHR) system comes in.
In simple terms, this means taking the data generated by these systems, transforming it into a standardized,
self-describing event stream, and making it accessible for a wide range of applications, including direct-care (e.g.
alerts),
analytics (a dashboard of all antimicrobials across an estate), and research.</p>

<p>This unbundling process begins with extracting data from these systems. While many suppliers can provide data feeds in a
standardized format, some may require transformation and extraction to create a consistent event stream. Once achieved,
this event stream becomes a valuable resource for healthcare professionals.</p>

<p><strong>The Power of Data Annotation</strong></p>

<p>To maximize the utility of this event stream, we must recognise the importance of data annotation. This involves
enriching the data with additional information using terminology and drug dictionaries. For instance, a healthcare
provider can perform lookups against drug codes in the event stream, providing meaningful insights about the medications
prescribed. We must recognise that mapping and annotations are first-class problems in our domain, and those tools
should be available on demand, for a range of use-cases, and independent from individual vendors.</p>

<p>These annotated event streams can then be divided into substreams, each tailored to specific aspects of patient care.
For example, an antimicrobial event stream can be created and used to populate a real-time dashboard, enabling
healthcare providers to monitor antibiotic usage and make informed decisions promptly.</p>

<p><strong>Transforming Healthcare Data Management</strong></p>

<p>In essence, the vision revolves around turning the traditional approach to EHRs inside out. Instead of
relying solely on monolithic EHR systems, healthcare institutions should be able to embrace the power of first-class, composable data
and software services. This shift would allow for dynamic data composition and interoperability spanning organizational
boundaries, ultimately benefiting patient care, analytics, and research.</p>

<p>As healthcare continues to evolve, the ability to harness and analyze data effectively becomes paramount. By breaking
down data silos, enhancing interoperability, and making data truly first-class, the healthcare industry can take
significant strides
toward improved patient outcomes and a more efficient healthcare system. It’s a journey that requires effort and
collaboration and a foundation of vendor-neutral, and open, data and computing services.</p>

<h1 id="first-class-annotation-and-mapping">First-class annotation and mapping</h1>

<p><img src="/images/unbundling/unbundling.008.png" alt="terminology" /></p>

<p>In the world of healthcare and patient care, one of the most crucial aspects is the ability to <strong>make sense</strong> of the vast
and ever-evolving landscape of health and care data. This requires the use of standardized terminologies, and one example 
is SNOMED CT. Driven by the need for dynamic and adaptable software systems, we need ubiquitous tooling that harnesses 
the potential of SNOMED CT in healthcare, providing access to its power in any application, for any purpose.</p>

<p>For example, we can standardise on vocabularies, such as the list of specialty codes. However, such an approach leads
to concretions in our software systems, because if the master list of codes is updated, any software that needs to make
sense of those codes needs to be updated. In addition, these codes are usually not self-describing, or contain 
information about the relationships between those codes.</p>

<p>Instead, an ontology provides rich semantics supporting inference and sense-making.</p>

<p>For example, imagine I wish to build a compelling user experience so that a user can rapidly search for correspondence
relating to a speciality. If I have a flat list of codes, ‘neurology’ and ‘paediatric neurology’ are considered 
independent specialties, and if I wish to allow users to search for ‘paediatrics’ and also include ‘paediatric neurology’
then I must build those kinds of rules outside of my vocabulary / classification list.</p>

<p>A more dynamic and flexible approach such as that enabled by SNOMED CT might allow a user to choose ‘neurology’ and find 
letters for all types and subtypes of ‘neurology’ as a specialty, which would, of course, include ‘paediatric neurology’ 
by virtue of the ontological basis underpinning SNOMED CT. While some might think that this a dry and academic subject,
by getting our foundations right, we make it easier to build compelling, and delightful user experiences.</p>

<p>I’ve open-sourced a number of libraries and services that can support this kind of work:</p>

<ul>
  <li><a href="https://github.com/wardle/hermes">https://github.com/wardle/hermes</a> - SNOMED CT library and web service</li>
  <li><a href="https://github.com/wardle.codelists">https://github.com/wardle/codelists</a> - Generate code lists via declarative rules</li>
  <li><a href="https://github.com/wardle/dmd">https://github.com/wardle/dmd</a> - UK drug dictionary</li>
  <li><a href="https://github.com/wardle/hades">https://github.com/wardle/hades</a> - HL7 FHIR terminology wrapper around hermes</li>
</ul>

<p>This work should be open-source and available widely, and used for many different purposes, including direct care,
analytics pipelines and research. Such libraries and tools need to be quick to set-up and use, and not require 
significant investments in infrastructure or server capacity. It makes little sense to not share foundational software 
services. It is otherwise akin to developers needing to start by building their own encryption libraries; no-one should do that 
but instead use open, readily available and battle-tested ready-made libraries.</p>

<h1 id="mapping-and-projections-are-first-class-problems-in-our-domain">Mapping and projections are first-class problems in our domain</h1>

<p>We need to recognise, and therefore provide compelling, battle-tested solutions to first-class problems in our domain. 
Recognising the issues forces use to focus our limited resources on things that have the greatest benefit. If we think
that building user interfaces is the biggest issue, then we spend our time (erroneously) building solutions for e-forms.
Instead, if we recognise the true (shared) challenges, then we should quickly see that semantic interoperability depends
fundamentally on data and software that can make sense of, and map between different ways of encoding health and care 
data.</p>

<p><img src="/images/unbundling/unbundling.011.png" alt="data projections" /></p>

<p>Here we see that, for a specific research project, I’ve taken complex, real-life hierarchical and nested health data and
generated a simplifed <strong>rectangular projection</strong> to make it easier for subsequent analysis. Fundamentally, the projections
will vary depending on context and use-case, so we must focus on capturing highly granular and specific data at the point
of care, while building tools and chains and data pipelines that permit turning that specific data into a variety of
formats convenient for the use-case at hand. Importantly, we delay the loss of granular information until as late a 
stage as possible. Too many times, we look to ask professionals to record categorical data in order to simplify subsequent use of
those data, rather than capturing data at the right level of granularity for clinical use, and making use of the right tools,
and right expertise, to classify those data later on according to the need at hand. We should derive those projections.</p>

<p>Here is an overview of some of the tools I have built so far, in my spare time.</p>

<p><img src="/images/unbundling/unbundling.012.png" alt="tools" /></p>

<p>My current focus has been on lower-level, fundamental software and data services. However the future should be that we
build an ecosystem of composable higher-value components, well-tested and built by community efforts, relating to design 
systems and decision support. Why are we building prescribing rules in proprietary software systems when the community
could be building in the open to be used for whatever purpose necessary?</p>

<p><img src="/images/unbundling/unbundling.013.png" alt="graph api" /></p>

<p>Finally, I’ve recognised that one way of composing together data and software services within health and care is through
the use of graph APIs. Such APIs allow one to seamlessly navigate across disparate data services permitting user-facing
applications to be built independently of the the underlying software and data subsystems resolving the queries at hand.</p>

<p>In the video, I give a demonstration of using Hermes from a Clojure REPL, permitting interactive programming. For example,
I show how I can build a data pipeline that takes diagnostic and problem codes and map them into a specific emergency reference
set (subset) to simplify analysis.</p>

<h1 id="conclusion">Conclusion</h1>

<p>In conclusion, the concept of unbundling the EHR and adopting Clojure-derived principles has the potential to
transform healthcare data management. By focusing on data, achieving interoperability, and leveraging standardized
terminologies, we can create a more flexible, adaptable, and patient-centric healthcare ecosystem. This approach not
only benefits direct patient care but also empowers analytics and research, ultimately leading to better outcomes in the
healthcare industry.</p>]]></content><author><name></name></author><category term="strategy" /><summary type="html"><![CDATA[This is a talk I gave at the re-Clojure 22 technology conference in December 2022:]]></summary></entry><entry><title type="html">Mapping the electronic health record 2/2</title><link href="https://www.wardle.org/strategy/2021/12/14/mapping-the-epr-2.html" rel="alternate" type="text/html" title="Mapping the electronic health record 2/2" /><published>2021-12-14T14:14:14+00:00</published><updated>2021-12-14T14:14:14+00:00</updated><id>https://www.wardle.org/strategy/2021/12/14/mapping-the-epr-2</id><content type="html" xml:base="https://www.wardle.org/strategy/2021/12/14/mapping-the-epr-2.html"><![CDATA[<p>This is part 2/2 of my presentation at <a href="https://www.map-camp.com">MapCamp 2021</a>.</p>

<p><img src="/images/mapcamp21/map-ehr.001.png" alt="Title page" /></p>

<p>You can <a href="https://www.youtube.com/watch?v=9Vk6bg0uk4o">watch a video recording</a> of that presentation if you like, but
this post is a
deeper dive into some of the detail.</p>

<h1 id="2-algorithms">2. Algorithms</h1>

<p>So what about algorithms, and machine learning in health and care? Are you sure machines need anything different to what
we as humans need?</p>

<p><img src="/images/mapcamp21/map-ehr.007.png" alt="The need for interoperability" /></p>

<p>I’ve
previously <a href="https://wardle.org/strategy/2018/08/30/algorithm-strategy.html">drawn Wardley maps for the use of algorithms and machine learning in healthcare</a>:</p>

<p><img src="/images/mapcamp21/map-ehr.008.png" alt="The need for interoperability" /></p>

<p>There’s a complete <a href="https://wardle.org/strategy/2018/08/30/algorithm-strategy.html">blog post</a> about it, if you want
more detail.</p>

<p>But, you’ll see that whether you are looking to support a new clinical score that calculates stroke risk, or a new
machine learning algorithm that can interpret chest x-rays, computers need the same types of information and support and
feedback loops and monitoring as do professionals and services and facilities.</p>

<p>Our Wardley map shows dependencies on:</p>

<ul>
  <li>meaningful data</li>
  <li>feedback looks</li>
  <li>monitoring and evaluation pipelines</li>
  <li>consent and control</li>
  <li>readily available tools and libraries</li>
</ul>

<p><img src="/images/mapcamp21/map-ehr.011.png" alt="Machines vs humans" /></p>

<hr />

<h1 id="what-do-we-currently-have">What do we currently have?</h1>

<p>This map looks at the electronic health record from the viewpoint of an organisation, because that’s the usual model of
how software used in health and care, whether for acute hospitals or community services.</p>

<p><img src="/images/mapcamp21/map-ehr.013.png" alt="The need for interoperability" /></p>

<p>In this map, I show a dependency from the organisation to an electronic patient record.</p>

<p><em>NB: I think that dependency and mapping from organisation to health record can and should be challenged. A patient
shouldn’t have to know that one specialist service is managed by that organisation and another from another; aren’t they
interacting with a health service that should feel seamless? They should not need to know organisational or department
structures. We need to move to a single logical patient record, even if that record is aggregated or federated from
multiple providers.</em></p>

<p>Many organisations have bought off-the-shelf electronic health record products. As part of that, they might also buy,
usually but not always from the same supplier, the following products:</p>

<ul>
  <li>a patient portal - to allow patients to interact with, or at least view their record, at that organisation</li>
  <li>population analytics - to allow analytics across groups of patients</li>
  <li>health information exchange - to share data between organisations in, for example, a region</li>
  <li>an API product - to allow developers to build products using the data and services within the EPR.</li>
</ul>

<p>An API is an “application programming interface” - in essence a way for systems or subsystems or modules of software to
communicate.</p>

<p>You’ll see from the map that the API product is usually dependent on the EHR in this model. We’re going to need to
challenge that, because health and care records should surely be built using open APIs, not the other way around?</p>

<p>How can it be that our APIs our dependent on the EPR, rather than our EPR being dependent on our APIs?</p>

<p>In addition, the organization wants to share data across a region so they buy the product’s linked health information
exchange (HIE) product. This means that they can exchange data with other hospitals and other organizations within that
region.</p>

<p>But far from reducing the need for in-house development capacity, the need for integration and customisation means many
organisations embarking on their EPR journey end up increasing the numbers of in-house developers. The EPR, costing
rather a lot of money, hasn’t really solved all of our problems at all, and what we think of as research remains
entirely separate. It is akin to thinking we’ve bought an ‘off-the-shelf’ suit, but actually needing to do a significant
amount of tailoring ourselves.</p>

<p>Importantly, spending this money on an EPR does not prevent us having to do some real work on proper collaboration and
cooperation across health and care whether it be for direct patient care, service management, improvement, monitoring or
research, for otherwise we still end up with data silos in which we cannot easily pool data. Indeed, we surely need to
prioritise that work rather than thinking we can defer until after our EPR deployment?</p>

<h1 id="dependencies-and-tell-me-again-why-are-we-building-our-own-data-centres">Dependencies, and tell me again why are we building our own data centres?</h1>

<p>Our map also shows us that our in-house development, our EPR, our analytics platform, our health information exchange,
and patient portal(s) need infrastructure on which to run such as ‘compute’ and ‘data’ services. However, the map shows
that health systems still have significant legacy services that are bespoke and proprietary and custom-built, instead of
recognising that, particularly with limited local resource, we should be focusing on the things only the public sector
can do, and using off-the-shelf infrastructure on demand.</p>

<p>The big technology companies have so far pretty much failed in healthcare. I give them a D- “Could do better”.</p>

<p><img src="/images/mapcamp21/map-ehr.014.png" alt="Technology companies have failed" /></p>

<p>They’re fixated on infrastructure, particularly cloud services. Apple is an exception with its Apple Health app serving
as a patient portal.</p>

<p>The fundamental question for electronic health records (EHRs) is whether to buy from one vendor or assemble a
best-of-breed system, both of which can lead to complexity, particularly if we use point-to-point integration patterns
rather than a common shared platform. Buying the components from a single vendor, or integrating best-of-breed can result 
in a fossilised architecture which is difficult to change, proving to be inflexible and difficult to adapt to changing 
requirements. There can be a combinatorial explosion unless we build a platform on which disparate applications and 
services can sit.</p>

<p><img src="/images/mapcamp21/map-ehr.015.png" alt="Component parts of the EHR" /></p>

<p>Finally, we should also ask why are healthcare data for analysis, research and direct care kept separate? They should be
integrated for better patient care, with the same data used for multiple purposes. For example, direct patient care can 
surely be aided by real-time analytics - I’ve previously shown examples where my own EPR has plotted disease outcomes over
time for a single patient compared to an aggregated dataset of similar patients to aid shared decision making and understanding.</p>

<p>Many of our issues in health and care are cultural and historically. Why do we use different programming languages
and data stores for analytics and EHRs and therefore hindering progress. We need to challenge these assumptions while embracing
standardization and mapping of data. Why are we standardising applications that are difficult to integrate, rather than
standardising our data?</p>

<p><img src="/images/mapcamp21/map-ehr.016.png" alt="Why separate direct care, analytics and research?" /></p>

<h1 id="what-do-we-need">What do we need?</h1>

<p><img src="/images/mapcamp21/map-ehr.017.png" alt="What do we need" /></p>

<p>For EHRs, you can choose a monolithic system like Epic or Cerner, which handle billing, clinical data, identity
services, and orders. Workflow support is lacking and clinical calculators are often proprietary. Data sharing is
challenging and should be standardized.</p>

<h2 id="decomposing-the-electronic-health-record">Decomposing the electronic health record</h2>

<p><img src="/images/mapcamp21/map-ehr.018.png" alt="map-ehr.018.png" /></p>

<p>Here’s what happens when we buy a monolith:</p>

<p><img src="/images/mapcamp21/map-ehr.019.png" alt="Monolith" /></p>

<p>It still will need to be integrated to a PAS, to radiology and likely a separate laboratory 
management system. It may even not be a monolith itself - for example, Cerner’s EPR is
actually a combination of a number of different products made to look like a single application.</p>

<p><img src="/images/mapcamp21/map-ehr.020.png" alt="Best of breed" /></p>

<p>Alternatively, a best-of-breed approach allows you to integrate various systems, but it’s complex and custom and 
potentially difficult to flex and adapt in the future.</p>

<p><img src="/images/mapcamp21/map-ehr.021.png" alt="User needs" /></p>

<p>Instead of focusing on the needs of an EHR, we should prioritize <em>user needs</em>, including researchers, clinicians, and
patients. Decision-making relies on evidence, communication, and action. We lack sophisticated decision support tools
and meaningful data, often siloed within organizations.</p>

<p>To improve healthcare, we need to share data securely, enhance trust, and implement open standards. Workflow management
and process orchestration are vital. Users should control their data with advanced technologies like secure enclaves and
homomorphic encryption.</p>

<p><img src="/images/mapcamp21/map-ehr.022.png" alt="Future state" /></p>

<p>Foundational services should be open and standardized, focusing on clinical data, reference data, and commodity compute
providers.</p>

<p>In the future, we should aim for user-centered applications, trust, and outcomes. Transparency, collaboration, and
openness are key principles. Open source should drive the adoption of open standards.</p>

<p><img src="/images/mapcamp21/map-ehr.023.png" alt="Future state" /></p>

<p>Modern technology companies should view the cloud as more than a data center relocation, leveraging its potential for
healthcare. This shift can disrupt the existing market, emphasizing value and patient outcomes.</p>

<p><img src="/images/mapcamp21/map-ehr.024.png" alt="Future state" /></p>

<p>Thank you.</p>]]></content><author><name></name></author><category term="strategy" /><summary type="html"><![CDATA[This is part 2/2 of my presentation at MapCamp 2021.]]></summary></entry><entry><title type="html">Mapping the electronic health record 1/2</title><link href="https://www.wardle.org/strategy/2021/10/21/mapping-the-epr-1.html" rel="alternate" type="text/html" title="Mapping the electronic health record 1/2" /><published>2021-10-21T08:00:00+01:00</published><updated>2021-10-21T08:00:00+01:00</updated><id>https://www.wardle.org/strategy/2021/10/21/mapping-the-epr-1</id><content type="html" xml:base="https://www.wardle.org/strategy/2021/10/21/mapping-the-epr-1.html"><![CDATA[<p>This blog post is based on my presentation at <a href="https://www.map-camp.com">MapCamp 2021</a>.</p>

<p><img src="/images/mapcamp21/map-ehr.001.png" alt="Title page" /></p>

<p>You can <a href="https://www.youtube.com/watch?v=9Vk6bg0uk4o">watch a video recording</a> of that presentation if you like, but this post is a 
deeper dive into some of the detail.</p>

<iframe width="560" height="315" src="https://www.youtube.com/embed/9Vk6bg0uk4o" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>

<p>The source code for the Wardley maps are <a href="https://github.com/wardle/mapcamp-2021">available on github</a>.</p>

<h1 id="key-messages">Key messages</h1>

<p>The main messages are:</p>

<ol>
  <li>There are many parallels between clinical decision making and strategy.</li>
  <li>We need information at the right time for our decision making, with feedback loops, whether for direct care, management of services or reproducible research.</li>
  <li>Health and care should be data-driven.</li>
  <li>Health and care should be based upon openness, transparency, collaboration and continuity, with meaningful data.</li>
  <li>Trying to fix health information technology through the prism of an organisation-centric electronic health record is difficult and we need to step back and re-appraise.</li>
  <li>Understanding the needs of the EHR (electronic health record) is useful in understanding how to deconstruct the EHR.</li>
  <li>Modern technology companies have generally failed in health and care so far. They now focus on generic data and computing services but cloud technology should be more than simply moving a data centre to someone else’s kit.</li>
  <li>We should be building the tools to build EHRs, not another EHR.</li>
  <li>The foundational software and services and tools to build EHRs should be commodities, open-source and widely available leaving the market concentrate on higher-value differentiation to support decision-making. Such services can and should be provided as shared, highly resilient, managed services.</li>
  <li>A learning health and care system must be flexible and adaptable, so the priority should be in providing configurable composable tools to manage workflow and orchestration of processes across organisational boundaries.</li>
</ol>

<h3 id="what-is-wardley-mapping">What is Wardley mapping?</h3>

<p>An excellent way to understand our current state is to start with <em>user needs</em>. We need <em>situational awareness</em>.</p>

<p>Wardley mapping draws a chain of user needs from top to bottom, demonstrating the required capabilities on a chart that moves from left-to-right in which things on the left are poorly understood and in evolution and things on the right are evolved and commodities.</p>

<p><img src="/images/mapcamp21/stages-of-evolution.png" alt="Stages of evolution" /></p>
<blockquote>
  <p>From <a href="https://blog.gardeviance.org/2016/04/whats-in-wardley-map-and-need-for-cheat.html">“What’s in a Wardley map / cheatsheet”</a>
By Simon Wardley, 2016.</p>
</blockquote>

<p>As a result, you will find potentially high-value but perhaps poorly understood, poorly systematised elements on the left of the chart.</p>

<p>Over time, these elements are likely to move rightwards and become more accepted. As such, the map helps you realise, or at least prompt debate, on where to invest your time and effort.</p>

<p>In addition, you might find elements of your organisation, or your ecosystem, seem to be in the wrong place. For example, you might be building your own data centre and your organisation is treating it as leading edge; as you draw your map you realise that is wrong.</p>

<p>Maps facilitate shared discussion.</p>

<p>All my maps are wrong but I hope they might be useful!</p>

<p>Let’s start!</p>

<h1 id="1-clinical-decision-making">1. Clinical decision making</h1>

<p>I would like to discuss diagnosis, management and decision making and the use of <em>data</em> in supporting the health of a nation.</p>

<p>There are parallels between clinical decision making and strategy.</p>

<p>Our default must be that we’re always orientated towards action - so that means <em>making decisions</em>.</p>

<p><img src="/images/mapcamp21/map-ehr.002.png" alt="Be orientated towards action" /></p>

<p>If I’m seeing you in my clinic, we need to make a diagnosis and a management plan together - we need to ask ourselves what are we going to do; it requires *information at the right time. We might take action and measure the response and change our course.</p>

<p>Most of what we have in health information technology is unintentionally designed and it’s evolved organically and it’s not fit for purpose.</p>

<p>If you’ve not used Wardley mapping before, this map may look complex. But it isn’t. We simply need to read it from top to bottom. I’ll explain this in more detail below.</p>

<p><img src="/images/mapcamp21/map-ehr.004.png" alt="Mapping clinical decision making" /></p>

<p>We start with a situation of uncertainty. We  benefit if we reduce the uncertainty around a specific decision so that we can make better decisions.</p>

<p>At the top of the map, we have a professional and a patient and we’re making a decision.</p>

<ul>
  <li>Are you going to have that knee replacement?</li>
  <li>What are the trade-offs here?</li>
  <li>What are the pros, what are the cons?</li>
  <li>Are we going to use this new treatment with this patient with COVID-19?</li>
  <li>How do we identify the patient with COVID-19 who’s going to need higher level care?</li>
</ul>

<p>Whatever the decision, it is dependent on our diagnosis of the problem. I’ve annotated ‘diagnosis’ on the map as ‘possible’, ‘probable’ or ‘definite’ so already we’re starting to think  we are creating something that is <em>iterative</em>.</p>

<p>We’re going to need feedback loops because we may need to make a speculative diagnosis and confirm and adapt as we go. We do not have a linear process but instead we use results to not only confirm but refute our thinking. Feedback loops support the ‘clinical method’ in which we combine information about diseases with information about the patient and our context.</p>

<p>We need to understand that <em>a priori</em> probabilities - we use Bayesian inference based on the prior probability of that diagnosis.</p>

<p>That means that if a patient has a fever and we are  in Africa it’s likely to be a different diagnosis than if one has a fever in the United Kingdom. Context matters.</p>

<p>We also work on pattern matching:
<img src="/images/mapcamp21/map-ehr.004-pattern.png" alt="Mapping clinical decision making" />
That’s what we teach at medical school and it’s well developed and well evolved. We teach it we expect it to be done propertly and consistently. We look at symptoms, what the patient describes; signs, the things that we can find on examination and we use investigations and all are dependent on training that occurs over many years. It should be no surprise that the patterm-matching pathway is over on the right of our map.</p>

<h3 id="learning-through-doing---feedback">Learning through doing - feedback</h3>

<p>We learn through a process of ‘doing’ which makes feedback essential. As a result, in the map you can see an iterative loop where we’re continuously learning.</p>

<p><img src="/images/mapcamp21/map-ehr.004-feedback-loops.png" alt="Feedback loops and iteration" /></p>

<p>You can imagine that type of feedback loop in three broad categories of iteration:</p>

<ol>
  <li>
    <p>You’re caring for a single patient and arrange 
tests or treatments and use the results and outcomes to modify the plan.</p>
  </li>
  <li>
    <p>You’re looking at cohorts of people - e.g. those attending a service or facility, or somebody as defined by their characteristics such as ethnicity,  age, problems, diagnoses, or the treatments that they’ve had.</p>
  </li>
  <li>
    <p>You’re trying to advance science so it’s actually clinical research in which a group of patients is compared to another after, perhaps, randomisation, or simple observation.</p>
  </li>
</ol>

<p>Frequently, these three use-cases are treated very separately, which we’ll discuss later. Ask yourself why when they are fundamentally the same, but perhaps at different levels of acuity and consent. All need to be reproducible and replicable across different cohorts.</p>

<h3 id="supporting-continuous-learning">Supporting continuous learning</h3>

<p>For continuous learning we need <em>continuity</em> and a <em>focus on outcomes</em>.</p>

<p>Paradoxically, many digital tools reduce continuity because they are used as part of reducing health and care to a system focused on transactions like a call-centre.</p>

<p>How do we assess what we’re doing unless we’re measuring outcomes in a systematic fashion?</p>

<p>If we need continuity and we want to focus on outcomes we need to routinely and systematically <em>share data</em>. Currently, it is not, except for specific examples such as registries.</p>

<p>Similarly, we’ve got to make sense of that data in context. If we’re going to measure outcomes, it’s no good just assuming that we can interpret those results - we need, as is plotted on the map, <em>meaningful data</em> and supporting <em>data standards</em> in order to aggregate, make sense and deduce.</p>

<p>For example, I’ve got work that shows how a patient with multiple sclerosis progresses over time but that only makes sense if you compare that patient to the rest of the cohort.</p>

<p>We need tools and data in order to support our decision making. That’s because we need to collect the same kind of structured data and inculcate semantic interoperability with widespread implementation of open standards.</p>

<p><img src="/images/mapcamp21/map-ehr.004-interop.png" alt="The need for interoperability" /></p>

<p>Current implementation of standards is patchy. Many implementations are proprietary, and it is usually more difficult to use a standards-based approach than something custom-built that tightly couples components with end-to-end bespoke integration.</p>

<p>In many circumstances, health and care providers need to buy a product in order to implement a standard. That’s akin to you needing to buy encryption for your web browser.</p>

<p>It therefore follows that there is an opportunity to disrupt the status quo with readily available open-source implementations of data standards and making data standards and data interoperability commodity ubiquitous across our ecosystem. This is a cultural change that is gaining momentum.</p>

<p>So the maps help us identify the areas we might wish to focus our attention.</p>

<p>I’ve spent my own time building a suite of <a href="https://github.com/wardle">open-source libraries and services</a> because that work is important and foundational.</p>

<h3 id="building-a-learning-health-system">Building a learning health system</h3>

<p><img src="/images/mapcamp21/map-ehr.005.png" alt="Where to focus" /></p>

<p>If we are to build a learning health system, we 
must therefore:</p>

<ul>
  <li>focus on meaningful outcomes</li>
  <li>consider <em>value</em></li>
  <li>share data - safely distributed at scale with consent and control</li>
  <li>consider how to make sense of data - making it <em>useful</em>.</li>
  <li>open source commodity tools and resourcec that make collecting and making sense of data straightforward - a <em>‘commons’</em> for health and care.</li>
</ul>

<p>We want the right information at the right time; accessible and useful.</p>

<p><img src="/images/mapcamp21/map-ehr.006.png" alt="Plan for learning" /></p>

<p>The next post in this series will explore what we already have in health and care and will be published soon.</p>

<p><em>Mark</em></p>]]></content><author><name></name></author><category term="strategy" /><summary type="html"><![CDATA[This blog post is based on my presentation at MapCamp 2021.]]></summary></entry><entry><title type="html">Data all the way down</title><link href="https://www.wardle.org/clinical-informatics/2021/09/08/data-all-the-way.html" rel="alternate" type="text/html" title="Data all the way down" /><published>2021-09-08T15:14:14+01:00</published><updated>2021-09-08T15:14:14+01:00</updated><id>https://www.wardle.org/clinical-informatics/2021/09/08/data-all-the-way</id><content type="html" xml:base="https://www.wardle.org/clinical-informatics/2021/09/08/data-all-the-way.html"><![CDATA[<blockquote>
  <p>“Turtles all the way down”</p>

  <p><a href="https://en.wikipedia.org/wiki/Turtles_all_the_way_down">https://en.wikipedia.org/wiki/Turtles_all_the_way_down</a></p>
</blockquote>

<p>Are you a clinician about to see a patient, reviewing the patient record in order to prepare for the consultation?</p>

<p>Or are you a manager assessing the demands on your services, and running quality improvement projects in order to improve your outcomes?</p>

<p>Or perhaps you’re a researcher looking to extend knowledge and inform the best clinical practices of tomorrow?</p>

<p>Most of us working in health and care operate in all three of these domains, but historically, each is considered separate.</p>

<p>Patient records might still be on paper, or perhaps you use an electronic patient record system.</p>

<p>Your administrative data including time from referral to different assessments and treatments are held in a patient administrative system, or more likely, across multiple administrative systems, one or more for each organisation.</p>

<p>Your research might use a bespoke registry or research system for each of your research projects.</p>

<h1 id="software-in-health-and-care">Software in health and care</h1>

<p>Software is <a href="https://wardle.org/digital/2018/10/23/on-digital.html">like magic</a> offering us the potential to automate and systematise safe and effective clinical processes and most importantly, build learning systems that can continuously monitor and improve from where we are currently.</p>

<blockquote>
  <p>As a result, digital, and being data-driven, means our planning cycles should become shorter and shorter, because we’re using small focused interventions and data to constantly evaluate our hypotheses and build a continuously learning healthcare system.</p>

  <p><a href="https://wardle.org/digital/2018/10/23/on-digital.html">Digital transformation in healthcare, 23 October 2018</a></p>
</blockquote>

<p>But we have major problem:</p>

<p><em>We separate the things that should be the same.and we join the things that should be separate.</em></p>

<p>Here’s an example from the past. The Bristol Heart scandal occurred in Bristol, UK in the 1990s, in which babies died at high rates after cardiac surgery. The report into the scandal including this in its conclusion:</p>

<blockquote>
  <p>The various clinical systems, many of them paper based, differed from one another and had no relationship with the administrative hospital-wide systems. The funding made available in the late 1980s and early 1990s for medical and later clinical audit helped to reinforce this separation by making available to groups of clinicians money for small local computer systems. The lack of any connection between these different systems, one administrative, the others clinical, for collecting data cannot be explained solely on the basis of some technical or technological reason. It was just as strongly a reflection of a mindset that clinical matters were the sole domain of clinicians and non-clinical matters, to do with the management of resources and with the movement of patients into and through the hospital, were the preserve of managers and administrators.</p>

  <p>Bristol Heart Enquiry, 2001.
(quoted in <a href="https://wardle.org/clinical-informatics/2017/06/06/using-data-patient-safety.html">“Using data for patient safety”</a>)</p>
</blockquote>

<p>But why do we separate clinical from administrative systems, and both of those from research systems?</p>

<p>There are a variety of reasons, including understanding who has responsibility for procuring, funding and designing such systems as well as issues of information governance.</p>

<p>The false separation of the needs of these three - administration, clinical practice and research, is so embedded in our culture that we even often use different tools, languages and frameworks to build the software - you might run R and python for your research, your analytics by an off-the-shelf package such as Qlik, and your electronic health record software in java or C++.</p>

<p>But are they so different really?</p>

<p>It’s straightforward to think of examples in which it is helpful to combine data relating to what is traditionally considered to be administrative with clinical data. One good example is the flow of patients through a hospital - lengths of stay are best understood in the context of medical problems and co-morbidities. Another might be making sense of clinical or diagnostic information, such as how many patients are referred with problem ‘x’. It’s all data, and it makes little sense that we use widely different software for the different purposes to which we put our data.</p>

<p>So what’s the solution?</p>

<h1 id="a-single-system">A single system?</h1>

<p>So why not have a <em>single system</em>? What a seductive yet naive idea!</p>

<p>Its naive because what do we know about building effective, adaptable, flexible computer software?</p>

<blockquote>
  <p>“Flow is difficult to achieve when each team depends on a complicated web of interactions with many other teams. For a fast flow of change to software systems, we need to remove hand-offs and align most teams to the main streams of change within the organization. However, many organizations experience huge problems with the responsibility boundaries assigned to teams. Typically, little thought is given to the viability of the boundaries for teams, resulting in a lack of ownership, disengagement, and a glacially slow rate of delivery.”
Skelton, Matthew. Team Topologies (Kindle Locations 2147-2151). IT Revolution Press. Kindle Edition.</p>
</blockquote>

<p>We <em>have</em> to break up our complex health and care domain into smaller subunits. It’s just too complicated and its safer and more effective to break up our problems into smaller tractable problems.</p>

<p>Health professionals can be seduced by the idea of buying a ‘single system’ in order to solve their problems because, quite rightly, they’ve been scarred by their experiences having to use multiple ‘systems’ in order to get the information that they need - paper records, that system, another system, another login, a different login.</p>

<p>But, but but!</p>

<blockquote>
  <p>But there is a paradox. We know that the worst ways to create a seamless “one system” approach are thinking we can build a single monolithic application or expect the procurement of a “system” to solve, for example, closer working and communication between health and social care.</p>

  <p>The best way to slow down delivery is to have an approach to technical architecture and wider governance structures that slows down the software value chain. The key to software delivery is delivery, and yet some organisations treat software as if they were managing capital projects such as building a new road or a bridge. In most cases, the technology is the easy bit; its the implementation on the ground across multiple complex adaptive systems that is most difficult. But we also need a design that makes it easy and safe to change.</p>

  <p>People who think building technology is difficult and complex tend to want to centralise and control its development, because that feels less risky, but that approach is wrong.</p>

  <p><a href="https://wardle.org/strategy/2019/09/22/health-technology.html">“Healthcare and Technology, September 2019”</a></p>
</blockquote>

<p>If we believe that software can make our work in health and care more effective, are we using the right methods in order to safely deliver that software, at pace?</p>

<p>We know existing software systems, whether for direct patient care, for analytics / governance or for research, we seem to make the same mistakes again and again:</p>

<blockquote>
  <p>Too often, software architecture and system design occurs as a consequence of organisational or management structures, rather than stepping back and truly understanding the problem domain and how to carve it up into manageable chunks. You need to understand Conway’s Law, and the reverse Conway’s manoeuvre - and place the patient foremost in the design and architecture of your systems.
<a href="https://wardle.org/ehr/2021/06/30/dependency-conway.html">The dependencies between architecture, governance and standards, June 2021</a></p>
</blockquote>

<p>The conclusion must be, therefore, that we need to <em>step back and re-appraise how we architect, fund and standardise digital health and care</em>.</p>

<p>We can get a ‘single system’ by focusing on data, standards, architecture and modularity, and enable innovation and continuous improvement - and not unduly limit our ability to adapt and change to the ever changing requirements the complex adaptive domains of health and care throw at us!</p>

<h1 id="towards-data-data-driven-health-and-composable-modularity-in-software-infrastructure">Towards data, data-driven health and composable modularity in software infrastructure.</h1>

<p>Firstly, we’ve got to recognise that fundamentally, it’s all just data.</p>

<p>We need to collect, make sense of and use data in order to do what we want to do, whether that’s running our health services, seeing an individual patient, improving our services or furthering our medical knowledge.</p>

<p>Secondly, we need structured meaningful <em>data</em> and that means data standards.</p>

<p>If we think you have had a myocardial infarction, then we need computer systems that can make the same sense of the data, and we need to think of our different sources as reference data as <em>products</em> - and use ‘product-thinking’ in how we publish, document, and make those data available - it needs to consider user experience of that data product in just as much of a way as a user-facing application.</p>

<p>Thirdly, we need to make using data, and using data standards as easy as possible.</p>

<p>No-one starts building software nowadays from nothing, but instead make use of a range of standard building blocks in order to achieve the functionality they want - whether its a cryptography library, or machine learning, or networking, these elements are commodities - usually open-source, widely available and widely-tested.</p>

<p>So if you’re starting to build clinical applications now, where are the building blocks, the commodity libraries, frameworks and services with which you can innovate?  Where is the platform on which you can build?</p>

<p>We need readily-available software modules that can make consuming and making sense of data as easy as possible. That means many software services that simply wrap reference data in order to make it usable in whatever context you need and that those services are composable with others much like an orchestra is made up of many different sections, instruments and musicians. And who can argue with Aristotle? “the whole is greater than the sum of its parts”.</p>

<h1 id="what-are-the-building-blocks">What are the building blocks?</h1>

<p>When I first built an electronic health record system, I needed the basics - understanding who the patient is, who the professional was, when someone was seen, where they were seen, what were the characteristics that help define the cohort - such as problem, diagnosis, treatment whether by surgery or drug, and geographical indicators, or derived indicators such as socio-economic deprivation.</p>

<p>I’ve realised that building such functionality into a single system is wrong.</p>

<p>As such, I’ve developed replacements as individual software components. Each is usable in isolation but I can combine them, whether as libraries in a single application or as a suite of individually running microservices, in order to solve problems. Each is designed for use in operational administrative, clinical and research systems - so are usable for direct care, service management or in my analytics. Each is open source.</p>

<ul>
  <li><a href="https://github.com/wardle/hermes">hermes</a> - a SNOMED CT terminology server and library.</li>
  <li><a href="https://github.com/wardle/dmd">dmd</a> - UK dictionary of medicines and devices server and library</li>
  <li><a href="https://github.com/wardle/clods">clods</a> - UK organisational and geographical reference data, including a FHIR r4 server</li>
  <li><a href="https://github.com/wardle/deprivare">deprivare</a> - UK deprivation indices made available in-process or via a microservice</li>
  <li><a href="https://github.com/wardle/concierge">concierge</a> - integration / interoperability with other NHS software services including patient identity / demographics / staff indexes / PAS / document repositories etc Each underlying system can be viewed through the lens of an open standard - e.g. a HL7 FHIR view.</li>
  <li><a href="https://github.com/wardle/trud">trud</a> - easy access to NHS Digital’s UK reference data services</li>
  <li><a href="https://github.com/wardle/hades">hades</a> - a FHIR R4 SNOMED terminology server</li>
  <li><a href="https://github.com/wardle/nhspd">nhspd</a> - UK postal code database with links to geographical, administrative and organisational units</li>
</ul>

<p>Let’s look at two examples in more detail:</p>

<p><a href="https:/github.com/wardle/hermes">hermes</a> makes SNOMED CT available to you easily, so you can embed as a library and run in-process or run as a terminology server. I use this to make it easy to use and make sense of SNOMED CT - both in clinical applications <em>and</em> in my analytics pipelines.</p>

<p><a href="https://github.com/wardle/clods">clods</a> allows you to download and make sense of organisational reference data. I use that with the NHS postcode directory (make available via <a href="https://github.com/wardle/nhspd">nhspd</a> to find out where a patient lives and make sense of those data - including linking to indices of deprivation. My software applications record the date, time and place of clinical encounters, and can leverage those codes - and so other systems not under my control can understand that a particular encounter occurred at, say, the University Hospital of Wales. Using these data is essential for interoperability.</p>

<p><a href="https://github.com/wardle/dmd">dmd</a> allows you to download and make sense of drug data in the UK. What kind of drugs is that patient given? How have the doses changed over time? How many milligrams of <em>that</em> ingredient is the patient taking? Is this patient on a type of immunosuppressant drug? This library and microservice takes the NHSBSA dm+d distribution and makes it usable in your applications - whether that’s for clinical care or for running your analytics.</p>

<p>For example, I switch on different functionality in my homegrown EPR based on the diagnoses of a specific patient - e.g. does this patient have a type of motor neurone disease - while doing the same in analytics - e.g. give me all of the patients who have received a type of botulinum toxin - while doing the same in clinical research - e.g. how do the outcomes of multiple sclerosis vary by socioeconomic deprivation?</p>

<h1 id="whats-needed-next">What’s needed next?</h1>

<p>We need to think carefully about how we conceive and build the clinical applications of the future. They should be data-focused, data-driven and made up of a blend of open, modular services.</p>

<p>So what are the shared services that are needed for clinical, administrative and research applications?</p>

<p>There is usually a pattern - usually interesting but often parochial data sources - we need to identify those and make them more usable - by treating them as first-class data products. That means at a minimum, good documentation, persistent non-reused identifiers and simple ways of tracking publication such as a good metadata.</p>

<p>It’s too often the case that governmental data are published on a portal for download, but do not meet these minimum requirements, making it difficult to write software that can recognise  publication of a new version and download that latest issue.</p>

<p>After the data, we need easily usable computing services that wrap that data product and make it accessible to applications, irrespective of context. That means building software that provides an API, whether in-process or as distributed microservices.</p>

<p>It is usually necessary to provide different abstractions across these services to make it easier for clients to consume health and care computing services. That usually means providing a ‘view’ of those data in an open standard.</p>

<p>For example, while <a href="https://github.com/wardle/clods">clods</a> adopts the DCB0090 standard for organisation data, that is a UK standard. Most applications will simply want to make use of organisational data at a simpler, high-level, so I make a FHIR R4 server available with a <a href="https://github.com/wardle/clods/blob/main/fhir_r4/com/eldrix/clods/fhir/r4/convert.clj">few lines of code</a> mapping DCB0090 into FHIR to make it simpler to consume.</p>

<p>So I don’t expect client software to understand the different categories of health and care organisation in DCB0090, but we can provide a ‘facade’ across those data to map into data and formats that clients can understand, such as X.500, FHIR or openEHR and provide those data on-demand. So, for example, I can view the NHS Wales’ staff directory in X.500 format (native) or as a facade using a <a href="https://www.w3.org/TR/vocab-org/">W3C organization ontological</a> view or as a <a href="https://www.hl7.org/fhir/practitioner.html">HL7 FHIR ‘view’</a>. It’s all data.</p>

<p>Data are first-class and we should treat data products as first-class as well, together with composable software tools that make using those data products in our applications, whether user-facing for direct care, analytics or research.</p>

<p><em>Mark</em></p>

<p>PS. The title of this post alludes to the phrase “Turtles all the way down”, an expression of the problem of infinite regress.</p>]]></content><author><name></name></author><category term="clinical-informatics" /><summary type="html"><![CDATA[“Turtles all the way down” https://en.wikipedia.org/wiki/Turtles_all_the_way_down]]></summary></entry><entry><title type="html">Data analytics using SNOMED CT</title><link href="https://www.wardle.org/snomed/2021/07/26/making-valuesets.html" rel="alternate" type="text/html" title="Data analytics using SNOMED CT" /><published>2021-07-26T15:14:14+01:00</published><updated>2021-07-26T15:14:14+01:00</updated><id>https://www.wardle.org/snomed/2021/07/26/making-valuesets</id><content type="html" xml:base="https://www.wardle.org/snomed/2021/07/26/making-valuesets.html"><![CDATA[<p>There’s a problem with using SNOMED CT for data analytics; in this post, I’ll explain the issue and how to mitigate! If you’re using SNOMED CT for data analytics, you <em>will</em> encounter this issue, and need to handle it!</p>

<h1 id="data-analytics-using-snomed-ct">Data analytics using SNOMED CT</h1>

<p>Imagine I’m building a real-time analytics pipeline for patients with multiple sclerosis.</p>

<p>I need to understand patient outcomes - and that means we need to define patient data into <em>cohorts</em>.</p>

<p>A cohort is a group of patients with shared characteristics.</p>

<p>That cohort might be defined by diagnosis, by treatment type, by age, by gender, by geography, by levels of socio-economic deprivation, or by something else.</p>

<p>SNOMED CT is a sophisticated and comprehensive clinical terminology that provides codes representing many of these characteristics. SNOMED is special because
it isn’t simply a flat list of codes, but instead it is an ontology. As a result SNOMED defines concepts and the relationships between them.</p>

<p>For example, it defines <em>multiple sclerosis</em> as a type of <em>demyelinating disorder</em>. This means, used properly, I can not only search health and care
data for patients with multiple sclerosis, but I can also search for demyelinating diseases and patients recorded as having multiple sclerosis will be
included in that cohort simply as a result of the SNOMED ontological hierarchies. I don’t need end-users to record a diagnosis of <em>demyelinating disorder</em>, 
but I can search for that and include all disorders that are a sub-type of that disorder.</p>

<p>I can do the same for drugs in SNOMED CT - so I might want to search for drugs that contain, say, Glatiramer acetate  - an immunological drug used in multiple sclerosis. Because the UK drug extension for SNOMED CT includes relationships such as “Has specific active ingredient”, it is straightforward to use SNOMED 
CT to slice and dice our health and care data in order to make valuable inferences.</p>

<h1 id="hermes">Hermes</h1>

<p>Hermes is an open-source terminology server that I wrote.</p>

<p>I have a tiny ($3/mo) demonstration server running - for example, you can look at detailed information about multiple sclerosis (SNOMED code 24700007) by going to <a href="http://128.140.5.148:8080/v1/snomed/concepts/24700007/extended">http://128.140.5.148:8080/v1/snomed/concepts/24700007/extended</a>.</p>

<p>You can have your own server running in minutes by <a href="https://github.com/wardle/hermes">following the instructions</a>, or use my demonstration links below. It can even download and install SNOMED automatically if you live in the UK.</p>

<p>Here’s an example of one of the endpoints:</p>

<p><a href="http://128.140.5.148:8080/v1/snomed/concepts/24700007/extended">http://128.140.5.148:8080/v1/snomed/concepts/24700007/extended</a>:</p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"concept"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">24700007</span><span class="p">,</span><span class="w">
    </span><span class="nl">"effectiveTime"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2002-01-31"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"active"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w">
    </span><span class="nl">"moduleId"</span><span class="p">:</span><span class="w"> </span><span class="mi">900000000000207008</span><span class="p">,</span><span class="w">
    </span><span class="nl">"definitionStatusId"</span><span class="p">:</span><span class="w"> </span><span class="mi">900000000000074008</span><span class="w">
  </span><span class="p">},</span><span class="w">
  </span><span class="nl">"descriptions"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
    </span><span class="p">{</span><span class="w">
      </span><span class="nl">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">41398015</span><span class="p">,</span><span class="w">
      </span><span class="nl">"effectiveTime"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2017-07-31"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"active"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w">
      </span><span class="nl">"moduleId"</span><span class="p">:</span><span class="w"> </span><span class="mi">900000000000207008</span><span class="p">,</span><span class="w">
      </span><span class="nl">"conceptId"</span><span class="p">:</span><span class="w"> </span><span class="mi">24700007</span><span class="p">,</span><span class="w">
      </span><span class="nl">"languageCode"</span><span class="p">:</span><span class="w"> </span><span class="s2">"en"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"typeId"</span><span class="p">:</span><span class="w"> </span><span class="mi">900000000000013009</span><span class="p">,</span><span class="w">
      </span><span class="nl">"term"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Multiple sclerosis"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"caseSignificanceId"</span><span class="p">:</span><span class="w"> </span><span class="mi">900000000000448009</span><span class="p">,</span><span class="w">
      </span><span class="nl">"refsets"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
        </span><span class="mi">900000000000509007</span><span class="p">,</span><span class="w">
        </span><span class="mi">900000000000508004</span><span class="p">,</span><span class="w">
        </span><span class="mi">999001261000000100</span><span class="w">
      </span><span class="p">],</span><span class="w">
      </span><span class="nl">"preferredIn"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
        </span><span class="mi">900000000000509007</span><span class="p">,</span><span class="w">
        </span><span class="mi">900000000000508004</span><span class="p">,</span><span class="w">
        </span><span class="mi">999001261000000100</span><span class="w">
      </span><span class="p">],</span><span class="w">
      </span><span class="nl">"acceptableIn"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
        
      </span><span class="p">]</span><span class="w">
    </span><span class="p">},</span><span class="w">
  </span><span class="p">],</span><span class="w">
  </span><span class="nl">"parentRelationships"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"116680003"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
      </span><span class="mi">6118003</span><span class="p">,</span><span class="w">      </span><span class="mi">138875005</span><span class="p">,</span><span class="w">      </span><span class="mi">404684003</span><span class="p">,</span><span class="w">
      </span><span class="mi">123946008</span><span class="p">,</span><span class="w">      </span><span class="mi">118234003</span><span class="p">,</span><span class="w">
      </span><span class="mi">128139000</span><span class="p">,</span><span class="w">      </span><span class="mi">23853001</span><span class="p">,</span><span class="w">
      </span><span class="mi">246556002</span><span class="p">,</span><span class="w">      </span><span class="mi">363170005</span><span class="p">,</span><span class="w">
      </span><span class="mi">64572001</span><span class="p">,</span><span class="w">      </span><span class="mi">118940003</span><span class="p">,</span><span class="w">
      </span><span class="mi">414029004</span><span class="p">,</span><span class="w">      </span><span class="mi">362975008</span><span class="p">,</span><span class="w">
      </span><span class="mi">363171009</span><span class="p">,</span><span class="w">      </span><span class="mi">39367000</span><span class="p">,</span><span class="w">
      </span><span class="mi">80690008</span><span class="p">,</span><span class="w">      </span><span class="mi">362965005</span><span class="w">
    </span><span class="p">]</span><span class="w">
  </span><span class="p">},</span><span class="w">
  </span><span class="nl">"refsets"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
    </span><span class="mi">991381000000107</span><span class="p">,</span><span class="w">
    </span><span class="mi">999002271000000101</span><span class="p">,</span><span class="w">
    </span><span class="mi">991411000000109</span><span class="p">,</span><span class="w">
    </span><span class="mi">1127581000000103</span><span class="p">,</span><span class="w">
    </span><span class="mi">1127601000000107</span><span class="p">,</span><span class="w">
    </span><span class="mi">900000000000497000</span><span class="p">,</span><span class="w">
    </span><span class="mi">447562003</span><span class="w">
  </span><span class="p">],</span><span class="w">
  </span><span class="nl">"preferredDescription"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">41398015</span><span class="p">,</span><span class="w">
    </span><span class="nl">"effectiveTime"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2017-07-31"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"active"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w">
    </span><span class="nl">"moduleId"</span><span class="p">:</span><span class="w"> </span><span class="mi">900000000000207008</span><span class="p">,</span><span class="w">
    </span><span class="nl">"conceptId"</span><span class="p">:</span><span class="w"> </span><span class="mi">24700007</span><span class="p">,</span><span class="w">
    </span><span class="nl">"languageCode"</span><span class="p">:</span><span class="w"> </span><span class="s2">"en"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"typeId"</span><span class="p">:</span><span class="w"> </span><span class="mi">900000000000013009</span><span class="p">,</span><span class="w">
    </span><span class="nl">"term"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Multiple sclerosis"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"caseSignificanceId"</span><span class="p">:</span><span class="w"> </span><span class="mi">900000000000448009</span><span class="w">
  </span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>It can also provide a FHIR terminology server API via <a href="https://github.com/wardle/hades">hadex</a>.</p>

<p>Hermes operates as a library, or a microservice. It is designed to be immutable once running - so that we might have services running providing different versions of SNOMED CT, each load-balancing. Other terminology servers do not use this approach, but instead update-in-place, with management of versions within the same terminology. I prefer multiple small services and switch at the API gateway level, or reverse proxy to different versions, all of which run independently.</p>

<hr />

<h1 id="the-snomed-ct-expression-constraint-language">The SNOMED CT expression constraint language</h1>

<p>The specification for the SNOMED CT expression constraint language (ECL) is <a href="http://snomed.org/ecl">available here</a>. 
It’s a way of defining a set of SNOMED CT concepts.</p>

<p>Here’s a simple example:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>&lt;&lt;  73211009 |Diabetes mellitus|     
</code></pre></div></div>

<p>This means, give me a set of codes that represent diabetes mellitus, including its sub-types. You can see the codes this expands to <a href="http://128.140.5.148:8080/v1/snomed/expand?ecl=&lt;&lt;73211009">here</a>.</p>

<p>When I am building a user interface component to allow a pop-up and autocompletion box, for say, country of birth, I might search based on 
the text the user has entered and limit the search to the set of concepts defined by:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>&lt;370159000|Country of birth|
</code></pre></div></div>

<p>This will mean a search for <a href="http://128.140.5.148:8080/v1/snomed/search?s=cro&amp;constraint=&lt;370159000">“Cro”</a> will give me “Born in Croatia” but exclude “Crohn’s disease”.</p>

<p><a href="http://128.140.5.148:8080/v1/snomed/search?s=cro&amp;constraint=&lt;370159000">http://128.140.5.148:8080/v1/snomed/search?s=cro&amp;constraint=&lt;370159000</a>:</p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">[</span><span class="w">
  </span><span class="p">{</span><span class="w">
    </span><span class="nl">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">459924011</span><span class="p">,</span><span class="w">
    </span><span class="nl">"conceptId"</span><span class="p">:</span><span class="w"> </span><span class="mi">315409004</span><span class="p">,</span><span class="w">
    </span><span class="nl">"term"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Born in Croatia"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"preferredTerm"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Born in Croatia"</span><span class="w">
  </span><span class="p">}</span><span class="w">
</span><span class="p">]</span><span class="w">
</span></code></pre></div></div>

<p>I wouldn’t want to record a diagnostic term in a field that should only record concepts that are a sub-type of country of birth. I can both configure and validate user input.</p>

<p>You can think of ECL as providing a quick and easy way to define a set of codes that you’re interested in. In essence, it builds codelists - a subset of codes which can be used or searched.</p>

<p>As you might expect, you can use boolean logic in an expression to 
combine different terms. For example, if you’re searching for patients who have reduced splenic function you might use</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>&lt;&lt;234319005|Splenectomy| OR &lt;&lt;23761004|Hyposplenism|
</code></pre></div></div>

<p>You can see the results of this expression <a href="http://128.140.5.148:8080/v1/snomed/search?constraint=&lt;&lt;234319005%20OR%20&lt;&lt;23761004">here</a></p>

<p>I use the combination of a user-entered search string (e.g. “MND”) and a constraint to help users enter information in a context-appropriate way - e.g. by type, or by membership of a reference set etc.</p>

<h1 id="building-code-lists">Building code lists</h1>

<p>In summary, we can use the expressions to <em>realise</em> a codeset. For example,
we might want to build a list of diagnoses that are a type of neurological disease, suitable for use when interrogating data sources for an audit or for research.</p>

<p>In HL7 FHIR, the operation to turn an expression like this into a value set is called <em>expansion</em>.</p>

<h1 id="so-whats-the-problem">So what’s the problem?</h1>

<p>SNOMED CT is an evolving clinical terminology. That means it is updated, refined and changed over time. Fortunately, concepts are never deleted, and 
identifiers are never re-used, but concepts can be inactivated.</p>

<p>When this happens, all of its relationships are removed.</p>

<p>What this means in practice is that a now-outdated or redundant concept will not be found when we use the SNOMED CT relationships to define an interesting
set of codes!</p>

<p>Let’s look at an example related to multiple sclerosis.</p>

<p>So we want all patients who have multiple sclerosis?</p>

<p>Won’t the codes we need will be included in the result of:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>&lt;&lt; 24700007
</code></pre></div></div>

<p>That means give me the concept 24700007 and all of its descendants (sub-types). Have a look at the results <a href="http://128.140.5.148:8080/v1/snomed/expand?ecl=&lt;&lt;24700007">here</a>.</p>

<p>No it won’t!</p>

<p>Have a look at <a href="https://termbrowser.nhs.uk/?perspective=full&amp;conceptId1=24700007">24700007 in the SNOMED online browser</a>  - and start clicking on the children and
the children of those children. The expression ‘«24700007’ will, in essence, return all of them for you in an instant.</p>

<h2 id="legacy-data">Legacy data!</h2>

<p>But let’s look at our legacy data. In our electronic health and care record, we have some old data that includes the concept <a href="https://termbrowser.nhs.uk/?perspective=full&amp;conceptId1=155023009">155023009</a> - this is an outdated, inactive concept representing multiple sclerosis, and it won’t be found using <code class="language-plaintext highlighter-rouge">&lt;&lt;24700007</code>! It won’t be found because inactivated concepts don’t have any active relationships.</p>

<p>This a problem!</p>

<p>That patient, just because they’ve been recorded as having multiple sclerosis using a term now inactive, potentially won’t show up in our dataset! This isn’t an uncommon scenario; and it is a problem that will increase as more health and care software uses SNOMED CT.</p>

<h1 id="what-are-the-potential-solutions">What are the potential solutions?</h1>

<p>There are four options:</p>

<ol>
  <li>Highlight now inactive concepts in our dataset and manually update to the modern equivalents ie. fix our source data manually by flagging to end clinical users. Fix the problem by fixing our source data.</li>
  <li>When processing our dataset, highlight inactive concepts and append the modern replacements or equivalents.</li>
  <li>When generating searches, include outdated concepts in the code lists.</li>
  <li>Provide easy access to multiple versions of SNOMED CT, selectable at runtime, so that inferences can be made based on the date the data were entered at each point.</li>
</ol>

<p>There are a variety of trade-offs for each option but each can make use of the historical
association reference sets that are provided as part of SNOMED CT.</p>

<p>An historical reference set provides a linkage between a now-outdated concept and what it might be better represented as nowadays.</p>

<p>Unfortunately, this isn’t as simple as it might sound. Some concepts are genuinely now regarded as wrong - or may be better represented using one of many more specific terms. Imagine a disease we used to think of as a single entity, but now realise that diagnostic entity is better represented
as one of three different more specific entities, that might or might not
be exactly equivalent?</p>

<p>Here are some example reference sets that will help us:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Name                     Concept identifier

REPLACED-BY              900000000000526001
SAME-AS                  900000000000527005
POSSIBLY-EQUIVALENT-TO   900000000000523009
</code></pre></div></div>

<p>The simplest is REPLACED-BY. There’ll be a 1:1 mapping between an old concept and a new concept if there is one that is conceptually REPLACED-BY the new one! But some concepts are truly outdated, and there will be some ambiguity in how to use that now outdated term.</p>

<p>We can even ask SNOMED to give us all of the historical association reference set types: <a href="http://128.140.5.148:8080/v1/snomed/expand?ecl=%3C900000000000522004">http://128.140.5.148:8080/v1/snomed/expand?ecl=&lt;900000000000522004</a></p>

<p>Result:</p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">[</span><span class="w">
  </span><span class="p">{</span><span class="w">
    </span><span class="nl">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">900000000001151017</span><span class="p">,</span><span class="w">
    </span><span class="nl">"conceptId"</span><span class="p">:</span><span class="w"> </span><span class="mi">900000000000523009</span><span class="p">,</span><span class="w">
    </span><span class="nl">"term"</span><span class="p">:</span><span class="w"> </span><span class="s2">"POSSIBLY EQUIVALENT TO association reference set"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"preferredTerm"</span><span class="p">:</span><span class="w"> </span><span class="s2">"POSSIBLY EQUIVALENT TO association reference set"</span><span class="w">
  </span><span class="p">},</span><span class="w">
  </span><span class="p">{</span><span class="w">
    </span><span class="nl">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">900000000001152012</span><span class="p">,</span><span class="w">
    </span><span class="nl">"conceptId"</span><span class="p">:</span><span class="w"> </span><span class="mi">900000000000524003</span><span class="p">,</span><span class="w">
    </span><span class="nl">"term"</span><span class="p">:</span><span class="w"> </span><span class="s2">"MOVED TO association reference set"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"preferredTerm"</span><span class="p">:</span><span class="w"> </span><span class="s2">"MOVED TO association reference set"</span><span class="w">
  </span><span class="p">},</span><span class="w">
  </span><span class="p">{</span><span class="w">
    </span><span class="nl">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">900000000001154013</span><span class="p">,</span><span class="w">
    </span><span class="nl">"conceptId"</span><span class="p">:</span><span class="w"> </span><span class="mi">900000000000525002</span><span class="p">,</span><span class="w">
    </span><span class="nl">"term"</span><span class="p">:</span><span class="w"> </span><span class="s2">"MOVED FROM association reference set"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"preferredTerm"</span><span class="p">:</span><span class="w"> </span><span class="s2">"MOVED FROM association reference set"</span><span class="w">
  </span><span class="p">},</span><span class="w">
  </span><span class="p">{</span><span class="w">
    </span><span class="nl">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">900000000001157018</span><span class="p">,</span><span class="w">
    </span><span class="nl">"conceptId"</span><span class="p">:</span><span class="w"> </span><span class="mi">900000000000526001</span><span class="p">,</span><span class="w">
    </span><span class="nl">"term"</span><span class="p">:</span><span class="w"> </span><span class="s2">"REPLACED BY association reference set"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"preferredTerm"</span><span class="p">:</span><span class="w"> </span><span class="s2">"REPLACED BY association reference set"</span><span class="w">
  </span><span class="p">},</span><span class="w">
  </span><span class="err">...</span><span class="w">
</span></code></pre></div></div>

<p>When operating interactively, we can ask our user to resolve ambiguities and map to a more modern term.
But what about for analytics? We might be processing millions of health records in which it will not be practical to update legacy terms by hand.</p>

<p>We are left with two options:</p>

<ol>
  <li>
    <p>Pre-process each health and care record mapping legacy terms to modern equivalents.</p>
  </li>
  <li>
    <p>Pre-process our searches, valuesets and code lists so that they include legacy inactive concepts as well as the modern equivalents.</p>
  </li>
</ol>

<h3 id="pre-process-the-health-and-care-record">Pre-process the health and care record</h3>

<p>For our inactive term, in our data pipeline, we could look for
this concept’s historical association reference sets and include some or all of the modern replacements in-place.</p>

<p>We then perform analysis on a modified patient record that has been updated to use only active terms.</p>

<p>We can do this easily by identifying now inactivated concepts, and following the historical associations for that concept.</p>

<p>Let’s try a worked example:</p>

<p>You can see that <a href="https://termbrowser.nhs.uk/?perspective=full&amp;conceptId1=155023009">155023009</a> is inactive - and you can see the same via <a href="http://128.140.5.148:8080/v1/snomed/concepts/155023009">hermes</a>:</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">155023009</span><span class="p">,</span><span class="w">
  </span><span class="nl">"effectiveTime"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2002-01-31"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"active"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w">
  </span><span class="nl">"moduleId"</span><span class="p">:</span><span class="w"> </span><span class="mi">900000000000207008</span><span class="p">,</span><span class="w">
  </span><span class="nl">"definitionStatusId"</span><span class="p">:</span><span class="w"> </span><span class="mi">900000000000074008</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>We can use this concept’s reference set membership to see how SNOMED thinks we might be able to map into the current version of the terminology:</p>

<p><a href="http://128.140.5.148:8080/v1/snomed/concepts/155023009/historical">http://128.140.5.148:8080/v1/snomed/concepts/155023009/historical</a>:</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"900000000000527005"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
    </span><span class="p">{</span><span class="w">
      </span><span class="nl">"id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"cc542ff9-d695-52ff-a20b-8091e5b0145b"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"effectiveTime"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2002-01-31"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"active"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w">
      </span><span class="nl">"moduleId"</span><span class="p">:</span><span class="w"> </span><span class="mi">900000000000207008</span><span class="p">,</span><span class="w">
      </span><span class="nl">"refsetId"</span><span class="p">:</span><span class="w"> </span><span class="mi">900000000000527005</span><span class="p">,</span><span class="w">
      </span><span class="nl">"referencedComponentId"</span><span class="p">:</span><span class="w"> </span><span class="mi">155023009</span><span class="p">,</span><span class="w">
      </span><span class="nl">"targetComponentId"</span><span class="p">:</span><span class="w"> </span><span class="mi">24700007</span><span class="w">
    </span><span class="p">}</span><span class="w">
  </span><span class="p">]</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>This end-point picks out only the historical association reference set types and lists them conveniently keyed by the association reference type. This makes it straightforward to follow SAME-AS, REPLACED-BY or POSSIBLY-EQUIVALENT-TO links.</p>

<p>So here we see that <code class="language-plaintext highlighter-rouge">155023009</code> is linked to <code class="language-plaintext highlighter-rouge">24700007</code> by virtue of a <code class="language-plaintext highlighter-rouge">SAME-AS</code> definition - <a href="http://128.140.5.148:8080/v1/snomed/concepts/900000000000527005/extended">900000000000527005</a>.</p>

<h3 id="pre-process-our-searches-valuesets-and-codelists">Pre-process our searches, valuesets and codelists</h3>

<p>Alternatively, when we’re generating a list of codes for our code list, we could reverse this process and look at the modern concept(s) in which we are interested and get back the legacy inactive identifiers that we want to include.</p>

<p>We can ask hermes to expand any arbitrary SNOMED expression constraint language (ECL) expression:</p>

<p>e.g.</p>

<p><a href="http://128.140.5.148:8080/v1/snomed/expand?ecl=%3C%3C24700007">http://128.140.5.148:8080/v1/snomed/expand?ecl=%3C%3C24700007</a>:</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">[</span><span class="w">
  </span><span class="p">{</span><span class="w">
    </span><span class="nl">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">1223980016</span><span class="p">,</span><span class="w">
    </span><span class="nl">"conceptId"</span><span class="p">:</span><span class="w"> </span><span class="mi">24700007</span><span class="p">,</span><span class="w">
    </span><span class="nl">"term"</span><span class="p">:</span><span class="w"> </span><span class="s2">"MS - Multiple sclerosis"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"preferredTerm"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Multiple sclerosis"</span><span class="w">
  </span><span class="p">},</span><span class="w">
  </span><span class="err">...</span><span class="w">
</span></code></pre></div></div>

<p>But we can also ask for the expansion to include historical associations:</p>

<p><a href="http://128.140.5.148:8080/v1/snomed/expand?ecl=%3C%3C24700007&amp;include-historic=true">http://128.140.5.148:8080/v1/snomed/expand?ecl=%3C%3C24700007&amp;include-historic=true</a></p>

<p>You’ll see that our expanded codelist now also includes outdated, inactivated concepts from past versions of SNOMED. We can now use this expanded list for our data analytics.</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">[</span><span class="w">
  </span><span class="p">{</span><span class="w">
    </span><span class="nl">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">27239011</span><span class="p">,</span><span class="w">
    </span><span class="nl">"conceptId"</span><span class="p">:</span><span class="w"> </span><span class="mi">16092000</span><span class="p">,</span><span class="w">
    </span><span class="nl">"term"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Cord multiple sclerosis"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"preferredTerm"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Cord multiple sclerosis"</span><span class="w">
  </span><span class="p">},</span><span class="w">
  </span><span class="p">{</span><span class="w">
    </span><span class="nl">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">30986014</span><span class="p">,</span><span class="w">
    </span><span class="nl">"conceptId"</span><span class="p">:</span><span class="w"> </span><span class="mi">18353007</span><span class="p">,</span><span class="w">
    </span><span class="nl">"term"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Brain stem multiple sclerosis"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"preferredTerm"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Brain stem multiple sclerosis"</span><span class="w">
  </span><span class="p">},</span><span class="w">
  </span><span class="p">{</span><span class="w">
    </span><span class="nl">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">1223980016</span><span class="p">,</span><span class="w">
    </span><span class="nl">"conceptId"</span><span class="p">:</span><span class="w"> </span><span class="mi">24700007</span><span class="p">,</span><span class="w">
    </span><span class="nl">"term"</span><span class="p">:</span><span class="w"> </span><span class="s2">"MS - Multiple sclerosis"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"preferredTerm"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Multiple sclerosis"</span><span class="w">
  </span><span class="p">},</span><span class="w">
  </span><span class="err">...</span><span class="w">
</span></code></pre></div></div>

<h1 id="conclusions">Conclusions</h1>

<p>Managing real-life health and care data is complex; health informatics needs to build software, ideally open-source, that manages some of these complexities. Open-source tools are ideal, because we create a shared community.</p>

<p><a href="https://github.com/wardle/hermes">Hermes</a> is a library and microservice that provides some of that capability in relation to SNOMED CT and other terminologies.</p>

<p>You cannot ignore the issue of managing codes now thought of as inactive, or outdated from your health and care data, but instead you need to think carefully about how to manage change over time.</p>

<p>Hermes provides a number of ways of managing those changes including versioned distributions, methods to identify and understand how to map outdated concepts to modern equivalents, to methods to create codelists based on expressions that optionally include historical equivalents of their members.</p>

<p><em>Mark</em></p>]]></content><author><name></name></author><category term="snomed" /><summary type="html"><![CDATA[There’s a problem with using SNOMED CT for data analytics; in this post, I’ll explain the issue and how to mitigate! If you’re using SNOMED CT for data analytics, you will encounter this issue, and need to handle it!]]></summary></entry></feed>