<?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: Jakub</title>
    <description>The latest articles on DEV Community by Jakub (@jakub_inithouse).</description>
    <link>https://dev.to/jakub_inithouse</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%2F3847884%2Fd5cc2611-0246-4150-95e0-c1145fa35d05.png</url>
      <title>DEV Community: Jakub</title>
      <link>https://dev.to/jakub_inithouse</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jakub_inithouse"/>
    <language>en</language>
    <item>
      <title>What 14 Products Reveal That One Can't: Cross-Portfolio Analytics as a Cheat Code</title>
      <dc:creator>Jakub</dc:creator>
      <pubDate>Sun, 19 Apr 2026 06:40:57 +0000</pubDate>
      <link>https://dev.to/jakub_inithouse/what-14-products-reveal-that-one-cant-cross-portfolio-analytics-as-a-cheat-code-pmb</link>
      <guid>https://dev.to/jakub_inithouse/what-14-products-reveal-that-one-cant-cross-portfolio-analytics-as-a-cheat-code-pmb</guid>
      <description>&lt;p&gt;Running a single product, you get a steady line of charts: traffic, signups, conversion. You tune each dial and hope the curve bends. Running a portfolio of fourteen products, a different picture emerges. The patterns that were invisible in one product stand out like blinking lights once you can compare them against each other.&lt;/p&gt;

&lt;p&gt;This is a building-in-public note about the unfair advantage that comes from measuring many products side by side, with shared GA4 + Clarity instrumentation and one place to look at the data.&lt;/p&gt;

&lt;h2&gt;
  
  
  The setup
&lt;/h2&gt;

&lt;p&gt;We run a portfolio at Inithouse: roughly fourteen small products at different lifecycle stages. Some are days old, others have months of real traffic. All of them ship the same analytics baseline:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GA4 on every domain, with the same event schema&lt;/li&gt;
&lt;li&gt;Microsoft Clarity on every domain, with the same session triggers&lt;/li&gt;
&lt;li&gt;A shared Looker Studio workspace where every property is a drop-down away&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The payoff is not faster dashboards. The payoff is being able to ask one question across the whole portfolio and see how the answer changes with product age, category, and traffic source.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pattern 1: the "first 48 hours" scroll signal
&lt;/h2&gt;

&lt;p&gt;One of the cleanest cross-portfolio patterns we see is on new products. For every product we launch, Clarity's scroll-depth distribution in the first 48 hours is weirdly predictive of what that product's bounce rate will look like three months later.&lt;/p&gt;

&lt;p&gt;Products where the 75th percentile scrolled past the first CTA on day one (our Czech vibe-coding community &lt;a href="https://vibecoderi.cz" rel="noopener noreferrer"&gt;Vibe Codéři&lt;/a&gt; was a textbook case) tend to keep a healthy return-visit cohort. Products where the 75th percentile bailed before the fold (early iterations of &lt;a href="https://petimagination.com" rel="noopener noreferrer"&gt;Pet Imagination&lt;/a&gt;) needed a rebuild of the hero section before anything else moved.&lt;/p&gt;

&lt;p&gt;A single-product founder gets this data too. But they see one number. Without peer products to anchor against, "60% scroll past fold" is just a number. With a portfolio, that same 60% is either a red flag (our stable baseline is 78%) or a green light (our new-product baseline is 45%).&lt;/p&gt;

&lt;h2&gt;
  
  
  Pattern 2: event mapping drift
&lt;/h2&gt;

&lt;p&gt;Here's one that only shows up when you audit many properties at once: event names drift.&lt;/p&gt;

&lt;p&gt;We standardized an event set early: &lt;code&gt;cta_click&lt;/code&gt;, &lt;code&gt;form_submit&lt;/code&gt;, &lt;code&gt;generator_run&lt;/code&gt;, &lt;code&gt;pricing_view&lt;/code&gt;. When one developer ships, they use the canon. When another ships, they sometimes improvise: &lt;code&gt;cta_clicked&lt;/code&gt;, &lt;code&gt;form_submitted&lt;/code&gt;, &lt;code&gt;run_generator&lt;/code&gt;. In a single GA4 property you notice nothing. The chart still goes up. In a portfolio view, one property is suddenly missing from a funnel comparison and the cause is almost always event drift.&lt;/p&gt;

&lt;p&gt;We now run a weekly job that lists the top 10 events per property and flags anything that doesn't match the canon. It takes 30 seconds of attention per week and has saved us at least two incidents where a product looked dead in analytics and was actually converting fine, just under a renamed event.&lt;/p&gt;

&lt;p&gt;If you're building a portfolio, put the canonical event list in a YAML file next to your deploy config. Future you will send a thank-you note.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pattern 3: the Clarity "rage-click" correlation that wasn't
&lt;/h2&gt;

&lt;p&gt;We had a belief for months that rage-clicks correlated with churn. Clarity surfaces them on every session recording, and it's intuitive: angry user, bad product, user leaves. We even had a threshold in our head.&lt;/p&gt;

&lt;p&gt;Running the same analysis across the portfolio broke the belief. On &lt;a href="https://auditvibecoding.com" rel="noopener noreferrer"&gt;Audit Vibe Coding&lt;/a&gt;, high rage-click rate correlated with &lt;em&gt;higher&lt;/em&gt; engagement, because the product is interactive and users were spamming the "re-run" button on purpose. On &lt;a href="https://magicalsong.com" rel="noopener noreferrer"&gt;Magical Song&lt;/a&gt;, rage-clicks meant exactly what we thought. On a third product, the rage-click signal was dominated by a single broken element in the nav.&lt;/p&gt;

&lt;p&gt;The same metric meant three different things across three products. A single-product founder would have picked one interpretation and been either dangerously right or confidently wrong. The portfolio forces you to unlearn the metric as a universal signal and treat it as something that needs per-product context.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pattern 4: traffic source tells you which product you actually have
&lt;/h2&gt;

&lt;p&gt;One of the most humbling cross-portfolio insights: the traffic source mix tells you what kind of product you built, regardless of what you thought you were building.&lt;/p&gt;

&lt;p&gt;We have products we pitched as "tools for professionals" that turn out to get most of their traffic from casual search intent. We have products we launched as "casual consumer apps" that quietly rank for long-tail professional queries and have a tiny, high-intent conversion cohort. &lt;a href="https://watchingagents.com" rel="noopener noreferrer"&gt;Watching Agents&lt;/a&gt; is a live example: launched as a general-audience prediction platform, but the organic traffic that actually sticks is coming from research and analyst queries.&lt;/p&gt;

&lt;p&gt;If you run one product, you optimize toward whichever audience you originally imagined. If you run a portfolio, you learn early that audience is discovered, not chosen, and you start following the analytics instead of leading them.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I would tell a portfolio-curious founder
&lt;/h2&gt;

&lt;p&gt;Three things, based on a year of watching this data roll in:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;One&lt;/strong&gt;: standardize your analytics baseline before you have anything to measure. Same GA4 property setup, same event names, same Clarity config. The cost when the portfolio is small is invisible. The cost when the portfolio grows and you have to retroactively unify eight different schemas is painful.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Two&lt;/strong&gt;: look at the same dashboard view across every product every week, even the dead ones. The patterns you need to see live in the differences between products, not in any one chart.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Three&lt;/strong&gt;: treat your weakest-performing product as a control, not a failure. It tells you what "normal bad" looks like, which is the baseline you need to recognize when something on a stronger product quietly goes wrong.&lt;/p&gt;

&lt;p&gt;A portfolio is not just a hedge against one product failing. It's a continuous calibration instrument for every other product you run. Once you get used to reading it that way, going back to single-product tunnel vision feels like flying with one eye closed.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Building &lt;a href="https://inithouse.com" rel="noopener noreferrer"&gt;Inithouse&lt;/a&gt; in public: 14 products, one analytics stack, one weekly review. More portfolio notes to come.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>analytics</category>
      <category>buildinpublic</category>
      <category>webdev</category>
      <category>startup</category>
    </item>
    <item>
      <title>The Compounding Effect of Domains: Why We Buy URLs Before Writing Code</title>
      <dc:creator>Jakub</dc:creator>
      <pubDate>Wed, 15 Apr 2026 11:09:36 +0000</pubDate>
      <link>https://dev.to/jakub_inithouse/the-compounding-effect-of-domains-why-we-buy-urls-before-writing-code-1j46</link>
      <guid>https://dev.to/jakub_inithouse/the-compounding-effect-of-domains-why-we-buy-urls-before-writing-code-1j46</guid>
      <description>&lt;p&gt;We have a rule at our studio: buy the domain first, build later. It sounds backwards. But after launching 14 products in under a year, I can tell you it changes how you think about every project.&lt;/p&gt;

&lt;h2&gt;
  
  
  The domain is the land grab
&lt;/h2&gt;

&lt;p&gt;When I started &lt;a href="https://inithouse.com" rel="noopener noreferrer"&gt;Inithouse&lt;/a&gt;, the first thing I did for each product idea wasn't writing a spec or prototyping. It was opening a registrar and checking if the .com was available.&lt;/p&gt;

&lt;p&gt;Why? Because a domain is the one thing that gets harder to acquire over time. Code can be rewritten. Designs can be iterated. But a clean, memorable .com that matches your product name? Those disappear. And once they're gone, you're stuck with hyphens, weird TLDs, or paying $5k on the aftermarket.&lt;/p&gt;

&lt;p&gt;For &lt;a href="https://voicetables.com" rel="noopener noreferrer"&gt;VoiceTables&lt;/a&gt; (a voice-first workspace builder), the domain was available and it perfectly described what the product does. Same with &lt;a href="https://watchingagents.com" rel="noopener noreferrer"&gt;WatchingAgents&lt;/a&gt;, our AI prediction platform. Both names were registered months before we wrote a single line of code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Some domains start working before the product exists
&lt;/h2&gt;

&lt;p&gt;This is the part most people don't expect. A few of our domains started ranking in Google before we had anything live.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://verdictbuddy.com" rel="noopener noreferrer"&gt;VerdictBuddy&lt;/a&gt; is a good example. The domain itself signals what the product does. Google started indexing the holding page, and by the time we launched the actual product, we already had some search visibility. No ads, no backlinks. Just a domain name that matched what people were searching for.&lt;/p&gt;

&lt;p&gt;Same story with &lt;a href="https://auditvibecoding.com" rel="noopener noreferrer"&gt;AuditVibeCoding&lt;/a&gt;. "Vibe coding" started trending as a term, and having "audit" + "vibe coding" in the domain gave us a head start when people started searching for tools to review AI-generated code.&lt;/p&gt;

&lt;h2&gt;
  
  
  The .com tax is worth it
&lt;/h2&gt;

&lt;p&gt;I know there's a whole debate about whether .com still matters. For us, it absolutely does. Here's why:&lt;/p&gt;

&lt;p&gt;People type .com by default. When someone hears "Pet Imagination" and wants to check it out, they'll type &lt;a href="https://petimagination.com" rel="noopener noreferrer"&gt;petimagination.com&lt;/a&gt; without thinking. If we had petimagination.app or petimagination.io, we'd lose a chunk of direct traffic to whoever owns the .com.&lt;/p&gt;

