

Use pytest-airflow or ruff to scan DAGs for cross-task XCom pulls that don't use key or that pull from non-parent tasks. Example rule: XCOM001 – "Pull from non-upstream task."
If using SqlXComBackend with PostgreSQL, you can use SELECT ... FOR UPDATE to lock an XCom row during push/pull, but Airflow does not do this automatically. airflow xcom exclusive
| Practice | Why it matters |
|----------|----------------|
| Never store large data (>1MB) | XCom is stored in the metadata DB; large data degrades performance. Use S3/GCS for big payloads. |
| Use explicit keys | Avoid default return_value key; name keys uniquely. |
| Limit cross-DAG XCom | xcom_pull(dag_id='other_dag') breaks encapsulation. |
| Clear XCom after use | Delete sensitive or one-time data manually. |
| Set xcom_disable=True for tasks that don't need it | Reduces DB bloat. |
| Use taskflow API for automatic XCom handling | Reduces race conditions by design. | Use pytest-airflow or ruff to scan DAGs for
XCom rows are uniquely identified by this combination of columns in Airflow database: If using SqlXComBackend with PostgreSQL, you can use
Implication: XComs are scoped to a specific DAG run and task instance; different execution_date/run_id or task_id isolates them.
By default, XCom allows any task to write to any key, and any task to read from any key. This creates several issues: