Project 2: Interactive Capacity Planning Workbench
Turn a pure capacity formula into a multi-user Livebook workbench with validated Kino forms, explicit shared state, and race-safe results.
Quick Reference
| Attribute | Value |
|---|---|
| Difficulty | Level 1 — Beginner |
| Time Estimate | 6–10 hours |
| Language | Elixir (alternatives: Erlang, JavaScript only for an optional custom Kino view) |
| Prerequisites | Project 1 or equivalent Livebook basics; pure functions; tagged results; percentages and basic capacity arithmetic |
| Key Topics | Kino inputs and controls, form validation, per-user versus shared state, request generations, frames, sensitivity analysis |
1. Learning Objectives
By completing this project, you will:
- Build a capacity calculation as a pure function before introducing events or UI state.
- Validate field-level and cross-field constraints while returning all actionable errors in one submission.
- Explain the collaboration semantics of a regular
Kino.Inputversus events emitted by aKino.Control.form. - Deliberately separate shared team assumptions from per-user scenario inputs and outputs.
- Prevent late work from overwriting a newer scenario by tagging requests with monotonically increasing generations.
- Render ready, invalid, calculating, and failed states without destroying the last valid result.
- Use
Kino.Framereplacement and bounded history intentionally rather than accumulating unbounded output. - Prove the workbench’s behavior with two simultaneous browser clients, rapid submissions, and a runtime reconnect.
2. Theoretical Foundation
2.1 Core Concepts
Functional core, interactive shell. The calculation should accept a complete validated scenario and return a complete result. It should not read inputs, update a frame, or depend on a listener process. The interactive shell gathers events, validates boundary values, calls the core, and renders the returned state. This separation makes the mathematics testable without a browser and keeps event-lifecycle defects from contaminating domain logic.
Capacity model. A useful first approximation comes from Little’s Law: average concurrency is arrival rate multiplied by average time in the system. If peak traffic is r requests per second and measured service time is s seconds, workload concurrency is approximately r × s. Provisioning then accounts for target utilization, growth, redundancy, and a safety margin. This is a model, not a guarantee. It must expose its assumptions, units, and sensitivity to service-time tails.
Field and cross-field validation. A field validator can decide that peak RPS is positive and service time has a supported unit. Cross-field validation asks whether the combination is meaningful: target utilization must remain below 100%; redundancy must be compatible with a requested failure tolerance; and a growth horizon should not silently apply a percentage twice. Accumulating independent errors makes correction faster than failing on the first field.
Collaboration semantics. Livebook notebooks are collaborative. Regular input values are synchronized in the notebook session, which is useful for a deliberately shared baseline but surprising for private scenario work. Form submissions are handled as events and can identify the submitting client. The workbench must choose state scope based on product intent rather than convenience.
Event ownership and sequential listeners. Kino.listen runs a callback in a background process and handles events sequentially. That simplicity avoids concurrent mutation inside one listener, but it does not eliminate stale asynchronous work that the callback starts elsewhere. The design still needs a clear owner for scenario generation, per-client last valid result, and any running task reference.
Request generations. Every accepted submission receives a generation number. Completion is renderable only if its generation equals the latest accepted generation for that client. This distinguishes request order from completion order. It is the same pattern used in search suggestions, dashboards, and any interface where older slow work can finish after newer work.
Frame semantics. A Kino.Frame is a dynamic output target. Replacing frame content is appropriate for a current result card; appending is appropriate only for a deliberately bounded audit trail. Invalid input should update the error region while retaining the last valid result with an “outdated relative to current form” label, instead of erasing useful context or presenting it as current.
2.2 Why This Matters
Many internal tools begin as a formula in a spreadsheet and become unsafe when multiple people use them. Users need validation, visible assumptions, scenario comparison, and predictable behavior under rapid interaction. The hard problem is not drawing a form; it is preserving the meaning and ownership of state.
This project introduces the event-driven architecture used later by dashboards and deployed Livebook Apps. By keeping the capacity model pure, you can test business logic thoroughly. By making collaboration intent explicit, you avoid leaking one user’s work to another. By tagging generations, you learn a general defense against stale asynchronous results.
2.3 Historical Context / Background
Queueing theory formalized relationships among arrival rate, service time, concurrency, and waiting. Little’s Law is powerful because it is broadly applicable under stable long-run conditions, yet easy to misuse when averages hide bursts or tail latency. Modern capacity engineering therefore combines a transparent model with measurements, headroom policy, failure-domain assumptions, and load tests.
Interactive notebooks evolved from single-user exploratory tools toward collaborative applications. Livebook’s Kino library provides server-rendered inputs, controls, frames, and app-oriented components without requiring a separate frontend project. That convenience makes state semantics more important: a shared notebook value, a client-targeted event, and a deployed multi-session app are different scopes.
2.4 Common Misconceptions
- “A synchronized input is always collaborative in a good way.” Shared state is correct only for assumptions the team intends to share. It is a privacy and correctness bug for private scenarios.
- “A form value is valid because its HTML widget constrained it.” Browser constraints improve usability; the Elixir boundary must still validate every value and combination.
- “Sequential event handling eliminates races.” A listener callback may be sequential while spawned work completes out of order.
- “Invalid input should clear the result.” Preserving the last valid result can be useful if it is clearly labeled as belonging to an earlier generation.
- “More workers always improve capacity.” External bottlenecks, queue growth, contention, and downstream limits can make additional concurrency harmful.
- “The formula predicts production exactly.” It is a planning model. Its inputs, uncertainty, sensitivity, and validation against load evidence must be visible.
3. Project Specification
3.1 What You Will Build
Create capacity-planner.livemd, an app-like workbench with a deliberately shared baseline and private scenario submissions. The page has:
- A header explaining the model, units, formula revision, and limitations.
- A shared team-baseline input for the currently agreed measured service time or traffic reference.
- A per-user form for scenario name, peak RPS, measured service time, target utilization, growth, redundancy, and safety margin.
- A validation region that displays every actionable error without terminating the listener.
- A current-result frame showing required concurrency, capacity with redundancy, recommended provisioned capacity, projected utilization, headroom, and warnings.
- A sensitivity table varying service time and traffic around the submitted assumptions.
- A small, bounded per-user event transcript with generation and status.
- A lifecycle section that can stop an existing listener before starting another.
3.2 Functional Requirements
- Pure scenario calculation: Accept a validated scenario and return a structured result with no Kino dependency.
- Explicit units: Store and display service time consistently; any millisecond-to-second conversion happens once.
- Accumulated validation: Return all independent field errors and relevant cross-field errors in one submission.
- Shared baseline: Use shared input semantics only for a clearly labeled team assumption.
- Private scenarios: Route form submissions and result frames by client identity so two users can use different values simultaneously.
- Generation tracking: Assign a new generation to every accepted submission and ignore obsolete completions.
- Preserve last valid result: Invalid input shows errors while keeping the prior result labeled with its generation.
- Sensitivity analysis: Produce a bounded table for at least three traffic factors and three service-time factors.
- Warnings: Identify high utilization, weak redundancy, extreme growth, and sensitivity above a declared service-time threshold.
- Lifecycle control: Re-evaluating setup must not create duplicate listeners; cleanup is idempotent.
- Replay: A fresh runtime reconstructs controls, listener, frames, and reference scenario.
3.3 Non-Functional Requirements
- Responsiveness: Validation feedback should appear within 100 ms; normal calculations within 250 ms on the small model.
- Isolation: Per-user assumptions and results never appear in another client’s private frame.
- Reliability: Expected invalid values never crash the listener process.
- Race safety: A late generation can be logged as discarded but cannot replace the current result.
- Boundedness: Sensitivity output, event history, and frame content have declared maximum sizes.
- Accessibility: Every field has a label, unit, range guidance, and textual error; status is not communicated by color alone.
- Reproducibility: Formula version, defaults, and test scenarios live in the notebook or committed fixtures.
3.4 Example Usage / Output
One user submits “Holiday peak” with valid assumptions. The result frame replaces its loading state with:
Scenario: Holiday peak
Required concurrency: 238
Capacity with redundancy: 476
Recommended provisioned capacity: 550
Projected utilization: 73%
Status: PASS — 12 percentage points below target ceiling
Warning: result is sensitive to service time above 180 ms
Generation: 12 — CURRENT
The same user submits invalid values next:
SCENARIO NOT RECALCULATED
peak_rps: must be greater than 0
target_utilization: must be between 0.40 and 0.90
redundancy: must be at least 2 when one-node failure tolerance is selected
Last valid result: generation 12 (input form has newer invalid values)
3.5 Real World Outcome
Open the notebook in Livebook and evaluate the setup section. A compact planning page appears below the explanation. The shared team-baseline control is visually separated from the private scenario form and explicitly says “changes are visible to notebook collaborators.” The form labels every numeric field with its unit and accepted range.
After a valid submission, the result panel briefly shows CALCULATING generation=12 and then renders the scenario transcript, headroom status, assumptions, and sensitivity table. If the learner submits a malformed scenario, a validation panel lists every problem beside the relevant field; the previous valid panel remains visible but is labeled as older than the form.
Open the notebook from a second browser window. Change the shared baseline in the first and observe it synchronize. Then submit distinct private scenarios in both windows and verify that results remain different. Use a deliberate delay fixture to submit generations 16 and 17 quickly; when 16 finishes last, the transcript records discarded_obsolete, while 17 remains displayed. Finally, re-evaluate the initialization cell and confirm that one submission creates one transcript entry rather than duplicated output.
4. Solution Architecture
4.1 High-Level Design
LIVEBOOK NOTEBOOK SESSION
┌─────────────────────────────────────────────────────────────────────┐
│ Shared baseline input │
│ team traffic/service reference ───────────────┐ │
└───────────────────────────────────────────────┼─────────────────────┘
v
Client A ┌────────────────────────────┐ Client B
┌──────────────────┐ event A │ Interaction coordinator │ event B ┌──────────────────┐
│ Private form A │───────────▶│ client -> latest generation│◀──────────│ Private form B │
└──────────────────┘ │ client -> last valid result│ └──────────────────┘
└────────────┬───────────────┘
│ raw submission
v
┌────────────────────────────┐
│ Validation boundary │
│ parse + field + cross-field│
└────────────┬───────────────┘
errors │ validated scenario
┌───────┴─────────┐
v v
┌──────────────────┐ ┌──────────────────────┐
│ Client errors │ │ Pure capacity model │
│ keep old result │ │ + sensitivity grid │
└──────────────────┘ └──────────┬───────────┘
│ generation-tagged result
v
┌────────────────────────┐
│ Current-generation gate│
└───────┬────────┬───────┘
│current │obsolete
v v
private frame bounded log
4.2 Key Components
| Component | Responsibility | Key Decisions |
|---|---|---|
| Shared baseline control | Hold explicitly collaborative defaults | Never store private scenario fields here |
| Per-user form | Submit one coherent scenario event | Validate server-side; identify origin client |
| Validator | Parse, normalize units, accumulate field/cross-field errors | Return structured errors, never raise for expected input |
| Capacity model | Calculate concurrency, redundancy, provisioned capacity, utilization | Pure and deterministic with documented formulas |
| Sensitivity engine | Recalculate bounded traffic/service-time combinations | Reuse validated base assumptions, no UI dependency |
| Interaction coordinator | Own per-client generation and last valid result | One clear state owner and sequential event intake |
| Generation gate | Decide render versus discard | Completion must match client’s latest accepted generation |
| Frames | Render errors, current results, and bounded transcript | Replace current state; append only within fixed log limit |
| Lifecycle handle | Stop listener/tasks safely | Startup and cleanup are idempotent |
4.3 Data Structures
RawScenario = {
client_id,
scenario_name_text,
peak_rps_text,
service_time_ms_text,
target_utilization_percent_text,
growth_percent_text,
redundancy_text,
safety_margin_percent_text,
failure_tolerance_choice
}
ValidatedScenario = {
name,
peak_rps: positive_number,
service_time_seconds: positive_number,
target_utilization: fraction_in_supported_range,
growth_factor: non_negative_factor,
redundancy: positive_integer,
safety_margin: non_negative_fraction,
formula_revision
}
ClientState = {
latest_generation: non_negative_integer,
last_valid_result: optional_Result,
current_status: IDLE | INVALID | CALCULATING | READY | FAILED,
bounded_events: queue_at_most_limit
}
GenerationResult = {
client_id,
generation,
scenario,
required_concurrency,
redundant_capacity,
provisioned_capacity,
projected_utilization,
warnings,
sensitivity_rows
}
4.4 Algorithm Overview
Key Algorithm: Generation-Safe Scenario Evaluation
- Receive a form event with submitting client identity.
- Parse all fields and accumulate errors.
- If invalid, update that client’s error state and preserve its last valid result.
- If valid, increment that client’s latest generation and render calculating state.
- Calculate base concurrency from arrival rate and service time.
- Adjust for utilization, growth, redundancy, and safety policy in a documented order.
- Evaluate a fixed-size sensitivity grid and derive warnings.
- Return the result tagged with client and generation.
- Compare result generation with the client’s latest accepted generation.
- Render only on equality; otherwise record a bounded discard event.
Complexity Analysis:
- Base calculation:
O(1)time and space. - Sensitivity grid:
O(t × s)forttraffic factors andsservice-time factors; fixed limits make it bounded. - Coordinator state:
O(c × h)forcactive clients and event-history caph.
5. Implementation Guide
5.1 Development Environment Setup
Use Livebook 0.19.8 and Kino 0.19.0 or document the actual compatible versions you use.
$ elixir --version
$ livebook --version
$ livebook server
Open two independent browser contexts for collaboration tests. Two tabs may share browser storage and still be acceptable for a notebook-session test, but a regular and private window makes client separation easier to observe.
5.2 Project Structure
capacity-planner/
├── capacity-planner.livemd
├── fixtures/
│ ├── reference-scenarios.fixture
│ ├── invalid-scenarios.fixture
│ └── delayed-generations.fixture
├── evidence/
│ ├── two-client-test.txt
│ └── reconnect-test.txt
└── README.md
5.3 The Core Question You’re Answering
“How do you turn an immutable calculation into a multi-user workflow without hiding state, weakening validation, or rendering obsolete work?”
Answer it by naming the owner, scope, lifetime, and update rule of every state value. If you cannot say whether a value is notebook-shared, client-specific, process-owned, derived, or persistent, the UI is not ready to implement.
5.4 Concepts You Must Understand First
- Tagged validation and error accumulation
- Which failures are independent enough to show together?
- What distinguishes a parse error from a policy error?
- Book Reference: Elixir in Action, 3rd ed., Chapters 3–4.
- Little’s Law and utilization
- Why does
arrival rate × service timeestimate in-flight work? - Why does target utilization below 100% create necessary headroom?
- Book Reference: Site Reliability Engineering, capacity-planning and overload chapters.
- Why does
- Kino input and control semantics
- Which values synchronize for collaborators?
- How does a form event identify the submitting client?
- Primary References: Kino.Input and Kino.Control.
- Frames and rendering state
- When should output replace versus append?
- How do you bound a transcript?
- Primary Reference: Kino.Frame.
- Request generation
- Why does completion order differ from submission order?
- What data must be tagged to reject an obsolete completion safely?
5.5 Questions to Guide Your Design
- Are percentages represented internally as fractions or whole-number percentages?
- Which shared baseline changes should invalidate private results, and how will that be labeled?
- Does the form submit one coherent snapshot or react independently to every field change?
- Which validations are field-local and which require two or more fields?
- Where does per-client latest generation live?
- Can an invalid submission accidentally increment generation or erase a valid result?
- How is a closed or disconnected client cleaned from coordinator state?
- What will happen if the startup cell is evaluated twice?
- Which sensitivity factors are pedagogically useful without overwhelming the screen?
5.6 Thinking Exercise
Trace this event sequence on paper:
T0 client=A submits invalid generation candidate
T1 client=A submits valid slow scenario -> accepted generation 7
T2 client=B submits valid fast scenario -> accepted generation 3
T3 client=A submits valid fast scenario -> accepted generation 8
T4 result B/3 completes
T5 result A/8 completes
T6 result A/7 completes late
T7 shared baseline changes
T8 client B disconnects
For every step, record validation result, coordinator state, visible frame, last valid result, and transcript entry. Decide whether the baseline change automatically recalculates, marks results stale, or waits for resubmission; document one policy consistently.
5.7 The Interview Questions They’ll Ask
- “When would you use a regular Kino input instead of a form?”
- “How do you prevent stale asynchronous work from replacing current output?”
- “Why preserve the last valid result after an invalid submission?”
- “Where does state live in a Livebook interaction?”
- “How would you test two-client isolation?”
- “What assumptions make Little’s Law useful, and when does it mislead?”
- “Why should rendering cadence differ from data or event cadence?”
5.8 Hints in Layers
Hint 1: Start synchronous. Make a pure function return a full result for one scenario before adding any Kino component.
Hint 2: Normalize once. Convert milliseconds and percentages at the validation boundary, not throughout the formula.
Hint 3: Accumulate errors. Keep parse failures and cross-field policy failures addressable by field.
Hint 4: Route by collaboration intent. Shared assumptions use shared controls; private workflow state follows client-targeted events and frames.
Hint 5: Add generation before artificial delay. Once the gate works synchronously, inject controlled delay to prove it.
Hint 6: Keep a lifecycle handle. It should identify every listener or task that startup may need to stop.
5.9 Books That Will Help
| Topic | Book | Chapter |
|---|---|---|
| Elixir data modeling and failures | Elixir in Action, 3rd ed. | Chapters 3–4 |
| Capacity and stability | Release It!, 2nd ed. | Capacity, timeouts, and stability chapters |
| Queueing and overload | Site Reliability Engineering | Handling overload and capacity-planning chapters |
| Usable experiment design | Design It! | Experiments, feedback, and design-risk chapters |
5.10 Implementation Phases
Phase 1: Foundation (2–3 hours)
Goals: Establish the formula, units, validation contract, and reference cases.
Tasks:
- Write assumptions and calculate one scenario by hand.
- Define raw, validated, result, and validation-error schemas.
- Implement pure validation and calculation with reference tests.
- Add a bounded sensitivity grid without UI.
Checkpoint: Reference and invalid fixtures pass with no Kino component evaluated.
Phase 2: Core Functionality (2–4 hours)
Goals: Add form events, private results, shared baseline, and generation safety.
Tasks:
- Build labeled inputs and a single coherent form submission.
- Create coordinator state keyed by client.
- Render invalid, calculating, ready, and failed variants.
- Add generation tags and a deliberate out-of-order completion fixture.
Checkpoint: Two clients maintain different results; a late result is discarded visibly.
Phase 3: Polish & Edge Cases (2–3 hours)
Goals: Prove lifecycle, accessibility, bounds, and replay.
Tasks:
- Add shared-baseline labeling and the chosen stale-result policy.
- Test duplicate startup, orderly cleanup, reconnect, and client disconnect.
- Check keyboard navigation, field guidance, and text status labels.
- Record two-client and rapid-submit evidence.
Checkpoint: Clean replay reconstructs one listener and all controls; the complete Definition of Done passes.
5.11 Key Implementation Decisions
| Decision | Options | Recommendation | Rationale |
|---|---|---|---|
| Form behavior | React on every change, submit coherent snapshot | Coherent submission | Avoids expensive or invalid intermediate states |
| Error policy | First error, all actionable errors | Accumulate actionable errors | Faster correction and better boundary evidence |
| Private state | Shared inputs, client-keyed form events | Client-keyed events/results | Matches per-user planning intent |
| Result update | Completion order, generation order | Latest accepted generation | Prevents old slow work from overwriting current state |
| Invalid submission | Clear result, retain labeled prior result | Retain and label | Preserves useful comparison without claiming currency |
| Frame history | Append forever, bounded replace/log | Replace result and cap log | Prevents browser and process memory growth |
6. Testing Strategy
6.1 Test Categories
| Category | Purpose | Examples |
|---|---|---|
| Unit tests | Verify pure math and validation | unit conversion, utilization, all-errors accumulation |
| Model property tests | Check general relationships | more traffic never lowers required capacity; added safety never lowers provision |
| Interaction tests | Verify state transitions | invalid→valid, valid→invalid, loading→ready |
| Concurrency tests | Prove request ordering | generation 7 completes after generation 8 |
| Collaboration tests | Verify scopes | shared baseline sync, private scenario isolation |
| Lifecycle tests | Prevent duplicated processes | evaluate startup twice, cleanup twice, reconnect |
6.2 Critical Test Cases
- Reference scenario: Produces 238 required concurrency, 476 redundant capacity, 550 provisioned capacity, and 73% projected utilization under the fixture’s declared policy.
- All-errors submission: Blank name, negative RPS, invalid utilization, and incompatible redundancy appear together without a listener crash.
- Last-valid preservation: Invalid generation candidate leaves the prior valid result visible and labeled old.
- Out-of-order completion: Generation 17 renders; late generation 16 records discard and never flashes in the current frame.
- Two clients: A and B submit canary scenario names and see only their own names/results.
- Shared baseline: A baseline edit appears in both clients and follows the documented invalidation policy.
- Duplicate startup: Starting twice yields one event per submission.
- Reconnect: A new runtime rebuilds controls and state with no reliance on old process identifiers.
6.3 Test Data
REFERENCE Holiday_peak
inputs=source-controlled_Holiday_peak_fixture
redundancy=2
formula_revision=capacity-model-v1
expected required_concurrency=238
expected redundant_capacity=476
expected provisioned_capacity=550
expected projected_utilization_percent=73
INVALID Combined_errors
peak_rps=-1 target_utilization_percent=110 redundancy=1 failure_tolerance=one_node
expected error_fields=[peak_rps, target_utilization, redundancy]
RACE A
submit generation=16 delay_ms=300
submit generation=17 delay_ms=20
expected current=17 discarded=[16]
ISOLATION
client_A scenario_name=CANARY_A
client_B scenario_name=CANARY_B
expected cross_client_canary_visibility=NONE
7. Common Pitfalls & Debugging
7.1 Frequent Mistakes
| Pitfall | Symptom | Solution |
|---|---|---|
| Shared control for private data | Another browser changes or sees a scenario | Use per-user form events and client-targeted result state |
| Boundary raises | One invalid number terminates the listener | Parse into tagged errors and render a controlled invalid state |
| No generation tag | Old slow result overwrites a newer result | Tag submissions and gate completion by latest generation |
| Percent confusion | Utilization is 100× too high or low | Normalize percentage to fraction once at the boundary |
| Repeated listener startup | Each submission renders twice | Stop prior lifecycle handle or return existing initialized system |
| Unbounded frame append | Browser/process memory grows | Replace current result and cap transcript length |
7.2 Debugging Strategies
- Log state transitions, not raw secrets: Record client surrogate, generation, status, and discard reason.
- Inject deterministic delay: Make one generation intentionally slow to reproduce the race reliably.
- Use two canary clients: Distinct scenario names reveal accidental sharing immediately.
- Test pure core separately: If math is correct without Kino, focus debugging on validation, routing, or lifecycle.
- Inspect process count before/after startup: Duplicate listeners often explain duplicate output.
7.3 Performance Traps
Do not recalculate sensitivity on every keystroke. Bound the sensitivity grid and avoid appending a new large table for every submission. Kino.listen is sequential, so a slow callback can delay later events; keep the callback small or delegate work with generation/cancellation semantics. Clean up per-client state after disconnection or inactivity to avoid unbounded coordinator growth.
8. Extensions & Challenges
8.1 Beginner Extensions
- Add presets for normal day, campaign peak, and holiday peak without bypassing validation.
- Add a comparison card between the current and previous valid scenario.
- Export a text-only scenario summary that includes formula revision and units.
8.2 Intermediate Extensions
- Add Monte Carlo input ranges and render bounded percentiles rather than one point estimate.
- Add a cancellation signal for obsolete long-running sensitivity calculations.
- Package the pure model in a Mix project with ExUnit and property-based tests.
8.3 Advanced Extensions
- Turn the notebook into a multi-session Livebook App and repeat isolation tests.
- Compare analytical recommendations with measured load-test evidence and calculate model error.
- Add failure-domain topology so redundancy reflects zones rather than multiplying one scalar.
9. Real-World Connections
9.1 Industry Applications
- Cloud capacity planning: Convert workload and latency observations into explicit headroom scenarios.
- Database connection pools: Model concurrency while respecting downstream hard limits.
- Autoscaling policy review: Explore assumptions and sensitivity before translating them into production controls.
- Internal decision tools: Give multiple operators private scenarios against a shared baseline.
9.2 Related Open Source Projects
- Kino: Livebook’s interactive widgets, controls, frames, and app building blocks.
- Livebook: Collaborative notebook runtime and application host.
- Elixir: Functional core and BEAM concurrency foundation.
9.3 Interview Relevance
- State ownership: Explain shared, per-client, process-owned, and derived state.
- Race conditions: Demonstrate why request generations solve completion-order bugs.
- Validation design: Contrast browser constraints, boundary parsing, and domain policy.
- Capacity reasoning: Discuss Little’s Law, headroom, redundancy, tails, and model limits.
10. Resources
10.1 Essential Reading
- Elixir in Action, 3rd ed., by Saša Jurić — Chapters 3–4 for data modeling and explicit failures.
- Release It!, 2nd ed., by Michael T. Nygard — capacity, stability, and overload discussions.
- Site Reliability Engineering, edited by Betsy Beyer et al. — capacity planning and handling overload.
10.2 Video Resources
- Livebook official homepage — demos and current interactive-workflow overview.
- Elixir official learning resources — maintained talks and courses.
10.3 Tools & Documentation
- Kino 0.19.0: official documentation — interactive components and current API.
- Kino.Input: official input documentation — supported inputs and synchronization behavior.
- Kino.Control: official control documentation — forms, events, and listening.
- Kino.Frame: official frame documentation — dynamic output and client targeting.
- Livebook use cases: official documentation — notebooks, apps, and interactive workflows.
10.4 Related Projects in This Series
- Project 1: Executable Energy Field Report — establishes pure calculations, invariants, and clean replay.
- Project 3: Messy CSV Triage Clinic — deepens accumulated validation and evidence design.
- Project 4: Fault-Tolerant Telemetry Lab — replaces simple interaction state with supervised concurrent processes.
11. Self-Assessment Checklist
Before considering this project complete, verify:
11.1 Understanding
- I can explain the difference between regular input synchronization and per-user form submission.
- I can derive the capacity model and state its limitations.
- I can explain why sequential listening does not automatically prevent stale completion.
- I can identify the owner, scope, and lifetime of every state value.
- I can explain replacement versus append semantics for frames.
11.2 Implementation
- Pure calculation and validation tests pass independently of Kino.
- All actionable validation errors render without crashing.
- Last valid result survives an invalid submission with correct labeling.
- Two simultaneous clients keep private scenarios isolated.
- Shared baseline behavior is explicit and tested.
- Late results, duplicate startup, cleanup, and reconnect tests pass.
- Sensitivity output and transcript history are bounded.
11.3 Growth
- I documented one state-scope mistake I avoided or corrected.
- I can describe how this architecture would change in a multi-session app.
- I can present the race test and its evidence in a technical interview.
12. Submission / Completion Criteria
Minimum Viable Completion:
- A pure, tested capacity model accepts one validated scenario and returns documented units.
- A Kino form reports all actionable errors without terminating its listener.
- A valid submission renders the reference result and bounded sensitivity table.
- Duplicate startup is prevented or cleaned up deterministically.
Full Completion:
- All minimum criteria plus:
- Shared baseline and per-user scenario semantics are visibly distinct.
- Generation-tagged rapid submissions prove that obsolete work cannot overwrite current output.
- Two-client isolation, invalid-after-valid, disconnect, cleanup, and clean-runtime replay pass.
- Results display assumptions, formula revision, headroom, and sensitivity warnings.
Excellence (Going Above & Beyond):
- Property tests verify monotonic capacity relationships over generated valid scenarios.
- A multi-session app version repeats the privacy and load tests under independent sessions.
- Results are compared against a controlled load-test fixture and model error is documented.
- Accessibility and usability are reviewed by a second person using only the notebook instructions.
This guide was generated from LEARN_LIVEBOOK_ELIXIR_DEEP_DIVE.md. For the complete learning path, see the parent directory.