-
Notifications
You must be signed in to change notification settings - Fork 316
Create documentation for backward compatibility supported scenarios #8809
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
jorgerangel-msft
merged 9 commits into
main
from
copilot/create-back-compat-documentation
Oct 27, 2025
Merged
Changes from all commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
eb52af3
Initial plan
Copilot 1ac17bd
Add comprehensive backward compatibility documentation
Copilot bd6df7d
Complete backward compatibility documentation
Copilot eb56853
Address PR feedback: simplify documentation and fix inaccuracies
Copilot dfaed6b
Revert removal of Scenario 3 and fix typo
Copilot cc1015c
Run prettier to fix markdown formatting
Copilot 9c2d956
Remove Scenario 3 (Model Factory Method Renamed)
Copilot 0bb1320
Remove scenario numbers from sub-headers
Copilot 01678e3
Add 'Scenario:' prefix back to sub-headers
Copilot File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
187 changes: 187 additions & 0 deletions
187
packages/http-client-csharp/generator/docs/backward-compatibility.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,187 @@ | ||
| # Backward Compatibility Support | ||
|
|
||
| ## Table of Contents | ||
|
|
||
| - [Overview](#overview) | ||
| - [How It Works](#how-it-works) | ||
| - [Supported Scenarios](#supported-scenarios) | ||
| - [Model Factory Methods](#model-factory-methods) | ||
| - [Model Properties](#model-properties) | ||
| - [API Version Enum](#api-version-enum) | ||
|
|
||
| ## Overview | ||
|
|
||
| The TypeSpec C# generator supports backward compatibility (back-compat) to ensure that existing client code continues to work when the service API evolves. This is achieved by comparing the current generated code against a previous contract (assembly) and automatically generating compatibility shims where needed. | ||
|
|
||
| The backward compatibility system uses the `LastContractView` mechanism to access information about previously generated types, methods, and properties from the last released version of the library. | ||
|
|
||
| ## How It Works | ||
|
|
||
| When generating code, the generator can optionally receive a compiled assembly from the previous version of the library (the "last contract"). The generator: | ||
|
|
||
| 1. Analyzes the current TypeSpec specification and generates types based on the current API | ||
| 2. Compares the generated types against the types in the last contract | ||
| 3. Automatically generates compatibility methods, properties, or enum members where differences are detected | ||
|
|
||
| ## Supported Scenarios | ||
|
|
||
| ### Model Factory Methods | ||
|
|
||
| Model factory methods are public static methods that enable creating model instances for mocking and testing purposes. The generator attempts to maintain backward compatibility for these methods across API changes. | ||
|
|
||
| #### Scenario: New Model Property Added | ||
|
|
||
| **Description:** When a new property is added to a model, the generator creates a backward-compatible factory method overload that excludes the new parameter. | ||
|
|
||
| **Example:** | ||
|
|
||
| Previous version had a model with three properties: | ||
|
|
||
| ```csharp | ||
| public static PublicModel1 PublicModel1( | ||
| string stringProp = default, | ||
| Thing modelProp = default, | ||
| IEnumerable<string> listProp = default) | ||
| ``` | ||
|
|
||
| Current version adds a new property (`dictProp`): | ||
|
|
||
| ```csharp | ||
| public static PublicModel1 PublicModel1( | ||
| string stringProp = default, | ||
| Thing modelProp = default, | ||
| IEnumerable<string> listProp = default, | ||
| IDictionary<string, string> dictProp = default) | ||
| ``` | ||
|
|
||
| **Generated Compatibility Method:** | ||
|
|
||
| ```csharp | ||
| [EditorBrowsable(EditorBrowsableState.Never)] | ||
| public static PublicModel1 PublicModel1( | ||
| string stringProp, | ||
| Thing modelProp, | ||
| IEnumerable<string> listProp) | ||
| { | ||
| return PublicModel1(stringProp, modelProp, listProp, dictProp: default); | ||
| } | ||
| ``` | ||
|
|
||
| **Key Points:** | ||
|
|
||
| - The old method signature is preserved | ||
| - It delegates to the new method with default value for the new parameter | ||
| - Parameters in the compatibility method have no default values to avoid ambiguous call sites | ||
| - The method is marked with `[EditorBrowsable(EditorBrowsableState.Never)]` to hide it from IntelliSense | ||
|
|
||
| #### Scenario: Parameter Ordering Changed | ||
|
|
||
| **Description:** When only the parameter ordering changes in a factory method (same parameters, different order), the generator replaces the current method with the previous method signature. | ||
|
|
||
| **Example:** | ||
|
|
||
| Previous version: | ||
|
|
||
| ```csharp | ||
| public static PublicModel1 PublicModel1( | ||
| Thing modelProp = default, | ||
| string stringProp = default, | ||
| IEnumerable<string> listProp = default, | ||
| IDictionary<string, string> dictProp = default) | ||
| ``` | ||
|
|
||
| Current version would generate different ordering: | ||
|
|
||
| ```csharp | ||
| public static PublicModel1 PublicModel1( | ||
| string stringProp = default, | ||
| Thing modelProp = default, | ||
| IEnumerable<string> listProp = default, | ||
| IDictionary<string, string> dictProp = default) | ||
| ``` | ||
|
|
||
| **Result:** The generator keeps the previous parameter ordering to maintain compatibility. | ||
|
|
||
| ### Model Properties | ||
|
|
||
| The generator attempts to maintain backward compatibility for model property types, particularly for collection types. | ||
|
|
||
| #### Scenario: Collection Property Type Changed | ||
|
|
||
| **Description:** When a property type changes from a read-only collection to a read-write collection (or vice versa), the generator attempts to preserve the previous property type to avoid breaking changes. | ||
|
|
||
| **Example:** | ||
|
|
||
| Previous version: | ||
|
|
||
| ```csharp | ||
| public IReadOnlyList<string> Items { get; } | ||
| ``` | ||
|
|
||
| Current TypeSpec would generate: | ||
|
|
||
| ```csharp | ||
| public IList<string> Items { get; set; } | ||
| ``` | ||
|
|
||
| **Result:** The generator detects the type mismatch and preserves the previous type: | ||
|
|
||
| ```csharp | ||
| public IReadOnlyList<string> Items { get; } | ||
| ``` | ||
|
|
||
| **Implementation Details:** | ||
|
|
||
| - The generator compares property types against the `LastContractView` | ||
| - For read-write lists and dictionaries, if the previous type was different, the previous type is retained | ||
| - A diagnostic message is logged: `"Changed property {ModelName}.{PropertyName} type to {LastContractType} to match last contract."` | ||
|
|
||
| ### API Version Enum | ||
|
|
||
| Service version enums maintain backward compatibility by preserving version values from previous releases. | ||
|
|
||
| #### Scenario: API Version Removed or Changed | ||
|
|
||
| **Description:** When API versions are removed or changed in the TypeSpec, the generator preserves previous version enum members to prevent breaking existing code that references them. | ||
|
|
||
| **Example:** | ||
|
|
||
| Previous version had enum members: | ||
|
|
||
| ```csharp | ||
| public enum ServiceVersion | ||
| { | ||
| V2021_10_01 = 1, | ||
| V2022_01_01 = 2, | ||
| V2022_06_01 = 3, | ||
| } | ||
| ``` | ||
|
|
||
| Current TypeSpec removes `V2021_10_01` and adds `V2023_01_01`: | ||
|
|
||
| ```csharp | ||
| public enum ServiceVersion | ||
| { | ||
| V2022_01_01 = 1, | ||
| V2022_06_01 = 2, | ||
| V2023_01_01 = 3, | ||
| } | ||
| ``` | ||
|
|
||
| **Generated Result:** | ||
|
|
||
| ```csharp | ||
| public enum ServiceVersion | ||
| { | ||
| V2021_10_01 = 1, // Preserved from previous version | ||
| V2022_01_01 = 2, | ||
| V2022_06_01 = 3, | ||
| V2023_01_01 = 4, | ||
| } | ||
| ``` | ||
|
|
||
| **Key Points:** | ||
|
|
||
| - Previous enum members are preserved even if removed from TypeSpec | ||
| - Enum values are re-indexed to maintain sequential ordering | ||
| - Version format and separator are detected from current versions and applied to previous versions | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.