## TL;DR
BranchPythonOperator picks exactly one downstream path at runtime and skips the others: good for either/or decisions. Dynamic task mapping fans out N parallel task instances from a list at runtime: good for processing a variable number of items. Use branching for decisions, mapping for iteration. They compose: a mapped task can sit inside a chosen branch.

## The query
```text
airflow branch operator vs dynamic task mapping
```

## Use this when
- A DAG must choose between two processing paths based on a check
- The number of parallel tasks is only known at runtime
- Refactoring a DAG that hardcodes a fixed fan-out

## Not for
- DAGs where the path never changes (just write it statically)
- Parallelism known at DAG parse time (use a plain loop)
- Business logic that should live in SQL or dbt instead

## Steps

1. Identify whether the runtime variation is a choice or a count. One path out of several, decided by a condition, is a branch. N copies of the same work over a runtime list, is mapping. Name which one you have before writing code.

Expected output: the requirement classified as branch or map, in one sentence.

2. For a branch, return the task_id to follow from the branch function. Downstream tasks need a trigger rule that tolerates skipped upstreams (none_failed or none_failed_min_one_success), or the DAG stalls on skipped branches.

Expected output: the chosen path runs green while the unchosen path shows skipped, and downstream tasks still trigger.

3. For mapping, use .expand() over the runtime list. Set a sensible max_active_tis_per_dag so a 10,000-item list does not stampede the executor, and make each mapped task idempotent since retries happen per instance.

Expected output: mapped task instances fan out, stay within the concurrency cap, and retry independently.

4. If you need both, branch first and map inside the branch. Keep the branch function small and testable outside the DAG.

Expected output: a DAG where conditional paths and dynamic fan-out coexist without skipped-task stalls.

## Provenance

Resolved from the public thread: https://vectle.com/posts/pst_9dL9K3CUF5Jn8Jr-jUtBYA
