Stage 1 · Code
Building a Terminal Dashboard with Bubble Tea
Scoping burnrate & Building the Core Logic
Before touching Bubble Tea, define exactly what the tool does and build its math as a small, pure package with no dependency on the terminal at all.
Write the Spec First
The capstone module taught you to write a plain-language spec before writing code, so temptation to over-build has nowhere to hide. The same discipline applies here — a live terminal dashboard is more visually interesting than a task tracker, and that is exactly the kind of project where scope quietly balloons if you don't pin it down first.
# User: an engineer who owns a production service with an SLO
# Problem: see, at a glance, whether the service is burning its error
# budget too fast — without digging through a metrics dashboard
# Data: either simulated live traffic, or a replayed JSON snapshot
# Commands:
burnrate demo [--service NAME] [--slo 99.9]
burnrate run --file snapshot.json [--service NAME] [--slo 99.9]
burnrate help
# Behavior:
# - "demo" starts an instant live dashboard fed by simulated traffic
# - "run --file" replays a fixed snapshot of real per-minute counts
# - both modes show two burn-rate windows (fast + slow) and a 30-day
# error-budget gauge
# - "q" quits, "space" pauses/resumes the live feedThis spec fits on one screen and describes behavior, not implementation — the same shape the tasker spec took. Everything you build for the rest of this module exists to satisfy exactly these lines, nothing more.
Lay Down the Skeleton
burnrate/go.modcreateResponsibility: Declares the module path so bubbletea, lipgloss, and bubbles resolve correctly.
Why now: A TUI project needs its dependencies resolvable from the very first go get.
burnrate/cmd/burnrate/main.gocreateResponsibility: Parses the demo and run subcommands and starts the Bubble Tea program.
Why now: A shipped CLI needs one obvious entrypoint, exactly like tasker's cmd/tasker/main.go.
burnrate/internal/slo/budget.gocreateResponsibility: Pure burn-rate arithmetic and a rolling error-budget tracker — no terminal code at all.
Why now: The math has to exist and be correct before there is anything worth displaying.
Connects to: Consumed by internal/tui/model.go in the next lesson.
burnrate/internal/slo/budget_test.gocreateResponsibility: Verifies burn-rate math and budget tracking against known inputs.
Why now: This is the part of the project where a subtle bug would be easy to miss just by watching the dashboard — tests catch it before the UI ever hides it.
burnrate/internal/data/source.gocreateResponsibility: Defines the Source interface plus a simulated demo feed and a JSON snapshot reader.
Why now: The dashboard should not care whether numbers come from a fake feed or a real file — that's what the interface is for.
Connects to: Implements the data the internal/tui model consumes.
burnrate/internal/tui/model.gocreateResponsibility: The Bubble Tea model: Init, Update, and View for the whole dashboard.
Why now: This is where the Elm Architecture from the first lesson becomes real, running code.
Connects to: Built on top of internal/slo and internal/data; styled with internal/tui/styles.go.
mkdir -p burnrate/{cmd/burnrate,internal/{slo,data,tui}}
cd burnrate
go mod init github.com/thesyscoder/burnrate
go get github.com/charmbracelet/bubbletea
go get github.com/charmbracelet/lipgloss
go get github.com/charmbracelet/bubblesThree packages, one from each concern: internal/slo for math, internal/data for where numbers come from, internal/tui for the screen. That separation is the single most important design decision in this project.
The Core: internal/slo
An SLO (service level objective) target like 99.9% implies an allowed error rate of 0.1%. If a service's actual error rate over some window is higher than that, it is "burning" its error budget faster than intended. A burn rate of 1.0 means "exactly on pace to spend the whole budget by the end of the period"; a burn rate of 14.4 means "fast enough to exhaust a 30-day budget in about two hours." Pairing a short, sensitive window with a longer, noise-resistant one catches both a sudden outage and a slow-building regression without paging on every blip — a real technique described in Google's SRE workbook.
Testing the Math, Not the Screen
Because internal/slo has no dependency on Bubble Tea, testing it is ordinary table-driven Go — the same pattern from earlier modules, with no need to simulate keypresses or capture rendered frames.
Separating internal/task from internal/cli in the capstone, and internal/slo from internal/tui here, is the same architectural instinct applied twice. Business logic that doesn't know what will eventually display it is logic you can test in milliseconds and reuse anywhere — a script, a web handler, or, as you're about to see, a live terminal dashboard.
Mark this lesson complete to store local progress and unlock a cleaner resume path the next time you visit.