task scheduling
sequential execution
automation
workflow management
process sequencing

Run sequence of tasks, one after the other

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Running tasks one after another is fundamentally about preserving order and failure semantics. The next task should not start until the current one has finished successfully, or until you have decided how to handle failure. The implementation depends on the environment, but the design principle is always the same: explicit sequencing beats accidental concurrency.

The Simplest Case Is Straight-Line Code

If tasks are synchronous and local, sequencing is trivial.

python
1def task_a():
2    print("task A")
3
4def task_b():
5    print("task B")
6
7def task_c():
8    print("task C")
9
10task_a()
11task_b()
12task_c()

This works because each call completes before the next line runs. The moment tasks become asynchronous, remote, or distributed, you need stronger coordination.

Use await for Async Sequencing

In asynchronous code, the most direct way to preserve order is awaiting each task in sequence.

python
1import asyncio
2
3async def task_a():
4    await asyncio.sleep(1)
5    print("task A")
6
7async def task_b():
8    await asyncio.sleep(1)
9    print("task B")
10
11async def main():
12    await task_a()
13    await task_b()
14
15asyncio.run(main())

The important part is that await task_a() finishes before task_b() begins. If you created both tasks up front with create_task, you would be moving toward concurrency instead.

Stop or Continue Explicitly on Failure

Real task sequencing needs a failure policy. Sometimes the sequence should stop on the first failure. Sometimes later cleanup tasks should still run.

python
1def run_pipeline(tasks):
2    for task in tasks:
3        task()
4
5run_pipeline([task_a, task_b, task_c])

Now add error handling:

python
1def run_pipeline(tasks):
2    for task in tasks:
3        try:
4            task()
5        except Exception as exc:
6            print(f"task failed: {exc}")
7            break

That break is not just a coding detail. It defines the workflow contract.

Queues and Workers Still Need Ordering Logic

If tasks are fed through a worker system, running them "one after the other" usually means using a single worker or chaining completion events rather than throwing everything into a concurrent pool.

In distributed systems, sequencing is not automatic just because tasks were enqueued in order. Worker parallelism, retries, and failure recovery can reorder work unless the workflow system explicitly supports chaining.

That is why tools such as Airflow, Prefect, and job queues expose dependency graphs instead of assuming order from submission time alone.

Use a Workflow Engine When the Sequence Is Operationally Important

If the tasks matter operationally and span machines, services, or time windows, use a tool that models dependencies directly.

For example, an Airflow DAG expresses order explicitly.

python
task_a >> task_b >> task_c

Even if your actual implementation does not use Airflow, the concept is helpful: order should be represented as dependency, not as an accidental side effect of timing.

This becomes especially important once retries or manual reruns enter the picture.

Keep Tasks Small and Idempotent

Sequencing becomes much easier to reason about when each step has a clear boundary and can safely be retried or skipped. Long tasks with hidden side effects make the whole chain brittle.

So when designing ordered workflows, do not only ask "how do I run one after another." Also ask:

  • what happens if task three fails
  • can task two be retried safely
  • should task four still run during cleanup

Those questions define whether the sequence is operationally robust.

Common Pitfalls

  • Assuming submission order automatically guarantees execution order in asynchronous or distributed systems.
  • Accidentally creating concurrent work when the requirement was strict sequencing.
  • Forgetting to define whether the pipeline should stop or continue on failure.
  • Encoding important workflow rules only in comments instead of in the control flow itself.
  • Building huge monolithic tasks instead of smaller ordered steps with clear boundaries.

Summary

  • Task sequencing is about preserving order and failure behavior explicitly.
  • Straight-line code works for synchronous tasks; await works for asynchronous sequencing.
  • In queued or distributed systems, ordering must be modeled deliberately.
  • Failure handling is part of sequencing, not an afterthought.
  • Small, well-bounded tasks make sequential workflows easier to reason about and maintain.

Course illustration
Course illustration

All Rights Reserved.