<?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>DEV Community: AurinAilean</title>
    <description>The latest articles on DEV Community by AurinAilean (@aurinaileandot).</description>
    <link>https://dev.to/aurinaileandot</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3967611%2F5e4853e5-9e45-4461-abab-8e0701766331.png</url>
      <title>DEV Community: AurinAilean</title>
      <link>https://dev.to/aurinaileandot</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/aurinaileandot"/>
    <language>en</language>
    <item>
      <title>Der Pivot um 04:30 — Eine Solo-Dev-Nacht mit KI</title>
      <dc:creator>AurinAilean</dc:creator>
      <pubDate>Sat, 20 Jun 2026 07:31:00 +0000</pubDate>
      <link>https://dev.to/aurinaileandot/der-pivot-um-0430-eine-solo-dev-nacht-mit-ki-47b2</link>
      <guid>https://dev.to/aurinaileandot/der-pivot-um-0430-eine-solo-dev-nacht-mit-ki-47b2</guid>
      <description>&lt;p&gt;Gestern Abend hatte ich keine Unity-Installation und einen Schreck mit einer 59 €-Token-Rechnung. Heute Morgen um 06:00 läuft ein Götter-Spiel mit einem Affen-Avatar, einem Marmor-Tempel und einer Vision, die schärfer ist als alles, was ich je auf Papier gebracht habe.&lt;/p&gt;

&lt;p&gt;Das ist die Geschichte einer 9-Stunden-Solo-Nacht, in der ich mehr über Game-Design gelernt habe als in den letzten zwölf Monaten. Vor allem über mich und meinen AI-Partner.&lt;/p&gt;

&lt;h2&gt;
  
  
  20:00 — Wo es begann
&lt;/h2&gt;

&lt;p&gt;API-Key-Leak in einem Railway-Deployment, Bot-Crawler triggerten Übersetzungen, 59 USD weg. Key revoke'd, Spend-Limits gesetzt, durchgeatmet.&lt;/p&gt;

&lt;p&gt;Status: Mac, Claude Max-Plan, eine Idee. Ein Götter-Spiel inspiriert von &lt;strong&gt;Black &amp;amp; White&lt;/strong&gt; (Lionhead, 2001), mit 6 mythologischen Tier-Avataren — Affe (Hanuman-Geist), Wolf (Anubis), Löwe (Sphinx), Elefant (Ganesha), Ochse (Minotaurus), Jaguar (Maya).&lt;/p&gt;

&lt;p&gt;Plan: Unity installieren, Charaktere mit &lt;strong&gt;Meshy.ai&lt;/strong&gt; generieren, &lt;strong&gt;Claude Code&lt;/strong&gt; als Coding-Partner, Claude Chat als Architekt und Sparring.&lt;/p&gt;

&lt;h2&gt;
  
  
  02:00 — Der Affe steht im Tempel
&lt;/h2&gt;

&lt;p&gt;In sechs Stunden lief das hier durch:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Unity Hub + Unity 6.5 + iOS/Android/WebGL-Module&lt;/li&gt;
&lt;li&gt;Claude Code 2.1 (Max-Plan, kein API-Cost extra)&lt;/li&gt;
&lt;li&gt;24 Charaktere via Meshy, je vier Moral-Varianten&lt;/li&gt;
&lt;li&gt;~80 Animationen pro Charakter (Kampf, Tanz, Akrobatik, Sozial)&lt;/li&gt;
&lt;li&gt;Unity-Projekt mit 17 Namespaces, asmdef-sauber, &lt;strong&gt;keine Cycles&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;34 C#-Skripte mit ScriptableObject-Pattern und GOBT-AI-Stub&lt;/li&gt;
&lt;li&gt;Fünf Docs: &lt;code&gt;CLAUDE.md&lt;/code&gt;, &lt;code&gt;ARCHITECTURE.md&lt;/code&gt;, &lt;code&gt;ROADMAP.md&lt;/code&gt;, &lt;code&gt;CONVENTIONS.md&lt;/code&gt;, &lt;code&gt;PERFORMANCE.md&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Eine &lt;code&gt;IDENTITY.md&lt;/code&gt; mit Farb-Trias, Anti-Patterns und kulturellen Respekt-Regeln&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Um 02:00 lief der erste Play-Mode: weißer Marmor-Tempel auf grüner Wiese, ein Goldfinial drauf, zwei Affen daneben, Faith-Counter zeigt „Glaube 0". &lt;/p&gt;

