The Modern .NET Show
For AI agents: a curated index of this site is available at /llms.txt, with the full version at /llms-full.txt. A clean markdown version of any page is available by appending index.md to its URL path.

S09E03 50mSeason 9

Artwork for S09E03 - You Might Not Need to Upgrade: The Case for Staying Put
Audio player for S09E03 - You Might Not Need to Upgrade: The Case for Staying Put. Nothing is downloaded until you press play.
S09E03
0:0050m

You Might Not Need to Upgrade: The Case for Staying Put

.NET Framework 4.8 has no end-of-support date of its own. It follows the Windows it runs on. Part two of Bridging the Gap does the support arithmetic.

Episode Summary

Part 2 of 6 of Bridging the Gap, a series for .NET developers who are still working on .NET Framework. Start with Part 1: You Are Not Behind.

In February 2020, one of the busiest websites in the world came on this show to discuss .NET Framework. It had looked at a newer version and decided not to move to it. The reasons were specific, commercial, and entirely reasonable.

The short answer to the question most people arrive with: .NET Framework 4.8 has no end-of-support date of its own. It follows the lifecycle of the Windows version it runs on, which for some versions of Windows means the 2030s and for others means January 2027.

This episode is about that kind of decision. Not the failure to upgrade, which everybody has an opinion about, but the deliberate choice not to, which almost nobody discusses.

The problem is that our industry has no vocabulary for it. Every available phrase concedes the point. “We have not got round to it.” “It is on the roadmap.” “It is on the backlog.” All of them mean the same thing to the person listening, which is that you should have, and you did not. So a team that made the right call sounds exactly like a team that made no call at all.

Topics covered in this episode include:

  • Why Stack Overflow declined .NET Framework 4.8, in their architecture lead’s own words
  • What happened to Stack Overflow afterwards, and why the answer takes five years
  • Iris Classon telling listeners to ask “why” first, weeks before her own migration book shipped
  • The second edition of that book, and what a publisher commissioning it in 2024 tells you
  • Chris Woodruff’s test, transposed: every upgrade must pay for itself
  • The support arithmetic, including why .NET 11 expires before .NET 10 does
  • Umbraco 8 outliving the version built to replace it, by more than two years
  • The half-step that carries a deadline when standing still does not
  • Taking It Upstairs: how to write a decision down so that it protects you later

A note on dates. Every support date and lifecycle window in this episode was correct at the time of writing, which was 2 September 2026. Microsoft’s published lifecycle pages are the authority, and they are linked at the end of these notes. Check them before you act on anything here.

The Dates in This Episode

Every date below carries the caveat above. Microsoft’s lifecycle pages are the authority and are linked at the end of these notes.

  • .NET Framework 4.8 has no end-of-support date of its own. It follows the lifecycle of the Windows version it runs on. On Windows Server 2022 that reaches 14 October 2031 under extended support. On Windows Server 2016 and Windows 10 Enterprise LTSC 2021 it ends on 12 January 2027.
  • .NET Framework 4.6.2 reaches end of support on 12 January 2027.
  • ASP.NET Core 2.3 reaches end of support on 13 April 2027. This applies only to ASP.NET Core running on .NET Framework. Applications built on System.Web, which means Web Forms, MVC 5 and Web API 2, are not affected and have no date of their own.
  • .NET 8 and .NET 9 both reach end of support on 10 November 2026.
  • .NET 10 reaches end of support on 14 November 2028. .NET 11 reaches end of support on 9 November 2028. The Standard Term Support release therefore ends five days before the Long Term Support release it follows.

Taking It Upstairs

A written version of this episode’s segment, in business language. Forward it to whoever needs to read it.

Some organisations should not modernise a working application this year. The problem is that almost none of them have said so on paper, which leaves the decision looking like neglect rather than judgement.

The request is not permission to stay on the current platform. It is permission to record the decision. That distinction matters, because it is a much smaller thing to approve. It needs one meeting and no budget.

A recorded decision has six parts. The platform version currently in use. The options the team considered. The cost of moving, in people and in weeks. The benefit of moving, stated honestly, including where there is none. The support end dates for every option, including the current one. And the conditions that would change the answer.

It must carry a review date. Twelve or eighteen months. A decision with no review date is not a decision. It is a delay in formal clothing, and it will be read that way later.

It must be signed by the budget holder, not the engineer. A decision an engineer records alone remains that engineer’s decision. A decision the business signs becomes the organisation’s decision. Those two documents behave very differently when somebody asks questions eighteen months from now.

The honest caveat. This exercise can return the opposite answer. If the arithmetic says the work should happen, the resulting document says so, in writing, with a signature on it. Anybody who only wants the exercise when it agrees with them is not running a decision process. They are looking for permission.

Every episode in this series adds one of these. They are collected on a single page at dotnetcore.show/bridging-the-gap, which is the one link to send if you would rather not forward a podcast episode.

Episode Transcription

Hey everyone, and welcome back to The Modern .NET Show; the premier .NET podcast, focusing entirely on the knowledge, tools, and frameworks that all .NET developers should have in their toolbox. I’m your host Jamie Taylor, bringing you conversations with the brightest minds in the .NET ecosystem.

