2026-06-29·2 min read

SkinAtlas, part 1: explainability over magic

field-notesskinatlas

SkinAtlas is a personal atlas of your skin: your products, their ingredients, your routines, and a daily journal, connected into one model that can answer questions like “which product correlates with the bad weeks.” The founding rule is written into every design doc: explainability over magic. Every benefit, risk, and conflict the app shows traces back to a concrete record in a hand-curated ingredient knowledge base. Nothing is a black-box model opinion, including the AI copilot, which is only allowed to answer from your own data and cites the records it used.

It’s the youngest of my projects — first commit June 21 — and the fastest moving: 762 commits in under two weeks, through five roadmap phases. Paste an ingredient list and get an analysis; build AM/PM routines and get conflict detection; keep a skin journal and get correlations; photograph your skin and get a vision-model read, grounded in the same knowledge base.

SkinAtlas on a phone: an “Understand your skin” hero with an ingredient card and a skin-score badge

The discover page. Every card on it traces back to an ingredient record.

Boring choices, on purpose

The architecture decisions are recorded as ADRs, and most of them choose the boring option. SQLite (via Turso) instead of Postgres, which forces every list field to be serialized JSON and produced a small in-house rule: all serialization goes through one helper module, no raw JSON.parse in feature code. Hand-written CSS, no UI framework. Anthropic models as the single intelligence layer, with one chokepoint file so cost and behavior are tunable in one place — the skin analyzer runs on a mid-tier model after a commit titled “swap Opus → Sonnet” made the cost math work.

One decision was legal rather than technical: the obvious data source for ingredient information is a popular ingredient-decoding site, and SkinAtlas deliberately never scrapes it. The decision doc names the terms-of-service risk to a future paid tier and lists the alternatives that got built instead — open product databases, the EU’s 24,000-entry cosmetic ingredient registry, retailer pages verified case by case.

The outage that improved the build

Three days after launch, production went down. Not one cause — three, stacked: a missing auth env var, a hosting quota, and a database schema that had never been migrated for the new auth tables. Each was invisible locally because dev-mode fallbacks masked them. The fix that mattered wasn’t any of the three patches; it was a build-time schema-drift check that now fails the deploy if the live database doesn’t match the code’s expectations. Fail at build, not at 2am.

Parts 2 and 3: how the knowledge base grows by a hundred ingredients a night, and the memory system that lets a new session pick up where the last one died.