diff --git a/docs/README.instructions.md b/docs/README.instructions.md index b9eb2941a..ad99c5a53 100644 --- a/docs/README.instructions.md +++ b/docs/README.instructions.md @@ -32,6 +32,8 @@ See [CONTRIBUTING.md](../CONTRIBUTING.md#adding-instructions) for guidelines on | [Astro Development Instructions](../instructions/astro.instructions.md)
[![Install in VS Code](https://img.shields.io/badge/VS_Code-Install-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/instructions?url=vscode%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fastro.instructions.md)
[![Install in VS Code Insiders](https://img.shields.io/badge/VS_Code_Insiders-Install-24bfa5?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/instructions?url=vscode-insiders%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fastro.instructions.md) | Astro development standards and best practices for content-driven websites | | [AWS AppSync Event API Instructions](../instructions/aws-appsync.instructions.md)
[![Install in VS Code](https://img.shields.io/badge/VS_Code-Install-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/instructions?url=vscode%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Faws-appsync.instructions.md)
[![Install in VS Code Insiders](https://img.shields.io/badge/VS_Code_Insiders-Install-24bfa5?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/instructions?url=vscode-insiders%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Faws-appsync.instructions.md) | Production-grade guidance for AWS AppSync Event API handlers using APPSYNC_JS runtime restrictions, utilities, modules, and datasource patterns | | [Azure DevOps Pipeline YAML Best Practices](../instructions/azure-devops-pipelines.instructions.md)
[![Install in VS Code](https://img.shields.io/badge/VS_Code-Install-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/instructions?url=vscode%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fazure-devops-pipelines.instructions.md)
[![Install in VS Code Insiders](https://img.shields.io/badge/VS_Code_Insiders-Install-24bfa5?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/instructions?url=vscode-insiders%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fazure-devops-pipelines.instructions.md) | Best practices for Azure DevOps Pipeline YAML files | +| [Azure Durable Functions C# Development](../instructions/azure-durable-functions-csharp.instructions.md)
[![Install in VS Code](https://img.shields.io/badge/VS_Code-Install-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/instructions?url=vscode%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fazure-durable-functions-csharp.instructions.md)
[![Install in VS Code Insiders](https://img.shields.io/badge/VS_Code_Insiders-Install-24bfa5?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/instructions?url=vscode-insiders%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fazure-durable-functions-csharp.instructions.md) | Guidelines and best practices for building Azure Durable Functions in C# using the isolated worker model | +| [Azure Functions C# Development](../instructions/azure-functions-csharp.instructions.md)
[![Install in VS Code](https://img.shields.io/badge/VS_Code-Install-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/instructions?url=vscode%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fazure-functions-csharp.instructions.md)
[![Install in VS Code Insiders](https://img.shields.io/badge/VS_Code_Insiders-Install-24bfa5?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/instructions?url=vscode-insiders%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fazure-functions-csharp.instructions.md) | Guidelines and best practices for building Azure Functions in C# using the isolated worker model | | [Azure Functions Typescript](../instructions/azure-functions-typescript.instructions.md)
[![Install in VS Code](https://img.shields.io/badge/VS_Code-Install-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/instructions?url=vscode%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fazure-functions-typescript.instructions.md)
[![Install in VS Code Insiders](https://img.shields.io/badge/VS_Code_Insiders-Install-24bfa5?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/instructions?url=vscode-insiders%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fazure-functions-typescript.instructions.md) | TypeScript patterns for Azure Functions | | [Azure Logic Apps and Power Automate Instructions](../instructions/azure-logic-apps-power-automate.instructions.md)
[![Install in VS Code](https://img.shields.io/badge/VS_Code-Install-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/instructions?url=vscode%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fazure-logic-apps-power-automate.instructions.md)
[![Install in VS Code Insiders](https://img.shields.io/badge/VS_Code_Insiders-Install-24bfa5?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/instructions?url=vscode-insiders%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fazure-logic-apps-power-automate.instructions.md) | Guidelines for developing Azure Logic Apps and Power Automate workflows with best practices for Workflow Definition Language (WDL), integration patterns, and enterprise automation | | [Azure Terraform Best Practices](../instructions/terraform-azure.instructions.md)
[![Install in VS Code](https://img.shields.io/badge/VS_Code-Install-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/instructions?url=vscode%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fterraform-azure.instructions.md)
[![Install in VS Code Insiders](https://img.shields.io/badge/VS_Code_Insiders-Install-24bfa5?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/instructions?url=vscode-insiders%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fterraform-azure.instructions.md) | Create or modify solutions built using Terraform on Azure. | diff --git a/instructions/azure-durable-functions-csharp.instructions.md b/instructions/azure-durable-functions-csharp.instructions.md new file mode 100644 index 000000000..5ebee995c --- /dev/null +++ b/instructions/azure-durable-functions-csharp.instructions.md @@ -0,0 +1,149 @@ +--- +description: 'Guidelines and best practices for building Azure Durable Functions in C# using the isolated worker model' +applyTo: '**/*.cs, **/host.json, **/local.settings.json, **/*.csproj' +--- + +# Azure Durable Functions C# Development + +## General Instructions + +- Always use the **isolated worker model** with the `Microsoft.Azure.Functions.Worker.Extensions.DurableTask` NuGet package for new Durable Functions projects. +- Use `Microsoft.DurableTask` namespaces for orchestrator and activity context types (`TaskOrchestrationContext`, `TaskActivityContext`). +- Separate orchestrators, activities, entities, and client starter functions into distinct classes or files for clarity. +- Never mix orchestration logic with activity logic — orchestrators coordinate; activities do work. +- Always use `context.CreateReplaySafeLogger(nameof(OrchestratorName))` inside orchestrator functions for logging; never use injected `ILogger` directly in orchestrators as it logs on every replay. +- Use `async Task` or `async Task` for all orchestrator and activity methods — never `async void`. +- Treat orchestrator code as **deterministic and replay-safe**: no `DateTime.Now`, `Guid.NewGuid()`, `Random`, direct HTTP calls, or non-deterministic I/O inside orchestrators. +- Use `context.CurrentUtcDateTime` instead of `DateTime.UtcNow` inside orchestrators. + +## Project Structure + +- Register Durable Functions support in `Program.cs` via `builder.Services.AddDurableTaskClient()` and `builder.ConfigureFunctionsWorkerDefaults(x => x.UseDurableTask())`. +- Organize orchestrators, activities, and entities into feature-based folders (e.g., `/Orchestrations/OrderProcessing/`), not by function type. +- Name orchestrators with the suffix `Orchestrator` (e.g., `ProcessOrderOrchestrator`), activities with the suffix `Activity` (e.g., `ChargePaymentActivity`), and entities with the suffix `Entity` (e.g., `CartEntity`). +- Use constants or static readonly strings for activity/orchestrator/entity names passed to `CallActivityAsync`, `CallSubOrchestratorAsync`, and `GetEntityStateAsync` to prevent typos. + +## Configuration Files + +### local.settings.json +- Always include `AzureWebJobsStorage` connection string for local development — Durable Functions requires storage to maintain orchestration state. +- Use `"UseDevelopmentStorage=true"` or Azurite connection string for local testing — never use a production storage account from local dev. +- Set `FUNCTIONS_WORKER_RUNTIME` to `"dotnet-isolated"` in local.settings.json. +- For Netherite or MSSQL storage providers, include provider-specific connection strings (e.g., `EventHubsConnection` for Netherite). +- Never commit `local.settings.json` to source control — add it to `.gitignore`; use `local.settings.json.example` with placeholder values instead. +- Store sensitive values (storage keys, Event Hub connection strings) using Azure Key Vault locally via `@Microsoft.KeyVault(...)` references if needed. + +### host.json +- Configure Durable Functions-specific settings under `"extensions": { "durableTask": { ... } }` — do not rely on defaults for production. +- Set `"hubName"` to a meaningful, environment-specific value (e.g., `"MyAppProd"`, `"MyAppDev"`) to isolate Task Hubs across environments sharing the same storage account. +- Tune `"maxConcurrentActivityFunctions"` and `"maxConcurrentOrchestratorFunctions"` based on expected throughput and hosting plan — defaults are conservative. +- Enable extended sessions (`"extendedSessionsEnabled": true`) for long-running orchestrations on Premium/Dedicated plans to reduce replay overhead. +- Configure the storage provider: use `"storageProvider": { "type": "netherite" }` or `"mssql"` for high-scale scenarios instead of default Azure Storage. +- Set `"maxQueuePollingInterval"` appropriately — lower values increase responsiveness but increase storage transaction costs on Consumption plan. +- Configure Application Insights sampling rate under `"logging": { "applicationInsights": { "samplingSettings": { ... } } }` to control telemetry volume. + +## Orchestration Patterns + +### Function Chaining +- Use sequential `await context.CallActivityAsync(nameof(ActivityName), input)` calls for step-by-step workflows where each step depends on the result of the previous. +- Pass only serializable, lightweight data as inputs/outputs between activities — avoid passing entire domain objects with circular references. + +### Fan-Out / Fan-In +- Use `Task.WhenAll(tasks)` after fanning out with multiple `context.CallActivityAsync` calls to aggregate parallel results. +- Cap the degree of parallelism when fanning out over large collections — use batching (e.g., partitioning input lists) to avoid overwhelming downstream services or hitting Durable Functions storage limits. +- Prefer `List>` over dynamic task arrays; capture all tasks before awaiting to avoid replay issues. + +### Async HTTP API (Human Interaction / Long-Running) +- Use `client.ScheduleNewOrchestrationInstanceAsync` from an HTTP trigger starter function; return `await client.CreateCheckStatusResponseAsync(req, instanceId)` to provide polling URLs to callers. +- Use `context.WaitForExternalEvent("EventName", timeout)` combined with `context.CreateTimer(deadline, CancellationToken)` to implement approval/callback patterns with timeouts. +- Always handle the timeout race: use `Task.WhenAny(externalEventTask, timerTask)` and cancel the timer if the event arrives first. + +### Monitoring / Polling Pattern +- Use a `while` loop with `context.CreateTimer(context.CurrentUtcDateTime.Add(interval), CancellationToken.None)` for polling workflows instead of separate timer-triggered functions. +- Ensure the monitoring loop has a clear exit condition to avoid infinite loops that never terminate. +- For recurring eternal workflows, use `context.ContinueAsNew(input)` to restart the orchestration with fresh state and prevent unbounded history growth. + +### Eternal Orchestrations +- Use `context.ContinueAsNew(newInput)` at the end of the orchestrator body to restart with clean state for long-lived recurring workflows. +- Drain any pending external events before calling `ContinueAsNew` when using `isKeepRunning` patterns. +- Combine `ContinueAsNew` with `context.CreateTimer` to implement periodic tasks (e.g., daily report generation, cache refresh). + +### Sub-Orchestrations +- Use `context.CallSubOrchestratorAsync(nameof(SubOrchestrator), instanceId, input)` to decompose complex workflows into reusable child orchestrations. +- Provide an explicit `instanceId` for sub-orchestrations when idempotency or correlation is required. +- Limit sub-orchestration nesting depth to avoid history size issues; flatten workflows where possible. + +### Entity Functions (Stateful Entities) +- Define entities using class-based syntax implementing `TaskEntity` for typed, encapsulated state management. +- Access entity state only via entity operations (`entity.State`); never read or write entity storage directly. +- Use `context.Entities.CallEntityAsync` from activities or `context.Entities.SignalEntityAsync` from orchestrators for fire-and-forget entity operations. +- Prefer `SignalEntityAsync` over `CallEntityAsync` from orchestrators when the return value is not needed, to avoid unnecessary blocking. +- Use entities for scenarios requiring distributed counters, distributed locks, aggregators, or per-user/per-session state. +- Keep entity state small and serializable; avoid storing large blobs or collections that grow unboundedly in entity state. + +## Activity Functions + +- Keep activity functions focused on a single unit of work — they are the only place to perform I/O (database reads/writes, HTTP calls, queue sends). +- Inject services (e.g., `IRepository`, `IHttpClientFactory`) via constructor DI into the class containing activity functions; do not use `[FromServices]` inside the activity method. +- Make activities **idempotent** where possible — orchestrators may call the same activity multiple times on retry. +- Use `TaskActivityContext` parameter type for activity context; log using the injected `ILogger` (not a replay-safe logger — activities are not replayed). +- Return only serializable types from activities; avoid returning domain entities with navigation properties. + +## Error Handling and Compensation + +- Wrap `context.CallActivityAsync` calls in try/catch blocks within the orchestrator to handle `TaskFailedException` for graceful error handling and compensation. +- Implement compensating transactions (saga pattern) in the catch block by calling undo activities when a step fails mid-workflow. +- Use `RetryPolicy` (via `new TaskOptions(new RetryPolicy(maxRetries, firstRetryInterval))`) on activity calls for automatic retries with backoff on transient failures. +- Distinguish between transient errors (retry) and business errors (fail-fast and compensate) — do not retry validation or authorization failures. +- Always terminate stuck orchestrations via the Durable Functions management API or client if they enter an error state that cannot self-resolve. + +## Timers + +- Use `context.CreateTimer(fireAt, CancellationToken)` for durable delays inside orchestrators — never use `Task.Delay` or `Thread.Sleep`. +- Always cancel timers that are no longer needed (e.g., when an external event arrives before the timer fires) by passing and cancelling a `CancellationTokenSource`. +- Avoid very short timer intervals (under 1 minute) in production on the Consumption plan; they may cause excessive storage polling costs. + +## Instance Management + +- Use meaningful, deterministic `instanceId` values (e.g., `$"order-{orderId}"`) instead of GUIDs when the orchestration needs to be correlated to a business entity. +- Check for existing instances using `client.GetInstanceMetadataAsync(instanceId)` before scheduling new ones to prevent duplicate orchestrations (singleton pattern). +- Use `client.TerminateInstanceAsync`, `client.SuspendInstanceAsync`, and `client.ResumeInstanceAsync` for lifecycle management in management APIs or administrative functions. +- Purge completed/failed orchestration history periodically using `client.PurgeInstanceAsync` or bulk purge to control Task Hub storage growth. + +## Observability + +- Use `context.CreateReplaySafeLogger(nameof(Orchestrator))` for all logging inside orchestrators to prevent duplicate log entries during replay. +- Log the `instanceId` in every log statement from orchestrators and starters for end-to-end traceability. +- Use Application Insights with the Durable Functions integration to track orchestration lifecycle events, activity durations, and failures. +- Monitor orchestration health via the Durable Functions HTTP management API endpoints (`/runtime/webhooks/durabletask/instances`) or the Durable Functions Monitor VS Code extension. +- Set `durableTask.maxConcurrentOrchestratorFunctions` and `durableTask.maxConcurrentActivityFunctions` in `host.json` to control concurrency and prevent resource exhaustion. + +## Storage and Task Hub Configuration + +- Configure the Task Hub name in `host.json` under `"extensions": { "durableTask": { "hubName": "MyTaskHub" } }` to isolate environments (dev/staging/prod) sharing the same storage account. +- Use separate storage accounts or Task Hub names per environment to avoid cross-environment interference. +- For high-throughput scenarios, use the **Netherite** or **MSSQL** storage provider instead of the default Azure Storage provider to improve performance and reduce costs. +- Avoid storing large payloads (>64KB) directly as orchestration inputs/outputs; store large data in Blob Storage and pass the reference (URL/ID) instead. + +## Testing Durable Functions + +- Use the `Microsoft.Azure.Functions.Worker.Extensions.DurableTask.Tests` NuGet package (if available) or manually mock `TaskOrchestrationContext` for unit testing orchestrators. +- Test activity functions in isolation as regular methods — inject mocks for their dependencies (repositories, HTTP clients) and assert on return values. +- Test orchestrator logic by mocking `context.CallActivityAsync`, `context.CreateTimer`, and `context.WaitForExternalEvent` using a test harness or manual mocks. +- Avoid testing the Durable Functions runtime itself (event sourcing, replay) — focus tests on the business logic inside orchestrators and activities. +- Use integration tests with Azurite or an isolated Azure Storage account to test end-to-end workflows, including starter → orchestrator → activity → completion. +- Use deterministic instance IDs in tests (e.g., `$"test-{Guid.NewGuid()}"`) to enable querying and verifying orchestration state via `client.GetInstanceMetadataAsync`. +- Test timeout scenarios by mocking `context.CreateTimer` to fire immediately and verifying the orchestrator handles the timeout branch. +- Test compensation/error handling by forcing activity failures (throw exceptions in mocked activities) and asserting the orchestrator calls compensating activities. +- Use `client.WaitForInstanceCompletionAsync` in integration tests instead of polling — it blocks until the orchestration completes or times out. +- For entity tests, use `context.Entities.SignalEntityAsync` in test orchestrators and verify entity state via `client.ReadEntityStateAsync` after the orchestration completes. + +## Existing Code Review Guidance + +- If `DateTime.UtcNow` or `DateTime.Now` is used inside an orchestrator, flag it and replace with `context.CurrentUtcDateTime`. +- If `Guid.NewGuid()` or `Random` is used inside an orchestrator, flag it as non-deterministic and move it to an activity. +- If direct HTTP calls (`HttpClient.GetAsync`, etc.) are made inside an orchestrator, flag them immediately and move the call into an activity function. +- If `Task.Delay` or `Thread.Sleep` is used inside an orchestrator, replace with `context.CreateTimer`. +- If orchestration history is growing unboundedly without `ContinueAsNew` on long-running loops, suggest adding `ContinueAsNew` to reset history. +- If entity state is storing large collections or blob data, suggest externalizing large data to Blob Storage and storing only references in entity state. +- If activity functions are not idempotent and the workflow has no retry/compensation logic, flag this as a reliability risk. diff --git a/instructions/azure-functions-csharp.instructions.md b/instructions/azure-functions-csharp.instructions.md new file mode 100644 index 000000000..df9d52e2c --- /dev/null +++ b/instructions/azure-functions-csharp.instructions.md @@ -0,0 +1,103 @@ +--- +description: 'Guidelines and best practices for building Azure Functions in C# using the isolated worker model' +applyTo: '**/*.cs, **/host.json, **/local.settings.json, **/*.csproj' +--- + +# Azure Functions C# Development + +## General Instructions + +- Always use the **isolated worker model** (not the legacy in-process model) for all new Azure Functions projects targeting .NET 6 or later. +- Use `FunctionsApplication.CreateBuilder(args)` or `HostBuilder` in `Program.cs` for host setup and dependency injection. +- Decorate function methods with `[Function("FunctionName")]` and use strongly typed trigger and binding attributes. +- Keep function methods focused — each function should do one thing and delegate business logic to injected services. +- Never put business logic directly inside the function method body; extract it into testable service classes registered via DI. +- Use `ILogger` injected through the constructor, not `ILogger` passed as a function parameter, for consistent structured logging. +- Always use `async/await` for all I/O-bound operations; never block with `.Result` or `.Wait()`. +- Prefer `CancellationToken` parameters where supported to enable graceful shutdown. + +## Project Structure and Setup + +- Use the `Microsoft.Azure.Functions.Worker` and `Microsoft.Azure.Functions.Worker.Extensions.*` NuGet packages. +- Register services in `Program.cs` using `builder.Services.Add*` extension methods for clean dependency injection. +- Group related functions into separate classes by domain concern, not by trigger type. +- Store configuration in `local.settings.json` for local development; use Azure App Configuration or Application Settings for deployed environments. +- Never hardcode connection strings or secrets in code; always read from `IConfiguration` or environment variables. +- Use Key Vault references (`@Microsoft.KeyVault(SecretUri=...)`) in App Settings for secrets in deployed environments. +- Use `Managed Identity` (`DefaultAzureCredential`) for authenticating to Azure services — avoid connection strings with keys wherever possible. +- Keep `host.json` tuned per trigger type: configure `maxConcurrentCalls`, `batchSize`, and retry policies at the host level. + +## Triggers + +- **HttpTrigger**: Use `AuthorizationLevel.Function` or higher for production endpoints; reserve `AuthorizationLevel.Anonymous` only for public-facing APIs with explicit justification. Use ASP.NET Core integration (`UseMiddleware`, `IActionResult` returns) when using the ASP.NET Core integration model. +- **TimerTrigger**: Use NCRONTAB expressions (`"0 */5 * * * *"`) for schedules; avoid `RunOnStartup = true` in production as it executes immediately on every cold start. +- **QueueTrigger / ServiceBusTrigger**: Configure `MaxConcurrentCalls`, dead-letter policies, and `MaxDeliveryCount` in `host.json` and Azure portal; handle `ServiceBusReceivedMessage` directly for advanced message control (complete, abandon, dead-letter). +- **BlobTrigger**: Prefer Event Grid-based blob triggers (`Microsoft.Azure.Functions.Worker.Extensions.EventGrid`) over polling-based blob triggers for lower latency and reduced storage transaction costs. +- **EventHubTrigger**: Set `cardinality` to `many` for batch processing; use `EventData[]` or `string[]` parameter types for batch mode; always checkpoint using the `EventHubTriggerAttribute`'s built-in checkpointing. +- **CosmosDBTrigger**: Use the change feed trigger for event-driven processing of Cosmos DB changes; set `LeaseContainerName` and manage lease containers separately from data containers. + +## Input and Output Bindings + +- Use input bindings to read data declaratively rather than using SDKs directly inside function bodies where the binding covers the use case. +- For multiple output bindings, define a custom return type with properties annotated with the appropriate output binding attributes (e.g., `[QueueOutput]`, `[BlobOutput]`, `[HttpResult]`). +- Use `[BlobInput]` and `[BlobOutput]` for blob read/write; prefer `Stream` over `byte[]` for large blobs to avoid memory pressure. +- Use `[CosmosDBInput]` for point reads and simple queries; for complex queries, inject `CosmosClient` via DI with `Managed Identity`. +- Use `[ServiceBusOutput]` for single-message sends; inject `ServiceBusSender` via DI for batching or advanced send scenarios. +- Avoid mixing SDK clients obtained via DI with binding-based I/O for the same resource — choose one pattern per resource to maintain consistency. + +## Dependency Injection and Configuration + +- Register all external clients (e.g., `BlobServiceClient`, `ServiceBusClient`, `CosmosClient`) as singletons using `services.AddAzureClients()` from the `Azure.Extensions.AspNetCore.Configuration.Secrets` package with `DefaultAzureCredential`. +- Use `IOptions` or `IOptionsMonitor` for strongly typed configuration sections. +- Avoid using `static` state in functions; all shared state should flow through DI-registered services. +- Register `HttpClient` instances via `IHttpClientFactory` to manage connection pooling and avoid socket exhaustion. + +## Error Handling and Retry + +- Configure built-in retry policies in `host.json` using `"retry"` with `fixedDelay` or `exponentialBackoff` strategy for trigger-level retries. +- For transient fault handling at the code level, use `Microsoft.Extensions.Http.Resilience` or Polly v8 (`ResiliencePipeline`) with retry, circuit breaker, and timeout strategies. +- Always catch specific exceptions and log them with structured context (e.g., correlation ID, input identifier) before re-throwing or dead-lettering. +- Use dead-letter queues for messages that fail after all retries; never silently swallow exceptions in function handlers. +- For HTTP triggers, return appropriate `IActionResult` types (`BadRequestObjectResult`, `NotFoundObjectResult`) rather than throwing exceptions for expected error conditions. + +## Observability and Logging + +- Use `ILogger` with structured log properties: `_logger.LogInformation("Processing message {MessageId}", messageId)`. +- Configure Application Insights via `builder.Services.AddApplicationInsightsTelemetryWorkerService()` and `builder.Logging.AddApplicationInsights()` in `Program.cs`. +- Use `TelemetryClient` for custom events, metrics, and dependency tracking beyond what is automatically collected. +- Set appropriate log levels in `host.json` under `"logging"` to avoid excessive telemetry costs in production. +- Use `Activity` and `ActivitySource` from `System.Diagnostics` for distributed tracing context propagation between functions and downstream services. +- Avoid logging sensitive data (PII, secrets, connection strings) in any log statement. + +## Performance and Scalability + +- Keep function startup time minimal: defer expensive initialization to lazy-loaded singletons, not the function constructor. +- Use the Consumption plan for event-driven, unpredictable workloads; use Premium or Dedicated plans for low-latency, high-throughput, or VNet-integrated scenarios. +- For CPU-intensive work, offload to a background `Task` or use Durable Functions rather than blocking the function host thread. +- Batch operations where possible: process `IEnumerable` or `ServiceBusReceivedMessage[]` arrays in a single function invocation rather than one message at a time. +- Set `FUNCTIONS_WORKER_PROCESS_COUNT` and `maxConcurrentCalls` appropriately for the hosting plan and expected throughput. +- Enable `WEBSITE_RUN_FROM_PACKAGE=1` in App Settings for faster cold starts by running directly from a deployment package. + +## Security + +- Always validate and sanitize HTTP trigger inputs before processing; use FluentValidation or Data Annotations. +- Use `AuthorizationLevel.Function` with function keys stored in Key Vault for internal API-to-API calls. +- Integrate Azure API Management (APIM) in front of HTTP-triggered functions for public-facing APIs to handle auth, rate limiting, and routing. +- Restrict inbound access using App Service networking features (IP restrictions, Private Endpoints) for sensitive functions. +- Never log request bodies containing PII or secrets. + +## Testing + +- Unit-test service classes independently of the function host using standard xUnit/NUnit with mocked dependencies. +- Integration-test functions using `Azurite` (local Azure Storage emulator) and `TestServer` or the Azure Functions Core Tools. +- Use the `Microsoft.Azure.Functions.Worker.Testing` helpers where available to construct mock `FunctionContext` instances. +- Avoid testing the trigger plumbing itself; focus tests on the business logic extracted into services. + +## Existing Code Review Guidance + +- If a project uses the legacy **in-process model** (`FunctionsStartup`, `IWebJobsStartup`), suggest migrating to the isolated worker model and provide the migration path via `dotnet-isolated-process-guide`. +- If hardcoded connection strings or storage account keys are found in code or config files, flag them and suggest replacing with `DefaultAzureCredential` and Key Vault references. +- If `RunOnStartup = true` is set on a `TimerTrigger` in a production app, flag it as a risk and suggest using deployment slots or feature flags instead. +- If `async void` is used in any function, flag it immediately — use `async Task` instead. +- If retry logic is implemented manually with `Thread.Sleep` or `Task.Delay` inside a function, suggest replacing with host-level retry policies or Polly resilience pipelines. +