← Lab / 001 · Engineering · · 3 min read

From Bash Scripts to Software Systems

How small Bash automation projects teach the foundations of larger software systems: inputs, validation, orchestration, state and separation of concerns.

  • bash
  • automation
  • architecture
  • learning

Several of my earliest public projects are Bash scripts. They are not production platforms, and I would not describe them that way. They were course projects and experiments, written to understand how Linux behaves and how information moves through a system.

Looking back, they were also my first lessons in software design. Almost every idea I now use in larger systems showed up there first, in a smaller and rougher form.

A script is already a system

A script has inputs, a sequence of steps, outputs and failure modes. That is a system, just a small one. The difference between a script and a software system is mostly how deliberately those parts are separated and how much you can trust each one.

  1. 01INPUTwhat comes in, from where
  2. 02VALIDATErefuse bad input early
  3. 03ORCHESTRATEdecide what runs, in what order
  4. 04RECORDkeep state and results
  5. 05REPORTmake the outcome readable

Most of my Bash projects followed this shape, whether or not I named it. Naming it is what turned scripts into design.

Inputs and validation

The first thing a script teaches you is that input is never what you expect. A missing argument, a path that does not exist, a value with a space in it. Validating inputs at the edge, before any work happens, is the cheapest reliability improvement there is.

require_file() {
  [[ -r "$1" ]] || { echo "error: cannot read '$1'" >&2; return 1; }
}

The habit matters more than the snippet: check first, fail clearly, and say what was wrong.

Orchestration and error handling

Once a script runs several tools, the interesting question is no longer what each tool does. It is what happens when one of them fails halfway through. Do the next steps still run? Is the partial result kept or discarded? Does the user find out?

Handling that explicitly, with exit codes checked and failures reported, is the beginning of error handling as a design concern rather than an afterthought.

State and structured output

Early scripts print text to the screen. Useful ones write results somewhere predictable, in a consistent layout, so a later step or a later day can read them. That is state, and it is the first time a script starts to look like a program with memory.

Structured output follows from the same pressure. As soon as something else needs to consume the results, free-form text stops being good enough.

The moment output has a second reader, it has a format.

Separation of concerns

The hardest lesson was keeping decisions apart from presentation. A script that mixes logic, prompts and printing is hard to change. One that keeps them apart can be tested, reused and read.

Where it leads

English Journy is the clearest example of where those instincts ended up. Its curriculum is structured data, its learning engine is free of rendering code, and the interface is a thin layer on top. That is the same separation I was groping toward in shell scripts, applied to a project designed to be tested.

The language changed from Bash to JavaScript, but the questions did not: what goes in, what is checked, what decides, what is remembered, what is shown.

What I take from it

Small projects are not lesser projects. They are where the foundations are cheapest to learn, because the whole system fits in your head. The goal is not to have outgrown them, but to recognise the same shapes when the system gets bigger.