Chapter 05Lesson 02~195 minutes

Issues, Labels, Milestones, Iterations, Boards, Epics, and Work Planning: Guided Hands-On Workflow and Core Operations

Create a disposable GitLab Free backlog, classify it with labels and a milestone, operate a basic board, connect one issue to a branch and merge request, and verify state through UI, glab, Git, and REST evidence.

Disposable backlogglabREST APIBoard workflowBranch linkageVerification

Learning objectives

  • Create a small synthetic backlog in a disposable GitLab Free project using safe, reproducible labels, a milestone, and issues.
  • Build or inspect a basic issue board and prove that card movement changes underlying issue metadata rather than creating duplicate state.
  • Use glab and the Issues/Boards REST APIs to inspect structured planning state with explicit project scope and pagination awareness.
  • Connect one issue to a Git branch and a minimal merge request without requiring CI, paid features, or a production merge.
  • Predict before/after state, verify the change independently, and leave a clean evidence trail for the checkpoint lab.
Availability baseline (verified 2026-08-21). Issues/work items, ordinary project/group labels, milestones, and basic issue boards are documented for Free, Premium, and Ultimate on GitLab.com, Self-Managed, and Dedicated. Scoped labels, iterations, configurable work-item status, and epics are Premium/Ultimate. Iterations are group-level timeboxes. Epics are now exposed through the work-item model in current GitLab; the legacy Epics REST API is deprecated and GitLab 18.1+ documentation directs integrations to the Work Items API/GraphQL path. Board list/scope capabilities vary by tier. Always verify the current GitLab version/tier before relying on a planning field or API.

1. Scenario and preflight

You are planning a tiny fictional release for the disposable project used in earlier chapters. The mandatory path uses GitLab Free, one project you own, ordinary labels, one project milestone, one project board, three synthetic issues, and one short-lived branch. Iterations, scoped labels, configurable status, and epics are optional read-only/fixture extensions.

  • Required tools: Git, glab, and jq (or equivalent JSON inspection).
  • Required role: enough permission to create/edit issues and planning metadata in the disposable project. Current GitLab documents Planner/Reporter/Developer/Maintainer/Owner as relevant planning roles for many issue/board operations; exact mutation permissions vary by operation.
  • No runner, pipeline, registry, cloud, Kubernetes, paid subscription, or extra account is required.
  • Do not use a real product backlog. Prefix synthetic items with [CH05 LAB].

2. Inspect host, project, default branch, and current backlog first

Before changing anything, capture the target. The default branch matters later because issue-closing patterns take effect when the referenced change reaches that branch.

glab auth status

glab repo view --output json   --jq '{path_with_namespace,visibility,default_branch,web_url}'

git remote -v
git status --short --branch

glab issue list --all --output json   --jq '.[] | {iid,title,state,labels,milestone}' 

Stop if the repository or GitLab host is not the disposable target. This is the same target-confirmation discipline used for credentials and namespaces in earlier chapters.

3. Define the metadata policy before creating items

Create the vocabulary on paper first. A small policy prevents the “label everything” anti-pattern.

Dimension Lab convention Owner/rule
Work type type-bug, type-docs, type-feature Exactly one type label by team convention; Free does not enforce exclusivity.
Flow flow-ready, flow-doing At most one flow label by convention; board lists visualize it.
Delivery goal Milestone CH05 Demo One milestone for the miniature release slice.
Ownership Assignee only when someone is actively accountable Do not create labels that duplicate usernames.
Priority Omitted from this lab Add only when the team can define consistent semantics.

4. Create ordinary labels safely and verify them

The Project Labels API is Free. Use distinct colors only for readability; color has no authorization or workflow semantics.

PROJECT_ID="123456"  # replace with your disposable project ID

# First inspect existing labels to avoid duplicate names.
glab api "projects/$PROJECT_ID/labels?per_page=100" --paginate   --jq '.[] | {id,name,color,description}'

# Create only if these synthetic labels do not already exist.
glab api "projects/$PROJECT_ID/labels" -X POST   -f name='type-bug' -f color='#D9534F'   -f description='CH05 lab: defect work'
glab api "projects/$PROJECT_ID/labels" -X POST   -f name='type-docs' -f color='#428BCA'   -f description='CH05 lab: documentation work'
glab api "projects/$PROJECT_ID/labels" -X POST   -f name='type-feature' -f color='#5CB85C'   -f description='CH05 lab: feature work'
glab api "projects/$PROJECT_ID/labels" -X POST   -f name='flow-ready' -f color='#F0AD4E'   -f description='CH05 lab: ready for implementation'
glab api "projects/$PROJECT_ID/labels" -X POST   -f name='flow-doing' -f color='#8E44AD'   -f description='CH05 lab: active implementation' 
Idempotence note: do not blindly rerun create calls after a partial failure. Inspect first, then create only missing labels. Production automation should compare desired and current state and handle API status codes explicitly.

5. Create a project milestone and understand its scope

A milestone is a delivery grouping, not a board column. The project milestone in this lab cannot automatically coordinate sibling projects. A group milestone would be the appropriate shared resource when several descendant projects must participate.

# Inspect first.
glab api "projects/$PROJECT_ID/milestones?state=active&per_page=100" --paginate   --jq '.[] | {id,iid,title,state,start_date,due_date}'

