Build1 distinct publisher3 min readUpdated
A dev.to walkthrough finishes a standard-library Go API with paging, updates and a spec. The store clamps what the service rejects, and sorts the whole map on every page.
The Engineer · Build desk
Compiled by The EngineerSomething wrong?How this is made
Two layers now validate the same two integers, and they do not agree. `MemoryStore.List` repairs bad input quietly: a negative offset becomes 0, a limit of zero or less becomes 20, anything above 100 becomes 100 [7]. `Service.ListPage` refuses the same values instead, returning `ErrInvalidOffset` or `ErrInvalidLimit` [9]. Since the service is what handlers call, the store's repairs sit in branches no HTTP request can reach, including the default page size of 20, which is not a default at all as long as the service stands in front [1].
The choice of offset paging is defended on grounds of simplicity, with cursor paging filed under things to add when there is scale to justify it [3], and a swipe at people who add it earlier as a matter of taste [4]. The design is better than that defence. Ids come from a counter that starts at 1 and increments on every `Create` [6], `List` sorts the key set ascending [8], and the `Store` interface has no delete method at all [5]. New rows therefore land past the end of every existing window, so the classic offset failure, a record shifting between two page requests, cannot happen in this store [4]. Cursor paging here would buy nothing a reader could measure.
What it would buy is the thing that does bite. `List` allocates a slice of every id in the map and runs `sort.Ints` over it before slicing out twenty items [8]. Serving a page costs O(n log n) in the size of the whole store, so the first page and the ten-thousandth cost the same [2]. In a demo map that is free. It is also the first line to be deleted when `Store` is backed by a database, at which point the ordering guarantee that made offset paging safe stops being a property of the code and starts being a property of a query someone else wrote.
One caveat on the other half of the instalment. Structured logging and a published OpenAPI spec are both listed as part of this final upgrade [2], but the text we were given stops inside `UpdateNote`, at the call into the store, with no logging setup and no spec in it [7]. The paging and update code can be read. The other two remain a table of contents.
The rest of the service holds up. Every method opens with a non-blocking select on `ctx.Done()` [12], validation is centralised behind named package errors [11], and `Create` normalises currency and note before checking them [14]. `Page` carries items, total, offset and limit with json tags [10], which is more than most hand-rolled list endpoints return. This is a small API that has decided what its rules are and where they live, and the one place it has two answers is the place a second caller will find first.
Follow any of these and your For You feed starts watching them — no settings page required.
Ranked by verification strength, evidence, and original report placement.
Before this instalment the series' Go API already had routes, a service layer, middleware, timeouts and tests.
Part 3 adds pagination for GET /links, PUT /links/{id} to update the note, structured logging so a request can be traced, and a small OpenAPI spec, while staying in the standard library.
The author uses offset pagination because it is easy to understand, saying cursor pagination is better at scale but can be added later when you actually have scale.
The author writes that most people add cursor pagination early "as a personality trait".
The Store interface declares Create, Get, UpdateNote, List(offset, limit) and Count; it has no delete method and none of its methods take a context.
MemoryStore holds a map[int]PayLink guarded by a sync.RWMutex with a next id field starting at 1 and incremented on every Create.
Evidence-backed comparisons of source perspectives and observed adoption signals. Read the methodology
Which Builder, Operator, and Investor concerns the observed source mix emphasized—not a truth score.
Evidence, demonstrated adoption, hype gap, incentives, and confidence are assessed independently, each on its own current evidence. How these are measured.
Self-verifying code, single unreplicated source
Nearly every factual claim is checkable against code pasted in the article itself — the Store interface, MemoryStore.List's clamps and sort, ListPage's validation, the sentinel var block, the per-method ctx select — which is unusually strong grounding for a tutorial. It is capped by there being exactly one source, no repository, tests or benchmark output to run against, and a body that truncates before the promised spec.
No adoption signal in supplied material
The cluster contains one tutorial post and no releases, downloads, deployments, benchmarks, pricing or usage disclosures. Nothing in the supplied text indicates whether anyone builds on this pattern, so adoption cannot be scored without inventing facts.
Production framing outruns the delivered code
The article positions this instalment as the step from "cute demo" to "this could actually run" and lists four additions, but the supplied text ships three and never reaches the OpenAPI spec. Meanwhile the code carries production-relevant defects the prose does not flag: clamping logic unreachable behind the service's own validation, a full key-set sort on every page request, a Total read under a different lock than its Items, and a storage interface that cannot accept a deadline. The overstatement is modest rather than severe because the author repeatedly frames the choices as deliberate simplifications (offset paging now, cursor later; "you can allow empty later if needed").
Audience-building tutorial, no commercial stake visible
Observable incentive is the ordinary one for a numbered dev.to series: readership and follow-through across parts, with the closing instalment framed as the payoff. No vendor product, sponsorship, paid tier, hosted service or dependency is promoted — the piece explicitly stays in the standard library — so there is little commercial pull on the technical claims.
Confident on code facts, thin on everything else
Confidence in the code-level facts and their direct consequences is high because the listings are reproduced in full and the derivations follow from them mechanically. Confidence in the story as a whole is held down by one publisher, zero adoption evidence, and a truncated body that leaves part of the advertised scope unverifiable.
build
Your meter now runs on someone else's machine: signed receipts, fsync, and failing open1 distinct publisher
build
A Timed-Out Reset SMS Is Not A Failed One, And Your Retry Code Probably Disagrees1 distinct publisher
build
Buy transactional email on recovery controls, not send price1 distinct publisher
build
Count invalid JSON as a failed classification, and model choice becomes a reliability problem1 distinct publisher
Distinct publishers with included, body-backed reporting in this cluster.
dev.to
1 article · August 22, 2026