Code of the Day
BeginnerUtility Thinking

Composability

A composable program reads from stdin, writes to stdout, and exits cleanly — and that is all it takes to work with every other tool on the system.

UtilitiesBeginner6 min read
Recommended first
By the end of this lesson you will be able to:
  • Explain what a pipe does and how programs chain together
  • Understand stdin and stdout as the universal connector between programs
  • Describe the three properties that make a program composable

The character | is one of the most powerful ideas in computing. It takes the of one program and feeds it directly into the of the next — with no files, no intermediate state, and no coordination required. The programs do not know about each other. They just speak the same language: lines of text.

How pipes work

Consider this shell pipeline:

cat server.log | grep ERROR | sort | uniq -c | sort -rn | head -10

Read it left to right:

  1. cat server.log — reads the file and writes every line to stdout
  2. grep ERROR — reads those lines and passes through only the ones containing "ERROR"
  3. sort — sorts the filtered lines alphabetically
  4. uniq -c — collapses consecutive identical lines and prefixes each with a count
  5. sort -rn — sorts numerically in reverse (largest count first)
  6. head -10 — prints only the first 10 lines

The result: the ten most common error messages in your log, ranked by frequency. None of these programs were designed to work together. They compose because they each follow the same contract.

The composability contract

A program is composable if it does three things:

  1. Reads from stdin (or from a file named as an argument — but defaults to stdin)
  2. Writes results to stdout — data only, no diagnostic noise
  3. Exits with a meaningful exit code — 0 means success; anything else means failure

That is the whole contract. A program that follows all three works correctly in any pipeline position: as the first step, a middle step, or the final step.

A program that writes errors to stdout breaks step 2 — downstream tools will treat the error message as data. A program that always exits 0, even on failure, breaks step 3 — a pipeline cannot know whether to continue.

Composability is a design decision

Composability does not happen automatically. You have to decide to:

  • Send results to stdout and only results
  • Send all messages, warnings, and errors to stderr
  • Return a non-zero exit code when something goes wrong
  • Accept input from stdin when no filename is given

These are not hard requirements to meet, but you have to think about them. A program that opens a GUI, prompts for interactive input, or pops up a dialog cannot be used in a pipeline at all.

Many excellent Python programs are not composable — and that is fine if composability is not the goal. The question to ask yourself before you start writing is: should this tool work in a pipeline? If yes, design for it from the beginning. Retrofitting composability is much harder than building it in.

What this track builds

Every tool in this track is designed to be composable: reads stdin or a named input, writes clean results to stdout, reports errors to stderr, and exits with a meaningful code. You will also learn argparse — which lets your tool accept both stdin and named files, and gives you a --help flag for free.

Where to go next

Next: CLI arguments — using argparse to give your utility a real command-line interface with named flags and positional arguments.

Finished reading? Mark it complete to track your progress.

On this page