BizTalk Migration: Understand the Existing Integrations Before Designing the Target

On a BizTalk migration this year, the hardest problem was understanding what the existing integrations actually did.
The target architecture mattered, of course. But before choosing how to rebuild an interface, we needed to establish which behaviour had to survive the migration. The integrations were running, the source artifacts were available, and there was some documentation. That still left important questions unanswered about side effects, execution sequences, and dependencies.
One interface made the problem concrete.
The Interface That Looked Like an Easy First Wave
It processed only a handful of messages a day. From a volume perspective, it looked like an obvious candidate for an early migration wave: limited traffic and an apparently small scope.
Reading the sources told a different story. The interface cleared tables in a legacy database, then triggered a chain of stored procedures across two database engines.
That behaviour changed the migration discussion. We needed to understand the effects on the data, the sequence of downstream operations, and what would happen if only part of the chain completed successfully.
A migration plan built primarily on message volume would have started with this interface. Its low traffic concealed dependencies and destructive operations that deserved closer examination.
Message volume tells you how often an integration runs. It does not tell you what an execution can change.
Reconstructing Behaviour from the Artifacts
The missing behaviour was in the .odx orchestration, the .btm maps, the XSDs, and the test messages. It had never made its way into the available documentation.
Each artifact carried part of the explanation:
- Orchestrations described the processing sequence and control flow.
- Maps described transformations between message structures.
- XSDs defined the message contracts.
- Test messages provided concrete examples of the data those contracts and transformations handled.
The work was in connecting those pieces. An inventory can tell you that an orchestration references a map. Migration planning needs an explanation of what that relationship means within the wider processing chain.
So I built a pattern engine that reads the sources and reconstructs behaviour: orchestrations, transformations, contracts, Enterprise Integration Patterns (EIP), and dependencies between flows.
The purpose was to turn scattered implementation details into an analysis that people could inspect and discuss. Which integrations depend on the same downstream components? Where do transformations sit in the processing sequence? Which behaviours need explicit validation before a cutover?
Source analysis also has limits. It can only reconstruct what the available artifacts reveal. Runtime configuration, missing external code, and operational knowledge can still leave gaps. Those gaps need to remain visible and guide further investigation.
Deterministic First, Model Second
The engine is written in Go. It computes the scores, detects patterns, and builds the dependency graph.
The language model reads those results. It does not produce them.
This separation is central to the design. Given the same inputs, rules, and engine version, the analysis should be reproducible. An assistant can explain the findings in different words, but the underlying calculation should remain stable.
When someone asks why an integration landed in wave 3, I want an answer grounded in the analysis: the dependencies identified, the behaviours detected, and the criteria used. That explanation should not depend on model sampling.
A score is still decision support. The final wave assignment needs human review, including business constraints, operational readiness, and any unresolved questions. Deterministic computation makes the technical basis easier to inspect; it does not remove the need for judgement.
Giving an Assistant Access to the Analysis
Once the analysis existed, I put an MCP server in front of it so an assistant could query the results directly.
That makes it possible to approach the estate through questions such as:
- What does this integration actually do?
- Which downstream dependencies need review before migration?
- What makes this interface more complex than its message volume suggests?
- What evidence supports its proposed migration wave?
The assistant provides a conversational way to explore the analysis. Its usefulness depends on the quality and completeness of the information underneath it.
This is also why I kept a clear boundary around what the assistant can access.
Read-Only, Closed World
The MCP tools expose the analysis through a read-only, closed-world interface, behind a revocable token. Each tool is annotated with readOnlyHint and idempotentHint to describe its expected behaviour.
Those annotations communicate intent; they are not an access-control mechanism. The actual boundary depends on the tool implementation and the permissions available to the server.
In this design, the assistant reads the analysis. It never touches BizTalk, the databases, or the target repositories. There is no autonomous write path from the assistant to the systems involved in the migration.
That boundary keeps the assistant’s role focused: help people understand the existing integrations and prepare decisions. Changes to the source or target systems remain part of the migration team’s controlled implementation process.
Planning Around Behaviour and Dependencies
Return to the interface that processed a handful of messages a day. Once its database operations and dependencies were visible, the planning questions became much more specific.
Which downstream procedures must be considered together? What data state should exist before and after execution? How should partial failure be handled? Which tests would demonstrate that the migrated implementation preserves the required behaviour?
Those are useful inputs to a migration plan. They help determine sequencing, validation effort, and the evidence needed before moving an interface into production.
They also make the plan easier to explain. A wave assignment can be discussed in terms of concrete behaviours and dependencies, with uncertainty called out explicitly.
The most demanding engineering in this project was making a legacy estate legible enough that an assistant became useful at all.
Before deciding how to migrate an integration, we need to be able to explain what it does. When that knowledge is missing from the documentation, reconstructing it becomes a migration workstream in its own right.