<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Mauricio Acuña</title>
    <description>The latest articles on DEV Community by Mauricio Acuña (@mauricio_acua_5650fb8a8c).</description>
    <link>https://dev.to/mauricio_acua_5650fb8a8c</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4144734%2Fc92aa06e-b5a1-459a-a590-404cbe81d814.jpg</url>
      <title>DEV Community: Mauricio Acuña</title>
      <link>https://dev.to/mauricio_acua_5650fb8a8c</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mauricio_acua_5650fb8a8c"/>
    <language>en</language>
    <item>
      <title>WhatsApp Business API Pricing Explained: How Much Does It Cost in 2026?</title>
      <dc:creator>Mauricio Acuña</dc:creator>
      <pubDate>Wed, 07 Oct 2026 21:42:18 +0000</pubDate>
      <link>https://dev.to/mauricio_acua_5650fb8a8c/whatsapp-business-api-pricing-explained-how-much-does-it-cost-in-2026-hdb</link>
      <guid>https://dev.to/mauricio_acua_5650fb8a8c/whatsapp-business-api-pricing-explained-how-much-does-it-cost-in-2026-hdb</guid>
      <description>&lt;p&gt;WhatsApp Business API pricing confuses many businesses. Per conversation? Per message? Per user? What's included for free? This guide breaks it all down clearly.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Core Pricing Model: Messages, Not Conversations
&lt;/h2&gt;

&lt;p&gt;Since July 1, 2025, Meta charges &lt;strong&gt;per delivered template message&lt;/strong&gt;, not per conversation. The rate depends on the template category and on the country code of the recipient's phone number. Per-conversation pricing no longer exists.&lt;/p&gt;

&lt;p&gt;When a customer messages you, a &lt;strong&gt;24-hour customer service window&lt;/strong&gt; opens, and it resets with every new customer message. Inside it you can reply with free-form (non-template) messages, which Meta calls &lt;strong&gt;service messages&lt;/strong&gt;. Once the window closes, you can only reach the customer with a template.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What changes on October 1, 2026&lt;/strong&gt;: service messages are charged too, at the same rate as utility in each market, after &lt;strong&gt;1,000 free per business phone number each month&lt;/strong&gt;. Utility templates sent inside the 24-hour window stop being free as well.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three Template Categories (2026)
&lt;/h2&gt;

&lt;p&gt;Meta restructured categories in 2023. In 2026, there are three template types, each with a different per-message rate, plus service messages:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;th&gt;Examples&lt;/th&gt;
&lt;th&gt;Price Range&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Marketing&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Re-engagement, promotions, customer acquisition&lt;/td&gt;
&lt;td&gt;Offers, newsletters, win-back campaigns&lt;/td&gt;
&lt;td&gt;$0.0109-$0.1597/message&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Utility&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Transactional / post-purchase notifications&lt;/td&gt;
&lt;td&gt;Order updates, appointment reminders, OTP&lt;/td&gt;
&lt;td&gt;$0.0008-$0.0550/message&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Authentication&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;One-time passwords and verification codes&lt;/td&gt;
&lt;td&gt;Login codes, 2FA&lt;/td&gt;
&lt;td&gt;$0.0008-$0.0550/message&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Service&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Free-form replies inside the 24-hour window&lt;/td&gt;
&lt;td&gt;Answers to customer questions, support&lt;/td&gt;
&lt;td&gt;Utility rate, after 1,000 free per number/month (from Oct 1, 2026)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Prices vary significantly by country. The same message costs more in some markets (Netherlands, Germany, France) and less in others (Colombia, Türkiye, India).&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Free
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Messages customers send you&lt;/strong&gt; are always free.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Service messages&lt;/strong&gt;: from October 1, 2026, each business phone number gets &lt;strong&gt;1,000 free per month&lt;/strong&gt;. Unused ones don't roll over. After that, each one is charged at the market's utility rate. Without a payment method in Billing Hub, Meta doesn't deliver service messages beyond the first 1,000.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Free entry point window&lt;/strong&gt;: if the customer came from a Click-to-WhatsApp ad or your Facebook Page button and you reply within 24 hours, a 72-hour window opens in which all messages, templates included, are free.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Utility templates inside the 24-hour window&lt;/strong&gt; are free until September 30, 2026. From October 1, 2026 they are charged like any other utility template.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Pricing by Country
&lt;/h2&gt;

&lt;p&gt;Prices differ by country. These are &lt;strong&gt;Meta's official rates in USD, effective October 1, 2026&lt;/strong&gt;, per delivered message. Source: &lt;a href="https://developers.facebook.com/documentation/business-messaging/whatsapp/pricing" rel="noopener noreferrer"&gt;Meta's WhatsApp Business Platform pricing&lt;/a&gt;. To estimate your own bill, use the &lt;a href="https://answerforme.io/en/tools/whatsapp-api-cost-calculator" rel="noopener noreferrer"&gt;WhatsApp API cost calculator&lt;/a&gt;.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Country&lt;/th&gt;
&lt;th&gt;Marketing&lt;/th&gt;
&lt;th&gt;Utility&lt;/th&gt;
&lt;th&gt;Authentication&lt;/th&gt;
&lt;th&gt;Service&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;United States *&lt;/td&gt;
&lt;td&gt;$0.0250&lt;/td&gt;
&lt;td&gt;$0.0034&lt;/td&gt;
&lt;td&gt;$0.0034&lt;/td&gt;
&lt;td&gt;$0.0034&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Brazil&lt;/td&gt;
&lt;td&gt;$0.0625&lt;/td&gt;
&lt;td&gt;$0.0068&lt;/td&gt;
&lt;td&gt;$0.0068&lt;/td&gt;
&lt;td&gt;$0.0068&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mexico&lt;/td&gt;
&lt;td&gt;$0.0397&lt;/td&gt;
&lt;td&gt;$0.0085&lt;/td&gt;
&lt;td&gt;$0.0085&lt;/td&gt;
&lt;td&gt;$0.0085&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Argentina&lt;/td&gt;
&lt;td&gt;$0.0618&lt;/td&gt;
&lt;td&gt;$0.0260&lt;/td&gt;
&lt;td&gt;$0.0260&lt;/td&gt;
&lt;td&gt;$0.0260&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Colombia&lt;/td&gt;
&lt;td&gt;$0.0125&lt;/td&gt;
&lt;td&gt;$0.0008&lt;/td&gt;
&lt;td&gt;$0.0008&lt;/td&gt;
&lt;td&gt;$0.0008&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;India&lt;/td&gt;
&lt;td&gt;$0.0118&lt;/td&gt;
&lt;td&gt;$0.0014&lt;/td&gt;
&lt;td&gt;$0.0014&lt;/td&gt;
&lt;td&gt;$0.0014&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Germany&lt;/td&gt;
&lt;td&gt;$0.1365&lt;/td&gt;
&lt;td&gt;$0.0550&lt;/td&gt;
&lt;td&gt;$0.0550&lt;/td&gt;
&lt;td&gt;$0.0550&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Spain&lt;/td&gt;
&lt;td&gt;$0.0707&lt;/td&gt;
&lt;td&gt;$0.0200&lt;/td&gt;
&lt;td&gt;$0.0200&lt;/td&gt;
&lt;td&gt;$0.0200&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Nigeria&lt;/td&gt;
&lt;td&gt;$0.0516&lt;/td&gt;
&lt;td&gt;$0.0067&lt;/td&gt;
&lt;td&gt;$0.0067&lt;/td&gt;
&lt;td&gt;$0.0067&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;* The United States has no rate of its own: it pays the "North America" regional rate.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note: Meta can update rates on the first day of each quarter. Always verify current rates on Meta's official pricing page.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Platform Fees vs. Meta Fees
&lt;/h2&gt;

