DEV Community

MEROLINE LIZLENT
MEROLINE LIZLENT

Posted on

Learning YAML From Scratch: What I Wish I Knew Before My First GitHub Actions Workflow published: true

I thought that the hard part of learning CI/CD was understanding pipelines, build stages and deployment strategies. It was much simpler than that, it was a .yaml file. Specifically, it was when one of the spaces in a GitHub Actions workflow is missing that it breaks silently and there is no error message that tells you what the real problem is. After I "got" YAML, all of the rest of CI/CD started making sense and I wanted to write the introduction I wished I'd had.

What YAML actually is

YAML stands for "YAML Ain't Markup Language," and that name is a clue to what makes it different from something like XML or JSON. There are no angle brackets, no closing tags, and almost no punctuation. Structure comes entirely from indentation and line breaks, which is what makes YAML so readable and also what makes it so easy to break if you're not paying attention.

GitHub Actions uses YAML for every workflow file, stored under .github/workflows/ in a repository. Once you understand how YAML represents data, reading and writing these workflows stops feeling like guesswork.

Key-value pairs: the foundation

The simplest thing YAML does is store a value under a name, separated by a colon and a space:

name: Build and Test
Enter fullscreen mode Exit fullscreen mode

That's it. No quotes required unless the value contains special characters like a colon or a hash symbol. In a GitHub Actions workflow, this line just gives the workflow a human-readable name that shows up in the Actions tab.

Indentation defines structure

This is the part that took me the longest to internalize: in YAML, whitespace isn't decoration, it's syntax. Two lines at the same indentation level are siblings. A line indented further than the one above it is nested inside it.

on:
  push:
    branches:
      - main
Enter fullscreen mode Exit fullscreen mode

Here, on has a child called push, which has a child called branches, which holds a list. Every level is indented by exactly two spaces GitHub Actions files conventionally use two spaces, not tabs, and mixing tabs and spaces is one of the fastest ways to produce a parsing error that gives you almost no useful information about where things went wrong.

Lists: the dash syntax

YAML represents lists using a hyphen followed by a space:

branches:
  - main
  - develop
Enter fullscreen mode Exit fullscreen mode

This says the value of branches is a list containing two items. You'll see this pattern constantly in workflow files for branches to trigger on, for steps in a job, for strategy matrices.

Mappings inside lists

Where YAML started to feel genuinely powerful to me was realizing that list items can themselves be mappings (key-value groups), not just plain strings. This is exactly how GitHub Actions defines steps:

steps:
  - name: Checkout code
    uses: actions/checkout@v4
  - name: Set up Python
    uses: actions/setup-python@v5
    with:
      python-version: "3.12"
Enter fullscreen mode Exit fullscreen mode

Each hyphen starts a new step, and everything indented under that hyphen name, uses, with, belongs to that one step. Once I saw this pattern, an entire real-world workflow file went from looking like an alien script to looking like a straightforward nested outline.

A minimal, complete workflow

Putting the pieces together, here's a small but functional GitHub Actions workflow that runs on every push to main and executes a Python test suite:

name: Run Tests

on:
  push:
    branches:
      - main

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: "3.12"
      - name: Install dependencies
        run: pip install -r requirements.txt
      - name: Run tests
        run: pytest
Enter fullscreen mode Exit fullscreen mode

Reading this top to bottom: the workflow is named Run Tests, it triggers on pushes to main, and it defines one job called test that runs on an Ubuntu runner and executes four steps in order. There is nothing here that isn't one of the four patterns above, key-value pairs, indentation, lists, and mappings inside lists.

Mistakes that cost me time

A few things caused most of my early frustration, and knowing about them in advance will save you the same trouble:

Mixing tabs and spaces is invalid YAML, even though many editors visually display them as equivalent. Configure your editor to insert spaces when you press Tab.

Inconsistent indentation within the same list breaks parsing. If one step is indented two spaces and the next is indented four, YAML will either misinterpret the structure or reject the file outright.

Forgetting the space after a colon turns a key-value pair into plain text. name:Run Tests is not the same as name: Run Tests YAML requires that space to recognize the colon as a separator.

Unquoted values that look like other types can be misread. A version number like 3.10 can be interpreted as the number 3.1 rather than the string "3.10", which is why you'll often see version numbers wrapped in quotes in real workflow files.

Why this was worth learning properly

Once YAML stopped being a mystery, reading other people's GitHub Actions workflows and debugging my own became dramatically easier. CI/CD pipelines are, underneath all the tooling, just structured configuration describing a sequence of steps. Learning to read that structure fluently is a small investment that pays off every time you touch a .yml file, whether that's in GitHub Actions, GitLab CI, or any other tool built on the same format.

If you're just starting out with CI/CD, I'd genuinely recommend spending an afternoon with nothing but YAML syntax before diving into pipeline concepts. It made everything downstream click faster for me than jumping straight into workflow documentation did.

Top comments (0)