babygo

package module
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 29, 2026 License: MIT Imports: 1 Imported by: 0

README

babygo

Go by Example, run like rustlings.

Go by Example is 85 complete, beautifully annotated programs. It is excellent to read — and nothing is ever asked of you. Rustlings inverts that: it hands you broken code and a compiler, and you are not done until the check passes.

This is Go by Example with the load-bearing lines cut out. You put them back.

Credit. Every exercise here is derived from Go by Example by Mark McGranaghan and Eli Bendersky, used under CC BY 3.0. All 85 programs and all of the teaching are theirs; this project only removes lines and adds a checker. Go read the original — it is excellent.

Install

brew install krushiraj/tap/babygo          # macOS
go install github.com/krushiraj/babygo/cmd/babygo@latest

Or take a binary from releases.

Then set up somewhere to work:

babygo init      # writes ./babygo — 85 exercises and their solutions
cd babygo
babygo

The exercises travel inside the binary, so init needs no network and no clone. You do need Go 1.26+ on your PATH — babygo ships the exercises, the Go toolchain compiles them.

  babygo  13/85 solved  ·  14. Closures

  ✗ Closures  (exercises/14_closures/closures.go)

  compiles ✓   output ✗

  expected  got
  1         1
  2         2
  3         3
  1         4    ←

  hint: babygo hint closures

  h hint · r re-run · x reset · l list · q quit
  watching exercises/14_closures/closures.go — save to re-run

Edit the file, hit save, and it re-runs. Solve it and it stays put so you can keep poking at it; press n when you want the next one.

Commands

Run these inside the workspace babygo init made.

babygo the main loop — first unsolved exercise, re-runs on save
babygo run <name> check one exercise
babygo list progress
babygo hint <name> a nudge, not the answer
babygo reset <name> restore an exercise to how it shipped
babygo solution <name> show the working version
babygo update add exercises a newer babygo has and your workspace does not

Names work either way: closures or 14_closures.

update only ever writes exercises that are not there. It cannot touch one you have started, so upgrading babygo never costs you work.

Working in a clone

A clone of this repo is a workspace — a go.mod with exercises/ next to it is all babygo looks for — so contributors need no install:

git clone https://github.com/krushiraj/babygo && cd babygo
make watch

No dependencies for the exercises themselves: they are stdlib only, and a fresh clone works with no network.

How it relates to upstream

Go by Example is not forked. It is cloned read-only into upstream/ (gitignored), and the exercises here are a derived artifact that records the upstream hash it was cut from.

That is a deliberate choice. If you fork it, your edits are deletions of specific lines, so every upstream merge conflicts precisely where you cut — the conflict surface is 100% of your work. Instead:

make sync

re-fetches upstream and just tells you what moved:

  + new       range-over-iterators   no exercise yet
  ~ changed   closures               upstream source moved; re-cut the exercise

No merge, so nothing to conflict.

How exercises are checked

By running them and diffing stdout — the expected output is lifted from the solution itself, and in Go by Example the output is the lesson.

Not everything can be diffed exactly, so each exercise declares a mode:

mode check count
exact stdout matches byte for byte 60
regex each expected line is a pattern — timestamps, addresses, random values 13
test go test in the exercise dir 10
unordered same lines, any order — waitgroups, range-over-built-in-types 2

The modes were not assigned by guessing. babygo capture runs every solution ten times and sees what moves — that is how range-over-built-in-types was caught (it ranges a two-key map, and Go randomizes map order, so it agrees with itself half the time, and a two-run check calls it deterministic on a coin flip).

All 85 are machine-checked. There is no "tick the box to say you did it" mode. The design allowed for one as a last resort, on the assumption that http-server, tcp-server, context and signals could not be driven by a harness — they either block forever or want a second terminal. They can: httptest covers the HTTP ones, a port-0 listener with socket deadlines covers the TCP one, and signals sends itself the interrupt. An "I promise I solved it" checkbox is exactly the thing a learner reaches for on the exercises that would have taught them the most.