&lt;p&gt;A-Pose noch. Aber sichtbar. &lt;strong&gt;Es funktionierte.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  04:30 — Der Pivot
&lt;/h2&gt;

&lt;p&gt;Ich wurde übermütig. Bat Claude um eine vollständige Asset-Liste: „Häuser, Werkstätten, Berufe, Schulen, alles."&lt;/p&gt;

&lt;p&gt;Bekam ein 601-Zeilen-Dokument zurück: &lt;strong&gt;53 Gebäude, 18 Berufe, 12 Ressourcen, eine Wirtschafts-Kette von Erz über Schmiede zu Werkzeug, eine 7-Phasen-Roadmap über 12 Monate.&lt;/strong&gt; Mit recherchiertem Hintergrund aus Black &amp;amp; White 2, Settlers, Banished, Manor Lords, Anno 1800.&lt;/p&gt;

&lt;p&gt;Erst war ich begeistert. Vollständig! Tabellen! Phasen!&lt;/p&gt;

&lt;p&gt;Dann las ich es nochmal — und es klickte: Das war kein Götter-Spiel. Das war &lt;strong&gt;Settlers mit Mythologie-Skin&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Ich schrieb zurück: &lt;em&gt;„Analysier alles nochmal und sag mir, was wirklich nötig ist, um den Kern von NUMEN hervorzuheben. Inklusive Lob und Kritik an dem, was du gerade getan hast."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Claudes Antwort begann mit: &lt;em&gt;„Du hast recht. Ich hab dich in den Settlers-Modus gezogen."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Es folgte eine selbstkritische Analyse plus eine überraschende Erkenntnis:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;NUMEN's Kern-Verb ist nicht TUN. Es ist ZEUGE SEIN.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Animal Crossing, Tamagotchi, Black &amp;amp; White — alle bauen auf einer Welt mit Gedächtnis. Du bist nicht Chef, du bist Zeuge. Das ist die Marktlücke. Das ist NUMEN.&lt;/p&gt;

&lt;h2&gt;
  
  
  06:00 — Was bleibt
&lt;/h2&gt;

&lt;p&gt;Nach dem Pivot:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;53 Gebäude → &lt;strong&gt;6 Gebäude&lt;/strong&gt; (Tempel, Hütte, Schrein, Versammlungsplatz, Avatar-Statue, Friedhof)&lt;/li&gt;
&lt;li&gt;18 Berufe → &lt;strong&gt;2 Rollen&lt;/strong&gt; (Bewohner + Erwachte)&lt;/li&gt;
&lt;li&gt;12 Ressourcen → &lt;strong&gt;1 Ressource&lt;/strong&gt; (Glaube)&lt;/li&gt;
&lt;li&gt;7 Phasen → &lt;strong&gt;4 Phasen&lt;/strong&gt;, 12 Monate bis Release&lt;/li&gt;
&lt;li&gt;Eine schlanke &lt;code&gt;CORE_VISION.md&lt;/code&gt; ersetzt das aufgeblähte alte Doc&lt;/li&gt;
&lt;li&gt;Ein Master-Prompt für Claude Code mit eingebautem Filter: &lt;em&gt;„Dient dieses Feature dem Bezeugen?"&lt;/em&gt; — vor jedem Feature anzuwenden&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Die fünf Säulen, die NUMEN tragen sollen:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Ein Kern-Verb: &lt;strong&gt;gesture-and-watch&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Eine Welt mit &lt;strong&gt;Gedächtnis&lt;/strong&gt; (NPCs erinnern sich)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stille&lt;/strong&gt; zwischen Aktionen ist erlaubt (Aquarium-Modus)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Visuelle Transformation&lt;/strong&gt; statt XP/Coins&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Kulturelle Tiefe&lt;/strong&gt; als Identität, nicht als Skin&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  Was ich aus dieser Nacht mitnehme
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;AI-Tools machen Solo-Dev brutal schnell — aber sie biasen Richtung Feature-Stapeln.&lt;/strong&gt; LLMs wollen helfen, indem sie Listen liefern. Du musst aktiv „weniger" sagen, sonst kriegst du ein Verwaltungs-Strategiespiel statt deiner Vision.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Der schwerste Teil von Game-Design ist nicht das Bauen. Es ist das Streichen.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Behandle deinen AI-Partner wie einen Design-Sparrings-Partner&lt;/strong&gt;, nicht wie einen Code-Generator. Frag „was ist wirklich wichtig?" und „was war schlecht?" — nicht nur „bau mir X". Das beste Ergebnis kam aus dem Moment, in dem ich Claude widersprach.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Vertrau dem Bauchgefühl.&lt;/strong&gt; Wenn die KI dir eine schöne Tabelle gibt und du fühlst dich trotzdem unwohl — push back. Hörbarer Widerstand vom Menschen ist genau der Input, den ein guter AI-Partner braucht, um besser zu werden.&lt;/p&gt;