Today’s episode is another one without a guest; it’s just me again. This is part two of the series I started at the top of the season, Bridging the Gap, and if you’re wondering where part two got to, we had a standard episode in between. That’ll keep happening. The series numbers and the season numbers are going to drift apart, and I’ll say which part we’re on every time so nobody has to do arithmetic.

So let’s sit back, open up a terminal, type in dotnet new podcast and we’ll dive into the core of Modern .NET.

The Decision Nobody Talks About

I want to start in February 2020, with a conversation I had on this show.

The guest was Nick Craver, who was then the architecture lead at Stack Exchange. The subject was migrating Stack Overflow to .NET Core, which is a genuinely interesting story and one I’d recommend going back for. But that isn’t the bit I want today.

The bit I want is almost an aside. Because somewhere in the middle of that conversation, Nick mentioned, entirely in passing, that Stack Overflow was running on .NET Framework 4.6.2. And that .NET Framework 4.8 had been out since April 2019. And that they had looked at it, and decided not to move.

Here’s how he put it.

But the reason we don’t upgrade to like .NET [Framework] 4.8 or above is because an enterprise customer is able to run it on their own infrastructure, right. Their own servers. Now right now, that’s a Windows install with .NET on it, and it’s a little bit of hand holding to upgrade. So you don’t want to incur that unless you need to. And there’s no real big wins in newer versions of .NET. So we don’t require it. We can upgrade to and run on it, but we don’t target it.

Listen to what’s in there, because there are four separate reasons and not one of them is “we couldn’t be bothered”.

Enterprise customers self-host the product, on their own servers. Upgrading those installations takes hand-holding, which is a cost that lands on Stack Overflow’s own support people. There are no real wins in the newer version for their use case. And so they don’t require it.

And then that last clause, which is the one I’ve been thinking about for six years.

“We can upgrade to and run on it, but we don’t target it.”

That is not a refusal. It’s a held option. They’d done the work to make sure the door still opened, and then they’d chosen not to walk through it, because walking through it would have cost their customers something and returned them nothing.

I should say the obvious thing straight away, because a good chunk of you already know it and I’d rather say it than have you wait for me to. Stack Overflow were, at that exact moment, in the middle of porting to .NET Core. Nick explains why in the same breath: appliances, containers, deployment options. So this was never “Stack Overflow refuses to modernise”. It was a company declining a specific intermediate upgrade that would have paid them nothing, while spending the same money on a move that would.

Hold on to that, because I’m going to tell you what happened next later in the episode, and it isn’t what you’d guess.

Part Two, and the Thing You Should Know

This is part two of six of Bridging the Gap.

If you missed part one, the premise in two sentences: if you’re working on .NET Framework in 2026, that’s a constraint rather than a character flaw, and the two feel identical from the inside. And the destination for this series is not the latest .NET. It’s a version you can stand still on.

This episode is the one I deliberately put second rather than last. Because a series like this could easily spend five episodes explaining modern .NET and then, right at the end, add a polite little caveat that says “of course, some of you shouldn’t bother”. That caveat, arriving last, is worth nothing. Everybody’s already gone.

So it goes here, at the front, where it costs me something.

And before I make a single argument, the same disclosure I’ll be making every episode. It matters more in this one than in any of the others.

This show is produced by RJJ Software. RJJ Software is also who I work for, and some of what RJJ does is application modernisation.

So I have a commercial interest in you deciding to modernise. And this is the episode where I’m going to spend forty-odd minutes arguing that a good number of you shouldn’t. You should know that before I start, and you should weigh everything after it accordingly.

No pitch, no offer, no URL. There won’t be one in any episode of this series.

Upgrading Is Not Tidying Up

Right. Let’s start with something that I think gets assumed and almost never gets said.

Nobody has ever asked you why you haven’t rewritten your billing engine.

Think about that. You’ve almost certainly got a component somewhere in your estate that everybody agrees is unpleasant. Nobody demands you rewrite it, because everybody understands that rewriting it would be a piece of work, and work has a cost, and somebody would have to decide that the cost was worth paying.

But “why are you still on .NET Framework” gets asked constantly, at conferences and in interviews and in code review comments from people who don’t work at your company. And it gets asked in a completely different register. Not “have you costed that piece of work”, but “why haven’t you tidied up”.

That’s the swap I want to name. Somewhere along the way, upgrading a runtime stopped being understood as work and started being understood as hygiene. Something you either did, or failed to do. And hygiene doesn’t need a business case; it just needs you to be less lazy.

So let’s put the cost back.

Upgrading a large line-of-business application off .NET Framework means developer months, not developer days. It means a full regression pass on a system where a decent portion of the behaviour lives in nobody’s head and only in the code. It means a risk window on something that is currently making the company money every single day. And in a regulated environment, it means all the paperwork wrapped around the code: the re-certification, the re-accreditation, the penetration test, the vendor support matrix that has to be checked again. I went through the four things your manager is actually afraid of in part one, and I’m not going to do it all again, but every one of those four is a cost that doesn’t appear in an engineering estimate.

