Read surrounding module, class, and function docstrings plus non-obvious comments before changing backend behavior. They are local contracts; update them only when their owned behavior changes, and keep them aligned with the current code.
Run backend checks from the repository root:
- Format and lint:
make lint - Type check:
make type-check - Unit tests:
make test - Targeted tests:
make test TARGET_TESTS=./api/tests/<path>
Run direct Python commands through uv run --project api. Docker-backed integration suites are normally CI-owned. Do not start long-running services as part of routine agent work.
Follow the backend developer guide's test layout and naming requirements when adding or moving unit tests.
Read all backend architecture documentation before modifying backend code:
- Reconstruct trusted internal references from validated database state after payload or async boundaries.
- Read configuration through
configs.dify_config, access storage throughextensions.ext_storage.storage, and route outbound HTTP through the existing SSRF-safe owner incore.helper.ssrf_proxy. - Use existing Celery task and queue owners for asynchronous work; do not route unrelated jobs through workflow-specific services.
- Celery tasks that may be retried or redelivered must keep side effects idempotent and log affected resource identifiers.
New SQLAlchemy models must inherit from models.base.TypeBase. The ast-grep
guard rejects new Base subclasses. Existing Base subclasses may be edited
or migrated without adding more legacy inheritance. make lint checks local
changes, and CI checks the PR against its target revision.
- Call billing and saas only through the generated functions in
services/billing_client; do not hand-write paths or response types. - Ruff's banned-api rule (TID251) enforces this: outside
services/billing_client, reading the billing URL and secret settings or callingtransport.requestfails lint. - To call a new endpoint, add it to
ALLOWED_OPERATIONSindev/generate_billing_client.pyand rerun that script against a dify-saas checkout. Never edit the generated modules or snapshots by hand.
- Treat nullable types (
T | None) strictly as union types; document the semantic meaning for each branch. - Treat default parameter values as introducing distinct function overloads; verify that every added default has demonstrable practical utility and avoids masking unsafe runtime states.
- Start each design from explicit principles and domain invariants. An invariant is a condition that all valid states preserve.
- Derive implementation rules and memory documents from these principles. Explain the invariant that each rule protects.
- Document each invariant in the docstring of the module, class, or function that owns it. Use a comment for a local invariant.
- Use abstract interpretation to reason about possible values and state transitions. Check that each operation preserves the relevant invariants.
- Use dedicated types to distinguish domain values with different meanings. Use
NewTypefor static distinctions between values with identical representations. NewTypedoes not validate values at runtime. Use validated value objects when an invariant requires runtime checks.- Model mutually exclusive alternatives (an OR relationship) with union types. Use a tagged union when variants need an explicit discriminator.
- Give each tagged variant a distinct
Literaltag. Include only the fields that are valid for that variant. - Prefer safe-by-construction functional idioms. Use immutable values, explicit result variants, and pure functions where practical.
- Validate external values at boundaries with Pydantic v2 models or
TypeAdapter. Construct trusted domain values only after validation succeeds.
- Use structured control flow. Do not add
continueorbreakstatements. - Express iteration with explicit conditions, comprehensions, or small functions that return a result.
- State preconditions and postconditions for each new or changed function in the code you produce.
- Put these contracts in the function docstring. Include accepted inputs, required state, results, side effects, and failure behavior as applicable.
- Specify which invariants remain true after success and failure. Use types and executable checks to enforce contracts where practical.
- Include preconditions and postconditions in the change report. State which checks support the claimed postconditions.