Module 12 — Migration & Version Strategy
1. 1.0.x vs. 1.1.x vs. 2.0.0-Mx — Choosing for a Project Timeline
This is the direct, fully-developed version of the framework first sketched in Module 1 — worth restating now with everything the intervening modules have added as concrete evidence.
| Track | Stability posture | What’s landed there in 2026 | Right choice when… |
|---|---|---|---|
| 1.0.x | Production-stable, maintenance-only | Mature ChatModel/ChatClient, established Advisors, stable provider/vector-store starters | The project has a near-term ship date, the team can’t absorb mid-project breaking changes, and the agentic/MCP-frontier features aren’t required |
| 1.1.x | Stable, but actively receiving backported features | Advisors API maturation, Structured Output refinements, earlier MCP integration work | The project needs capability beyond 1.0.x’s feature set but the timeline still doesn’t tolerate milestone-level breaking-change risk |
| 2.0.0-Mx | Pre-GA, explicit breaking-change risk between milestones | First-class MCP annotations, the architectural direction toward Recursive Advisors and the agentic stack (Modules 8-9), Jackson 3 | Prototyping, internal tooling, learning/evaluation work, or a project timeline genuinely long enough to absorb migration effort before going live |
The decision framework, stated as a question sequence rather than a fixed rule — because “which version” genuinely depends on project-specific tolerance for two different kinds of risk, and a good answer shows that tradeoff explicitly rather than reciting a version number:
- Does the project need anything that only exists in 2.0.0-Mx (native MCP annotations, the agentic-stack direction)? If no — stop here, default to 1.0.x or 1.1.x, since there’s no reason to absorb milestone risk for capability you don’t need.
- If yes — how long until this needs to be in production, and does that timeline include slack for at least one migration cycle as 2.0 moves through further milestones toward GA? If the timeline is tight, build the MCP/agentic-dependent piece as an isolated, swappable module (the same “isolate the volatile dependency behind a seam” discipline from Modules 3, 7, and 8) so the rest of the system can ship on 1.x while the frontier piece absorbs migration risk independently.
- Is the team prepared to read every milestone’s release notes before upgrading, not just bump the version number? If genuinely no — that’s itself a strong signal to stay off 2.0.0-Mx until GA, regardless of feature appeal, because milestone-to-milestone breaking changes assume active monitoring as a precondition, not an optional nicety.
Talking point that synthesizes this as the single most important judgment call in this entire track: “If I had to compress this whole Spring AI curriculum into one piece of advice for a team adopting it today, it would be this version-selection framework, not any individual API. Every other module in this track — tool calling, memory, RAG, MCP, the agentic stack — is genuinely valuable, but none of it matters if the team picks the wrong version track for their actual risk tolerance and ships on milestone code they weren’t prepared to actively maintain through breaking changes. This is the same judgment a senior architect applies to any pre-GA dependency — Kubernetes alpha/beta API versioning is the most direct parallel from earlier in this curriculum’s broader scope — and the specific lesson transfers cleanly: pre-GA capability is real and worth tracking, but adopting it in production is a deliberate risk decision, not a default.”
2. Jackson 2 → Jackson 3 Migration — What It Touches
What changed, concretely: 2.0.0-M3 included a Jackson 2 → Jackson 3 migration as one of its explicitly flagged breaking changes (alongside the MCP annotation renames covered in Module 8). Jackson is the JSON serialization library underpinning a large amount of what Spring AI does internally — Structured Output deserialization (Module 3), tool-call argument/result marshaling (Module 4), and the JSON Schema generation that drives both.
Why this single dependency upgrade has outsized blast radius, worth understanding even without memorizing every API difference between Jackson 2 and 3: any application code that does its own custom JSON handling alongside Spring AI — a custom StructuredOutputConverter that manually parses JSON (Module 3), a custom advisor that inspects or transforms request/response payloads (Module 5), or any place where application code directly imports com.fasterxml.jackson.* types rather than going through Spring AI’s own abstractions — is exposed to this migration directly, not just Spring AI’s internals.
The practical exposure-surface checklist worth running through before any 2.0 upgrade:
- Does any custom code import Jackson types directly (
ObjectMapper,JsonNode, annotations like@JsonProperty) rather than relying purely on Spring AI’s.entity()mapping? - Does the broader application (outside the AI-specific code) also depend on Jackson 2 for unrelated serialization (REST controllers, persistence)? If so, a Jackson 3 migration forced by the Spring AI BOM could create a version conflict with the rest of the application’s own Jackson dependency, not just an isolated AI-feature concern.
- Are there any custom
StructuredOutputConverterimplementations (Module 3) with Jackson-specific logic baked in that would need updating for Jackson 3’s API surface?
Talking point: “A dependency migration like Jackson 2 to 3 is exactly the kind of change that looks contained in a changelog bullet point but can ripple far wider than the AI-specific code — the actual risk assessment isn’t ‘does my Spring AI code still compile,’ it’s ‘does anything else in this application depend on Jackson 2 in a way that now conflicts with what the Spring AI BOM is pulling in.’ I’d treat this the same way I’d treat any major version bump of a foundational, widely-shared library anywhere in a dependency tree — run a full dependency tree analysis (mvn dependency:tree or the Gradle equivalent) before upgrading, specifically looking for version conflicts on Jackson artifacts, rather than assuming the BOM’s version alignment (Module 1) automatically protects against conflicts with dependencies the BOM doesn’t manage.”
3. The OpenAI Module’s Move to the Official openai-java SDK
What changed structurally: the spring-ai-openai module now uses the official openai-java SDK across all OpenAI model types (chat, embedding, image, audio, moderation) — and the previously separate spring-ai-openai-sdk module was removed and merged into this change. This was explicitly called out as a no-breaking-change migration for typical usage: existing spring.ai.openai properties, builders, and chat options remain fully compatible.
Why this is worth understanding as an architectural signal, not just a changelog entry, directly extending the build-vs-wrap discussion from Module 1’s section 2: this is Spring AI deliberately moving from “Spring AI maintains its own HTTP client/request logic for OpenAI” to “Spring AI composes the officially-maintained SDK underneath its own abstraction.” That’s a meaningfully different engineering posture — it means OpenAI-specific correctness, new-feature support, and edge-case handling increasingly inherits directly from the vendor’s own maintained client, rather than depending on Spring AI’s team independently keeping pace with every OpenAI API nuance.
The practical implication worth naming, even though this specific migration was advertised as non-breaking: when a portable framework wraps an official SDK underneath, provider-specific escape-hatch code (Module 2’s OpenAiChatOptions-style direct access) becomes more likely to track the official SDK’s own object model and versioning, not just Spring AI’s own internal conventions. That’s generally a good thing for staying current with the provider’s actual capabilities, but it also means provider-specific code is now coupled to two layers’ release cadences (Spring AI’s own version, and the underlying SDK’s version) rather than one.
Talking point: “This is the same ‘compose the official SDK rather than reimplement it’ pattern Module 1 flagged as a maturity signal, and seeing it actually land in a real release is a good confirmation of that trajectory. The practical lesson for anyone using OpenAI-specific options beyond the portable API: when you reach into provider-specific configuration, you’re now implicitly depending on the official openai-java SDK’s own object shapes, not just Spring AI’s wrapper around it — which argues for treating that escape-hatch code with the same version-pinning care as any other direct third-party SDK dependency, not assuming it’s fully insulated by Spring AI’s own versioning.”
4. Reading Release Notes and Upgrade Guides as a Discipline
This is the closing, unifying practice for the entire Spring AI track — and it’s worth presenting explicitly as a discipline, a repeatable habit, rather than a one-time task, because nearly every concrete fact covered across all twelve modules of this track came from exactly this kind of release-note vigilance: the memory-advisor-ordering fix (Modules 1, 4, 5, 6), the streaming chunk-dropping bug (Modules 2, 5, 11), the ToolContext history removal (Module 4), the immutability shift (Module 11), the CVE cadence (Module 11) — none of these are things you’d discover by reading the framework’s main documentation page once; they’re things you only catch by treating release notes as a primary, ongoing source of architectural truth.
A concrete reading practice worth establishing, synthesizing the version-selection framework from section 1 with the CVE discipline from Module 11:
- Read release notes in this order of priority: security/CVE fixes first (Module 11), explicitly-flagged breaking changes second, then bug fixes relevant to features you actually use, then new features last. This ordering matters because it’s roughly in order of “how much does missing this cost me” — a missed CVE is the highest-cost miss; a missed feature announcement just means you adopt it a release later than you could have.
- For any milestone release specifically, read the upgrade notes before attempting the upgrade, not after hitting a compile error — Module 1 already flagged this as the practical discipline; this section is just making explicit that it has to be a standing habit applied every milestone, not a one-time lesson learned from this curriculum and then forgotten.
- Maintain your own internal changelog/migration log for your specific application’s Spring AI dependency, noting which release introduced which behavior change relevant to your code — this turns “what changed in the version we’re on” from a re-derivation task (re-reading every release note back to your last upgrade) into a quick lookup, valuable specifically because this framework’s pace makes that re-derivation cost real if it’s not tracked incrementally.
The closing talking point for the entire Spring AI track, synthesizing everything: “If there’s one meta-lesson underneath all twelve modules of this track, it’s that Spring AI in 2026 rewards exactly the kind of architectural discipline that’s been the throughline of my own background — clean abstraction boundaries, explicit ordering and dependency management, defensive isolation of volatile dependencies, and treating version upgrades as a deliberate risk decision rather than a routine bump. None of that is unique to AI tooling — it’s the same engineering judgment I’d apply to any fast-moving dependency in an enterprise Java system. What’s different here is the pace: a framework shipping breaking changes inside milestone releases, multiple CVE fixes a year, and genuinely new architectural territory (MCP, agentic loops) being defined in real time means that judgment gets exercised far more frequently than it would against a slower-moving, decade-mature library. The skill being tested isn’t really ‘do you know Spring AI’s API surface’ — it’s ‘do you have the architectural discipline to safely adopt a framework that’s still actively defining itself,’ and that’s a genuinely transferable, senior-level capability regardless of which specific framework it’s applied to.”