Skip to content

docs(azure_core): add migration guide#4239

Open
ronniegeraghty wants to merge 2 commits intoAzure:mainfrom
ronniegeraghty:migration-guide/azure-core
Open

docs(azure_core): add migration guide#4239
ronniegeraghty wants to merge 2 commits intoAzure:mainfrom
ronniegeraghty:migration-guide/azure-core

Conversation

@ronniegeraghty
Copy link
Copy Markdown
Member

Migration guide: azure_core v0.22+ from ≤0.21

Closes #4230
Refs #4228

Placement decision

The migration guide is placed at sdk/core/azure_core/MIGRATION.md — the crate root, alongside the existing README.md and CHANGELOG.md. This follows the pattern used across Tier 1 Azure SDKs:

  • .NET: sdk/{service}/{Package}/MigrationGuide.md
  • Java: sdk/{service}/{artifact}/migration_guide.md
  • JS: sdk/{service}/{package}/MigrationGuide.md
  • Python: sdk/{service}/{package}/migration_guide.md

The Rust repo uses UPPER_CASE.md naming (matching README.md, CHANGELOG.md, TROUBLESHOOTING.md), so MIGRATION.md is the natural fit.

What this covers

  • Cargo.toml version bump and feature flag changes
  • Module restructuring (azure_core::http, re-exports from typespec_client_core)
  • TokenCredential trait signature change (scopes, options, AccessToken return type)
  • ClientOptions unified configuration pattern
  • Pipeline and HTTP client changes
  • Pager (replacing Pageable) and Poller new APIs
  • Custom policy implementation
  • Error handling with ErrorKind matching
  • Async runtime (tokio) requirement
  • Complete feature flags table

Reviewer checklist (Quality Bar)

  • Compilable code samples — every rust code block compiles against the current crate version
  • Side-by-side before/after — every common operation shows old code and new code
  • Cargo.toml diff — includes a clear diff showing old → new dependency lines
  • Authentication migration — covers TokenCredential trait changes
  • Error handling section — documents ErrorKind matching patterns
  • API docs link — links to docs.rs/azure_core
  • Examples directory link — links to in-repo examples/
  • Feature flags table — lists all Cargo features with descriptions
  • Async runtime callout — states tokio requirement with #[tokio::main] example
  • No dead links — all links resolve

Closes Azure#4230
Refs Azure#4228

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions github-actions Bot added the Azure.Core The azure_core crate label Apr 20, 2026
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@ronniegeraghty
Copy link
Copy Markdown
Member Author

Fixed an authoring error in the Cargo.toml diff blocks: marker characters had been doubled (`--`/`++`/`-+`/`+-`), which doesn't render as a real diff. Inside a diff fenced block the first column is the diff marker, so single -/+ is correct. The blocks now render as a clean removed/added pair.

@ronniegeraghty ronniegeraghty changed the title docs(azure_core): add migration guide for v0.22+ GA release docs(azure_core): add migration guide Apr 21, 2026
@ronniegeraghty ronniegeraghty marked this pull request as ready for review April 21, 2026 20:16
@ronniegeraghty ronniegeraghty requested a review from heaths as a code owner April 21, 2026 20:16
Copilot AI review requested due to automatic review settings April 21, 2026 20:16
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new MIGRATION.md at the azure_core crate root to document how to migrate from azure_core ≤0.21 to the current (v0.22+) API surface, including dependency/feature changes and updated patterns for credentials, pipelines, paging, polling, and error handling.

Changes:

  • Introduces a structured migration guide with side-by-side “before/after” guidance for key APIs.
  • Documents new module layout and configuration patterns (azure_core::http, ClientOptions, etc.).
  • Adds a feature-flag table and links to further resources.

