From 11264c33826b36bf20f9d47d802fa6922605a615 Mon Sep 17 00:00:00 2001 From: Eric StJohn Date: Tue, 18 Nov 2025 12:06:45 -0800 Subject: [PATCH 1/6] Add guidance for .NET library dependency versions Added guidance for selecting .NET library dependency versions, including strategies, tradeoffs, and recommendations for library authors. --- .../library-guidance/dependencies-net.md | 152 ++++++++++++++++++ 1 file changed, 152 insertions(+) create mode 100644 docs/standard/library-guidance/dependencies-net.md diff --git a/docs/standard/library-guidance/dependencies-net.md b/docs/standard/library-guidance/dependencies-net.md new file mode 100644 index 0000000000000..85610a95c74f0 --- /dev/null +++ b/docs/standard/library-guidance/dependencies-net.md @@ -0,0 +1,152 @@ +# Guidance for Selecting .NET Library Dependency Versions + +When building libraries that target multiple .NET versions, choosing dependency versions has implications for compatibility, servicing, and ecosystem health. This document outlines the main strategies, their tradeoffs, and recommendations. + +--- + +## **Overview** + +Library authors face challenges when deciding which version of a .NET dependency to reference. Newer versions have more API and features, but may require a local redistribution increasing servicing responsibilities of the library and size of the application. The decision impacts: + +- **Friction of updates** on older runtimes +- **Engineering complexity** in maintaing the solution +- **Servicing complexity** in managing supported releases + +This guidance provides options, tradeoffs, and a decision matrix to help you choose the best approach. +✔️ DO be deliberate in choosing a dependency policy for your library +✔️ DO remove out of support target frameworks from your package. +✔️ CONSIDER choosing an approach that minimizes engineering costs and adjusting based on customer feedback +✔️ CONSIDER changing your approach through the lifecycle of your library +❌ DO NOT assume any policy is incorrect, all policies are technically sound and supported with different tradeoffs for consumption + +--- + +## **Strategies for Dependency Version Selection** + +### **Option 1: Latest Supported Versions** +Reference the latest supported version of the dependency across all target frameworks. For example: reference 10.0 packages on `netstandard2.0`, `net8.0`, `net9.0`, and `net10.0` +> **Note:** The lifetime of the latest Short-Term Support (STS) release now aligns with the latest LTS release, so choosing “latest” effectively means choosing the latest supported version regardless of STS or LTS designation. +> **Note:** Not all packages support targeting older frameworks in their latest version. For example: `Microsoft.AspNetCore.Authorization`. These packages must be excluded from this policy and must always follow option 2 when used. + +**Pros** +- Simplifies decision-making and aligns with stability expectations. +- Reduces complexity in dependency management. +- Encourages modernization and access to new features. + +**Cons** +- Older TFMs may require app-local deployments, increasing app size. +- Larger number of packages to update in comparison to relying on framework provided API. +- May create friction for customers who are not on the latest supported runtime. +- May prevent consumption in enviroments where dependencies are managed by a host application - eg: MSBuild, Visual Studio, Azure Functions V1. + +--- + +### **Option 2: TFM-Specific Versions** +Reference different dependency versions per Target Framework Moniker (TFM). Don't reference packages when the framework provides the API. For example: 8.0 packages on `net8.0`, 9.0 packages on `net9.0`, and so on. + +**Pros** +- Minimizes change for apps running on older runtimes. +- Minimizes app-local libraries app size on older runtimes. Uses runtime optimized library on older runtimes. + +**Cons** +- Reduced API available to library which may lead to more complex implementations (polyfills). Polyfills increase the total cost of engineering and servicing. +- Slows innovation in libraries. +- Greater complexity of infrastructure to maintain seperate dependency sets per target framework. Central packagge management and dependabot can be be configured to work with this, but it's challenging to get it right. + +--- + +### **Option 3: Branching** +Reference the latest supported version of the dependency across all target frameworks, but branch your library in sync with target frameworks. This is the same as Option 1, but each time a new framework is added, a new major version / branch is created to allow for updating the major version of dependencies and adding the new target framework. + +**Pros** +- Balances compatibility with flexibility for fast-moving areas. +- Encourages modernization and access to new features. +- Simplifies decision-making and aligns with stability expectations. +- Reduces complexity in dependency management. + +**Cons** +- Increased infrastructure cost to maintain concurrent branches. +- No new features for folks who want to stay on older dependencies. + +--- + +## **Decision Matrix** + +| Strategy | Update Friction | Engineering Cost | Servicing Cost | +|---------------------------------------|------------------|------------------|------------------| +| Latest Supported Versions (Option 1) | Moderate | Low | Moderate | +| TFM-Specific Versions (Option 2) | Low | High | Low / Moderate | +| Branching | Low | Low | High | + +--- + +## **Key Tradeoffs** + +- **Friction vs. Innovation:** Latest versions offer new features but have more friction for existing applications on older runtimes. +- **Engineering cost:** Configuring multiple dependency groups, dependency updates for those, and managing API gaps can all accumulate to slow down innovation in a fast moving library. +- **Servicing cost:** More package dependencies means more updates. More branches mean more concurrent builds that need to stay healthy. Additional code in the form of polyfills means more LOC with potential bugs. + +--- + +## **Recommended Guidance** + +- Option 1: Recommended for libraries moving fast and undergoing lots of innovation and feature development. Consumers are more likely to be on the latest framework. +- Option 2: Recommended for libraries that are stable and do not require new features. Could be a transition strategy when a library reaches maturity and stops taking large features/ +- Option 3: Recommended for libraries that are part of .NET or receive strong customer feedback that option 1 is limiting consumption or blocking adoption. Could be a transition strategy when Option 1 has too much friction. + +--- + +## **Examples** + +### Example 1: Latest Supported Version +```xml + + net8.0;net9.0;net10.0 + + + + +``` + +### Example 2: TFM-Specific Versions +```xml + + net8.0;net9.0;net10.0 + + + + + + +``` + +### Example 3: Branching +Branch: release/8.0 +```xml + + net6.0;net7.0;net8.0 + + + + +``` + +Branch: release/9.0 +```xml + + net8.0;net9.0 + + + + +``` + +Branch: release/10.0 +```xml + + net8.0;net9.0;net10.0 + + + + +``` From f4314cfc6cdfe99f34b7dba562cffe4221b3d319 Mon Sep 17 00:00:00 2001 From: Eric StJohn Date: Tue, 18 Nov 2025 12:32:05 -0800 Subject: [PATCH 2/6] Apply suggestions from code review --- docs/standard/library-guidance/dependencies-net.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/docs/standard/library-guidance/dependencies-net.md b/docs/standard/library-guidance/dependencies-net.md index 85610a95c74f0..523658fb3bb8c 100644 --- a/docs/standard/library-guidance/dependencies-net.md +++ b/docs/standard/library-guidance/dependencies-net.md @@ -1,4 +1,7 @@ -# Guidance for Selecting .NET Library Dependency Versions +--- +ai-usage: ai-assisted +--- +# Guidance for .NET library dependency versions When building libraries that target multiple .NET versions, choosing dependency versions has implications for compatibility, servicing, and ecosystem health. This document outlines the main strategies, their tradeoffs, and recommendations. @@ -23,12 +26,14 @@ This guidance provides options, tradeoffs, and a decision matrix to help you cho ## **Strategies for Dependency Version Selection** -### **Option 1: Latest Supported Versions** +### **Option 1: Latest supported versions** + Reference the latest supported version of the dependency across all target frameworks. For example: reference 10.0 packages on `netstandard2.0`, `net8.0`, `net9.0`, and `net10.0` > **Note:** The lifetime of the latest Short-Term Support (STS) release now aligns with the latest LTS release, so choosing “latest” effectively means choosing the latest supported version regardless of STS or LTS designation. > **Note:** Not all packages support targeting older frameworks in their latest version. For example: `Microsoft.AspNetCore.Authorization`. These packages must be excluded from this policy and must always follow option 2 when used. **Pros** + - Simplifies decision-making and aligns with stability expectations. - Reduces complexity in dependency management. - Encourages modernization and access to new features. From e441cc0c7725a785a830bbb898d70f5911f3d870 Mon Sep 17 00:00:00 2001 From: Eric StJohn Date: Tue, 18 Nov 2025 12:36:08 -0800 Subject: [PATCH 3/6] Apply feedback --- .../library-guidance/dependencies-net.md | 23 ++++++++++++------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/docs/standard/library-guidance/dependencies-net.md b/docs/standard/library-guidance/dependencies-net.md index 523658fb3bb8c..3d68a97ba45ce 100644 --- a/docs/standard/library-guidance/dependencies-net.md +++ b/docs/standard/library-guidance/dependencies-net.md @@ -39,21 +39,25 @@ Reference the latest supported version of the dependency across all target frame - Encourages modernization and access to new features. **Cons** -- Older TFMs may require app-local deployments, increasing app size. + +- Users targeting older TFMs will get the latest version's behavior, including any potential breaking changes. - Larger number of packages to update in comparison to relying on framework provided API. - May create friction for customers who are not on the latest supported runtime. - May prevent consumption in enviroments where dependencies are managed by a host application - eg: MSBuild, Visual Studio, Azure Functions V1. --- -### **Option 2: TFM-Specific Versions** +### **Option 2: TFM-specific versions** + Reference different dependency versions per Target Framework Moniker (TFM). Don't reference packages when the framework provides the API. For example: 8.0 packages on `net8.0`, 9.0 packages on `net9.0`, and so on. **Pros** + - Minimizes change for apps running on older runtimes. - Minimizes app-local libraries app size on older runtimes. Uses runtime optimized library on older runtimes. **Cons** + - Reduced API available to library which may lead to more complex implementations (polyfills). Polyfills increase the total cost of engineering and servicing. - Slows innovation in libraries. - Greater complexity of infrastructure to maintain seperate dependency sets per target framework. Central packagge management and dependabot can be be configured to work with this, but it's challenging to get it right. @@ -61,21 +65,24 @@ Reference different dependency versions per Target Framework Moniker (TFM). Don --- ### **Option 3: Branching** + Reference the latest supported version of the dependency across all target frameworks, but branch your library in sync with target frameworks. This is the same as Option 1, but each time a new framework is added, a new major version / branch is created to allow for updating the major version of dependencies and adding the new target framework. **Pros** + - Balances compatibility with flexibility for fast-moving areas. - Encourages modernization and access to new features. - Simplifies decision-making and aligns with stability expectations. - Reduces complexity in dependency management. **Cons** + - Increased infrastructure cost to maintain concurrent branches. - No new features for folks who want to stay on older dependencies. --- -## **Decision Matrix** +## **Decision matrix** | Strategy | Update Friction | Engineering Cost | Servicing Cost | |---------------------------------------|------------------|------------------|------------------| @@ -85,7 +92,7 @@ Reference the latest supported version of the dependency across all target frame --- -## **Key Tradeoffs** +## **Key tradeoffs** - **Friction vs. Innovation:** Latest versions offer new features but have more friction for existing applications on older runtimes. - **Engineering cost:** Configuring multiple dependency groups, dependency updates for those, and managing API gaps can all accumulate to slow down innovation in a fast moving library. @@ -93,17 +100,17 @@ Reference the latest supported version of the dependency across all target frame --- -## **Recommended Guidance** +## **Recommended guidance** - Option 1: Recommended for libraries moving fast and undergoing lots of innovation and feature development. Consumers are more likely to be on the latest framework. -- Option 2: Recommended for libraries that are stable and do not require new features. Could be a transition strategy when a library reaches maturity and stops taking large features/ +- Option 2: Recommended for libraries that are stable and do not require new features. Could be a transition strategy when a library reaches maturity and stops taking large features. - Option 3: Recommended for libraries that are part of .NET or receive strong customer feedback that option 1 is limiting consumption or blocking adoption. Could be a transition strategy when Option 1 has too much friction. --- ## **Examples** -### Example 1: Latest Supported Version +### Example 1: Latest supported version ```xml net8.0;net9.0;net10.0 @@ -113,7 +120,7 @@ Reference the latest supported version of the dependency across all target frame ``` -### Example 2: TFM-Specific Versions +### Example 2: TFM-specific versions ```xml net8.0;net9.0;net10.0 From f590816cd41c5e2e99ee5d9ebf59ededccdcd69a Mon Sep 17 00:00:00 2001 From: Eric StJohn Date: Tue, 18 Nov 2025 12:47:34 -0800 Subject: [PATCH 4/6] Update examples Show contrast to framework overlapping package and not. --- docs/standard/library-guidance/dependencies-net.md | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/docs/standard/library-guidance/dependencies-net.md b/docs/standard/library-guidance/dependencies-net.md index 3d68a97ba45ce..49e15512167d0 100644 --- a/docs/standard/library-guidance/dependencies-net.md +++ b/docs/standard/library-guidance/dependencies-net.md @@ -116,6 +116,7 @@ Reference the latest supported version of the dependency across all target frame net8.0;net9.0;net10.0 + ``` @@ -126,9 +127,10 @@ Reference the latest supported version of the dependency across all target frame net8.0;net9.0;net10.0 - - - + + + + ``` @@ -139,6 +141,7 @@ Branch: release/8.0 net6.0;net7.0;net8.0 + ``` @@ -149,6 +152,7 @@ Branch: release/9.0 net8.0;net9.0 + ``` @@ -159,6 +163,7 @@ Branch: release/10.0 net8.0;net9.0;net10.0 + ``` From f40abc0b6954e3f9cbc2b4d04c29fc4477d3bd9a Mon Sep 17 00:00:00 2001 From: Eric StJohn Date: Tue, 18 Nov 2025 13:52:17 -0800 Subject: [PATCH 5/6] Apply suggestions from code review --- docs/standard/library-guidance/dependencies-net.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/standard/library-guidance/dependencies-net.md b/docs/standard/library-guidance/dependencies-net.md index 49e15512167d0..3040050b78d8d 100644 --- a/docs/standard/library-guidance/dependencies-net.md +++ b/docs/standard/library-guidance/dependencies-net.md @@ -11,9 +11,9 @@ When building libraries that target multiple .NET versions, choosing dependency Library authors face challenges when deciding which version of a .NET dependency to reference. Newer versions have more API and features, but may require a local redistribution increasing servicing responsibilities of the library and size of the application. The decision impacts: -- **Friction of updates** on older runtimes -- **Engineering complexity** in maintaing the solution -- **Servicing complexity** in managing supported releases +- **Friction of updates** on older runtimes. Friction might be more changes than are desired by the library directly referenced. This could be the set of changes introduced between major versions of a dependency, the application size due to more app-local dependencies, the application startup performance due to using app-local dependencies without pre-generated native images, etc. +- **Engineering cost** in maintaining the solution. Here engineering cost is the cost of doing work in the latest codebase. This might be managing more complex projects with conditions, the total number of dependencies that need to be updated regularly, the cost of maintaining local source to account for missing features in older dependencies, dealing with ifdefs around inconsistent API/features across versions, etc. +- **Servicing cost** in managing supported releases. Here servicing cost is the ongoing cost in keeping release branches building, up to date, and compliant with all supported build tools. This guidance provides options, tradeoffs, and a decision matrix to help you choose the best approach. ✔️ DO be deliberate in choosing a dependency policy for your library @@ -28,7 +28,7 @@ This guidance provides options, tradeoffs, and a decision matrix to help you cho ### **Option 1: Latest supported versions** -Reference the latest supported version of the dependency across all target frameworks. For example: reference 10.0 packages on `netstandard2.0`, `net8.0`, `net9.0`, and `net10.0` +Reference the latest supported version of the dependency across all target frameworks. For example at the time this document was written when .NET 10.0 is the latest current release a project would reference reference 10.0 packages on `netstandard2.0`, `net8.0`, `net9.0`, and `net10.0`. > **Note:** The lifetime of the latest Short-Term Support (STS) release now aligns with the latest LTS release, so choosing “latest” effectively means choosing the latest supported version regardless of STS or LTS designation. > **Note:** Not all packages support targeting older frameworks in their latest version. For example: `Microsoft.AspNetCore.Authorization`. These packages must be excluded from this policy and must always follow option 2 when used. From c4431abdf5d796fde2291d70f60246548f9eca1e Mon Sep 17 00:00:00 2001 From: Eric StJohn Date: Tue, 18 Nov 2025 14:14:06 -0800 Subject: [PATCH 6/6] Apply suggestions from code review Co-authored-by: Stephen Toub --- docs/standard/library-guidance/dependencies-net.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/standard/library-guidance/dependencies-net.md b/docs/standard/library-guidance/dependencies-net.md index 3040050b78d8d..1edfff4102cab 100644 --- a/docs/standard/library-guidance/dependencies-net.md +++ b/docs/standard/library-guidance/dependencies-net.md @@ -36,7 +36,7 @@ Reference the latest supported version of the dependency across all target frame - Simplifies decision-making and aligns with stability expectations. - Reduces complexity in dependency management. -- Encourages modernization and access to new features. +- Encourages modernization and enables access to new features. **Cons**