Architecture¶
This document explains how azure-functions-doctor is structured internally and why key design choices support deterministic, actionable diagnostics.
Design Objectives¶
The package is intentionally focused:
- Keep diagnostics rule-driven and data-defined (JSON rule assets, not hardcoded checks).
- Preserve exit-code-based CI integration as a first-class concern.
- Add invocation context without requiring heavy runtime dependencies.
- Stay dependency-light and operationally predictable.
High-Level Components¶
Core modules and responsibilities:
__init__.py: public exports and version string.cli.py: Typer-based CLI entrypoint; maps flags toDoctoroptions.doctor.py:Doctorrunner — loads rules, executes handlers, aggregates results.handlers/package:Ruletype and shared helpers (_helpers.py), plusgeneric_handlerand type-based rule dispatch viaHandlerRegistry(registry.py) over per-domain implementation modules (generic,dependencies,runtime,monitoring,deployment,bindings,project,durable,integrations).config.py: configuration management (reserved for future use; not yet in the runtime path).target_resolver.py: resolves runtime values (Python version, Core Tools version) for version-comparison checks.logging_config.py: internal logging setup.schemas/: JSON schema definitions for rule assets.assets/: built-in rule inventory (e.g.rules/v2.json).
Public API Boundary¶
Public symbols intentionally kept small:
Doctor__version__run_diagnostics()(fromapi.py— programmatic entrypoint)
CLI is the primary consumer. Python import use is for programmatic embedding only.
Module Boundaries¶
Diagnostic Pipeline¶
Doctor.run_all_checks() is the entrypoint for a full diagnostic scan.
Execution flow:
- Load rule asset (
assets/rules/v2.json) or customrules_path. - Validate rule asset against JSON schema.
- Apply profile filter if
--profileis given. - Dispatch each rule to its handler by the rule's
typefield. - Aggregate
SectionResultlist with per-itemCheckResultentries. - Return
list[SectionResult]— overall pass/fail status is derived later by the CLI.
Rule Asset Design¶
Rules are data, not code:
- Each rule is a JSON object with
id,category,label,type,condition, and optionalhint. - Handlers dispatch on the rule's
typefield and evaluateconditionagainst the target project. - New checks can be added without touching Python logic.
See Rule Inventory and RULE_POLICY for the full rule catalogue.
Exit Code Contract¶
The CLI follows a strict exit code contract:
| Exit code | Meaning |
|---|---|
0 |
No checks failed |
1 |
One or more checks failed |
2 |
Usage error (bad arguments) |
CI pipelines can rely on these codes directly. See JSON Output Contract for structured output.
Profile System¶
Profiles allow subsets of rules:
- Profile is a string selector, not a file-based system. Four profiles are accepted:
minimal(only rules withrequired=True),deploy(core-group rules minus dev-environment checks — runtime/hosting/deployment correctness),development(dev-environment checks: venv, Python executable, Core Tools, local.settings), andfull(default when omitted: all rules). - Membership is computed in
azure_functions_doctor.profilesfrom rule metadata (single source of truth shared with the generated rule inventory). - Custom profile names raise
ValueError; only the four names above are accepted.
See Configuration and Minimal Profile.
Key Design Decisions¶
1. JSON rule assets over hardcoded checks¶
Every diagnostic check is defined as a JSON object in the rule inventory (assets/rules/v2.json). New checks are added by appending rule data — no Python handler changes required unless a new rule type is introduced.
2. HandlerRegistry type-based dispatch¶
The handlers/ package (registry.py) maintains a HandlerRegistry that maps rule type strings to handler functions. The Doctor runner dispatches each rule to its handler by type, enabling extensibility without modifying dispatch logic.
3. Exit code contract¶
cli.py sets exit code 1 explicitly when any check fails. Exit code 0 results from explicit typer.Exit(0) in structured-output paths and normal return in table mode. Exit code 2 is not explicitly coded — it is the default behaviour of Typer/Click when invoked with invalid arguments.
4. String-based profile selection¶
Profiles are not file-based. The --profile flag accepts minimal, deploy, development, or full (default when the flag is omitted: all rules). Membership is computed from rule metadata in azure_functions_doctor.profiles (Doctor.run_all_checks() filters with it). This avoids external profile file management while covering the common cases: gating CI on required rules (minimal) or on deployment correctness (deploy).
5. Typer CLI framework¶
The CLI uses Typer for argument parsing, help generation, and shell completion. This was chosen over argparse/Click for its decorator-driven API and automatic type inference from Python type hints.
Related Documents¶
Sources¶
- Azure Functions Python developer reference
- Azure Functions host.json reference
- Supported languages in Azure Functions
See Also¶
- azure-functions-validation — Architecture — Request/response validation and serialization
- azure-functions-openapi — Architecture — API documentation and spec generation
- azure-functions-logging — Architecture — Structured logging with contextvars
- azure-functions-scaffold — Architecture — Project scaffolding CLI
- azure-functions-langgraph — Architecture — LangGraph runtime exposure for Azure Functions