Code Examples

Code examples are the core of technical documentation. Readers trust your prose because your code works. This chapter covers creating, formatting, and maintaining code examples that are accurate, instructive, and maintainable.

Principles of Good Code Examples

Complete over clever. A reader should be able to copy an example and run it. Include imports, setup, and necessary context. An elegant one-liner that requires invisible context is less useful than a verbose example that works.

Realistic over minimal. Examples should resemble real code readers will write. Use realistic variable names, realistic data, and realistic error handling. Minimal examples teach syntax; realistic examples teach practice.

Focused over comprehensive. Each example should illustrate one concept. Avoid combining multiple new ideas in a single example. If an example requires understanding A, B, and C, ensure A and B were introduced in previous examples.

Consistent over varied. Use the same patterns throughout the book. If chapter 3 uses result as a variable name for operation results, chapter 7 should too. Variation confuses readers who are pattern-matching.

Code Block Formatting

mdBook uses standard Markdown fenced code blocks with language identifiers:

```rust
fn main() {
    println!("Hello, world!");
}
```

Always specify the language. This enables syntax highlighting and signals the technology to readers.

Supported Language Identifiers

Common identifiers:

LanguageIdentifier
Rustrust
JavaScriptjavascript or js
TypeScripttypescript or ts
Pythonpython or py
Gogo
Javajava
Cc
C++cpp
Shell/Bashbash or shell
JSONjson
YAMLyaml
TOMLtoml
SQLsql
HTMLhtml
CSScss

For configuration or output that isn't a programming language, use text or omit the identifier.

Line Highlighting

mdBook supports line highlighting to draw attention to specific parts:

```rust,hl_lines=3 4
fn main() {
    let name = "World";
    // These lines are highlighted
    println!("Hello, {}!", name);
}
```

Use highlighting to focus attention on new concepts within familiar boilerplate.

Hiding Lines

Hide setup code that distracts from the lesson:

```rust
# fn main() {
let x = 5;
println!("{}", x);
# }
```

Lines starting with # are hidden in the output but included in compilation. This keeps examples focused while remaining complete.

Prompting for Code Examples

When requesting code examples from Claude Code, be specific:

Weak prompt:

Show how to handle errors.

Strong prompt:

Create a code example showing HTTP error handling in [framework].

Requirements:
- Complete, runnable example
- Include imports
- Handle both client errors (4xx) and server errors (5xx)
- Show the custom error type definition
- Include a handler function that returns these errors
- Use realistic error messages

Context: This follows an example showing basic request handling.
The reader hasn't seen error handling yet.

Requesting Incremental Examples

For complex topics, request a series of examples that build on each other:

Create a progression of 4 code examples teaching async/await:

Example 1: Single async function call
- Simplest possible async operation
- Show the await point explicitly

Example 2: Sequential async calls
- Two operations that must happen in order
- Demonstrate value passing between awaits

Example 3: Concurrent async calls
- Two operations that can run simultaneously
- Show the concurrency primitive used

Example 4: Error handling in async code
- Async operation that can fail
- Proper error propagation

Each example should be complete and runnable.
Each should build on concepts from the previous example.

Code Example Types

Conceptual Examples

Illustrate a concept in isolation:

#![allow(unused)]
fn main() {
// Ownership moves when assigned
let s1 = String::from("hello");
let s2 = s1;  // s1 is no longer valid

// This would fail to compile:
// println!("{}", s1);  // error: value borrowed after move
}

Conceptual examples can be short. Their purpose is understanding, not replication.

Procedural Examples

Show how to accomplish a task:

use std::fs::File;
use std::io::{self, BufRead};
use std::path::Path;

fn read_lines<P>(filename: P) -> io::Result<Vec<String>>
where
    P: AsRef<Path>,
{
    let file = File::open(filename)?;
    let reader = io::BufReader::new(file);
    reader.lines().collect()
}

fn main() -> io::Result<()> {
    let lines = read_lines("input.txt")?;
    for line in lines {
        println!("{}", line);
    }
    Ok(())
}

Procedural examples should be complete and directly usable.

Anti-Pattern Examples

Show what not to do:

#![allow(unused)]
fn main() {
// DON'T: This creates a race condition
let counter = Rc::new(RefCell::new(0));
let c1 = counter.clone();
let c2 = counter.clone();

thread::spawn(move || {
    *c1.borrow_mut() += 1;  // Not thread-safe!
});

thread::spawn(move || {
    *c2.borrow_mut() += 1;  // Not thread-safe!
});
}

Always label anti-patterns clearly. Follow with the correct approach:

#![allow(unused)]
fn main() {
// DO: Use Arc and Mutex for thread-safe shared state
let counter = Arc::new(Mutex::new(0));
let c1 = counter.clone();
let c2 = counter.clone();

thread::spawn(move || {
    let mut num = c1.lock().unwrap();
    *num += 1;
});

thread::spawn(move || {
    let mut num = c2.lock().unwrap();
    *num += 1;
});
}

