Applications are running on old technologies need to go through transition into latest technologies. Running core applications using obsolete technologies can pose big risks, and limit to achieve high availability, reliability, and scalability. These limitation and others hinder the organization’s ability to innovate, scale and maintain a secure application environment.
This proposal enable application to migrate from .NET
framework 4.x to .NET 8 aims to improve performance, reduces security VITs and
container ready, ensuring the longevity and scalability of the application. As
part of the strategy, it is proposed to create a new code repository (this will
help to migrate from old repository to new for example GitHub) to help
developers establish a more organized version control history, making it easier
to track changes, manage branches and handle pull requests effectively. It also
provides a clear distinction between the old and new codebases, reducing the
risk of accidental dependencies on legacy code and simplifying the deprecation
process of the legacy application version. Deploying an application upgraded
version side-by-side with legacy version can be a highly effective strategy for
minimizing risk and ensuring a smooth transition. This approach allows to
compare both versions in a production like environment and provides a fallback
option if issues arise with the new version.
Applications built using the technology stack and framework
which now old or obsolete, are challenge to upgrade using new technologies
because of business continuity. However, the team is currently encountering
several challenges due to the outdated technology and frameworks, such as
issues with scalability, observability, security vulnerabilities, unit testing
and high operation cost, among others.
This paper is focused on various aspects of performance, maintainability, security and future readiness. Below are the key objective:
·
Remove all the dependencies on the legacy .NET
Framework 4.x. The use of .Net 8 will allow application to run in
containerized environments, and subsequent support the container deployment
strategy.
·
Reduce
vulnerabilities due to outdated technologies.
· Reduce technical debt and make future upgrades simpler: Below are the few examples.
o The application is running in 32 bits. With the .NET 8 upgrade we can run the application in 64-bit, which will enhance performance.
o
Application lacks a dependency injection
framework, limiting the team’s ability to write true unit tests. Dependency
injection is a built-in part of .NET 8, which will help improve product
quality.
Known Industry Data:
Security Vulnerabilities |
Critical Vulnerabilities should be identified and remediated within 30 days. Security patches should be applied within 7 days of the release |
Developer Productivity |
Build times should be less than 15 minutes. Deployment frequency should be biweekly |
Performance |
For standard operation, response times for API endpoints should be less than 100 milliseconds. Should handle 1000 requests per second. CPU Usage should be less than 60% under normal load. Memory usage should be less than 65% of available RAM under typical operation |
This document proposes Canary Deployment approach to migrate
legacy code from .NET Framework 4.x to .NET 8. This approach aims to minimize
risk, reduce downtime and ensure continuous delivery
of new features/improvements by deploy the upgraded version alongside the
legacy version.
It is proposed to create new code repository and .NET 8 instead of using existing repository, below are some of the advantages:
·
Clean Slate for Development: Developers
can focus on code upgrade without the distraction of dealing with legacy code
issues, leading to more efficient development cycles.
· Improve Version Control: By starting a new repository, developers can establish a more organized version control history, making it easier to track changes, manage branches and handle pull requests effectively.
· Clear Separation of Concerns: It provides a clear distinction between the old and new codebases, reducing the risk of accidental dependencies on legacy code and simplifying the deprecation process of the legacy application version.
·
Risk Management: By
isolating the new codebase, the risk of introducing bugs or vulnerabilities
into the legacy application during the upgrade process is minimized.
.NET Upgrade Assistant can be used to upgrade application legacy code.
.NET Upgrade Assistant: Command line tool/VS Extension that helps upgrade .NET Framework application to .NET 8.
Installation: Its available as a Visual
Studio extension or as command line tool.
Following are steps involved in upgrading from .NET 4.x Framework to .NET 8:
1. Preparation Phase
1. Initial Planning and Assessment: Evaluate the current state of the application, identify potential risks, create mitigation plans and establish rollback procedures. Communicate the development plan to all stake holders.
2. Development and Testing:
1. Initial Setup: Identify the team member who are responsible of upgrade the application from legacy solution to .NET 8, compile it successfully. To achieve this following team, need to perform the following:
1. Update Solution/Project Files.
2. Update Dependencies.
3. Refactor startup project.
2. Code Changes: In this stage development team will join the initiative to resolve any runtime error/issues and deploy the application in DEV environment. In this stage team will focus on issues which may require other team support or work around to make the application work. Following are few example of such cases:
1. Refactoring of WCF Call.
2. Create proxy service to handle legacy integration which not compatible with .NET 8 such as MSMQ.
3. Configuration management (AppSettings and System.Web, runtime tags).
4. Refactor logging code.
5. Dependency Injection Framework Refactor.
6. Integration Test Migration.
7. Update Configuration and Settings (encrypt the config file).
3. DevOps Changes: Update CI/CD pipeline to use .NET 8 for building and deploying .NET 8 application.
4. Configure Monitoring and Logging tools: Work with operation team to configure monitoring tools for the application .NET 8 version. For better support, it is recommended to create separate account for application .NET 8 version. This will provide a clear distinction between the old and new application.
5. Testing and Validation: In this stage team will conduct unit and integration testing and begin UAT with clients. Below are the testing techniques team will use:
1. Automated Tests: Run application integration tests suite using application upgraded .NET 8 API endpoint to ensure that the application behaves as expected.
2. Manual Testing: Conduct extensive manual testing to catch any issues not covered by automated integration tests.
6. Build Infrastructure: These are the three approaches we can adopt:
1. Build new Servers: This approach requires lot of co-ordination between different team for example, provision servers, open firewall, updating DevOps pipeline. Due to time constraint this may not work well for us.
2. Re-purpose existing Servers: With this approach you can initially repurpose few of servers to host application .NET 8 version. As you gain confidence in .NET 8 version, more servers can shift from legacy to upgraded version. In the end all servers will be repurposed to upgraded application version.
3. Use both Legacy and New application on same Servers: In this approach both legacy and new application version will be hosted on same servers. This technique may create some hardware resource (CPU, Memory) conflict.
My recommendation is to “Re-purpose Existing Server” approach as this provide
clear distinction between the old and new application but in
the same time quick to provision.
2.
Deployment Phase
Deploying an application upgraded version side-by-side with legacy version can
be a highly effective strategy for minimizing risk and ensuring a smooth
transition. This approach allows to compare both versions in a production like
environment and provides a fallback option if issues arise with the new
version.
1. Canary Deployment:
1. Setup: Deploy the upgraded version alongside the legacy version.
2. Partial Traffic: Route a small percentage of user traffic to the upgraded version while the majority continues to use the legacy version.
3. Monitoring: Monitoring the behavior of the canary release closely. if successful, gradually increase the percentage of traffic directed to the upgraded version.
2.
Implementation
Gradually increase the percentage of traffic to the upgraded version as
confidence grows. Once the upgraded version is stable and performs well,
transition all traffic to it.
1. Load Balancer Configuration
1. Load Balancer: Use application Load balancer to manage traffic distribution between the two versions.
2. Routing Rules: Configure routing rules to direct traffic based on deployment strategy.
3. Rollback Plan:
If issues arise with the upgraded version,
immediately route all traffic back to the legacy version. The rollback involves
only a change to the ALB routing and can be performed without system downtime.
3.
Code Sync Process/Strategy:
To support the business continuity, development of new API or bug fixes will
not be freeze at anytime. Application .NET 8 team
will make sure to sync the code from legacy to .NET code repository. As we make
the progress there will be following phases of the upgrade that requires
different Code sync strategy:
1. Pre Deployment: After code fork from legacy and upgraded to .NET 8 version, .NET 8 team is responsible to sync the code from the legacy after every application release cycle.
2. Post Deployment: After Upgraded code deployed in production, application developer who creates a PR in legacy repo is also responsible for PR creation in the new repo. This is the most challenging phase of the code sync as it create too much work for developers.
3.
Legacy Freeze: After all
traffic transits to application .NET 8 version, .NET 8 repository will become
primary repository and all the bug fixes and enhancement activities will shift
to .NET 8 repository. At this point developers will create all PRs in .NET 8
code repository and manually merge the code to legacy code repository, just to
keep both repository in sync to support the situation where we need to rollback
to legacy.
After all traffic is shifted to application .NET 8 version, it is preferred to
fix the issue in .NET 8 version instead of legacy repo, as we consider that the
probability of rolling back to legacy after moving to .NET 8 version is less.
To ensure the success and effectiveness of the incremental
upgrade to .NET 8 and modularization, it is essential to measure the impact
throughout the migration process. Below are key metrics and
methodologies for evaluating the impact:
Application
performance:
o Response
times: Measure the average response time for key application functions before
and after each migration phase.
o Throughput:
Monitor the number of requests handled per second.
·
Resource Utilization: Track CPU, memory, and
disk usage to assess any changes in resource efficiency.
·
Startup time : Compare
application startup times before and after migration
·
Load testing: Conduct load testing to determine
how the application performs under high traffic conditions and compare results
across migration phase.
Error
rates: Monitor error rates and types of errors occurring in the application,
aiming for a reduction as the migration progresses.
·
Bug counts: Track the number of reported bugs
and issues in each phase, targeting a decrease as stability improves.
·
Test coverage: Ensure comprehensive test
coverage for all migrated components and track improvements over time.
User
feedback: Work with the product team to collect user feedback and satisfaction
scores though surveys and support channels comparing results before and after
upgrades.
·
API response times: Measure API response time
and aim for reductions as migration progresses.
·
Application downtime: Track and downtime or
disruptions experienced during the migration and aim to minimize these
occurrences.
Development time:
Measure the time taken to migrate each module or component and compare against
estimates to improve planning accuracy.
2.
Deployment frequency: Track the frequency of
development and updates during the migration process, aiming to maintain or
increase deployment cadence.
3.
Code quality: Use static code analysis tools
(such as SonarQube) to measure code quality metrics and adherence to coding
standards.
4.
Integration and regression testing: Monitor the
number of successful and failed integration and regression tests to ensure
continuous stability.
Baseline
comparison: Establish a baseline for all metrices before starting the migration
and compare these baselines to post migration data to measure improvements.
2.
A/B testing: For APIs, conduct testing to
compare client experience between legacy API and migrated API versions.
3.
Monitoring and logging: Improve comprehensive
monitoring and logging to capture real-time data on application performance, errors and resource utilization.
4.
Performance benchmarks: Conduct regular
performance benchmarking tests to measure and compare the performance of the
application throughout the migration process.
By systematically measuring these metrics and employing these methodologies, we can accurately assess the impact of the incremental upgrade to .NET 8 and modularization, ensuring that each phase delivers tangible improvements, and that the overall migration achieves its intended goals.
Although most .NET and third-party
libraries support both .Net Framework 4.x and .Net 8, there is still the
possibility that some legacy libraries don’t support .Net 8. The team will need
to identify potential incompatibilities during migration and remediate them.
Platform dependencies are another big challenge with migration projects from .NET Framework to .NET 8. Most dependencies are caused by a reliance on OS-specific features or CPU configurations (32 vs 64 bit binary support). Platform-specific dependencies in applications built by .NET framework may vary, but problems commonly arise with third-party components or system-level integration points such as Windows Registry access or COM interop calls.
One way of solving this challenge is by doing away with the dependencies altogether, especially if they are unnecessary for core functionality. However, if the team cannot replace or do away with certain dependencies, other options will have to be explored.
Upgrading to .NET 8 from .NET
Framework 4.x comes with new behavior patterns, causing differences between the
APIs of the two frameworks. For example, .NET 8 might lack certain classes or
methods, requiring the team to rewrite the sections of the code that apply to
them. In other words, refactoring the existing code is necessary.
A typical migration project
requires extensive testing to validate the application’s functionality and
stability when deployed across different platforms. In addition to leveraging
the existing tests, developers might also need to conduct compatibility testing,
especially if they are going to identify and remediate platform-specific issues
emanating from the migration process.
Below are pros and cons of .NET and .netstandard2
approaches:
This proposal enables application to migrate from .NET
framework 4.x to .NET 8 aims to improve performance, reduces security VITs and
GP container ready, ensuring the longevity and scalability of the application.
As part of the strategy, it is proposed to create a new code repository to help
developers establish a more organized version control history, making it easier
to track changes, manage branches and handle pull requests effectively. It also
provides a clear distinction between the old and new codebases, reducing the
risk of accidental dependencies on legacy code and simplifying the deprecation
process of the legacy application version. Deploying an application upgraded
version side-by-side with legacy version can be a highly effective strategy for
minimizing risk and ensuring a smooth transition. This approach allows to
compare both versions in a production like environment and provides a fallback
option if issues arise with the new version. Below are the pros and cons of
this approach:
· Can be delivered in fast pace.
· With Canary deployment, rollback will be easy. Using application load balancer's weighted configuration, traffic can be shifted back to the legacy system without requiring any changes to the legacy or client application.
· The Application Code will be ready for Containerization.
· Application performance will be improved by upgrading to .NET 8 and running it on 64-bit IIS process.
· Can statically validate whether the code is truly portable.
· With .NET 8, the team can eliminate the use of .NET Framework 4.x VITs.
· For short period, two versions of the Application will need to be supported until the full rollout of .NET 8 is complete.
· Code merge from the legacy repository to new .NET 8 repository.
· Legacy code history will not be available in the .NET 8 repository.
A proposal for upgrading application with the intention of
reducing risk, time to delivery, and a future path for further development.
This would be accomplished in incremental steps against the existing codebase.
This would be accomplished by converting dependent libraries to .NET Standard
2.0 and then swapping out the primary project with a new .NET 8 API. Following
that, dependent libraries can be upgraded or replaced over time to .NET 8+.
Below are the pros and cons of .netstand2 incremental
approach
·
Can be worked on in parallel with any
alternative approaches.
· Code can be upgraded and tested iteratively and on a regular basis.
· Only one version of application needs to be supported.
·
Code history is preserved.
·
As per the
current process, full regression and load testing are required whenever
project's target framework is changed.
·
New
VITs may be introduce due to refencing older versions of NuGet packages with.
netstandard2.
·
Application
requires another upgrade cycle for .NET 8.
·
The
rollback strategy involves reverting to the previous version, which
necessitates downtime, adding complexity, especially if the release includes
database schema changes.
· As upgrades and regular enhancement/bug fixes occur in the same repository/solution, there is a possibility that developers may step into each other’s code, potentially leading to merge issues.
Proposing upgrade application from .NET Framework 4.x to .NET 8. Here
are reasons why .NET 8 is preferred over .NET Standard2.0:
·
Latest Language Features:
.NET 8 supports the newest C# language features, improving developer
productivity and code quality.
·
Performance Improvement: .NET 8
includes numerous performance enhancements, making application faster and more
efficient.
·
Long Term Support:
.NET 8 is cross-platform, allows to run application on Windows and Linux. This
opens the opportunity to host application in container.
·
NuGet Package .NET Standard2.0 Support:
In some cases, it is observed that latest NuGet package not compatible with .netstandard and need to use
previous version to make it work which may create VITs problem (for example - System.ServiceModel dlls, FeatureToggle doesn't have support in .net standard
2.0)
·
Duplicate Effort: Need to go though another upgrade process to upgrade to .NET 8 from
.NET standard. For example, Implementations of System.Web and System.Configuration
is different in both frameworks.
·
Testing Scope: If we update the
same solution with .NET Standard every incremental release needs full
regression test as the dlls are used across the
functionalities.
In
Conclusion, upgrading as application from .NET Framework 4.x to .NET 8 offers
significant benefits in terms of performance, security
and modern development practices. The migration process may involve substantial
changes, such as upgrading dependencies, refactoring code
and ensuring compatibility with new APIs. However, these efforts are rewarded
with enhanced scalability, access to the latest .NET ecosystem features and
long-term support from Microsoft. The upgraded future proofs the application,
enabling it to leverage the power of modern tools, cloud-native architectures,
and improved development workflows, making it more resilient and adaptable to
future technological advancements.