.NET modernisation
Where money is the system of record.
Calvrix runs Microsoft-stack platforms for enterprises where the work is unforgiving: revenue, settlement, multi-currency, multi-gateway, multi-storefront. .NET Framework 4.8 → .NET 10, Azure-native, with the continuity the business actually depends on.
What the practice covers
Modern .NET, framework, and live migration between them.
Project model, dependency model, packaging, deployment, runtime upgrades. .NET 10 today; .NET Framework 4.8 maintained until safe to retire.
Entity Framework Core engineered for correctness.
Migrations, query performance, transactional integrity, schema evolution. Domain models that survive seven-year platform lifetimes.
Azure-native, production-grade.
App Service, Functions, SQL, Key Vault, Service Bus. Deployment safety, secret management, observability, cost discipline: built in from day one.
Audit-grade financial systems.
Money, ledgers, settlement, commission. Where the system is the source of truth. Designed for reconciliation, replayability, and provable correctness.
Why .NET modernisation, why now
The cost of waiting is not zero. It just shows up late.
.NET Framework 4.8 is in long-term support, not active development. Every year a workload stays on Framework, the surface of the problem grows: dependencies fall out of patch coverage; the ecosystem of compatible libraries thins; the available engineering pool that has Framework muscle memory shrinks; cloud-native runtime cost-per-request widens against modern .NET; and the operational gap between what Framework can be observed to do and what modern .NET surfaces by default keeps growing.
Modern .NET is faster, cheaper to run, and dramatically more observable. Project SDK-style projects are easier to reason about than packages.config-and-csproj sprawl. AOT compilation is genuinely viable for hot paths. Diagnostic tooling (dotnet-counters, dotnet-trace, dotnet-dump) ships in the runtime. Container images are an order of magnitude smaller. The default web stack (Kestrel) outperforms IIS-hosted Framework for the workloads Calvrix typically sees.
None of which means "modernise this weekend." It means the cost of the modernisation engagement compounds the longer it is deferred. The systems that are most expensive to modernise (multi-storefront commerce, multi-gateway payment routing, settlement-grade ledgers) are precisely the ones that benefit most from the move, because their operational reality is harder to fix on Framework than the migration itself would have been.
Calvrix runs these migrations live, with the business continuing to operate. The first contract is two weeks of audit; the migration itself follows the strangler-fig discipline. See the live-modernisation page for the migration method in detail.
The technical surface
What actually changes between .NET Framework 4.8 and .NET 10.
"Migrating to .NET 10" is a phrase that hides a list of concrete decisions. Below is the surface that almost every Calvrix .NET modernisation touches, and the way the choice falls inside Calvrix's operating standard.
Project SDK.
packages.config + csproj sprawl → SDK-style csproj. The package graph is explicit, transitive references are visible, and the .csproj diffs become readable in a pull request again. We move this first; everything downstream is easier once the project file tells the truth.
Target framework.
.NET Framework 4.8 → net10.0 for libraries with no UI surface, with multi-targeting (net48;net10.0) used during the migration window to let services consume both. Framework support is retired per-assembly, not per-solution, so the migration can advance one assembly at a time.
Hosting model.
IIS + System.Web → Kestrel + ASP.NET Core. The hosting boundary moves; controllers and Razor pages port largely intact; OWIN middleware ports to the ASP.NET Core middleware pipeline; HttpModules and HttpHandlers get rewritten to middleware. Out-of-process IIS hosting remains an option during the cutover phase.
Configuration.
web.config / app.config → appsettings.json + IConfiguration. Connection strings, AppSettings, custom config sections all migrate. The opportunity is to centralise secrets in Key Vault and remove every plaintext connection string from source control at the same time.
Data access.
Entity Framework 6 → Entity Framework Core (or Dapper for the read-heavy hot paths). EF6 → EF Core is not a syntactic rename: query translation, change tracking, and migration tooling all behave differently. We port one bounded context at a time, run dual-stack against shadow traffic, and only retire EF6 once the read/write parity is verified.
Logging and telemetry.
log4net / NLog / custom trace listeners → ILogger + OpenTelemetry. The wire format changes; the consumer (Azure Monitor / Application Insights / Datadog / Grafana stack) is chosen per engagement. The point of the upgrade is the dimensional richness OTel brings, not just doing the same thing more verbosely.
Background work.
WCF / WebJobs / Windows Services → Azure Functions or hosted services (IHostedService) inside the main process. The migration target depends on workload shape: queue-driven background work tends to land cleanly on Functions; long-running stateful workers stay in-process.
Deployment.
Web Deploy / MSI / manual IIS → container images shipped through reviewed CI/CD. The application packaging is the migration's most visible artefact and is the moment the operating standard takes over from the pre-migration habits.
The migration discipline
Strangler-fig in practice: what actually happens, day to day.
The strangler-fig pattern is widely cited and rarely operationalised. In a Calvrix engagement it means: at any moment of the migration, the Framework path and the modern .NET path both exist in production, both can serve a given request, and the production decision of which one to use is governed by a feature flag: sometimes a global one, more often a per-customer or per-route one.
Traffic shifts incrementally. We typically begin with 1% of live traffic on the modern path for a defined observation window, long enough to surface low-frequency edge cases that synthetic testing will not catch, then 5%, 20%, 50%, 100%. Each step gates on observability evidence: error rate parity, latency parity (in both directions; a faster path is also a behavioural change worth investigating), response-body parity for the same input where reconciliation is feasible.
Dual-running is not optional. For workloads with reconciliation surface (payments, ledgers, settlement) both paths receive the request, both produce the answer, and a reconciler compares the two before the modern path is allowed to advance. This is the same operating discipline Calvrix applies to gateway routing changes and ledger-path migrations. The cost is engineering time; the alternative is finding the divergence on quarter-end close.
Every transition is reversible until the Framework path is intentionally retired. The retirement itself is a written decision: an ADR documenting that the modern path has met the success criteria, the observability is sufficient, and the rollback option is being relinquished. The Framework binary stays in source control; the runtime is no longer reachable.
Non-negotiables
What you cannot skip: even when the project is behind.
Every modernisation engagement reaches a point where shortcuts look attractive. Each item below is on the list because Calvrix or our customers have paid for the omission of it in a previous engagement. None of them are skippable.
Common traps
The migration patterns that fail, and why.
The five patterns below are the ones that recur in long-lived .NET estates. They share a common structure: a decision that looked rational on a slide and that the production system disagreed with.
The rewrite.
The team decides the .NET Framework codebase is so encrusted that a fresh build is faster than a migration. Eighteen months later, the rewrite has matched 40% of the original feature set, the Framework codebase is still in production, and the engineering team has accumulated debt in both. Rewrites fail because the original codebase encodes years of edge-case knowledge that nobody has written down. The strangler-fig discipline preserves that knowledge by migrating around it.
The big-bang cutover.
A go-live weekend. The team plans for every contingency they can think of. The production system finds one they did not. The rollback path either does not exist or has not been rehearsed. We do not do go-live weekends. Traffic ramps; rollbacks are warm; the migration completes in a series of unremarkable Tuesdays.
Ignoring the integration layer.
The team scopes the migration as the application code. The integration layer (message contracts, partner APIs, ETL feeds, scheduled jobs) is invisible until cutover. We do not quote a migration that does not include an integration audit in week one. The integration layer is usually 30–50% of the real work.
Deferring the test suite.
"We'll add integration tests as we go." The team ships the migration first; the tests never quite get prioritised; six months in, the team is afraid to make changes because there is no safety net. The migration is the moment to land the test suite that should have existed already. We build the boundary-test layer before the strangler-fig starts.
Mocking out the database.
Integration tests against an in-memory database. Production hits SQL Server. The tests pass; production breaks on transaction isolation, deadlocks, or the truncation behaviour of a column that the mock does not enforce. Mocking the data layer is a way of guaranteeing the test suite cannot find the bugs that production will.
The Azure-native landing
Where the modernised platform actually lives.
The runtime upgrade is half the work; the other half is landing the modernised application on Azure-native infrastructure that the operating standard can hold. The components below are what almost every Calvrix .NET modernisation engagement converges on.
The shape adapts to the customer's existing Azure investment and compliance posture. The constants are: secrets out of source control, identity out of connection strings, observability dense enough to operate the platform from, and infrastructure declared in code that survives the people who wrote it.
The migrations we refuse
What we will not quote, and why.
Selectivity is part of the standard. The engagements below are ones Calvrix declines on principle, accepting them would compromise the operating standard the engagements we do take depend on.
How engagement works
A two-week audit. A modernisation plan with explicit reversibility. A senior owner end-to-end.
A .NET modernisation engagement starts with a two-week audit of the current platform, target runtime, integration surface, and the failure modes that have actually shipped in production. We do not quote a migration that ignores the integration layer.
- Week one and two. Audit. Map the codebase, the integration surface, the deployment pipeline, the observability coverage, and the operational ownership today. Output: a written assessment, a target architecture, and a phased migration plan with reversibility criteria at every step.
- The migration itself. Live, in-place, never a stop-the-world rebuild. Strangler-fig pattern; new and old paths run side by side; traffic shifted incrementally with feature flags; any single transition reversible.
- How it is priced. The audit is a fixed-scope engagement. The migration itself is a senior retainer plus scoped milestones tied to the phases of the plan, with explicit success criteria the customer signs off.
- What we will not do. Rewrites. Big-bang cutovers. Migrations that ignore the integration layer. Quotes that pretend the test suite is in better shape than it actually is.
// start the conversation · [email protected]
Frequently asked
What buyers ask about .NET modernisation.
Every modernisation we have been pitched turns into a rebuild. What does yours actually involve?
A live migration from .NET Framework 4.8 (or older) to modern .NET (currently .NET 10), executed as a sequence of small, reversible, observable transitions. We do not rebuild the platform; we modernise it in place. The application stays online for the entire migration.
We need a date for the board. How long does a .NET Framework to .NET 10 migration take?
It depends on the surface area, the test coverage you start with, and how much of the integration layer is third-party. A focused single-service migration is weeks. A multi-storefront, multi-gateway commerce platform is measured in quarters. We will not give a quote that ignores the integration surface.
We were burned by a rewrite once already. Will you rewrite our codebase?
No. Rewrites fail. We modernise in place using the strangler-fig pattern: new and old paths run side by side, traffic is shifted incrementally with feature flags, and any single step is reversible. The business does not stop because the engineering is in flight.
We want to move to Azure at the same time. Can that run alongside the .NET upgrade?
Yes. The two are normally co-ordinated: as services come off .NET Framework, they land on Azure App Service / Functions / SQL / Key Vault with the operating posture we hold them to: managed identity, observability, secret rotation, infrastructure-as-code.
Have you actually done this at our scale, or only in theory?
A multi-storefront commerce platform in continuous production since 2019, currently mid-flight on a .NET Framework 4.8 → .NET 10 migration. The platform belongs to the customer, so its trading figures are theirs to publish, not ours.
We have standardised on .NET 8 for compliance and cannot jump to 10. Does that rule us out?
No. .NET 10 is our target runtime because it is the current long-term-support release, but engagements that land on .NET 8 are common when a customer has hardened on a specific LTS for their compliance posture.