Overview

12 Best practices

This chapter distills practical best practices for building Airflow pipelines that are easy to read, safe to operate, and efficient at scale. It emphasizes writing clean, consistent DAGs; managing configuration and secrets in a predictable, centralized way; designing tasks that are reproducible through idempotency and determinism; and handling data and resources efficiently. Along the way it encourages making deliberate choices about scheduling, structure, and orchestration so teams can iterate quickly while avoiding hidden performance and reliability pitfalls.

The guidance starts with code quality and DAG design: adopt a shared Python style and enforce it with linters and formatters; choose one Airflow convention for DAG definitions and dependency notation and use it consistently. Store credentials in Airflow connections (or supported secret backends) and standardize how configuration is supplied, being mindful of where it’s loaded and avoiding any heavy I/O or computation during DAG parsing—push that work into tasks. To reduce duplication, generate repeated patterns with factory functions and organize complexity with Task Groups. Define schedules explicitly with timetable classes for clarity across Airflow versions and instance settings. When the amount of work varies by run, use Dynamic Task Mapping (ideally via the TaskFlow API) to create data-driven, parallel tasks on the fly.

For correctness and speed, make tasks idempotent and deterministic, and favor a functional data engineering mindset that minimizes side effects. Process only what’s needed: filter and aggregate early, prefer incremental over monolithic runs, and cache intermediate results so steps can be retried independently—keeping raw snapshots while cleaning up temporary artifacts. Use shared storage rather than local disks to ensure downstream accessibility, and offload heavy computation to systems built for it (databases, clusters) while Airflow orchestrates. Finally, protect shared services and control throughput with resource pools that cap concurrent access, tuning pool sizes to the capacity of each dependent system.

Generating repeated patterns of tasks using factory methods. This example DAG contains multiple sets of almost identical tasks, which were generated from a configuration object using a task factory method.
Multiple DAGs generated from a single file using a DAG factory function. (Screenshot taken from the Airflow UI, showing multiple DAGs that were generated from a single DAG file using a DAG factory function.)
Task groups can help organize DAGs by grouping related tasks. Initially, task groups are depicted as single nodes in the DAG, as shown for the customers task group in this figure. By clicking on a task group you can expand it and view the tasks within the group, as shown here for the sales task group. Note that task groups can be nested, meaning that you can have task groups within task groups.
Using task groups to organize the umbrella DAG from chapter 6. Here, grouping the tasks for fetching and cleaning the weather and sales data sets helps greatly simplify the complex task structures involved in these processes. (Code example is given in dags/04_task_groups_umbrella.py.)
A schematic overview of the example code shown in Listing 12.31
The Airflow UI’s grid view gives insight into Dynamic Task Mapping in a number of places. Most notably with the [] notation at the top level, showing that the specific task has dynamically mapped instances beneath it.
In the graph view, the mapped task is visually displayed (with additional tasks behind the “top” one). The mapped instances are also visible and show the individual instances
For TaskGroups, the UI gives a similar representation, with [] indicating at both the task and the taskgroup level that this is a mapped task. For all mapped tasks, the individual instances are available in the right panel, as before.
Example of an inefficient data process compared to a more efficient one. (A) One way to calculate the aggregate sales per customer is to first fully join both data sets and then aggregate sales to the required granularity and filter for the customers of interest. Although this may give the desired result, it is not very efficient due to the potentially large size of the joined table. (B) A more efficient approach is to first filter/aggregate the sales and customer tables down to the minimum required granularity, allowing us to join the two smaller data sets.
Illustration of monolithic processing (A), in which the entire data set is processed on every run, compared to incremental processing (B), in which the data set is analyzed in incremental batches as data comes in
Storing intermediate data from tasks ensures that each task can easily be rerun independently of other tasks. In this case, cloud storage (indicated by the bucket) is used to store intermediate results of the fetch/preprocess tasks.
Creating a new resource pool in the Airflow web UI. Note the final checkbox offers more control over whether or not deferrable tasks count towards the pool slot consumption.

Summary

  • Adopting common style conventions together with supporting linting/formatting tools can greatly increase the readability of your DAG code.
  • Credentials should always be managed centrally. Airflow offers multiple secret backend interfaces with external secret management systems, allowing you to re-use existing infrastructure if available.
  • Be consistent in how you specify configuration in order to avoid confusion.
  • Doing computations in your DAG definition should be avoided, as it can greatly slow down Airflow.
  • Factory methods allow you to efficiently generate recurring DAGs or task structures while capturing differences between instances in small configuration objects or files.
  • Dynamic Task Mapping enables you to make your DAG structure dynamic, depending on the data at hand. This can enhance both parallelism as well as atomicity of your tasks.
  • TaskGroups allow you to structure your DAGs visually, especially when your DAGs have a lot of tasks.
  • Idempotent and deterministic tasks are key to building reproducible tasks and DAGs, which are easy to rerun and backfill from within Airflow. Concepts from functional programming can help you design tasks with these characteristics.
  • Data processes can be implemented efficiently by carefully considering how data is handled (i.e., processing in the appropriate systems, limiting the amount of data that is loaded, and using incremental loading) and by caching intermediate data sets in available file systems that are available across workers.
  • You can manage/limit access to your resources in Airflow using resource pools.

FAQ