&lt;p&gt;Phase 1 wird heute Abend fertig (Mac-Build). Phase 2 startet morgen früh.&lt;/p&gt;

&lt;p&gt;NUMEN bezeugt. NUMEN baut nicht.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Wenn du das Projekt verfolgen willst: ich werde unregelmäßig Devlogs hier posten. Frage, Kritik, Mitfiebern alles willkommen.&lt;/em&gt; &lt;/p&gt;

</description>
      <category>ai</category>
      <category>indiedev</category>
      <category>gamedev</category>
      <category>unity3d</category>
    </item>
    <item>
      <title>I'm a web dev who has never touched 3D. Here's my first creature.</title>
      <dc:creator>AurinAilean</dc:creator>
      <pubDate>Sun, 14 Jun 2026 18:56:04 +0000</pubDate>
      <link>https://dev.to/aurinaileandot/im-a-web-dev-who-has-never-touched-3d-heres-my-first-creature-3p88</link>
      <guid>https://dev.to/aurinaileandot/im-a-web-dev-who-has-never-touched-3d-heres-my-first-creature-3p88</guid>
      <description>&lt;p&gt;New project, and I'm starting it the same way I start everything: slightly out of my depth, in public.&lt;/p&gt;

&lt;p&gt;I build web things. TypeScript, the browser, 2D canvas — that's home. The new project, NUMEN, is a god-game: you raise a creature and a little world grows around it. Which means for the first time I need 3D. And I have never made a single 3D model in my life.&lt;/p&gt;

&lt;p&gt;So this post isn't a tutorial. It's day one.&lt;/p&gt;

&lt;p&gt;The first thing I did was generate a creature with Meshy — text and an image in, a .glb model out. A cow, in three versions: gentle, neutral, and a darker one for when the creature gets raised badly. Twenty minutes ago they didn't exist; now they're sitting in my assets folder, rotating in a viewer, looking back at me.&lt;/p&gt;

&lt;p&gt;Here's the honest part, the thing I want to remember when this gets hard: getting a model out was easy. That's not the work.&lt;/p&gt;

&lt;p&gt;Two things hit me immediately:&lt;/p&gt;

&lt;p&gt;One model isn't a world. A cow that looks great alone means nothing if the temple, the trees, and the terrain all look like they came from different games. The actual skill I have to learn isn't "generate a creature" — it's keeping a whole world consistent. One style, every asset, or it's just a bag of mismatched outputs.&lt;br&gt;
It's not even in the game yet. My renderer is still a 2D placeholder. These .glb files are raw material, not a running scene. Wiring them into an actual 3D engine is the next mountain, and I haven't climbed it.&lt;/p&gt;

&lt;p&gt;So I'm not going to pretend I've "added 3D to my game." I made one creature. The renderer's still 2D. I don't know yet if I can pull the art of a whole world together as a solo dev who learned 3D yesterday.&lt;/p&gt;

&lt;p&gt;But there's a cow standing in 3D space that wasn't there this morning. For day one, that's enough to keep going.&lt;/p&gt;

&lt;p&gt;If you've made the jump from 2D/web into 3D: what's the thing you wish someone had told you on day one?&lt;/p&gt;