&lt;p&gt;When you use a WhatsApp Business platform or provider (AnswerForMe is one of them), you pay &lt;strong&gt;two costs&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Meta per-message fees&lt;/strong&gt; (as listed above, based on usage)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Platform subscription fee&lt;/strong&gt; (to the provider, covers the software, automation, agents, etc.)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Some platforms include Meta message credits in their plans. Others pass through Meta fees at cost.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Monthly cost for a typical small business (200 marketing templates/month):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Meta fees: $5.00 in the United States, $12.50 in Brazil (plus any utility templates and service messages beyond 1,000)&lt;/li&gt;
&lt;li&gt;Platform fee: varies by provider, from free tier to $50-100/month&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Estimating Your Monthly Cost
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Simple formula:&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;Monthly cost = (Marketing templates × marketing rate) + (Utility templates × utility rate) + (Authentication templates × authentication rate) + (Service messages above 1,000 per number × service rate)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example: Brazilian e-commerce, 500 template messages and 1,500 service replies per month:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;350 utility (order confirmations, shipping) × $0.0068 = $2.38&lt;/li&gt;
&lt;li&gt;100 marketing (promotions, re-engagement) × $0.0625 = $6.25&lt;/li&gt;
&lt;li&gt;50 authentication (login codes) × $0.0068 = $0.34&lt;/li&gt;
&lt;li&gt;1,500 service replies: 1,000 free, 500 × $0.0068 = $3.40 (from October 1, 2026)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Total Meta fees: ~$12.37/month&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Platform fee: $30-60/month&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Total: ~$42-72/month&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For scale reference: 10,000 marketing templates a month cost $250 in Meta fees in the United States and $625 in Brazil.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Changed from the Old Pricing Model
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Until June 30, 2025&lt;/strong&gt;, Meta charged per 24-hour conversation, by category (marketing, utility, authentication, service).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;From July 1, 2025&lt;/strong&gt;, Meta charges per delivered template message. Free-form replies and utility templates inside the 24-hour window were free.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;From October 1, 2026&lt;/strong&gt;, service messages are charged at the utility rate after 1,000 free per number each month, and utility templates inside the 24-hour window are charged too. The 72-hour free entry point window stays free.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How to Minimize WhatsApp API Costs
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Classify Templates Correctly
&lt;/h3&gt;

&lt;p&gt;Using "utility" templates for transactional messages (order confirmation, shipping updates) costs much less than marketing templates: in Brazil, $0.0068 vs. $0.0625 per message. Don't send a transactional message using a marketing template category.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Resolve Issues Inside the 24-Hour Window
&lt;/h3&gt;

&lt;p&gt;While the window is open you can reply with free-form messages instead of templates. From October 1, 2026 each reply counts as a service message: free up to 1,000 per number each month, then the utility rate. Once the window closes, you can only reach the customer with a paid template.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Use Automation to Reduce Agent Workload
&lt;/h3&gt;

&lt;p&gt;A bot's replies are billed like any other service message, the same as a human agent's. The saving is in agent labor, not in Meta fees, so the total cost of service decreases.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Know What the 1,000 Free Messages Cover
&lt;/h3&gt;

&lt;p&gt;The 1,000 free per number each month apply only to service messages (replies inside the 24-hour window). Marketing campaigns are always charged per delivered template. Add a payment method in Billing Hub so replies beyond 1,000 are still delivered.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Prioritize High-Value Customers for Marketing Spend
&lt;/h3&gt;

&lt;p&gt;Don't blast everyone. Target marketing templates at high-LTV segments where the conversion value justifies the per-message rate ($0.0250 in the United States, $0.0625 in Brazil).&lt;/p&gt;

&lt;h2&gt;
  
  
  Frequently Asked Questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Is WhatsApp Business API free?
&lt;/h3&gt;

&lt;p&gt;The API itself has no access fee, but Meta charges per delivered template message and, from October 1, 2026, for service messages beyond 1,000 per number each month. Messages customers send you are free. You'll also pay a platform/BSP subscription fee.&lt;/p&gt;

&lt;p&gt;According to the &lt;a href="https://business.whatsapp.com/products/business-app" rel="noopener noreferrer"&gt;WhatsApp Business Platform documentation&lt;/a&gt;, businesses that respond to messages within the first hour see significantly higher conversion rates.&lt;/p&gt;

&lt;h3&gt;
  
  
  Who pays the Meta message fees: me or my platform provider?
&lt;/h3&gt;

&lt;p&gt;You pay them, but usually through your platform provider. Most BSPs collect Meta fees and pass them through to you, either bundled in your plan or billed separately.&lt;/p&gt;

&lt;h3&gt;
  
  
  What do the 1,000 free messages cover?
&lt;/h3&gt;