# Create the synthetic milestone if it does not already exist.
glab api "projects/$PROJECT_ID/milestones" -X POST   -f title='CH05 Demo'   -f description='Disposable planning milestone for Chapter 05' 

6. Create a three-item backlog and inspect structured state

Use issue titles that clearly identify the lab. The first issue is the delivery slice you will connect to Git; the other two make the board useful enough to reason about.

REPO="group/example-project"   # replace with your disposable path

glab issue create -R "$REPO" -t '[CH05 LAB] Fix parser edge case'   -d 'Synthetic defect used to demonstrate planning metadata.'   -l type-bug,flow-ready -m 'CH05 Demo' --yes

glab issue create -R "$REPO" -t '[CH05 LAB] Add operator note'   -d 'Synthetic documentation task.'   -l type-docs,flow-ready -m 'CH05 Demo' --yes

glab issue create -R "$REPO" -t '[CH05 LAB] Add dry-run option'   -d 'Synthetic feature item.'   -l type-feature,flow-ready -m 'CH05 Demo' --yes

# Structured inspection. Capture the IIDs.
glab api "projects/$PROJECT_ID/issues?scope=all&state=opened&labels=flow-ready&per_page=100"   --paginate --jq '.[] | {iid,title,labels,milestone:(.milestone.title // null),assignees:[.assignees[].username]}' 

Expected state: three open issues, each with one type label, flow-ready, and milestone CH05 Demo. If a label is missing, repair the issue metadata rather than “fixing the board.”

7. Create or inspect a basic board, then prove card movement

Current glab includes glab issue board create. Create a project board named CH05 Flow if one does not exist. Add label lists for flow-ready and flow-doing through the current board UI (Plan → Issue boards) or the Boards API. The UI label may move in future releases, so the resource/API is the stable anchor.

glab issue board create 'CH05 Flow' -R "$REPO"

# Read-only board evidence.
glab api "projects/$PROJECT_ID/boards" --paginate   --jq '.[] | {id,name,lists:[.lists[]? | {id,label:(.label.name // null),position}]}' 

Now choose the parser issue. Prediction: moving it from the flow-ready label list to flow-doing should alter the issue’s labels. Perform the drag in the board UI, then verify independently:

ISSUE_IID="1"  # replace with the parser issue IID
glab api "projects/$PROJECT_ID/issues/$ISSUE_IID"   --jq '{iid,title,state,labels,milestone:(.milestone.title // null)}' 

If the result still contains flow-ready instead of flow-doing, do not repeat the drag blindly. Inspect the board list type/configuration and confirm you moved the correct card on the correct board.

8. Connect one work item to Git and a merge request

This chapter does not replace the later merge-request chapter. The goal here is only to prove that planning state and code-delivery state are separate objects with explicit links.

Use the issue IID in the branch name. GitLab can associate an issue/task when a branch starts with the item number. Make a harmless documentation-only change so the lab does not require application knowledge.

git switch -c "$ISSUE_IID-ch05-parser-note"
printf '
Chapter 05 planning lab marker.
' >> CH05_LAB.md
git add CH05_LAB.md
git commit -m "docs: add chapter 05 planning marker"
git push -u origin HEAD

# Create a draft MR that references the work item but does not merge it yet.
glab mr create -R "$REPO"   --source-branch "$ISSUE_IID-ch05-parser-note"   --title '[CH05 LAB] Parser planning slice'   --description "Closes #$ISSUE_IID

Disposable Chapter 05 planning exercise."   --draft --yes

glab mr list -R "$REPO" --output json   --jq '.[] | select(.title | startswith("[CH05 LAB]")) | {iid,title,state,source_branch,target_branch}' 

Expected state: the issue remains open because the draft MR has not merged into the project’s default branch. The MR description expresses a future closing relationship. This is a crucial distinction: writing Closes #N is not itself the close event.

10. Challenge: choose the correct control

Your team asks for three changes: “mark the parser issue as actively worked,” “put all three items in the same release goal,” and “show the items on a board.” Choose the control before touching the UI:

  • Active work → change the workflow metadata (flow-doing on the Free path).
  • Release goal → milestone.
  • Visual workflow → board configured to project those metadata values.

If your answer was “create three more boards,” the mental model is backwards. The metadata is the source state; boards are views.

Knowledge check

After a card moves to the Doing list, what is the best independent verification?

Why does the draft MR not immediately close the issue even though its description says Closes #N?

Why inspect labels and milestones before POSTing them?

Does creating a project board create new copies of the three issues?

If the team needs recurring two-week sprints on GitLab Free, should the lesson claim milestones are iterations?

Summary

You created the free-compatible planning core: ordinary labels, one project milestone, three issues, a board, a branch, and a draft merge request. Every important step was verified from the underlying issue/project state instead of trusting one visual view. The next lesson focuses on choosing which planning dimensions belong in a maintainable operating model.

Official references

Next lesson

Design the planning policy, not just the board

Lesson 3 compares labels, milestones, iterations, project/group scope, free versus portfolio features, and automation-driven transitions so the planning system remains explainable as it grows.

Keep the academy open

Support free, practical DevOps education.

Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.

Ethereum / ERC-20
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0Send only Ethereum/ERC-20 compatible assets to this address.