Contributing an exercise

Read docs/AUTHORING.md. The short version: cut the idea the example exists to teach, keep upstream's comments, and make sure

make verify

is green. It asserts the two things that actually matter, for all 85: every solution passes its own checker (so the exercise is solvable), and every shipped exercise fails it (so there is something to solve). An exercise that passes untouched is worse than no exercise — it teaches nothing while appearing to.

Design

The original design, from when the command was called gorun and only ran in a clone, and how it became installable.

Releasing

Tag it. .github/workflows/release.yml runs make verify, then GoReleaser builds macOS and Linux binaries and updates the Homebrew cask.

git tag v0.1.0 && git push origin v0.1.0

One-time setup: a krushiraj/homebrew-tap repo, and a HOMEBREW_TAP_TOKEN secret on this one holding a PAT with contents: write on the tap. make snapshot builds everything locally without publishing.

Credits and licensing

All 85 examples, and all of the teaching, are Mark McGranaghan and Eli Bendersky's Go by Example (CC BY 3.0).

The runner is MIT (LICENSE). exercises/ and solutions/ are derivatives of Go by Example and carry its attribution — see NOTICE. The exercise format is rustlings'.

Documentation

Overview

Package babygo carries the exercise set inside the binary.

It exists at the repo root for one boring reason: //go:embed cannot reach above its own directory, and exercises/ and solutions/ have to stay at the root so that a clone of this repo is itself a usable workspace. Everything that reads these bytes lives in internal/workspace and takes an fs.FS, so it can be tested against a fake tree instead of all 85 real ones.

Index

Constants

This section is empty.

Variables

View Source
var Assets embed.FS

Assets is the exercise set as it shipped: exercises/<slug>/… and solutions/<slug>/…, byte for byte what was in the tree this binary was built from.

The all: prefix is belt and braces. Nothing in the set starts with "." or "_" today, but an exercise about dotfiles is an obvious thing to write, and it would go missing from every installed copy without a word of warning.

Functions

This section is empty.

Types

This section is empty.

Directories