&lt;p&gt;Only service messages: the free-form replies you send inside the 24-hour window, counted per business phone number each month, with no rollover. Incoming messages from customers are always free and don't count. Templates (marketing, utility, authentication) are always charged, including utility templates inside the window from October 1, 2026.&lt;/p&gt;

&lt;h3&gt;
  
  
  Will WhatsApp pricing increase in the future?
&lt;/h3&gt;

&lt;p&gt;Meta adjusts pricing periodically. The general trend has been toward more granular pricing and adjusted country rates. Always check the current official Meta pricing page for up-to-date rates before making long-term business cases.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Related guides&lt;/strong&gt;: WhatsApp Business API platform · WhatsApp automation features · All WhatsApp guides&lt;/p&gt;

&lt;p&gt;Ready to start sending WhatsApp Business messages with predictable, transparent costs? Create your free AnswerForMe account — see exactly what you'll pay before you commit to any plan.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on the &lt;a href="https://answerforme.io/en/blog/whatsapp-business-api-pricing-explained-2026" rel="noopener noreferrer"&gt;AnswerForMe blog&lt;/a&gt;. I'm Mauricio, I build AnswerForMe, an AI assistant for WhatsApp Business.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>whatsapp</category>
      <category>api</category>
      <category>saas</category>
      <category>business</category>
    </item>
    <item>
      <title>Moving off Microsoft.Azure.ServiceBus and WindowsAzure.ServiceBus before September 30, 2026</title>
      <dc:creator>Mauricio Acuña</dc:creator>
      <pubDate>Sat, 26 Sep 2026 20:05:00 +0000</pubDate>
      <link>https://dev.to/mauricio_acua_5650fb8a8c/moving-off-microsoftazureservicebus-and-windowsazureservicebus-before-september-30-2026-4bcg</link>
      <guid>https://dev.to/mauricio_acua_5650fb8a8c/moving-off-microsoftazureservicebus-and-windowsazureservicebus-before-september-30-2026-4bcg</guid>
      <description>&lt;p&gt;On September 30, 2026, Microsoft retires the old Azure Service Bus client libraries for .NET: &lt;code&gt;WindowsAzure.ServiceBus&lt;/code&gt; and &lt;code&gt;Microsoft.Azure.ServiceBus&lt;/code&gt; (plus &lt;code&gt;com.microsoft.azure.servicebus&lt;/code&gt; on Java). Support for the SBMP protocol ends the same day. The replacement is &lt;code&gt;Azure.Messaging.ServiceBus&lt;/code&gt;, which has been available since November 2020.&lt;/p&gt;

&lt;p&gt;I went through the official docs and migration guides to separate what actually stops working from what just loses support, and turned the rest into a type map and a checklist. Every claim below links to the source.&lt;/p&gt;

&lt;h2&gt;
  
  
  What retires, and what that means in practice
&lt;/h2&gt;

&lt;p&gt;The notice is repeated on several Microsoft Learn pages, including the &lt;a href="https://learn.microsoft.com/en-us/azure/service-bus-messaging/service-bus-faq" rel="noopener noreferrer"&gt;Service Bus FAQ&lt;/a&gt;. In short:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;WindowsAzure.ServiceBus&lt;/code&gt;, &lt;code&gt;Microsoft.Azure.ServiceBus&lt;/code&gt; and &lt;code&gt;com.microsoft.azure.servicebus&lt;/code&gt; are retired on September 30, 2026.&lt;/li&gt;
&lt;li&gt;Support for the SBMP protocol ends and "you'll no longer be able to use this protocol after 30 September 2026".&lt;/li&gt;
&lt;li&gt;The old libraries "can still be used beyond 30 September 2026", but they get no official support and no updates from Microsoft.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So there are two different situations, depending on the package and the transport.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;WindowsAzure.ServiceBus&lt;/code&gt; on its default settings stops working.&lt;/strong&gt; That package talks to Service Bus over SBMP by default (&lt;a href="https://learn.microsoft.com/en-us/azure/service-bus-messaging/service-bus-amqp-dotnet" rel="noopener noreferrer"&gt;Microsoft Learn&lt;/a&gt;). If your connection string does not contain &lt;code&gt;TransportType=Amqp&lt;/code&gt;, your app is using SBMP, and SBMP is what goes away.&lt;/p&gt;

&lt;p&gt;There is a documented stopgap: append &lt;code&gt;;TransportType=Amqp&lt;/code&gt; to the connection string and the same package uses AMQP 1.0 instead. The same page lists the behavior differences when you do that: &lt;code&gt;OperationTimeout&lt;/code&gt; is ignored, &lt;code&gt;Receive(TimeSpan.Zero)&lt;/code&gt; becomes a 10 second receive, and only the receiver that got a message can complete it by lock token. It also changes how message bodies are serialized (more on that below). Treat it as a way to buy a few weeks, not as the migration.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;Microsoft.Azure.ServiceBus&lt;/code&gt; keeps connecting, without support.&lt;/strong&gt; This package already uses AMQP (the transport options the FAQ lists for it are AMQP and AMQP over WebSockets), so nothing on the wire changes on October 1. What changes is that it gets no more fixes, including security fixes, and it has been &lt;a href="https://github.com/Azure/azure-sdk-for-net/blob/main/sdk/servicebus/Azure.Messaging.ServiceBus/MigrationGuide.md" rel="noopener noreferrer"&gt;officially deprecated&lt;/a&gt; for a while.&lt;/p&gt;

&lt;h3&gt;
  
  
  The exception: WCF Relay
&lt;/h3&gt;

&lt;p&gt;If you reference &lt;code&gt;WindowsAzure.ServiceBus&lt;/code&gt; for Azure WCF Relay (&lt;code&gt;NetTcpRelayBinding&lt;/code&gt; and friends), that use is not part of this retirement. Microsoft staff confirmed on Microsoft Q&amp;amp;A that the package is "NOT deprecated for usage with Relay" and will stay supported with WCF Relay until further notice (&lt;a href="https://learn.microsoft.com/en-us/answers/questions/1822432/retiring-of-the-windowsazure-servicebus-nuget-pack" rel="noopener noreferrer"&gt;answer&lt;/a&gt;, &lt;a href="https://learn.microsoft.com/en-in/answers/questions/5910253/will-the-retiring-of-the-smbp-protocol-also-hit-wc" rel="noopener noreferrer"&gt;second answer about SBMP ports&lt;/a&gt;). The same thread points to Azure Relay Hybrid Connections as the long-term direction.&lt;/p&gt;