Before/After Examples

Show refactoring improvements:

Before:

#![allow(unused)]
fn main() {
fn process(data: Option<String>) -> String {
    if data.is_some() {
        let s = data.unwrap();
        if s.len() > 0 {
            s.to_uppercase()
        } else {
            "empty".to_string()
        }
    } else {
        "none".to_string()
    }
}
}

After:

#![allow(unused)]
fn main() {
fn process(data: Option<String>) -> String {
    match data {
        Some(s) if !s.is_empty() => s.to_uppercase(),
        Some(_) => "empty".to_string(),
        None => "none".to_string(),
    }
}
}

Before/after pairs make improvements concrete and memorable.

External Code Inclusion

For longer examples, store code in separate files and include it:

```rust
{{#include ../examples/ch05/complete_server.rs}}
```

The {{#include path}} directive pulls the entire file into the code block.

Including Specific Sections

Use anchor comments to include portions:

In complete_server.rs:

fn main() {
    // ANCHOR: setup
    let config = Config::load();
    let db = Database::connect(&config.database_url);
    // ANCHOR_END: setup

    // ANCHOR: server
    let server = Server::new(config.port);
    server.run(db);
    // ANCHOR_END: server
}

In your markdown, reference anchors with {{#include path:anchor}}:

First, set up the configuration:

```rust
{{#include ../examples/ch05/complete_server.rs:setup}}
```

Then start the server:

```rust
{{#include ../examples/ch05/complete_server.rs:server}}
```

This keeps examples maintainable while showing only relevant portions.

Validating Code Examples

Code examples must work. Establish a validation process:

Inline Validation

For short examples, test manually during review:

I've written a code example for error handling. Before I include
it in the chapter, verify it compiles and runs correctly.

[paste example]

If there are issues, fix them and explain what was wrong.

Automated Validation

For books with many examples, create a validation script:

#!/bin/bash
# scripts/validate-examples.sh

set -e

echo "Validating Rust examples..."
for dir in examples/*/; do
    if [ -f "$dir/Cargo.toml" ]; then
        echo "Checking $dir"
        (cd "$dir" && cargo check)
    fi
done

echo "All examples valid!"

Run validation before committing and as part of CI.

Testing Examples

Where possible, add tests to example code:

#![allow(unused)]
fn main() {
fn add(a: i32, b: i32) -> i32 {
    a + b
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_add() {
        assert_eq!(add(2, 2), 4);
        assert_eq!(add(-1, 1), 0);
    }
}
}

Tests prove examples work and catch regressions when updating.

Common Code Example Problems

Missing Imports

Problem: Examples assume imports exist.

// Won't compile alone
fn main() {
    let file = File::open("test.txt")?;  // File not imported
}

Solution: Always include necessary imports or use hidden lines:

use std::fs::File;
use std::io;
fn main() -> io::Result<()> {
let file = File::open("test.txt")?;
Ok(())
}

Unrealistic Error Handling

Problem: Examples use .unwrap() everywhere.

fn main() {
    let file = File::open("config.json").unwrap();
    let config: Config = serde_json::from_reader(file).unwrap();
    // ...
}

Solution: Show proper error handling for examples readers will copy:

fn main() -> Result<(), Box<dyn Error>> {
    let file = File::open("config.json")
        .context("Failed to open config file")?;
    let config: Config = serde_json::from_reader(file)
        .context("Failed to parse config")?;
    // ...
    Ok(())
}

Reserve .unwrap() for examples explicitly teaching happy-path concepts.

Context-Dependent Examples

Problem: Example requires context established elsewhere.

#![allow(unused)]
fn main() {
// What is `db`? Where does it come from?
let users = db.query("SELECT * FROM users")?;
}

Solution: Show context or use comments:

#![allow(unused)]
fn main() {
// Assuming `db` is a database connection from setup code
// (see Chapter 3 for database setup)
let users = db.query("SELECT * FROM users")?;
}

Obsolete Examples

Problem: API changes make examples outdated.

Solution: Document the version. Use Cargo.toml in example projects:

[dependencies]
some-crate = "2.1"  # Examples tested with this version

Requesting Code Reviews from Claude Code

After writing examples, request review:

Review all code examples in chapter 5. For each example, check:

1. Does it compile without modification?
2. Are all imports shown or properly hidden?
3. Does error handling match our book's conventions?
4. Would copying this example actually teach the intended concept?
5. Are variable names consistent with earlier chapters?

List any issues found with specific corrections.

Address issues before finalizing the chapter.

Next Steps

With solid code examples in place, the next chapter covers quality control — systematic approaches to ensuring comprehensive, accurate documentation.