And if you want a number rather than my adjectives, here’s one from Nick again, talking to InfoQ in April 2020 as the Stack Overflow port was finishing.

That migration took, in his words, about one and a half to two developer-years.

One and a half to two developer-years. For a team that knew exactly what they were doing, on a codebase they’d written themselves, with the person who understood the dependency graph still in the building. Whatever your number is, it is not smaller than that because you are cleverer. It’s a different number because your application is a different size.

That’s not an argument against upgrading. It’s an argument for treating it like the capital decision it actually is.

There Is No Word for a Considered No

Here’s the part I’ve become slightly obsessed with while making this episode.

Try to say, out loud, that your organisation has deliberately decided not to move off .NET Framework this year. Try to phrase it in a way that doesn’t sound like an excuse.

I’ll wait.

Because I’ve tried, and I can’t do it, and I don’t think the words exist. Watch what happens with the ones we’ve got.

“We haven’t got round to it yet.” That’s an admission of failure with a “yet” bolted on for comfort.

“It’s on the roadmap.” Which means it isn’t happening, and everyone in the room knows it isn’t happening, and now you’ve also promised something.

“It’s in the backlog.” That’s where work goes to be quietly buried, and the person you’re talking to knows that too.

“We’re planning to, eventually.” That one’s just the first one wearing a suit.

Every single one of those concedes the premise. Every one of them agrees that you should have done it, and offers a reason you haven’t. There is no ordinary, comfortable, professional phrase in our industry that means: we looked at this properly, we decided against it on the following grounds, and we’ll look again in eighteen months.

And because the phrase doesn’t exist, something quite unfair happens. The team that ran the numbers and made a considered decision sounds precisely the same as the team that never thought about it once. From the outside there is no way to tell them apart. And here’s the genuinely damaging bit: after a few years, from the inside there’s no way to tell either. You stop being able to remember whether this was a decision or a drift.

There’s a second thing tangled up in here, and Chris Woodruff put his finger on it when he was on the show back in May.

You know what? I think developers and engineers love the idea of building a Ferrari, but if you actually ask them the truth, they would probably say that they would love to maintain something like a Volkswagen solution.

I laughed when he said it and I’ve thought about it a lot since, because it cuts both ways in this conversation.

Some of the pressure you feel to modernise is genuinely about the health of the system. And some of it, if we’re all being honest in a room with the door shut, is about wanting to work on the interesting thing. That’s not a character defect. I’ve felt it. I suspect you have. But it does mean that “we should upgrade” and “I would enjoy upgrading” can be very hard to tell apart from the inside, and only one of them is an argument you can take to a budget holder.

The Question, Then

So that’s where we are. Upgrading gets treated as tidiness rather than as work. We have no language for deciding against it. And our own enthusiasm is quietly on the scales.

Which gives me two questions, and I’m going to answer them straight away rather than making you wait until the end.

Is there such a thing as a correct decision not to upgrade?

And if you’ve made one, how would you know? How would anybody else?

Every Upgrade Must Pay for Itself

Yes. There absolutely is. And the test for it isn’t exotic, it’s the same test you’d apply to any other piece of engineering work you were asked to justify.

Chris again, at the end of that same episode, summing up his whole argument in six words.

Every abstraction must pay for itself. Enough said. That is the challenge that this simplicity-first approach boils down to. It’s not rocket science. Rocket science is very complex. Most of our solutions that we build are not rockets, so we don’t have to worry about that. Ours is just common sense.

Every abstraction must pay for itself.

Change one word and you get: Every upgrade must pay for itself.

That’s it. That’s the whole test, and it’s not a low bar or a get-out. An upgrade that delivers a genuine reduction in risk pays for itself. An upgrade that unblocks three pieces of work you actually want to do pays for itself. An upgrade that lets you hire, or lets you stop paying for something, pays for itself. Plenty of upgrades pass this test comfortably, and if yours does, this episode isn’t telling you not to do it.

But an upgrade whose entire justification is that a newer number exists has not paid for anything. It has spent something.

And here’s the other half of Chris’s argument, which I think is the part people skip.

It’s remembering that complexity is not sophistication, that everything you do has a complexity budget. You only have so much complexity budget to use, and if you use that all up in the beginning, you’ve got nothing left down the road.

He’s talking about architectural complexity there, but the shape of it transfers exactly.

Your organisation can absorb a finite amount of change in a year. Not a theoretical amount; a real one, bounded by how many people you have, how much testing capacity exists, how much appetite for risk the business has left after whatever went wrong in March. That’s a budget. And a migration that produces nothing anybody outside engineering can see is still a withdrawal from it.

So the question isn’t only “would this be good”. It’s “is this the best available use of the one big change we get to make this year”. Sometimes the answer is obviously yes. And sometimes the honest answer is that the warehouse integration would do more for the business, and the runtime can wait, and there is nothing shameful about that at all.

The Author with a Book to Sell

I want to give you the best piece of evidence I have, and it’s seven and a half years old.