&lt;p&gt;If one project uses the package for both relay and queues, only the queue and topic code has to move.&lt;/p&gt;

&lt;h2&gt;
  
  
  The type map
&lt;/h2&gt;

&lt;p&gt;The new library has a single entry point, &lt;code&gt;ServiceBusClient&lt;/code&gt;, and you create senders, receivers and processors from it. One client holds one AMQP connection that everything created from it shares (&lt;a href="https://github.com/Azure/azure-sdk-for-net/blob/main/sdk/servicebus/Azure.Messaging.ServiceBus/MigrationGuide.md#connection-pooling" rel="noopener noreferrer"&gt;migration guide, connection pooling&lt;/a&gt;).&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Old&lt;/th&gt;
&lt;th&gt;New (&lt;code&gt;Azure.Messaging.ServiceBus&lt;/code&gt;)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;QueueClient&lt;/code&gt;, &lt;code&gt;TopicClient&lt;/code&gt;, &lt;code&gt;MessageSender&lt;/code&gt; (sending)&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;ServiceBusClient.CreateSender(queueOrTopic)&lt;/code&gt; returns &lt;code&gt;ServiceBusSender&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;MessageReceiver&lt;/code&gt;, &lt;code&gt;QueueClient.ReceiveAsync&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;ServiceBusClient.CreateReceiver(...)&lt;/code&gt; returns &lt;code&gt;ServiceBusReceiver&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;RegisterMessageHandler&lt;/code&gt; + &lt;code&gt;MessageHandlerOptions&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;ServiceBusProcessor&lt;/code&gt; + &lt;code&gt;ServiceBusProcessorOptions&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;SessionClient&lt;/code&gt;, &lt;code&gt;RegisterSessionHandler&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;ServiceBusSessionProcessor&lt;/code&gt;, &lt;code&gt;ServiceBusSessionReceiver&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;Message&lt;/code&gt; / &lt;code&gt;BrokeredMessage&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;ServiceBusMessage&lt;/code&gt; to send, &lt;code&gt;ServiceBusReceivedMessage&lt;/code&gt; when received&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;Message.UserProperties&lt;/code&gt; / &lt;code&gt;BrokeredMessage.Properties&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ApplicationProperties&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Label&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Subject&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ScheduledEnqueueTimeUtc&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ScheduledEnqueueTime&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;ManagementClient&lt;/code&gt; / &lt;code&gt;NamespaceManager&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ServiceBusAdministrationClient&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;MessagingFactory&lt;/code&gt;, shared &lt;code&gt;ServiceBusConnection&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;One &lt;code&gt;ServiceBusClient&lt;/code&gt; per app, one connection&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Library-specific exception types&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;ServiceBusException&lt;/code&gt; with a &lt;code&gt;Reason&lt;/code&gt; (&lt;a href="https://learn.microsoft.com/en-us/dotnet/api/azure.messaging.servicebus.servicebusfailurereason" rel="noopener noreferrer"&gt;&lt;code&gt;ServiceBusFailureReason&lt;/code&gt;&lt;/a&gt;), for example &lt;code&gt;MessageLockLost&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Sources: &lt;a href="https://github.com/Azure/azure-sdk-for-net/blob/main/sdk/servicebus/Azure.Messaging.ServiceBus/MigrationGuide.md" rel="noopener noreferrer"&gt;guide for Microsoft.Azure.ServiceBus&lt;/a&gt;, &lt;a href="https://github.com/Azure/azure-sdk-for-net/blob/main/sdk/servicebus/Azure.Messaging.ServiceBus/MigrationGuide_WindowsAzureServiceBus.md" rel="noopener noreferrer"&gt;guide for WindowsAzure.ServiceBus&lt;/a&gt;, &lt;a href="https://learn.microsoft.com/en-us/dotnet/api/azure.messaging.servicebus.servicebusmessage" rel="noopener noreferrer"&gt;ServiceBusMessage reference&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;A typical handler, before and after:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Microsoft.Azure.ServiceBus&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;queue&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;QueueClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;connectionString&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"orders"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;RegisterMessageHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;JsonSerializer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Deserialize&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Order&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Body&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;HandleAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CompleteAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SystemProperties&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LockToken&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;MessageHandlerOptions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nf"&gt;Log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CompletedTask&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;AutoComplete&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;MaxConcurrentCalls&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;MaxAutoRenewDuration&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TimeSpan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromMinutes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Azure.Messaging.ServiceBus&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;var&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ServiceBusClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;connectionString&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// one per app&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;var&lt;/span&gt; &lt;span class="n"&gt;processor&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateProcessor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"orders"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;ServiceBusProcessorOptions&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;AutoCompleteMessages&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;MaxConcurrentCalls&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;MaxAutoLockRenewalDuration&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TimeSpan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromMinutes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="n"&gt;processor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ProcessMessageAsync&lt;/span&gt; &lt;span class="p"&gt;+=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ToObjectFromJson&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Order&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;HandleAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CancellationToken&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CompleteMessageAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="n"&gt;processor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ProcessErrorAsync&lt;/span&gt; &lt;span class="p"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nf"&gt;Log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CompletedTask&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;processor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;StartProcessingAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Behavior changes that compile fine and break at runtime
&lt;/h2&gt;

