<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: gyani</title>
    <description>The latest articles on Forem by gyani (@ggyanie).</description>
    <link>https://forem.com/ggyanie</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3933445%2Fdb95e6da-0e56-463e-9ec5-439be36878dc.png</url>
      <title>Forem: gyani</title>
      <link>https://forem.com/ggyanie</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/ggyanie"/>
    <language>en</language>
    <item>
      <title>I shipped 19 SEO essays in 12 days from a single Next.js page file</title>
      <dc:creator>gyani</dc:creator>
      <pubDate>Sat, 16 May 2026 07:22:48 +0000</pubDate>
      <link>https://forem.com/ggyanie/i-shipped-19-seo-essays-in-12-days-from-a-single-nextjs-page-file-5615</link>
      <guid>https://forem.com/ggyanie/i-shipped-19-seo-essays-in-12-days-from-a-single-nextjs-page-file-5615</guid>
      <description>&lt;p&gt;I have been quietly running an experiment for the last twelve days. I wanted to know how minimal the publishing pipeline for a real SEO essay corpus can be if I gave up every CMS, every markdown loader, and every static-site generator that pretended to be lightweight but turned out to require its own ecosystem.&lt;/p&gt;

&lt;p&gt;The answer ended up being one Next.js page file, one slug allowlist, one sitemap function, and a postscript script that probes the live URL after deploy. Nineteen essays are live as I write this, all ranked individually in &lt;code&gt;sitemap.xml&lt;/code&gt;, all internally linked, all with stable URLs and zero rebuild surprises. The whole pipeline is small enough that a human in a hurry can read it in five minutes.&lt;/p&gt;

&lt;p&gt;This post is the file. Not a tutorial about the file. The actual structure.&lt;/p&gt;

&lt;h2&gt;
  
  
  The shape
&lt;/h2&gt;

&lt;p&gt;The route lives at &lt;code&gt;apps/web/app/essays/[slug]/page.tsx&lt;/code&gt;. Everything an essay needs is in two arrays in that file plus one small allowlist file next to it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// apps/web/app/essays/[slug]/page.tsx&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;notFound&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;next/navigation&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ESSAY_SLUGS&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@/lib/essay-slugs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dynamic&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;force-static&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dynamicParams&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Essay&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;ESSAY_SLUGS&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="nl"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;publishedAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ESSAYS&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Essay&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;why-matching-layer-is-physically-blind&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Why the matching layer is physically blind, on purpose&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;publishedAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2026-05-08&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`
      ... essay prose here, plain markdown-ish strings ...
    `&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="c1"&gt;// ... 18 more&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;generateStaticParams&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;ESSAY_SLUGS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;slug&lt;/span&gt; &lt;span class="p"&gt;}));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;EssayPage&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;essay&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ESSAYS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;slug&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;essay&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nf"&gt;notFound&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Article&lt;/span&gt; &lt;span class="na"&gt;essay&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;essay&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;ESSAY_SLUGS&lt;/code&gt; is a &lt;code&gt;const&lt;/code&gt; tuple in its own file so the type system catches typos at compile time. Slugs in the renderer that are not in the allowlist will not type-check; slugs in the allowlist with no renderer entry will hit &lt;code&gt;notFound()&lt;/code&gt; and return a real &lt;code&gt;404&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// apps/web/lib/essay-slugs.ts&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ESSAY_SLUGS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;why-matching-layer-is-physically-blind&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;letters-mode-is-mercy&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;why-dating-apps-feel-exhausting&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="c1"&gt;// ... 16 more&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is the single source of truth for the corpus. Sitemap reads it. Index page reads it. Renderer reads it. Three callers, one list. When a new essay ships, one line in this file and one entry in the renderer is the whole diff.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sitemap, index, internal linking
&lt;/h2&gt;

&lt;p&gt;Because &lt;code&gt;ESSAY_SLUGS&lt;/code&gt; is a typed tuple, the sitemap generator is six lines.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// apps/web/app/sitemap.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;MetadataRoute&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;next&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ESSAY_SLUGS&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@/lib/essay-slugs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;sitemap&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;MetadataRoute&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Sitemap&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;ESSAY_SLUGS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`https://byvibration.com/essays/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;lastModified&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="p"&gt;}));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;/essays&lt;/code&gt; index page does the same lookup and renders a card per essay, ordered by &lt;code&gt;publishedAt&lt;/code&gt; descending. Adding a new essay automatically promotes it to the top of the index and into the sitemap on the next deploy. There is nothing else to remember.&lt;/p&gt;

&lt;p&gt;Internal linking is a function call inside the prose. Each essay body has a small &lt;code&gt;Related&lt;/code&gt; block at the bottom that pulls related slugs by cluster tag (introvert cluster, friendship cluster, etc.). The cluster mapping is another tiny &lt;code&gt;const&lt;/code&gt; next to the slug list. Total moving parts so far: three files.&lt;/p&gt;

&lt;h2&gt;
  
  
  The "is it actually live" probe
&lt;/h2&gt;

&lt;p&gt;Vercel deploys are usually fast, but there is one failure mode that bit me hard. A page can return &lt;code&gt;HTTP 200&lt;/code&gt; while serving the home shell when something upstream of the renderer crashes silently. The status code lies.&lt;/p&gt;

&lt;p&gt;To catch this, the post-deploy probe asserts three things: the page returns &lt;code&gt;200&lt;/code&gt;, the rendered &lt;code&gt;&amp;lt;title&amp;gt;&lt;/code&gt; contains a stem of the slug, and the slug is present in the live &lt;code&gt;sitemap.xml&lt;/code&gt;. If any of those fail, the deploy is treated as not-live, even on &lt;code&gt;200&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# superbot/util/essay_liveness.py
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;is_live&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;httpx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://byvibration.com/essays/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;slug&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;
    &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;title&amp;gt;([^&amp;lt;]+)&amp;lt;/title&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="nf"&gt;any&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;stem&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;group&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;stem&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stem&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;
    &lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;
    &lt;span class="n"&gt;sitemap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;httpx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://byvibration.com/sitemap.xml&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/essays/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;slug&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;sitemap&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the single most-useful seven-minute piece of code in the pipeline. It caught a soft-&lt;code&gt;404&lt;/code&gt; for me on essay number five before I noticed the slug was being silently rewritten by middleware. The fix took ten minutes; without the probe it would have taken a week of confusion about why search was not seeing the page.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I did not build
&lt;/h2&gt;

&lt;p&gt;A markdown loader. A frontmatter parser. An MDX pipeline. A CMS adapter. A headless preview environment. A content directory. A draft state machine. A separate build pipeline for content vs. application code.&lt;/p&gt;

&lt;p&gt;Every one of those was suggested by some part of my brain along the way. None of them earned their place. The reason is honest: the corpus is small, the cost of typing prose inline is trivial, and the type checker is the only quality gate that actually catches the bugs that ship in production. The simplest model that works is the model.&lt;/p&gt;

&lt;p&gt;The day I have one hundred essays I will probably move to a markdown directory. Until then, the file fits on one screen of any normal editor, the array is sorted by date, and I can grep my own corpus instantly.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this looks like in practice
&lt;/h2&gt;

&lt;p&gt;The cadence has been roughly one essay every fifteen hours, written in plain prose, ported into the array, slug added to the allowlist, pushed. Vercel deploys. The probe runs. The sitemap updates. Google indexes it within forty-eight hours. The internal links to the rest of the corpus stay correct because they are computed, not hand-maintained.&lt;/p&gt;

&lt;p&gt;The unit of friction per new essay is "write the essay." Everything downstream of that is one diff against one file.&lt;/p&gt;

&lt;p&gt;If you have an essay practice and you are intimidated by the pipeline question, this is a version of "just ship it" that has an answer. One file. One allowlist. One probe. Nineteen essays in twelve days from that pattern, with the type checker as your friend.&lt;/p&gt;

&lt;p&gt;I would skip every CMS until you actually need one.&lt;/p&gt;