&lt;p&gt;The one exception we make is for local markets. &lt;a href="https://vibecoderi.cz" rel="noopener noreferrer"&gt;VibeCoderi.cz&lt;/a&gt; targets Czech developers, so .cz makes sense there. And for &lt;a href="https://zivafotka.cz" rel="noopener noreferrer"&gt;ZivaFotka&lt;/a&gt;, our AI photo animation tool, we went with .cz for the Czech market but also grabbed &lt;a href="https://alivephoto.online" rel="noopener noreferrer"&gt;alivephoto.online&lt;/a&gt; for international, &lt;a href="https://zywafotka.pl" rel="noopener noreferrer"&gt;zywafotka.pl&lt;/a&gt; for Poland, and &lt;a href="https://lebendigfoto.de" rel="noopener noreferrer"&gt;lebendigfoto.de&lt;/a&gt; for Germany. Multiple domains, same codebase.&lt;/p&gt;

&lt;h2&gt;
  
  
  Our decision framework
&lt;/h2&gt;

&lt;p&gt;Not every domain purchase makes sense. Here's roughly how we think about it:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Buy immediately&lt;/strong&gt; if the .com is available and under $15/year. The yearly cost is basically nothing compared to the optionality you get. We grabbed &lt;a href="https://magicalsong.com" rel="noopener noreferrer"&gt;MagicalSong&lt;/a&gt;, &lt;a href="https://partychallenges.com" rel="noopener noreferrer"&gt;PartyChallenges&lt;/a&gt;, &lt;a href="https://scarychallenges.com" rel="noopener noreferrer"&gt;ScaryChallenges&lt;/a&gt;, &lt;a href="https://naughtychallenges.com" rel="noopener noreferrer"&gt;NaughtyChallenges&lt;/a&gt;, &lt;a href="https://hereweask.com" rel="noopener noreferrer"&gt;HereWeAsk&lt;/a&gt;, &lt;a href="https://berecommended.com" rel="noopener noreferrer"&gt;BeRecommended&lt;/a&gt;, and &lt;a href="https://withouthuman.com" rel="noopener noreferrer"&gt;WithoutHuman&lt;/a&gt; all this way. Each one cost less than a coffee subscription for the year.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Think twice&lt;/strong&gt; if it's a premium domain ($500+). At that point, you need conviction that the product will actually ship.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Skip&lt;/strong&gt; if you're forcing a name to fit a domain. The name should come naturally. If you need to add "app" or "hq" or "get" as a prefix, the name probably isn't right.&lt;/p&gt;

&lt;h2&gt;
  
  
  The portfolio effect
&lt;/h2&gt;

&lt;p&gt;Here's where it gets interesting. When you have 14 products, domains start reinforcing each other. A blog post on &lt;a href="https://withouthuman.com" rel="noopener noreferrer"&gt;WithoutHuman&lt;/a&gt; can mention &lt;a href="https://watchingagents.com" rel="noopener noreferrer"&gt;WatchingAgents&lt;/a&gt;. A solutions page on &lt;a href="https://voicetables.com" rel="noopener noreferrer"&gt;VoiceTables&lt;/a&gt; can reference &lt;a href="https://berecommended.com" rel="noopener noreferrer"&gt;BeRecommended&lt;/a&gt;. Each domain becomes a node in a network, and links between them carry SEO value.&lt;/p&gt;

&lt;p&gt;This isn't something you plan from day one. It emerges naturally when you have a portfolio of real products on real domains. And it compounds. Every new product you add creates linking opportunities with all existing ones.&lt;/p&gt;

&lt;h2&gt;
  
  
  The cost math
&lt;/h2&gt;

&lt;p&gt;Let's do some quick numbers. We own roughly 20 domains across our portfolio (including localized variants). At $10-15/year each, that's about $200-300/year total. For context, a single Google Ads click in most of our niches costs $1-3.&lt;/p&gt;

&lt;p&gt;One domain that ranks organically for its target keyword pays for the entire portfolio's registration costs many times over. It's not even close.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd tell someone starting out
&lt;/h2&gt;

&lt;p&gt;If you're building side projects or launching a startup, here's the practical version: spend 20 minutes checking domain availability before you commit to a product name. If the .com is taken, reconsider the name. Not because .com is magic, but because naming constraints often lead to better, more distinctive names anyway.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://petimagination.com" rel="noopener noreferrer"&gt;PetImagination&lt;/a&gt; is a better name than "AI Pet Portrait Generator Pro." &lt;a href="https://magicalsong.com" rel="noopener noreferrer"&gt;MagicalSong&lt;/a&gt; beats "AI Music Creator Tool." The domain forces clarity.&lt;/p&gt;

&lt;p&gt;And once you buy it, point it somewhere immediately. Even a single landing page with your email. Let Google know the domain is alive. Time is your friend here. The longer a domain has been active, the more authority it builds.&lt;/p&gt;

&lt;p&gt;That compounding never stops working.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>startup</category>
      <category>seo</category>
      <category>buildinpublic</category>
    </item>
    <item>
      <title>Building a Voice-First Workspace: What We Learned Shipping VoiceTables</title>
      <dc:creator>Jakub</dc:creator>
      <pubDate>Tue, 14 Apr 2026 07:39:08 +0000</pubDate>
      <link>https://dev.to/jakub_inithouse/building-a-voice-first-workspace-what-we-learned-shipping-voicetables-15d8</link>
      <guid>https://dev.to/jakub_inithouse/building-a-voice-first-workspace-what-we-learned-shipping-voicetables-15d8</guid>
      <description>&lt;p&gt;Most developer tools start with a UI mockup. Ours started with a microphone.&lt;/p&gt;

&lt;p&gt;We just launched &lt;a href="https://voicetables.com" rel="noopener noreferrer"&gt;VoiceTables&lt;/a&gt; -- a workspace where you describe what you need and the AI builds it. Say "I need a client tracker with status and revenue" and you get a structured table with the right columns, sample data, and connected docs. No forms, no drag-and-drop, no 20 minutes of manual setup.&lt;/p&gt;

&lt;p&gt;Here's what we learned building it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The core problem: voice is messy, tables are not
&lt;/h2&gt;

&lt;p&gt;The hardest part wasn't the speech-to-text. That's basically a solved problem in 2026. The real challenge was turning fuzzy human language into clean, structured data.&lt;/p&gt;

&lt;p&gt;When someone says "track my invoices," they don't specify column types, data formats, or relationships. They just... say it. And they expect the result to make sense.&lt;/p&gt;

&lt;p&gt;So the actual engineering challenge is the middle layer: understanding intent, inferring schema, generating realistic sample data, and presenting it all in under 60 seconds.&lt;/p&gt;

&lt;h2&gt;
  
  
  How the pipeline works
&lt;/h2&gt;

&lt;p&gt;At a high level:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Voice capture&lt;/strong&gt; -- browser-native Web Speech API for real-time transcription. We tried a few third-party APIs early on but the latency was killing the experience. Native browser speech recognition is faster and good enough for workspace commands.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Intent parsing&lt;/strong&gt; -- the transcribed text goes through an LLM layer that extracts the workspace type, expected fields, and data relationships. This is where most of the prompt engineering lives. We tested dozens of system prompts before landing on one that reliably generates sensible schemas from vague descriptions.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Schema generation&lt;/strong&gt; -- the parsed intent becomes a typed schema (column names, types, constraints). We generate sample data that actually looks real, not "lorem ipsum" rows but things like "Acme Design, $2,400, Paid."&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Real-time rendering&lt;/strong&gt; -- the table appears progressively as the AI generates it. No loading spinner. You see the columns form, then the rows fill in. It feels like someone is building it in front of you.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Conversational refinement was the surprise hit
&lt;/h2&gt;

&lt;p&gt;We built the initial "voice to table" flow and thought that was the product. But during testing, people kept talking to it. "Add a priority column." "Filter by overdue items." "Summarize my revenue."&lt;/p&gt;

&lt;p&gt;So we leaned into it. VoiceTables now supports ongoing conversation with your workspace. You can restructure, filter, add docs, ask questions about your data, all without touching a menu.&lt;/p&gt;

&lt;p&gt;The technical bit here: we maintain a conversation context that includes the current schema state. Each new command is interpreted relative to what already exists. It's basically a stateful agent loop where the workspace IS the memory.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three-in-one was intentional
&lt;/h2&gt;

&lt;p&gt;We ship tables, docs, and AI chat in one workspace. Not because "more features = better" but because in practice, data and context always live together.&lt;/p&gt;

&lt;p&gt;When a freelancer tracks invoices, they also need a note about payment terms. When a team lead sets up a sprint board, they need a doc for the retrospective. Forcing people into three separate tools (Notion for docs, Airtable for tables, ChatGPT for questions) is a workflow tax nobody wants to pay.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's next
&lt;/h2&gt;

&lt;p&gt;We're in beta right now. Free tier, $19/mo Plus plan, custom Enterprise. No credit card needed to start.&lt;/p&gt;

&lt;p&gt;The honest truth: we don't know yet which use cases will stick. Freelancers tracking clients? Small teams running projects? Field workers logging inventory by voice? We built it broad on purpose and we're watching what people actually do with it.&lt;/p&gt;

&lt;p&gt;If you want to try it: &lt;a href="https://voicetables.com" rel="noopener noreferrer"&gt;voicetables.com&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I'm Jakub, building &lt;a href="https://voicetables.com" rel="noopener noreferrer"&gt;VoiceTables&lt;/a&gt; and a portfolio of AI products at &lt;a href="https://inithouse.com" rel="noopener noreferrer"&gt;Inithouse&lt;/a&gt;. Other things we've shipped recently: &lt;a href="https://berecommended.com" rel="noopener noreferrer"&gt;Be Recommended&lt;/a&gt; (check if AI chatbots recommend your brand), &lt;a href="https://auditvibecoding.com" rel="noopener noreferrer"&gt;Audit Vibecoding&lt;/a&gt; (security audit for AI-generated code), and &lt;a href="https://watchingagents.com" rel="noopener noreferrer"&gt;Watching Agents&lt;/a&gt; (AI prediction platform). Follow along for more build logs.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>startup</category>
      <category>buildinpublic</category>
    </item>
    <item>
      <title>11 Landing Pages for 11 Niches: How We're Testing Who Actually Wants VoiceTables</title>
      <dc:creator>Jakub</dc:creator>
      <pubDate>Sun, 12 Apr 2026 11:42:41 +0000</pubDate>
      <link>https://dev.to/jakub_inithouse/11-landing-pages-for-11-niches-how-were-testing-who-actually-wants-voicetables-1aki</link>
      <guid>https://dev.to/jakub_inithouse/11-landing-pages-for-11-niches-how-were-testing-who-actually-wants-voicetables-1aki</guid>
      <description>&lt;p&gt;Most founders pick a niche and go all-in. We did the opposite. We built 11 landing pages for 11 different professions and shipped them all at once.&lt;/p&gt;

&lt;p&gt;Here's why, and what we're learning from it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem with "pick one niche"
&lt;/h2&gt;

&lt;p&gt;When we started building &lt;a href="https://voicetables.com" rel="noopener noreferrer"&gt;VoiceTables&lt;/a&gt; (a voice-first AI workspace where you talk instead of type), we had the classic early-stage dilemma. The product works for a lot of different people. Freelancers tracking invoices. Sales reps logging calls from the car. Craftsmen on job sites with dirty hands.&lt;/p&gt;

