Founder & sole engineer (Software Is Nothing, LLC) · 2024–present
tailharbor.eu
A European pet-adoption platform spanning 40 countries and 16 UI languages — full-stack web, native iOS + Android apps, and a nearly-4,000-adapter scraper system feeding ~132K live listings, ~3,900 shelters, and 700K+ managed photos.
What it is
A consumer-facing pet-adoption aggregator covering 40 European countries in 16 languages. It pulls listings from a long tail of rescue and shelter sites, normalises the data, scores photos, translates descriptions, and serves a unified search experience at tailharbor.eu — on the web and on native iOS and Android apps. Built and run solo.
Stack
- Web: Next.js 16 (App Router, server components, ISR), Clerk for auth, full UI in 16 locales (EN / DE / FR / ES / IT / NL / PT / PL / RO / SV / EL / HU / CS / DA / FI / NO).
- API: Express + Prisma ORM on PostgreSQL 16 with PostGIS, behind PgBouncer, with a Redis cache. The homepage mixed-sort query was cut from 10,610 ms to 89 ms (~120×) by moving it to a materialized view refreshed every 5 minutes.
- Search: Meilisearch (migrated off Elasticsearch in 2026), with typo-tolerance disabled on breed fields so exact breed names don't fuzzy-match.
- Scraper system: a BullMQ job queue driving close to 4,000 site-specific adapters. Cheerio for static HTML, Puppeteer for JS-rendered sites (pooled browser with
[puppeteer-pool]telemetry that warns at p95 > 5 s). WP REST, Wix data APIs, and ACF endpoints used where exposed; a proxy layer for IP-blocked targets. - Mobile: React Native via Expo EAS — native iOS and Android apps in TestFlight / Play internal testing, with dozens of builds shipped via a one-command
ship-both.shparallel pipeline (auto-bump versioning, preflight checks,eas submit) that reaches both stores in ~8 min wall-clock. Full 16-locale parity with the web app. - Storage: Cloudflare R2 for animal photos (700,000+ photos under lifecycle management: stamp-on-sight + stale cleanup + null-stored retry, all gated by an
off → observe → dry-run → enforcemode flag). - Payments: Stripe (freemium → PRO).
- AI: Anthropic Claude for breed/colour/size enrichment (runtime translation runs on local open-weight models, not a paid API). Both photo aesthetic scoring and face detection were moved off paid cloud AI onto local on-device Apple Vision inference — lifting aesthetic-score coverage from ~3% to ~96% of photos while removing the per-call cost.
- Deploy: Docker Compose on a Hetzner box, with a local
ci-local.sh+ pre-push hook that mirrors CI so a billed pipeline is never load-bearing. - Compounding layer: a Recipe DB of ~80 markdown patterns backing a compiled index, queried by both the builder agent and the maintenance agent before they touch code.
Scale (mid-2026)
- ~3,900 site-specific scraper adapters across 40 countries.
- ~132,000 live animal listings, refreshed continuously.
- ~3,900 shelters in the canonical registry.
- 700,000+ photos in R2, lifecycle-managed.
- ~2,700 long-form articles (country guides, breed pages, health-certificate walkthroughs).
- 16-locale UI across web and native mobile.
Why it's an integration story
The hard problems aren't the user-facing app — they're the compounding automated maintenance of a long-tail data pipeline. Three agents form a self-improving loop:
scraper-wave(builder): orchestrates parallel sub-agent builds of new adapters. Pre-flight hands each sub-agent a briefing of matching recipe patterns (Wix DOM paths, WP REST quirks, Drupal/Joomla/Squarespace conventions, per-language adopted-filter regexes, photo-source priority orderings).scraper-triage(maintainer): a daily cron diffs broken adapters against last week, classifies the breakage via an 8-cause decision tree (FALSE_BROKEN/DEAD/WAF_NEW/URL_CHANGED/JS_NEW/PHOTO_CDN_MIGRATED/SELECTOR_DRIFT/AGGREGATOR_CHANGED), and walks per-cause recipes. Hard rules baked in from real incidents — e.g. "fail-closed when the adopted-leak filter has no data" (prevents 1,000+ ghost-listing leaks) and "no synthetic descriptions, ever."recipe-distiller(compounder): every wave merge runs a distillation agent that proposes new recipes from net-new patterns, so the next wave consumes what the last one learned.
Other automated discipline:
- A mobile release pipeline that gates a 7-minute build behind a 5-second sanity check — certs, profiles, keystores, fingerprints, EAS env, JSON validity, toolchain present — so failures surface up front.
- A photo-lifecycle pipeline rolled out behind the same
off → observe → dry-run → enforceflag, with a 30-day post-rollout review before any destructive operation goes live. - An MV-refresh + nightly-ANALYZE cron pair that recovered the site from a 504 outage in ~200 ms after Postgres stats drifted under heavy scrape ingest.
The same integration-and-reliability patterns show up in the marketing-data systems I build at Bagjump and in the AI-agent platform I run for a consulting client.