Path Synopsis
cmd
babygo command
Command babygo is a rustlings-style exercise runner for Go by Example.
Command babygo is a rustlings-style exercise runner for Go by Example.
exercises
01_hello-world command
Our first program will print the classic "hello world" message.
Our first program will print the classic "hello world" message.
02_values command
03_variables command
04_constants command
05_for command
06_if-else command
07_switch command
08_arrays command
09_slices command
10_maps command
11_functions command
14_closures command
15_recursion command
17_pointers command
19_structs command
20_methods command
21_interfaces command
22_enums command
24_generics command
26_errors command
28_goroutines command
29_channels command
33_select command
34_timeouts command
38_timers command
39_tickers command
40_worker-pools command
41_waitgroups command
44_mutexes command
46_sorting command
48_panic command
49_defer command
50_recover command
55_json command
56_xml command
57_time command
58_epoch command
62_url-parsing command
67_line-filters command
Here's an example line filter in Go that writes a capitalized version of all input text.
Here's an example line filter in Go that writes a capitalized version of all input text.
68_file-paths command
The `filepath` package provides functions to parse and construct *file paths* in a way that is portable between operating systems; `dir/file` on Linux vs.
The `filepath` package provides functions to parse and construct *file paths* in a way that is portable between operating systems; `dir/file` on Linux vs.
69_directories command
71_embed-directive command
`//go:embed` is a [compiler directive](https://pkg.go.dev/cmd/compile#hdr-Compiler_Directives) that allows programs to include arbitrary files and folders in the Go binary at build time.
`//go:embed` is a [compiler directive](https://pkg.go.dev/cmd/compile#hdr-Compiler_Directives) that allows programs to include arbitrary files and folders in the Go binary at build time.
77_logging command
The Go standard library provides straightforward tools for outputting logs from Go programs, with the log(https://pkg.go.dev/log) package for free-form output and the log/slog(https://pkg.go.dev/log/slog) package for structured output.
The Go standard library provides straightforward tools for outputting logs from Go programs, with the log(https://pkg.go.dev/log) package for free-form output and the log/slog(https://pkg.go.dev/log/slog) package for structured output.
78_http-client command
The Go standard library comes with excellent support for HTTP clients and servers in the `net/http` package.
The Go standard library comes with excellent support for HTTP clients and servers in the `net/http` package.
79_http-server command
Writing a basic HTTP server is easy using the `net/http` package.
Writing a basic HTTP server is easy using the `net/http` package.
80_tcp-server command
The `net` package provides the tools we need to easily build TCP socket servers.
The `net` package provides the tools we need to easily build TCP socket servers.
81_context command
In the previous example we looked at setting up a simple [HTTP server](http-server).
In the previous example we looked at setting up a simple [HTTP server](http-server).
84_signals command
85_exit command
internal
exercise
Package exercise loads the exercise set from disk.
Package exercise loads the exercise set from disk.
progress
Package progress tracks which exercises are solved.
Package progress tracks which exercises are solved.
upstream
Package upstream reads a checkout of Go by Example.
Package upstream reads a checkout of Go by Example.
verify
Package verify decides whether an exercise is solved.
Package verify decides whether an exercise is solved.
workspace
Package workspace creates and maintains the directory a learner works in.
Package workspace creates and maintains the directory a learner works in.
solutions
01_hello-world command
Our first program will print the classic "hello world" message.
Our first program will print the classic "hello world" message.
02_values command
03_variables command
04_constants command
05_for command
06_if-else command
07_switch command
08_arrays command
09_slices command
10_maps command
11_functions command
14_closures command
15_recursion command
17_pointers command
19_structs command
20_methods command
21_interfaces command
22_enums command
24_generics command
26_errors command
28_goroutines command
29_channels command
33_select command
34_timeouts command
38_timers command
39_tickers command
40_worker-pools command
41_waitgroups command
44_mutexes command
46_sorting command
48_panic command
49_defer command
50_recover command
55_json command
56_xml command
57_time command
58_epoch command
62_url-parsing command
67_line-filters command
Here's an example line filter in Go that writes a capitalized version of all input text.
Here's an example line filter in Go that writes a capitalized version of all input text.
68_file-paths command
The `filepath` package provides functions to parse and construct *file paths* in a way that is portable between operating systems; `dir/file` on Linux vs.
The `filepath` package provides functions to parse and construct *file paths* in a way that is portable between operating systems; `dir/file` on Linux vs.
69_directories command
71_embed-directive command
`//go:embed` is a [compiler directive](https://pkg.go.dev/cmd/compile#hdr-Compiler_Directives) that allows programs to include arbitrary files and folders in the Go binary at build time.
`//go:embed` is a [compiler directive](https://pkg.go.dev/cmd/compile#hdr-Compiler_Directives) that allows programs to include arbitrary files and folders in the Go binary at build time.
77_logging command
The Go standard library provides straightforward tools for outputting logs from Go programs, with the log(https://pkg.go.dev/log) package for free-form output and the log/slog(https://pkg.go.dev/log/slog) package for structured output.
The Go standard library provides straightforward tools for outputting logs from Go programs, with the log(https://pkg.go.dev/log) package for free-form output and the log/slog(https://pkg.go.dev/log/slog) package for structured output.
78_http-client command
The Go standard library comes with excellent support for HTTP clients and servers in the `net/http` package.
The Go standard library comes with excellent support for HTTP clients and servers in the `net/http` package.
79_http-server command
Writing a basic HTTP server is easy using the `net/http` package.
Writing a basic HTTP server is easy using the `net/http` package.
80_tcp-server command
The `net` package provides the tools we need to easily build TCP socket servers.
The `net` package provides the tools we need to easily build TCP socket servers.
81_context command
In the previous example we looked at setting up a simple [HTTP server](http-server).
In the previous example we looked at setting up a simple [HTTP server](http-server).
84_signals command
85_exit command

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL