<?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: Marco Orta</title>
    <description>The latest articles on DEV Community by Marco Orta (@marco_odev).</description>
    <link>https://dev.to/marco_odev</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%2F4135235%2F70d2d5a4-642c-4df8-bd37-8dcaef1e7d17.png</url>
      <title>DEV Community: Marco Orta</title>
      <link>https://dev.to/marco_odev</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/marco_odev"/>
    <language>en</language>
    <item>
      <title>Laravel 13: What Actually Breaks When You Upgrade (and What Doesn't)</title>
      <dc:creator>Marco Orta</dc:creator>
      <pubDate>Mon, 21 Sep 2026 08:29:07 +0000</pubDate>
      <link>https://dev.to/marco_odev/laravel-13-what-actually-breaks-when-you-upgrade-and-what-doesnt-13d6</link>
      <guid>https://dev.to/marco_odev/laravel-13-what-actually-breaks-when-you-upgrade-and-what-doesnt-13d6</guid>
      <description>&lt;p&gt;&lt;strong&gt;Laravel 12 stopped receiving bug fixes on August 13, 2026.&lt;/strong&gt; Since then only security patches remain, and those run out on February 24, 2027. If you have a production app on 12, you are already in the last window.&lt;/p&gt;

&lt;p&gt;The good news is that Laravel 13 really is a small upgrade. The official docs estimate &lt;strong&gt;10 minutes&lt;/strong&gt; and say most applications can upgrade "without changing much application code." That is true.&lt;/p&gt;

&lt;p&gt;The bad news is the nuance almost nobody is covering: &lt;em&gt;minimal breaking changes&lt;/em&gt; is not &lt;em&gt;zero breaking changes&lt;/em&gt;. The &lt;a href="https://laravel.com/docs/13.x/upgrade" rel="noopener noreferrer"&gt;official upgrade guide&lt;/a&gt; flags &lt;strong&gt;two high-impact changes&lt;/strong&gt;, two medium-impact ones, and roughly fifteen low-impact ones. And if your application caches PHP objects, uses &lt;code&gt;upsert&lt;/code&gt; on MySQL, or excludes the CSRF middleware in tests, one of them is going to bite.&lt;/p&gt;

&lt;p&gt;This article is the complete list, with the concrete code for each change. If what you need is the general upgrade procedure — backup, branch, &lt;code&gt;composer update&lt;/code&gt;, verification — that lives in the &lt;a href="https://ortamarco.me/en/blog/how-to-upgrade-laravel-9-to-laravel-10/" rel="noopener noreferrer"&gt;guide to upgrading Laravel to the latest version&lt;/a&gt;; here we go straight to what breaks.&lt;/p&gt;

&lt;h2&gt;
  
  
  First: where you are and how urgent this is
&lt;/h2&gt;

&lt;p&gt;This is the official support table, not the one making the rounds:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Version&lt;/th&gt;
&lt;th&gt;Supported PHP&lt;/th&gt;
&lt;th&gt;Released&lt;/th&gt;
&lt;th&gt;Bug fixes until&lt;/th&gt;
&lt;th&gt;Security until&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Laravel 13&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;8.3 – 8.5&lt;/td&gt;
&lt;td&gt;Mar 17, 2026&lt;/td&gt;
&lt;td&gt;Q3 2027&lt;/td&gt;
&lt;td&gt;Mar 17, 2028&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Laravel 12&lt;/td&gt;
&lt;td&gt;8.2 – 8.5&lt;/td&gt;
&lt;td&gt;Feb 24, 2025&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Aug 13, 2026&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Feb 24, 2027&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Laravel 11&lt;/td&gt;
&lt;td&gt;8.2 – 8.4&lt;/td&gt;
&lt;td&gt;Mar 12, 2024&lt;/td&gt;
&lt;td&gt;Sep 3, 2025&lt;/td&gt;
&lt;td&gt;Mar 12, 2026 ❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Laravel 10&lt;/td&gt;
&lt;td&gt;8.1 – 8.3&lt;/td&gt;
&lt;td&gt;Feb 14, 2023&lt;/td&gt;
&lt;td&gt;Aug 6, 2024&lt;/td&gt;
&lt;td&gt;Feb 4, 2025 ❌&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Two things worth reading carefully in that table:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Laravel 11 has received no security patches since March 12, 2026.&lt;/strong&gt; If you're there, you aren't "a little behind" — you're uncovered.&lt;/li&gt;
&lt;li&gt;Laravel 12 &lt;strong&gt;is not LTS&lt;/strong&gt;. Laravel stopped shipping LTS releases years ago; the current policy is uniform across every major — 18 months of bug fixes and 2 years of security.
&lt;/li&gt;
&lt;/ul&gt;

&lt;pre data-lang="mermaid"&gt;&lt;code&gt;flowchart TB
    A["Which version are you on?"] --&amp;gt; B["Laravel 12"]
    A --&amp;gt; C["Laravel 11"]
    A --&amp;gt; D["Laravel 10 or older"]
    B --&amp;gt; B1["✅ Jump straight to 13&amp;lt;br/&amp;gt;Read this list and do it"]
    C --&amp;gt; C1["⚠️ No security patches&amp;lt;br/&amp;gt;since Mar 12, 2026"]
    C1 --&amp;gt; C2["Go 11 → 12 → 13,&amp;lt;br/&amp;gt;one major at a time"]
    D --&amp;gt; D1["🚨 Fully end of life"]
    D1 --&amp;gt; C2&lt;/code&gt;&lt;/pre&gt;



&lt;h2&gt;
  
  
  Entry requirement: PHP 8.3
&lt;/h2&gt;

&lt;p&gt;Laravel 13 requires &lt;strong&gt;PHP 8.3 minimum&lt;/strong&gt; and supports up to 8.5. No more, no less: if you read somewhere that "in practice it needs 8.4," that's wrong — 8.3 works.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;php &lt;span class="nt"&gt;-v&lt;/span&gt;          &lt;span class="c"&gt;# you need 8.3.0 or higher&lt;/span&gt;
composer &lt;span class="nt"&gt;-V&lt;/span&gt;     &lt;span class="c"&gt;# and an up-to-date Composer 2.x&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you're coming from Laravel 12 on PHP 8.2, &lt;strong&gt;this is the only real infrastructure work&lt;/strong&gt; in the upgrade. Do it before touching &lt;code&gt;composer.json&lt;/code&gt;, not at the same time, so that if something breaks you know what broke it.&lt;/p&gt;




&lt;h2&gt;
  
  
  High impact
&lt;/h2&gt;

&lt;p&gt;There are two. Both affect practically every application.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Dependencies in &lt;code&gt;composer.json&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;No surprises, but there are more packages than people remember:&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;"require"&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;"php"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^8.3"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"laravel/framework"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^13.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;"laravel/tinker"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^3.0"&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;"require-dev"&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;"laravel/boost"&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;"phpunit/phpunit"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^12.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;"pestphp/pest"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^4.0"&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;The two most commonly forgotten are &lt;strong&gt;&lt;code&gt;laravel/tinker&lt;/code&gt; to &lt;code&gt;^3.0&lt;/code&gt;&lt;/strong&gt; and the jump to &lt;strong&gt;PHPUnit &lt;code&gt;^12.0&lt;/code&gt;&lt;/strong&gt;. If you use Pest, you need &lt;code&gt;^4.0&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;And if you installed the global Laravel installer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;composer global update laravel/installer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With Herd, updating Herd is enough.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. The CSRF middleware changes name and behavior
&lt;/h3&gt;

&lt;p&gt;This is the significant change in the release, and the one most likely to quietly break your tests.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;VerifyCsrfToken&lt;/code&gt; is renamed to &lt;strong&gt;&lt;code&gt;PreventRequestForgery&lt;/code&gt;&lt;/strong&gt;, and it isn't just a rename: it now performs &lt;strong&gt;origin verification&lt;/strong&gt; by reading the browser's &lt;code&gt;Sec-Fetch-Site&lt;/code&gt; header, on top of the usual token validation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Illuminate\Foundation\Http\Middleware\PreventRequestForgery&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Illuminate\Foundation\Http\Middleware\VerifyCsrfToken&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Laravel &amp;lt;= 12.x&lt;/span&gt;
&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;withoutMiddleware&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nc"&gt;VerifyCsrfToken&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

&lt;span class="c1"&gt;// Laravel &amp;gt;= 13.x&lt;/span&gt;
&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;withoutMiddleware&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nc"&gt;PreventRequestForgery&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;VerifyCsrfToken&lt;/code&gt; and &lt;code&gt;ValidateCsrfToken&lt;/code&gt; &lt;strong&gt;still exist as deprecated aliases&lt;/strong&gt;, so your app won't explode on day one. But:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Referencing them directly stops being a good idea, especially when excluding middleware in tests or route definitions.&lt;/li&gt;
&lt;li&gt;The middleware configuration API now exposes &lt;code&gt;preventRequestForgery(...)&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Where this really bites is test suites that call &lt;code&gt;withoutMiddleware([VerifyCsrfToken::class])&lt;/code&gt; expecting to disable the protection: because the alias points elsewhere, the exclusion may not apply the way you assumed. Grep for it before upgrading:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-rn&lt;/span&gt; &lt;span class="s2"&gt;"VerifyCsrfToken&lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="s2"&gt;ValidateCsrfToken"&lt;/span&gt; app/ tests/ routes/ bootstrap/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since you're hardening request forgery protection anyway, this is a good moment to check which security headers your domain is actually returning in production:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Free tool:&lt;/strong&gt; &lt;a href="https://ortamarco.me/en/tools/security-headers/" rel="noopener noreferrer"&gt;HTTP Security Headers&lt;/a&gt; — Analyse a site's HTTP security headers (HSTS, CSP, X-Frame-Options and more) and get a grade plus the ones you are missing.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Medium impact
&lt;/h2&gt;

&lt;h3&gt;
  
  
  3. &lt;code&gt;serializable_classes&lt;/code&gt; in the cache config
&lt;/h3&gt;

&lt;p&gt;The default &lt;code&gt;cache&lt;/code&gt; configuration now includes &lt;code&gt;serializable_classes =&amp;gt; false&lt;/code&gt;. It's a hardening measure against &lt;strong&gt;deserialization gadget chain attacks&lt;/strong&gt;: if your &lt;code&gt;APP_KEY&lt;/code&gt; leaks, without this protection an attacker could inject serialized PHP objects into the cache and chain magic methods until they achieve code execution.&lt;/p&gt;

&lt;p&gt;The price is that &lt;strong&gt;if your application deliberately stores PHP objects in cache, it stops working&lt;/strong&gt; unless you explicitly declare which classes may be unserialized:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// config/cache.php&lt;/span&gt;
&lt;span class="s1"&gt;'serializable_classes'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nc"&gt;App\Data\CachedDashboardStats&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nc"&gt;App\Support\CachedPricingSnapshot&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&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;p&gt;If you were caching arbitrary objects, you have two ways out: an explicit allow-list like the one above, or migrating to object-free payloads (plain arrays). The second is more work and the better idea.&lt;/p&gt;

&lt;p&gt;Watch out for one detail: these errors &lt;strong&gt;don't show up at deploy time&lt;/strong&gt;, they show up the first time someone reads a cache entry that contained an object. Which means in production, a while later. Flush the cache on deploy and cover it with a test.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. &lt;code&gt;upsert&lt;/code&gt; with MySQL or MariaDB
&lt;/h3&gt;

&lt;p&gt;Laravel now validates that &lt;code&gt;uniqueBy&lt;/code&gt; isn't empty, and throws &lt;code&gt;InvalidArgumentException&lt;/code&gt; instead of generating invalid SQL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Laravel &amp;gt;= 13.x: this now blows up with InvalidArgumentException&lt;/span&gt;
&lt;span class="no"&gt;DB&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;table&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'stats'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;upsert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$rows&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="s1"&gt;'views'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The curious part is that the MySQL and MariaDB drivers &lt;strong&gt;ignore the value of &lt;code&gt;uniqueBy&lt;/code&gt;&lt;/strong&gt; — they always use the table's primary and unique indexes to detect existing records. The validation applies regardless. So if your code passed an empty array because "MySQL ignores it anyway," you now have to pass the real columns.&lt;/p&gt;




&lt;h2&gt;
  
  
  Low impact (the ones that will actually bite)
&lt;/h2&gt;

&lt;p&gt;This is where the real value is, because nobody lists these and they're the ones that surface three weeks after the deploy.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Cache prefixes and session cookie name
&lt;/h3&gt;

&lt;p&gt;The default prefixes move from underscore to hyphen:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Laravel &amp;lt;= 12.x&lt;/span&gt;
&lt;span class="nc"&gt;Str&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'APP_NAME'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'laravel'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s1"&gt;'_'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="s1"&gt;'_cache_'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nc"&gt;Str&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'APP_NAME'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'laravel'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s1"&gt;'_'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="s1"&gt;'_database_'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nc"&gt;Str&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'APP_NAME'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'laravel'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s1"&gt;'_'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="s1"&gt;'_session'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Laravel &amp;gt;= 13.x&lt;/span&gt;
&lt;span class="nc"&gt;Str&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'APP_NAME'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'laravel'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="s1"&gt;'-cache-'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nc"&gt;Str&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'APP_NAME'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'laravel'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="s1"&gt;'-database-'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nc"&gt;Str&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'APP_NAME'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'laravel'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="s1"&gt;'-session'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It only affects you if you &lt;strong&gt;don't&lt;/strong&gt; have those values defined in your configuration and rely on the framework fallback. The symptom is ugly and confusing: &lt;strong&gt;the entire cache is invalidated at once and every active session is logged out&lt;/strong&gt; the moment you deploy, because the keys and the cookie name changed.&lt;/p&gt;

&lt;p&gt;To avoid it, pin the values explicitly before upgrading:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;CACHE_PREFIX&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;myapp_cache_
&lt;span class="nv"&gt;REDIS_PREFIX&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;myapp_database_
&lt;span class="nv"&gt;SESSION_COOKIE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;myapp_session
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  6. &lt;code&gt;JobAttempted&lt;/code&gt; now exposes the exception
&lt;/h3&gt;

&lt;p&gt;The event swaps a boolean property for the exception object:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Laravel &amp;lt;= 12.x&lt;/span&gt;
&lt;span class="nv"&gt;$event&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;exceptionOccurred&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;   &lt;span class="c1"&gt;// bool&lt;/span&gt;

&lt;span class="c1"&gt;// Laravel &amp;gt;= 13.x&lt;/span&gt;
&lt;span class="nv"&gt;$event&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;exception&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;           &lt;span class="c1"&gt;// Throwable|null&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's a change for the better — now you know &lt;em&gt;what&lt;/em&gt; failed, not just &lt;em&gt;that&lt;/em&gt; it failed — but any listener reading &lt;code&gt;exceptionOccurred&lt;/code&gt; stops working. And since it's a boolean read inside an &lt;code&gt;if&lt;/code&gt;, the failure is silent: &lt;code&gt;null&lt;/code&gt; is falsy, so your listener simply stops reacting.&lt;/p&gt;

&lt;h3&gt;
  
  
  7. &lt;code&gt;QueueBusy&lt;/code&gt;: &lt;code&gt;$connection&lt;/code&gt; becomes &lt;code&gt;$connectionName&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;A pure rename, for consistency with the rest of the queue events. If you monitor queues with your own listener, update it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-rn&lt;/span&gt; &lt;span class="s2"&gt;"QueueBusy"&lt;/span&gt; app/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  8. &lt;code&gt;Container::call&lt;/code&gt; respects nullable defaults
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$container&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;?Carbon&lt;/span&gt; &lt;span class="nv"&gt;$date&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$date&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Laravel &amp;lt;= 12.x: a Carbon instance&lt;/span&gt;
&lt;span class="c1"&gt;// Laravel &amp;gt;= 13.x: null&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It now matches the constructor injection behavior introduced in Laravel 12. If you had code relying on the container resolving the class despite the &lt;code&gt;= null&lt;/code&gt;, the result changes without warning.&lt;/p&gt;

&lt;h3&gt;
  
  
  9. Domain route precedence
&lt;/h3&gt;

&lt;p&gt;Routes with an explicit domain are now evaluated &lt;strong&gt;before&lt;/strong&gt; routes without one. This fixes the catch-all subdomain case, which previously depended on registration order.&lt;/p&gt;

&lt;p&gt;If you do multi-tenancy by subdomain, or you declare domain routes after general ones, review your matching. It's one of the few changes on this list that can alter &lt;strong&gt;which controller handles a request&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  10. &lt;code&gt;DELETE&lt;/code&gt; with &lt;code&gt;JOIN&lt;/code&gt;, &lt;code&gt;ORDER BY&lt;/code&gt;, and &lt;code&gt;LIMIT&lt;/code&gt; on MySQL
&lt;/h3&gt;

&lt;p&gt;Previously, in a delete with a join, the &lt;code&gt;ORDER BY&lt;/code&gt; and &lt;code&gt;LIMIT&lt;/code&gt; clauses were &lt;strong&gt;silently ignored&lt;/strong&gt;. Now they're compiled and sent to the database.&lt;/p&gt;

&lt;p&gt;In practice: code you believed was bounded (&lt;code&gt;-&amp;gt;limit(100)&lt;/code&gt;) but was actually deleting without a limit can now throw &lt;code&gt;QueryException&lt;/code&gt;, because MySQL and MariaDB don't support that syntax in joined deletes.&lt;/p&gt;

&lt;p&gt;It's an awkward change but a healthy one — it was lying to you. If you run into it, inspect the generated SQL before touching anything:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Free tool:&lt;/strong&gt; &lt;a href="https://ortamarco.me/en/tools/sql-formatter/" rel="noopener noreferrer"&gt;SQL Formatter / Beautifier&lt;/a&gt; — Format and beautify your SQL code with ease.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  11. Polymorphic pivot table names
&lt;/h3&gt;

&lt;p&gt;When inferring the table name for a polymorphic pivot model with a custom class, Laravel now &lt;strong&gt;pluralizes&lt;/strong&gt;. If you relied on the singular inferred name, declare the table explicitly on the pivot:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Taggable&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;MorphPivot&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="nv"&gt;$table&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'taggable'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;   &lt;span class="c1"&gt;// this used to be inferred&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  12. Collection serialization restores eager-loaded relations
&lt;/h3&gt;

&lt;p&gt;When a model collection is serialized and restored — the usual case in queued jobs — relations loaded with &lt;code&gt;with()&lt;/code&gt; are now &lt;strong&gt;present again&lt;/strong&gt; after deserialization.&lt;/p&gt;

&lt;p&gt;That's usually what you want. But if your job counted on the relations &lt;em&gt;not&lt;/em&gt; being there (to force a fresh reload from the database, say), it now works with potentially stale data. That bug is especially treacherous because nothing fails: it just processes old data.&lt;/p&gt;

&lt;h3&gt;
  
  
  13. &lt;code&gt;Str&lt;/code&gt; factories reset between tests
&lt;/h3&gt;

&lt;p&gt;Laravel now clears custom &lt;code&gt;Str&lt;/code&gt; factories during test teardown. If you defined a deterministic UUID or ULID generator once — in a &lt;code&gt;setUpBeforeClass&lt;/code&gt; or a global helper — and expected it to persist across methods, you now have to define it in each test or in &lt;code&gt;setUp&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Symptom: tests that pass in isolation and fail in the suite.&lt;/p&gt;

&lt;h3&gt;
  
  
  14. &lt;code&gt;extend&lt;/code&gt; callbacks are bound to the manager
&lt;/h3&gt;

&lt;p&gt;Custom driver closures registered via &lt;code&gt;extend&lt;/code&gt; now have the &lt;strong&gt;manager&lt;/strong&gt; as &lt;code&gt;$this&lt;/code&gt;, not whatever was there before. If you used &lt;code&gt;$this&lt;/code&gt; inside the closure expecting the service provider, move it into a capture:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Before: $this was the service provider&lt;/span&gt;
&lt;span class="nc"&gt;Cache&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'mydriver'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$app&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;createStore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$app&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;      &lt;span class="c1"&gt;// no longer works&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Now&lt;/span&gt;
&lt;span class="nv"&gt;$provider&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nc"&gt;Cache&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'mydriver'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$app&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$provider&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$provider&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;createStore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$app&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;h3&gt;
  
  
  15. Bootstrap 3 pagination view names
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Laravel &amp;lt;= 12.x
pagination::default
pagination::simple-default

// Laravel &amp;gt;= 13.x
pagination::bootstrap-3
pagination::simple-bootstrap-3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Only relevant if you reference those view names directly. The old names were confusing — &lt;code&gt;default&lt;/code&gt; wasn't the actual default — and now they're explicit.&lt;/p&gt;

&lt;h3&gt;
  
  
  16. &lt;code&gt;Js::from&lt;/code&gt; stops escaping Unicode
&lt;/h3&gt;

&lt;p&gt;It now uses &lt;code&gt;JSON_UNESCAPED_UNICODE&lt;/code&gt; by default. If you have tests or output comparisons expecting &lt;code&gt;è&lt;/code&gt; instead of &lt;code&gt;è&lt;/code&gt;, update the expectations. Any accented character triggers this.&lt;/p&gt;

&lt;h3&gt;
  
  
  17. The PHP 8.5 polyfill and global helpers
&lt;/h3&gt;

&lt;p&gt;This is the easiest one to miss. Laravel 13 adds a dependency on &lt;strong&gt;&lt;code&gt;symfony/polyfill-php85&lt;/code&gt;&lt;/strong&gt;, which on PHP below 8.5 defines global functions such as &lt;code&gt;array_first()&lt;/code&gt; and &lt;code&gt;array_last()&lt;/code&gt; if they don't already exist.&lt;/p&gt;

&lt;p&gt;The conflict: the historical &lt;code&gt;array_first()&lt;/code&gt; from &lt;code&gt;laravel/helpers&lt;/code&gt; &lt;strong&gt;accepted a callback&lt;/strong&gt; to return the first element matching a condition. The polyfilled one only returns the array's first element. Same apparent signature, different behavior.&lt;/p&gt;

&lt;p&gt;If you still carry &lt;code&gt;laravel/helpers&lt;/code&gt; or your own global helpers with those names, always use &lt;code&gt;Arr&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Illuminate\Support\Arr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nc"&gt;Arr&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;first&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$array&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$value&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;active&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Very low impact: only if you maintain packages
&lt;/h2&gt;

&lt;p&gt;If you implement framework contracts yourself, there are new methods to add. If you only consume Laravel, skip this section entirely.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Contract&lt;/th&gt;
&lt;th&gt;Method to add&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Illuminate\Contracts\Cache\Store&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;touch($key, $seconds)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Illuminate\Contracts\Bus\Dispatcher&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;dispatchAfterResponse($command, $handler = null)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Illuminate\Contracts\Routing\ResponseFactory&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;eventStream(...)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Illuminate\Contracts\Auth\MustVerifyEmail&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;markEmailAsUnverified()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Illuminate\Contracts\Queue\Queue&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;pendingSize&lt;/code&gt;, &lt;code&gt;delayedSize&lt;/code&gt;, &lt;code&gt;reservedSize&lt;/code&gt;, &lt;code&gt;creationTimeOfOldestPendingJob&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;On top of that, &lt;strong&gt;instantiating a model while that model is still booting now throws &lt;code&gt;LogicException&lt;/code&gt;&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;boot&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;parent&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;boot&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="nc"&gt;static&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getTable&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;   &lt;span class="c1"&gt;// LogicException on Laravel 13&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the HTTP client's &lt;code&gt;throw&lt;/code&gt; and &lt;code&gt;throwIf&lt;/code&gt; signatures now declare their callbacks explicitly, which only matters if you override those methods in a custom response class.&lt;/p&gt;

&lt;h2&gt;
  
  
  What does NOT break, whatever you may have read
&lt;/h2&gt;

&lt;p&gt;A couple of false claims are circulating about this upgrade:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;app/Http/Kernel.php&lt;/code&gt; does not disappear in Laravel 13.&lt;/strong&gt; That file was removed in &lt;strong&gt;Laravel 11&lt;/strong&gt;, with the new application structure. If you're coming from 12 you no longer have it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Laravel 13 does not require PHP 8.4.&lt;/strong&gt; The minimum is 8.3 and the supported range goes up to 8.5.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;VerifyCsrfToken&lt;/code&gt; is not removed.&lt;/strong&gt; It remains a deprecated alias. Migrating it is wise, but it won't take your deploy down.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;There are no "zero breaking changes."&lt;/strong&gt; The documentation itself says &lt;em&gt;minimal&lt;/em&gt;, and flags two high-impact changes. The gap between "minimal" and "zero" is exactly this article.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The upgrade, in practice
&lt;/h2&gt;

&lt;p&gt;With the full list in front of you, the procedure is short:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 1. New branch, always&lt;/span&gt;
git checkout &lt;span class="nt"&gt;-b&lt;/span&gt; upgrade/laravel-13

&lt;span class="c"&gt;# 2. PHP 8.3+ before anything else&lt;/span&gt;
php &lt;span class="nt"&gt;-v&lt;/span&gt;

&lt;span class="c"&gt;# 3. Pin cache and session prefixes so you don't invalidate everything&lt;/span&gt;
&lt;span class="c"&gt;#    (CACHE_PREFIX, REDIS_PREFIX, SESSION_COOKIE in your .env)&lt;/span&gt;

&lt;span class="c"&gt;# 4. Grep for what you know changes&lt;/span&gt;
&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-rn&lt;/span&gt; &lt;span class="s2"&gt;"VerifyCsrfToken&lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="s2"&gt;ValidateCsrfToken"&lt;/span&gt; app/ tests/ routes/ bootstrap/
&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-rn&lt;/span&gt; &lt;span class="s2"&gt;"exceptionOccurred&lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="s2"&gt;QueueBusy"&lt;/span&gt; app/
&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-rn&lt;/span&gt; &lt;span class="s2"&gt;"array_first&lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="s2"&gt;array_last"&lt;/span&gt; app/

&lt;span class="c"&gt;# 5. Update dependencies&lt;/span&gt;
composer update &lt;span class="nt"&gt;--with-all-dependencies&lt;/span&gt;

&lt;span class="c"&gt;# 6. Clear everything&lt;/span&gt;
php artisan optimize:clear

&lt;span class="c"&gt;# 7. Tests&lt;/span&gt;
php artisan &lt;span class="nb"&gt;test&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Laravel also offers an assisted route: if you have &lt;strong&gt;Laravel Boost &lt;code&gt;^2.0&lt;/code&gt;&lt;/strong&gt; installed, the &lt;code&gt;/upgrade-laravel-v13&lt;/code&gt; command walks the upgrade from Claude Code, Cursor, OpenCode, Gemini, or VS Code. And for large projects, &lt;a href="https://laravelshift.com" rel="noopener noreferrer"&gt;Shift&lt;/a&gt; is still the paid option that automates the bulk of it.&lt;/p&gt;

&lt;p&gt;One last step almost nobody takes, and it saves surprises: diff your configuration files against &lt;code&gt;laravel/laravel&lt;/code&gt; on the 13.x branch. Many changes on this list — &lt;code&gt;serializable_classes&lt;/code&gt; being the obvious one — live in &lt;code&gt;config/&lt;/code&gt;, not in the framework, and don't arrive on their own with &lt;code&gt;composer update&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;One catch when you copy, though: the 13 skeleton's &lt;code&gt;config/session.php&lt;/code&gt; ships &lt;code&gt;'serialization' =&amp;gt; 'json'&lt;/code&gt;. If your app was on &lt;code&gt;php&lt;/code&gt; and you sync that value as-is, &lt;strong&gt;every active session is invalidated&lt;/strong&gt; on deploy and every user has to log in again. For a seamless upgrade, keep it at &lt;code&gt;php&lt;/code&gt;. Moving to &lt;code&gt;json&lt;/code&gt; is the recommended hardening against deserialization attacks, but only if you don't store PHP objects in the session, and only when you're fine forcing that re-login.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Free tool:&lt;/strong&gt; &lt;a href="https://ortamarco.me/en/tools/diff-checker/" rel="noopener noreferrer"&gt;Text Diff Checker&lt;/a&gt; — Compare two texts and highlight the differences line by line, with unified and side-by-side views and ignore case or whitespace. All in your browser.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Checklist before you merge
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;[ ] PHP 8.3 or higher on local, CI, and production&lt;/li&gt;
&lt;li&gt;[ ] &lt;code&gt;laravel/framework ^13.0&lt;/code&gt;, &lt;code&gt;laravel/tinker ^3.0&lt;/code&gt;, &lt;code&gt;phpunit ^12.0&lt;/code&gt; or &lt;code&gt;pest ^4.0&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;[ ] Zero references to &lt;code&gt;VerifyCsrfToken&lt;/code&gt; in tests and routes&lt;/li&gt;
&lt;li&gt;[ ] &lt;code&gt;CACHE_PREFIX&lt;/code&gt;, &lt;code&gt;REDIS_PREFIX&lt;/code&gt;, and &lt;code&gt;SESSION_COOKIE&lt;/code&gt; pinned explicitly&lt;/li&gt;
&lt;li&gt;[ ] &lt;code&gt;config/cache.php&lt;/code&gt; reviewed if you cache PHP objects&lt;/li&gt;
&lt;li&gt;[ ] Every &lt;code&gt;upsert()&lt;/code&gt; call passes a non-empty &lt;code&gt;uniqueBy&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;[ ] &lt;code&gt;JobAttempted&lt;/code&gt; and &lt;code&gt;QueueBusy&lt;/code&gt; listeners updated&lt;/li&gt;
&lt;li&gt;[ ] Domain routes reviewed if you use subdomains&lt;/li&gt;
&lt;li&gt;[ ] Test suite green &lt;strong&gt;as a whole&lt;/strong&gt;, not just file by file&lt;/li&gt;
&lt;li&gt;[ ] Cache flushed on deploy&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Laravel 13 is an honest upgrade: 10 minutes for most apps, and the new capabilities — the first-party AI SDK, JSON:API resources, &lt;code&gt;Queue::route()&lt;/code&gt;, PHP attributes for middleware and jobs, &lt;code&gt;Cache::touch()&lt;/code&gt;, vector search with &lt;code&gt;whereVectorSimilarTo()&lt;/code&gt; — arrive without demanding a rewrite in exchange.&lt;/p&gt;

&lt;p&gt;But "minimal" is not "zero," and the changes that actually hurt aren't the two high-impact ones — those are documented and surface immediately — but the low-impact ones that fail silently: the cache that invalidates wholesale, the listener that stops reacting, the job that processes stale data, the test that only fails in the suite.&lt;/p&gt;

&lt;p&gt;That date has passed: &lt;strong&gt;August 13, 2026&lt;/strong&gt;. Since then, every bug you find in Laravel 12 is yours.&lt;/p&gt;

&lt;h2&gt;
  
  
  Frequently asked questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  When does Laravel 12 support end?
&lt;/h3&gt;

&lt;p&gt;Laravel 12 stopped receiving bug fixes on August 13, 2026 and stops receiving security patches on February 24, 2027. Laravel applies the same policy to every major version: 18 months of bug fixes and 2 years of security. Laravel 12 is not an LTS release.&lt;/p&gt;

&lt;h3&gt;
  
  
  Which PHP version does Laravel 13 require?
&lt;/h3&gt;

&lt;p&gt;Laravel 13 requires PHP 8.3 as a minimum and supports up to PHP 8.5. It is not true that it requires PHP 8.4: it works correctly on 8.3. If you are coming from Laravel 12 on PHP 8.2, raising the PHP version is the only real infrastructure work in the upgrade.&lt;/p&gt;

&lt;h3&gt;
  
  
  What are the high-impact breaking changes in Laravel 13?
&lt;/h3&gt;

&lt;p&gt;There are two. The first is updating dependencies in composer.json: laravel/framework to ^13.0, laravel/tinker to ^3.0, laravel/boost to ^2.0, phpunit/phpunit to ^12.0, and pestphp/pest to ^4.0. The second is that the CSRF middleware VerifyCsrfToken is renamed to PreventRequestForgery and adds origin verification through the Sec-Fetch-Site header. VerifyCsrfToken still exists as a deprecated alias.&lt;/p&gt;

&lt;h3&gt;
  
  
  Is app/Http/Kernel.php removed in Laravel 13?
&lt;/h3&gt;

&lt;p&gt;No. That file was removed in Laravel 11 with the new application structure, not in Laravel 13. If you are coming from Laravel 12 your project no longer has it and there is nothing to do about it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why did my entire cache disappear after upgrading to Laravel 13?
&lt;/h3&gt;

&lt;p&gt;Because the default cache, Redis, and session prefixes changed from underscore to hyphen: from myapp_cache_ to myapp-cache-. It only affects applications that rely on the framework fallback instead of defining those values in their configuration. The effect is that every cache key changes and active sessions are logged out. To avoid it, pin CACHE_PREFIX, REDIS_PREFIX, and SESSION_COOKIE in your .env before upgrading.&lt;/p&gt;

&lt;h3&gt;
  
  
  How long does upgrading from Laravel 12 to Laravel 13 take?
&lt;/h3&gt;

&lt;p&gt;The official documentation estimates 10 minutes for a typical application. In practice, the time goes into raising PHP to 8.3 if you were on 8.2, reviewing third-party packages that do not yet declare Laravel 13 compatibility, and checking the low-impact changes that fail silently. With Laravel Boost 2.0 installed you can use the /upgrade-laravel-v13 command to have an AI assistant guide the process.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://ortamarco.me/en/blog/what-breaks-upgrading-to-laravel-13/" rel="noopener noreferrer"&gt;ortamarco.me&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>laravel</category>
      <category>php</category>
      <category>migration</category>
      <category>webdev</category>
    </item>
    <item>
      <title>The AI model your business runs on is being retired: the 2026 shutdown calendar</title>
      <dc:creator>Marco Orta</dc:creator>
      <pubDate>Mon, 21 Sep 2026 08:20:03 +0000</pubDate>
      <link>https://dev.to/marco_odev/the-ai-model-your-business-runs-on-is-being-retired-the-2026-shutdown-calendar-58h8</link>
      <guid>https://dev.to/marco_odev/the-ai-model-your-business-runs-on-is-being-retired-the-2026-shutdown-calendar-58h8</guid>
      <description>&lt;p&gt;&lt;strong&gt;OpenAI already shut down the Assistants API on 26 August 2026. The next cutoffs are 24 September, when it retires the Videos API and &lt;code&gt;sora-2&lt;/code&gt;; 28 September, for the last legacy completion snapshots; and 23 October, for &lt;code&gt;gpt-3.5-turbo&lt;/code&gt; and &lt;code&gt;gpt-4&lt;/code&gt;.&lt;/strong&gt; If your business runs a chatbot, an email classifier or any AI automation that was built for you in 2023 or 2024, there is a very high chance it is wired to one of those last two models. And on shutdown day it will not degrade gracefully: it will return an error and stop working.&lt;/p&gt;

&lt;p&gt;This is not speculation or industry chatter. These are dates published by the providers themselves on their own deprecation pages, with six months' notice. The problem is that the notice arrives by email to the developer account that created the API key — which half the time belongs to an agency that no longer works with you.&lt;/p&gt;

&lt;p&gt;This article is the full calendar, how to find out in three minutes whether it affects you, and what to do if it does. If what you need is what each model costs, that lives in the &lt;a href="https://ortamarco.me/en/blog/how-much-openai-claude-gemini-api-costs-2026/" rel="noopener noreferrer"&gt;guide to OpenAI, Claude and Gemini API pricing&lt;/a&gt;; here we go straight to what shuts down and when.&lt;/p&gt;

&lt;h2&gt;
  
  
  What nobody told you when you bought it
&lt;/h2&gt;

&lt;p&gt;AI models are not software you buy once. They are a service with an expiry date, and the provider decides the date.&lt;/p&gt;

&lt;p&gt;OpenAI puts it plainly in its documentation: &lt;em&gt;"software relying on OpenAI models may need occasional updates to keep working."&lt;/em&gt; Anthropic writes almost the same sentence. Translated into the language of the person paying the invoice: &lt;strong&gt;the chatbot you were handed as "finished" carries mandatory maintenance, and nobody put it in the quote.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This changes a decision plenty of businesses got wrong. When you asked for a quote to automate something with AI, you compared the implementation price and the monthly token cost. What you did not compare — because nobody showed you — was &lt;strong&gt;how often you would be paying for a migration&lt;/strong&gt;. And on that, as we will see, providers are nothing alike.&lt;/p&gt;




&lt;h2&gt;
  
  
  First: does this affect you? The three-minute check
&lt;/h2&gt;

&lt;p&gt;Before reading the calendar, find out which model you actually use. Not what you think you use: what is written in the code.&lt;/p&gt;

&lt;h3&gt;
  
  
  If you have access to the OpenAI dashboard
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Sign in to &lt;a href="https://platform.openai.com" rel="noopener noreferrer"&gt;platform.openai.com&lt;/a&gt; with the account that pays for the API.&lt;/li&gt;
&lt;li&gt;Go to &lt;strong&gt;Usage&lt;/strong&gt; and switch the breakdown to group &lt;strong&gt;by model&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Look at the last 30 days. Those names are what you are really running.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  If you have access to the Anthropic console
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Open the &lt;a href="https://platform.claude.com/usage" rel="noopener noreferrer"&gt;Usage page in the Claude Console&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Export&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;The CSV gives you usage broken down by API key and by model. Anthropic documents this exact procedure for auditing deprecated models.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  If you have access to nothing
&lt;/h3&gt;

&lt;p&gt;This is the most common case, and it is the finding in itself. If nobody at your company can log into the provider's dashboard:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The API key belongs to a third party.&lt;/strong&gt; The retirement notice goes to them, not to you.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You cannot audit the spend&lt;/strong&gt;, so you also do not know whether you are overpaying.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You cannot migrate without them&lt;/strong&gt;, which means the continuity of a business process depends on that person picking up the phone.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All three are governance problems, not technical ones, and they are fixed the same way: the AI provider account is opened in the company's name, paid by the company, and the external developer is added as an invited user. If this is your situation, that is the first job — before any migration.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Free tool:&lt;/strong&gt; &lt;a href="https://ortamarco.me/en/tools/llm-token-counter/" rel="noopener noreferrer"&gt;LLM Token Counter &amp;amp; AI Cost Calculator&lt;/a&gt; — Count the tokens in your text or prompt and estimate the cost across GPT-5, Claude, Gemini, Kimi K3, Grok and DeepSeek. Exact OpenAI counting, per-call and monthly costs, with prompt caching.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  OpenAI's shutdown calendar
&lt;/h2&gt;

&lt;p&gt;These are the dates published on &lt;a href="https://developers.openai.com/api/docs/deprecations" rel="noopener noreferrer"&gt;OpenAI's official deprecations page&lt;/a&gt;, checked on 16 August 2026 and reconfirmed on 13 September 2026. I have bolded the ones that hit small businesses hardest.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Shutdown&lt;/th&gt;
&lt;th&gt;What dies&lt;/th&gt;
&lt;th&gt;Recommended replacement&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;26 Aug 2026&lt;/strong&gt; &lt;em&gt;(already happened)&lt;/em&gt;
&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Assistants API&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Responses API + Conversations API&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;24 Sep 2026&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;sora-2&lt;/code&gt;, &lt;code&gt;sora-2-pro&lt;/code&gt;, Videos API&lt;/td&gt;
&lt;td&gt;&lt;em&gt;no replacement&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;28 Sep 2026&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;gpt-3.5-turbo-instruct&lt;/code&gt;, &lt;code&gt;babbage-002&lt;/code&gt;, &lt;code&gt;davinci-002&lt;/code&gt;, &lt;code&gt;gpt-3.5-turbo-1106&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;gpt-5.6-terra&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1 Oct 2026&lt;/td&gt;
&lt;td&gt;&lt;code&gt;gpt-5.4-cyber&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;gpt-5.6-cyber&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;23 Oct 2026&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;gpt-3.5-turbo-0125&lt;/code&gt;, &lt;code&gt;gpt-4-0613&lt;/code&gt;, &lt;code&gt;gpt-4-turbo&lt;/code&gt;, &lt;code&gt;gpt-4-1106-preview&lt;/code&gt;, &lt;code&gt;gpt-4o-2024-05-13&lt;/code&gt;, &lt;code&gt;o1&lt;/code&gt;, &lt;code&gt;o1-pro&lt;/code&gt;, &lt;code&gt;o3-mini&lt;/code&gt;, &lt;code&gt;o4-mini&lt;/code&gt;, &lt;code&gt;gpt-4.1-nano&lt;/code&gt;, &lt;code&gt;gpt-image-1&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;gpt-5.6-sol&lt;/code&gt; / &lt;code&gt;gpt-5.6-terra&lt;/code&gt; / &lt;code&gt;gpt-5.6-luna&lt;/code&gt; (&lt;code&gt;gpt-image-1&lt;/code&gt; → &lt;code&gt;gpt-image-2&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;31 Oct 2026&lt;/td&gt;
&lt;td&gt;Evals: existing evals become &lt;strong&gt;read-only&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;Promptfoo (OpenAI's migration path)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;30 Nov 2026&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;v1/prompts&lt;/code&gt; API and reusable prompts, Agent Builder, Evals dashboard and API&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Prompts in your own code · Agents SDK or ChatGPT Workspace Agents · Promptfoo&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1 Dec 2026&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;gpt-image-1-mini&lt;/code&gt;, &lt;code&gt;gpt-image-1.5&lt;/code&gt;, &lt;code&gt;chatgpt-image-latest&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;gpt-image-2&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;11 Dec 2026&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;gpt-5-2025-08-07&lt;/code&gt;, &lt;code&gt;gpt-5-mini-2025-08-07&lt;/code&gt;, &lt;code&gt;gpt-5-nano-2025-08-07&lt;/code&gt;, &lt;code&gt;gpt-5-pro-2025-10-06&lt;/code&gt;, &lt;code&gt;o3-2025-04-16&lt;/code&gt;, &lt;code&gt;o3-pro-2025-06-10&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;gpt-5.6-sol&lt;/code&gt; (&lt;code&gt;reasoning.mode: pro&lt;/code&gt; for the pro ones) / &lt;code&gt;gpt-5.6-terra&lt;/code&gt; (mini) / &lt;code&gt;gpt-5.6-luna&lt;/code&gt; (nano)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;20 Jan 2027&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;gpt-realtime&lt;/code&gt;, &lt;code&gt;gpt-audio&lt;/code&gt;, &lt;code&gt;gpt-4o-audio&lt;/code&gt;, &lt;code&gt;gpt-4o-realtime&lt;/code&gt; and mini variants&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;gpt-realtime-2.1&lt;/code&gt; (&lt;code&gt;gpt-realtime-2.1-mini&lt;/code&gt; for the realtime minis) / &lt;code&gt;gpt-audio-1.5&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;26 Feb 2027&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;whisper-1&lt;/code&gt;, &lt;code&gt;gpt-4o-transcribe&lt;/code&gt;, &lt;code&gt;gpt-4o-mini-transcribe&lt;/code&gt;, &lt;code&gt;gpt-4o-transcribe-diarize&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;gpt-transcribe&lt;/code&gt; / &lt;code&gt;gpt-live-transcribe&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Two of those dates are closer than the headline one, and easy to miss because they read like small print next to it. &lt;strong&gt;24 September&lt;/strong&gt; retires the Videos API along with &lt;code&gt;sora-2&lt;/code&gt; and &lt;code&gt;sora-2-pro&lt;/code&gt; — with no replacement listed, so anything built on video generation needs a different plan, not just a different model string. &lt;strong&gt;28 September&lt;/strong&gt; retires the last of the old completion-style snapshots (&lt;code&gt;gpt-3.5-turbo-instruct&lt;/code&gt;, &lt;code&gt;babbage-002&lt;/code&gt;, &lt;code&gt;davinci-002&lt;/code&gt;, &lt;code&gt;gpt-3.5-turbo-1106&lt;/code&gt;) into &lt;code&gt;gpt-5.6-terra&lt;/code&gt;. Neither is wired into as many small-business chatbots as &lt;code&gt;gpt-3.5-turbo&lt;/code&gt; or &lt;code&gt;gpt-4&lt;/code&gt; are — but if your stack touches either one, your date is 24 or 28 September, not 23 October.&lt;/p&gt;

&lt;p&gt;And a third one shows that six months' notice does not apply to everything: &lt;code&gt;gpt-5.4-cyber&lt;/code&gt; was announced on 11 September and shuts down on 1 October, &lt;strong&gt;twenty days later&lt;/strong&gt;. The six-month minimum OpenAI promises covers generally available models, not its whole catalogue, so do not assume it for variants like this one.&lt;/p&gt;

&lt;p&gt;Three readings of that table matter more than the table itself:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. 23 October is the big one.&lt;/strong&gt; &lt;code&gt;gpt-3.5-turbo&lt;/code&gt; and &lt;code&gt;gpt-4-0613&lt;/code&gt; are the models that appear literally in every tutorial from 2023 and 2024. Any integration copied from a blog, a course or the documentation of that era has them hardcoded. And because they worked without complaining for three years, nobody has touched them since. (For what actually changed in the replacements, see the &lt;a href="https://ortamarco.me/en/blog/new-chatgpt-models-gpt-5-6-2026/" rel="noopener noreferrer"&gt;GPT-5.6 launch rundown&lt;/a&gt;.) ⚠️ And if the &lt;strong&gt;GPT-6 Astra&lt;/strong&gt; launch on 3 September tempts you to migrate straight there: for a chatbot or a classifier that is an expensive mistake. Astra costs 20x the input and 33x the output of &lt;code&gt;gpt-3.5-turbo&lt;/code&gt;, and its million-token window hides a 2x surcharge past 272K. The migration ladder by workload type is in &lt;a href="https://ortamarco.me/en/blog/gpt-6-astra-api-developers/" rel="noopener noreferrer"&gt;GPT-6 Astra for developers&lt;/a&gt;. And if you're the one migrating the code: changing the name isn't enough, because LangChain and LlamaIndex still default to &lt;code&gt;gpt-3.5-turbo&lt;/code&gt; and the substitutes reject tools on Chat Completions unless you turn reasoning off. The details, with audit greps, are in &lt;a href="https://ortamarco.me/en/blog/what-breaks-openai-gpt-4-shutdown-october-23/" rel="noopener noreferrer"&gt;what breaks in your code on October 23&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Whole APIs are shutting down, not just models.&lt;/strong&gt; The Assistants API, Agent Builder, the Evals platform and reusable prompts all disappear. That is not fixed by changing a string: it is rewriting the integration. Anyone who built on Assistants is looking at days of work, not minutes.&lt;/p&gt;

&lt;p&gt;Reusable prompts are the sneakiest case, because the prompt text is not in your repository: it is stored in the OpenAI dashboard, and your code only references it with &lt;code&gt;prompt: { id: "pmpt_…" }&lt;/code&gt; in the Responses API call. On 30 November the &lt;code&gt;v1/prompts&lt;/code&gt; API and those prompt objects shut down, so the &lt;a href="https://developers.openai.com/api/docs/guides/prompting/migrate-from-prompt-object" rel="noopener noreferrer"&gt;official migration guide&lt;/a&gt; asks you to take the content out of the dashboard, move it into your code and send it as &lt;code&gt;input&lt;/code&gt;. Evals also has an intermediate step: &lt;strong&gt;from 31 October your existing evals become read-only&lt;/strong&gt;, a month before the dashboard and API disappear.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Pinned snapshots die even when the model "lives on".&lt;/strong&gt; Look at &lt;code&gt;gpt-5-2025-08-07&lt;/code&gt;. GPT-5 is not disappearing as a family, but &lt;strong&gt;that specific snapshot is&lt;/strong&gt;. If your code pins the date — which is the good practice for reproducible results — then you are on the shutdown calendar even while running a model that feels current.&lt;/p&gt;




&lt;h2&gt;
  
  
  Anthropic's calendar works differently
&lt;/h2&gt;

&lt;p&gt;Anthropic publishes the same thing on its &lt;a href="https://platform.claude.com/docs/en/about-claude/model-deprecations" rel="noopener noreferrer"&gt;model deprecations page&lt;/a&gt;, but with one difference that changes how you plan.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;Status&lt;/th&gt;
&lt;th&gt;Retirement&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;claude-opus-4-1-20250805&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Retired&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;5 Aug 2026&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;claude-opus-4-20250514&lt;/code&gt;, &lt;code&gt;claude-sonnet-4-20250514&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Retired&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;15 Jun 2026&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;claude-3-7-sonnet-20250219&lt;/code&gt;, &lt;code&gt;claude-3-5-haiku-20241022&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Retired&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;19 Feb 2026&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;claude-3-haiku-20240307&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Retired&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;20 Apr 2026&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;claude-sonnet-4-5-20250929&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Active&lt;/td&gt;
&lt;td&gt;not sooner than 29 Sep 2026&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;claude-haiku-4-5-20251001&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Active&lt;/td&gt;
&lt;td&gt;not sooner than 15 Oct 2026&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;claude-opus-4-5-20251101&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Active&lt;/td&gt;
&lt;td&gt;not sooner than 24 Nov 2026&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;claude-opus-5&lt;/code&gt;, &lt;code&gt;claude-sonnet-5&lt;/code&gt;, &lt;code&gt;claude-fable-5&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Active&lt;/td&gt;
&lt;td&gt;2027&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Careful not to read those "not sooner than" entries as shutdown dates: they are floors, not announcements. As of 13 September 2026 none of the three Claude 4.5 models has an announced deprecation, and with the 60 days' notice Anthropic commits to, none of them can be retired before 12 November.&lt;/p&gt;

&lt;p&gt;The difference is in the notice period, and it is a big one:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Provider&lt;/th&gt;
&lt;th&gt;Committed advance notice&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;OpenAI&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;6 months&lt;/strong&gt; for generally available models · 3 months for specialised variants · &lt;strong&gt;2 weeks&lt;/strong&gt; for preview models&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Anthropic&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;60 days&lt;/strong&gt; for publicly released models&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Google (Gemini)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Its deprecations page sets no notice period: it tracks &lt;em&gt;"the announced earliest shutdown dates"&lt;/em&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Sixty days is not much. &lt;code&gt;claude-opus-4-1&lt;/code&gt; was announced on 5 June and switched off on 5 August: &lt;strong&gt;two months exactly&lt;/strong&gt;. If your business slows down in August, or your developer is on holiday, or nobody simply reads that email, the entire notice window passes you by.&lt;/p&gt;

&lt;p&gt;This does not make Anthropic a bad choice — its models are excellent, and the retirement pace is the flip side of a very high release pace. It means &lt;strong&gt;the notice period is a product feature you should compare before choosing&lt;/strong&gt;, the same as price or latency. And it appears in none of the comparison posts doing the rounds.&lt;/p&gt;




&lt;h2&gt;
  
  
  Gemini: two shutdowns between September and October
&lt;/h2&gt;

&lt;p&gt;Google publishes its own on the &lt;a href="https://ai.google.dev/gemini-api/docs/deprecations" rel="noopener noreferrer"&gt;Gemini API deprecations page&lt;/a&gt;, checked on 13 September 2026. Between September and December 2026 there are two dates, plus one that has already passed and is worth checking if your integration dates from 2025:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Shutdown&lt;/th&gt;
&lt;th&gt;What dies&lt;/th&gt;
&lt;th&gt;Recommended replacement&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1 Jun 2026 &lt;em&gt;(already happened)&lt;/em&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;gemini-2.0-flash&lt;/code&gt;, &lt;code&gt;gemini-2.0-flash-001&lt;/code&gt;, &lt;code&gt;gemini-2.0-flash-lite&lt;/code&gt;, &lt;code&gt;gemini-2.0-flash-lite-001&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;gemini-3.6-flash&lt;/code&gt; / &lt;code&gt;gemini-3.1-flash-lite&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;30 Sep 2026&lt;/td&gt;
&lt;td&gt;&lt;code&gt;gemini-omni-flash-preview&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;gemini-omni-1.1-flash&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2 Oct 2026&lt;/td&gt;
&lt;td&gt;&lt;code&gt;gemini-2.5-flash-image&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;gemini-3.1-flash-image&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Two details in that table. &lt;code&gt;gemini-omni-flash-preview&lt;/code&gt;, the video generation model, launched on 30 June and shuts down three months later: the textbook example of why you do not build a business process on a &lt;em&gt;preview&lt;/em&gt;. And for &lt;code&gt;gemini-2.5-flash-image&lt;/code&gt; — the original Nano Banana — the official page lists &lt;code&gt;gemini-3.1-flash-image-preview&lt;/code&gt; as the replacement, but that preview was already shut down on 25 June 2026 and the same page gives the stable version, &lt;code&gt;gemini-3.1-flash-image&lt;/code&gt; (Nano Banana 2), as its replacement, which is where it makes sense to migrate directly.&lt;/p&gt;

&lt;p&gt;Gemini 2.5's text models (&lt;code&gt;gemini-2.5-pro&lt;/code&gt;, &lt;code&gt;gemini-2.5-flash&lt;/code&gt;, &lt;code&gt;gemini-2.5-flash-lite&lt;/code&gt;) still have no announced shutdown date.&lt;/p&gt;




&lt;h2&gt;
  
  
  The trap that catches the most people: where you run it changes the date
&lt;/h2&gt;

&lt;p&gt;This is the detail nobody has written up properly, and the one that produces the most expensive surprises.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The dates above are the original provider's. If you consume the same model through somebody else's cloud, the dates are different.&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Anthropic says so explicitly: its dates apply to the Claude API, the Claude Platform on AWS and Microsoft Foundry. &lt;strong&gt;Amazon Bedrock and Google Cloud Vertex AI set their own retirement schedules&lt;/strong&gt;, so the same model's status and dates can differ.&lt;/li&gt;
&lt;li&gt;The same is true of OpenAI: &lt;strong&gt;Azure OpenAI publishes its own retirements table&lt;/strong&gt;, which is not always in sync with OpenAI direct.
&lt;/li&gt;
&lt;/ul&gt;

&lt;pre data-lang="mermaid"&gt;&lt;code&gt;flowchart TB
    A["Same model,&amp;lt;br/&amp;gt;three different dates"] --&amp;gt; B["Provider's direct API"]
    A --&amp;gt; C["Azure OpenAI / Microsoft Foundry"]
    A --&amp;gt; D["Amazon Bedrock / Google Vertex"]
    B --&amp;gt; B1["Date on the official&amp;lt;br/&amp;gt;deprecations page"]
    C --&amp;gt; C1["Microsoft's own schedule"]
    D --&amp;gt; D1["Cloud operator's&amp;lt;br/&amp;gt;own schedule"]
    B1 --&amp;gt; E["⚠️ Check the table for&amp;lt;br/&amp;gt;WHERE YOU RUN IT,&amp;lt;br/&amp;gt;not the model maker's"]
    C1 --&amp;gt; E
    D1 --&amp;gt; E&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;The practical consequence: if your integration goes through Bedrock or Azure, &lt;strong&gt;the table you need is not the one I just published&lt;/strong&gt;. It is your cloud's. Setting the alarm for the wrong date is as bad as not setting one.&lt;/p&gt;




&lt;h2&gt;
  
  
  What else breaks, and it isn't a model
&lt;/h2&gt;

&lt;p&gt;Beyond the shutdowns, there are silent retirements that break code without any model dying.&lt;/p&gt;

&lt;p&gt;The live example right now: &lt;strong&gt;Anthropic deprecated the &lt;code&gt;temperature&lt;/code&gt;, &lt;code&gt;top_p&lt;/code&gt; and &lt;code&gt;top_k&lt;/code&gt; parameters&lt;/strong&gt; as of Claude Opus 4.7. It does not ignore them — it &lt;strong&gt;returns a 400 error&lt;/strong&gt; if you send them with anything other than the default value. Any integration that tuned the model's "creativity" with &lt;code&gt;temperature: 0.7&lt;/code&gt; — which is practically every integration from 2023–2024 — stops working the moment you move to a current model.&lt;/p&gt;

&lt;p&gt;This matters because it dismantles the naive plan. Plenty of people assume migrating means swapping &lt;code&gt;"gpt-4"&lt;/code&gt; for &lt;code&gt;"gpt-5.6-sol"&lt;/code&gt; and done. Sometimes it is. But you may also be facing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Parameters that are no longer accepted (the case above).&lt;/li&gt;
&lt;li&gt;Different response formats, if you used structured output.&lt;/li&gt;
&lt;li&gt;Entire APIs rewritten (Assistants → Responses).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prompts that stop performing the same.&lt;/strong&gt; A new model is not the old one but better: it is a different one. Prompts tuned by trial and error against &lt;code&gt;gpt-4&lt;/code&gt; can do worse on &lt;code&gt;gpt-5.6-sol&lt;/code&gt; until they are readjusted.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That last point eats the most budget and is the one that never gets quoted.&lt;/p&gt;




&lt;h2&gt;
  
  
  The migration procedure
&lt;/h2&gt;

&lt;p&gt;If you have a technical team, this is the order that works. If you do not, skip to the next section.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Inventory before touching anything
&lt;/h3&gt;

&lt;p&gt;Search the whole codebase for model strings, not just the obvious file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# OpenAI models hardcoded in the code&lt;/span&gt;
&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-rn&lt;/span&gt; &lt;span class="s2"&gt;"gpt-3.5-turbo&lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="s2"&gt;gpt-4-0613&lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="s2"&gt;gpt-4-turbo&lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="s2"&gt;gpt-4o-2024"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--include&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"*.php"&lt;/span&gt; &lt;span class="nt"&gt;--include&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"*.js"&lt;/span&gt; &lt;span class="nt"&gt;--include&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"*.ts"&lt;/span&gt; &lt;span class="nt"&gt;--include&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"*.py"&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt;

&lt;span class="c"&gt;# Prompts stored in the OpenAI dashboard (v1/prompts shuts down 30 Nov 2026)&lt;/span&gt;
&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-rn&lt;/span&gt; &lt;span class="s2"&gt;"pmpt_&lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="s2"&gt;prompt_id"&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt;

&lt;span class="c"&gt;# Anthropic models&lt;/span&gt;
&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-rn&lt;/span&gt; &lt;span class="s2"&gt;"claude-3&lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="s2"&gt;claude-sonnet-4-2025&lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="s2"&gt;claude-opus-4-2025"&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt;

&lt;span class="c"&gt;# And what nearly everyone forgets: environment variables and config&lt;/span&gt;
&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-rn&lt;/span&gt; &lt;span class="s2"&gt;"MODEL"&lt;/span&gt; .env .env.example config/ 2&amp;gt;/dev/null
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also check the places where the model name is not in the repository at all: n8n or Make panels, serverless functions, cron jobs, and any third-party automation consuming your key.&lt;/p&gt;

&lt;p&gt;This kind of break stays invisible until the exact day for a specific reason: a hardcoded model ID needs no validation to keep working. It succeeds every single time you call it, for as long as the model is alive, then fails on the very next call after retirement. There is no warning header on the &lt;code&gt;200&lt;/code&gt; responses the week before, no gradual dip in quality to notice — the API goes straight from working to a &lt;code&gt;400&lt;/code&gt; or &lt;code&gt;404&lt;/code&gt; the moment the shutdown date arrives. If nothing in your code specifically catches that error, the person using your product just sees something break, with nothing in the interface pointing at the real cause.&lt;/p&gt;

&lt;p&gt;That is also why it is worth pulling your API logs now, not only grepping your code. A spike in &lt;code&gt;400&lt;/code&gt; or &lt;code&gt;404&lt;/code&gt; responses from &lt;code&gt;api.openai.com&lt;/code&gt; or &lt;code&gt;api.anthropic.com&lt;/code&gt; around a retirement date is the model dying in production — and it is usually a customer who reports it first, not a dashboard.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Pin the version, don't use the alias
&lt;/h3&gt;

&lt;p&gt;If your code says plain &lt;code&gt;"gpt-4"&lt;/code&gt;, you are using an alias the provider repoints whenever it likes. That looks convenient and is exactly the opposite: it means &lt;strong&gt;your system's behaviour can change on a Tuesday without you touching anything&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The correct practice is to pin the dated snapshot and control yourself when you move up. Yes, that puts you on the shutdown calendar — but it puts you there predictably, which is precisely what you want.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Compare side by side before switching
&lt;/h3&gt;

&lt;p&gt;Do not swap the model in production and wait to see what happens. Take 30 or 50 real cases from your history, run them through the old model and the new one, and compare outputs side by side. It is an afternoon's work and it is the difference between a migration and an incident.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Free tool:&lt;/strong&gt; &lt;a href="https://ortamarco.me/en/tools/diff-checker/" rel="noopener noreferrer"&gt;Text Diff Checker&lt;/a&gt; — Compare two texts and highlight the differences line by line, with unified and side-by-side views and ignore case or whitespace. All in your browser.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  4. Put the provider behind your own layer
&lt;/h3&gt;

&lt;p&gt;This is the work that makes the next shutdown boring. Instead of calling the provider's SDK from twenty places, call it from one:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// A single place where the decision of which model to use lives.&lt;/span&gt;
&lt;span class="c1"&gt;// The rest of the application asks for "reasoning" or "classification",&lt;/span&gt;
&lt;span class="c1"&gt;// not for "gpt-5.6-sol".&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;MODELS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;reasoning&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;      &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;openai&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;    &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;gpt-5.6-sol&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;classification&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;openai&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;    &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;gpt-5.6-terra&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;summarisation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;anthropic&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;claude-sonnet-5&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;complete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;task&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;keyof&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;MODELS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;model&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;MODELS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;task&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;openai&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nf"&gt;callOpenAI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;callAnthropic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;prompt&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;p&gt;With that in place, the next shutdown is editing one line of an object and re-running step 3's comparison. Without it, it is find-and-replace across twenty files and hoping.&lt;/p&gt;

&lt;p&gt;You do not need a framework for this. LangChain or LlamaIndex solve the same problem and bring far more baggage than a small business needs; thirty lines of your own usually do the job without adding a dependency that can also be deprecated.&lt;/p&gt;




&lt;h2&gt;
  
  
  If you have no technical team: what to demand, and from whom
&lt;/h2&gt;

&lt;p&gt;For the owner or operations lead who will not be touching code, the job is different — and more important.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to ask, in writing, from whoever maintains your integration:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Which exact model each process uses&lt;/strong&gt;, with the full name and snapshot date. "We use ChatGPT" is not an answer.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Which platform it runs on&lt;/strong&gt; — direct API, Azure, Bedrock, Vertex — because the real date depends on it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The shutdown date for each one&lt;/strong&gt;, taken from that platform's table.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Who receives the deprecation emails.&lt;/strong&gt; If it is an account you do not control, change that this week.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What the migration costs and who pays for it.&lt;/strong&gt; This is the uncomfortable question, and it needs asking now, not in October.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;And what belongs in the contract from here on:&lt;/strong&gt; deprecation maintenance is not an enhancement or a nice-to-have — it is what keeps the system switched on. Either it is covered by a retainer, or it is priced up front. Discovering it on shutdown day, with the process already down, is the worst negotiating position available.&lt;/p&gt;

&lt;p&gt;I say this from the uncomfortable side of the table: I make a living building these integrations. And even so, a client who does not understand that this carries maintenance is a client who will feel ripped off a year from now — rightly.&lt;/p&gt;




&lt;h2&gt;
  
  
  What the switch costs (and why you sometimes come out ahead)
&lt;/h2&gt;

&lt;p&gt;Migrating is not always a net expense. Newer models tend to be considerably cheaper per token than the ones they replace, and considerably more capable, so three effects offset each other:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;In your favour:&lt;/strong&gt; the replacement's price per million tokens is usually lower. A process that cost X on &lt;code&gt;gpt-4&lt;/code&gt; can cost a fraction on its successor.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;In your favour:&lt;/strong&gt; more capable models need shorter prompts for the same quality, and a shorter prompt is fewer tokens.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Against you:&lt;/strong&gt; reasoning models burn internal tokens that you also pay for. Switching to one of those by accident can multiply the bill even as the unit price drops.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The only honest way to know is to measure it with your real data before switching. The numbers and the method are in the &lt;a href="https://ortamarco.me/en/blog/how-much-openai-claude-gemini-api-costs-2026/" rel="noopener noreferrer"&gt;OpenAI, Claude and Gemini API pricing guide&lt;/a&gt;, and if you want to estimate a specific prompt's consumption before sending it, the token counter alongside does it with no signup.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Free tool:&lt;/strong&gt; &lt;a href="https://ortamarco.me/en/tools/tokenizer-visualizer/" rel="noopener noreferrer"&gt;Tokenizer Visualizer&lt;/a&gt; — Paste text and see exactly which tokens a language model splits it into, with their IDs. Check why Spanish costs more tokens than English.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  The radical way out: make it un-switchoffable
&lt;/h2&gt;

&lt;p&gt;There is one way to make this article never apply to you: &lt;strong&gt;run the model yourself&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;An open model running on your own hardware cannot be retired by anyone. It keeps working exactly the same three years from now, with the same outputs, no deprecation emails and no date on the calendar. For stable internal processes — classifying email, extracting data from documents, summarising — it is a real option, and today it runs on affordable hardware.&lt;/p&gt;

&lt;p&gt;It is not free: you pay in hardware, in electricity, and in quality that trails the frontier models. I wrote the full analysis, with the machine I use and measured numbers, in &lt;a href="https://ortamarco.me/en/blog/local-llm-business-privacy/" rel="noopener noreferrer"&gt;AI without sending data outside: a local LLM for small firms&lt;/a&gt;. The continuity section of that article is exactly this problem seen from the other side.&lt;/p&gt;

&lt;p&gt;For most people the sensible call is not picking a side but splitting: provider models for what needs frontier quality, your own model for the stable processes you would rather not tie to anybody's calendar.&lt;/p&gt;




&lt;h2&gt;
  
  
  Frequently asked questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What exactly happens on shutdown day?
&lt;/h3&gt;

&lt;p&gt;The request fails. There is no degradation and no automatic substitution: the model stops being reachable and the API returns an error. Anthropic says it literally — &lt;em&gt;"requests to retired models will fail"&lt;/em&gt; — and OpenAI that the model or endpoint &lt;em&gt;"will no longer be accessible."&lt;/em&gt; If your application does not handle that error, what the end user sees is a broken screen.&lt;/p&gt;

&lt;h3&gt;
  
  
  I use ChatGPT in the browser, not the API. Does this affect me?
&lt;/h3&gt;

&lt;p&gt;Far less. The dates in this article are for the &lt;strong&gt;API&lt;/strong&gt;, which is what integrations consume. In the ChatGPT app, retired models are swapped for the current one automatically and at most you notice it answers differently. The serious risk is to anything automated.&lt;/p&gt;

&lt;h3&gt;
  
  
  How do I know if my chatbot uses the Assistants API?
&lt;/h3&gt;

&lt;p&gt;Ask whoever built it, and if you cannot, check the OpenAI dashboard: if assistants appear under &lt;strong&gt;Assistants&lt;/strong&gt;, it is in use. The code gives it away too — it will call endpoints under &lt;code&gt;/v1/assistants&lt;/code&gt; or &lt;code&gt;/v1/threads&lt;/code&gt;. If you have not migrated yet, that one is no longer a countdown: the Assistants API already shut down on 26 August 2026, so a chatbot still calling it is already broken, not about to be.&lt;/p&gt;

&lt;h3&gt;
  
  
  Is migrating just changing the model name?
&lt;/h3&gt;

&lt;p&gt;Sometimes yes, sometimes no. Swapping the string is enough if you only used plain text generation. But you may need to rewrite if you used the Assistants API, if you send already-deprecated parameters — &lt;code&gt;temperature&lt;/code&gt; returns a 400 error on current Claude models — or if your prompts were tuned against the old model. Budget for the output comparison, which is the part that actually takes time.&lt;/p&gt;

&lt;h3&gt;
  
  
  How often will this happen again?
&lt;/h3&gt;

&lt;p&gt;Permanently. OpenAI gives at least 6 months' notice for generally available models; Anthropic, 60 days. At that pace, the realistic plan is &lt;strong&gt;a model review every six months&lt;/strong&gt; and a small migration once a year. It is recurring maintenance, like upgrading the framework or renewing an SSL certificate.&lt;/p&gt;

&lt;h3&gt;
  
  
  Does using LangChain or a chatbot SaaS protect me?
&lt;/h3&gt;

&lt;p&gt;Partly, with caveats. An abstraction layer makes the switch cheaper, but it does not stop the underlying model dying or remove the need to revalidate quality. And if the middleman is a SaaS, you inherit its calendar &lt;strong&gt;and&lt;/strong&gt; the risk that it deprecates features of its own — which is exactly what OpenAI did with Agent Builder, its own product, retiring on 30 November 2026.&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusion: put it in the calendar today
&lt;/h2&gt;

&lt;p&gt;Boiled down to what to do this week:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Find out which model you actually use.&lt;/strong&gt; OpenAI dashboard grouped by model, or the CSV export from the Claude console. Three minutes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Check which platform it runs on&lt;/strong&gt;, because that is what determines the real date.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Set an alarm for every date that applies to you:&lt;/strong&gt; 24 September 2026 if you use Sora or the Videos API; 28 September if you still call old completion-style models; 30 September or 2 October if you generate video or images with Gemini (&lt;code&gt;gemini-omni-flash-preview&lt;/code&gt;, &lt;code&gt;gemini-2.5-flash-image&lt;/code&gt;); 23 October if any &lt;code&gt;gpt-3.5-turbo&lt;/code&gt; or &lt;code&gt;gpt-4&lt;/code&gt; shows up in your inventory; 31 October and 30 November if you use Evals, Agent Builder or prompts stored in the dashboard; 1 December if you generate images with &lt;code&gt;gpt-image-1.5&lt;/code&gt; or &lt;code&gt;gpt-image-1-mini&lt;/code&gt;; and 11 December if you pin &lt;code&gt;gpt-5&lt;/code&gt; or &lt;code&gt;o3&lt;/code&gt; snapshots. (If you were still calling the Assistants API, that alarm already went off on 26 August — check today whether it is broken.)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;If your code calls the Responses API with &lt;code&gt;prompt: { id: "pmpt_…" }&lt;/code&gt;, copy that prompt into your repository.&lt;/strong&gt; The text lives in the OpenAI dashboard, not in your code, and on 30 November 2026 the &lt;code&gt;v1/prompts&lt;/code&gt; API and reusable prompts shut down. The official guide says to move the content into your code and send it as &lt;code&gt;input&lt;/code&gt;. If the account belongs to your agency, ask them for it now, not on 30 November.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Verify who receives the deprecation emails.&lt;/strong&gt; If it is not somebody at your company, fix that before anything else.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;And ask what the migration costs&lt;/strong&gt;, while there is still room to negotiate it calmly.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Model shutdowns are not an exotic technical problem: they are ordinary maintenance for a technology moving fast. What is a problem is finding out about them on the day the chatbot stops answering.&lt;/p&gt;

&lt;p&gt;If you have an AI integration in production and cannot answer question 1 on that list, &lt;a href="https://ortamarco.me/en/#contacto" rel="noopener noreferrer"&gt;get in touch&lt;/a&gt; and we will review it. Auditing which models you run and when they die is a short job, and it is better done weeks before the shutdown than the day after.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://ortamarco.me/en/blog/ai-model-retirements-2026/" rel="noopener noreferrer"&gt;ortamarco.me&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>openai</category>
      <category>claude</category>
      <category>migration</category>
    </item>
    <item>
      <title>Node 26: What Breaks When You Upgrade, and When the Jump Is Worth It (LTS Guide)</title>
      <dc:creator>Marco Orta</dc:creator>
      <pubDate>Mon, 21 Sep 2026 08:19:46 +0000</pubDate>
      <link>https://dev.to/marco_odev/node-26-what-breaks-when-you-upgrade-and-when-the-jump-is-worth-it-lts-guide-4go2</link>
      <guid>https://dev.to/marco_odev/node-26-what-breaks-when-you-upgrade-and-when-the-jump-is-worth-it-lts-guide-4go2</guid>
      <description>&lt;p&gt;&lt;strong&gt;Node 26 shipped on May 5, 2026 and enters LTS on October 28. It's a calm upgrade — the list of things that actually break is short, and almost all of it has been warned about for years — but it has one property no previous version had: it's the last release of the two-per-year model.&lt;/strong&gt; Starting with Node 27, the project moves to &lt;strong&gt;one release per year, and every release is LTS&lt;/strong&gt;. The odd/even distinction that spent a decade deciding which version you could run in production disappears.&lt;/p&gt;

&lt;p&gt;That makes this upgrade more than a version bump: it's the last train on the old schedule, and it's worth boarding knowing what's inside. Here's what really breaks, sorted by impact, and at the end the full calendar so you can decide when to move.&lt;/p&gt;

&lt;h2&gt;
  
  
  First: where are you coming from?
&lt;/h2&gt;

&lt;p&gt;The effort depends on your starting point:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;From Node 24 (the current LTS):&lt;/strong&gt; the comfortable scenario. The incompatible changes are few and very localized; for most projects the migration is checklist work, not refactoring work.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;From Node 22:&lt;/strong&gt; still reasonable, but with more fronts — you absorb everything that changed in 24 (V8, permissions, minimum glibc) plus what's new in 26. Node 22 leaves maintenance in &lt;strong&gt;April 2027&lt;/strong&gt;, so you have a real deadline.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;From Node 25:&lt;/strong&gt; odd releases were never for production, and this was the last one that will ever exist. Jump to 26 without thinking twice.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The schedule change: Node 27 debuts a new model
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://nodejs.org/en/blog/announcements/evolving-the-nodejs-release-schedule" rel="noopener noreferrer"&gt;official project announcement&lt;/a&gt; sums it up:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;Through Node 26&lt;/th&gt;
&lt;th&gt;From Node 27 on&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Major releases per year&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;1&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LTS versions&lt;/td&gt;
&lt;td&gt;Even-numbered only&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;All of them&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Short-lived odd versions&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Gone&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Alpha channel&lt;/td&gt;
&lt;td&gt;Didn't exist&lt;/td&gt;
&lt;td&gt;6 months, allows semver-major changes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Total support per version&lt;/td&gt;
&lt;td&gt;36 months&lt;/td&gt;
&lt;td&gt;36 months (unchanged)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The concrete dates that affect you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Node 26&lt;/strong&gt;: Active LTS from &lt;strong&gt;October 28, 2026&lt;/strong&gt;, maintenance from October 20, 2027, end of life on &lt;strong&gt;April 30, 2029&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Node 27&lt;/strong&gt;: alpha channel opens &lt;strong&gt;October 28, 2026&lt;/strong&gt; (the same day 26 goes LTS), release on &lt;strong&gt;April 22, 2027&lt;/strong&gt;, and it will also be LTS.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Node 24&lt;/strong&gt;: Active LTS until &lt;strong&gt;October 20, 2026&lt;/strong&gt;, when it moves to maintenance (critical and security fixes only); end of life on &lt;strong&gt;April 30, 2028&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The exact dates come from the &lt;a href="https://github.com/nodejs/Release/blob/main/schedule.json" rel="noopener noreferrer"&gt;&lt;code&gt;schedule.json&lt;/code&gt; in Node's release repository&lt;/a&gt;, the file the project itself works from; the new schedule was announced on &lt;a href="https://nodejs.org/en/blog/announcements/evolving-the-nodejs-release-schedule" rel="noopener noreferrer"&gt;the Node.js blog&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The practical consequence: &lt;strong&gt;you'll no longer have to memorize which versions "count"&lt;/strong&gt;. Every release from here on is production-grade, and one release per year means fewer migrations per decade for your team. If you maintain libraries, the part that concerns you is the alpha channel: six months to test breaking changes before each release ships.&lt;/p&gt;

&lt;h2&gt;
  
  
  High impact
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. The internal &lt;code&gt;_stream_*&lt;/code&gt; modules are gone
&lt;/h3&gt;

&lt;p&gt;The removal with the most shrapnel, because it doesn't break &lt;em&gt;your&lt;/em&gt; code: it breaks old dependencies. The internal modules &lt;code&gt;_stream_readable&lt;/code&gt;, &lt;code&gt;_stream_writable&lt;/code&gt;, &lt;code&gt;_stream_duplex&lt;/code&gt;, &lt;code&gt;_stream_transform&lt;/code&gt;, &lt;code&gt;_stream_passthrough&lt;/code&gt; and &lt;code&gt;_stream_wrap&lt;/code&gt; — deprecated since Node 12 — have been removed entirely. Any &lt;code&gt;require('_stream_readable')&lt;/code&gt; now throws &lt;code&gt;MODULE_NOT_FOUND&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;No package maintained in recent years does that, but ecosystems drag fossils along. To find out whether you have one:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-rn&lt;/span&gt; &lt;span class="s2"&gt;"_stream_"&lt;/span&gt; node_modules &lt;span class="nt"&gt;--include&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"*.js"&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt; | &lt;span class="nb"&gt;head&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If something shows up, you have two exits: update that package to a version using the public API (&lt;code&gt;node:stream&lt;/code&gt;), or — if it's abandoned — replace it. The &lt;a href="https://www.npmjs.com/package/readable-stream" rel="noopener noreferrer"&gt;&lt;code&gt;readable-stream&lt;/code&gt;&lt;/a&gt; polyfill covers most cases where the package is yours and you just need a mechanical migration.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Undici 8: the global &lt;code&gt;fetch&lt;/code&gt; gets strict
&lt;/h3&gt;

&lt;p&gt;Node 26 ships &lt;strong&gt;Undici 8.0&lt;/strong&gt;, the implementation behind the global &lt;code&gt;fetch&lt;/code&gt;. It brings stricter header validation and changes to redirect handling. If your code builds requests with dynamically generated headers — from a database, from user input, from another API — what used to pass silently can now throw a &lt;code&gt;TypeError&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The pattern that shows up most in practice:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// If any value carries \n or \r (e.g. a token copied with a trailing newline),&lt;/span&gt;
&lt;span class="c1"&gt;// Undici 8 rejects it instead of silently sanitizing.&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;Authorization&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Bearer &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&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;p&gt;The right fix is not catching the error: it's sanitizing the value at the edge, where it enters your system (&lt;code&gt;token.trim()&lt;/code&gt; when you read it). Having the request blow up before it leaves is the good behavior — with Undici 7 that token with a &lt;code&gt;\n&lt;/code&gt; would travel and fail at the server on the other side, which is far harder to debug.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Native addons: mandatory recompilation
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;NODE_MODULE_VERSION&lt;/code&gt; moves to &lt;strong&gt;147&lt;/strong&gt;, so every compiled addon (bcrypt, sharp, better-sqlite3, canvas…) needs to be rebuilt or ship prebuilds for the new ABI. The error is unmistakable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Error: The module was compiled against a different Node.js version using NODE_MODULE_VERSION 137.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;npm rebuild&lt;/code&gt; solves it locally; in CI and Docker, rebuilding the image on the new base is enough. The detail that can actually stall you: the build requirements went up — &lt;strong&gt;GCC 13.2 minimum&lt;/strong&gt;, and &lt;strong&gt;Python 3.9 support was dropped&lt;/strong&gt; from the build toolchain. If you compile addons inside an old Debian/Alpine image, update the image before fighting the wrong error.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Free tool:&lt;/strong&gt; &lt;a href="https://ortamarco.me/en/tools/docker-compose-validator/" rel="noopener noreferrer"&gt;Docker Compose Validator&lt;/a&gt; — Validate your compose.yaml against the official Compose schema and catch key typos, wrong types and YAML errors, with the exact line. All in your browser.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Medium impact
&lt;/h2&gt;

&lt;h3&gt;
  
  
  4. &lt;code&gt;module.register()&lt;/code&gt; is runtime-deprecated
&lt;/h3&gt;

&lt;p&gt;The async module-hooks API — the one used by instrumentation loaders, on-the-fly transpilation and APM agents — now emits a runtime deprecation. The designated replacement is the synchronous &lt;strong&gt;&lt;code&gt;module.registerHooks()&lt;/code&gt;&lt;/strong&gt; API.&lt;/p&gt;

&lt;p&gt;This reaches you mostly through third parties: observability agents (Datadog, New Relic, OpenTelemetry) and tools like &lt;code&gt;tsx&lt;/code&gt; live in that layer. The action item isn't rewriting anything of yours: it's &lt;strong&gt;updating those dependencies to the versions that already migrated&lt;/strong&gt; before moving production to 26.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. &lt;code&gt;writeHeader()&lt;/code&gt; removed from the HTTP server
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;http.Server.prototype.writeHeader()&lt;/code&gt; was an undocumented alias of &lt;code&gt;writeHead()&lt;/code&gt;. It's gone. If it appears in your code, the fix is literal, letter for letter:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Before&lt;/span&gt;
&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writeHeader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="c1"&gt;// Now&lt;/span&gt;
&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writeHead&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A &lt;code&gt;grep -rn "writeHeader" src/&lt;/code&gt; tells you in seconds whether you're affected. Frameworks (Express, Fastify, Nest) have always used the documented API; this bites hand-written HTTP code from many years ago.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. &lt;code&gt;type: "module"&lt;/code&gt; packages: the extensionless-CJS exception is gone
&lt;/h3&gt;

&lt;p&gt;In packages declared as ESM, loading a CommonJS file &lt;strong&gt;without an extension&lt;/strong&gt; relied on a historical resolver exception that Node 26 removes. If a &lt;code&gt;"type": "module"&lt;/code&gt; package of yours had a &lt;code&gt;require&lt;/code&gt; of a path without an explicit &lt;code&gt;.cjs&lt;/code&gt;/&lt;code&gt;.js&lt;/code&gt;, it now fails. The fix is adding the extension — which is what ESM resolution always asked for.&lt;/p&gt;

&lt;h2&gt;
  
  
  Low impact
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;localStorage&lt;/code&gt; with no backing file returns &lt;code&gt;undefined&lt;/code&gt; instead of throwing.&lt;/strong&gt; If you probed availability with try/catch around &lt;code&gt;localStorage&lt;/code&gt;, invert the check: it's a plain &lt;code&gt;if&lt;/code&gt; now.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;QuotaExceededError&lt;/code&gt; becomes a &lt;code&gt;DOMException&lt;/code&gt;-derived interface.&lt;/strong&gt; Only affects code comparing the error by constructor or numeric &lt;code&gt;code&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Several crypto APIs advance in the deprecation cycle&lt;/strong&gt; — one reaches end of life and others start warning at runtime. If you see no warnings with &lt;code&gt;--pending-deprecation&lt;/code&gt; today, you're not affected.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;assert&lt;/code&gt; accepts printf-style messages&lt;/strong&gt; in assertion errors. It's an addition, but it changes text output: if your CI parses assert messages (bad idea, while you're here), review it.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What does NOT break, even if you've read otherwise
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Temporal API doesn't break &lt;code&gt;Date&lt;/code&gt;.&lt;/strong&gt; Temporal arrives &lt;strong&gt;enabled by default&lt;/strong&gt; in Node 26, but it's an addition: all your &lt;code&gt;Date&lt;/code&gt; code keeps working exactly as (badly as) before. Migrating is optional and gradual — I have a &lt;a href="https://ortamarco.me/en/blog/temporal-api-in-node-26/" rel="noopener noreferrer"&gt;full guide to Temporal in Node 26&lt;/a&gt; with the conversion table from date-fns and dayjs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Running TypeScript directly is now stable.&lt;/strong&gt; &lt;code&gt;node app.ts&lt;/code&gt; works with no flags: type stripping left experimental status. The only thing that "breaks" is the &lt;code&gt;--experimental-transform-types&lt;/code&gt; flag, which was removed precisely because the feature graduated — if you had it in an npm script, delete it and move on.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;V8 14.6 only adds.&lt;/strong&gt; &lt;code&gt;Map.prototype.getOrInsert()&lt;/code&gt;, &lt;code&gt;Iterator.concat()&lt;/code&gt; and the rest of the Chromium 146 features are additions with zero migration cost.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The upgrade, in practice
&lt;/h2&gt;

&lt;p&gt;The order that produces the fewest surprises:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Install 26 next to your current version&lt;/strong&gt;, not over it. With &lt;a href="https://ortamarco.me/en/blog/nvm-vs-fnm-vs-volta-node-version-managers/" rel="noopener noreferrer"&gt;fnm or Volta&lt;/a&gt; it's one command, and rolling back takes seconds.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;npm rebuild&lt;/code&gt;&lt;/strong&gt; (or delete &lt;code&gt;node_modules&lt;/code&gt; and reinstall) to regenerate native addons against ABI 147.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Run the suite with deprecations visible:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   node &lt;span class="nt"&gt;--pending-deprecation&lt;/span&gt; &lt;span class="nt"&gt;--trace-deprecation&lt;/span&gt; ./node_modules/.bin/vitest run
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;module.register()&lt;/code&gt; and crypto ones show up here, with a stack trace to the culprit.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;grep&lt;/code&gt; for the fossils:&lt;/strong&gt; &lt;code&gt;_stream_&lt;/code&gt;, &lt;code&gt;writeHeader(&lt;/code&gt;. Two minutes that save you a production surprise.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Update your agents&lt;/strong&gt; (APM, telemetry, loaders) to versions that declare Node 26 support — it's the layer that takes longest.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Review the &lt;code&gt;engines&lt;/code&gt; field&lt;/strong&gt; in your &lt;code&gt;package.json&lt;/code&gt; and your critical dependencies: &lt;code&gt;npm ls --depth=0&lt;/code&gt; plus a glance at &lt;code&gt;EBADENGINE&lt;/code&gt; warnings during install.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  When should you upgrade?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;New project:&lt;/strong&gt; start on 26 today. There's no reason to begin something that will live for years on 24.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Production on 24:&lt;/strong&gt; the sensible window opens on &lt;strong&gt;October 28, 2026&lt;/strong&gt;, when 26 enters LTS and eight days after 24 moves to maintenance — with 24 supported until April 2028 there's no rush, but no reason to wait until the end either: the longer you wait, the further this list drifts from fresh memory.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Production on 22:&lt;/strong&gt; move now. April 2027 looks far away until it doesn't, and the 22 → 26 jump is bigger than 24 → 26.&lt;/p&gt;

&lt;p&gt;Further reading:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://ortamarco.me/en/blog/temporal-api-in-node-26/" rel="noopener noreferrer"&gt;The Temporal API in Node 26&lt;/a&gt; — the big feature of this release, with migration from date-fns and dayjs.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ortamarco.me/en/blog/nvm-vs-fnm-vs-volta-node-version-managers/" rel="noopener noreferrer"&gt;nvm vs fnm vs Volta&lt;/a&gt; — the comfortable way to keep 24 and 26 side by side while you migrate.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ortamarco.me/en/blog/complete-docker-guide-2026/" rel="noopener noreferrer"&gt;The Complete Docker Guide for 2026&lt;/a&gt; — test the upgrade in a container before touching anything.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Frequently asked questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  When does Node 26 enter LTS?
&lt;/h3&gt;

&lt;p&gt;On October 28, 2026, according to schedule.json in the nodejs/Release repository. Node 26 shipped as the Current release on May 5, 2026, moves to maintenance on October 20, 2027, and its end of life is scheduled for April 30, 2029. It has no announced codename yet.&lt;/p&gt;

&lt;h3&gt;
  
  
  What changes in the Node.js release schedule starting with Node 27?
&lt;/h3&gt;

&lt;p&gt;Starting with Node 27 (April 2027) the project moves from two major releases per year to one, and every release will be LTS: the odd/even distinction disappears. A six-month alpha channel is added (the Node 27 alpha opens on October 28, 2026) where breaking changes are tested before release. Total support per version remains 36 months.&lt;/p&gt;

&lt;h3&gt;
  
  
  What breaks when upgrading from Node 24 to Node 26?
&lt;/h3&gt;

&lt;p&gt;Little, and very localized: the removed internal &lt;em&gt;stream&lt;/em&gt;* modules (affects very old dependencies), the removed writeHeader() HTTP server alias, Undici 8 with stricter header validation in fetch, the runtime deprecation of module.register(), and native addon recompilation due to the ABI change to NODE_MODULE_VERSION 147. For most projects it is checklist work, not refactoring work.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can I run TypeScript directly in Node 26?
&lt;/h3&gt;

&lt;p&gt;Yes. Type stripping is stable in Node 26: node app.ts works with no flags. The --experimental-transform-types flag was removed precisely because the feature is no longer experimental; if you had it in a script, just delete it.&lt;/p&gt;

&lt;h3&gt;
  
  
  How long is Node 24 supported?
&lt;/h3&gt;

&lt;p&gt;Node 24 moves from Active LTS to maintenance on October 20, 2026 (critical and security fixes only from then on) and its end of life is scheduled for April 30, 2028, so there is no immediate urgency to leave it. The recommended migration window to 26 opens on October 28, 2026, when 26 enters LTS.&lt;/p&gt;

&lt;h3&gt;
  
  
  Does the Temporal API in Node 26 break code that uses Date?
&lt;/h3&gt;

&lt;p&gt;No. Temporal ships enabled by default but it is a new API that coexists with Date: it does not change or remove any existing behavior. Migrating to Temporal is optional and can be done gradually, file by file.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://ortamarco.me/en/blog/what-breaks-upgrading-to-node-26/" rel="noopener noreferrer"&gt;ortamarco.me&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>node</category>
      <category>javascript</category>
      <category>backend</category>
      <category>migration</category>
    </item>
  </channel>
</rss>
