Create your own
Lesson illustration

Selecting a .NET Target Framework Using Support Policy and Deployment Constraints

Welcome to the first lesson of the Modern C# and .NET Upgrade module. This module establishes a current, supportable baseline for the game-backend portfolio project before we improve its code quality and architecture.

A target framework is not merely a line in a .csproj file. It commits the application to a runtime, an operating-system compatibility surface, a security-patching process, and an upgrade cadence. For a senior developer, the useful question is not “Which version is newest?” but: Which supported target can we deploy, patch, operate, and upgrade responsibly under this project’s constraints?

By the end of this lesson, you will be able to choose an LTS or STS release, select the correct Target Framework Moniker (TFM), and justify the choice for this project.


Support policy: LTS and STS are different upgrade cadences

Microsoft ships a new major .NET release each November. The releases alternate:

  • Even-numbered releases are Long Term Support (LTS).
  • Odd-numbered releases are Standard Term Support (STS).
  • Both are production-quality, fully supported releases while they remain in support.
  • The difference is support duration and therefore the team’s expected upgrade rhythm.

NET and .NET Core official support policy

Read Microsoft’s official policy rather than relying on release folklore. It defines the support periods, shows the currently supported versions and end dates, and explains the patching obligation that is easy to overlook.

In the opening section, “What releases qualify for servicing and how do updates affect servicing qualifications?”, read the support choice. Then inspect the table in “.NET and .NET Core release lifecycle,” especially “Supported versions,” noting the release type, support phase, and end-of-support date. Next, in “Servicing,” read patch servicing. Finish with support phases, including the surrounding “Active support,” “Maintenance support,” and “End of life (EOL)” material.

The practical policy is:

TrackTypical durationBest fitUpgrade expectation
LTS3 yearsProducts that favor a lower major-version upgrade frequencyMove major versions roughly every two years, with planning time before EOL
STS2 yearsTeams that deliberately adopt the latest platform capabilities and performance improvementsPlan a major-version upgrade annually
Maintenance phaseFinal 6 months of either lifecycleA temporary period before upgrade completionSecurity fixes only; no functional improvements

The terms “LTS” and “STS” do not mean “stable versus experimental.” A general-availability STS release is suitable for production. Selecting STS simply means accepting a more frequent upgrade cycle.

Support is conditional on patching

A major version alone is not a support status. A deployment on an old patch of an otherwise supported major version is not fully current under the policy. Patches are cumulative, so the operational responsibility is to keep the runtime and deployed application current with the latest applicable patch.

That requirement has different forms depending on deployment style:

  • A framework-dependent application uses a .NET runtime installed on its host. The host runtime must be patched.
  • A self-contained application carries its runtime with the published output. Patching normally means republishing with an updated runtime.
  • A containerized application inherits its runtime from its base image. Patching means rebuilding from a current supported base image, testing, and deploying a new image revision.

A container does not remove the need to patch .NET; it makes the runtime version part of the image supply chain.

Read the policy table as a planning tool

The current table in the policy resource lists:

  • .NET 10 as an LTS release, supported through November 2028.
  • .NET 9 as an STS release, supported through November 2026.
  • .NET 8 as an LTS release, also ending support in November 2026.

Those dates will change as Microsoft publishes patches and releases, so a senior-level decision includes an explicit habit: check the official policy at the time of the decision and again during upgrade planning.


A target framework is a compile-time and deployment contract

The value placed in TargetFramework is called a Target Framework Moniker, or TFM. Examples include net8.0, net9.0, and net10.0.

The TFM determines the API surface against which the application compiles. In broad terms, an ASP.NET Core application targeting net10.0 can use .NET 10 APIs and needs a compatible .NET 10 runtime when deployed framework-dependently.

It is useful to separate three related concepts:

ConceptExampleWhat it controls
Target frameworknet10.0APIs available at compile time and the compatible runtime family
SDK.NET 10 SDKBuild tools, compiler, templates, restore, and publish behavior
Runtime patchA current .NET 10 patchSecurity and servicing level used in production
Deployment modelFramework-dependent containerHow the runtime reaches the host environment

Confusing these leads to common mistakes. Installing a newer SDK locally does not automatically retarget a project. Conversely, changing the TFM is not enough if the hosting environment cannot run that runtime or if a package dependency cannot support it.

Target frameworks in SDK-style projects - .NET | Microsoft Learn

Microsoft Learn’s target-framework reference gives the exact TFM syntax and distinguishes a portable base target from an operating-system-specific target. Focus on decisions relevant to an ASP.NET Core backend rather than older framework families.

In “Latest versions,” read the TFM table and inspect its TFM column for .NET 8, .NET 9, and .NET 10. Then read “OS-specific TFMs,” concentrating on the “Suggested targets” guidance. In particular, read portable target guidance. Finally, in “How to specify a target framework,” read the project setting and briefly inspect the subsequent explanation of the plural TargetFrameworks element.

Base TFMs versus OS-specific TFMs

For the game backend, the important distinction is between:

  • net10.0, a base TFM, appropriate for portable server-side applications.
  • net10.0-windows, an OS-specific TFM, which exposes Windows-specific APIs and commits the application to Windows.

