chore: initial scaffold for latprobe CLI tool
Sets up project structure, working conventions (CLAUDE.md), README with the full assignment and roadmap, seeded CHANGELOG, and a buildable Go scaffold (Step 0) with flag parsing and stub probe types. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
122
docs/plans/2026-07-01-00-08-go-latency-tool.md
Normal file
122
docs/plans/2026-07-01-00-08-go-latency-tool.md
Normal file
@@ -0,0 +1,122 @@
|
||||
# Plan: Website latency CLI tool (Go first, then Python)
|
||||
|
||||
## Context
|
||||
|
||||
We're building a small CLI tool that measures the latency of one or more
|
||||
websites given their URLs. The point of difference from a naive timer is a
|
||||
**full per-phase breakdown of an HTTP request** — DNS resolution, TCP connect,
|
||||
TLS handshake, server processing (time-to-first-byte), and content transfer —
|
||||
not just a single total number.
|
||||
|
||||
We start simple (total latency only) and grow the tool in clear steps. The Go
|
||||
implementation comes first; once the user is satisfied, we port the same
|
||||
behaviour to Python. This plan also establishes project-wide working
|
||||
conventions (plans, changelog, per-feature docs) up front.
|
||||
|
||||
Design decisions confirmed with the user:
|
||||
- **Output:** human-readable text by default, with a `--json` flag for machine output.
|
||||
- **Sampling:** a `--count`/`-n` flag repeats N requests per URL and reports min/avg/max per phase.
|
||||
- **Input:** accept multiple URLs as positional arguments, measured in turn.
|
||||
|
||||
Toolchain present: Go 1.26.4, Python 3.14.6.
|
||||
|
||||
## Proposed layout
|
||||
|
||||
```
|
||||
tt-excercise/
|
||||
├── CLAUDE.md # working conventions (created first)
|
||||
├── README.md # the assignment + implementation steps
|
||||
├── CHANGELOG.md # timestamped completed features
|
||||
├── docs/
|
||||
│ ├── plans/ # timestamped plans (this plan copied here)
|
||||
│ └── usage/ # per-feature user documentation
|
||||
├── go/
|
||||
│ ├── go.mod # module: latprobe (name adjustable)
|
||||
│ ├── main.go # CLI: flag parsing, orchestration, output
|
||||
│ └── internal/probe/
|
||||
│ └── probe.go # timing logic via net/http/httptrace
|
||||
└── python/ # added later, during the port
|
||||
```
|
||||
|
||||
Binary/module name proposed: **`latprobe`** (easy to change).
|
||||
|
||||
## Foundation files (created at the start)
|
||||
|
||||
### CLAUDE.md — working conventions
|
||||
- Every plan is saved under `docs/plans/` with a filename starting with the
|
||||
current timestamp in `yyyy-mm-dd-hh-mm` format
|
||||
(e.g. `2026-07-01-00-08-go-latency-tool.md`).
|
||||
- Every completed feature is appended to `CHANGELOG.md` with a timestamp.
|
||||
- Every feature ships with user documentation under `docs/usage/` including a
|
||||
concrete example of how to use it.
|
||||
|
||||
### README.md — the assignment
|
||||
- Project purpose: CLI tool to measure website latency, with a full per-request
|
||||
phase breakdown including DNS resolution.
|
||||
- Two implementations: Go first, then a Python port.
|
||||
- Usage sketch and the staged implementation roadmap (below).
|
||||
- Note that Go 1.26 / Python 3.14 are the development versions.
|
||||
|
||||
### CHANGELOG.md
|
||||
- Seeded with a header; entries appended per completed feature.
|
||||
|
||||
## Implementation steps (Go)
|
||||
|
||||
Each step is a self-contained, reviewable increment. We pause for the user's
|
||||
sign-off between steps. On completion of each: append to `CHANGELOG.md` and add
|
||||
its `docs/usage/` page.
|
||||
|
||||
**Step 0 — Scaffold.** Create `go/go.mod` (`go mod init latprobe`), a minimal
|
||||
`main.go` that prints usage, and the directory structure. Confirms the toolchain
|
||||
builds.
|
||||
|
||||
**Step 1 — Simple total latency.** Accept a single URL argument, perform an
|
||||
HTTP GET, and print the total wall-clock time of the request. No breakdown yet.
|
||||
Reuse the standard library only (`net/http`, `time`).
|
||||
|
||||
**Step 2 — Full per-phase breakdown (the core feature).** Instrument the request
|
||||
with `net/http/httptrace.ClientTrace` to capture timestamps and derive each phase:
|
||||
- DNS lookup (`DNSStart` → `DNSDone`)
|
||||
- TCP connect (`ConnectStart` → `ConnectDone`)
|
||||
- TLS handshake (`TLSHandshakeStart` → `TLSHandshakeDone`, HTTPS only)
|
||||
- Server processing / TTFB (`WroteRequest` → `GotFirstResponseByte`)
|
||||
- Content transfer (`GotFirstResponseByte` → response body fully read)
|
||||
- Total
|
||||
Print an aligned text breakdown. `httptrace` is the key reusable stdlib piece —
|
||||
no third-party dependency needed.
|
||||
|
||||
**Step 3 — Multiple URLs + sampling.** Accept multiple positional URLs; add
|
||||
`--count`/`-n` (default 1) to repeat each URL N times and report **min/avg/max**
|
||||
per phase. Group output clearly per URL.
|
||||
|
||||
**Step 4 — JSON output.** Add a `--json` flag producing structured output
|
||||
(per URL, per phase, with the min/avg/max aggregates and sample count). Keep the
|
||||
human-readable text as the default.
|
||||
|
||||
Reasonable extras to consider later (not committed now): `--timeout`,
|
||||
`--method`, redirect handling, HTTP/2 vs HTTP/1.1 reporting, exit codes on failure.
|
||||
|
||||
## Port to Python (after Go is approved)
|
||||
|
||||
Mirror the same CLI and output. Python has no direct `httptrace` equivalent, so
|
||||
the plan will measure phases at a lower level — e.g. custom timing around
|
||||
`socket.getaddrinfo` (DNS), connection establishment, and TLS wrap, or by using
|
||||
`urllib3`/`httpx` connection hooks. The detailed Python design will be drafted
|
||||
as its own plan (saved to `docs/plans/`) when we reach that stage.
|
||||
|
||||
## Verification
|
||||
|
||||
- `cd go && go build ./...` succeeds at every step.
|
||||
- Manual runs against known hosts, e.g.:
|
||||
- `./latprobe https://example.com` → total only (Step 1), full breakdown (Step 2+).
|
||||
- `./latprobe -n 5 https://example.com https://www.google.com` → min/avg/max per URL.
|
||||
- `./latprobe --json https://example.com` → valid JSON (pipe through a JSON validator).
|
||||
- Sanity-check phase numbers: phases should roughly sum to the total; HTTPS shows
|
||||
a TLS phase, plain HTTP does not.
|
||||
- `go vet ./...` clean.
|
||||
|
||||
## Bookkeeping at execution time
|
||||
|
||||
On exiting plan mode, before/with Step 0:
|
||||
1. Copy this plan to `docs/plans/2026-07-01-00-08-go-latency-tool.md`.
|
||||
2. Create `CLAUDE.md`, `README.md`, `CHANGELOG.md` as described above.
|
||||
51
docs/usage/step-0-scaffold.md
Normal file
51
docs/usage/step-0-scaffold.md
Normal file
@@ -0,0 +1,51 @@
|
||||
# Step 0 — Scaffold
|
||||
|
||||
## What this step delivers
|
||||
|
||||
A buildable Go binary (`latprobe`) that parses flags and URLs, prints usage
|
||||
when invoked without arguments, and exits cleanly. No measurement logic yet —
|
||||
the full implementation is wired in during Steps 1–4.
|
||||
|
||||
## Build
|
||||
|
||||
```sh
|
||||
cd go
|
||||
go build -o latprobe .
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```sh
|
||||
# No arguments → prints usage and exits with code 1
|
||||
./latprobe
|
||||
|
||||
# With a URL → scaffold message until Step 1 is implemented
|
||||
./latprobe https://example.com
|
||||
```
|
||||
|
||||
Expected output (scaffold stage):
|
||||
|
||||
```
|
||||
latprobe — measure per-phase HTTP request latency
|
||||
|
||||
Usage:
|
||||
latprobe [flags] <url> [url ...]
|
||||
|
||||
Flags:
|
||||
-n, --count int Number of requests per URL (default 1)
|
||||
--json Output results as JSON instead of text
|
||||
-h, --help Show this help
|
||||
|
||||
Examples:
|
||||
latprobe https://example.com
|
||||
latprobe -n 5 https://example.com https://www.google.com
|
||||
latprobe --json https://example.com | jq .
|
||||
```
|
||||
|
||||
## Available flags (accepted by the parser, not yet functional)
|
||||
|
||||
| Flag | Short | Default | Description |
|
||||
|------|-------|---------|-------------|
|
||||
| `--count` | `-n` | `1` | Number of requests per URL |
|
||||
| `--json` | — | false | Output as JSON instead of text |
|
||||
| `--help` | `-h` | — | Show usage |
|
||||
Reference in New Issue
Block a user