&lt;p&gt;But which one actually &lt;em&gt;wants&lt;/em&gt; it enough to pay?&lt;/p&gt;

&lt;p&gt;The lean startup playbook says: pick one, validate, iterate. That's solid advice. But when your product is horizontal (like a database or a spreadsheet), picking one niche too early means you might be optimizing for the wrong audience for months.&lt;/p&gt;

&lt;h2&gt;
  
  
  So we built 11 solution pages instead
&lt;/h2&gt;

&lt;p&gt;Each page targets a different profession with tailored copy, specific voice command examples, and use cases that speak directly to their daily pain points. Here's the full list:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Craftsmen &amp;amp; Tradespeople&lt;/strong&gt; -- "Add a job: kitchen rewiring, 4 hours, 340 euros"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sales Reps &amp;amp; Field Agents&lt;/strong&gt; -- "Update Acme deal: met with Sarah, follow up Friday"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Real Estate Agents&lt;/strong&gt; -- "The Johnsons loved the backyard, follow up Thursday"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Freelancers &amp;amp; Solopreneurs&lt;/strong&gt; -- "Add project: rebrand for Olive Studio, 3500 euros"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Small Business Owners&lt;/strong&gt; -- "Received shipment: 50 candle sets, invoice 4821"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Event Planners&lt;/strong&gt; -- "Add vendor: Bloom Florals, delivering at 2pm, budget 1200"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Coaches &amp;amp; Personal Trainers&lt;/strong&gt; -- "Log Jake: bench press 80kg, 4 sets, felt strong"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Consultants &amp;amp; Agencies&lt;/strong&gt; -- "Log 3 hours on DataFlow strategy, competitor analysis"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Students &amp;amp; Researchers&lt;/strong&gt; -- "Add source: Smith 2024, relevant to chapter 3"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Content Creators&lt;/strong&gt; -- "New brand deal: FitGear, 3 posts, budget 2000"&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Each page lives under &lt;a href="https://voicetables.com/solutions" rel="noopener noreferrer"&gt;voicetables.com/solutions&lt;/a&gt;. Same product, same features, completely different framing.&lt;/p&gt;

&lt;h2&gt;
  
  
  The thinking behind it
&lt;/h2&gt;

&lt;p&gt;This isn't random. We grouped the niches into three buckets:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;In the Field&lt;/strong&gt; -- people whose hands are literally too busy to type. Craftsmen, sales reps, real estate agents. Their phone is in their pocket, they need to capture data between appointments or while standing in someone's kitchen.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Running a Business&lt;/strong&gt; -- solopreneurs and small teams wearing every hat. Freelancers, small business owners, event planners. They don't have time for complex tools. They need something that just works when they talk to it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Knowledge Work&lt;/strong&gt; -- people drowning in information. Coaches tracking client progress, consultants juggling multiple projects, students collecting research sources, creators managing brand deals.&lt;/p&gt;

&lt;p&gt;The hypothesis: the "in the field" group probably has the strongest pull because typing is genuinely painful for them. But we don't know yet. That's the whole point.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we're actually measuring
&lt;/h2&gt;

&lt;p&gt;For each solution page, we're watching:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Traffic and bounce rate&lt;/strong&gt; -- does the page title and meta description attract the right clicks from search?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Time on page&lt;/strong&gt; -- does the copy resonate or do people leave after 5 seconds?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CTA clicks&lt;/strong&gt; -- does anyone actually click "Try it free"?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Signup-to-activation&lt;/strong&gt; -- the real metric. Did they create their first table by voice?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We're using Google Search Console for indexation tracking and GA4 for behavior data. It's early days, but we already see differences in how each page performs.&lt;/p&gt;

&lt;h2&gt;
  
  
  The portfolio scout mindset
&lt;/h2&gt;

&lt;p&gt;This approach comes from something we practice at &lt;a href="https://inithouse.com" rel="noopener noreferrer"&gt;Inithouse&lt;/a&gt; more broadly. We run a portfolio of products, each testing a different market. &lt;a href="https://zivafotka.cz" rel="noopener noreferrer"&gt;Ziva Fotka&lt;/a&gt; animates old photos. &lt;a href="https://magicalsong.com" rel="noopener noreferrer"&gt;Magical Song&lt;/a&gt; generates custom songs with real vocals. &lt;a href="https://berecommended.com" rel="noopener noreferrer"&gt;Be Recommended&lt;/a&gt; checks if AI chatbots recommend your business. &lt;a href="https://auditvibecoding.com" rel="noopener noreferrer"&gt;Audit Vibe Coding&lt;/a&gt; audits AI-generated codebases.&lt;/p&gt;

&lt;p&gt;Same philosophy: ship fast, measure everything, double down on what works. Kill what doesn't.&lt;/p&gt;

&lt;p&gt;With VoiceTables, we're applying that same mindset &lt;em&gt;within&lt;/em&gt; a single product. Instead of launching 11 separate products for 11 niches, we built one product with 11 entry points. Each solution page is basically a micro-experiment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Early observations
&lt;/h2&gt;

&lt;p&gt;We're only a couple weeks in, so take this with a grain of salt. But a few things are already becoming visible:&lt;/p&gt;

&lt;p&gt;The "hands-busy" niches (craftsmen, sales reps) generate the most intuitive copy. When the value prop is "your hands are full, just talk," people get it immediately. There's no explaining needed.&lt;/p&gt;

&lt;p&gt;Knowledge worker niches need more convincing. A consultant already has Notion and Airtable. Why switch? The answer ("because you can just say what you need instead of building a schema") is compelling, but it takes more words to land.&lt;/p&gt;

&lt;p&gt;Students are a wild card. Low willingness to pay, but high engagement when they try it. Could be a growth lever, could be a distraction.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's next
&lt;/h2&gt;

&lt;p&gt;Over the next few weeks, we'll start seeing real SEO data come in. Which pages rank, which keywords drive clicks, which niches have enough search demand to justify further investment.&lt;/p&gt;

&lt;p&gt;Then comes the hard part: picking 2-3 winners and going deep. Custom features, tailored onboarding, maybe even niche-specific pricing.&lt;/p&gt;

&lt;p&gt;But for now, 11 pages are out there, collecting data. And that beats guessing.&lt;/p&gt;




&lt;p&gt;If you're building a horizontal product and struggling with niche selection, try this approach. Ship multiple landing pages, each framed for a different audience. Let the data tell you who your real users are.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://voicetables.com" rel="noopener noreferrer"&gt;VoiceTables&lt;/a&gt; is free to try, no credit card needed. And if you want to see all 11 solution pages: &lt;a href="https://voicetables.com/solutions" rel="noopener noreferrer"&gt;voicetables.com/solutions&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We also recently launched &lt;a href="https://watchingagents.com" rel="noopener noreferrer"&gt;Watching Agents&lt;/a&gt;, a prediction platform where AI agents track questions about the future, and &lt;a href="https://partychallenges.com" rel="noopener noreferrer"&gt;Party Challenges&lt;/a&gt; / &lt;a href="https://hereweask.com" rel="noopener noreferrer"&gt;Here We Ask&lt;/a&gt;, two card games testing the social games market. Different products, same experiment-first philosophy.&lt;/p&gt;

&lt;p&gt;Would love to hear if anyone else is running multi-niche landing page tests. What worked, what didn't?&lt;/p&gt;

</description>
      <category>saas</category>
      <category>startup</category>
      <category>buildinpublic</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Our Portfolio Runs on One-Time Payments. Here's the Stripe Setup.</title>
      <dc:creator>Jakub</dc:creator>
      <pubDate>Fri, 10 Apr 2026 07:44:19 +0000</pubDate>
      <link>https://dev.to/jakub_inithouse/our-portfolio-runs-on-one-time-payments-heres-the-stripe-setup-4l5b</link>
      <guid>https://dev.to/jakub_inithouse/our-portfolio-runs-on-one-time-payments-heres-the-stripe-setup-4l5b</guid>
      <description>&lt;p&gt;Every SaaS playbook says the same thing: build recurring revenue. MRR is king. Subscriptions scale.&lt;/p&gt;

&lt;p&gt;I went the other way. Our portfolio of 14 products runs entirely on one-time payments. No monthly fees, no annual plans, no "cancel anytime" copy. You pay once, you get your thing, done.&lt;/p&gt;

&lt;p&gt;Here's why, and how we set it up with Stripe.&lt;/p&gt;

&lt;h2&gt;
  
  
  The subscription fatigue problem
&lt;/h2&gt;

&lt;p&gt;People are tired. The average consumer now manages 12+ active subscriptions. Every new tool wants $9/month, $29/month, $99/month. And for products people use once or twice? That math just doesn't work for either side.&lt;/p&gt;

&lt;p&gt;We build AI-powered tools. Most of our users come in, do one thing, and leave happy. An animated photo. A custom song. A code audit report. Charging $9/month for something someone uses once a quarter felt dishonest.&lt;/p&gt;

&lt;p&gt;So we asked: what if we just... didn't?&lt;/p&gt;

&lt;h2&gt;
  
  
  What our payment model looks like
&lt;/h2&gt;

&lt;p&gt;Three products, three simple prices:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://magicalsong.com" rel="noopener noreferrer"&gt;Magical Song&lt;/a&gt;&lt;/strong&gt; - AI-generated custom songs with real vocals. $6 per song. No account needed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://auditvibecoding.com" rel="noopener noreferrer"&gt;Audit Vibecoding&lt;/a&gt;&lt;/strong&gt; - Professional audit of AI-generated code. $4-9 per report. 47 checks across security, SEO, performance, accessibility.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://zivafotka.cz" rel="noopener noreferrer"&gt;Ziva Fotka&lt;/a&gt;&lt;/strong&gt; - AI photo animation. One-time payment per animation. Serves 5 markets (CZ, SK, PL, EN, DE) from a single codebase.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No free trials that convert to paid. No credit card upfront. No dark patterns. You see the price, you pay, you get value. Immediately.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Stripe implementation
&lt;/h2&gt;

&lt;p&gt;Setting up one-time payments with Stripe is surprisingly simple. Here's the architecture we use across all products:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User clicks "Buy" &amp;gt; Stripe Checkout Session (server-side) &amp;gt; Payment &amp;gt; Webhook &amp;gt; Deliver value
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The key piece is &lt;strong&gt;Stripe Checkout&lt;/strong&gt; in payment mode (not subscription):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;stripe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;checkout&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sessions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;payment&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;line_items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;
    &lt;span class="na"&gt;price_data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;currency&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;usd&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;product_data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Custom AI Song&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Studio-quality song with real vocals&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="na"&gt;unit_amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;600&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// $6.00&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;quantity&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="p"&gt;}],&lt;/span&gt;
  &lt;span class="na"&gt;success_url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;YOUR_DOMAIN&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/success?session_id={CHECKOUT_SESSION_ID}&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;cancel_url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;YOUR_DOMAIN&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/cancel&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then handle the webhook:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Listen for checkout.session.completed&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/webhook&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;stripe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;webhooks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;constructEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;stripe-signature&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="nx"&gt;webhookSecret&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="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;checkout.session.completed&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;object&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="c1"&gt;// Trigger value delivery (generate song, run audit, etc.)&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;deliverProduct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;productType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;received&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&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;That's it. No subscription logic, no billing cycles, no failed payment retry flows, no churn tracking infrastructure. The simplicity is the point.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we learned
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Conversion rates are higher than we expected.&lt;/strong&gt; When someone sees "$6, one time" vs "$9/month", the barrier drops significantly. People don't need to calculate annual cost or worry about forgetting to cancel.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Support load dropped.&lt;/strong&gt; No "how do I cancel?" emails. No "I was charged again" disputes. No proration questions. Our support inbox is mostly "cool product, thanks."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The economics work differently.&lt;/strong&gt; Yes, we don't get monthly compounding. But we also don't have churn. Every dollar earned is earned. No MRR anxiety, no cohort decay charts to stare at.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Multi-currency is free complexity.&lt;/strong&gt; Stripe handles it. We set prices in USD and Stripe converts automatically based on the buyer's card. For Ziva Fotka, which runs across 5 countries, this was a huge win.&lt;/p&gt;