&lt;p&gt;Renaming types is the easy half. These are the changes that pass the build and show up later as duplicates, lost locks or messages nobody can read.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Settlement moves off the message.&lt;/strong&gt; With &lt;code&gt;WindowsAzure.ServiceBus&lt;/code&gt; you called &lt;code&gt;CompleteAsync&lt;/code&gt;, &lt;code&gt;AbandonAsync&lt;/code&gt; or &lt;code&gt;DeadLetterAsync&lt;/code&gt; on the &lt;code&gt;BrokeredMessage&lt;/code&gt;. Now those calls live on the &lt;code&gt;ServiceBusReceiver&lt;/code&gt;, or on the event args when you use the processor (&lt;a href="https://github.com/Azure/azure-sdk-for-net/blob/main/sdk/servicebus/Azure.Messaging.ServiceBus/MigrationGuide_WindowsAzureServiceBus.md#brokeredmessage-changed-to-message" rel="noopener noreferrer"&gt;guide&lt;/a&gt;). There is also no batch settlement API; the guide's FAQ explains why and shows the &lt;code&gt;Task.WhenAll&lt;/code&gt; loop to write instead.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Auto-complete is on by default.&lt;/strong&gt; The processor completes the message after your handler returns unless you set &lt;code&gt;AutoCompleteMessages = false&lt;/code&gt;. If the handler throws without settling, the processor abandons the message. Decide per handler which one you want, and write it down in the options instead of relying on the default.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Lock renewal has a 5 minute ceiling by default.&lt;/strong&gt; &lt;code&gt;ServiceBusProcessorOptions.MaxAutoLockRenewalDuration&lt;/code&gt; defaults to 5 minutes (&lt;a href="https://learn.microsoft.com/en-us/dotnet/api/azure.messaging.servicebus.servicebusprocessoroptions.maxautolockrenewalduration" rel="noopener noreferrer"&gt;reference&lt;/a&gt;). A handler that runs longer loses the lock, the message is delivered again with a higher &lt;code&gt;DeliveryCount&lt;/code&gt;, and after enough attempts it lands in the dead-letter queue. Also note that automatic renewal is a processor feature: &lt;code&gt;ServiceBusReceiverOptions&lt;/code&gt; has no renewal setting, so with a plain receiver you call &lt;code&gt;RenewMessageLockAsync&lt;/code&gt; yourself.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Properties and dead-lettering.&lt;/strong&gt; &lt;code&gt;UserProperties&lt;/code&gt; becomes &lt;code&gt;ApplicationProperties&lt;/code&gt;. Instead of writing the reason and description into &lt;code&gt;UserProperties&lt;/code&gt; with the &lt;code&gt;DeadLetterReasonHeader&lt;/code&gt; constants, you pass them to &lt;code&gt;DeadLetterMessageAsync(message, reason, description)&lt;/code&gt; and read them back from &lt;code&gt;DeadLetterReason&lt;/code&gt; and &lt;code&gt;DeadLetterErrorDescription&lt;/code&gt;. To read the dead-letter queue you set &lt;code&gt;SubQueue = SubQueue.DeadLetter&lt;/code&gt; on the receiver options instead of building the &lt;code&gt;$DeadLetterQueue&lt;/code&gt; path yourself (&lt;a href="https://github.com/Azure/azure-sdk-for-net/blob/main/sdk/servicebus/Azure.Messaging.ServiceBus/MigrationGuide.md#dead-letter-messages" rel="noopener noreferrer"&gt;guide&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. The body is &lt;code&gt;BinaryData&lt;/code&gt;, and old bodies may not be what you think.&lt;/strong&gt; &lt;code&gt;Message.Body&lt;/code&gt; was a &lt;code&gt;byte[]&lt;/code&gt;; &lt;code&gt;ServiceBusMessage.Body&lt;/code&gt; is &lt;code&gt;BinaryData&lt;/code&gt;, and the &lt;code&gt;string&lt;/code&gt; constructor encodes UTF-8. The bigger issue is with &lt;code&gt;WindowsAzure.ServiceBus&lt;/code&gt;: &lt;code&gt;new BrokeredMessage(myObject)&lt;/code&gt; serialized the object for you, with &lt;code&gt;DataContractSerializer&lt;/code&gt; on SBMP, and the receiver got it back with &lt;code&gt;GetBody&amp;lt;T&amp;gt;()&lt;/code&gt; (&lt;a href="https://learn.microsoft.com/en-us/azure/service-bus-messaging/service-bus-messages-payloads#payload-serialization" rel="noopener noreferrer"&gt;Microsoft Learn: payload serialization&lt;/a&gt;). The new library has no &lt;code&gt;GetBody&amp;lt;T&amp;gt;()&lt;/code&gt;. Messages already sitting in a queue, or sent by services you have not migrated yet, still carry that old format. The same page recommends taking explicit control of serialization and, while old and new versions run side by side, trying more than one deserializer before dead-lettering. In practice: switch senders to an explicit format (JSON with &lt;code&gt;ContentType&lt;/code&gt; set) and make receivers able to read both formats until the queues are clean, or drain the queues before the switch.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6. &lt;code&gt;MessageId&lt;/code&gt; is no longer generated for you.&lt;/strong&gt; &lt;code&gt;BrokeredMessage&lt;/code&gt; constructors assigned a new GUID to &lt;code&gt;MessageId&lt;/code&gt;; &lt;code&gt;ServiceBusMessage&lt;/code&gt; leaves it empty. If you rely on duplicate detection, set &lt;code&gt;MessageId&lt;/code&gt; to a stable value from your domain (an order id, not a new GUID per retry). On a partitioned entity with duplicate detection enabled, a send with no &lt;code&gt;SessionId&lt;/code&gt;, &lt;code&gt;PartitionKey&lt;/code&gt; or &lt;code&gt;MessageId&lt;/code&gt; fails (&lt;a href="https://github.com/Azure/azure-sdk-for-net/blob/main/sdk/servicebus/Azure.Messaging.ServiceBus/MigrationGuide_WindowsAzureServiceBus.md#sending-messages" rel="noopener noreferrer"&gt;guide&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;7. Batches are measured before sending.&lt;/strong&gt; Sending a list still works, but the safe path is &lt;code&gt;CreateMessageBatchAsync&lt;/code&gt; plus &lt;code&gt;TryAddMessage&lt;/code&gt;, which returns &lt;code&gt;false&lt;/code&gt; when a message does not fit. Batching messages bound for different partitions is no longer supported.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;8. Plugins and baggage are gone.&lt;/strong&gt; &lt;code&gt;RegisterPlugin&lt;/code&gt; has no direct equivalent; the guide points to extending the types, with a dedicated claim-check sample. Activity baggage no longer flows through a &lt;code&gt;Correlation-Context&lt;/code&gt; property; with the OpenTelemetry support, &lt;code&gt;tracestate&lt;/code&gt; goes into &lt;code&gt;ApplicationProperties&lt;/code&gt; instead (&lt;a href="https://github.com/Azure/azure-sdk-for-net/blob/main/sdk/servicebus/Azure.Messaging.ServiceBus/MigrationGuide.md#distributed-tracing" rel="noopener noreferrer"&gt;guide&lt;/a&gt;). If a downstream consumer reads &lt;code&gt;Correlation-Context&lt;/code&gt;, it will stop getting it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;9. Client lifetime.&lt;/strong&gt; Code that creates a &lt;code&gt;QueueClient&lt;/code&gt; per send and closes it opens and closes a connection each time. With the new library, keep one &lt;code&gt;ServiceBusClient&lt;/code&gt; for the life of the app (a singleton in DI) and dispose it on shutdown; it implements &lt;code&gt;IAsyncDisposable&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Azure Functions: the Service Bus extension 5.x
&lt;/h2&gt;

