Every lead or architect interview for a team that owns anything more than a few years old eventually lands on this topic: what actually changed between .NET Framework and modern .NET, and what does that mean for a codebase that still runs on 4.8? This isn't nostalgia — with .NET Framework 4.8 receiving no further feature investment and Microsoft steering all new work toward the yearly .NET release train, leads are expected to make and defend real decisions about migration timing, risk and sequencing. Interviewers use these questions to test whether you understand the architecture deeply enough to estimate migration effort accurately, know which .NET Framework technologies simply have no modern equivalent, and can lead a multi-team migration without breaking a production system along the way. The ten questions below cover architecture, removed technology, support policy, deployment and the judgment calls a lead is expected to own.
Q1 What are the fundamental architectural differences between .NET Framework and modern .NET?#
Short answer: .NET Framework is a single, Windows-only, machine-wide installation with assemblies resolved through the Global Assembly Cache and web.config/machine.config-driven configuration; modern .NET is a set of independently versioned, side-by-side-installable, cross-platform runtimes where an app's dependency graph is resolved from NuGet packages into a self-describing deps.json manifest, with configuration driven by IConfiguration and the Generic Host rather than XML config sections.
The practical differences that matter for a migration estimate:
| Aspect | .NET Framework | Modern .NET |
|---|---|---|
| Platform | Windows only | Windows, Linux, macOS |
| Install model | One machine-wide version (with in-place updates) | Side-by-side versions per app |
| Assembly resolution | GAC, bindingRedirect in web.config/app.config | deps.json, NuGet, no GAC |
| Web hosting | IIS + System.Web in-process pipeline | Kestrel, with IIS/Nginx/YARP as reverse proxy |
| Configuration | XML config sections, ConfigurationManager | IConfiguration, layered JSON/env/secrets |
| DI container | None built in (third-party required) | Built into the Generic Host |
| Release cadence | Infrequent, tied to Windows/VS releases | Yearly, predictable LTS/STS cycle |
| Open source | No | Yes, developed on GitHub |
Beyond the table, the deeper shift is that modern .NET treats the framework itself as a package- addressable, replaceable component rather than an OS-level fixture — dotnet --list-sdks and dotnet --list-runtimes show exactly what's installed, and an app pins its own version via global.json/TargetFramework instead of depending on whatever happens to be registered machine-wide. That single change is responsible for most of the operational benefits leads care about: reproducible builds, safe side-by-side upgrades, and containers that carry their own runtime instead of depending on host state.
What interviewers look for: an answer that goes beyond "it's cross-platform now" to name concrete, operationally relevant differences — assembly resolution, hosting model, configuration — that affect how a migration is planned and estimated.
Q2 Which .NET Framework technologies have no equivalent in modern .NET, and what replaces them?#
Short answer: AppDomains, .NET Remoting, Code Access Security, System.EnterpriseServices/COM+ and Windows Workflow Foundation were not ported to modern .NET at all; WCF's server-side hosting isn't in the box either (though a community project provides it), and classic ASP.NET Web Forms has no modern .NET counterpart — each requires a genuine architectural replacement, not a drop-in API swap.
| .NET Framework technology | Status in modern .NET | Typical replacement |
|---|---|---|
| AppDomains | Not available | Separate processes/containers, or AssemblyLoadContext for in-process isolation of loadable code |
| .NET Remoting | Not available | gRPC, StreamJsonRpc, or named pipes/MemoryMappedFile for local IPC |
| Code Access Security | Not available | OS-level isolation: containers, users, virtualization |
| System.EnterpriseServices (COM+) | Not available | Re-architected service boundaries; no direct replacement |
| Windows Workflow Foundation | Not available | The community-maintained CoreWF library |
| WCF (server hosting) | Not in the box | CoreWCF (community project) for SOAP server hosting, or re-platform to gRPC/REST |
| ASP.NET Web Forms | Not available | Blazor, Razor Pages, or MVC, with a genuine UI rewrite |
The common thread across the first five rows is that each depended on a CLR capability — full AppDomain isolation, a distinct security transparency model, remoting's proxy-based object activation — that modern .NET's designers deliberately did not carry forward, because the isolation and security guarantees they promised were either weaker than advertised or better served by OS-level primitives (containers, processes) that didn't exist as a mainstream option when those technologies were designed in the early 2000s. WCF and Web Forms are a different category: both are still enormously prevalent in real enterprise codebases, which is exactly why they're the two a lead is most likely to actually plan a migration around, and both require treating the migration as a rewrite of that layer rather than a recompile.
What interviewers look for: accurate, specific knowledge of what's actually gone (not "everything still basically works"), and a credible replacement story for each — vague answers here are a strong signal the candidate hasn't led a real Framework migration.
Common mistakes: assuming WCF is entirely gone (the client libraries for consuming SOAP services do ship as NuGet packages; it's server-side self-hosting that needs CoreWCF or a re-platform); assuming Web Forms can be "upgraded in place" rather than rewritten.
Q3 What role does .NET Standard play today? Should a new class library target it?#
Short answer: .NET Standard peaked at version 2.1 and isn't evolving further; for a brand-new library with no requirement to run on .NET Framework, target the current TFM directly (net10.0 or similar) rather than .NET Standard, and reach for .NET Standard 2.0 — not 2.1 — only when the library genuinely must support .NET Framework 4.6.1+ consumers, since .NET Framework never implemented 2.1.
.NET Standard was designed to solve a real problem — one set of APIs a library could target to run unmodified on multiple different .NET implementations (.NET Framework, the old .NET Core, Xamarin, Mono) — but that problem has largely dissolved now that "modern .NET" is the single implementation going forward and simply targets a specific version's TFM. Today the guidance is straightforward: executables and apps that don't need to run on old Framework should target a current TFM directly, because doing so gives access to the full, current API surface (including all C# language features compiled against current library behavior) with no lowest-common-denominator constraint. A library should multi-target — for example <TargetFrameworks>netstandard2.0;net8.0</TargetFrameworks> — only when it has actual .NET Framework consumers to support; if it doesn't, multi-targeting just adds build complexity and forces the library to avoid APIs newer TFMs offer, for no real benefit. Note the version detail that trips people up: .NET Framework 4.8 implements .NET Standard 2.0, not 2.1, so a library that needs Framework compatibility must target 2.0 specifically, even though 2.1 is the "latest" Standard version.
What interviewers look for: correct, current guidance rather than outdated "always target netstandard2.0 for libraries" advice, plus the specific 2.0-vs-2.1-and-Framework-4.8 nuance that shows real multi-targeting experience.
Follow-up questions:
- Why would a library maintainer choose to drop .NET Standard support entirely in a major version?
- What build-time technique lets a multi-targeted library use a newer API only on newer TFMs?
Q4 You're leading migration of a 15-year-old WCF + Web Forms monolith. What's your strategy?#
Short answer: Treat it as two separate migrations bundled in one codebase — a WCF service layer that can often move behind an interface and be re-platformed to gRPC or REST incrementally, and a Web Forms UI that cannot be incrementally ported and needs a genuine rewrite — and sequence the work so the system stays shippable throughout, typically by carving out and migrating the least-coupled, highest- value slice first rather than attempting a big-bang rewrite.
The biggest risk isn't technical difficulty, it's schedule risk from underestimating coupling: Web Forms code-behind files routinely reach directly into business logic and data access with no separation of concerns, so "rewrite the UI" quietly becomes "rewrite the UI and extract business logic that was never isolated in the first place." A credible plan: first, introduce a strangler-fig boundary — a reverse proxy such as YARP routing by path so old and new coexist on the same host name for the whole transition; second, extract shared business logic into a portable class library both the legacy and new apps reference, forcing the separation Web Forms never had; third, migrate the WCF layer to gRPC or REST behind the same contracts consumers already use, since that's usually more mechanical than the UI work; fourth, rebuild UI surfaces one workflow at a time behind the proxy, ordered by business value, not by "easiest first." Treat Framework-era data access as its own migration work stream — it rarely falls out of the UI rewrite for free.
What interviewers look for: a strangler-pattern-based, incrementally shippable plan rather than "we rewrite it," explicit naming of the coupling risk in Web Forms code-behind, and separate treatment of the WCF and UI migration tracks.
Q5 Explain LTS vs STS in the .NET support model. How should it inform a team's version choice?#
Short answer: .NET ships a new major version every November; even-numbered versions (.NET 8, .NET
- are Long Term Support releases supported for three years, while odd-numbered versions (.NET 9) are Standard Term Support releases supported for roughly eighteen months, and a team building anything that will run in production for more than a year or two should default to the LTS release unless it has a specific reason to want an STS release's newer features sooner.
| Version | Type | Released | End of support |
|---|---|---|---|
| .NET 8 | LTS | November 2023 | November 10, 2026 |
| .NET 9 | STS | November 2024 | November 10, 2026 |
| .NET 10 | LTS | November 2025 | November 2028 |
| .NET 11 | STS | Planned November 2026 | — |
The table above has a detail worth calling out explicitly in an interview: .NET 8 and .NET 9 reach end of support on the same date, November 10, 2026, even though .NET 8 is LTS and shipped a full year earlier — because .NET 9's shorter STS window and .NET 8's three-year LTS window happen to converge on that date. Any team still on .NET 8 or .NET 9 needs a concrete upgrade plan landing before that date, or they're running an unsupported runtime — no more security patches, and typically a hard blocker for regulated environments. The practical rule of thumb for a lead: pick LTS for anything with a maintenance lifetime measured in years (most line-of-business systems), reserve STS for teams that have committed to upgrading every version and specifically want features a few months earlier, and track support end dates as a first-class item in the team's roadmap rather than discovering them reactively.
What interviewers look for: the LTS/STS duration numbers roughly correct, and — more importantly — the operational implication (mandatory upgrade cadence) rather than just reciting the policy.
Q6 What deployment model options does modern .NET offer, and how do you choose between them?#
Short answer: Framework-dependent deployment relies on a shared runtime already installed on the target machine and produces the smallest output; self-contained deployment bundles the runtime with the app so it runs without any pre-installed dependency, at the cost of a much larger output; single-file publishing packages either of those into one executable for easier distribution; and Native AOT compiles straight to native code ahead of time, trading the most flexibility (no reflection-heavy frameworks, no runtime code generation) for the fastest startup and smallest memory footprint.
<!-- Framework-dependent (default): smallest output, requires the matching runtime on the host -->
<PropertyGroup>
<SelfContained>false</SelfContained>
</PropertyGroup>
<!-- Self-contained single-file: no runtime dependency, one distributable executable -->
<PropertyGroup>
<SelfContained>true</SelfContained>
<RuntimeIdentifier>linux-x64</RuntimeIdentifier>
<PublishSingleFile>true</PublishSingleFile>
</PropertyGroup>
<!-- Native AOT: ahead-of-time native compilation, no JIT in the published app -->
<PropertyGroup>
<PublishAot>true</PublishAot>
<RuntimeIdentifier>linux-x64</RuntimeIdentifier>
</PropertyGroup>The choice mostly comes down to the deployment environment and startup sensitivity. Framework-dependent is the right default for internal servers where you control and standardize the installed runtime — it keeps images and packages small and lets you patch the runtime independently of the app. Self-contained (optionally single-file) suits distributing a tool or agent to machines you don't control, where you can't assume any .NET runtime is present. Native AOT is worth the extra constraints specifically when startup latency and memory footprint are first-order concerns — serverless functions billed per invocation, CLI tools, or a service that needs to scale out (and back in) very quickly — and the app's dependency graph is known to be trim- and AOT-compatible; it's the wrong choice for an app leaning heavily on runtime reflection, plugin loading, or a framework not yet AOT-ready.
What interviewers look for: all four options named correctly with their actual trade-offs, and a decision framework tied to real constraints (startup latency, environment control, dependency compatibility) rather than "AOT is always better/newer so use it."
Q7 How does assembly binding differ between the GAC-based model and modern .NET's resolution?#
Short answer: .NET Framework resolves assemblies against a machine-wide Global Assembly Cache with version conflicts patched by bindingRedirect entries in app.config/web.config; modern .NET has no GAC at all — every app carries its own resolved dependency graph, computed at build/publish time into a deps.json manifest that the host (hostfxr/hostpolicy) uses to build the trusted platform assembly list at startup, so version conflicts are resolved once, at build time, not patched at runtime.
This is a meaningful operational difference, not just a syntax change. Under .NET Framework, "DLL hell" was managed reactively: two libraries wanting different versions of a shared dependency required a binding redirect telling the runtime "when anyone asks for version X, actually load version Y," which worked but pushed conflict resolution to deploy time and made an app's effective dependency graph something you had to reconstruct by reading config, not just the project file. Modern .NET's NuGet-based model resolves the same kind of conflict at restore/build time through explicit version selection rules (nearest-wins, package references overriding transitive ones), and the result is recorded directly in deps.json, which ships with the app — so "what version of this library is actually running" is always answerable by inspecting a file next to the executable, not by tracing config transforms across environments. The absence of a machine-wide GAC also means side-by-side app versions on one machine no longer fight over a shared, global assembly registry at all.
What interviewers look for: understanding that this is a shift from runtime-patched to build-time- resolved dependency resolution, and the operational consequence (reproducibility, inspectability) that follows from it — this connects directly to how CI/CD and container builds behave more predictably on modern .NET.
Q8 A key library your team depends on only targets .NET Framework 4.8. What are your options?#
Short answer: First check whether it's actually pure managed code with no Framework-only dependencies, since a lot of older libraries run fine on modern .NET without modification even though they only formally target Framework; if it genuinely needs Framework-only APIs, the Windows Compatibility Pack restores a large surface of Windows-specific APIs (registry, performance counters, System.Drawing-style GDI+, WMI) for use from modern .NET on Windows; and if neither works, you're looking at forking, replacing the dependency, or keeping that one component on Framework behind a process boundary while the rest of the system moves forward.
In practice, the triage order a lead should follow: first, try referencing the library directly from a modern .NET project — many older libraries that only target net48 are pure IL with no Framework- specific API calls and will load and run correctly on modern .NET despite the stated target, which the .NET Upgrade Assistant and dotnet-apiport-style analysis can help confirm quickly. Second, if it fails because of genuinely Framework-only APIs (registry access, System.Configuration.ConfigurationManager patterns, GDI+-based imaging), add the Microsoft.Windows.Compatibility package, which reintroduces a large slice of that surface for Windows-hosted modern .NET apps — with the caveat that it only helps on Windows, so it's not an option if part of the migration's point is running cross-platform. Third, if the dependency is unmaintained and blocks the migration outright, evaluate whether it's small enough to fork and port, whether a modern equivalent package exists, or whether isolating that one component behind a service boundary (keep it running on Framework, call it over HTTP/gRPC from the modern app) lets the rest of the migration proceed without being blocked on that one piece.
What interviewers look for: a triage sequence rather than a single answer, specific knowledge of the Windows Compatibility Pack and its Windows-only caveat, and the isolation-behind-a-boundary fallback as a legitimate, pragmatic option rather than treating it as failure.
Q9 How do hosting and configuration differ between a Framework-era app and a Generic Host app?#
Short answer: A .NET Framework Windows Service or IIS-hosted app wires up its own startup by hand — Program.Main starting a ServiceBase, or IIS/ASP.NET's pipeline reading web.config through ConfigurationManager — while modern .NET centers everything on the Generic Host (Host.CreateApplicationBuilder/WebApplication.CreateBuilder), which provides a single, consistent place for dependency injection, layered configuration and hosted background services regardless of whether the process is a web app, a Windows Service, or a plain console worker.
var builder = Host.CreateApplicationBuilder(args);
builder.Services.AddHostedService<OrderProcessingWorker>();
builder.Services.Configure<OrderOptions>(builder.Configuration.GetSection("Orders"));
if (OperatingSystem.IsWindows())
{
builder.Services.AddWindowsService(); // run as a Windows Service when hosted that way
}
var host = builder.Build();
await host.RunAsync();The configuration model is the more consequential change for day-to-day development. Framework's ConfigurationManager.AppSettings reads a single, flat XML file resolved once at process start, usually requiring config-transform files (Web.Release.config) to vary settings per environment. The Generic Host's IConfiguration is explicitly layered — appsettings.json, then appsettings.{Environment}.json, then environment variables, then command-line arguments, then optionally user secrets or a key vault provider — with later layers overriding earlier ones, and it's strongly typed via the options pattern (IOptions<T>/IOptionsSnapshot<T>) instead of stringly-typed lookups. A background worker that used to be its own bespoke Windows Service project is now IHostedService/BackgroundService, hostable as a console app, a Windows Service, or a container with the same code — one of the more concrete productivity wins a lead can point to when justifying migration effort to stakeholders.
What interviewers look for: fluency with the Generic Host's DI/configuration/hosted-service model specifically, and the ability to connect it to a real productivity argument for migration, not just API recall.
Q10 As a lead, how do you sequence and de-risk a large legacy .NET migration across teams?#
Short answer: Migrate the lowest-risk, highest-leverage slice first to prove the pattern and build team confidence, keep the legacy and modern systems running side by side behind a routing layer for the entire transition instead of a cutover event, invest early in automated tests and telemetry so regressions are caught before customers notice, and treat the migration as an ongoing engineering practice with its own backlog and owner rather than a side project squeezed between feature work.
Concretely: start with a service or module that's well-isolated, has good or addable test coverage, and isn't on the critical revenue path — this proves the pipeline, coexistence strategy and team's fluency without betting the business on the first attempt. Put a reverse proxy or gateway in front of the whole system early so traffic routes to old or new per route without a big-bang cutover, and a regression can be rolled back by re-routing rather than redeploying. Invest in characterization tests for legacy code before touching it — you can't safely migrate behavior you haven't captured — and instrument both paths with the same metrics so you compare directly instead of trusting that "it looks fine." Resource it as a staffed initiative: migrations treated as something engineers do "in the gaps between features" reliably stall for years, while ones with a dedicated owner and visible sponsorship actually finish. Finally, anchor a hard deadline to the support dates discussed earlier — an external, non-negotiable date is far more effective at securing sustained investment than an open-ended "modernize when we can" mandate.
What interviewers look for: a genuine sequencing and risk-management strategy — coexistence, characterization testing, staffing, and deadline-anchoring — that shows you've led this kind of multi-quarter effort before, not just technical migration mechanics.
Quick-Fire Round#
| Question | Answer |
|---|---|
| Does modern .NET have a Global Assembly Cache? | No — dependencies resolve from deps.json, built at publish time. |
| Is WCF server-side hosting included in modern .NET? | No; use the community CoreWCF project or re-platform to gRPC/REST. |
| Which .NET Standard version does .NET Framework 4.8 implement? | 2.0, not the newer 2.1. |
| Are .NET 8 and .NET 9 both unsupported after the same date? | Yes — both reach end of support on November 10, 2026. |
| Which deployment model has the smallest output but needs a pre-installed runtime? | Framework-dependent. |
| Which deployment model has no JIT at all in the published app? | Native AOT. |
What replaces ConfigurationManager.AppSettings in modern .NET? | Layered IConfiguration plus the options pattern. |
| Can AppDomains be used for in-process isolation in modern .NET? | No; use AssemblyLoadContext or separate processes. |
How to Prepare#
- Be ready to name, specifically, which Framework technologies have no modern equivalent — vague "mostly everything's compatible" answers underperform in a lead-level interview.
- Rehearse the LTS/STS support-date table until you can state it without hesitation; leads are expected to own this as roadmap input, not trivia.
- Prepare a concrete migration sequencing story, even a small one from your own experience, that shows coexistence and incremental delivery rather than a rewrite narrative.
- Know the deployment-model trade-offs cold, including when Native AOT is the wrong choice — that nuance is what separates a lead answer from a "just use the newest thing" answer.
- Practice explaining
deps.json-based resolution versus GAC/binding redirects in plain language, since this comes up constantly when debugging "works on my machine" dependency issues.