&lt;p&gt;I work on Byvibration, where the corpus this file feeds lives. The essays index is at byvibration.com/essays.&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>productivity</category>
      <category>showdev</category>
      <category>webdev</category>
    </item>
    <item>
      <title>A dating algorithm that physically cannot read photos (and why I wrote it that way)</title>
      <dc:creator>gyani</dc:creator>
      <pubDate>Sat, 16 May 2026 00:44:18 +0000</pubDate>
      <link>https://forem.com/ggyanie/a-dating-algorithm-that-physically-cannot-read-photos-and-why-i-wrote-it-that-way-268d</link>
      <guid>https://forem.com/ggyanie/a-dating-algorithm-that-physically-cannot-read-photos-and-why-i-wrote-it-that-way-268d</guid>
      <description></description>
    </item>
    <item>
      <title>A dating algorithm that physically cannot read photos (and why I wrote it that way)</title>
      <dc:creator>gyani</dc:creator>
      <pubDate>Fri, 15 May 2026 15:13:14 +0000</pubDate>
      <link>https://forem.com/ggyanie/a-dating-algorithm-that-physically-cannot-read-photos-and-why-i-wrote-it-that-way-5gb</link>
      <guid>https://forem.com/ggyanie/a-dating-algorithm-that-physically-cannot-read-photos-and-why-i-wrote-it-that-way-5gb</guid>
      <description>&lt;p&gt;I have been writing a connection app for a year. Last week I open-sourced the matching engine, and the only design choice I want to walk through is the one that took the longest to talk myself into: the matcher does not have access to photos. Not "it ignores them." Not "it deprioritizes them." It cannot see them. The TypeScript build fails if you try.&lt;/p&gt;

&lt;p&gt;If you only want the punchline, here it is.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// soulmate-core/src/rank.ts&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;rank&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;viewer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Profile&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;candidate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Profile&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Profile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;prompts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PromptAnswers&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;       &lt;span class="c1"&gt;// five short text answers&lt;/span&gt;
  &lt;span class="nl"&gt;voice&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;VoiceTranscript&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;       &lt;span class="c1"&gt;// ~30 sec, kept as text&lt;/span&gt;
  &lt;span class="nl"&gt;intent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Intent&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;               &lt;span class="c1"&gt;// friendship | relationship | community&lt;/span&gt;
  &lt;span class="nl"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ProfileMeta&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;            &lt;span class="c1"&gt;// age band, city, language, etc.&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="c1"&gt;// no photo field. anywhere on this type.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The image bytes live in a different table, behind a different read path, behind a &lt;code&gt;mutualVibe&lt;/code&gt; boolean. The function above has no reference to that table and no way to obtain one through normal app wiring. The constraint is enforced by the compiler.&lt;/p&gt;

&lt;p&gt;The repo is at &lt;code&gt;github.com/donnowyu/soulmate-core&lt;/code&gt; if you want to read along.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why type-level, not flag-level
&lt;/h2&gt;

&lt;p&gt;The natural shape of this is a feature flag. &lt;code&gt;if (allow_photo_in_ranking) { ... }&lt;/code&gt;. Several products built on this shape. I think it is the wrong shape. Three reasons.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Flags get flipped by people who weren't in the room when the principle was set.&lt;/strong&gt; A future engineer, looking at the engagement dashboard on a tired evening, will propose a "secondary signal" A/B test. They will be right that the metric will move. They will be wrong that what is being measured is what we said we cared about. A flag does not survive that conversation. A type signature does.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The constraint should live in the artifact, not the documentation.&lt;/strong&gt; A README that says "do not use photos in ranking" is a memo. A type that has no photo field is a build error. Banks do not enforce referential integrity with memos.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;It is honest in a way I can verify in public.&lt;/strong&gt; The repo is open. You can look at the entry-point type and convince yourself in 60 seconds. You do not have to take my word for anything.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The cost of doing it this way
&lt;/h2&gt;

&lt;p&gt;I will not pretend this was free.&lt;/p&gt;

&lt;p&gt;The most expensive part was the data model. I had to design the schema so that the photo entity has its own service, its own access control, its own read path. The image upload pipeline never returns to the matching service. The "show me a face" step is a separate request, gated server-side on the existence of a &lt;code&gt;mutualVibe&lt;/code&gt; row keyed by both user IDs. That is not a refactor you do in an afternoon.&lt;/p&gt;

&lt;p&gt;The second cost was deciding what &lt;code&gt;Profile&lt;/code&gt; should contain so that ranking still works. I tried a lot of things. The current shape (five prompts plus a transcribed voice clip plus intent metadata) is the smallest set I found that produces matches I can defend on inspection. Most of a year was spent reducing it to that.&lt;/p&gt;