&lt;h2&gt;
  
  
  When one-time payments DON'T work
&lt;/h2&gt;

&lt;p&gt;I'm not saying subscriptions are bad. They make sense when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your product delivers ongoing value (project management, communication)&lt;/li&gt;
&lt;li&gt;Users come back daily or weekly&lt;/li&gt;
&lt;li&gt;You need to fund continuous infrastructure costs per user&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But if your product is transactional, if users come for a specific output, one-time payments align incentives better. You only get paid when you deliver value. There's something clean about that.&lt;/p&gt;

&lt;h2&gt;
  
  
  The tech stack
&lt;/h2&gt;

&lt;p&gt;All our products run on React + Supabase + Stripe, deployed through Lovable. The payment flow is identical across products, just with different prices and delivery logic. We built it once and reuse it everywhere.&lt;/p&gt;

&lt;p&gt;If you're building something where users want a result, not a relationship, consider skipping the subscription. Your Stripe integration will be simpler, your users will be happier, and you might be surprised by the conversion numbers.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Building 14 products in public at &lt;a href="https://inithouse.com" rel="noopener noreferrer"&gt;inithouse.com&lt;/a&gt;. Follow the journey.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>stripe</category>
      <category>webdev</category>
      <category>startup</category>
      <category>buildinpublic</category>
    </item>
    <item>
      <title>We Query ChatGPT, Claude, and Perplexity About Your Brand. Here's the Architecture</title>
      <dc:creator>Jakub</dc:creator>
      <pubDate>Thu, 09 Apr 2026 12:43:14 +0000</pubDate>
      <link>https://dev.to/jakub_inithouse/we-query-chatgpt-claude-and-perplexity-about-your-brand-heres-the-architecture-1fgn</link>
      <guid>https://dev.to/jakub_inithouse/we-query-chatgpt-claude-and-perplexity-about-your-brand-heres-the-architecture-1fgn</guid>
      <description>&lt;p&gt;A few months ago I had a question that didn't let me sleep. When people ask ChatGPT "what's the best tool for X", which brands actually get mentioned? And more importantly: does &lt;em&gt;my&lt;/em&gt; brand get mentioned?&lt;/p&gt;

&lt;p&gt;Turns out this is a surprisingly hard thing to measure. So we built a product for it. It's called &lt;a href="https://berecommended.com" rel="noopener noreferrer"&gt;Be Recommended&lt;/a&gt;, and under the hood it's a system that parallel-queries ChatGPT, Claude, Perplexity, and Gemini, then scores how visible a brand is inside their answers.&lt;/p&gt;

&lt;p&gt;This post is about the architecture. And about the data we found, which frankly shocked us.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem in one line
&lt;/h2&gt;

&lt;p&gt;Traditional SEO tells you how you rank on Google. But when someone opens ChatGPT and asks "what's the best CRM for a solo consultant", Google isn't in the room. The LLM answers directly, from its training data plus whatever live retrieval it does. If your brand isn't in that answer, you lose the customer before you even know they existed.&lt;/p&gt;

&lt;p&gt;The Gartner forecast everyone quotes says roughly a quarter of search volume moves to AI assistants by 2026. Our own data, which I'll get to, suggests the shift is faster than that in some verticals.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this is harder than it looks
&lt;/h2&gt;

&lt;p&gt;The naive version is easy: open ChatGPT, type a prompt, see if your brand is mentioned. Done.&lt;/p&gt;

&lt;p&gt;The real version is not.&lt;/p&gt;

&lt;p&gt;Problem one is prompt variance. "Best CRM for solo consultant" and "what CRM should a one person agency use" return different answers. Any scoring that hits a single prompt is basically noise. You need a prompt set per vertical, seeded with realistic intent, and you need to rotate through it.&lt;/p&gt;

&lt;p&gt;Problem two is model variance. ChatGPT and Claude don't even agree on what decade it is, let alone which SaaS product is the market leader. Averaging them hides the signal. We report per-engine scores separately and then a blended composite.&lt;/p&gt;

&lt;p&gt;Problem three is response variance. The same prompt to the same model on two different days will surface different brands. You need to sample, not snapshot.&lt;/p&gt;

&lt;p&gt;Problem four, and this is the one that took us the longest, is parsing. LLMs don't return JSON when you ask "what CRM should I use". They return prose with brand names sprinkled inside sentences, sometimes with typos, sometimes with "CRM Hub" instead of "HubSpot CRM", sometimes with a brand mentioned in a dismissive footnote. You can't just grep.&lt;/p&gt;

&lt;h2&gt;
  
  
  The architecture, top to bottom
&lt;/h2&gt;

&lt;p&gt;Here is how a single "analyze my brand" request flows through the system.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step one: intent expansion.&lt;/strong&gt; The user gives us a brand name and a URL. We pull the landing page, classify the vertical, and generate a set of 20 to 40 representative buyer intents. "Best X for Y", "alternatives to Z", "is brand A worth it", "what's the cheapest way to do W". The prompts are not random. They are templated against real search intents we've scraped from Quora, Reddit, and Google's People Also Ask.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step two: parallel fanout.&lt;/strong&gt; Each prompt is dispatched to every engine in parallel. Four engines, up to forty prompts, that's one hundred and sixty concurrent inference calls per scan. We don't wait serially. If you do, a single scan takes twenty minutes and the user bounces.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step three: rate limiting.&lt;/strong&gt; This is where it got real. OpenAI's TPM limits on the standard tier make a naive fanout impossible. We implemented a token-bucket limiter per engine, per model, with exponential backoff and jittered retries. When one engine is saturated we spill to another to keep the user's scan moving. Responses get deduplicated against a short-lived cache so two prompts with near-identical semantics don't double-bill.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step four: response normalization.&lt;/strong&gt; This is the parsing layer. Each response gets run through a second, cheaper LLM call whose only job is to extract mentioned entities and classify the sentiment of each mention. Positive mention, neutral mention, negative mention, dismissive footnote. We also track position: was your brand first, third, or buried at the end of a list of ten? Position matters because users don't read past the top three.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step five: scoring.&lt;/strong&gt; The scoring algorithm is where the opinion lives. We weight by engine reach (ChatGPT dominates), by mention sentiment, by position, and by the share of prompts that mentioned the brand at all. The output is a 0 to 100 score, the AI Visibility Score. The math is tuned so that a brand mentioned positively in the top three of most prompts on most engines lands around 80. That's the "top performer" band.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step six: reporting.&lt;/strong&gt; We don't dump raw data on users. The final report shows the composite score, a per-engine breakdown, the exact prompts where you were mentioned and where you weren't, the top competitors that showed up in your place, and a prioritized list of fixes. The fixes come from a rules engine that pattern-matches on the gaps. Missing G2 listing, thin landing page, no schema markup, no authoritative third-party mention, and so on.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the data showed us
&lt;/h2&gt;

&lt;p&gt;We ran the system against a few hundred brands across SaaS, e-commerce, agencies, and consumer apps. A few findings that genuinely surprised me.&lt;/p&gt;

&lt;p&gt;The average AI Visibility Score is 31. Out of 100. That means for most companies, most of the time, the major LLMs either don't mention them or mention them in a throwaway sentence. Think about that for a second. A whole industry is optimizing for Google while the actual decision-making conversation has already moved somewhere they're not even present.&lt;/p&gt;

&lt;p&gt;Brand recognition and AI visibility are barely correlated. We scanned some household SaaS names and found scores in the 40s. We scanned smaller tools with great documentation and active third-party review coverage and found scores in the 70s. Training data eats marketing budgets for breakfast. What the internet wrote about you five years ago matters more than what your ad agency shipped last quarter.&lt;/p&gt;

&lt;p&gt;The engines disagree a lot. It's common to see a 30-point spread between ChatGPT and Perplexity for the same brand. Perplexity leans heavily on fresh retrieval, so it rewards recent coverage. ChatGPT leans on training snapshots, so it rewards longevity and repeat mentions in tech press. Claude sits in the middle. Gemini is the wildcard.&lt;/p&gt;

&lt;p&gt;Position bias is brutal. Being mentioned fifth in a list of ten is almost the same as not being mentioned at all. Users read the first two or three answers and stop. If the LLM puts you in sixth place, you're invisible.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we learned building it
&lt;/h2&gt;

&lt;p&gt;Two things I'd tell anyone trying to build something similar.&lt;/p&gt;

&lt;p&gt;First: the parsing layer is where your product lives or dies. Getting prompts right is easy. Calling the APIs is easy. Extracting clean, structured mentions from messy LLM prose is the whole game. We rewrote ours three times before it was usable.&lt;/p&gt;

&lt;p&gt;Second: this space is moving fast enough that any snapshot is a guess at the current weather. We rescan on a schedule because the same brand can swing 20 points in a month when a big review lands or a model version ships. A one-shot audit is entertainment. A tracked score over time is actually useful.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;If you're curious what the three big LLMs say about your own brand, &lt;a href="https://berecommended.com" rel="noopener noreferrer"&gt;Be Recommended&lt;/a&gt; runs a scan for $4. You get a full report with the prompts, the engines, the competitors, and the fixes. We built it because we were running this for ourselves every week and decided it was silly not to let other people use it.&lt;/p&gt;

&lt;p&gt;Would love to hear what score you get. Drop it in the comments.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>buildinpublic</category>
      <category>webdev</category>
      <category>machinelearning</category>
    </item>
    <item>
      <title>AI Style Transfer That Doesn't Lose the Dog</title>
      <dc:creator>Jakub</dc:creator>
      <pubDate>Wed, 08 Apr 2026 08:41:55 +0000</pubDate>
      <link>https://dev.to/jakub_inithouse/ai-style-transfer-that-doesnt-lose-the-dog-4927</link>
      <guid>https://dev.to/jakub_inithouse/ai-style-transfer-that-doesnt-lose-the-dog-4927</guid>
      <description>&lt;p&gt;Style transfer sounds simple in theory. You take an image, apply an artistic style, done. Except when your input is a photo of someone's actual pet, "done" is the beginning of the problem.&lt;/p&gt;