</description>
      <category>buildinpublic</category>
      <category>gamedev</category>
      <category>ai</category>
      <category>indiedev</category>
    </item>
    <item>
      <title>My analysis engine has two brains now</title>
      <dc:creator>AurinAilean</dc:creator>
      <pubDate>Sat, 13 Jun 2026 18:19:50 +0000</pubDate>
      <link>https://dev.to/aurinaileandot/my-analysis-engine-has-two-brains-now-e7o</link>
      <guid>https://dev.to/aurinaileandot/my-analysis-engine-has-two-brains-now-e7o</guid>
      <description>&lt;p&gt;The thing I'm building, App Store Analyzer, is a website that does one thing: it reads an iOS niche and writes a deep market analysis for indie devs. For a long time that analysis had one brain — and it spoke German.&lt;/p&gt;

&lt;p&gt;That made sense at the start. German is my home market and my own language, so I built the analysis logic in German first. I could actually feel whether the output was good or garbage, section by section, because I was reading it in the language I think in. It got deep. Reliable. I trusted it.&lt;/p&gt;

&lt;p&gt;Then it started to hurt.&lt;/p&gt;

&lt;p&gt;Every time I wanted the analysis in another language, I was basically running the whole expensive thinking step again from scratch. German code, German slugs, German routes, German everything — and a goal of serving 14 languages. The whole thing fought itself.&lt;/p&gt;

&lt;p&gt;So I rebuilt the brain in English. Not "translated the code" — rebuilt the canonical brain so English is the one source of truth. Now the engine thinks &lt;strong&gt;once&lt;/strong&gt; in native English, and that single analysis gets translated and cached into &lt;strong&gt;13 other languages&lt;/strong&gt;. Generate once, translate many.&lt;/p&gt;

&lt;p&gt;It was not a clean ride.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The lows.&lt;/strong&gt; A refactor left a pile of undefined names and quietly 500'd my detail pages — live, in production, while I thought everything was fine. I misread a normal cache warm-up window as a dead backend more than once and "fixed" things that were never broken. I spent an embarrassing stretch hammering an endpoint with a wrong key, watching &lt;code&gt;403&lt;/code&gt; scroll by, before realizing my terminal had eaten the line that set the key. Small things. Hours each.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The highs.&lt;/strong&gt; Two of them I didn't expect:&lt;/p&gt;

&lt;p&gt;It got &lt;em&gt;cheaper&lt;/em&gt;, not just cleaner. I'm not paying for a full deep analysis per language anymore — one real generation, then lightweight translations. For a solo dev watching every API cent, that's the whole game.&lt;/p&gt;

&lt;p&gt;And the English brain was actually &lt;em&gt;sharper&lt;/em&gt;. I ran the old German output against the new English one side by side, fully expecting English to be the weaker copy. It wasn't. In a few sections it was tighter and clearer than the original. The German one stays native too — home market, my voice — but English is now the spine everything hangs off.&lt;/p&gt;

&lt;p&gt;I'm not going to pretend I planned this cleanly. This is my first real project and I'm building it with AI as my pair, learning the architecture as I trip over it. The "one canonical language, translate outward" idea is obvious in hindsight. It was not obvious at 11pm three deploys deep with production throwing 500s.&lt;/p&gt;

&lt;p&gt;If you're building something solo and multilingual: pick your canonical brain early. Future-you will thank present-you.&lt;/p&gt;

&lt;p&gt;Building in public — would love to hear how others handle the multi-language cost problem. 🌍&lt;/p&gt;

</description>
      <category>buildinpublic</category>
      <category>ai</category>
      <category>indiedev</category>
      <category>devjournal</category>
    </item>
    <item>
      <title>Coming back to your own project after a week feels like onboarding a stranger</title>
      <dc:creator>AurinAilean</dc:creator>
      <pubDate>Fri, 12 Jun 2026 18:07:35 +0000</pubDate>
      <link>https://dev.to/aurinaileandot/coming-back-to-your-own-project-after-a-week-feels-like-onboarding-a-stranger-4k84</link>
      <guid>https://dev.to/aurinaileandot/coming-back-to-your-own-project-after-a-week-feels-like-onboarding-a-stranger-4k84</guid>
      <description>&lt;p&gt;You take a week off. You come back, open the repo, and for about twenty minutes you are a complete beginner in a codebase you wrote yourself. The function names look vaguely familiar. You're sure past-you had a plan. You just can't remember what it was, or which of the seventeen markdown files on your desktop is the one that's actually current.&lt;/p&gt;