&lt;p&gt;The third cost is a soft one. There is a class of user who, on the existing apps, sorts mostly by face. They will look at this product and bounce. That is fine. They were not the users I was trying to find.&lt;/p&gt;

&lt;h2&gt;
  
  
  The trick of the embedding
&lt;/h2&gt;

&lt;p&gt;The text answers and the voice transcript get concatenated into a single document per user. That document is embedded into a 1536-dim vector. Ranking is cosine similarity over those vectors, with two soft rerankers (ideology distance, shared-passion overlap) breaking ties.&lt;/p&gt;

&lt;p&gt;This is not exotic. The trick is not in the math. The trick is in the input. By construction, the model has never seen a pixel. By construction, the model has no learned latent dimension that correlates with attractiveness, because nothing in the training distribution ever encoded one. The rerank loop is small enough to read.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// rerank pseudocode&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;baseline&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;cosine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;viewerEmb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;candidateEmb&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ideologyPenalty&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;distance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;viewer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ideology&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;candidate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ideology&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;passionBoost&lt;/span&gt;   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;jaccard&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;viewer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;passions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;candidate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;passions&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;baseline&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mf"&gt;0.15&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;ideologyPenalty&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mf"&gt;0.10&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;passionBoost&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can argue with the coefficients. I have. The coefficients are not the point of the post.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I am writing this on Dev.to
&lt;/h2&gt;

&lt;p&gt;Because the type-system argument is the part of the project that is interesting to people who write code for a living, and because most of the press around "no photo dating apps" handles the question at the marketing layer, where it is much less interesting. The interesting question is whether the constraint is structural, and structural constraints are something a dev audience can read in source. I wanted that audience to be able to verify the claim without me in the room.&lt;/p&gt;

&lt;p&gt;If you want the long-form essay version of this argument, it is on the product site at &lt;code&gt;byvibration.com/essays/why-matching-layer-is-physically-blind&lt;/code&gt;. If you want the code, the repo link is at the top. If you want to push back on any of the choices, the comments are open and I will be in them.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I work on byvibration. The matching engine is open source. I am writing about it here because I think the type-signature framing is a transferable idea: constraints you want to honor across a long time should be expressed in the artifact, not the team's memory.&lt;/em&gt;---&lt;br&gt;
title: A dating algorithm that physically cannot read photos (and why I wrote it that way)&lt;br&gt;
published: false&lt;br&gt;
canonical_url: &lt;a href="https://byvibration.com/essays/why-matching-layer-is-physically-blind" rel="noopener noreferrer"&gt;https://byvibration.com/essays/why-matching-layer-is-physically-blind&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  tags: typescript, webdev, discuss, architecture
&lt;/h2&gt;

&lt;p&gt;I have been writing a connection app for a year. Last week I open-sourced the matching engine, and the only design choice I want to walk through is the one that took the longest to talk myself into: the matcher does not have access to photos. Not "it ignores them." Not "it deprioritizes them." It cannot see them. The TypeScript build fails if you try.&lt;/p&gt;

&lt;p&gt;If you only want the punchline, here it is.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// soulmate-core/src/rank.ts&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;rank&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;viewer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Profile&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;candidate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Profile&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Profile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;prompts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PromptAnswers&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;       &lt;span class="c1"&gt;// five short text answers&lt;/span&gt;
  &lt;span class="nl"&gt;voice&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;VoiceTranscript&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;       &lt;span class="c1"&gt;// ~30 sec, kept as text&lt;/span&gt;
  &lt;span class="nl"&gt;intent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Intent&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;               &lt;span class="c1"&gt;// friendship | relationship | community&lt;/span&gt;
  &lt;span class="nl"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ProfileMeta&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;            &lt;span class="c1"&gt;// age band, city, language, etc.&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="c1"&gt;// no photo field. anywhere on this type.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The image bytes live in a different table, behind a different read path, behind a &lt;code&gt;mutualVibe&lt;/code&gt; boolean. The function above has no reference to that table and no way to obtain one through normal app wiring. The constraint is enforced by the compiler.&lt;/p&gt;

