Skip to content

I keep a lessons file in this repository: one entry each time something failed for a non-obvious reason or a decision got made. These are the entries worth sharing. Most apply to any small team shipping a web app with an API behind it.

On AI features#

1. Give the model a catalogue and validate against it twice. The project planner's AI writes scope lines in the visitor's words, which is what makes it useful. But it chooses sizes from a fixed list the site publishes, and both the API and the browser re-check every id and clamp every number. The free model is good at mapping and happy to guess; validation makes guessing harmless.

2. Tell it what null means. The model invented timelines ("Within 1–3 months") for every project until the prompt said: when in doubt, null, because most descriptions say nothing about timing. Age of the current system is not urgency.

3. Lines must not overlap. Asked to scope a 40-screen migration, it produced "migrate 40 screens" and "migrate the panel", doubling the price. One sentence in the prompt fixed it: never add an umbrella line over its parts.

On data#

4. Decide what you're not storing before the first row. I built visitor logging with full IP, city and coordinates, then took it out the same day. Hashed IP (salted with the date), country and region give the same behavioural picture, repeat visits included, with none of the exposure. The privacy page is one paragraph because there's little to disclose.

5. Disclose in one place, not on every control. Notices on the page made the site look like it was apologising. A "Privacy" link in the footer is the standard, and it's enough.

6. The script outside the pipeline needs its own test. The Apps Script runs on Google's runtime, so I load its helpers into Node with the Google services stubbed and test the column order, escaping and pruning. A column shift there corrupts the Sheet silently; the test is ten lines.

On the build#

7. The quality gate goes in front of the build, not beside it. CI going red while the host still deployed the commit is the worst of both worlds. npm run build now runs lint, format, types and unit tests first; a red commit can't deploy anywhere.

8. Check what the host does to your files. The format check passed locally and in GitHub but failed on Vercel: the platform rewrites vercel.json in its sandbox before the build. One line in .prettierignore.

9. Dependabot and npm workspaces disagree. A workspace package with its own Dependabot entry gets its package.json bumped while the root lockfile stays stale, and npm ci fails. One root entry covers the workspaces. Hold back majors the toolchain can't take yet (ignore rules) so a grouped PR doesn't block the security fixes inside it.

10. Refetch what you cache when you deploy. The Worker cached the planner catalogue for an hour, so a deploy that changed the catalogue's shape served a stale one and every brief failed with a 502. The cache key now includes the Worker version.

On tests#

11. Test what computes or decides; turn design rules into assertions. A landing page doesn't need snapshot tests of its hero. It does need tests for the brief normaliser, the planner totals, the API's status codes, and the UX rules the site claims to follow (no horizontal scroll, tap targets, contrast). On the first run those found a 10px-wide footer link and code blocks a keyboard couldn't scroll.

12. Phone and desktop are different pages. Hidden panels and shorter button labels broke the first end-to-end test at 390px. Assert on visible occurrences and accept both labels; the test now reflects what a visitor sees rather than what's in the DOM.

None of these are clever. They're the kind of thing a second engineer would have caught in review, which is the point of writing them down when you're the only engineer.

Sources: Dependabot ignore rules; Cloudflare Workers cache.