&lt;p&gt;I've been building &lt;a href="https://petimagination.com" rel="noopener noreferrer"&gt;Pet Imagination&lt;/a&gt;, an AI pet art generator with 9 artistic styles. The pitch is straightforward: upload a photo of your dog, cat, bird, whatever, pick a style like Watercolor or Renaissance, and get back a portrait that actually looks like &lt;em&gt;your&lt;/em&gt; pet. Not a generic cute animal wearing a costume.&lt;/p&gt;

&lt;p&gt;Turns out that last part is really, really hard.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Identity Problem
&lt;/h2&gt;

&lt;p&gt;Most style transfer approaches treat the content image as a loose suggestion. You feed in a photo of a French Bulldog with a distinctive underbite and brindle pattern, and out comes... a vaguely dog-shaped blob in the target style. The "artistic interpretation" ate everything that made the dog recognizable.&lt;/p&gt;

&lt;p&gt;This is fine for landscapes. Nobody cares if the mountains shift a bit. But pet owners notice &lt;em&gt;everything&lt;/em&gt;. The ear shape is wrong? They'll tell you. The markings switched sides? Absolutely not. The expression changed from "skeptical" to "happy"? That's not their dog anymore.&lt;/p&gt;

&lt;p&gt;We went through dozens of iterations trying to solve this. Early versions were basically unusable. The AI would latch onto the style so hard that breed-specific features just vanished. A Dalmatian without spots. A Husky with floppy ears. A Siamese cat that came out tabby.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Renaissance Works Better Than Anime
&lt;/h2&gt;

&lt;p&gt;Here's something I didn't expect: some art styles are inherently better at preserving identity than others.&lt;/p&gt;

&lt;p&gt;Renaissance style turned out to be one of the most reliable. It makes sense when you think about it. Renaissance portraiture was obsessed with capturing the exact likeness of the subject. The style itself demands accurate proportions, realistic fur textures, and faithful color reproduction. The AI just needs to add dramatic lighting, a dark background, and maybe a lace collar. The subject stays intact.&lt;/p&gt;

&lt;p&gt;Anime, on the other hand, was a nightmare. The anime aesthetic actively fights against individual features. It wants to simplify. Round the eyes, smooth the fur, standardize the proportions. Every cat wants to become the same cat. We had to significantly constrain the style influence to keep breed characteristics visible, and even then it's the style most likely to drift.&lt;/p&gt;

&lt;p&gt;Watercolor sits in a nice middle ground. The loose brushwork actually helps because it doesn't need pixel-perfect accuracy, but the composition stays true to the original. Sketch works similarly. The abstraction is in the rendering technique, not in the subject's features.&lt;/p&gt;

&lt;p&gt;The costume styles (Sheriff, Wizard, Astronaut) introduced a completely different challenge. You're adding elements that don't exist in the source photo. A sheriff hat, a wizard robe, a space helmet. The AI has to figure out where the pet ends and the costume begins, and it has to do that without obscuring the face. We found that any costume element overlapping with the pet's face would tank recognition. So the generation pipeline specifically protects the facial region.&lt;/p&gt;

&lt;h2&gt;
  
  
  The 4K Upscale Problem
&lt;/h2&gt;

&lt;p&gt;Generation at base resolution is one thing. Pushing to 4K for print-quality output is another game entirely.&lt;/p&gt;

&lt;p&gt;The naive approach is generate-then-upscale. Run the style transfer at a manageable resolution, then use a super-resolution model to blow it up to 4K. Problem: super-resolution models are trained on photos, not art. They try to "fix" the artistic style by sharpening brushstrokes into photo-realistic textures. Your watercolor portrait suddenly has crispy fur detail that breaks the whole aesthetic.&lt;/p&gt;

&lt;p&gt;We ended up needing style-aware upscaling that respects the artistic treatment. Watercolor gets soft upscaling that preserves the bleed edges. Sketch keeps the pencil texture without adding phantom detail. Renaissance gets the most aggressive sharpening because it wants that level of detail.&lt;/p&gt;

&lt;p&gt;Processing time was another constraint. Pet owners aren't patient. They uploaded a photo for fun, probably on their phone, probably with three other tabs open. If it takes more than 60 seconds, they're gone. Getting 4K output within that window required some aggressive optimization on the pipeline side.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Actually Ships
&lt;/h2&gt;

&lt;p&gt;The live product at &lt;a href="https://petimagination.com" rel="noopener noreferrer"&gt;petimagination.com&lt;/a&gt; does 9 styles: Watercolor, Renaissance, Anime, Sketch, Sheriff, Wizard, Astronaut, Final Boss, and Blocky. Each one was individually tuned. There's no universal "style strength" slider because the right balance is different for every style.&lt;/p&gt;

&lt;p&gt;It accepts any pet species. Dogs and cats are the majority of uploads, obviously, but we've had birds, rabbits, reptiles, hamsters. The identity preservation challenge scales with how distinctive the animal looks. A golden retriever is harder than a parrot because golden retrievers look more similar to each other than parrots do.&lt;/p&gt;

&lt;p&gt;No account required. No watermarks. Print-ready output with optional 4K upscale. The whole thing runs in the browser, upload to download in under 60 seconds.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lessons for Anyone Doing Style Transfer
&lt;/h2&gt;

&lt;p&gt;If you're working on something similar, a few things I'd pass along:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Test with breed pairs, not random images.&lt;/strong&gt; Get two photos of different dogs of the same breed and verify the output looks different. If your style transfer makes two different Golden Retrievers look identical, your identity preservation is broken.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Protect the face region explicitly.&lt;/strong&gt; Whatever else you do with style strength, dial it back around the eyes and muzzle. That's where recognition lives.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Different styles need different pipelines.&lt;/strong&gt; A single model with a style parameter will always compromise. Renaissance and Anime are so fundamentally different in what they preserve vs. abstract that treating them the same way guarantees mediocre results for both.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Upscaling is not an afterthought.&lt;/strong&gt; If you're promising print quality, your upscaling strategy needs to be style-aware from day one. Bolting it on later creates artifacts that are obvious at print size.&lt;/p&gt;




&lt;p&gt;This is one of ~14 products I'm building at &lt;a href="https://inithouse.com" rel="noopener noreferrer"&gt;Inithouse&lt;/a&gt;. We're a small team running lean experiments across different niches. Some of our other projects: &lt;a href="https://magicalsong.com" rel="noopener noreferrer"&gt;Magical Song&lt;/a&gt; for AI-generated custom songs, &lt;a href="https://berecommended.com" rel="noopener noreferrer"&gt;Be Recommended&lt;/a&gt; for checking your AI visibility score, and &lt;a href="https://watchingagents.com" rel="noopener noreferrer"&gt;Watching Agents&lt;/a&gt;, a prediction platform where AI agents track questions about the future.&lt;/p&gt;

&lt;p&gt;If you're building something with generative AI and hitting similar identity-preservation challenges, I'd love to hear how you're solving it.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>machinelearning</category>
      <category>webdev</category>
      <category>buildinpublic</category>
    </item>
    <item>
      <title>Same Components, Different Worlds: Two Card Games from One Codebase</title>
      <dc:creator>Jakub</dc:creator>
      <pubDate>Tue, 07 Apr 2026 09:39:00 +0000</pubDate>
      <link>https://dev.to/jakub_inithouse/same-components-different-worlds-two-card-games-from-one-codebase-17h9</link>
      <guid>https://dev.to/jakub_inithouse/same-components-different-worlds-two-card-games-from-one-codebase-17h9</guid>
      <description>&lt;p&gt;When I started building &lt;a href="https://partychallenges.com" rel="noopener noreferrer"&gt;Party Challenges&lt;/a&gt;, I didn't plan to ship a second product from the same code. But about two weeks in, I noticed something. The card component, the deck selector, the game loop, the PWA setup... none of it cared what was &lt;em&gt;on&lt;/em&gt; the card. A dare and a deep question look exactly the same to a React component.&lt;/p&gt;

&lt;p&gt;So I built &lt;a href="https://hereweask.com" rel="noopener noreferrer"&gt;Here We Ask&lt;/a&gt; on top of the same foundation. Same card flip animation, same deck navigation, same offline-first architecture. Completely different product.&lt;/p&gt;

&lt;p&gt;Here's what I learned shipping two products from one codebase, and why I think more indie builders should try this.&lt;/p&gt;

&lt;h2&gt;
  
  
  The shared layer
&lt;/h2&gt;

&lt;p&gt;Both products are React SPAs built with Lovable (a vibe-coding tool I use for rapid prototyping). They share roughly 70% of their component tree:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Card component&lt;/strong&gt; with flip animation and swipe gestures&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deck selector&lt;/strong&gt; with category filtering&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Game modes&lt;/strong&gt; (Hot Seat, Timed, Classic)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PWA service worker&lt;/strong&gt; for offline play&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Daily content&lt;/strong&gt; rotation logic&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Premium gate&lt;/strong&gt; and payment flow ($1.99/mo on both)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The key insight: if you build your components around &lt;em&gt;behavior&lt;/em&gt; rather than &lt;em&gt;content&lt;/em&gt;, reuse becomes trivial. A &lt;code&gt;&amp;lt;GameCard&amp;gt;&lt;/code&gt; doesn't need to know if it's holding "I dare you to sing your last text out loud" or "What's a belief you held strongly five years ago that you've since changed?" It just renders text, handles swipe, tracks progress.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where they diverge
&lt;/h2&gt;

&lt;p&gt;The interesting part isn't the shared code. It's where the products &lt;em&gt;have&lt;/em&gt; to be different.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Party Challenges&lt;/strong&gt; is loud. Bright colors, bold typography, animations that feel like confetti. The "Date Night" deck here means flirty dares and physical challenges. The copy says things like "no boring moments." The whole vibe is Friday night energy.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Here We Ask&lt;/strong&gt; is calm. Softer palette, more whitespace, slower transitions. The "Date Night" deck here means vulnerability prompts and relationship check-ins. The copy talks about "meaningful connections." Sunday morning energy.&lt;/p&gt;

&lt;p&gt;Same component renders both. The theming layer does all the heavy lifting. I use a config object per product that controls:&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;// Simplified example&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;productConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;party&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;calm&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;cardAnimation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bounce&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fade&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;deckOrder&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[...],&lt;/span&gt;
  &lt;span class="na"&gt;premiumDecks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[...],&lt;/span&gt;
  &lt;span class="na"&gt;dailyContentPool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;challenges&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;questions&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;copyVariant&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;energetic&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;reflective&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One config swap, entirely different product feel.&lt;/p&gt;

&lt;h2&gt;
  
  
  What "Date Night" taught me about product positioning
&lt;/h2&gt;

&lt;p&gt;This is my favorite example. Both products have a deck called "Date Night." In Party Challenges, it's cards like "feed each other blindfolded" and "recreate your first date but everything goes wrong on purpose." In Here We Ask, it's "what's something you've never told me but always wanted to?" and "where do you see us in five years, honestly?"&lt;/p&gt;

&lt;p&gt;Same feature name. Same UI. Completely different emotional register.&lt;/p&gt;

&lt;p&gt;When I show both to people, they often don't realize it's the same codebase. That's the goal. The tech should be invisible. Users don't care about your architecture. They care about whether the product fits their moment.&lt;/p&gt;

&lt;h2&gt;
  
  
  The portfolio math
&lt;/h2&gt;

