Blog

Notes from building it

Short write-ups about decisions we actually made, with the measurement that forced them.

2026-10-14

We deleted the chart library for a 130 pixel donut

A dependency was most of the dashboard bundle and bought us two shapes.

On a throttled phone the dashboard took 4.1 seconds to draw its charts. The waterfall was not subtle: 388 kB of JavaScript, and roughly 130 kB of it was a charting library used for one donut and one bar chart. Recharts also fought us on touch targets — fourteen columns inside a 300 pixel card is about 21 pixels per tap, which is not a thumb-sized control.

Both charts are hand-drawn SVG now. Because the library was ours to replace, we could change the interaction too: dragging across the chart scrubs the selection instead of requiring a precise tap, and the axis scales to the data instead of always topping out at ten. The largest chunk on /dashboard went from 314 kB to 170 kB. The lesson we keep: measure the bundle before you defend it.

2026-10-13

The model that spent the whole budget thinking

A 502 with a valid-looking error message, caused by tokens we never counted.

Document briefs started failing in production with an empty answer. The request was fine, the retrieval was fine, and the model had used its entire max_tokens on hidden reasoning and returned no content at all. Our retry logic could not help: five calls at 42 seconds each had already blown the client timeout.

Two changes fixed it. Brief generation runs with thinking disabled, and a response that arrives truncated is now repaired — we keep the fields that did form and mark the answer truncated rather than discarding the call. The deeper lesson is about testing: a suite that stubs the answer generator never touches a token budget, so any paid path has to be measured live, and measuring means counting characters against the source text instead of watching for a 200.

2026-10-11

A paywall the server agrees with

The UI gate and the API gate read one table, or the banner is a lie.

The upgrade banner existed before any limit did. Wiring billing meant picking the five numbers that actually matter — documents, storage, keys, API requests per day, chat messages per day — and having the API answer every mutation with a specific over-limit code that the front end can render as a modal.

That ordering is the part worth stating: define the limits in one place, enforce them in the service, and let the interface only report what the server said. A paywall that fires before the server rejects is a bug users feel as a lie.