&lt;p&gt;If you use the Service Bus trigger or output binding, the relevant package is the Functions extension, not the SDK directly.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Extension 4.x and earlier exposed types from &lt;code&gt;Microsoft.Azure.ServiceBus&lt;/code&gt; (&lt;code&gt;Message&lt;/code&gt;, &lt;code&gt;MessageReceiver&lt;/code&gt;, &lt;code&gt;IMessageSession&lt;/code&gt;). Extension 5.x is built on &lt;code&gt;Azure.Messaging.ServiceBus&lt;/code&gt;, so the trigger binds to &lt;code&gt;ServiceBusReceivedMessage&lt;/code&gt; and settlement goes through &lt;code&gt;ServiceBusMessageActions&lt;/code&gt; (&lt;a href="https://learn.microsoft.com/en-us/azure/azure-functions/functions-bindings-service-bus" rel="noopener noreferrer"&gt;bindings reference&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;Extension 4.x itself was retired on March 31, 2025 (&lt;a href="https://learn.microsoft.com/en-us/azure/azure-functions/migrate-service-bus-version-4-version-5" rel="noopener noreferrer"&gt;migration article&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;Recommended versions: &lt;code&gt;Microsoft.Azure.WebJobs.Extensions.ServiceBus&lt;/code&gt; 5.13.4 or later for in-process, &lt;code&gt;Microsoft.Azure.Functions.Worker.Extensions.ServiceBus&lt;/code&gt; 5.14.1 or later for isolated. In the isolated worker, binding to &lt;code&gt;ServiceBusReceivedMessage&lt;/code&gt; and &lt;code&gt;ServiceBusMessageActions&lt;/code&gt; needs 5.14.1 or later; older isolated extensions only bind to &lt;code&gt;string&lt;/code&gt;, &lt;code&gt;byte[]&lt;/code&gt; and POCOs.&lt;/li&gt;
&lt;li&gt;When you settle through &lt;code&gt;ServiceBusMessageActions&lt;/code&gt;, set &lt;code&gt;AutoCompleteMessages = false&lt;/code&gt; on the trigger so the runtime does not also try to complete the message.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;host.json&lt;/code&gt; changes shape between 4.x and 5.x. The nested &lt;code&gt;messageHandlerOptions&lt;/code&gt;, &lt;code&gt;sessionHandlerOptions&lt;/code&gt; and &lt;code&gt;batchOptions&lt;/code&gt; blocks become flat properties under &lt;code&gt;serviceBus&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"extensions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"serviceBus"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"autoCompleteMessages"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"maxAutoLockRenewalDuration"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"00:05:00"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"maxConcurrentCalls"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"maxConcurrentSessions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Compare the defaults while you are there. The documented default for &lt;code&gt;maxConcurrentSessions&lt;/code&gt; is 2000 in 4.x and 8 in 5.x, which changes throughput for session-enabled queues if you never set it explicitly.&lt;/p&gt;

&lt;p&gt;And if the function app still runs on the in-process model, that model reaches end of support on November 10, 2026 (&lt;a href="https://learn.microsoft.com/en-us/azure/azure-functions/migrate-dotnet-to-isolated-model" rel="noopener noreferrer"&gt;notice&lt;/a&gt;). It makes sense to plan both moves together: the isolated worker plus extension 5.x.&lt;/p&gt;

&lt;h2&gt;
  
  
  Checklist
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;List every project that references &lt;code&gt;WindowsAzure.ServiceBus&lt;/code&gt; or &lt;code&gt;Microsoft.Azure.ServiceBus&lt;/code&gt;, including shared libraries and Functions extensions below 5.x.&lt;/li&gt;
&lt;li&gt;For each &lt;code&gt;WindowsAzure.ServiceBus&lt;/code&gt; reference, note whether it is used for messaging, WCF Relay or both.&lt;/li&gt;
&lt;li&gt;Find connection strings without &lt;code&gt;TransportType=Amqp&lt;/code&gt; on &lt;code&gt;WindowsAzure.ServiceBus&lt;/code&gt;: those are on SBMP. If the migration will not be in production by September 30, add &lt;code&gt;;TransportType=Amqp&lt;/code&gt; and test it now.&lt;/li&gt;
&lt;li&gt;Add &lt;code&gt;Azure.Messaging.ServiceBus&lt;/code&gt; and create one &lt;code&gt;ServiceBusClient&lt;/code&gt; per app, registered as a singleton.&lt;/li&gt;
&lt;li&gt;Replace senders, receivers and handlers using the type map above.&lt;/li&gt;
&lt;li&gt;For every handler, set &lt;code&gt;AutoCompleteMessages&lt;/code&gt;, &lt;code&gt;MaxConcurrentCalls&lt;/code&gt; and &lt;code&gt;MaxAutoLockRenewalDuration&lt;/code&gt; explicitly, and check the longest processing time against the lock renewal window.&lt;/li&gt;
&lt;li&gt;Move &lt;code&gt;UserProperties&lt;/code&gt; / &lt;code&gt;Properties&lt;/code&gt; to &lt;code&gt;ApplicationProperties&lt;/code&gt; and &lt;code&gt;Label&lt;/code&gt; to &lt;code&gt;Subject&lt;/code&gt;, and check consumers that read them by name.&lt;/li&gt;
&lt;li&gt;Replace manual dead-letter headers with &lt;code&gt;DeadLetterMessageAsync(message, reason, description)&lt;/code&gt; and &lt;code&gt;SubQueue.DeadLetter&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Decide the body format (JSON with &lt;code&gt;ContentType&lt;/code&gt;), and handle messages in the old format until queues are drained, especially anything sent with &lt;code&gt;new BrokeredMessage(object)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Set &lt;code&gt;MessageId&lt;/code&gt; explicitly wherever duplicate detection matters.&lt;/li&gt;
&lt;li&gt;Replace exception handling on old types with &lt;code&gt;ServiceBusException.Reason&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Functions: move to the Service Bus extension 5.x, convert &lt;code&gt;host.json&lt;/code&gt;, and settle with &lt;code&gt;ServiceBusMessageActions&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Test against a real namespace (the emulator or a dev namespace), including redelivery, dead-lettering and a handler that runs longer than the lock duration.&lt;/li&gt;
&lt;li&gt;Roll out consumers before producers if the body format changes, so nothing arrives in a format no one can read.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Checking your own solution
&lt;/h2&gt;

&lt;p&gt;I maintain a small free command-line tool, Net10Check, that scans a solution for the things that block or complicate a move to .NET 10. It flags projects that reference &lt;code&gt;Microsoft.Azure.ServiceBus&lt;/code&gt; or &lt;code&gt;WindowsAzure.ServiceBus&lt;/code&gt; (and the old Event Hubs packages), in-process Azure Functions, and &lt;code&gt;BinaryFormatter&lt;/code&gt;. It runs locally and uploads nothing.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet tool &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; Net10Check
net10-check path/to/YourSolution.sln
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It works from project files, so it tells you where the old packages are referenced, not whether a &lt;code&gt;WindowsAzure.ServiceBus&lt;/code&gt; reference is only there for WCF Relay. That part still needs a look at the code. Source: &lt;a href="https://github.com/mauri0686/net10check" rel="noopener noreferrer"&gt;github.com/mauri0686/net10check&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you would rather hand the migration off, I do it at a fixed price and deliver it as a pull request: &lt;a href="https://mauri0686.github.io/onlts/servicebus/" rel="noopener noreferrer"&gt;Service Bus SDK migration&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>azure</category>
      <category>csharp</category>
      <category>servicebus</category>
    </item>
    <item>
      <title>Moving .NET 8/9 apps to .NET 10 before Nov 10: a checklist with links to every breaking change</title>
      <dc:creator>Mauricio Acuña</dc:creator>
      <pubDate>Sat, 26 Sep 2026 18:38:10 +0000</pubDate>
      <link>https://dev.to/mauricio_acua_5650fb8a8c/moving-net-89-apps-to-net-10-before-nov-10-a-checklist-with-links-to-every-breaking-change-5ge0</link>
      <guid>https://dev.to/mauricio_acua_5650fb8a8c/moving-net-89-apps-to-net-10-before-nov-10-a-checklist-with-links-to-every-breaking-change-5ge0</guid>
      <description>&lt;p&gt;.NET 8 and .NET 9 both reach end of support on November 10, 2026 (&lt;a href="https://devblogs.microsoft.com/dotnet/dotnet-8-9-end-of-support/" rel="noopener noreferrer"&gt;announcement&lt;/a&gt;). I have been going through the official breaking change lists for .NET 10 and turned them into a checklist. Sharing it in case it saves someone an afternoon. Everything below links to learn.microsoft.com.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. SDK and project files&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Install the .NET 10 SDK and update &lt;code&gt;global.json&lt;/code&gt; if you pin one.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;TargetFramework&lt;/code&gt; to &lt;code&gt;net10.0&lt;/code&gt; (check &lt;code&gt;Directory.Build.props&lt;/code&gt; first, it is often set there).&lt;/li&gt;
&lt;li&gt;Update &lt;code&gt;Microsoft.*&lt;/code&gt; packages to 10.x together: ASP.NET Core, EF Core, &lt;code&gt;Microsoft.Extensions.*&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;PackageReference&lt;/code&gt; without a version is now an error (&lt;a href="https://learn.microsoft.com/en-us/dotnet/core/compatibility/sdk/10.0/nu1015-packagereference-version" rel="noopener noreferrer"&gt;NU1015&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;You may get &lt;a href="https://learn.microsoft.com/en-us/dotnet/core/compatibility/sdk/10.0/nu1510-pruned-references" rel="noopener noreferrer"&gt;NU1510&lt;/a&gt; for direct references that the platform now provides. Usually you can just remove them.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2. NuGet audit now includes transitive packages&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;On &lt;code&gt;net10.0&lt;/code&gt;, &lt;code&gt;NuGetAuditMode&lt;/code&gt; defaults to &lt;code&gt;all&lt;/code&gt;, so vulnerable transitive packages now produce NU1901 to NU1904 warnings (&lt;a href="https://learn.microsoft.com/en-us/dotnet/core/compatibility/sdk/10.0/nugetaudit-transitive-packages" rel="noopener noreferrer"&gt;details&lt;/a&gt;). With &lt;code&gt;TreatWarningsAsErrors&lt;/code&gt;, restore fails. &lt;code&gt;dotnet nuget why &amp;lt;package&amp;gt;&lt;/code&gt; tells you which top-level package pulls it in.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;3. ASP.NET Core 10&lt;/strong&gt; (&lt;a href="https://learn.microsoft.com/en-us/aspnet/core/breaking-changes/10/overview" rel="noopener noreferrer"&gt;full list&lt;/a&gt;)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cookie auth no longer redirects to the login page for known API endpoints (&lt;code&gt;[ApiController]&lt;/code&gt;, minimal APIs with JSON, &lt;code&gt;TypedResults&lt;/code&gt;, SignalR). They return 401/403 instead (&lt;a href="https://learn.microsoft.com/en-us/aspnet/core/breaking-changes/10/cookie-authentication-api-endpoints" rel="noopener noreferrer"&gt;details&lt;/a&gt;). This one breaks integration tests that assert a 302. There is an AppContext switch to restore the old behavior if you need time.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;WebHostBuilder&lt;/code&gt;, &lt;code&gt;IWebHost&lt;/code&gt; and &lt;code&gt;WebHost&lt;/code&gt; are obsolete. Old &lt;code&gt;Program.cs&lt;/code&gt; files still using &lt;code&gt;WebHost.CreateDefaultBuilder&lt;/code&gt; will light up with warnings.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ForwardedHeadersOptions.KnownNetworks&lt;/code&gt; is obsolete, use &lt;code&gt;KnownIPNetworks&lt;/code&gt; with &lt;code&gt;System.Net.IPNetwork&lt;/code&gt; (warning ASPDEPR005).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;IActionContextAccessor&lt;/code&gt; is obsolete, &lt;code&gt;WithOpenApi&lt;/code&gt; is deprecated, Razor runtime compilation is obsolete.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;4. EF Core 10&lt;/strong&gt; (&lt;a href="https://learn.microsoft.com/en-us/ef/core/what-is-new/ef-core-10.0/breaking-changes" rel="noopener noreferrer"&gt;full list&lt;/a&gt;)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Contains()&lt;/code&gt; over a collection parameter now translates to multiple scalar parameters instead of a JSON array (&lt;code&gt;OPENJSON&lt;/code&gt;). Better plans in most cases, but check hot queries with large lists. You can switch back per query or globally.&lt;/li&gt;
&lt;li&gt;SQL parameter names are simplified (&lt;code&gt;@city&lt;/code&gt; instead of &lt;code&gt;@__city_0&lt;/code&gt;). Snapshot tests that compare SQL text will fail, and expect a burst of plan recompilation after deploy.&lt;/li&gt;
&lt;li&gt;EF now injects &lt;code&gt;Application Name&lt;/code&gt; into the connection string if you don't set one. If you mix EF with Dapper or ADO.NET inside a &lt;code&gt;TransactionScope&lt;/code&gt;, the two different connection strings can escalate to a distributed transaction. Setting &lt;code&gt;Application Name&lt;/code&gt; yourself avoids it.&lt;/li&gt;
&lt;li&gt;With &lt;code&gt;UseAzureSql&lt;/code&gt; or compatibility level 170, JSON columns map to the new &lt;code&gt;json&lt;/code&gt; type, and your next migration will alter existing &lt;code&gt;nvarchar(max)&lt;/code&gt; JSON columns. Read the migration before applying it.&lt;/li&gt;
&lt;li&gt;Check your EF Core provider before anything else. Some third-party providers have no EF Core 10 release yet: as of today the latest Pomelo MySQL provider (9.0.0) still requires EF Core 9.x, so the upgrade cannot restore until it ships or you switch providers.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;dotnet ef&lt;/code&gt; on multi-targeted projects now requires &lt;code&gt;--framework&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;If you use Microsoft.Data.Sqlite: &lt;code&gt;DateTimeOffset&lt;/code&gt; and &lt;code&gt;DateTime&lt;/code&gt; handling moved to UTC in three places, all marked high impact.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;5. Core libraries&lt;/strong&gt; (&lt;a href="https://learn.microsoft.com/en-us/dotnet/core/compatibility/10" rel="noopener noreferrer"&gt;full list&lt;/a&gt;)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;System.Linq.AsyncEnumerable&lt;/code&gt; is now in the framework. If you reference &lt;code&gt;System.Linq.Async&lt;/code&gt;, remove it or move to 7.0.0, otherwise you get ambiguity errors (&lt;a href="https://learn.microsoft.com/en-us/dotnet/core/compatibility/core-libraries/10.0/asyncenumerable" rel="noopener noreferrer"&gt;details&lt;/a&gt;). &lt;code&gt;SelectAwait&lt;/code&gt; call sites become &lt;code&gt;Select&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;BackgroundService.ExecuteAsync&lt;/code&gt; now runs entirely on a background thread. If you relied on the synchronous part before the first &lt;code&gt;await&lt;/code&gt; running during startup, move it to the constructor or &lt;code&gt;StartAsync&lt;/code&gt; (&lt;a href="https://learn.microsoft.com/en-us/dotnet/core/compatibility/extensions/10.0/backgroundservice-executeasync-task" rel="noopener noreferrer"&gt;details&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;Null values in configuration are now preserved, &lt;code&gt;System.Text.Json&lt;/code&gt; checks for property name conflicts, and the default trace context propagator is W3C. Worth a quick search in your codebase.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;6. Containers and hosting&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The default &lt;code&gt;10.0&lt;/code&gt; image tags are Ubuntu 24.04, and there are no Debian images for .NET 10 (&lt;a href="https://learn.microsoft.com/en-us/dotnet/core/compatibility/containers/10.0/default-images-use-ubuntu" rel="noopener noreferrer"&gt;details&lt;/a&gt;). If your Dockerfile installs packages with apt, check the package names.&lt;/li&gt;
&lt;li&gt;Azure Functions: the in-process model does not support .NET 10 and its own support ends November 10, 2026 too. You have to move to the isolated worker model (&lt;a href="https://learn.microsoft.com/en-us/azure/azure-functions/migrate-dotnet-to-isolated-model" rel="noopener noreferrer"&gt;migration guide&lt;/a&gt;). Watch out for &lt;code&gt;[JsonProperty]&lt;/code&gt; attributes: the isolated model uses System.Text.Json and silently ignores them.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;7. CI&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Bump the SDK version in your pipeline (&lt;code&gt;actions/setup-dotnet&lt;/code&gt;, &lt;code&gt;UseDotNet@2&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Run the full test suite, not just the build. Most of the items above are behavioral changes, so they compile fine and fail at runtime.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If I missed something that bit you, please add it in the comments and I'll update the list.&lt;/p&gt;

&lt;h2&gt;
  
  
  Checking your own solution
&lt;/h2&gt;

&lt;p&gt;I am a .NET developer and I now offer fixed-price .NET 10 upgrades, which is why I made this list. I also wrote a small free command-line tool that checks a solution against these items, including third-party packages that have no .NET 10 release yet, and prints what needs attention. It runs locally and uploads nothing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet tool &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; Net10Check
net10-check path/to/YourApp.sln
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The source is on &lt;a href="https://github.com/mauri0686/net10check" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;. Happy to hear what it misses. If you would rather hand the upgrade off, details are &lt;a href="https://mauri0686.github.io/onlts/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>csharp</category>
      <category>aspnetcore</category>
      <category>efcore</category>
    </item>
  </channel>
</rss>