April 2019. Episode 24 of this show. My guest was Iris Classon, who was then a Microsoft MVP and a newly elected member of the .NET Foundation board, elected that March. And she is, I’m pleased to say, still an MVP today.

Now. The reason she was on the show was her book. It was called Migrating ASP.NET Microservices to ASP.NET Core: By Example, it was being published by Apress, and when we recorded, it was finished. Technically reviewed, formatted, aimed at Build in early May. Weeks away.

And later in that same episode, I asked her what listeners should actually do about migrating. Her answer, and I promise I’m not editing this for effect, was “buy my book, and then do it.”

That’s episode twenty-four, and it’s linked in the show notes. Do go and listen to the whole thing if you get a chance, because I’m about to compress twenty minutes of Iris into roughly two, and she comes out of the full version rather better than she comes out of my edit of it.

So we’re completely clear about the incentives here. This is a person with a finished book about migrating to ASP.NET Core, about to go on sale, sitting on a .NET podcast, being asked how to migrate to ASP.NET Core.

And here’s what she said.

My first question would be: why? You’ve really got to make sure you know why you want to do this. Because ASP.NET is not gonna vanish overnight, it’s gonna be around for a long time. So it’s not like you’re sitting, you know, on a ship that already sailed, if you’re still maintaining an ASP.NET solution.

“You’re not sitting on a ship that already sailed.”

She then went further, and started listing reasons you might think you need to migrate, and explaining why several of them don’t hold.

And also in terms of performance, you might want to do some benchmarking first. And maybe the performance is not so much in the Web service itself. Maybe the performance is more in code and you can still benefit and leverage that without having to migrate to another ASP.NET framework.

Benchmark before you migrate for performance, because the performance problem is very often not where you think it is. Then dependency injection: you can have that already, and teams had been doing it for years before ASP.NET Core arrived. Then modularity, same answer.

And then this one, which I think about every time somebody tells me their line-of-business application needs to be cross-platform.

I know cross platform sounds really good, but not everybody’s going to actually use that, and you don’t want to do it just in case. So you want to make sure that you actually have a plan.

Don’t do it just in case.

Now I want to be careful about how I frame this, because there’s a cheap version of this story and I don’t want to tell it. The cheap version is “look, even the person selling the book admits it”. That’s a gotcha, and it’s unfair, and it inverts what actually happened.

What actually happened is that somebody with every commercial reason to tell a room full of .NET developers to migrate, told them to ask why first. That is the entire guest-selection test for this series, and I’ll say it plainly: the question isn’t whether somebody has something to sell. Almost everybody does. The question is whether they’ll tell you when not to buy it.

Iris passed that test live, on air, a fortnight before her book shipped. That’s not a slip. That’s a person being straight with an audience at her own expense.

And here’s my own contribution to that conversation, which I’d genuinely forgotten I’d said until I went back through the tape for this episode.

Like you said earlier on, it’s not like ASP.NET Framework is going anywhere. So looking at swapping out your entire system for something that’s brand new and shiny, when the thing that is brand new and shiny is literally brand new, may not be the best idea.

That’s April 2019. That is this episode, in three sentences, seven and a half years early. I said it, agreed with myself, and then made about a hundred and seventy episodes without ever building anything on it. I flagged that pattern in part one and it turns out I wasn’t finished finding examples.

But the reason I’ve spent this long on Iris isn’t the quotes. It’s what happened afterwards.

On the twenty-seventh of November 2024, Apress published a second edition. It’s called Migrating ASP.NET Microservices to ASP.NET Core 8. Same author, same publisher, two hundred and twelve pages. And the publisher’s own description of it says it will guide you “through the journey of migrating an ASP.NET Framework application to ASP.NET Core microservices”.

Sit with that for a second.

Five and a half years after she told this show that ASP.NET wasn’t going to vanish overnight, a commercial publisher looked at the market and decided to pay her to write the migration book again. Updated for .NET 8.

Nobody commissions a second edition of a book about migrating off a platform that nobody’s still on. That book exists because there is a large, real, ongoing population of .NET Framework applications in 2024, with people maintaining them, who want help. The market scored her prediction, and it scored it from inside exactly the same commercial interest that made her worth listening to in the first place.

The author with a book to sell told you not to buy it. And then the demand proved her right by wanting the book a second time.

A Note on Dates

Right. I’m about to start throwing dates at you, so one caveat first, and I’d like you to hold on to it for the next ten minutes or so.

Everything I’m about to say about support windows was correct at the time of writing, which was the second of September 2026. Support policies move, and they’ve moved twice in the last two years. Microsoft’s lifecycle pages are the authority here, not me, and they’re linked in the show notes. If you’re listening to this a year from now, please go and check them rather than trusting a recording.

The Arithmetic

Iris said ASP.NET wasn’t going anywhere. That’s a nice story. It’s also checkable, so let’s check it.

First: is .NET Framework actually supported?

Yes, and not grudgingly. Here’s Microsoft’s own policy wording, which is worth hearing in full because people paraphrase it badly. It’s on their .NET Framework support policy page, which is linked in the show notes along with everything else I’m about to cite.