&lt;p&gt;If you build solo, especially with AI assistants doing a lot of the typing, this feeling is sharper than it used to be. The code moves fast. Your memory of &lt;em&gt;why&lt;/em&gt; it moves does not.&lt;/p&gt;

&lt;p&gt;Here's what I've learned: the thing you lost over the week isn't skill. It's context. And context is recoverable if you treat it like a first-class artifact instead of something that lives only in your head.&lt;/p&gt;

&lt;h2&gt;
  
  
  The trap: trusting your own notes
&lt;/h2&gt;

&lt;p&gt;I keep a checkpoint document. After every session I write down where things stand, what's next, and the open threads. Coming back, that doc is the single most valuable file in the project. It is also a liar.&lt;/p&gt;

&lt;p&gt;Not on purpose. It just goes stale. I came back this week, opened the checkpoint, and it told me a specific cleanup job had ~215 items left in one place and ~154 in two others. Concrete numbers. Easy to plan around.&lt;/p&gt;

&lt;p&gt;So I almost planned around them. Then I ran the actual check against the code instead of the note. The real numbers were lower. Work had continued after the note was written, and the note never caught up. If I'd trusted it, I'd have scoped a sprint against figures that were days out of date, and wondered later why nothing added up.&lt;/p&gt;

&lt;p&gt;The lesson isn't "stop taking notes." Notes are how you rebuild context in minutes instead of hours. The lesson is: &lt;strong&gt;a note is a hypothesis, not a fact.&lt;/strong&gt; Coming back from a break is exactly when you're most tempted to skip verification, because the note feels like memory. It isn't. It's a snapshot of a moving thing.&lt;/p&gt;

&lt;h2&gt;
  
  
  A re-onboarding ritual
&lt;/h2&gt;

&lt;p&gt;After a few of these returns, I stopped winging it and made the first 30 minutes a fixed routine:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Read the checkpoint.&lt;/strong&gt; Get the story back: what shipped, what's next, what's blocked.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Diff your branch against the source of truth.&lt;/strong&gt; &lt;code&gt;git log HEAD..origin/main&lt;/code&gt;. Your local state is almost always behind where you think it is. This one command erases half the confusion.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Verify the top one or two claims against the code or production — not the note.&lt;/strong&gt; Pick the things your next decisions depend on and check them at the source. This is where the stale-note traps get caught.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ship the smallest real slice you can.&lt;/strong&gt; Don't start with the scary architectural item. Find one bounded, self-contained piece, finish it end to end, and merge it. You're not doing it for the feature. You're doing it to get your hands back on the wheel.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That last step is the underrated one. The newbie feeling doesn't go away by reading more. It goes away by shipping something small and watching it work. Momentum is a context-recovery tool.&lt;/p&gt;

&lt;h2&gt;
  
  
  For the AI-assisted builder specifically
&lt;/h2&gt;

&lt;p&gt;If an assistant is writing a lot of your code, your leverage is no longer typing speed. It's the quality of the context you can hand back to yourself — and to the model — when you sit down again. The builders who stay fast across breaks aren't the ones with the best memory. They're the ones with the best re-onboarding ritual and a checkpoint doc they're disciplined enough to distrust.&lt;/p&gt;

&lt;p&gt;So write the checkpoint. Then, every time you return, verify the part that matters against the code before you build on it. The twenty minutes of feeling like a stranger is unavoidable. Spending the next twenty &lt;em&gt;acting&lt;/em&gt; on a stale note is not.&lt;/p&gt;

&lt;p&gt;Take the week off. Just don't trust the version of the project that existed before you left.&lt;/p&gt;

</description>
      <category>buildinpublic</category>
      <category>productivity</category>
      <category>devjournal</category>
      <category>ai</category>
    </item>
    <item>
      <title>I shipped the product. I have no idea how to launch it. Tell me about your first time.</title>
      <dc:creator>AurinAilean</dc:creator>
      <pubDate>Sat, 06 Jun 2026 13:43:11 +0000</pubDate>
      <link>https://dev.to/aurinaileandot/whats-the-one-signal-that-tells-you-a-niche-is-worth-your-three-months-57bn</link>
      <guid>https://dev.to/aurinaileandot/whats-the-one-signal-that-tells-you-a-niche-is-worth-your-three-months-57bn</guid>
      <description>&lt;p&gt;For the past two weeks I've been head-down in code. 5400 lines refactored across 20+ PRs. Persistent job queue migrated to Postgres. Rate-limiting deployed. Voice-source pipelines fixed (two of them had been silently returning zero for months — that was a fun discovery). API documentation polished. Test coverage on the testable logic up to 70%.&lt;/p&gt;

