Write a Tutorial
Write a comprehensive, learning-oriented tutorial for newcomers to the ZIO library being documented.
Tutorial Topic
$ARGUMENTS
If no topic appears above (or the literal text $ARGUMENTS is shown), ask the user what tutorial they want to write before proceeding. Do not invent a topic.
Overview: What Makes a Good Tutorial
A tutorial is learning-oriented — it teaches concepts and builds mental models for newcomers encountering a topic for the first time. It is neither a reference page (which documents an API exhaustively) nor a how-to guide (which helps practitioners accomplish a specific task). A tutorial assumes the reader has no prior knowledge and follows a linear, carefully controlled learning path.
Key properties of a good tutorial:
- Targets newcomers: The reader is encountering this topic for the first time. Assume nothing.
- Teaches concepts, not tasks: The goal is understanding, not accomplishing a specific thing yet.
- Linear path: No branching ("if you need X, do Y instead"). Pick one path and follow it.
- Minimal, annotated code: Code demonstrates concepts; it is not production-ready. Every code example is annotated line-by-line.
- Learning objectives stated upfront: The reader knows what they will understand by the end.
- Intermediate output: After each step, show results so the learner can verify they're on track.
- Warm, welcoming tone: Use "Welcome", "Let's", "notice that", "try changing X to see Y".
- Recap at the end: "What You've Learned" restates objectives as completed achievements.
Step 1: Deep Research — Understand the Topic Landscape
Before writing a single word, you must build a complete mental model of every type, method, pattern, and concept relevant to the tutorial topic. This is the most critical step.
Delegate to the docs-researcher agent with the Task tool — it must NOT share your
conversation, so its only knowledge of what to research is what you tell it:
Task(
description: "Research <topic> for a tutorial",
subagent_type: "documentation:docs-researcher",
prompt: "Research <topic> for a learning-oriented tutorial. Find: the ONE core concept or skill this
tutorial teaches; prerequisite knowledge the learner must already have; for each core type,
what it is and its role in learning this concept; the dependency/composition order (first
understand X, then how Y builds on X, then combine with Z); factory methods and constructors
the learner will actually use; the simplest possible starting point and the layers of
complexity that can be added incrementally; a candidate 'aha moment'; imports and sbt
dependency; commit history for why this is shaped the way it is."
)
Read its findings before proceeding — if they're missing or thin, say so and delegate again rather than filling the gap yourself.
1e. Answer These Research Questions
Before proceeding to writing, you must be able to answer every one of these questions. Write the answers down (internally) as they will directly inform the tutorial structure:
About the learning goals:
- What is the ONE core concept or skill this tutorial teaches?
- What prerequisite knowledge must the learner already have? (e.g., "basic Scala syntax", "understanding of case classes")
- What will the learner be able to do after completing this tutorial?
- What mental model or conceptual framework will the learner have?
About the types involved:
- For each core type: What is it, in one sentence? What role does it play in learning this concept?
- What is the dependency/composition order? (e.g., "First understand X, then understand how Y builds on X, then combine them with Z")
- Which factory methods and constructors will the learner actually use?
- What type class instances are derived automatically vs. must be created manually?
About the narrative arc:
- What is the simplest possible starting point? (The "hello world" for this concept)
- What layers of complexity can be added incrementally? (e.g., start with a flat structure, then add nesting, then add variation)
- Where should you pause to show intermediate results? (e.g., print output, display a value, demonstrate behavior)
- What is the natural ending point — the "complete" version?
- What is one key "aha moment" you want the learner to have?
About the ecosystem:
- What imports does the learner need?
- What sbt dependencies are required?
- Are there Scala 2 vs. Scala 3 differences the learner should know about?
- Does this integrate with other ZIO libraries (ZIO HTTP, ZIO Streams, etc.)?
Step 2: Design the Tutorial Structure
Based on your research answers, design the tutorial's section structure before writing. A tutorial follows this general skeleton:
Structural Template
1. Introduction
- Who this is for (newcomer with no prior knowledge)
- Learning objectives (bullet list)
- Overview of what the tutorial covers (brief outline)
- "We recommend reading from top to bottom"
2. Background / The Big Picture (optional, 1-2 paragraphs)
- Conceptual framing: what problem this API was designed to solve
- No code — just mental model
3. Concept sections (3-6 sections, each one new idea)
- Explanation of the concept (1-3 sentences)
- Minimal working code block (annotated line-by-line)
- Output or result showing it worked
- No branching, no "alternatively"
4. Putting It Together
- The complete, runnable example combining all concepts
- mdoc:compile-only block
5. Running the Examples
- Git clone + sbt runMain per step (same format as how-to guides)
6. What You've Learned
- Bullet-point recap of each learning objective
7. Where to Go Next
- Links to how-to guides (for applying the knowledge in practice)
- Links to reference pages (for API depth)
Section Design Rules
- Linear progression: No branching. Never say "if you need X, do Y instead". Pick one path.
- One concept per section: Each section introduces exactly one new idea or builds incrementally on previous sections.
- Concept before code: Always explain what the code will do and why before showing it.
- Every section has code: No pure-prose sections. Every concept is demonstrated with code.
- Line-by-line annotation: Every code block is followed by a bullet-point breakdown explaining each line or block of lines.
- Show intermediate output: After meaningful steps, show or print results so the learner can verify they're on track.
- Limit scope aggressively: A tutorial about "understanding Scope" should not become "everything you can do with Scope". Stay on the learning objective.
Narrative Planning
Plan the tutorial arc:
- Choose a relatable domain: Pick something the learner can understand without domain expertise (e.g., "a simple configuration system", "managing a resource").
- Start with the simplest example: Something that can be explained in 3-4 lines and demonstrates the core concept.
- Build incrementally: Each subsequent section adds one layer of complexity or introduces one supporting concept.
- Plan the "show moments": Points where you print, observe, or demonstrate behavior. These are crucial for learner confidence.
Step 3: Write the Tutorial
File Location and Frontmatter
Place the file in docs/guides/ directory (same location as how-to guides):
---
id: <kebab-case-id>
title: "<Tutorial Title>"
description: "A 50-150 character summary of what this tutorial teaches."
keywords:
- "General Domain Concept"
- "Page-Specific Concept"
- "<Core Type Name>"
---
description and each keywords entry are double-quoted; keywords is a block list (one - "item"
per line), 3-6 entries. Write these now — don't rely on a later docs-backfill-metadata pass.
The id must match the filename (without .md), and must be specific to this tutorial's actual
angle: compositional-fiberref-updates, not fiberref. A vague id invites the next tutorial about the
same type to collide with it, or to be titled just as vaguely.
Writing the Sections
Section Heading Format: Use numbered section headings (e.g., "## 1. Topic Name", "## 2. Next Topic") instead of wordy descriptors. This makes sections scannable and progress clear to the learner.
Introduction
Start with a welcome and clear statement of who this tutorial is for:
Welcome to [Tutorial Title]! This tutorial is for [target learner] who [assumed prior knowledge]. You don't need any prior experience with [topic] to follow along.
Immediately follow with Learning Objectives — a concise bulleted list (3–5 bullets max) of what the learner will understand by the end, plus a brief outline of section titles:
## Learning Objectives
By the end of this tutorial, you will understand:
- What [concept A] is and why it matters
- How to [do task B] with [API C]
- The relationship between [concept D] and [concept E]
We'll learn these concepts through:
1. [Section Title]
2. [Section Title]
3. [Section Title]
We recommend reading from top to bottom.
Pattern:
Welcome to [Topic]! This tutorial is designed for newcomers who [assumed prior knowledge — be specific].
## Learning Objectives
By the end, you will understand:
- [objective 1]
- [objective 2]
- [objective 3]
We'll learn these concepts through:
1. [Introduction to core concept]
2. [Building on concept: variation 1]
3. [Building on concept: variation 2]
...
We recommend reading from top to bottom — each section builds on the previous one.
Background / The Big Picture (Optional)
If helpful, include 1-2 paragraphs that frame the conceptual motivation:
- What problem was this API designed to solve?
- What is the big mental model?
- Why does this matter?
Important: No code in this section. This is pure conceptual framing.
Pattern:
## Background
[1-2 paragraphs explaining the conceptual motivation, the problem this type solves, or the mental model you should have. No code.]
Concept Sections (3-6 sections, 1 new concept per section)
For each concept section:
- Lead with 1-3 sentences explaining the concept and why it matters.
- Show minimal, annotated code in an appropriate mdoc block.
- Annotate the code line-by-line with bullet points immediately after the code block:
```scala mdoc
val x = foo() // create something
println(x)
```
The code above:
- `foo()` — creates [what]
- `println(x)` — prints the result to see what was created
Show the result — if the code produces output, use
mdoc(notmdoc:compile-only) to show evaluated output. When a code block's output is important for understanding, always display the actual output after the code block (prefaced with "Output:" or similar).Add a brief explanation if the result is non-obvious.
Use Docusaurus admonitions for important notes and insights:
:::note
[Important observation that the learner should remember.]
:::
:::tip
[Practical guidance or a useful pattern.]
:::
:::caution
[Something to watch out for or a common mistake.]
:::
Provide copy-pasteable code whenever possible. If code is intentionally incomplete or demonstrates an error:
- Use clear prose descriptions instead of misleading comments like
// ERROR: ... - If showing a compile failure, provide actual compilable code or state explicitly that the code does not compile
- Never use pseudo-code with fake error messages
- Use clear prose descriptions instead of misleading comments like
Never branch: Do not write "alternatively, you could..." or "if you need X, use Y instead". Pick one approach and follow it.
Putting It Together
Near the end, show the complete working example that combines everything from the tutorial. This is
not an inline code block. It is an EMPTY block fenced scala mdoc:embed:<path-to-CompleteExample.scala>
(or the project's SourceFile.print equivalent — see docs-examples) pointing at the companion
CompleteExample.scala that Step 4 builds. Never soften this to "may include an embedded example" or
write the code inline instead: the embedded file is what the examples build actually compiles, so
inlining ships code no build has verified as standalone — and it also means Step 4 has nothing to
build, since nothing in the page names a file for it to create.
Add a brief explanation: "This example combines all the concepts we've learned — [quick summary]."
Running the Examples
Follow the "Running the Examples" section template from the docs-examples skill. It provides the exact Markdown pattern to use in your tutorial, substituting the correct <packagename> and example object names.
Two tutorial-specific details:
- Clone and
cdin one path straight into the examples module directory —cd <repo>/<examples-module>, not a separatecd <repo>followed by a separatecd <examples-module>. - Embed each concept's file, in order, the same way "Putting It Together" does: a collapsible
<details><summary>wrappingscala mdoc:embed:<path>:show-line-numbers, a short sentence on how to run it, and itssbt "<examples-module>/runMain ..."command.
What You've Learned
Recap what the learner accomplished. Mirror the "Learning Objectives" section from the introduction, but restate them as achievements:
## What You've Learned
In this tutorial, you learned:
- What [concept A] is and why it matters
- How to [do task B] with [API C]
- The relationship between [concept D] and [concept E]
- How to [construct/use] [type F]
You now have a solid foundation in [topic]. The next step is to see how to [apply this in practice].
Where to Go Next
Provide links to help the learner deepen their knowledge. Check each target page exists before linking to it. Mention a guide or reference page in prose, unlinked, when it doesn't exist yet — never a placeholder link to be resolved later, and never accept a stub page written just to make one resolve. A tutorial run writes a tutorial, not a reference page it happens to link to.
## Where to Go Next
- **Ready to use this in practice?** Check out the how-to guide [Guide Name](../guides/guide-name.md) which walks through a real-world example.
- **Want to dive deeper into the API?** Read the reference page for [`TypeName`](../reference/type-name.md).
- **Interested in related concepts?** Explore [Related Topic](./related-topic.md).
Writing Style Rules
See the docs-writing-style skill for universal prose style, Scala version rules, and code block conventions.
Additional style notes for tutorials:
- Use warm, welcoming language: "Welcome", "Let's", "Notice that", "Try changing X to see what happens"
- Use present tense: "we learn", "we see", "we observe"
- Address the learner directly: "you now understand", "you can now do"
- Keep explanations brief and clear — a tutorial is about understanding, not about exhaustively covering every detail
Compile-Checked Code Blocks with mdoc
See the docs-mdoc-conventions skill for the complete mdoc modifier table, key rules, and the "For Tutorials (Linear Learning Path)" section (or equivalent) which explains the recommended modifier sequence for tutorials specifically.
Docusaurus Admonitions
See the docs-mdoc-conventions skill for admonition syntax and usage guidelines.
Step 4: Create Companion Examples
Every tutorial has them — this step is never optional. "Putting It Together" and each concept in
"Running the Examples" are mdoc:embed blocks (Step 3), not inline code, so those embeds have nothing
to resolve to until this step runs. If you find yourself skipping this step because the page "doesn't
need it," that means Step 3 was written wrong — go back and fix the page to embed, then build the
files here. Skipping this because nothing appears to embed is how the whole examples phase silently
stops running.
Use the docs-companion-examples skill to commission and verify companion examples — it delegates
the build (directory structure, file templates, compilation, lint) and checks what came back.
Step 5: Verify Mdoc Compilation
Before integrating, verify that all code examples in the tutorial compile — scoped to this file, never
unscoped (sbt docs/mdoc alone recompiles all documentation, ~90 seconds):
sbt "docs/mdoc --in docs/guides/<tutorial-name>.md --out website/docs/guides/<tutorial-name>.md"
--out is the same path prefixed with website/. Fix any compilation errors before proceeding.
Step 6: Fact-Check
Same reason as docs-data-type-ref's fact-check step: a tutorial's prose is looser than a
reference page's, but its claims about the library are not. A step that names a method the library
doesn't have, or describes behavior the source contradicts, sends a learner nowhere — and nothing
before this step verifies tutorial prose against real source; mdoc only verified that the code
compiles, not that the surrounding claims about it are true.
Delegate to the docs-reviewer agent with the Task tool, asking it to fact-check (the agent
covers both a full-page checklist/style review and a fact-check — say plainly which this call is):
Task(
description: "Fact-check <tutorial-id> tutorial",
subagent_type: "documentation:docs-reviewer",
prompt: "Fact-check this page against source (not a checklist/style review).
Page: docs/guides/<tutorial-id>.md
Subject: <topic>
Library source root: <path>"
)
It reports each drift with the exact corrected statement, not just a description of what's wrong.
Never fix a drift yourself: delegate every reported drift, verbatim (its exact statement included), to
the docs-fixer agent with the Task tool — correcting only the tutorial, never the source:
Task(
description: "Apply fact-check fixes to <tutorial-id> tutorial",
subagent_type: "documentation:docs-fixer",
prompt: "Page: docs/guides/<tutorial-id>.md
Findings (apply each verbatim):
<every drift from the fact-check reply, its exact statement included>"
)
Bounded rounds, same discipline: after docs-fixer returns, re-run mdoc, then delegate the same
fact-check to docs-reviewer once more — that confirming round is what records the tutorial as
clean. Genuinely new drifts in a confirming round earn another (delegate those to docs-fixer too),
up to 3 total; a round repeating the same drifts ends it.
Step 7: Integrate
Delegate to the docs-integrator agent with the Task tool:
Task(
description: "Integrate <tutorial-id> tutorial",
subagent_type: "documentation:docs-integrator",
prompt: "Page: docs/guides/<tutorial-id>.md
Category: Guides
Cross-reference direction: add 'See also' links from related reference pages that already
exist (e.g. if this tutorial teaches Scope, from docs/reference/scope.md) — check first, and
name which do. Never ask for a reference page to be created to satisfy a link, and never
accept a stub written to make one resolve."
)
Tutorials should also link to related how-to guides in the "Where to Go Next" section (Step 3) to guide learners toward practical application — that's page content, not something to ask the agent for.
Step 8: Final Review
Delegate to the docs-reviewer agent with the Task tool, asking for a full-page review: give
it CHECKLIST.md's content, have it evaluate the tutorial against every item, and
tell it to give the exact corrected statement for every failing item, not just what's wrong.
Task(
description: "Review <tutorial-id> tutorial",
subagent_type: "documentation:docs-reviewer",
prompt: "Full-page review (not a fact-check): evaluate docs/guides/<tutorial-id>.md against this
checklist: <CHECKLIST.md's content>
For every failing item, give the exact corrected statement to apply."
)
Never fix a failing item yourself: delegate everything review reports, verbatim (the exact statement
for each), to the docs-fixer agent with the Task tool:
Task(
description: "Apply review fixes to <tutorial-id> tutorial",
subagent_type: "documentation:docs-fixer",
prompt: "Page: docs/guides/<tutorial-id>.md
Findings (apply each verbatim):
<every failing item from the review reply, its exact statement included>"
)
Bounded rounds: after docs-fixer returns, re-run mdoc, then delegate the same review to
docs-reviewer once more — that confirming round is what records the tutorial as passing, since the
verdict is whatever the last review found. Genuinely NEW failing items in a confirming round earn
another round (delegate those to docs-fixer too), up to 3 total; a round repeating the same
failures ends it — name what's still failing in your summary. A review that reported nothing needs no
confirming round.