An ASP.NET Core API that will ultimately run in Linux containers on Azure Container Apps should target the base TFM:

<Project Sdk="Microsoft.NET.Sdk.Web">
  <PropertyGroup>
    <TargetFramework>net10.0</TargetFramework>
  </PropertyGroup>
</Project>

Targeting net10.0 does not itself guarantee portability. Native libraries, filesystem assumptions, shell commands, and package choices can still make an application platform-specific. But it avoids unnecessarily declaring Windows-only APIs as part of the application contract.

Use an OS-specific TFM only when the application genuinely needs OS-specific capabilities. A WPF desktop application, for example, should target a Windows TFM. A general web API normally should not.

Do not multi-target by default

TargetFrameworks allows a library to build for several frameworks. This is appropriate when a reusable library truly has consumers on different supported platforms and you commit to testing each target.

It is usually the wrong response for a new service. Targeting both net8.0 and net10.0 does not make one deployed API more compatible; it creates two build outputs, two test targets, and a more complicated release decision. A deployable backend should normally have one deliberate production target.


Make the selection from constraints, not habit

A sound framework choice starts with non-negotiable constraints, then uses LTS or STS to set the upgrade cadence.

Use this sequence when assessing a project:

  1. Eliminate unsupported versions. Do not select an EOL framework for a new production service simply because it is familiar.
  2. Determine the required operating environment. Check the intended host, base OS image, runtime stack, and CPU architecture. If a managed host does not support the chosen runtime, either select another deployment model or choose a supported framework it can run.
  3. Check dependency compatibility. Database providers, authentication libraries, observability packages, and internal shared packages must support the target. “It restores” is necessary, but package maintenance and runtime behavior matter too.
  4. Estimate the project’s service lifetime and upgrade capacity. Select a version whose support window covers the expected operating period plus time to test and deploy the next major upgrade.
  5. Choose LTS or STS intentionally. LTS lowers major-upgrade frequency; STS gives earlier access to platform changes but requires scheduled annual upgrades.
  6. Define patch ownership. Name the CI/CD or platform process that rebuilds, tests, and deploys security updates.

The framework policy is only one input. Deployment constraints can correctly override a generic “always use the newest LTS” rule.

ScenarioSensible choiceReasoning
New cross-platform API deployed in containers, dependencies support the latest LTSLatest LTS base TFM, currently net10.0Long support runway, portable API surface, and a predictable patch process
Internal service requires a new feature available only in the latest STS releaseCurrent STS base TFMValid if the team explicitly funds annual major-version upgrades
Existing application can deploy only on an approved platform with .NET 8 todaynet8.0 temporarily, with an upgrade planThe current deployment constraint is real, but the approaching end date makes a host or application upgrade urgent
Windows-only desktop application using WPFCurrent supported netX.0-windows TFMWindows APIs are required by the product itself
Shared library genuinely consumed by multiple supported app targetsDeliberate multi-targetingCompatibility is part of the library’s product responsibility

A weak justification is: “We always use LTS.”

A stronger justification is: “This is a cross-platform backend planned for container deployment. The newest LTS is supported beyond the expected portfolio lifetime, our dependencies support it, and we will rebuild images when runtime patches are released. We therefore target net10.0.”

That sentence communicates supportability, architecture, operations, and delivery awareness.


Decision for the game-backend project

For this course project, use:

The reasoning is concrete:

  • It is the current LTS release in the provided policy table.
  • It offers the longest currently listed support horizon.
  • The planned API is cross-platform and headed toward Linux containers on Azure, so a base TFM is appropriate.
  • There are no stated legacy-host or legacy-client constraints requiring an earlier framework.
  • The project is intended to demonstrate current senior .NET practices rather than preserve compatibility with an old runtime.

Record the choice in the repository’s README or a short engineering decision note. Include:

  • Target: net10.0
  • Deployment assumption: Linux-capable container hosting
  • Patch approach: rebuild and redeploy from current runtime base images
  • Review trigger: a new .NET major release, an approaching support deadline, or a dependency incompatibility

Before committing the project baseline, verify that the local development environment has a compatible SDK and that restore and build succeed. If the intended deployment platform or a key package cannot support net10.0, treat that as a design constraint to investigate, not as a reason to silently downgrade the project.


Key takeaways

  • LTS and STS are both production-ready support tracks; their principal difference is support duration and upgrade cadence.
  • Staying supported requires the latest patch, not merely a supported major version.
  • A TFM such as net10.0 is a compile-time API and runtime-family contract, while the SDK and deployment runtime are separate concerns.
  • Use a base TFM for a portable ASP.NET Core API; use an OS-specific TFM only when OS-specific APIs are required.
  • For the game backend, net10.0 is the justified baseline, subject to verifying the real deployment environment and dependencies.

Next, we will make that baseline consistent across a solution by configuring compiler settings, nullable analysis, and code-quality analyzers with Directory.Build.props.

Can't find a good explanation? Sign up and we'll make it for you

Sign up