azure-sdk-for-rust / cosmos
AGENTS.md887 starsChanged 29 days agoMITView on GitHub
This file contains coding guidelines and architectural patterns specific to the Azure Cosmos DB SDK for Rust (sdk/cosmos). - Start with docs/README.md to inventory Cosmos project documentation. - Read docs/Project.md and docs/Architecture.md for project and architecture context, then use the indexes in docs/README.md to find relevant specs, ADRs, and reports. - Do not use relative links in docs/. Keep useful target paths as plain code instead, for example, specs/0017-patch-handler.md. Use absolute links from crate READMEs. - Do not link from…
Where files are headingNew AGENTS.md files have more rules about keeping secrets out of the code, and fewer about code style.
Of the AGENTS.md files we read, the 31,774 first seen in the last six months, against the older ones: the kind of rule that gained most share, and the kind that lost most. About files like this in general, not this one, and an estimate, since a kind is a pattern in the words.
In this repository22 other files it gives its agents.
- AGENTS.mdAGENTS.md
- eng/tools/generate_api/AGENTS.mdAGENTS.md
- samples/AGENTS.mdAGENTS.md
- sdk/core/AGENTS.mdAGENTS.md
- sdk/cosmos/azure_data_cosmos_emulator/AGENTS.mdAGENTS.md
- sdk/cosmos/docs/AGENTS.mdAGENTS.md
- agentic-workflowsSkill
- analyze-codeSkill
- check-spellingSkill
- create-crateSkill
- create-hotfixSkill
- create-perf-testSkill
- create-recorded-testSkill
- document-cratesSkill
- export-apiSkill
- lint-markdownSkill
- prepare-releaseSkill
- review-doc-commentsSkill
- update-emitterSkill
- update-rustSkill
- cosmos-design-structSkill
- cosmos-pre-commit-validationSkill
The file
Show its rules and text
We split this AGENTS.md into the rules it gives an agent
43 rules. About 0 are common, 22 are distinctive, and 21 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈22
Distinctive · estimate
This author's own choices. Worth a look.
21
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 13
Start with docs/README.md to inventory Cosmos project documentation.
- Very specific: line 19
Do not use relative links in docs/.
- Distinctive: line 22
Do not link from Markdown to Rust source files.
- Distinctive: line 24
Do not load the entire documentation set when only a few indexed documents are relevant.
- Distinctive: line 29
Follow the Azure SDK Design Guidelines for Rust as the primary reference for API design, error handling, async patterns, and module …
- Distinctive: line 33
Follow the Data-Oriented Programming in Rust principles from https://analogrelay.github.io/dop-in-rust:
- Distinctive: line 35
Separate data from behavior: Use plain structs for data and separate modules/traits for operations
- Distinctive: line 36
Model data with enums and structs: Prefer algebraic data types to represent domain concepts
- Distinctive: line 37
Treat data as immutable: Use owned types and transformations rather than mutation where practical
- Distinctive: line 42
Always implement standard Rust traits instead of creating custom public methods when equivalent functionality exists:
- Distinctive: line 71
If you need a non-fallible parse internally, create a private helper method and have the trait implementation call it.
- Very specific: line 79
Use usize only for memory-related numbers (addresses, array indices, memory/buffer sizes) — including at FFI boundaries when the number …
- Distinctive: line 80
Use a specific-size integer when it's necessary for a specific, well-understood memory optimization (e.g., packing a struct), or to match …
- Very specific: line 81
Use u16 or u32 for domain numbers when they will clearly fit — not just "u16/u32 should be enough", but "more than 65,535/4 billion of …
- Very specific: line 82
Use u64 for domain numbers when the expected range isn't clear, or is known to potentially exceed 32 bits.
- Distinctive: line 88
Use the builder pattern for complex requests (queries, operations)
- Distinctive: line 90
Prefer method chaining with clear, descriptive names
- Very specific: line 94
Use azure_data_cosmos::Result<T> (SDK) or azure_data_cosmos_driver::error::Result<T> (driver) for all fallible operations — both alias …
- Distinctive: line 104
All I/O operations must be async
- Distinctive: line 114
Use type state pattern for operations that require specific resource context
- Very specific: line 268
Critical Requirement: All types in models/ must be serializable and used for wire format.
- Distinctive: line 385
Use builders for complex client construction:
- Very specific: line 421
Use #[recorded::test] for integration tests that interact with Cosmos DB
- Distinctive: line 426
AVOID writing tests for trivial or obvious functionality, like derived trait behavior, constructor behavior that just assigns fields, or …
- Very specific: line 433
Use assert! for boolean assertions instead of assert_eq!(value, true/false)
- Very specific: line 434
Prefer the strongest assertion that matches the contract: Use assert_eq!(vec, expected_vec) instead of assert!(vec.contains(x)) — the …
- Very specific: line 450
This is the most common CI failure: - always run cargo fmt after making ANY code changes, including:
- Very specific: line 477
When to suppress warnings: Only use #[allow(clippy::...)] when:
- Distinctive: line 483
Always add a comment explaining why the warning is allowed.
- Very specific: line 487
Always run cargo fmt on generated or modified Rust code before considering the task complete.
- Very specific: line 488
When editing existing files, ensure the changes conform to rustfmt standards.
- Very specific: line 490
When editing non-Rust files (JSON, YAML, TOML, etc.), respect the root .editorconfig settings.
- Very specific: line 494
Run cspell to check for spelling errors in changed files (requires Node.js):
- Very specific: line 504
Cosmos-specific words not in the dictionary: add them to ignoreWords in sdk/cosmos/.cspell.json.
- Distinctive: line 509
Before considering any task complete, run the following checks in order on all modified crates:
- Distinctive: line 517
All documentation warnings must be resolved before completing the task
- Distinctive: line 540
When documenting factory methods or APIs, ensure the linked method names match the actual implementation
- Very specific: line 541
Use the exact method name (e.g., method_by_name not just method) in doc links like [StructName::method_by_name]
- Very specific: line 572
Use Cow<'_, str> for fields that might be borrowed or owned
- Distinctive: line 578
Every public API should document:
- Very specific: line 599
Put detailed rationale in docs/.
- Distinctive: line 604
Keep comments short and dense.
- Very specific: line 616
When a rationale really needs paragraphs, it belongs in docs/, not in the source.
≈ “Common” is an estimate: at least 1 in 5 of the 40,246 AGENTS.md files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Cosmos SDK Instructions
This file contains coding guidelines and architectural patterns specific to the Azure Cosmos DB SDK for Rust (sdk/cosmos).
Core Principles
Documentation navigation
- Start with
docs/README.mdto inventory Cosmos project documentation. - Read
docs/Project.mdanddocs/Architecture.mdfor project and architecture context, then use the indexes indocs/README.mdto find relevant specs, ADRs, and reports. - Do not use relative links in
docs/. Keep useful target paths as plain code instead, for example,specs/0017-patch-handler.md. Use absolute links from crate READMEs. - Do not link from Markdown to Rust source files. Those implementation links atrophy as code moves.
- Do not load the entire documentation set when only a few indexed documents are relevant.
Design guidelines
- Follow the Azure SDK Design Guidelines for Rust as the primary reference for API design, error handling, async patterns, and module organization unless they conflict with Cosmos-specific requirements outlined below or in other
cosmos.*.instructions.mdfiles.
Data-Oriented Programming (DOP)
Follow the Data-Oriented Programming in Rust principles from https://analogrelay.github.io/dop-in-rust:
- Separate data from behavior: Use plain structs for data and separate modules/traits for operations
- Model data with enums and structs: Prefer algebraic data types to represent domain concepts
- Treat data as immutable: Use owned types and transformations rather than mutation where practical
- Validate at boundaries: Parse/validate data when it enters the system, trust it internally
Prefer Standard Traits Over Custom Methods
Always implement standard Rust traits instead of creating custom public methods when equivalent functionality exists:
- Parsing strings: Implement
std::str::FromStrinstead ofpub fn from_str(s: &str) - Converting types: Implement
From<T>/Into<T>instead ofpub fn to_x()orpub fn from_x() - Default values: Implement
Defaultinstead ofpub fn new_default() - Iteration: Implement
Iterator/IntoIteratorinstead ofpub fn get_items() - Display formatting: Implement
Display/Debuginstead ofpub fn to_string() - Cloning: Implement
Cloneinstead ofpub fn copy() - Comparison: Implement
PartialEq/Eq/PartialOrd/Ordinstead of custom comparison methods
Rationale: Standard traits enable generic programming, work with Rust's type system, and follow Rust idioms.
Example:
// ❌ BAD: Custom public method duplicates trait functionality
impl MyType {
pub fn from_str(s: &str) -> Option<Self> { /* ... */ }
}
// ✅ GOOD: Implement the standard trait
impl std::str::FromStr for MyType {
type Err = azure_data_cosmos::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> { /* ... */ }
}
// Users can then use: "value".parse::<MyType>()If you need a non-fallible parse internally, create a private helper method and have the trait implementation call it.
Encode domain semantics in trait impls: When a type has a domain-specific comparison rule, ordering, or equality definition, implement it directly in the type's Ord/PartialOrd/Eq traits rather than creating standalone helper functions that callers must remember to use. This prevents inconsistency — every comparison automatically gets the right semantics, and you can't accidentally use the wrong one.
Integer Type Selection
Choose integer types deliberately; don't default to usize or i32 out of habit:
- Use
usizeonly for memory-related numbers (addresses, array indices, memory/buffer sizes) — including at FFI boundaries when the number refers to memory. - Use a specific-size integer when it's necessary for a specific, well-understood memory optimization (e.g., packing a struct), or to match an FFI/ABI requirement.
- Use
u16oru32for domain numbers when they will clearly fit — not just "u16/u32should be enough", but "more than 65,535/4 billion of these would be ridiculous" (e.g., retry counts, byte offsets within a single document). - Use
u64for domain numbers when the expected range isn't clear, or is known to potentially exceed 32 bits.
Cosmos-Specific Patterns
Request Building
- Use the builder pattern for complex requests (queries, operations)
- Builders should be type-safe, guiding users through required parameters
- Prefer method chaining with clear, descriptive names
Error Handling
- Use
azure_data_cosmos::Result<T>(SDK) orazure_data_cosmos_driver::error::Result<T>(driver) for all fallible operations — both aliasResult<T, crate::Error>over the typed Cosmos error. - Prefer returning
Result::Errover panicking in public methods whose inputs could originate from user-constructed types (even indirectly). Callers can then decide whether to propagate, log, or handle — rather than crashing their application. Useassert!/panic!only for true invariant violations that indicate programmer error in internal code. - Cosmos-specific errors should provide:
- HTTP status code
- Request charge (RU/s) when available
- Activity ID for tracing
- Sub-status code for detailed error classification
Async/Await
- All I/O operations must be async
- Async runtime
- For
sdk/cosmos/azure_data_cosmosandsdk/cosmos/azure_data_cosmos_driverkeep using the same async runtime abstractions asazure_coredoes.
- For
- Streaming (use of
futures::Stream)- There is no need to consider streaming for payloads of individual requests/responses because the Cosmos DB service enforces rather strict limits on request and response payload size (max. 4 MB - only via config overrides extendable to 16 MB per response payload)
- There is a need for pagination for example for query or change feed results (could return multiple pages - each page created by one or multiple responses) - so
futures::Streammight be used there to achieve pagination - but for transport, assuming buffered transport is sufficient.
Resource Management
- Model Cosmos resources (databases, containers, items) as distinct types
- Use type state pattern for operations that require specific resource context
- Lifetime parameters should be avoided in public APIs unless absolutely necessary
Partitioning
- Make partition key handling explicit and type-safe
- Provide clear guidance on partition key selection in documentation
- Support both single and hierarchical partition keys
Query API
- Provide a fluent, type-safe query builder
- Support both SQL API and parameterized queries
- Make continuation tokens transparent but accessible for pagination control
Code Organization
sdk/cosmos/azure_data_cosmos/
├── src/
│ ├── clients/ # Client types (CosmosClient, Database, Container)
│ ├── models/ # Data models (separate from behavior)
│ ├── operations/ # Operation implementations
│ ├── options/ # Request/Operation option builders
│ ├── query/ # Query builder and execution
│ └── responses/ # Response types with metadataAzure Cosmos DB Driver Architecture
Multi-Crate Strategy
The Cosmos DB implementation is split across three crates with distinct purposes and support models:
azure_data_cosmos_driver (Core Implementation Layer)
- Purpose: Core Cosmos DB protocol implementation, transport, routing, and retry handling
- API: Public API available for advanced scenarios and cross-language SDK implementation
- Support: Community/GitHub support only (no 24x7 Microsoft Support)
- Versioning: Strict semantic versioning; can move to new major versions more frequently
- Consumers:
azure_data_cosmos(Rust SDK), potentially other language SDKs
azure_data_cosmos (Primary Rust SDK)
- Purpose: Idiomatic Rust API for Cosmos DB with type-safe serialization
- API: Full public SDK following Azure SDK Design Guidelines
- Support: Full 24x7 Microsoft Support
- Versioning: Strict semantic versioning; must maintain backward compatibility for years per major version
- Dependency: Uses
azure_data_cosmos_driverinternally
Versioning Strategy: No Model Sharing
Avoid sharing models between crates to prevent version lock-in:
- ❌ Do NOT create
azure_data_cosmos_commonfor shared models - ❌ Do NOT directly expose
azure_data_cosmos_drivermodels inazure_data_cosmospublic API - ✅ DO keep models separate in each crate
- ✅ DO use adapter functions/traits for conversion between crate models
- ✅ DO duplicate model definitions when needed to maintain independent versioning
Rationale: azure_data_cosmos must maintain multi-year backward compatibility. If it exposes azure_data_cosmos_driver models directly, the driver cannot make breaking changes without forcing an SDK major version bump.
Exception: Truly stable types unlikely to change (e.g., fundamental constants) can be shared via workspace dependencies.
Schema-Agnostic Data Plane Principle
Critical Architectural Rule: azure_data_cosmos_driver is completely ignorant of document/item schemas.
Rationale:
- Cosmos DB is a schemaless database - item structure is application-defined
- Driver must support multiple language SDKs (Rust, Java, .NET, Python) each with native serialization patterns
- Mapping items to application types is a core capability that must be handled natively in the consuming SDK
Where the wire encoding sits: schema-ignorance does not extend to the Cosmos binary
wire encoding. That encoding is negotiated with the service by the driver, on a header the
driver owns (x-ms-cosmos-supported-serialization-formats), and the query pipeline cannot
do its job without understanding it — nodes such as the ordered merge and SkipTake split
a feed envelope apart, re-order documents across pages, and reassemble a new envelope. That
is impossible to do over opaque bytes. So the boundary is:
- Driver owns the envelope and the wire encoding: negotiating it, parsing feed pages, and emitting pages in the negotiated encoding.
- SDK owns the item: turning a document's bytes into an application type and back.
The driver never inspects or depends on the fields inside a document.
Driver Data Plane Contract:
// Driver APIs work with raw bytes (buffered, ≤16MB payload limit)
pub async fn create_item(
&self,
partition_key: &PartitionKey,
item_body: &[u8], // UTF-8 JSON or Cosmos binary encoding
options: &CreateItemOptions,
) -> Result<ItemResponse> {
// Driver handles transport, routing, retries
// Driver is IGNORANT of item schema
}
pub struct ItemResponse {
pub body: Vec<u8>, // Raw response body (buffered)
pub diagnostics: DiagnosticsContext,
pub charge: f64,
// ... metadata, no typed item
}SDK Layer Responsibility (azure_data_cosmos):
// SDK provides type-safe serialization on top of driver
pub async fn create_item<T: Serialize>(
&self,
item: &T,
options: &CreateItemOptions,
) -> azure_data_cosmos::Result<ItemResponse<T>>
where
T: for<'de> Deserialize<'de>,
{
let body = serde_json::to_vec(item)?; // SDK handles serialization
let raw_response = self.driver.create_item(pk, &body, options).await?;
let typed_item: T = serde_json::from_slice(&raw_response.body)?;
// SDK provides typed response
}Content Encoding: a received body is recognized as UTF-8 JSON or Cosmos binary from its
first byte, so decoding is transparent to the API. Do not extend that sniffing to decide
what a pipeline node emits — the emitted encoding is a property of the operation
(CosmosOperation::emits_binary_payload), not of the bytes that happened to arrive. Deriving
emission from received bytes lets two nodes in the same pipeline disagree when the service
answers in a format other than the one that was requested.
Note emits_binary_payload rather than negotiates_binary_response: the former is what the
caller receives, the latter is what the wire carries. They diverge under
BinaryEncodingOptions::request_text_response, where the wire stays binary but the driver
hands back text — so a node keyed off the wire format would re-encode items that
execute_plan immediately decodes again.
Implications:
- Driver
models/contains zero item/document types - Driver APIs accept
&[u8]for request bodies - Driver APIs return
Vec<u8>for response bodies (buffered; Cosmos enforces 4MB default, 16MB absolute max) - Each consuming SDK implements its own serialization strategy
Module Organization Patterns
What Qualifies as a "Model"
Definition: A serializable data structure representing values transmitted over the wire to or from the Cosmos DB service (request/response body or header values).
Critical Requirement: All types in models/ must be serializable and used for wire format. Configuration types that control SDK behavior belong in options/.
Include in models/ module:
- ✅ Management/metadata resource representations: Account properties, offers, database properties, container properties, partition key ranges (NOT data plane documents/items)
- ✅ Supporting structures: Types that are properties of models (IndexingPolicy, PartitionKeyDefinition, VectorEmbeddingPolicy, consistency levels, indexing modes, connection modes)
- ✅ Operation-specific envelopes: Structures created for operation support (TransactionalBatch, PatchInstructions, BulkOperations)
- ✅ Header/wire values: Types serialized into request headers or body (ETag, SessionToken, PartitionKey, TriggerReference)
Exclude from models/ (use dedicated modules):
- ❌ Configuration/options types →
options/module (e.g., Region for excluded regions, TriggerOptions for which triggers to use, ThroughputControlOptions) - ❌ Client types →
clients/module - ❌ Error types →
error.rsor crate root - ❌ Diagnostics/telemetry types →
diagnostics/module - ❌ Pipeline/routing/internal types → separate modules with
pub(crate)visibility - ❌ Data plane items/documents → NOT in driver (see Schema-Agnostic Data Plane above)
- ❌ Builders →
builders/submodule or inline inmodels/builders/
Model Characteristics:
/// Models should be serializable and debuggable
#[derive(Clone, Default, Debug, Deserialize, Serialize, PartialEq, Eq)]
#[non_exhaustive] // For forward compatibility
pub struct ContainerProperties {
pub id: Cow<'static, str>,
pub partition_key: PartitionKeyDefinition,
#[serde(skip_serializing_if = "Option::is_none")]
pub indexing_policy: Option<IndexingPolicy>,
}
impl ContainerProperties {
// Models CAN have methods (builders, accessors, conversions)
pub fn builder() -> ContainerPropertiesBuilder {
ContainerPropertiesBuilder::default()
}
}Diagnostics Module Pattern
Cosmos DB provides rich diagnostic information beyond standard Azure SDK telemetry. This operational metadata lives in a dedicated diagnostics/ module:
Structure:
src/diagnostics/
├── mod.rs # Public exports
├── context.rs # DiagnosticsContext (Rust equivalent of CosmosDiagnosticsContext)
├── metrics.rs # Request metrics, RU consumption, timings
└── regions.rs # Regional endpoint contact infoRationale: Diagnostics types are NOT service resources - they are SDK operational metadata. Keeping them separate from models/ maintains clean separation between:
- Models: What Cosmos DB service defines (resources, policies, envelopes)
- Diagnostics: What the SDK tracks about operations (timings, retries, endpoints contacted)
Example:
/// Context containing diagnostic information about a Cosmos DB operation
pub struct DiagnosticsContext {
pub request_charge: f64,
pub regions_contacted: Vec<RegionContactInfo>,
pub retry_attempts: Vec<RetryInfo>,
pub request_timeline: RequestTimeline,
}
// Attached to operation results, not stored in models
pub struct ItemResponse {
pub body: Vec<u8>, // Raw bytes, schema-agnostic
pub diagnostics: DiagnosticsContext,
}Options Module Pattern
Configuration types for individual operations/requests follow Azure SDK conventions in dedicated options/ module:
Structure:
src/options/
├── mod.rs # Public exports
├── client.rs # {Crate}Options (client-level config)
├── connection.rs # ConnectionPoolOptions, environment config
└── operations.rs # {Operation}Options (per-request config)Naming Convention:
- Client-level:
DriverOptions,CosmosClientOptions - Operation-level:
{Operation}{Action}Options(e.g.,ExecuteQueryOptions,CreateContainerOptions) - Domain-specific: Descriptive names (
ConnectionPoolOptions,RetryOptions)
Hierarchy (Environment → Client → Operation):
// Client options nest azure_core types
pub struct DriverOptions {
pub client_options: ClientOptions, // From azure_core
pub connection_pool: ConnectionPoolOptions,
pub default_consistency: Option<ConsistencyLevel>,
}
// Operation options can override client defaults
pub struct ExecuteQueryOptions<'a> {
pub method_options: ClientMethodOptions<'a>, // For tracing context
pub consistency: Option<ConsistencyLevel>, // Override client default
pub max_item_count: Option<i32>,
}Builder Pattern
Use builders for complex client construction:
pub mod builders {
pub struct DriverBuilder {
connection_pool: Option<ConnectionPoolOptions>,
retry: Option<RetryOptions>,
}
impl DriverBuilder {
pub fn new() -> Self { Self::default() }
pub fn with_connection_pool(mut self, opts: ConnectionPoolOptions) -> Self {
self.connection_pool = Some(opts);
self
}
pub async fn build(
self,
endpoint: impl Into<String>,
credential: impl TokenCredential,
options: DriverOptions,
) -> azure_data_cosmos_driver::error::Result<Driver> {
// ... construction logic
}
}
}
// Usage:
let driver = Driver::builder()
.with_connection_pool(pool_opts)
.build(endpoint, credential, opts).await?;Testing
- Use
#[recorded::test]for integration tests that interact with Cosmos DB - Unit tests should focus on:
- Request building logic
- Response parsing
- Error handling paths
- AVOID writing tests for trivial or obvious functionality, like derived trait behavior, constructor behavior that just assigns fields, or simple getters/setters.
- Integration tests should cover:
- CRUD operations
- Query scenarios
- Partition key handling
- Continuation token pagination
- Tests should use standard trait implementations (e.g.,
.parse::<T>()instead of callingT::from_str()directly) - Use
assert!for boolean assertions instead ofassert_eq!(value, true/false) - Prefer the strongest assertion that matches the contract: Use
assert_eq!(vec, expected_vec)instead ofassert!(vec.contains(x))— the former catches both missing and extra elements. In general, assert exact values over partial properties.
Code Quality and Validation
Automatic Formatting (CRITICAL)
ALWAYS run cargo fmt FIRST before any other validation steps. The CI pipeline will fail if code is not properly formatted:
# Format the modified crate
cargo fmt -p <crate-name>
# Check if formatting is correct without modifying files
cargo fmt -p <crate-name> -- --checkThis is the most common CI failure - always run cargo fmt after making ANY code changes, including:
- Adding new code
- Modifying existing code
- Moving code between files
- Refactoring
Automatic Clippy Validation
ALWAYS run cargo clippy after making code changes to catch common issues and ensure code quality:
# Run clippy for the modified crate
cargo clippy -p <crate-name> --all-features --all-targets
# Run clippy for the entire workspace (use sparingly)
cargo clippy --workspace --all-features --all-targets --keep-going --no-depsFix all clippy warnings before considering code complete. Common patterns to watch for:
- ✅ Implement standard traits instead of custom methods (
clippy::should_implement_trait) - ✅ Use
assert!(value)instead ofassert_eq!(value, true) - ✅ Use
assert!(!value)instead ofassert_eq!(value, false) - ✅ Avoid unnecessary clones, allocations, or copies
- ✅ Use idiomatic patterns (e.g.,
if let, pattern matching)
When to suppress warnings: Only use #[allow(clippy::...)] when:
- The warning is a false positive
- Following the suggestion would make code less clear
- The pattern is required for FFI or external constraints
Always add a comment explaining why the warning is allowed.
Code Formatting
- Always run
cargo fmton generated or modified Rust code before considering the task complete. - When editing existing files, ensure the changes conform to
rustfmtstandards. - The CI pipeline will reject any code that is not properly formatted.
- When editing non-Rust files (JSON, YAML, TOML, etc.), respect the root
.editorconfigsettings. For example, JSON files use 2-space indentation. Do not reformat entire files when making small changes — preserve the existing formatting.
Spell Checking
Run cspell to check for spelling errors in changed files (requires Node.js):
# Check spelling in files changed compared to upstream/main
pwsh eng/common/scripts/check-spelling-in-changed-files.ps1 -TargetCommittish "upstream/main"To fix spelling errors, either correct typos in source code or add legitimate terms to the appropriate spelling configuration:
- Crate names:
eng/dict/crates.txt - Cosmos-specific words not in the dictionary: add them to
ignoreWordsinsdk/cosmos/.cspell.json. Do not add Cosmos-specific words to the global.vscode/cspell.json; changing the global file triggers reviews from the central SDK reviewers. - Terms shared across SDKs:
.vscode/cspell.json
Pre-Completion Validation Checklist
Before considering any task complete, run the following checks in order on all modified crates:
- Format check (MUST BE FIRST):
cargo fmt -p <crate-name>- This is the #1 cause of CI failures - always run this first!
- Build check:
cargo build -p <crate-name> - Clippy lint check:
cargo clippy -p <crate-name> --all-features --all-targets - Documentation check:
cargo doc -p <crate-name> --no-deps --all-features- This catches broken intra-doc links (e.g., referencing non-existent methods in
[backtick links]) - All documentation warnings must be resolved before completing the task
- This catches broken intra-doc links (e.g., referencing non-existent methods in
- Test check (if tests exist):
cargo test -p <crate-name> --all-features- Tests gated by
test_category(e.g., emulator tests) are always compiled but are ignored at runtime unless the corresponding cfg is set viaRUSTFLAGS. - To actually run emulator tests:
RUSTFLAGS='--cfg test_category="emulator"' cargo test -p <crate-name> --tests To run against the new vnext (Linux) emulator instead, point at a vnext container and use the
emulator_vnextcfg:# Start the vnext emulator in Docker (HTTP mode): docker run -d --name cosmosdb-emulator-vnext \ -p 8081:8081 -p 8080:8080 \ -e ENABLE_EXPLORER=false \ mcr.microsoft.com/cosmosdb/linux/azure-cosmos-emulator:vnext-preview # Point the test framework at it and select the vnext-compatible test set: export AZURE_COSMOS_CONNECTION_STRING='AccountEndpoint=http://localhost:8081;AccountKey=C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==;' RUSTFLAGS='--cfg test_category="emulator_vnext"' \ cargo test -p azure_data_cosmos_driver --test emulator -- --test-threads=1A subset of emulator tests (backup-endpoint fallback, partition failover) are intentionally not gated on
"emulator_vnext"because they rely on multi-endpoint topology the vnext gateway does not model. See the per-file module headers for the exclusion rationale.
- Tests gated by
Common documentation link errors to avoid:
- When documenting factory methods or APIs, ensure the linked method names match the actual implementation
- Use the exact method name (e.g.,
method_by_namenot justmethod) in doc links like[StructName::method_by_name]
Copyright Header
All Rust source files in the sdk/cosmos directory must begin with the following copyright header:
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.This header must be:
- The first two lines of every
.rsfile - Followed by a blank line before any other content (imports, module docs, etc.)
- Exactly as shown above (no variations in formatting or text)
Example:
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
//! Module documentation starts here.
use std::time::Duration;Performance Considerations
- Minimize allocations in hot paths
- Use
Cow<'_, str>for fields that might be borrowed or owned - Stream large responses rather than buffering entirely
- Provide options for controlling request throughput (RU/s limits)
Documentation
Every public API should document:
- Purpose: What the API does
- Example: Runnable code showing typical usage
- Errors: What errors can be returned and why
- Performance: RU/s implications, if relevant
- Partition Key: Whether the operation is partition-scoped
Source comment references
- Rust source comments and rendered Rustdoc must not contain direct paths or links to Markdown files. Avoid stale pseudo-links such as repository-relative paths written as text or Markdown links.
include_str!may embed Markdown in adocattribute, including crate READMEs and shared Rustdoc fragments. The compiler validates these paths, and the path is not rendered in the generated documentation.- Public doc comments must not reference Cosmos specs or ADRs.
- Internal doc comments on private or
pub(crate)items may reference a spec or ADR only by its number and title, for example, "Spec 0017: Patch handler" or "ADR 0005: Flat native ABI data model." - Non-doc comments may use the same number-and-title form.
- Put detailed rationale in
docs/; usedocs/README.mdto locate it rather than embedding a file path in Rust source.
Comment Brevity (IMPORTANT)
Keep comments short and dense. Long, chatty comment blocks are noise: they age badly, bury the signal, and read as filler.
- Inline comments: 1–2 lines. 3 is the hard ceiling. Explain why, never what the code already says.
- Doc comments: lead with a one-sentence summary. Add detail only when a caller genuinely needs it to use the API correctly.
- Test comments: usually unnecessary — a good test name plus a clear assertion message says it. Comment only a non-obvious setup or a subtle invariant.
- Never restate an argument three ways, narrate the diff ("this used to be X, now it's Y"), re-explain something already covered by a nearby doc comment, or write a mini design doc inline. Refer to an applicable spec by number and title only when the source comment rules above allow it.
- When a rationale really needs paragraphs, it belongs in
docs/, not in the source.
// ❌ BAD: five lines to say one thing
// This is a client-side re-encode of a payload the driver itself produced,
// not a malformed service envelope, so it is a serialization fault (not the
// 500 `SERVICE_ORDER_BY_ENVELOPE_INVALID`). Include the item ordinal to
// correlate on a large page.
// ✅ GOOD
// Client-side re-encode failure, not a malformed service envelope.Additional Resources
- Cosmos documentation index
- Cosmos project overview
- Cosmos architecture overview
- Azure Cosmos DB REST API Reference
- Cosmos DB Best Practices
- Azure SDK Design Guidelines for Rust
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
Reviews can't be read right now.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