&lt;p&gt;Here's where it gets practical. Building the second product took maybe 30% of the effort of the first one. Most of that was content creation (writing 1,000+ questions vs. 1,000+ challenges) and the theming/copy work. The infrastructure, deployment, PWA setup, analytics, SEO patterns... all reused.&lt;/p&gt;

&lt;p&gt;I run a portfolio of products at &lt;a href="https://inithouse.com" rel="noopener noreferrer"&gt;Inithouse&lt;/a&gt;. The idea is simple: build small, focused tools and see what sticks. Some of them are AI-powered, like &lt;a href="https://magicalsong.com" rel="noopener noreferrer"&gt;Magical Song&lt;/a&gt; (personalized AI songs) or &lt;a href="https://petimagination.com" rel="noopener noreferrer"&gt;Pet Imagination&lt;/a&gt; (AI pet portraits). Others are content-driven, like &lt;a href="https://vibecoderi.cz" rel="noopener noreferrer"&gt;Vibe Coderi&lt;/a&gt; (a Czech vibecoding portal). Each one is an experiment.&lt;/p&gt;

&lt;p&gt;The card games taught me that portfolio building isn't just about launching separate products. It's about finding the shared bones across them. Every component you build well once is a component you never build again.&lt;/p&gt;

&lt;h2&gt;
  
  
  Practical takeaways for builders
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Start with behavior, not content.&lt;/strong&gt; If your component tree is organized around what &lt;em&gt;happens&lt;/em&gt; (flip, swipe, filter, gate) rather than what &lt;em&gt;says&lt;/em&gt; (dare, question, quote), you can reskin faster than you think.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Theme configs beat feature flags.&lt;/strong&gt; I tried feature flags first. It got messy fast. A dedicated config object per product is cleaner and easier to reason about. You always know which product you're looking at in the code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Content is the real work.&lt;/strong&gt; The tech took weeks. The content took months. Writing 1,000 good party dares is a different skill than writing 1,000 good conversation starters. Don't underestimate this part.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ship the second product before perfecting the first.&lt;/strong&gt; I could have spent another month polishing Party Challenges. Instead I shipped Here We Ask while the architecture was fresh in my head. Both products improved faster because fixes in one often applied to both.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's next
&lt;/h2&gt;

&lt;p&gt;I'm looking at whether this pattern extends beyond card games. &lt;a href="https://verdictbuddy.com" rel="noopener noreferrer"&gt;Verdict Buddy&lt;/a&gt; (AI conflict resolution) and &lt;a href="https://watchingagents.com" rel="noopener noreferrer"&gt;Watching Agents&lt;/a&gt; (AI prediction platform) use completely different architectures, but some lower-level patterns (SEO hooks, analytics setup, PWA configs) keep showing up. There might be a shared toolkit hiding in there.&lt;/p&gt;

&lt;p&gt;If you're building multiple products, or even thinking about it, I'd love to hear how you handle code reuse across them. Drop a comment or find me on &lt;a href="https://dev.to/jakub_inithouse"&gt;Dev.to&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I'm Jakub, founder of &lt;a href="https://inithouse.com" rel="noopener noreferrer"&gt;Inithouse&lt;/a&gt;. We build a portfolio of small AI and web products, testing ideas fast and seeing what resonates. Party Challenges and Here We Ask are two of about a dozen experiments currently running.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>react</category>
      <category>buildinpublic</category>
      <category>indiehackers</category>
    </item>
    <item>
      <title>Voice-to-Schema: Turning "Track My Invoices" Into a Real Table</title>
      <dc:creator>Jakub</dc:creator>
      <pubDate>Sun, 05 Apr 2026 07:08:16 +0000</pubDate>
      <link>https://dev.to/jakub_inithouse/voice-to-schema-turning-track-my-invoices-into-a-real-table-1b4a</link>
      <guid>https://dev.to/jakub_inithouse/voice-to-schema-turning-track-my-invoices-into-a-real-table-1b4a</guid>
      <description>&lt;p&gt;We rebuilt our NLP pipeline three times before it actually worked. Here's what went wrong each time and what we learned about the gap between what people say and what they mean.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem Nobody Talks About
&lt;/h2&gt;

&lt;p&gt;When we started building &lt;a href="https://voicetables.com" rel="noopener noreferrer"&gt;VoiceTables&lt;/a&gt;, we had a simple hypothesis: let people describe what they need, and generate a structured table from that description. User says "I need to track my invoices," system creates an invoices table with sensible columns. Easy, right?&lt;/p&gt;

&lt;p&gt;Turns out spoken language and structured data are almost completely different things. The first version took about two weeks to build and maybe 20 minutes to realize it was broken.&lt;/p&gt;

&lt;h2&gt;
  
  
  Attempt 1: Naive Prompt Engineering
&lt;/h2&gt;

&lt;p&gt;The first pipeline was embarrassingly simple. Take the transcript, send it to an LLM with a system prompt like "extract a table schema from this description," parse the JSON response.&lt;/p&gt;

&lt;p&gt;It worked perfectly for clean inputs. "Create a table with columns: client name, invoice amount, due date, status" produced exactly what you'd expect.&lt;/p&gt;

&lt;p&gt;Nobody talks like that.&lt;/p&gt;

&lt;p&gt;Real inputs looked like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"uh, I need something for... like tracking stuff, you know, for my clients"&lt;/li&gt;
&lt;li&gt;"make me a table, invoice things, the usual"&lt;/li&gt;
&lt;li&gt;"so I've got these freelance gigs and I keep losing track of who paid me"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The third one is actually the most useful input. It tells you what the user needs (payment tracking), who they're working with (freelance clients), and what the pain point is (losing track of payments). But our v1 pipeline couldn't extract any of that. It would either hallucinate random columns or return a generic two-column table that helped nobody.&lt;/p&gt;

&lt;h2&gt;
  
  
  Attempt 2: Two-Stage Extraction
&lt;/h2&gt;

&lt;p&gt;For the second version, we split the pipeline into two steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Intent extraction&lt;/strong&gt;: figure out what domain the user is working in (invoicing, project management, inventory, etc.) and what they actually want to track&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Schema generation&lt;/strong&gt;: given the intent, generate an appropriate table structure&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This was better. The intent layer caught things like "freelance gigs" mapping to freelancer invoicing, which gave the schema generator much better context.&lt;/p&gt;

&lt;p&gt;But we hit a new problem: ambiguity in spoken language vs. typed input.&lt;/p&gt;

&lt;p&gt;When someone types "client name," they mean a text column called "Client Name." When someone says "client name," they might mean:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The name of their client (text column)&lt;/li&gt;
&lt;li&gt;A reference to an existing clients table (foreign key)&lt;/li&gt;
&lt;li&gt;"the client" as filler while they think about what to say next&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Spoken language has pauses, restarts, filler words, self-corrections. "I need a table for... no wait... like, a list of my clients and their... the projects, and how much each one... you know, the budget for each project."&lt;/p&gt;

&lt;p&gt;That sentence contains at least three potential columns (client, project, budget) and a relationship (clients have projects). Our v2 would sometimes generate six columns because it treated "no wait" and "you know" as potential data points.&lt;/p&gt;

&lt;h2&gt;
  
  
  Attempt 3: What Actually Works
&lt;/h2&gt;

&lt;p&gt;The third rewrite introduced something we should have done from the start: a confidence-scored extraction with a clarification loop.&lt;/p&gt;

&lt;p&gt;The pipeline now works like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Transcript cleanup&lt;/strong&gt;: strip filler words, normalize speech patterns, handle self-corrections ("no wait" means "ignore what I just said")&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Entity extraction with confidence&lt;/strong&gt;: each potential column gets a confidence score. "Budget" from the sentence above gets 0.9. "Projects" gets 0.85. "The" gets filtered out entirely.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Schema proposal&lt;/strong&gt;: generate the table structure, but only include columns above a confidence threshold&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gap detection&lt;/strong&gt;: identify what's probably missing. If someone mentions invoices but no date column, that's a gap worth asking about.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The key insight was that we don't need to get it perfect on the first pass. We just need to get it good enough that the user can see what we understood and correct us quickly. "I see you want to track invoices for freelance clients. I've set up columns for Client Name, Project, Amount, and Status. Want me to add anything else?"&lt;/p&gt;

&lt;p&gt;That conversational correction loop is way more natural than trying to parse everything perfectly from a single voice input.&lt;/p&gt;

&lt;h2&gt;
  
  
  Failure Modes We Still Deal With
&lt;/h2&gt;

&lt;p&gt;It's not all solved. Some recurring edge cases:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Language mixing.&lt;/strong&gt; We have users who switch between languages mid-sentence. Our pipeline handles Czech and English separately, but code-switching mid-sentence still trips it up sometimes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Implicit schemas.&lt;/strong&gt; "Make it like a CRM" assumes shared knowledge about what a CRM table looks like. We built a library of common schema templates (CRM, invoice tracker, project board, inventory) that the intent layer can match against. It covers maybe 70% of cases.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Overspecification.&lt;/strong&gt; Some users describe every single column in detail, including data types and validation rules, all in one breath. The pipeline gets confused because it's optimized for the messy, underspecified case. We're still tuning the balance.&lt;/p&gt;

&lt;h2&gt;
  
  
  Numbers
&lt;/h2&gt;

&lt;p&gt;After the third rewrite:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;First-pass schema accuracy went from ~40% (v1) to ~78% (v3) measured by "did the user accept the generated schema without modifications"&lt;/li&gt;
&lt;li&gt;Average time from voice input to usable table dropped from asking 3+ clarification questions to usually 0-1&lt;/li&gt;
&lt;li&gt;The cleanup step alone (stripping fillers, handling corrections) improved extraction accuracy by about 15 percentage points&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What I'd Do Differently
&lt;/h2&gt;

&lt;p&gt;If I were starting from scratch, I'd skip the "parse everything from one input" approach entirely. Start with the clarification loop from day one. People are surprisingly patient with "let me make sure I understood you" if the follow-up question is smart.&lt;/p&gt;

&lt;p&gt;Also, collect real voice inputs as early as possible. We spent two weeks optimizing for typed test inputs that looked nothing like actual speech. The gap between "create a table with columns name, email, phone" and "uh yeah so I need like a contacts thing" is massive, and you won't close it without real data.&lt;/p&gt;




&lt;p&gt;I'm building &lt;a href="https://voicetables.com" rel="noopener noreferrer"&gt;VoiceTables&lt;/a&gt; as part of the &lt;a href="https://inithouse.com" rel="noopener noreferrer"&gt;Inithouse&lt;/a&gt; portfolio, where we ship AI-powered tools across different verticals. Some of our other projects include &lt;a href="https://berecommended.com" rel="noopener noreferrer"&gt;Be Recommended&lt;/a&gt; (check if AI chatbots recommend your brand), &lt;a href="https://watchingagents.com" rel="noopener noreferrer"&gt;Watching Agents&lt;/a&gt; (AI prediction platform), and &lt;a href="https://auditvibecoding.com" rel="noopener noreferrer"&gt;Audit Vibecoding&lt;/a&gt; (automated audits for AI-generated code). If you're building voice-first interfaces or have war stories about NLP pipelines, I'd love to hear about it in the comments.&lt;/p&gt;

