The Main Thread: Java, AI & Software Architecture

Markus Eisele

Practical lessons on enterprise Java, software architecture, and AI coding agents for experienced developers, architects, and technical leads. Drawing on more than two decades in the industry, Markus Eisele explores what works, where tools fall short, and how to make better decisions about the software you build and maintain. www.the-main-thread.com

  1. 15h ago

    Agent Skills: From Repeated Instructions to a Reusable Procedure

    I use agent skills throughout the production process behind The Main Thread, from tutorial structure and voice guidance to technical review and publication preparation. In this episode, I explain how those instructions developed from things I kept repeating to the model, and how I decide whether a skill helps me reach something I’m prepared to publish. We start with the open Agent Skills format: what belongs in SKILL.md, how the description helps the agent select a procedure, and how references and scripts become available when the task needs them. Then we follow a pull request summary through the inputs it needs: the right comparison base, the relevant diff, the requirements, and evidence for any claimed test result. The skill has to explain what happens when one of those is missing. Access and permissions also depend on the host running the agent. To create a skill, I start with a recurring task whose result I know how to assess. The examples and corrections help me separate this task’s facts from the procedure another task can reuse. Well, a clean file is only part of that. I discuss comparing the task with and without the skill, checking the agent’s actions, and counting the time someone spends reviewing and correcting the result. Sharing the skill adds another responsibility. A colleague needs to understand the inputs, environment assumptions, expected result, and version they are using. I draw on the team memory idea from the Book of Bob, my internal IBM guidance, and discuss repository ownership, skillsaw checks, and distributing skill files through versioned Maven artifacts with SkillsJars. I also talk openly about my use of AI across this production process. Drafting can become cheaper while checking the result takes time and understanding. I accept each reader and listener making their own judgment about that choice. The episode acknowledges the ethical and environmental implications and leaves them for a separate discussion. The episode is narrated locally with Fish S2 Pro and my trained LoRA voice adapter. Chapters * 00:00 — Intro * 00:12 — My publishing process and using AI openly * 02:55 — What an agent skill contains * 06:18 — Structure, voice, review, and publishing * 09:25 — Designing a pull request summary skill * 13:19 — Creating a skill from examples and corrections * 16:35 — Evaluating the result and the review effort * 19:27 — Sharing skills as team memory * 23:39 — Outro Links and references Format and evaluation * Agent Skills specification — The open folder format, SKILL.md metadata, optional files, and progressive disclosure. * Evaluating skill output quality — Designing realistic cases and comparing results with and without a skill. Related Main Thread articles * Agent Skills Need Guardrails, Not Just Prompts — Task procedures, supporting scripts, and checks around repeated agent work. * Use skillsaw to Lint Bob Skills Before They Drift — Structural checks, CI, and a separate local task to examine the skill’s behavior. * Stop Copying AI Skills: Version IBM Bob Instructions with Maven — The SkillsJars example discussed in the episode, packaging and consuming versioned skill files. Research * SkillsBench: Benchmarking How Well Agent Skills Work Across Diverse Tasks — Version 4 of a benchmark studying skill effects across different tasks and agent configurations. * SWE-Skills-Bench: Do Agent Skills Actually Help in Real-World Software Engineering? — Version 1 of a study comparing software engineering tasks with and without skills. The studies use different tasks and setups. Their findings are a reason to evaluate a skill in your own process, rather than assume a reported gain will transfer. Thanks for reading The Main Thread! Subscribe for free to receive new posts and support my work. This is a public episode. If you would like to discuss this with other subscribers or get access to bonus episodes, visit www.the-main-thread.com

    Agent Skills: From Repeated Instructions to a Reusable Procedure
  2. Nov 4

    Automatic Model Routing: What Your Coding Assistant Chooses for You

    When my coding assistant shows automatic in the model selector, my reaction is mixed. I want the convenience. I also want to see and influence the decision, especially when an investigation gets stuck or a new model gives me something else to try. In this episode, I explain what would make me comfortable handing over routine model selection. A cheaper model can leave more of my budget available for a difficult task. Repeated explanations and corrections can consume that saving, along with my time. I want to compare what it costs to reach a change I can accept. I use a hypothetical Java service that creates duplicate orders after a timeout to make the tradeoffs concrete. Finding the retry configuration may be straightforward. Establishing what happened after the receiving service saved an order needs more investigation. That gives a router more information to act on, while showing why a stronger model also needs the right code, context, and tools. I also question my own expectations about the latest models. Research on novelty and confidence gives me a reason to check whether a response handled the difficult constraint, even when I like the answer. Those studies have limits, which I discuss alongside what a developer can reasonably judge in a familiar service. Then I share experiments I’m driving at IBM to connect context information with recorded activity. I want to follow what the assistant read, which claims it checked, and where an assumption entered the conversation. Being able to inspect that history would help me decide whether to provide more information, request a more capable model, or accept the change. The episode is narrated locally with Fish S2 Pro and my trained LoRA voice adapter. Chapters * 00:00 — Intro * 00:12 — Why my reaction to automatic routing is mixed * 02:45 — More help from the same budget * 05:22 — Routing a Java duplicate-order investigation * 08:15 — Hidden choices and the control I want * 10:43 — What depends on the harness * 12:49 — New models, expectations, and judging quality * 15:39 — Making conversations easier to inspect * 18:28 — The controls that would make me comfortable * 20:47 — Outro Links and references * RouteLLM: Learning to Route LLMs with Preference Data — Research on choosing between stronger and cheaper models using preference data. Its reported cost savings describe the evaluated benchmarks, rather than a guaranteed saving on a development task or subscription. * SWE-Router: Routing in Multi-turn Agentic Software Engineering Tasks — A cheaper model takes exploratory turns before the router decides whether to continue or escalate. In this implementation, the stronger model restarts from the original prompt; the exploration counts toward the cost. * Quantifying the Novelty Bias when Evaluating Interactive Prototypes — A study of 48 participants using functionally identical technology pairs, including chatbots, presented as old and new. The brief interactions with general users do not establish an effect size for professional developers doing sustained coding tasks. Author-hosted paper. * Do Users Write More Insecure Code with AI Assistants? — An earlier security-task study in which assistant users produced less secure code overall while expressing greater confidence in its security. It used an older model and specific tasks; it is not a ranking of current assistants. This is a public episode. If you would like to discuss this with other subscribers or get access to bonus episodes, visit www.the-main-thread.com

    Automatic Model Routing: What Your Coding Assistant Chooses for You
  3. Oct 28

    AI Context Windows: Comparing the Cost of an Accepted Coding Result

    I see two things coming up regularly in our internal IBM Bob Slack channel. People want a larger context window, and people are surprised how expensive their conversations become. Well, both make sense when you look at what the agent sends to the model with each request. In this episode, I explain what a context window contains and how IBM Bob, Codex, Claude Code, and Gemini CLI decide what goes into it. We follow twenty model requests, starting at 10,000 input tokens and adding another 10,000 each time. The last request contains 200,000 tokens, but the total across those requests is 2.1 million. At the standard GPT-6.1 Sol rate used in the episode, that is $4.20 for input alone, with caching excluded and output or tool charges added separately. Then we look at how caching and compaction change the calculation. I also share guidance from the Book of Bob, my collection of agentic development best practices on an internal IBM website. Unfortunately, I haven’t been able to open source it so far. We go through standing context, tool descriptions, focused task briefs, and short handover notes, using a Java email-validation example to make the advice concrete. And, of course, there is the business model. The labs sell tokens, but serving those tokens costs them money too. I consider those incentives alongside the cost of reaching a result I can accept, including retries and review time. A larger window can help with a change across several modules; the capacity number alone doesn’t tell us what the task will cost or how reliably the model will connect the details. Model limits and API prices are a snapshot from October 14, 2026. The Codex defaults mentioned came from my local installation and can differ across accounts and sessions. The episode is narrated locally with Fish S2 Pro and my trained LoRA voice adapter. Chapters * 00:00 — Intro * 00:12 — Wanting more context and wondering about the bill * 01:52 — What goes into a context window * 04:25 — Model limits and the applications around them * 06:57 — How 200,000 tokens become 2.1 million across a conversation * 09:45 — Caching, pricing tiers, and response time * 13:01 — Long-context reasoning and what compaction can lose * 15:11 — Context practices from the Book of Bob * 20:24 — Selling tokens and the cost of an accepted result * 22:52 — What to measure in your own coding sessions * 24:45 — Outro Links and references Context windows and coding tools * IBM Bob: Context window management — Public documentation for the context breakdown, task limit, and compaction threshold. * OpenAI: GPT-6 Astra and Codex configuration reference — Model limits and application settings. The configuration reference is separate from the local catalog observation discussed in the episode. * Claude model overview and Claude Code extended context — Model capacities and the conditions that apply inside Claude Code. * Gemini 3.1 Pro, Gemini 4 Argon, and Gemini CLI model selection — Check the selected model and whether its limit describes input or a shared input/output window. Conversation costs and caching * OpenAI API pricing — Source for the GPT-6.1 Sol example: $2 per million uncached input tokens at the standard short-context rate on the research date. * OpenAI conversation state — Continuing a conversation with a response ID, including how earlier input is billed. * OpenAI prompt caching — Prefix reuse and the conditions for cache hits. * Claude API pricing — Cache rates and model-specific long-context pricing. Context practices and research * Anthropic: Effective context engineering for AI agents — Retrieving information when needed, compaction, notes, and separate agent contexts. * Lost in the Middle: How Language Models Use Long Contexts — Research on how information placement affected the models tested. These results describe earlier models. * RULER: What’s the Real Context Size of Your Long-Context Language Models? — Evaluating long-context behavior beyond finding a single fact. Thanks for reading The Main Thread! Subscribe for free to receive new posts and support my work. This is a public episode. If you would like to discuss this with other subscribers or get access to bonus episodes, visit www.the-main-thread.com

    AI Context Windows: Comparing the Cost of an Accepted Coding Result
  4. Oct 21

    Spec-Driven Development: Why Passing Tests Can Miss a Changed Requirement

    I want coding agents to take on more of the implementation. I also want to understand what I’m accepting when they come back with a result. Writing a spec gives those decisions somewhere to happen, but it gives me another set of files to keep current too. In this episode, I revisit the trade-offs around spec-driven development and the fear that the requirements, tests, and code start describing different behavior. In the cart-policy example with IBM Bob, changing the design from 30 to 90 days left the old tests green and every requirement ID connected. Well, the links were intact. The new decision hadn’t reached the assertions. Then I go through the Java inventory experiment with TLA+ and Lean. A model checker found the sequence that let two callers reserve the last item. A separate proof checked the arithmetic rule. Neither check proved that the Java implementation matched its formal definition, so the tests and the review of that connection stayed part of the process. I also consider how this changes as models become more capable. Needing fewer instructions about the implementation doesn’t settle the business rule or tell me which checks are enough to accept a change. The episode is narrated locally with Qwen3-TTS and my trained LoRA voice adapter. Chapters * 00:00 — Intro * 00:10 — What I want from specifications * 01:37 — The cost of vague specs and the maintenance of detailed ones * 04:05 — IBM Bob and the cart policy: 90 days in the design, 30 days in the code * 07:07 — How specifications and code drift apart * 09:31 — One item, two callers: trying TLA+ and Lean with Java * 13:23 — What the formal checks prove, and what they leave open * 15:46 — What better models could change * 19:02 — What I’d keep for the next Java change * 20:36 — Outro Related articles * Why AI Coding Agents Still Need Clear Specs — The correction effort that follows a vague request, and how acceptance criteria can make the intended behavior easier to check. * Spec-Driven Development Needs an Exit Strategy — Small change briefs, the cost of keeping design prose current, and where lasting constraints belong after implementation. * Formal Verification for AI-Generated Java — The inventory example with Java tests, a TLA+ model, and a Lean proof, including the limits of each check. Thanks for listening to The Main Thread podcast! Subscribe for free to receive new posts and support my work. This is a public episode. If you would like to discuss this with other subscribers or get access to bonus episodes, visit www.the-main-thread.com

    Spec-Driven Development: Why Passing Tests Can Miss a Changed Requirement
  5. Oct 15

    Qwen3-TTS Voice Cloning: What I Learned Building a Local Podcast Voice

    I wanted to bring The Main Thread Podcast back, and recording the episodes myself has a few practical problems. I do my job from home, and I don’t have access to a quiet studio. The kids come in and out, there’s noise in the house, and getting a long, uninterrupted recording is a challenge. Reading a transcript also changes how I sound. It’s different from freely explaining something to another developer. So my main motivation was to put the time into writing a script and developing a narrative that’s easy to follow, then have a text-to-speech model read it in my voice. I didn’t want to go with a paid provider either. I had tried training my voice on a provider’s platform, and the samples sounded flat to me. So I started trying local models on my MacBook. Those experiments eventually led to training a local voice adapter of my own. The target was an article of around three thousand five hundred words, which should become roughly twenty to twenty-five minutes of audio. That’s a long time to listen to a voice that keeps stopping in strange places. It also means the model has to get through technical names, longer explanations, and changes between paragraphs without sounding like a different person halfway through. Thanks for listening to The Main Thread Podcast! Subscribe for free to receive new posts and support my work. This is a public episode. If you would like to discuss this with other subscribers or get access to bonus episodes, visit www.the-main-thread.com

    Qwen3-TTS Voice Cloning: What I Learned Building a Local Podcast Voice
  6. Jun 28

    How to Find Regression Risks in a Quarkus App Before You Write the Feature

    User unregistration sounds simple until you look at the rest of the system. In a booking application, “delete this user” is never just one DELETE endpoint. It touches active bookings, foreign keys, historical reads, cache state, and the uncomfortable question every senior team eventually has to answer: is this really a hard delete, or is it deactivation, anonymization, or something in between? That is why I like the workflow Alex Soto shows in the latest video. He does not start by asking IBM Bob to write the feature. He starts by asking it to map the regression impact before implementation. That is a much better use of an assistant. Use the Agent Before the Feature Code I still think the basic rule is simple: use AI to go faster on work you understand already. If you hand core business logic to a model and hope for the best, you are outsourcing the part you still need to own. Impact analysis is different. Here the agent helps you read the shape of the system, collect likely break points, and build a regression plan before the feature branch gets noisy. In this case, “shift-left” just means you do the regression thinking before you touch the code that will trigger it. That matters because deletion flows are where business rules stop looking like CRUD. One missing constraint check and you get orphaned rows, broken history screens, or a second-order production issue that only shows up after cleanup jobs or cache refreshes run. Efficient. Also bad. Why IBM Bob Fits This Job It’s not IBM Bob’s magic that makes this work: It is context. Bob runs against the workspace, so it can inspect the actual resources, entities, and services in the application. That gives it a real basis for the regression plan. It is reading the model you are about to change, not guessing from a paragraph of pasted code. That is the difference. A browser chat can still help, but a workspace-aware tool can follow relationships across the codebase. For something like user unregistration, that is where the interesting bugs live. The Prompt Is Small on Purpose Alex starts with a constrained prompt: “Act as a tester. Identify the areas of the system that should be included in regression testing after changes are made to add the unregistration of a user. Don’t implement anything.” Don't implement anything keeps the session focused on analysis. Once an assistant starts writing code too early, it tends to collapse design, impact analysis, and implementation into one optimistic blob. That is fine for demos. It is much less fine when a deletion rule can invalidate half your booking flow. What a Good Regression Plan Should Surface Because Bob can inspect the workspace, it can point at actual system boundaries that deserve tests before the first delete path is implemented. In this example, the report highlights the kind of things I would expect a senior developer to check immediately: * Active booking rules. What happens if the user still has a live reservation, an open payment flow, or another stateful process attached to the account? * Database integrity. Do deletes cascade, fail fast, orphan records, or silently leave history in a shape the rest of the system no longer understands? * Historical read paths. Can past bookings, reports, and audit views still load after the user record changes or disappears? * Cache and derived state. If user data is cached, indexed, or copied into another read model, what gets invalidated and when? * API behavior. What does the endpoint return when the user has already been unregistered, cannot be removed, or does not exist anymore? This is where the workflow earns its keep. The assistant is not replacing engineering judgment. It is helping you build the list you will review anyway, only faster and with fewer blind spots. The Senior Java Questions Start After the First Report A regression plan is a good start. It is still only a start. If I were taking this feature forward, I would want the team to answer a few more questions before implementation: * Is “unregister” really delete? In many systems the right answer is soft delete, anonymization, or status change because legal retention, reconciliation, or audit rules still apply. * Where is the transaction boundary? If one part of the removal fails, what rolls back, and what can be left half-done? * What is the concurrency story? If one request unregisters the user while another creates or confirms a booking, which invariant wins? * What happens on the second call? Idempotence matters here. A repeated delete should not create fresh damage. * Which tests belong at which level? Some checks belong close to the domain model. Others need @QuarkusTest, HTTP assertions, persistence wiring, or a packaged-app check with @QuarkusIntegrationTest. That last point is worth calling out. A lot of teams mix regression planning with test implementation planning and then get a giant bag of tests with no clear boundary. I would rather make that mapping explicit up front. Turn the Report Into Proof The report is useful because it changes what we test first. For a change like this, I would usually split the work into a few layers: * business-rule tests around the unregister decision itself * persistence tests for relationship handling and transaction rollback * @QuarkusTest coverage for the HTTP and CDI wiring * packaged-app checks with @QuarkusIntegrationTest when the deployment shape matters After that, continuous feedback matters more than one big test run at the end. Quarkus continuous testing is a good fit here because the regression plan will usually touch multiple paths, and you want quick confirmation while the model is still moving. Coverage still matters, but later. First make sure the dangerous branches are tested. Then measure what you actually covered. Further Reading * Quarkus: Testing Your Application for @QuarkusTest, transactions in tests, mocks, and @QuarkusIntegrationTest. * Quarkus: Continuous Testing if you want fast feedback while the unregister flow is still changing. * JUnit User Guide for the current programming model, extension model, and test organization options once the regression plan becomes a real suite. * JaCoCo Coverage Counters if you want branch, instruction, and complexity numbers to reflect the paths that actually matter. * IBM Bob if you want to try the workspace-aware analysis workflow Alex uses in the video. If your unregister flow goes through JPA or Hibernate relationships, read the deletion and orphan-handling rules in the ORM documentation for the exact version you run. That is one of those areas where small mapping details decide whether cleanup is correct or catastrophic. Why I Like This Pattern I like this workflow because it uses the assistant where assistants are actually useful: reading a codebase, building a candidate regression map, and giving you a sharper review checklist before implementation starts. You still need to decide the delete semantics. You still need to own the transaction model. You still need to write the tests that prove the feature is safe. But you do not need to build the first regression checklist from memory every single time. Let the tool help there. Save your energy for the decisions that still need an engineer. This is a public episode. If you would like to discuss this with other subscribers or get access to bonus episodes, visit www.the-main-thread.com

    How to Find Regression Risks in a Quarkus App Before You Write the Feature
  7. Jun 22

    Using JaCoCo and AI to Prioritize Test Coverage in Quarkus Teams

    In the latest video, my co-author Alex Soto shows a test coverage workflow I actually like. I do not care about 100% coverage as a badge. I care about whether the important parts of the system are tested. Past that point, the number gets expensive fast. In some domains you still need to hit a minimum threshold because regulation says so. Ok. That might be a real constraint. Building out tests should follow a more simpler question: where are the gaps that matter, and what should we test first? Alex uses IBM Bob with a JaCoCo report from a Quarkus backend to answer exactly that. It is a boring workflow in the best way. The report grounds the analysis, so you get a list of real testing gaps instead of a random AI guess list. Quarkus and JaCoCo Without Extra Setup Before Bob can analyze anything, you need a coverage report. Quarkus keeps this part simple. Add the quarkus-jacoco dependency to your pom.xml or build.gradle file, run mvn verify, and Quarkus writes the report to target/jacoco-reports. The automatic setup from quarkus-jacoco only covers tests annotated with @QuarkusTest. If your suite also includes standard unit tests without that annotation, you need the regular JaCoCo Maven plugin for those. If you use both at the same time, configure them carefully or you will hit duplicate instrumentation errors. For many Quarkus projects, the extension alone is the simple option and usually the right one. Give the Agent the Real Report This workflow works because the agent has real context. If you ask a coding agent to improve test coverage with no grounding, it will guess. If you give it the JaCoCo XML report, it can see what is already covered, what is still untouched, and which areas deserve attention first. Here is the exact prompt Alex used for the analysis. Prompt: Act as a tester explorer to find test cases to improve the test coverage based on the current JaCoCo test coverage report placed at src/test/resources/jacoco.xml file. Read the JaCoCo file, explore what code has already been tested, and what tests could be done to improve the coverage. It is not necessary to have 100% coverage, but increasing coverage for relevant cases is necessary. It keeps the agent focused on relevant business logic instead of chasing 100% because the number looks nice in a report. Phases Beat a Giant Test Todo List Bob reads the XML and returns a structured view of the current instruction and branch coverage. More importantly, it points at the places that matter, such as core booking logic or user services sitting at zero percent coverage. After that, Bob breaks the work into phases you can actually use. You get a plan, not a wall of possible tests. * Phase 1: Critical Business Logic (High Priority) * Phase 2: REST API Integration (High Priority) * Phase 3: Entity Layer (Medium Priority) * Phase 4: DTOs and Utilities (Low Priority) It also estimates the coverage you can reach after each phase. That gives you a clear next step and a rough idea what the work buys you. Then you can move straight to a follow-up prompt: Follow-up Prompt: Implement the Phase 1! At that point the coding assistant has a bounded job. It reads the code for that phase, understands which classes are involved, and generates the tests for that slice. That is way better than asking it to “improve coverage” and hoping it reads your mind. What Comes Next I like this example because it shows where AI fits well in normal engineering work. The model works from the actual report, with a clear goal, on code that already exists. That is a much better use of these tools than vibing test coverage. Watch the full video to see the workflow in action. In the next video, Alex will look at test execution performance, because faster feedback matters almost as much as better coverage. You can download Bob and try this yourself at bob.ibm.com. This is a public episode. If you would like to discuss this with other subscribers or get access to bonus episodes, visit www.the-main-thread.com

  8. Apr 3

    API Versioning in Quarkus: The Part AI Doesn’t Solve

    Versioning a REST API is one of those architectural chores that everyone knows they need to do, but nobody actually wants to implement. As your data models evolve, you have to maintain backward compatibility for existing clients while serving new data structures to updated ones. Historically, this meant writing a lot of boilerplate: duplicating entities, adding routing logic, and carefully wiring fallback behavior. The problem is not that this is hard. The problem is that it is easy to get wrong, and once it is wrong, you carry that decision for years. If you want a deeper breakdown of the long-term consequences, the earlier article on Quarkus API Versioning Strategies goes into detail on how these decisions age over time. What changes now is not the problem. What changes is the effort required to implement it. As my co-author Alex Soto (Developer Advocate at IBM) demonstrated in our latest video, generative AI changes the balance. With IBM Bob, you no longer spend time writing versioning plumbing. You spend time choosing the right strategy. And that distinction matters. A Simple Mental Model There are only three real ways to version an API: * Change the URL * Change the request metadata * Change the representation Everything else is just a variation of these three. Bob can implement all of them in seconds. But Bob does not decide which one you should use. What is IBM Bob? Before diving into the strategies, it helps to understand the tool. IBM Bob is an AI coding assistant that understands your project context. It reads your existing Quarkus classes, generates new entities, updates JAX-RS endpoints, and even runs the application in dev mode to validate the result. You are not asking for snippets. You are asking for changes to your system. And this is where things get interesting. Bob will happily generate a second version of your API in seconds. If your strategy is wrong, you just created technical debt faster than ever. 1. Path Versioning (The Pragmatic Choice) This is the most visible approach. The version is part of the URI: /v1/customer /v2/customer In the demo, Alex asked Bob to update the Customer endpoint by renaming the name field to firstname in version two. Bob created a new CustomerV2 entity, added a new @Path("/v2/customer") endpoint, and kept the original version intact. This is simple. It is explicit. It is easy to cache and debug. But it also duplicates your API surface. Every version is a new endpoint, and over time you end up maintaining multiple parallel APIs. This approach works well when you want clarity and when external consumers depend on stable URLs. It becomes harder when you have many versions active at the same time and need to keep them consistent. If you want a deeper comparison of how this scales, the article on Quarkus API Versioning Strategies breaks this down in more detail. 2. Custom Headers (The Clean URI Approach) Here the URI stays the same: /booking The version is passed through a header: Accept-Version: 2 In the demo, Alex asked Bob to rename the customerName field to name in a Booking entity. Bob updated the resource to use @HeaderParam, routing requests to BookingV1 or BookingV2 depending on the header, and defaulting to version one if no header is present. This keeps your URIs clean. Clients do not need to change endpoints. Versioning becomes part of the request metadata. The problem shows up later. After a few versions, you no longer know which clients send which headers. Removing old logic becomes guesswork. You carry old branches in your code because you are not sure who still depends on them. This is where lifecycle management becomes critical. The article on When to Deprecate APIs explains how to handle that transition without breaking consumers. 3. Media Type / Content Negotiation (The REST Purist Way) This approach uses the Accept header with vendor-specific media types: application/vnd.acme.car.v1+json In the demo, Alex asked Bob to rename the year field in a Car entity to date. Bob updated the @Consumes and @Produces annotations to route requests based on the media type, without changing the URI or adding custom headers. This is the most flexible and the most aligned with REST principles. You keep a single resource and serve different representations. But flexibility comes with complexity. Media types are harder to debug. Clients need to construct correct headers. Tooling support is not always consistent. And once you introduce multiple representations, testing becomes more involved. This approach makes sense in large systems where representation evolves independently from the resource. If you want to see how this works in a real integration scenario, the Quarkus Stripe API Versioning Adapter Tutorial shows how to adapt external APIs with similar patterns. The Takeaway The hardest part of API versioning used to be the implementation. With tools like IBM Bob, implementation takes seconds. The hard part is choosing the right strategy and managing its lifecycle over time. Bob will implement whatever you ask for. It will do it correctly and consistently. But it will not fix a bad architectural decision. Pick a strategy that fits your consumers, enforce it consistently, and understand what happens when you need to evolve it. Want to try it on your own codebase? You can check out Bob at ibm.com/bob. This is a public episode. If you would like to discuss this with other subscribers or get access to bonus episodes, visit www.the-main-thread.com

    API Versioning in Quarkus: The Part AI Doesn’t Solve

About

Practical lessons on enterprise Java, software architecture, and AI coding agents for experienced developers, architects, and technical leads. Drawing on more than two decades in the industry, Markus Eisele explores what works, where tools fall short, and how to make better decisions about the software you build and maintain. www.the-main-thread.com

You Might Also Like