I like the new decorator. One small comment on short-circuiting itself (not your proposal specifically):
I never liked ShortCircuitOperator because it is semantically the other way around—short-circuiting means the dag would take a shortcut to the end, so *True* should make short-circuit happen, instead of continuing. This is _marginally_ acceptable with ShortCircuitOperator, but a decorator makes things even more confusing. I would prefer the new decorator be called e.g. short_circuit_if_false instead to make more sense. TP > On 17 Mar 2026, at 18:03, Dev-iL <[email protected]> wrote: > > |Hello everyone,| > || > |I'd like to initiate a vote on a new DAG authorship best practice.| > || > |## Proposal| > || > |When a `@task.branch` (or `BranchPythonOperator`) function has **at least > two `return` statements**, and **exactly one of them returns a non-empty > list**, the function is effectively acting as a binary gate — either run a > specific set of downstream tasks, or skip them all.| > || > |In that case, **`@task.short_circuit`** (`ShortCircuitOperator`) is a > simpler and more readable alternative: it expresses the same intent by > returning `True`/`False`, rather than requiring the caller to reason about > list-vs-empty-list semantics.| > || > |### Before| > || > |```python| > |from airflow.decorators import task| > || > |@task.branch| > |def check_condition():| > |if some_condition:| > |return ["downstream_task"]| > |return []| > |```| > || > |### After| > || > |```python| > |from airflow.decorators import task| > || > |@task.short_circuit| > |def check_condition():| > |return some_condition| > |```| > || > |The same principle applies to the classic operator form.| > || > |## Rationale| > || > |- **Clarity:** `@task.short_circuit` makes intent immediately obvious — the > task either proceeds or it doesn't. Readers no longer need to inspect return > values to determine that the branch is binary.| > |- **Less boilerplate:** Eliminates the need to wrap a boolean condition in a > single-element list.| > |- **Correct semantics:** `@task.branch` with a single non-empty return is a > degenerate case of branching; `@task.short_circuit` is the idiomatic tool for > this pattern.| > |- **Consistency:** The rule applies equally to the TaskFlow API > (`@task.branch`) and the traditional operator API (`BranchPythonOperator`), > covering both authorship styles uniformly.| > || > |## Cases NOT affected by this proposal| > || > |This best practice only applies when the function structure is unambiguously > binary:| > || > |- Functions with **multiple non-empty list returns** are genuine branching > logic — no change suggested.| > |- Functions with **only a single return** — no change suggested.| > |- Functions returning **non-list values** (strings, `None`, etc.) — no > change suggested.| > || > |## Call for Consensus| > || > |Please let me know if you have concerns, questions, or support.| > || > |Thank you,| > |Dev-iL| > || > |---| > || > |## See Also| > || > |1. [apache/airflow#43176 > (comment)](https://github.com/apache/airflow/issues/43176#issuecomment-2667604469) > — original proposal in the static checks tracking issue| > |2. [astral-sh/ruff#23579](https://github.com/astral-sh/ruff/pull/23579) — > Draft Ruff PR implementing (`task-branch-as-short-circuit`)| > --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
