A hospital that has run the same laboratory system for eleven years is not going to replace it because a new platform would prefer that. Nor should it. That system works, the people who use it are fast on it, and the risk of moving is real.
So the honest starting position for any new platform is that it will spend its entire life talking to software it did not write and cannot change. Integration is not a phase of the project. It is the permanent condition.
FHIR gets the attention. HL7 v2 carries the traffic. Admissions, orders and results in most hospitals still move as pipe-delimited v2 messages, and they will for years.
The practical consequence is that a platform needs to be fluent in both, and comfortable translating between them without losing meaning. The messages you will meet most are the admission and transfer events, the order messages, and the result messages and each has local dialect.
That dialect is the real work. Two hospitals running the same vendor's system will populate the same segment differently, because at some point somebody made a reasonable local decision and it stuck. There is no specification you can read that will tell you this. You find it by looking at real messages.
FHIR gives you well-modelled resources and a sane way to ask for them. It does not give you agreement about what goes in them, which is where projects actually stall.
Profiles and implementation guides exist precisely because the base spec is deliberately permissive. Two conformant systems can be mutually useless if they have made different choices about identifiers, code systems or cardinality. Conformance is necessary and nowhere near sufficient.
Our rule of thumb: agree the profile before writing the mapping, and write the profile down somewhere both sides can read. A shared document beats a shared assumption.
Identity is the first and worst. A patient has a hospital number, possibly a national identifier, and possibly several historical numbers from merges that happened before anyone currently employed arrived. A mapping that assumes one stable identifier per person will be wrong, and it will be wrong in the direction of merging two people.
Codes are the second. Local codes, standard codes, and the mapping table between them that somebody maintains in a spreadsheet. That table is a production dependency whether or not anyone treats it as one.
Time is the third, and it is underestimated constantly. Timestamps arrive in local time with no offset, in a different timezone from the sending system, or as the time the message was generated rather than the time the event occurred. Those are three different things and clinical reasoning depends on the difference.
Units are the fourth. A number without a unit is not a result, and a unit that is implied by convention rather than carried in the message is a unit waiting to be misread.
Interfaces are not built once. The other system gets upgraded, a new field starts being populated, an old one quietly stops, and nobody tells you because from their side nothing broke.
So we assume drift and build for detection rather than prevention. Validate structurally on the way in and reject clearly rather than coercing. A message that fails loudly on Tuesday is enormously cheaper than a field that has been silently empty since Tuesday.
Keep the raw inbound message. When something is wrong six weeks later, the only thing that reliably settles the argument is what was actually sent.
Synthetic test messages are written by people who have read the spec. Real messages are written by systems that were configured by people who had a deadline. They differ.
The most valuable day in an integration project is the day you get a de-identified sample of genuine production traffic, because that is the day the estimate becomes real. Ask for it first, not last.
If any of the above matches a problem you are dealing with, we are happy to go deeper than a blog post reasonably can.