</description>
      <category>nlp</category>
      <category>ai</category>
      <category>webdev</category>
      <category>buildinpublic</category>
    </item>
    <item>
      <title>We Shipped an AI Song Generator. The Hardest Part Wasn't the AI.</title>
      <dc:creator>Jakub</dc:creator>
      <pubDate>Sat, 04 Apr 2026 19:07:59 +0000</pubDate>
      <link>https://dev.to/jakub_inithouse/we-shipped-an-ai-song-generator-the-hardest-part-wasnt-the-ai-4e8a</link>
      <guid>https://dev.to/jakub_inithouse/we-shipped-an-ai-song-generator-the-hardest-part-wasnt-the-ai-4e8a</guid>
      <description>&lt;p&gt;We launched &lt;a href="https://magicalsong.com" rel="noopener noreferrer"&gt;Magical Song&lt;/a&gt; a few weeks ago. It's an AI song generator where you describe a story, pick a genre, and get a studio-quality track with real vocals in under two minutes.&lt;/p&gt;

&lt;p&gt;The AI generation part? That was the easy part. Seriously.&lt;/p&gt;

&lt;p&gt;The part that nearly broke us was everything around it. The UX flow, the payment model, and a fundamental misunderstanding about who our user actually is.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three steps sounds simple. It wasn't.
&lt;/h2&gt;

&lt;p&gt;Our flow is: describe your story &amp;gt; pick genre and mood &amp;gt; get your song. Three screens. Should be straightforward, right?&lt;/p&gt;

&lt;p&gt;The first version had a long form. Name of the person, occasion, details, inside jokes, mood preference, tempo, vocal style. We thought more input = better output. Users thought "this is homework" and bounced.&lt;/p&gt;

&lt;p&gt;We cut it down to the bare minimum. One text area for the story. A grid of genre cards. A generate button. Conversion went up immediately.&lt;/p&gt;

&lt;p&gt;The lesson wasn't new but it hit different when you see it in your own data: every extra field is a decision, and every decision is a chance to leave.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stripe one-time payment was a deliberate bet
&lt;/h2&gt;

&lt;p&gt;Most AI tools go subscription. We went with a single payment of $6 per song. No account needed.&lt;/p&gt;

&lt;p&gt;Here's why: our users aren't repeat customers in the traditional sense. Someone ordering a birthday song for their mom isn't coming back next week. They might come back in a year for another occasion, or never. A subscription would feel like a trap for something they need once.&lt;/p&gt;

&lt;p&gt;Stripe Checkout made this dead simple. We create a session, redirect, handle the webhook, unlock the download. No user accounts, no password resets, no subscription management UI. The entire payment flow is maybe 200 lines of code.&lt;/p&gt;

&lt;p&gt;One unexpected side effect: zero refund requests so far. When people pay once for something specific, they know exactly what they're getting. No "I forgot to cancel" frustration.&lt;/p&gt;

&lt;h2&gt;
  
  
  The gift economy thing we didn't expect
&lt;/h2&gt;

&lt;p&gt;We built Magical Song thinking people would make songs for themselves. Playlists, fun experiments, maybe content creators needing custom jingles.&lt;/p&gt;

&lt;p&gt;Wrong.&lt;/p&gt;

&lt;p&gt;Almost every song is a gift. Birthday songs, anniversary tracks, wedding surprises. And then the one that really caught us off guard: our first paying customer ordered a memorial song. Someone used it to create a tribute for a person who passed away.&lt;/p&gt;

&lt;p&gt;This completely changed how we think about the product. We're not building a "fun AI toy." We're building something people use for moments that actually matter to them. That raises the bar on everything: the quality of the output, the reliability of the service, the tone of the landing page.&lt;/p&gt;

&lt;p&gt;When your user is buying a gift, the UX pressure is different. They're not browsing. They're on a mission. They have a deadline (the birthday is tomorrow). They need confidence that the result will be good enough to share. Every friction point isn't just annoying, it's a risk that the gift falls through.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd do differently
&lt;/h2&gt;

&lt;p&gt;If I were starting over, I'd user-test the payment moment earlier. We spent weeks tuning the AI prompt engineering and almost no time watching people go through checkout. Turns out the moment someone decides to pay $6 for an AI-generated song is fragile. They need social proof right there, not on the landing page.&lt;/p&gt;

&lt;p&gt;I'd also build the sharing flow first. Right now we generate a shareable link, but it's an afterthought. For a gift product, the "unwrapping" experience should be the hero feature. That's next on our list.&lt;/p&gt;

&lt;h2&gt;
  
  
  The stack, briefly
&lt;/h2&gt;

&lt;p&gt;We built this with &lt;a href="https://lovable.dev" rel="noopener noreferrer"&gt;Lovable&lt;/a&gt; (React + Supabase), Stripe for payments, and a third-party AI music API for generation. The whole thing runs serverless. Total development time from idea to first paying customer was about two weeks.&lt;/p&gt;

&lt;p&gt;If you're building something similar, the boring advice is the right advice: keep the flow short, make the payment invisible, and watch real people use it before you optimize anything else.&lt;/p&gt;

&lt;p&gt;We're part of &lt;a href="https://inithouse.com" rel="noopener noreferrer"&gt;Inithouse&lt;/a&gt;, where we run ~14 MVPs simultaneously trying to find product-market fit. Each one teaches us something. Magical Song taught us that sometimes your users aren't who you think they are, and that's actually a good thing.&lt;/p&gt;

&lt;p&gt;If you want to try it: &lt;a href="https://magicalsong.com" rel="noopener noreferrer"&gt;magicalsong.com&lt;/a&gt;. And if you're curious about auditing your own vibecoded project, we also built &lt;a href="https://auditvibecoding.com" rel="noopener noreferrer"&gt;Audit Vibe Coding&lt;/a&gt; for exactly that.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>startup</category>
      <category>ai</category>
      <category>ux</category>
    </item>
    <item>
      <title>Running 5 International Domains from One Codebase (React SPA + Lovable)</title>
      <dc:creator>Jakub</dc:creator>
      <pubDate>Fri, 03 Apr 2026 05:30:25 +0000</pubDate>
      <link>https://dev.to/jakub_inithouse/running-5-international-domains-from-one-codebase-react-spa-lovable-43cn</link>
      <guid>https://dev.to/jakub_inithouse/running-5-international-domains-from-one-codebase-react-spa-lovable-43cn</guid>
      <description>&lt;p&gt;We run an AI photo animation product across 5 country domains, all from a single React codebase. No monorepo, no microservices, no framework gymnastics. Just one Lovable project serving zivafotka.cz, zivafotka.sk, zywafotka.pl, alivephoto.online, and lebendigfoto.de.&lt;/p&gt;

&lt;p&gt;Here's how it works and what we learned making it happen.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem
&lt;/h2&gt;

&lt;p&gt;We built &lt;a href="https://zivafotka.cz" rel="noopener noreferrer"&gt;Ziva Fotka&lt;/a&gt; as a Czech product. Upload a photo, AI animates it into a short video with natural movement. Simple concept, works well. Then we wanted to go international.&lt;/p&gt;

&lt;p&gt;The naive approach would be one codebase per country. Five repos, five deploys, five sets of bugs to fix. That's a maintenance nightmare for a small team. We needed one codebase that adapts to whichever domain it's running on.&lt;/p&gt;

&lt;h2&gt;
  
  
  Domain-based locale detection
&lt;/h2&gt;

&lt;p&gt;The key insight: the domain IS the locale selector. No language dropdown, no cookies, no URL prefixes like &lt;code&gt;/en/&lt;/code&gt; or &lt;code&gt;/de/&lt;/code&gt;. If you're on zivafotka.cz, you get Czech. If you're on lebendigfoto.de, you get German. Clean and obvious.&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;// Simplified locale detection&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getLocale&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;Locale&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;hostname&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hostname&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="nx"&gt;hostname&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;zivafotka.cz&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cs&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="nx"&gt;hostname&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;zivafotka.sk&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sk&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="nx"&gt;hostname&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;zywafotka.pl&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pl&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="nx"&gt;hostname&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lebendigfoto.de&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;de&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;en&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// alivephoto.online and fallback&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This runs once on app init and feeds into a React context that every component can consume. No runtime overhead, no flicker, no hydration mismatch.&lt;/p&gt;

&lt;h2&gt;
  
  
  Routing: same paths, different languages
&lt;/h2&gt;

&lt;p&gt;Each domain has its own URL structure that makes sense in that language:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CZ: &lt;code&gt;/srovnani/ziva-fotka-vs-myheritage&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;SK: &lt;code&gt;/porovnanie/ziva-fotka-vs-myheritage&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;PL: &lt;code&gt;/porownanie/zywa-fotka-vs-myheritage&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;DE: &lt;code&gt;/vergleich/lebendiges-foto-vs-myheritage&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;EN: &lt;code&gt;/comparison/alive-photo-vs-myheritage&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Routes are defined per locale in a central config. The React Router setup maps locale-specific paths to shared page components. The component receives locale as a prop and renders the right content.&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;// Route config per locale&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;routes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;cs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;comparison&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/srovnani&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;pricing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/cenik&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;sk&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;comparison&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/porovnanie&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;pricing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/cennik&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;pl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;comparison&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/porownanie&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;pricing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/cennik&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;de&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;comparison&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/vergleich&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;pricing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/preise&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;en&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;comparison&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/comparison&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;pricing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/pricing&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Sitemaps: one per domain
&lt;/h2&gt;

&lt;p&gt;This was the trickiest SEO piece. Each domain needs its own sitemap with its own URLs. We generate five separate sitemap files:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;sitemap-cz.xml&lt;/code&gt; for zivafotka.cz&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;sitemap-sk.xml&lt;/code&gt; for zivafotka.sk&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;sitemap-pl.xml&lt;/code&gt; for zywafotka.pl&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;sitemap-en.xml&lt;/code&gt; for alivephoto.online&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;sitemap-de.xml&lt;/code&gt; for lebendigfoto.de&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each sitemap only contains URLs for that specific domain. A &lt;code&gt;sitemap.xml&lt;/code&gt; index file references all five. The &lt;code&gt;robots.txt&lt;/code&gt; points to the index sitemap.&lt;/p&gt;

&lt;p&gt;Critical detail: every URL in every sitemap includes hreflang annotations pointing to the equivalent page on all other domains. This tells Google "these five pages are the same content in different languages."&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;url&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;loc&amp;gt;&lt;/span&gt;https://zivafotka.cz/srovnani/ziva-fotka-vs-myheritage&lt;span class="nt"&gt;&amp;lt;/loc&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;xhtml:link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"alternate"&lt;/span&gt; &lt;span class="na"&gt;hreflang=&lt;/span&gt;&lt;span class="s"&gt;"cs"&lt;/span&gt;
    &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://zivafotka.cz/srovnani/ziva-fotka-vs-myheritage"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;xhtml:link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"alternate"&lt;/span&gt; &lt;span class="na"&gt;hreflang=&lt;/span&gt;&lt;span class="s"&gt;"sk"&lt;/span&gt;
    &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://zivafotka.sk/porovnanie/ziva-fotka-vs-myheritage"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;xhtml:link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"alternate"&lt;/span&gt; &lt;span class="na"&gt;hreflang=&lt;/span&gt;&lt;span class="s"&gt;"pl"&lt;/span&gt;
    &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://zywafotka.pl/porownanie/zywa-fotka-vs-myheritage"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;xhtml:link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"alternate"&lt;/span&gt; &lt;span class="na"&gt;hreflang=&lt;/span&gt;&lt;span class="s"&gt;"de"&lt;/span&gt;
    &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://lebendigfoto.de/vergleich/lebendiges-foto-vs-myheritage"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;xhtml:link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"alternate"&lt;/span&gt; &lt;span class="na"&gt;hreflang=&lt;/span&gt;&lt;span class="s"&gt;"en"&lt;/span&gt;
    &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://alivephoto.online/comparison/alive-photo-vs-myheritage"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/url&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  SEO: dynamic meta per domain