Beginning with version 4.5.2 and later, .NET Framework is defined as a component of the Windows operating system (OS). Components receive the same support as their parent products, therefore, .NET Framework 4.5.2 and later follows the lifecycle policy of the underlying Windows OS on which it is installed.

So 4.8 doesn’t have an end-of-support date. It isn’t that the date is far away. There is no date. It’s a component of Windows, and it lives as long as the Windows it’s installed on.

Let me be precise about one thing there, because precision matters to this audience and I’ll come back to it later. That’s the runtime. .NET Framework 4.8, the thing your code runs on, has no announced end date. Things that run on top of it can have their own dates, and one of them does. I’ll get to that.

And it’s not sitting there rotting, either. .NET Framework gets a security rollup essentially every month. The most recent one when I wrote this was the eleventh of August 2026, and it carried six CVEs including a remote code execution flaw. That’s a patched, serviced, actively maintained platform.

One honest footnote, because some of you will be doing this. If you’re relying on “4.8 still gets patches on Windows 10” then check which channel those are coming through, because for Windows 10 that’s the Extended Security Update programme now, and that’s a paid arrangement rather than a free one.

Second: how long, actually?

This is where it gets counter-intuitive, and I need to name a specific Windows version rather than hand-waving, because the answer changes completely depending on which one you’re on.

Take Windows Server 2022, which ships and services .NET Framework 4.8. Its extended support runs until the fourteenth of October 2031. And I do mean extended support specifically, rather than mainstream. Mainstream support for Server 2022 actually runs out on the fourteenth of October 2026, which is about five days after this episode goes out. So that gap between mainstream and extended is not a technicality I’m glossing over; it’s the whole basis of the number, and you should know I’m leaning on it.

Now compare that to .NET 10. .NET 10 is the current Long Term Support release. It came out in November 2025, it gets thirty-six months, and it reaches end of support on the fourteenth of November 2028.

So: 2031 against 2028. The .NET Framework application on Windows Server 2022 has roughly three years more support than the modern .NET application somebody built to replace it.

I want to be scrupulous here, because this cuts both ways and if I only gave you the flattering half I’d be doing exactly what this series says it won’t. On Windows Server 2016, or Windows 10 Enterprise LTSC 2021, that support ends on the twelfth of January 2027. Which is before .NET 10, not after it. The operating system lifecycle giveth and it taketh away, and the answer genuinely depends on which Windows you’re running. So go and look, rather than taking the good news.

Third, and this is the one I’d like you to tell somebody about.

Modern .NET has two kinds of release. Long Term Support gets thirty-six months. Standard Term Support gets twenty-four. That second number used to be eighteen, and it changed in 2025, first applying to .NET 9. Why it changed is a story I’ll come back to later in this series, because it’s a better story than it sounds.

Now. Here’s Microsoft on .NET 11, in the release notes on their own GitHub repository. This is the single most surprising thing in this episode, so I’d rather you heard it in their words than in mine.

.NET 11 is a Standard Term Support (STS) release and will be supported for two years, from November 10, 2026 to November 9, 2028, on multiple operating systems.

The ninth of November 2028.

And .NET 10 is Long Term Support, which ends on the fourteenth of November 2028.

Read those two dates again.

.NET 11 reaches end of support five days before .NET 10 does.

The new version dies first. If you upgrade to .NET 11 in the week it ships, chasing the newest thing, you will have bought yourself five days less remaining support than if you had stayed exactly where you were and done nothing at all.

That is not a criticism of anybody. It’s just what happens when you have two release trains of different lengths leaving the station a year apart. But it’s the cleanest possible demonstration of the thing this series keeps saying: newest and best-supported are different properties, and chasing the first doesn’t necessarily get you the second.

It also, finally, lets me put a name and a date on the destination I’ve been describing vaguely since part one. The version you can stand still on, right now, is .NET 10. Until November 2028.

Fourth, briefly, because it deserves its own episode.

.NET 8 and .NET 9 both reach end of support on the tenth of November 2026. That’s about a month after this episode goes out. And it’s expected to be the same day .NET 11 ships.

Two of the three supported versions of modern .NET expire simultaneously, next month. I’m going to come back to that properly, because there’s a lot in it and it deserves more than a paragraph.

Fifth, and this one already happened rather than being arithmetic about the future.

Umbraco. Which is a .NET content management system a lot of you will have worked with, and which I covered on this show back in 2020 when they were planning their move.

Umbraco 8 ran on .NET Framework. It came out in February 2019 and reached end of life in February 2025. Six years.

Umbraco 9 ran on .NET 5. It came out in September 2021 and reached end of life in December 2022. Under fifteen months.

A developer who stayed on Umbraco 8 and did absolutely nothing was still supported for three years and two months after the version built to replace it was already dead.

Two things I want to be really clear about, because it would be very easy to tell that story unfairly.

