Skip to content

Instantly share code, notes, and snippets.

@jsquire
Last active February 1, 2024 18:45
Show Gist options
  • Save jsquire/d7a98cb42b9eaa9e3b942a7acd33a233 to your computer and use it in GitHub Desktop.
Save jsquire/d7a98cb42b9eaa9e3b942a7acd33a233 to your computer and use it in GitHub Desktop.
Azure SDK: Target frameworks and polyfill packages

Azure SDK: Target frameworks and polyfill packages

Problem statement

Developers using the Azure SDK on modern versions of .NET are required to download unnecessary packages that serve as polyfills for the legacy .NET framework and unsupported versions of .NET Core.

Impact

Developers are faced with longer build times, increased application size, and unnecessary network use.

Background

The Azure SDK packages target netstandard2.0 by default, and most packages have only that target. Because of the wide range of runtimes that netstandard2.0 supports, there is an inconsistency with what features and system namespaces are available with each platform.

To ensure consistency across targets, polyfill packages are available on NuGet for these system features supported by netstandard2.0. On runtimes where the features are missing, the polyfill packages provide them. On runtimes where the features are natively available, the polyfill packages do nothing. This was helpful in the early days of .NET Core, where the runtime feature set was undergoing many changes. Starting with .NET 6, features that the Azure SDK packages rely on, such as System.Text.Json are available natively in the runtime.

With runtimes earlier than .NET 6 reaching end-of-life, the polyfill packages are needed only on the legacy .NET Framework. Developers using the Azure SDK on modern versions of .NET are required to download these unnecessary packages, leading to longer build times, increased application size, and unnecessary network use.

Important Note

The proposals made herein are NOT intended to represent a "this or that" scenario. They independent of one another, though complimentary. It is intended that both be evaluated and enacted.

Proposal: Remove polyfill packages for modern runtimes

When building for modern frameworks, we should remove polyfill packages known to be included with the runtime.

Approach

  • Extend the Azure SDK repository engineering system to remove known-safe packages when building for explicit target frameworks.

  • The initial set of packages would be a constrained set known to be safe for net6.0+, to minimize potential issues.

  • As additional target frameworks are added over time and other packages are proven to be safe, the trimming can be extended.

Proposed initial packages:

  • System.Text.Json
  • Microsoft.Bcl.AsyncInterfaces
  • System.Buffers
  • System.Memory
  • System.Numerics.Vectors
  • System.Threading.Tasks.Extensions
  • System.Diagnostics.DiagnosticSource

Impact

While most of the Azure SDK packages target only netstandard2.0, some were extended with additional target framework. Notable examples are Azure.Core, Azure.Identity, the KeyVault packages, and the Storage packages which target net6.0 explicitly. Though these are currently in the minority, they are among our most used packages and would constitute a non-trivial savings.

Questions and challenges

  • Would our existing test matrix be enough to prove that any implementation is safe, or would there need to be additional work done?

  • Is there an effective way to automate detection of what package references are safe that we can use for identifying opportunities for new packages?

Proposal: Add an explicit target for the LTS framework to client libraries

All data-plane packages add an explicit target for the oldest supported LTS framework, following the Azure.Core TFM life cycle.

Approach

  • Extend the Azure SDK repository engineering system to add net6.0 into the $RequiredTargetFrameworks used by data-plane packages.

  • Manage the target framework according to the Azure.Core TFM life cycle, allowing it to stay relevant over time.

  • Keep the existing netstandard2.0 target, allowing libraries to run on the legacy .NET Framework and continue to run on unsupported runtimes when we move the LTS target forward.

  • If a specific package has a need to opt-out, they can do so via local override in the project or Directory.Build.props file.

Impact

The explicit target for the modern .NET LTS version allows the proposed package trimming to be applied to all of our data-plane libraries, with the same impact/benefits previously discussed. This also has the benefit of allowing libraries to take advantage of modern framework features or implementations on a conditional basis where it would improve the developer experience.

Questions and challenges

  • Should we apply this to the management libraries as well?

  • Does this provide an opportunity for improvements in generated code, due to the ability to conditionally target up-level features?

References and resources

Appendix: Azure.Core TFM life cycle

  • Azure.Core will always target the oldest supported LTS of the modern .NET.

  • Azure.Core may also target additional frameworks as features require, subject to architect approval and Core team review.

  • When a target framework reaches end-of-life, Azure.Core will add a target for the oldest supported sibling. For modern .NET, this will always be the "next" LTS.
    For example, when net6.0 reaches end-of-life, Azure.Core will add a target for net8.0, if not already present.

  • When a target framework reaches end-of-life, Azure.Core will continue to target it for 6 months after its retirement date. After that time it will be removed from the targets.

  • The Azure SDK support policy takes precedence; even if Azure.Core targets an unsupported framework, the Azure SDK team does not offer support for running on that framework.

@jsquire
Copy link
Author

jsquire commented Feb 1, 2024

@annelo-msft: We talked about adding just netstandard2.0;net6.0 for extending to the client libraries. Core is special in that it has all the HTTP infrastructure. The .NET Framework targets (nt461, net472) for Core are needed because they allow us to take advantage of runtime features that improve performance via #if conditionals.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment