IKRC Insights

The .NET Upgrade Is Decided. What Ships Alongside It Is Not.

Both support windows close on November 10, 2026, so the framework move stopped being a decision some time ago. Whether one workflow in the business comes out of it measurably better still is one.

Most upgrade conversations start in the wrong place, with a version number. That number is already decided and the date attached to it is not moving. What is still open is whether the upgrade arrives as three months of work that produces no visible change, or whether one workflow in the business comes out of it measurably better than it went in.

That second version costs slightly more and is the only one anybody outside the development team can evaluate afterwards. It also has to be chosen up front, because a workflow picked halfway through an upgrade gets picked from whatever is easiest to reach.

Both Support Windows Close on the Same Day

The support policy is published and specific. .NET 8 shipped in November 2023 as a Long Term Support release and reaches end of support on November 10, 2026. .NET 9 shipped a year later as Standard Term Support and reaches end of support on November 10, 2026 as well. Not the same quarter. The same day.

That coincidence is arithmetic rather than accident. Standard Term Support runs for two years, Long Term Support runs for a minimum of three years or one year after the next LTS release ships if that date is later, and a 2023 LTS plus three years lands on the same Patch Tuesday as a 2024 STS plus two. Both versions currently sit in the Maintenance phase of the published table.

The reason this is worth stating plainly is that it breaks a rule of thumb plenty of teams are relying on right now. A team that deliberately stayed on .NET 8 because it was the long-term option gets no additional room at all this cycle, and a team that jumped to .NET 9 for the newer features loses nothing by comparison. Both are looking at the same date. .NET 10 is the current LTS and is supported until November 14, 2028, so the version after this one buys back two years. The deadline itself and what it means for planning is a subject of its own, and everything below assumes it has been accepted.

Pick the Workflow Before You Start the Upgrade

An upgrade with no business-visible outcome is the hardest kind to fund a second time. The .NET 10 overview leads with JIT inlining, method devirtualization and stack allocation work in the runtime, which is real engineering value and is also precisely the kind of improvement nobody outside the development team notices on a Monday morning. So choose one workflow, name the number that describes it today, and let the upgrade carry it. The candidates are usually already known to whoever answers the phone: the quotes that take two days because somebody retypes them, the invoice exceptions that get worked in a spreadsheet, the report that four people rebuild every Monday morning.

The measurement has to exist before the upgrade starts, and it has to be crude enough to survive. How many exceptions last month. How long from request to first response. How many of the report rebuilds were needed. A count taken from the database this week is worth more than an instrumented dashboard delivered in month three, because the point is to have a before.

Then keep the AI work off the critical path. Adding a model call to an application you already run is a dependency-injection change and can land in its own release after the framework move is finished and stable. Sequencing them the other way round is how an upgrade acquires an open-ended research task in the middle of it, and how the November date arrives with the framework still half-moved.

Two sequencing details make this easier than it sounds. The documentation is explicit that you should upgrade to the latest SDK version even if it is an STS release, because it can target all available runtimes. The build toolchain and the target framework are two separate moves, and doing the first early is cheap.

The second detail is one that surprises people who expect the runtime to handle it. An app built to target .NET 8 does not automatically roll forward to .NET 9 or .NET 10, because that is a major version change. The patch version does roll forward automatically, so an app targeting .NET 9 picks up 9.0.3 as soon as it is installed. Putting the new runtime on the servers therefore moves nothing on its own. Somebody has to change the target framework, rebuild and retest before the runtime sitting on the server matters at all.

The Build Warning That Does Not Warn About Your App

There is a build-time signal a team can switch on today, and it is worth understanding precisely, because the obvious reading of it is wrong. Setting the MSBuild property CheckSdkVulnerabilities to true makes the build emit warning NETSDK1239 when the resolved .NET SDK is end of life. The message is blunt about what it means: the current .NET SDK is end of life as of a given date, and it will receive no further security updates.

Now the part that catches people. NETSDK1239 fires when the SDK that runs the build is out of support, regardless of which framework you target. The documentation draws the line itself and gives the other warning a number: NETSDK1138 is the one raised when your project target framework is out of support. So a team that turns on CheckSdkVulnerabilities in October expecting to be told about its .NET 8 applications hears nothing at all, because those applications are being built by a current .NET 10 SDK. The check is watching the toolchain, not the thing being shipped.

Both are worth having, for different questions. NETSDK1239 answers whether your build agents are running something Microsoft has stopped patching, which on a build server nobody has logged into for a year is a real question. NETSDK1138 answers whether the application itself has aged out. Wire up the second one if the deadline is what you are tracking.

Two operational details, since somebody has to make this work in a pipeline. The check is opt-in and off by default, so it has to be set in the project or passed as a property on the command line, and setting CheckSdkVulnerabilities to false switches off NETSDK1238, NETSDK1239 and NETSDK1240 together. And the build does not phone home: the CLI refreshes a local cache of SDK release metadata in the background at most once a day, and the MSBuild check reads only that cache. On a fresh build container with no cache and no prior CLI activity, expect the signal to be quiet until that refresh has happened at least once, which is exactly the environment where a stale SDK is most likely to be sitting.

Worth one grep before anybody reports that the build is clean: NETSDK1239 can be added to NoWarn like any other diagnostic, and the property that enables the check can simply be left at its default. A repository that has been quietened stays quiet, and the difference between a build with nothing to say and a build that has been told to say nothing is not visible in the output.

Where IKRC Fits

The framework move is the part with a known shape. Choosing which workflow rides along with it, agreeing the number that says whether it worked, and keeping the two releases separate enough that one cannot delay the other is the part that decides whether anybody funds the next one. IKRC does that work through legacy application modernization on .NET systems that are carrying a live business while they move.

A first conversation is short with three things in hand: which framework version the application targets today, one workflow somebody complains about by name, and whatever number already exists for it, even if that number is somebody counting rows once a month. Call IKRC at 646-783-1441 or email info@ikrc.co.

Related Reading

For the deadline in full and what it means for planning, read .NET 8 and 9 End of Support. For the workflow change that rides alongside the upgrade, read Adding AI to the .NET Application You Already Run.

Need this solved in your software?

IKRC builds the custom systems, integrations, and modernization work discussed in this article.

Ready to Build?

Let's engineer your solution.

Every project starts with a conversation. Tell us what you're trying to solve.