&lt;p&gt;The repo is at &lt;code&gt;github.com/donnowyu/soulmate-core&lt;/code&gt; if you want to read along.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why type-level, not flag-level
&lt;/h2&gt;

&lt;p&gt;The natural shape of this is a feature flag. &lt;code&gt;if (allow_photo_in_ranking) { ... }&lt;/code&gt;. Several products built on this shape. I think it is the wrong shape. Three reasons.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Flags get flipped by people who weren't in the room when the principle was set.&lt;/strong&gt; A future engineer, looking at the engagement dashboard on a tired evening, will propose a "secondary signal" A/B test. They will be right that the metric will move. They will be wrong that what is being measured is what we said we cared about. A flag does not survive that conversation. A type signature does.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The constraint should live in the artifact, not the documentation.&lt;/strong&gt; A README that says "do not use photos in ranking" is a memo. A type that has no photo field is a build error. Banks do not enforce referential integrity with memos.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;It is honest in a way I can verify in public.&lt;/strong&gt; The repo is open. You can look at the entry-point type and convince yourself in 60 seconds. You do not have to take my word for anything.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The cost of doing it this way
&lt;/h2&gt;

&lt;p&gt;I will not pretend this was free.&lt;/p&gt;

&lt;p&gt;The most expensive part was the data model. I had to design the schema so that the photo entity has its own service, its own access control, its own read path. The image upload pipeline never returns to the matching service. The "show me a face" step is a separate request, gated server-side on the existence of a &lt;code&gt;mutualVibe&lt;/code&gt; row keyed by both user IDs. That is not a refactor you do in an afternoon.&lt;/p&gt;

&lt;p&gt;The second cost was deciding what &lt;code&gt;Profile&lt;/code&gt; should contain so that ranking still works. I tried a lot of things. The current shape (five prompts plus a transcribed voice clip plus intent metadata) is the smallest set I found that produces matches I can defend on inspection. Most of a year was spent reducing it to that.&lt;/p&gt;

&lt;p&gt;The third cost is a soft one. There is a class of user who, on the existing apps, sorts mostly by face. They will look at this product and bounce. That is fine. They were not the users I was trying to find.&lt;/p&gt;

&lt;h2&gt;
  
  
  The trick of the embedding
&lt;/h2&gt;

&lt;p&gt;The text answers and the voice transcript get concatenated into a single document per user. That document is embedded into a 1536-dim vector. Ranking is cosine similarity over those vectors, with two soft rerankers (ideology distance, shared-passion overlap) breaking ties.&lt;/p&gt;

&lt;p&gt;This is not exotic. The trick is not in the math. The trick is in the input. By construction, the model has never seen a pixel. By construction, the model has no learned latent dimension that correlates with attractiveness, because nothing in the training distribution ever encoded one. The rerank loop is small enough to read.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// rerank pseudocode&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;baseline&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;cosine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;viewerEmb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;candidateEmb&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ideologyPenalty&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;distance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;viewer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ideology&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;candidate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ideology&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;passionBoost&lt;/span&gt;   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;jaccard&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;viewer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;passions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;candidate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;passions&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;baseline&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mf"&gt;0.15&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;ideologyPenalty&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mf"&gt;0.10&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;passionBoost&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can argue with the coefficients. I have. The coefficients are not the point of the post.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I am writing this on Dev.to
&lt;/h2&gt;

&lt;p&gt;Because the type-system argument is the part of the project that is interesting to people who write code for a living, and because most of the press around "no photo dating apps" handles the question at the marketing layer, where it is much less interesting. The interesting question is whether the constraint is structural, and structural constraints are something a dev audience can read in source. I wanted that audience to be able to verify the claim without me in the room.&lt;/p&gt;

&lt;p&gt;If you want the long-form essay version of this argument, it is on the product site at &lt;code&gt;byvibration.com/essays/why-matching-layer-is-physically-blind&lt;/code&gt;. If you want the code, the repo link is at the top. If you want to push back on any of the choices, the comments are open and I will be in them.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I work on byvibration. The matching engine is open source. I am writing about it here because I think the type-signature framing is a transferable idea: constraints you want to honor across a long time should be expressed in the artifact, not the team's memory.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>webdev</category>
      <category>discuss</category>
      <category>architecture</category>
    </item>
  </channel>
</rss>