This is not Umbraco doing something to their users. Umbraco 9 was short-lived because it was pinned to .NET 5, which was a Standard Term Support release, and .NET 5 itself went out of support in May 2022, seven months before Umbraco 9 did. Umbraco then made Umbraco 10 the first major version to follow the Long Term Support policy, and every LTS release since has had a proper three years. So the lesson here is about release-cadence coupling, and about what happens when your product’s lifetime is chained to a short runtime window. It is not a lesson about Umbraco, who spotted the problem and fixed it.

And the fair comparison isn’t really 8 against 9, because 9 was the anomaly. The fair comparison is Umbraco 8’s six years against Umbraco 13 or 17 at three years each. .NET Framework still wins that comparison. It just wins it by a normal amount rather than a shocking one, and the honest version is more persuasive anyway.

So let me put all of that together, because it’s the thing I’d want you to take out of this section.

“Upgrade to stay supported” is not one thing. It’s at least three, and they point in different directions.

A .NET Framework 4.8 application on a current Windows Server has support into the 2030s. A .NET 8 application, which somebody built or migrated specifically so it would be modern, has about a month. Umbraco 8, which nobody would call modern, outlived the thing built to replace it by more than two years.

The engineer who stayed has years. The engineer who moved in 2023 has until November.

What Happened to Stack Overflow

I promised you the end of the Stack Overflow story, and it’s better than the setup.

In February 2020, Nick told this show that they weren’t targeting .NET Framework 4.8.

By the sixth of May 2020, roughly eleven weeks later, the main Stack Overflow application was running on .NET Core 3.1. One of their engineers announced it publicly at the end of that month, and updated the canonical page describing their technology stack to say so, changing the entry that had previously read “.NET Framework 4.6.2”.

Eleven weeks. And then .NET 5 by September 2021. .NET 6 in May 2022. And as of September 2025, their own staff list the stack as .NET 8 on ASP.NET Core 8, running on Google Kubernetes Engine. There’s no public record of them ever running .NET 7 at all, and nothing public about 9 or 10, so I’ll stop there rather than guess.

Now, you could hear that and think it undermines everything I said at the top of this episode. It doesn’t. It’s the opposite. They declined the intermediate upgrade because the money was already committed to a better move, which is exactly what “we can upgrade to and run on it, but we don’t target it” meant. Eleven weeks later the question was moot. The decision was right on the day it was made and it had its own reversal built into it.

But here’s the part I actually want you to take away, and it took me by surprise.

“Ported the main application” and “off .NET Framework” turn out to be two very different claims, and there are five years between them.

Stack Overflow’s chat application didn’t leave .NET Framework until October 2024. Their Data Explorer didn’t leave until May 2025. And in August 2025, on their own engineering blog, they said this:

For Teams, all apps are on a modern version of .NET but for the public platform we have some older applications on the full .NET framework and we didn’t have the time to upgrade those to a modern version of .NET before the deadline.

Five and a half years after finishing the port. At a company with world-class engineers, enormous public visibility, every commercial motivation you could ask for, and a deadline they’d set themselves.

And the reason given is time. Not incompetence, not ignorance, not a failure of will. They didn’t have the time.

So if Stack Overflow needed more than five years to finish a tail like that, then the tail in your organisation is not evidence of anything being wrong with you.

One small note on sourcing, because I want to be straight about it: that .NET 8 figure comes from a page anyone can edit. I’m citing it because every single version change in that page’s history was made by a Stack Overflow employee, including the 4.6.2 entry that Nick himself set back in 2018.

Supported Is Not the Same as Alive

Now. If I stopped here, I’d have made a dishonest episode, and I’d have made the next four pointless. So let’s do the other side properly.

Everything I’ve told you is about the runtime. The runtime is supported. The runtime is patched. That is genuinely true and it is genuinely reassuring.

It also isn’t the whole system.

Because the thing that actually kills a platform isn’t the vendor withdrawing support. It’s the ecosystem around it quietly ceasing to care. Packages you depend on stop shipping a Framework-compatible target, not out of malice, but because the maintainer looked at their download numbers and made a reasonable decision. Documentation starts assuming things your project system doesn’t have. Sample code stops compiling for you. The answer to your Stack Overflow question is three years old and the newer one doesn’t apply. None of that shows up on a lifecycle page, and all of it is real.

I’m going to get properly into the dependency side of this in a later part, because there are some specific stories worth telling and they need room.

But there’s one example I want to give you now, and it’s first-party, which makes it much harder to wave away.

And I need to be careful here, because I’m about to say a date, and I really don’t want the wrong people to hear it. So let me start with who this does not apply to.

If your web application is Web Forms; or MVC 5; or Web API 2; if it’s built on System.Web, which is the overwhelming majority of the .NET Framework web applications I hear about, then none of what I’m about to say applies to you. This adds no deadline to your life. System.Web is part of the operating system story I described earlier, so your dates are the Windows dates, whatever they turn out to be. Nothing in the next ninety seconds changes them. Genuinely, if you want to stop paying attention for ninety seconds, that’s fine, and I’ll tell you when it’s over.

Right. Everybody else.