&lt;/h2&gt;

&lt;p&gt;Every page uses a custom &lt;code&gt;useSEO&lt;/code&gt; hook that sets the right meta tags based on the current locale and domain:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Title and description in the local language&lt;/li&gt;
&lt;li&gt;Canonical URL pointing to the current domain (not a "master" domain)&lt;/li&gt;
&lt;li&gt;Open Graph tags with locale-specific content&lt;/li&gt;
&lt;li&gt;JSON-LD schema with the correct publisher for each domain&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is where a lot of multi-domain setups go wrong. If your canonical tags all point to the .com domain, Google ignores your country-specific pages. Each domain must be treated as authoritative for its locale.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we got wrong (and fixed)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Wrong brand name per locale.&lt;/strong&gt; We initially used "Ziva Fotka" everywhere. But the Polish site is "Zywa Fotka" and the German one is "Lebendiges Foto." Every reference to the product name needs to be locale-aware, not just the UI copy.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Forgetting footers.&lt;/strong&gt; Comparison page links in the footer were hardcoded to Czech paths. On the Polish domain they led to 404s. Footer navigation has to be locale-routed like everything else.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sitemap submission per property.&lt;/strong&gt; Each domain is a separate property in Google Search Console. You need to submit the domain-specific sitemap to each property individually. We missed alivephoto.online for weeks.&lt;/p&gt;

&lt;h2&gt;
  
  
  The numbers
&lt;/h2&gt;

&lt;p&gt;Running five domains instead of one has a real impact on discoverability. Czech users searching "ozivit fotku" land on zivafotka.cz. Polish users searching "ozywic zdjecie" land on zywafotka.pl. Same product, but each market finds it through natural search in their own language.&lt;/p&gt;

&lt;p&gt;Our CTR in Google Ads runs 13-16% across CZ and SK domains, significantly above typical display benchmarks. The domain matching the user's language builds immediate trust.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Lovable makes this work
&lt;/h2&gt;

&lt;p&gt;We built everything in &lt;a href="https://lovable.dev" rel="noopener noreferrer"&gt;Lovable&lt;/a&gt;, which gives us a React SPA with Supabase backend. The single-project architecture means one deploy covers all five domains. DNS and domain routing handles the rest.&lt;/p&gt;

&lt;p&gt;If you're building with Lovable and considering multiple markets, this pattern scales well. The key principles: detect locale from domain (not URL), generate per-domain sitemaps with hreflang cross-references, and make every piece of content locale-aware, including things you'd never think of like footer links and schema markup.&lt;/p&gt;

&lt;p&gt;The codebase for all five domains of &lt;a href="https://zivafotka.cz" rel="noopener noreferrer"&gt;Ziva Fotka&lt;/a&gt; / &lt;a href="https://alivephoto.online" rel="noopener noreferrer"&gt;Alive Photo&lt;/a&gt; lives in a single Lovable project. Check out the live sites to see it in action.&lt;/p&gt;

&lt;p&gt;We're building more products at &lt;a href="https://inithouse.com" rel="noopener noreferrer"&gt;Inithouse&lt;/a&gt;, each with its own scaling challenges. &lt;a href="https://berecommended.com" rel="noopener noreferrer"&gt;Be Recommended&lt;/a&gt; checks if AI assistants recommend your business. &lt;a href="https://watchingagents.com" rel="noopener noreferrer"&gt;Watching Agents&lt;/a&gt; runs AI prediction agents. Different problems, same build-fast philosophy.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>react</category>
      <category>seo</category>
      <category>internationalization</category>
    </item>
    <item>
      <title>Who Audits the AI-Generated Code? We Built an AI to Do It</title>
      <dc:creator>Jakub</dc:creator>
      <pubDate>Thu, 02 Apr 2026 19:51:05 +0000</pubDate>
      <link>https://dev.to/jakub_inithouse/who-audits-the-ai-generated-code-we-built-an-ai-to-do-it-1ni</link>
      <guid>https://dev.to/jakub_inithouse/who-audits-the-ai-generated-code-we-built-an-ai-to-do-it-1ni</guid>
      <description>&lt;p&gt;Vibe coding is everywhere. Cursor, Lovable, Bolt, v0, Replit Agent... the tools keep shipping and the code keeps flowing. But here's the thing nobody wants to talk about: most of that code has never been reviewed by anyone.&lt;/p&gt;

&lt;p&gt;Not by a human. Not by a linter. Not by anything.&lt;/p&gt;

&lt;p&gt;We found out the hard way. After shipping a dozen products built almost entirely with AI tools, we started noticing patterns. Security headers missing. Lighthouse scores in the 40s. Meta tags that made zero sense. Accessibility? Forget about it.&lt;/p&gt;

&lt;p&gt;So we asked ourselves: if AI can write the code, can another AI catch what the first one missed?&lt;/p&gt;

&lt;h2&gt;
  
  
  The Meta Problem
&lt;/h2&gt;

&lt;p&gt;There's something almost funny about it. You prompt an AI to build your app. It generates thousands of lines of code in minutes. You deploy it because it works in the preview. And then you wonder why Google isn't indexing it, why your Core Web Vitals are red, and why someone on Reddit found an open API endpoint.&lt;/p&gt;

&lt;p&gt;The AI that wrote the code optimized for one thing: making it work. It didn't think about your robots.txt. It didn't check if your images have alt text. It didn't verify that your authentication flow actually prevents session hijacking.&lt;/p&gt;

&lt;p&gt;This is the gap we set out to close with &lt;a href="https://auditvibecoding.com" rel="noopener noreferrer"&gt;Audit Vibecoding&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What We Actually Check
&lt;/h2&gt;

&lt;p&gt;The audit runs dozens of automated checks across five categories:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Security&lt;/strong&gt; - Headers, CSP policies, exposed endpoints, authentication patterns, dependency vulnerabilities. The stuff that keeps you up at night (or should).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SEO&lt;/strong&gt; - Meta tags, canonical URLs, structured data, sitemap validity, robots.txt, Open Graph tags. Most AI-generated sites ship with generic or completely broken SEO.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Performance&lt;/strong&gt; - Core Web Vitals, bundle size, image optimization, lazy loading, render-blocking resources. AI loves to import entire libraries when you need one function.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Accessibility&lt;/strong&gt; - ARIA labels, color contrast, keyboard navigation, screen reader compatibility, heading hierarchy. This is where AI-generated code fails the hardest. Almost every project we've audited scores below 50% on accessibility.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Code Quality&lt;/strong&gt; - TypeScript strictness, error handling, unused imports, console.logs left in production, hardcoded values. The kind of stuff a senior dev would catch in code review but your AI pair programmer doesn't care about.&lt;/p&gt;

&lt;p&gt;Each check produces a pass/fail/warning result with a specific recommendation. You get an overall Audit Score from 0 to 100. The average across all projects we've audited? 31. Production-ready is 80+.&lt;/p&gt;

&lt;p&gt;Let that sink in. The average vibecoded project scores 31 out of 100.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Pipeline
&lt;/h2&gt;

&lt;p&gt;You give us a URL. That's it. No account, no setup, no GitHub integration needed.&lt;/p&gt;

&lt;p&gt;Behind the scenes, the audit crawls your deployed site and analyzes what it finds. It's looking at your actual production output, not your source code. This matters because a lot of problems only show up after build and deploy. Your local dev server might work fine while your production site is leaking environment variables.&lt;/p&gt;

&lt;p&gt;Within 24 hours you get a full report: every check with its result, a prioritized action plan (what to fix first), and an overall score. The whole thing costs between $4 and $9. Less than a coffee and a sandwich.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Not Just Use Lighthouse?
&lt;/h2&gt;

&lt;p&gt;Lighthouse is great. We use it ourselves. But it only covers performance and some accessibility. It doesn't check your security headers. It doesn't validate your structured data. It doesn't tell you that your sitemap returns a 404 or that your canonical URL points to localhost.&lt;/p&gt;

&lt;p&gt;We built this because we needed something that covers the full surface area of a shipped product, not just the parts that Chrome DevTools can see.&lt;/p&gt;

&lt;h2&gt;
  
  
  What We've Learned From Auditing Real Projects
&lt;/h2&gt;

&lt;p&gt;After running audits on projects built with Lovable, Cursor, Bolt, and others, a few patterns keep showing up:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Security is always the worst category.&lt;/strong&gt; AI-generated code almost never includes proper security headers. Content-Security-Policy? Missing. X-Frame-Options? Missing. Rate limiting? What's that?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SEO is broken in predictable ways.&lt;/strong&gt; Missing meta descriptions, duplicate title tags across pages, no sitemap, robots.txt blocking everything. The AI generates working pages but doesn't think about discoverability.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Accessibility gets ignored entirely.&lt;/strong&gt; This is the one that bothers me most. Screen reader support, keyboard navigation, ARIA labels... AI tools just don't prioritize this unless you explicitly ask. And even then, the implementation is often wrong.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Performance problems come from over-engineering.&lt;/strong&gt; AI loves to add dependencies. A simple landing page ends up importing React, three animation libraries, a state management solution, and a CSS-in-JS framework. The result loads in 8 seconds on mobile.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Uncomfortable Question
&lt;/h2&gt;

&lt;p&gt;"Quis custodiet ipsos custodes?" Who watches the watchmen?&lt;/p&gt;

&lt;p&gt;If AI writes the code and AI audits the code, where does the human fit in? Honestly, the human fits in the same place they always did: making decisions. The audit gives you a prioritized list of what's broken. You decide what to fix, when, and how much it matters for your specific use case.&lt;/p&gt;

&lt;p&gt;A personal blog with a security score of 40? Probably fine. A SaaS handling payment data with that same score? Fix it now.&lt;/p&gt;

&lt;p&gt;The point isn't to replace human judgment. The point is to give you the information you need to make good decisions, especially when the code was written by something that doesn't understand the context of your business.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try It
&lt;/h2&gt;

&lt;p&gt;If you've shipped anything built with AI tools, run an audit. Not because I'm selling you something (though yes, &lt;a href="https://auditvibecoding.com" rel="noopener noreferrer"&gt;auditvibecoding.com&lt;/a&gt; is the product). But because you should know what you shipped.&lt;/p&gt;

&lt;p&gt;The average score is 31. Yours might be better. It might be worse. Either way, you should know.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I'm Jakub, founder of &lt;a href="https://inithouse.com" rel="noopener noreferrer"&gt;Inithouse&lt;/a&gt;. We build AI-powered products and occasionally write about what we learn along the way. This is one of those learnings: ship fast, but know what you shipped.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>security</category>
      <category>programming</category>
    </item>
  </channel>
</rss>