Comment on lines +105 to +109
#[async_trait::async_trait]
pub trait TokenCredential: Send + Sync + std::fmt::Debug {
async fn get_token(
&self,
scopes: &[&str],
Copy link

Copilot AI Apr 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code block defines a new pub trait TokenCredential, which will conflict with the existing azure_core::credentials::TokenCredential if a reader copy/pastes it into their project (and therefore won’t compile). Consider presenting this as a signature excerpt (or marking the block rust,ignore/text) to make it clear it’s reference-only and to better align with the “compilable code samples” goal.

Copilot uses AI. Check for mistakes.
Comment on lines +196 to +209

// Service clients return a Pager. Iterate pages:
let pager: Pager<MyPageResponse> = client.list_items(None).await?;
let mut pages = pager.into_pages();
while let Some(page) = pages.next().await {
let page = page?;
// Process each page
}

// Or iterate items directly:
let pager: Pager<MyPageResponse> = client.list_items(None).await?;
let mut items = pager.into_items();
while let Some(item) = items.next().await {
let item = item?;
Copy link

Copilot AI Apr 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Pager example uses pages.next().await/items.next().await, but azure_core::http::pager iterators are TryStreams and the crate’s own docs use TryStreamExt::try_next() (not next). Updating the example to the supported iteration pattern will prevent broken migration guidance.

Suggested change
// Service clients return a Pager. Iterate pages:
let pager: Pager<MyPageResponse> = client.list_items(None).await?;
let mut pages = pager.into_pages();
while let Some(page) = pages.next().await {
let page = page?;
// Process each page
}
// Or iterate items directly:
let pager: Pager<MyPageResponse> = client.list_items(None).await?;
let mut items = pager.into_items();
while let Some(item) = items.next().await {
let item = item?;
use futures::TryStreamExt;
// Service clients return a Pager. Iterate pages:
let pager: Pager<MyPageResponse> = client.list_items(None).await?;
let mut pages = pager.into_pages();
while let Some(page) = pages.try_next().await? {
// Process each page
}
// Or iterate items directly:
let pager: Pager<MyPageResponse> = client.list_items(None).await?;
let mut items = pager.into_items();
while let Some(item) = items.try_next().await? {

Copilot uses AI. Check for mistakes.
Comment on lines +197 to +199
// Service clients return a Pager. Iterate pages:
let pager: Pager<MyPageResponse> = client.list_items(None).await?;
let mut pages = pager.into_pages();
Copy link

Copilot AI Apr 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These Pager/Poller snippets use placeholders like MyPageResponse/client/params, so they won’t compile as-is. Since the PR description emphasizes “compilable code samples”, consider either making the examples self-contained (real client type + minimal models) or marking them rust,ignore/rust,no_run so readers aren’t led to expect copy/paste compilation.

Copilot uses AI. Check for mistakes.

// Service clients return a Poller for LROs:
let poller: Poller<MyOperationResult> = client.begin_operation(params, None).await?;
let result = poller.wait().await?;
Copy link

Copilot AI Apr 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Poller doesn’t have a wait() method in azure_core::http::poller (it’s used via stream iteration and/or by awaiting the poller directly). As written, this example won’t compile and may mislead users; please update it to the supported Poller usage pattern.

Suggested change
let result = poller.wait().await?;
let result = poller.await?;

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right. This is old code. Don't just use the LLM trained old data. Use my heaths/akv-cli-rs repo for best practices.

Comment on lines +374 to +375
| `reqwest_rustls` | ✅ | Use [rustls](https://crates.io/crates/rustls) TLS backend (instead of OpenSSL) |
| `tokio` | ✅ | Enable [tokio](https://tokio.rs) async runtime support |
Copy link

Copilot AI Apr 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The feature table describes reqwest_rustls as “instead of OpenSSL”, but in this repo reqwest is built with default-features = false and TLS is only enabled via the reqwest_rustls feature. Without reqwest_rustls, consumers typically lose HTTPS/TLS support rather than automatically switching to an OpenSSL/native-tls backend.

Copilot uses AI. Check for mistakes.
Comment on lines +383 to +387
To switch from the default `rustls` TLS backend to OpenSSL:

```toml
azure_core = { version = "0.35", default-features = false, features = [
"reqwest",
Copy link

Copilot AI Apr 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This section says you can switch from rustls to OpenSSL by disabling default features and omitting reqwest_rustls, but azure_core currently doesn’t expose a feature that enables reqwest’s native-tls/OpenSSL backend. As a result, the shown feature set will generally compile without TLS and break HTTPS scenarios. Please adjust this guidance to reflect what’s actually supported (or introduce a dedicated feature if OpenSSL/native-tls is intended).

Copilot uses AI. Check for mistakes.
@heaths
Copy link
Copy Markdown
Member

heaths commented Apr 21, 2026

Note: after touch sdk/core/azure_core/MIGRATION.md, cargo package -lp azure_core --allow-dirty does show it gets included into the .crate file as well. May be useful to include it in the .crate that people download, sure.

Copy link
Copy Markdown
Member

@heaths heaths left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lots of changes needed. Overall topics covered are fine, but 1) this needs to be compiled as we do README.md and some of the code is outdated. Use my akv repo for context and best practices to better emit code if you're using an LLM, or at least see it for examples (I linked to specific ones).

@@ -0,0 +1,408 @@
# Migrating to azure_core v0.22+ from azure_core ≤0.21
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we just focus on GA? We're about to, and we don't support people on the prerelease so why even mention it?

Besides, we're well beyond 0.22 and a lot has changed. What we have in 0.34.0 is basically what we plan to ship with.


## Why Migrate

- **Stable API design**: The ≤0.21 versions were experimental and never reached a stable API. The v0.22+ rewrite follows the [Azure SDK Design Guidelines for Rust](https://azure.github.io/azure-sdk/rust_introduction.html) and targets GA stability.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I won't repeat this again, but just to drive the point home: 0.22 doesn't follow the guidelines. I updated the guidelines as we made new decisions. Hence, let's just focus on 1.0.0.

```diff
[dependencies]
- azure_core = "0.21"
+ azure_core = "0.35"
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not released, nor will it probably be. We shipped 0.34 and will next ship 1.0.


Authentication in `azure_core` is defined through the `TokenCredential` trait. The trait signature changed:

```rust
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You need to add no_run to these and include them in a tests/migration.rs as documented: https://azure.github.io/azure-sdk/rust_implementation.html#rust-client-tests-doc-examples. You don't necessarily have to use the include_markdown!() macro if you don't need it i.e., the code is self-contained. This makes sure it actually compiles. So the block would be:

```rust no_run
// ...
```

use azure_core::http::pager::{Pager, PageIterator, ItemIterator};

// Service clients return a Pager. Iterate pages:
let pager: Pager<MyPageResponse> = client.list_items(None).await?;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They shouldn't have to declare the type. That's just unnecessary and complicates the code they should write. See https://github.com/heaths/akv-cli-rs/blob/60e2bb1353728b67bbd7be81f7e6fe3ba818f805/examples/blobs.rs#L30-L36. Most of the time, the type is inferred.

// Service clients return a Pager. Iterate pages:
let pager: Pager<MyPageResponse> = client.list_items(None).await?;
let mut pages = pager.into_pages();
while let Some(page) = pages.next().await {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Better that they use try_next().await? and get the actual type. Less boiler plate and better DX. See my sample link above. If you're using an LLM to write this, I'd point it at that repo for sample context.


### Long-Running Operations with Poller

Long-running operations now use the `Poller` type instead of ad-hoc polling patterns.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use common terms for SEO after defining them.

Suggested change
Long-running operations now use the `Poller` type instead of ad-hoc polling patterns.
Long-running operations (LRO) now use the `Poller` type instead of ad-hoc polling patterns.

use azure_core::http::Poller;

// Service clients return a Poller for LROs:
let poller: Poller<MyOperationResult> = client.begin_operation(params, None).await?;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, don't need the type: https://github.com/heaths/akv-cli-rs/blob/60e2bb1353728b67bbd7be81f7e6fe3ba818f805/src/bin/akv/commands/certificate.rs#L335-L338. We should also be highlighting getting the final model. That's what the vast majority of people are interested in. Manual polling isn't something most people would have to or want to do. It's not really useful in most applications, especially since we don't know an end time i.e., can't report accurate progress.


// Service clients return a Poller for LROs:
let poller: Poller<MyOperationResult> = client.begin_operation(params, None).await?;
let result = poller.wait().await?;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right. This is old code. Don't just use the LLM trained old data. Use my heaths/akv-cli-rs repo for best practices.

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

Labels

Azure.Core The azure_core crate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Migration guide: azure_core (from azure_core ≤0.21.0 / azure_sdk_core)

3 participants