Some of you, a few years ago, did what looked like the sensible thing. You didn’t do the full migration, because you couldn’t, but you moved your web layer to ASP.NET Core while staying on .NET Framework. That was a supported, sanctioned, sensible half-step, and a lot of people took it in good faith.

If you’re running ASP.NET Core 2.x on .NET Framework, that support ends on the thirteenth of April 2027. The package is called ASP.NET Core 2.3, and .NET Framework is the only place it runs.

For context, 2.3 is essentially ASP.NET Core 2.1 shipped again under a higher version number, so that people stranded on the out-of-support 2.2 could perform what was really a downgrade as an upgrade. And it’s classified as a tool rather than as part of the operating system, which is why it can reach an end date while .NET Framework itself carries on without one.

Ninety seconds are up. Web Forms and MVC 5 people, welcome back. You’re still on the Windows clock, and nothing just changed for you.

Now look at what that actually means, because it’s remarkable.

The developer who stayed entirely on System.Web and did nothing has no end-of-support date of their own. Their clock is whatever Windows gives them, which, as we established a few minutes ago, might be 2031 and might be this coming January, depending entirely on which Windows it is.

The developer who took Microsoft’s own advice, moved to ASP.NET Core, and stayed on .NET Framework because that was as far as they could get, has until April 2027 regardless of what they’re running it on. That’s about six months from the day this episode goes out.

The half-step is the thing with the clock on it.

Moving part of the way left them worse off, in pure support terms, than not moving at all. Which is this episode’s argument arriving from a direction I genuinely did not expect when I started writing it.

And that gives us two hard deadlines inside the next six months, hitting completely different people.

If you’re on .NET Framework 4.6.2, you have until the twelfth of January 2027, which is the nearer of the two and about three months from now. This episode is not for you. Go and move to 4.8, which is an in-place framework upgrade rather than a rewrite, and is one of the very few genuinely small moves available in this whole space.

If you’re on ASP.NET Core 2.x on .NET Framework, you have until the thirteenth of April 2027, and you have some thinking to do.

And if you’re on 4.8 with System.Web, on a version of Windows that’s still in support, you have neither of those deadlines, and the decision in front of you is a real decision rather than a countdown. Go and check the Windows, though. I’d rather you checked and found good news than assumed it.

That’s the distinction this whole episode is built on. Three groups, three genuinely different situations, and only one of them gets to make a considered choice. Which is why it matters so much to know which one you’re in before anybody starts giving you advice.

And there’s a general version of this, which I think is the actual risk in all of it. Chris Woodruff gave me the language for it, in the form of the filter he says is the most important of the three he uses.

If you could have someone wake up at 2 a.m. when your solution or your system goes down, and they can… fix that solution quickly and not have to call 20 different people across 20 different teams to get a solution, that’s what you strive for… Could a tired, stressed engineer debug this problem at 2 a.m. without help?

The two in the morning test. Not is this elegant, not is this current, but: can a tired and slightly frightened person fix this at two in the morning without having to wake up five other people.

Now apply that to migration states rather than to architecture.

An application sitting entirely on .NET Framework passes that test comfortably, because the people who support it have been supporting it for years and they know where everything is. An application sitting entirely on .NET 10 passes it too, once the team is up to speed.

The state that fails is the one in the middle. Two stacks in production. Two sets of conventions. Two places the configuration might be hiding, and a fifty-fifty chance you look in the wrong one first at two in the morning. Half the team fluent in one side and half in the other, and the person who actually understood how the two talk to each other left the company in March.

That’s the dangerous state. Not staying, and not moving. Stopping in the middle and living there.

Which, if you were listening to part one, might sound familiar. When I listed the four things your manager is genuinely worried about, the third one was the fear of a half-finished migration. I framed it then as their anxiety, something to understand so you could answer it.

Having done the reading for this episode, I’d put it more strongly than that. It isn’t just their anxiety. It’s also correct.

So let me say the thing this all points at.

This is a case for a considered no. It is not a case for never. And the difference between those two is a review date.

Which brings us neatly to the part where you have to go and talk to somebody.

Taking It Upstairs

Right. Before I let you go, it’s time for Taking It Upstairs.

And I want to flag something before I start. In part one I made four commitments about this series, and the third one was that at least twice this season, this segment would end by concluding that your manager is right and the answer is no.

This is the first of those two. I’m calling it out so you can hold me to the second.

There’s a twist in this one, though. In most of these episodes, you’re the person who wants to move and your manager is the obstacle. In this episode it might well be the other way round. The pressure to modernise often comes down from somewhere: a director who read something, a new CTO with a mandate, a board-level line item about technical debt. And you’re the one who suspects the honest answer is “not this year”.

So here’s the ask, and it’s a different shape to the one you’d expect.

Don’t ask for permission to stay. That’s a terrible request. It sounds like avoidance, it’s impossible to say yes to, and it puts your manager in the position of formally blessing inaction, which no manager on earth wants to do in writing.

Ask for permission to write the decision down.

That’s a completely different conversation. It costs one meeting and no budget. And crucially, “let me spend a day documenting how we reached this position” is something almost any manager can approve without exposure, because documenting a decision isn’t the same as making one.