&lt;p&gt;The engineering work, I get. I can sit down with Claude Code at 1 AM and have a sensible PR in the morning. I can read a stack trace. I can grep my way out of any backend mystery in about 20 minutes. Code is a language I speak fluently enough.&lt;/p&gt;

&lt;p&gt;What I cannot do — and I'm two weeks pre-launch on &lt;a href="https://appstoreanalyzer.com" rel="noopener noreferrer"&gt;App Store Analyzer&lt;/a&gt; realizing this — is launch a product to actual humans.&lt;/p&gt;

&lt;p&gt;I don't know where people who would care about my tool hang out, or how to get there without being the guy who shows up to a community for the first time with a link. I don't know the cadence — post how often, where, when. I don't know what works and what just looks like work. Even with AI assistance, the launch playbook isn't something I can grep my way through. There's no error message that says "you posted on the wrong subreddit at the wrong hour."&lt;/p&gt;

&lt;p&gt;So I want to ask the indie devs here who've already been through this:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tell me about your first launch.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Not the success-story-clip-version. The actual version:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What did you do in the week BEFORE launching that you'd do again?&lt;/li&gt;
&lt;li&gt;What did you try that completely flopped — and you laugh about now?&lt;/li&gt;
&lt;li&gt;Where did your first 10 real users come from? (And how did you know they were real and not curious tourists?)&lt;/li&gt;
&lt;li&gt;What surprised you about how it actually felt — better than expected, worse, weirder?&lt;/li&gt;
&lt;li&gt;If you could go back to the week before your first launch and tell yourself one thing, what would it be?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I'm asking because I think I can build a useful tool. App Store Analyzer aggregates demand signals across 20 App Store markets and 50+ niches for indie iOS devs deciding what to build next. The engineering is solid now. But solid engineering doesn't find users.&lt;/p&gt;

&lt;p&gt;And honestly: launch stories are the kind of thing I think most of us love to tell once enough time has passed. The good parts, the embarrassing parts, what you'd do differently. There's no advice or research that beats hearing what someone actually went through. So this isn't really a "give me tips" post — it's a "tell me your story" post.&lt;/p&gt;

&lt;p&gt;Drop yours. Long or short. First project, second, third — doesn't matter. I'll read every one. And in two weeks I'll write a follow-up with the patterns I see: what looked completely different across stories, and what was weirdly the same.&lt;/p&gt;

&lt;p&gt;— Aurin&lt;/p&gt;

</description>
      <category>discuss</category>
      <category>indiedev</category>
      <category>buildinpublic</category>
      <category>beginners</category>
    </item>
    <item>
      <title>My Next.js 16 button was visible and completely dead in production. Here's why.</title>
      <dc:creator>AurinAilean</dc:creator>
      <pubDate>Fri, 05 Jun 2026 21:55:21 +0000</pubDate>
      <link>https://dev.to/aurinaileandot/my-nextjs-16-button-was-visible-and-completely-dead-in-production-heres-whyx-4k1b</link>
      <guid>https://dev.to/aurinaileandot/my-nextjs-16-button-was-visible-and-completely-dead-in-production-heres-whyx-4k1b</guid>
      <description>&lt;p&gt;I added a tiny test page to confirm my error monitoring was capturing frontend crashes. A title, a paragraph, one red button that throws an error on click. The kind of code you'd send to a code review and apologize for being trivial.&lt;/p&gt;

&lt;p&gt;Locally: worked perfectly. Click, error, captured, done.&lt;/p&gt;

&lt;p&gt;In production: the button rendered. Click did nothing. No error in the console. No network request. No visible feedback. Just dead HTML that looked exactly like a button.&lt;/p&gt;

