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.
Developers are faced with longer build times, increased application size, and unnecessary network use.
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.
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.
When building for modern frameworks, we should remove polyfill packages known to be included with the runtime.
-
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
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.
-
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?
All data-plane packages add an explicit target for the oldest supported LTS framework, following the Azure.Core
TFM life cycle.
-
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.
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.
-
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?
- Issue: Azure SDK references packages that live in net6.0+
- Issue: Consider adding explicit net6.0 targets to all packges
- Issue: Remove unnecessary package references for net 6+ TFM
- Pull request: Add explicit net6.0 targets (Community contribution)
- Azure SDK support policy - Runtimes
-
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, whennet6.0
reaches end-of-life, Azure.Core will add a target fornet8.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.
This is an amazing write-up, @jsquire thank you so much for putting it together!
To refresh my memory of where we stand today, is it correct to say the following?
netstandard2.0;net461;net472;net6.0
netstandard2.0
only as described in https://github.com/Azure/azure-sdk-for-net/blob/main/eng/Directory.Build.Common.props#L122netstandard2.0;net7.0;net6.0
per https://github.com/Azure/azure-sdk-for-net/blob/main/eng/Directory.Build.Common.props#L128-L130And that @KrzysztofCwalina had proposed that we try out the idea of targeting only
netstandard2.0;net6.0
(ornetstandard2.0
and oldest LTS?) as a way of assessing whether we would go ahead and apply that to all our Azure/unbranded clients? I don't remember offhand what the assessment/action plan was on this piece.