How do I keep Airflow DAG code clean and consistent across a team?Adopt a shared Python style (e.g., PEP 8) and enforce it with tools: - Linters: Flake8, Pylint, or Ruff (fast linter/formatter in one). - Formatters: Black or YAPF to auto-format code identically for everyone. - Editor integration and CI checks so issues are caught early. Ruff also includes Airflow-specific rules that catch DAG/task pitfalls and upgrade issues.
Which Airflow-specific style choices should we standardize?Pick one convention and use it everywhere: - DAG definition style: context manager (with DAG), classic dag=..., or TaskFlow API—choose one. - Dependency syntax: consistently use >>/<< or set_upstream/set_downstream, but don’t mix them within a DAG. Consistency beats preference; it makes DAGs easier to read and review.
Where should I store and access credentials for operators and custom Python code?Use Airflow Connections as the single source of truth: - Store secrets centrally in the metastore (or via a Secret Backend like cloud secret managers/Kubernetes). - In custom code, fetch credentials via BaseHook.get_connection(conn_id) at task execution time. - Avoid hardcoding secrets or keeping them in plaintext config files.
What’s the recommended way to pass configuration (paths, table names) to DAGs and tasks?Be consistent and mindful of where code runs: - Options: Airflow Variables, environment variables, configuration files (YAML/JSON), or Python modules. - Avoid fetching Variables or reading files in global scope; the scheduler parses DAG files often. Load config inside tasks to prevent repeated I/O. - Centralize shared config (DRY) and keep secrets out of plaintext configs.
Why shouldn’t my DAG definition do computation or external I/O?The scheduler repeatedly imports/parses DAG files. Any heavy work (API calls, long calculations, DB lookups) done at import time will: - Slow DAG parsing and the scheduler, - Potentially hammer external systems every parse cycle. Instead, move computations and credential lookups into task functions, so they run only when the task executes.
What patterns help scale authoring many similar pipelines?Use factories and grouping: - Task/DAG factories: generate repeated tasks or entire DAGs from parameters/config (e.g., YAML), reducing duplication. - If generating multiple DAGs from one file, ensure unique DAG IDs/variable names and use sparingly to avoid confusion. - Use Task Groups to visually organize repeated patterns and reduce graph clutter.
What is Dynamic Task Mapping and when should I use it?Dynamic Task Mapping creates tasks at runtime based on upstream output (e.g., one task per file or record): - Syntax: Operator.partial(...).expand(...) or TaskFlow @task with .expand(). - Great when the number of items varies per run; enables parallel, atomic processing and retries per item. - Prefer TaskFlow API for cleaner syntax and less manual XCom handling.
How do I design tasks to be reproducible?Focus on idempotency and determinism: - Idempotent: reruns overwrite outputs and avoid duplicate side effects (e.g., notifications). - Deterministic: same input yields the same output; fix seeds, sort data explicitly, avoid hidden global/external state, and handle concurrency carefully. - Favor functional patterns (pure functions, minimal side effects) to make behavior predictable.
How can I handle large data efficiently in Airflow?Reduce work and push it to the right systems: - Limit data early: select only needed rows/columns; aggregate before joins. - Process incrementally (by partition/time window) instead of reprocessing the full dataset each run. - Cache intermediates you may need to rerun downstream steps; always keep the rawest ingested snapshot. - Don’t rely on local worker disks; use shared storage (object stores/databases). - Offload heavy computation to databases, Spark, or other purpose-built systems; let Airflow orchestrate.
How do I control concurrency and protect shared resources?Use Airflow Pools: - Create a pool with a fixed number of slots for a shared resource (DB, GPU, Spark cluster). - Assign tasks to the pool (pool="name"); the scheduler only runs as many concurrent tasks as slots allow. - Tune slot counts to match resource capacity and desired concurrency.

pro $24.99 per month

  • access to all Manning books, MEAPs, liveVideos, liveProjects, and audiobooks!
  • choose one free eBook per month to keep
  • exclusive 50% discount on all purchases
  • renews monthly, pause or cancel renewal anytime

lite $19.99 per month

  • access to all Manning books, including MEAPs!

team

5, 10 or 20 seats+ for your team - learn more


choose your plan

team

monthly
annual
$49.99
$499.99
only $41.67 per month
  • five seats for your team
  • access to all Manning books, MEAPs, liveVideos, liveProjects, and audiobooks!
  • choose another free product every time you renew
  • choose twelve free products per year
  • exclusive 50% discount on all purchases
  • renews monthly, pause or cancel renewal anytime
  • renews annually, pause or cancel renewal anytime
  • Data Pipelines with Apache Airflow, Second Edition ebook for free
choose your plan

team

monthly
annual
$49.99
$499.99
only $41.67 per month
  • five seats for your team
  • access to all Manning books, MEAPs, liveVideos, liveProjects, and audiobooks!
  • choose another free product every time you renew
  • choose twelve free products per year
  • exclusive 50% discount on all purchases
  • renews monthly, pause or cancel renewal anytime
  • renews annually, pause or cancel renewal anytime
  • Data Pipelines with Apache Airflow, Second Edition ebook for free
choose your plan

team

monthly
annual
$49.99
$499.99
only $41.67 per month
  • five seats for your team
  • access to all Manning books, MEAPs, liveVideos, liveProjects, and audiobooks!
  • choose another free product every time you renew
  • choose twelve free products per year
  • exclusive 50% discount on all purchases
  • renews monthly, pause or cancel renewal anytime
  • renews annually, pause or cancel renewal anytime
  • Data Pipelines with Apache Airflow, Second Edition ebook for free