&lt;p&gt;I'm a solo dev building an iOS market intelligence tool, and this bug burned an hour of my pre-launch sprint. The cause is a real Next.js 16 trap. The fix is well-documented. The path between "this should work" and "ah, it's this" is what I want to write about — because the same shape of bug is going to bite a lot of people once Next 16 spreads.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I had
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;use client&lt;/span&gt;&lt;span class="dl"&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;useSearchParams&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="s2"&gt;next/navigation&lt;/span&gt;&lt;span class="dl"&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;SentryTestPage&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;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useSearchParams&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;key&lt;/span&gt;&lt;span class="dl"&gt;"&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;key&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;LockedScreen&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&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="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;test crash&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      Trigger crash
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;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;A page that reads a &lt;code&gt;?key=&lt;/code&gt; query param. If the key is missing, it shows a locked screen. If present, it shows the button. Trivial.&lt;/p&gt;

&lt;p&gt;In production with &lt;code&gt;?key=...&lt;/code&gt; in the URL, the button rendered. But nothing happened on click. No event, no error, no log. The button was a div pretending to be a button.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I tried first (and why each was wrong)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Theory 1: The env variable isn't set.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I thought maybe &lt;code&gt;NEXT_PUBLIC_SENTRY_DSN&lt;/code&gt; wasn't actually present in the build. So Sentry never initialized, so when I threw an error there was no one to catch it. I checked Vercel. The variable was there. I re-deployed without build cache to make sure. Still dead.&lt;/p&gt;

&lt;p&gt;This was the right thing to check — &lt;code&gt;NEXT_PUBLIC_*&lt;/code&gt; vars are inlined at build time, and adding them later doesn't help unless you rebuild. But it wasn't the bug.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Theory 2: A floating widget is overlaying the button.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The app has a feedback button fixed to the bottom-right corner. Z-index issues are a classic source of invisible click eaters. I inspected. The button received &lt;code&gt;pointer-events: auto&lt;/code&gt;. Nothing was on top. Not it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Theory 3: The build is stale.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Hard-reload with &lt;code&gt;Cmd + Shift + R&lt;/code&gt;. Then incognito. The button still didn't respond. Not the cache.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Theory 4: React just isn't hydrating the page.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This was closer. If React fails to hydrate a subtree, the HTML is there but the JavaScript event handlers never attach. That'd look exactly like what I was seeing.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the actual bug is
&lt;/h2&gt;