Here’s what goes in it. Six things.

The version you’re on now. The options you looked at, including the ones you rejected quickly. What moving would cost, in people and in weeks, with your reasoning shown. What moving would deliver, stated honestly, including the places where the honest answer is “nothing we can measure”. The support end dates for every option including staying put, which you now have from the last twenty minutes. And, most importantly, the conditions that would change the answer.

That last one is what separates this from an excuse. Write down what would have to become true. A dependency dropping Framework support. A hiring problem you can actually point at. A compliance requirement landing. A customer contract requiring something you can’t deliver. Name them, so that when one of them happens, the decision reopens automatically instead of requiring somebody to be brave.

Then two things about the document itself, and these matter more than the contents.

It needs a review date. Twelve months, eighteen at the outside. Put it in a calendar. A decision without a review date isn’t a decision; it’s a delay in formal clothing, and everybody will eventually read it that way, including you.

And it needs to be signed by the budget holder, not by you. This is the whole point of the exercise and it’s the bit people skip. A decision you write up on your own remains your decision, your risk, and your problem. A decision your manager or your director signs becomes the organisation’s decision. Same words, completely different document.

Because here’s what this is actually for.

Eighteen months from now, somebody new arrives. New CTO, new head of engineering, new consultant with a slide deck. And they ask why this application is still on .NET Framework.

“We never really got round to it” is a career problem. It sounds like nobody was minding the shop, and the person standing closest to the application when the question gets asked is you.

“Here’s the decision, here’s the arithmetic behind it, here’s who signed it, and here’s the review date we set” is not a career problem. It’s a demonstration that this system has been actively managed by somebody who was thinking. It might even be the best evidence of your judgement that exists anywhere in the company, because most engineering judgement leaves no trace at all.

And you already have a model for this, from the top of the episode. Stack Overflow’s no was twelve words: “We can upgrade to and run on it, but we don’t target it.” The decision, the condition, and the retained option, all in one sentence. One of the busiest websites in the world managed the entire artefact in a single line. Yours can be a page.

Now the honest caveat, and this one has two halves.

The first is that this gets you nothing tangible. No budget, no headcount, no time. If you were hoping this segment would help you win something, it won’t.

The second is more uncomfortable. If you do this exercise properly and honestly, it might come back the other way. You might sit down, work out the real cost, look at the real support dates, list the real conditions, and discover that the arithmetic says you should move. And now that’s written down, with a signature on it.

You have to be willing to live with that. Because if you only want the exercise when it agrees with you, you’re not running a decision process. You’re looking for permission, and everybody involved will be able to tell.

There’s a written version of all of this in the show notes, in business language, with no jargon in it. Send it to whoever needs to read it.

Call to Action and Close

So. Where does that leave us.

Existing code is more valuable than code in your head, because the existing code does something. That line belongs to Richard Campbell, as I established at some length last time, and it’s the line this series keeps coming back to.

And what I’d add this time is that the same is true of decisions. A decision that got made and written down is worth more than a decision that lives in somebody’s head as a vague sense that this was probably fine. Not because writing is magic, but because in eighteen months the version in your head will have quietly become indistinguishable from never having thought about it at all.

You might not need to upgrade. Genuinely, some of you should not, this year. But you should be able to say why, and you should be able to say what would change your mind, and somebody with budget authority should have put their name to it.

Next time in this series, we’re going to look at how applications actually start now: Global.asax and web.config and IIS, against Program.cs and the generic host and configuration providers. And specifically at why modern sample code can read like complete gibberish to somebody who’s spent a decade being extremely good at .NET Framework. That one’s not a failure of understanding on your part, and I want to explain exactly why.

If this episode was useful, share it with whoever keeps getting asked why they haven’t upgraded yet. They may well have a very good answer and no comfortable way to say it.

And the ask I made in part one still stands, because I meant it and one airing won’t fill it. Later in this series I want to talk to people actually living this. Somebody in an NHS trust, a local council, an insurer, a manufacturer. Not people with something to sell; people with a .NET Framework application and a set of real constraints. Anonymised, if your employer needs it to be. The contact page is in the show notes and I would genuinely love to hear from you.

Thank you for listening. Be kind to yourselves, be kind to each other. I’ll see you in the next one.

Wrapping Up

Thank you for listening to this episode of The Modern .NET Show with me, Jamie Taylor. Let me know if you found this monologue interesting or useful. I’d love to know if you did, and whether you’d like me to create more of them.

Be sure to check out the show notes for a bunch of links to some of the stuff that we covered, and full transcription of the interview. The show notes, as always, can be found at the podcast's website, and there will be a link directly to them in your podcatcher.

And don’t forget to spread the word, leave a rating or review on your podcatcher of choice—head over to dotnetcore.show/review for ways to do that—reach out via our contact page, or join our discord server at dotnetcore.show/discord—all of which are linked in the show notes.

But above all, I hope you have a fantastic rest of your day, and I hope that I’ll see you again, next time for more .NET goodness.

I will see you again real soon. See you later folks.

↑ ↓ to move · enter to open · esc to close