&lt;p&gt;Next.js 16 (and 13+, but it's stricter in 16) requires that any component reading &lt;code&gt;useSearchParams()&lt;/code&gt; be wrapped in a &lt;code&gt;&amp;lt;Suspense&amp;gt;&lt;/code&gt; boundary. Without it, here's what happens:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;During server-side rendering, &lt;code&gt;useSearchParams()&lt;/code&gt; returns an empty params object. The component renders the "no key" branch (&lt;code&gt;&amp;lt;LockedScreen /&amp;gt;&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;On the client, the URL has &lt;code&gt;?key=...&lt;/code&gt;, so &lt;code&gt;useSearchParams()&lt;/code&gt; returns the params, and the component wants to render the "button" branch.&lt;/li&gt;
&lt;li&gt;React tries to hydrate the server HTML and finds a mismatch: the server rendered &lt;code&gt;&amp;lt;LockedScreen /&amp;gt;&lt;/code&gt;, the client wants to render the button.&lt;/li&gt;
&lt;li&gt;React aborts hydration for that subtree. It logs a warning during development (if you're watching the console) but in production it just stops.&lt;/li&gt;
&lt;li&gt;The HTML the server sent — including whichever branch happened to be there — stays in the DOM. But no event handlers ever attach.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That last point is the cruel part. In my case the &lt;em&gt;server&lt;/em&gt; had rendered the locked screen, but my browser then &lt;em&gt;swapped to the button&lt;/em&gt; in the DOM via... actually I'm not entirely sure how the button appeared at all, given the hydration was supposed to be aborted. I suspect a streaming SSR quirk where the client did render the button but React refused to wire it up. Either way: the button I saw was dead HTML.&lt;/p&gt;

&lt;p&gt;It worked locally because dev mode uses a different hydration strategy that is more forgiving of mismatches — it logs the warning and patches the DOM. Production mode does not.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fix
&lt;/h2&gt;

&lt;p&gt;The documented Next.js 16 pattern is to put the &lt;code&gt;useSearchParams()&lt;/code&gt; call inside a child component, and wrap that child in &lt;code&gt;&amp;lt;Suspense&amp;gt;&lt;/code&gt;:&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;use client&lt;/span&gt;&lt;span class="dl"&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;Suspense&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="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&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;useSearchParams&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="s2"&gt;next/navigation&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;PageContent&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;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useSearchParams&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;key&lt;/span&gt;&lt;span class="dl"&gt;"&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;key&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;LockedScreen&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&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="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;test crash&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      Trigger crash
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&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;SentryTestPage&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="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Suspense&lt;/span&gt; &lt;span class="na"&gt;fallback&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="kc"&gt;null&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;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;PageContent&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Suspense&lt;/span&gt;&lt;span class="p"&gt;&amp;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;Why this works: &lt;code&gt;&amp;lt;Suspense&amp;gt;&lt;/code&gt; tells React that the wrapped content might not be ready during initial server render. The server emits the &lt;code&gt;fallback&lt;/code&gt; (&lt;code&gt;null&lt;/code&gt;), and the client renders the real content. Because the server doesn't commit to a branch, there's no mismatch to resolve. The client takes over cleanly, the button hydrates, the onClick attaches.&lt;/p&gt;

&lt;p&gt;After this fix the button worked on the first deploy.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I should have done first
&lt;/h2&gt;

&lt;p&gt;The build log was actually telling me. Next.js prints a warning during production builds when a page uses &lt;code&gt;useSearchParams()&lt;/code&gt; without &lt;code&gt;&amp;lt;Suspense&amp;gt;&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Entire page deopted into client-side rendering. Read more: ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I didn't see it because I was looking at runtime logs, not build logs. If I'd grep'd the build output for &lt;code&gt;deopted&lt;/code&gt;, I would have found this bug in 30 seconds instead of an hour.&lt;/p&gt;

&lt;p&gt;So the rule I'm internalizing:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When something works locally and breaks in production, read the build log first.&lt;/strong&gt; The framework probably already told you what's wrong. You're just not looking where it told you.&lt;/p&gt;

&lt;p&gt;A close second:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When a React event handler doesn't fire, suspect hydration before suspecting your code.&lt;/strong&gt; The button isn't broken — the path from rendered HTML to JavaScript-wired DOM is broken. Different debug target, different fix.&lt;/p&gt;

&lt;h2&gt;
  
  
  The pattern that made this worse than it needed to be
&lt;/h2&gt;

&lt;p&gt;I had an AI pair (Claude Code) writing most of the actual edits during this sprint. The AI is very good at producing plausible code fast. When I told it to add a test page with &lt;code&gt;useSearchParams()&lt;/code&gt; and a button, it wrote code that worked in dev mode — which is what AI training data is full of, because most demo apps run in dev.&lt;/p&gt;

&lt;p&gt;The Suspense boundary requirement is the kind of detail that lives in framework upgrade guides, not in tutorials. Training data doesn't catch it. I have to.&lt;/p&gt;

&lt;p&gt;The lesson isn't "AI bad." The lesson is that &lt;strong&gt;AI is great at execution and dangerous at diagnosis&lt;/strong&gt;. It can write the next line of code as fast as I can read it. But when something is wrong, it tends to confidently propose plausible explanations. If I act on the plausible ones without verifying, I waste an hour. If I reproduce first — actually click the button, actually read the build log — the wrong explanations get killed in three minutes.&lt;/p&gt;

&lt;p&gt;That's been the meta-pattern across every bug in this sprint. Plausible was wrong. Reproduction was right. Three minutes of curl/click/grep beat three hours of "let me try this fix."&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I'm writing this
&lt;/h2&gt;

&lt;p&gt;I'm Aurin. I'm building &lt;a href="https://appstoreanalyzer.com" rel="noopener noreferrer"&gt;AppStoreAnalyzer&lt;/a&gt; — a tool that scores 50+ iOS app niches across 20 markets, so indie devs can see which niches have room before they spend three months coding. It's pre-launch. I'm writing a build-in-public log as I get to launch.&lt;/p&gt;

&lt;p&gt;This is post one. If you build iOS apps and want to look at niche data, the explorer is free and there's no signup wall. If you ever hit a Next.js 16 hydration bug, hopefully this post saved you an hour.&lt;/p&gt;

&lt;p&gt;More posts as the launch gets closer. If this bug made you wince in recognition, we'd get along.&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>webdev</category>
      <category>javascript</category>
      <category>beginners</category>
    </item>
  </channel>
</rss>
