<?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: nuan xiang</title>
    <description>The latest articles on DEV Community by nuan xiang (@nuan_xiang_8e56577bde2fad).</description>
    <link>https://dev.to/nuan_xiang_8e56577bde2fad</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%2F3948385%2F708743d9-61da-41e4-82f3-ce40b564fb39.jpg</url>
      <title>DEV Community: nuan xiang</title>
      <link>https://dev.to/nuan_xiang_8e56577bde2fad</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/nuan_xiang_8e56577bde2fad"/>
    <language>en</language>
    <item>
      <title>I tried to build a SaaS. I'm shipping tiny libraries instead.</title>
      <dc:creator>nuan xiang</dc:creator>
      <pubDate>Sun, 24 May 2026 00:57:05 +0000</pubDate>
      <link>https://dev.to/nuan_xiang_8e56577bde2fad/i-tried-to-build-a-saas-im-shipping-tiny-libraries-instead-1n05</link>
      <guid>https://dev.to/nuan_xiang_8e56577bde2fad/i-tried-to-build-a-saas-im-shipping-tiny-libraries-instead-1n05</guid>
      <description>&lt;h2&gt;
  
  
  I tried to build a SaaS. I'm shipping tiny libraries instead.
&lt;/h2&gt;

&lt;p&gt;For 7 days I poured energy into ChatProof — a testing framework for AI chat UIs. The problem was real: every team building Claude/GPT wrappers hits the same streaming-render bugs, and existing e2e tools weren't built for non-deterministic LLM output. I had a competitive analysis. I had a PRD. I even had a name.&lt;/p&gt;

&lt;p&gt;Then I admitted something to myself: &lt;strong&gt;product/market work just isn't my strength.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I'm a builder. I love writing TypeScript. I love designing APIs. I love &lt;code&gt;npm test&lt;/code&gt; going green. I do not love writing cold DMs. I do not love figuring out which subreddit my ICPs hang out in. I do not love crafting positioning documents.&lt;/p&gt;

&lt;p&gt;Once I named it, the pivot was obvious.&lt;/p&gt;

&lt;h2&gt;
  
  
  The pivot
&lt;/h2&gt;

&lt;p&gt;Instead of one ambitious SaaS, I'd ship &lt;strong&gt;tiny zero-deps libraries that solve specific Anthropic SDK pain points I personally hit&lt;/strong&gt;. Each one:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&amp;lt; 500 lines of TypeScript&lt;/li&gt;
&lt;li&gt;Zero runtime dependencies&lt;/li&gt;
&lt;li&gt;MIT licensed&lt;/li&gt;
&lt;li&gt;Published to npm&lt;/li&gt;
&lt;li&gt;1–2 weeks of work max&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The goal isn't to build a unicorn. It's to &lt;strong&gt;leave a GitHub trail that demonstrates I can ship real code&lt;/strong&gt;, get organic distribution through npm + search, and let the work compound over months.&lt;/p&gt;

&lt;h2&gt;
  
  
  The 5 days
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Day 1 — &lt;a href="https://github.com/xiangnuans/claude-stream-collector" rel="noopener noreferrer"&gt;&lt;code&gt;claude-stream-collector&lt;/code&gt;&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Consume Anthropic Messages streaming events into a typed &lt;code&gt;CollectedResult&lt;/code&gt;. Handles &lt;code&gt;tool_use&lt;/code&gt; input JSON delta accumulation (the SDK leaves this to you) and merges cache token usage across &lt;code&gt;message_start&lt;/code&gt; + &lt;code&gt;message_delta&lt;/code&gt; (the SDK overwrites). ~120 lines, 6 tests.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Day 3 — &lt;a href="https://github.com/xiangnuans/claude-retry" rel="noopener noreferrer"&gt;&lt;code&gt;claude-retry&lt;/code&gt;&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Smart retry for Anthropic API calls. Respects &lt;code&gt;retry-after&lt;/code&gt; headers (which the SDK's built-in retry doesn't always honor), exponential backoff with jitter, custom retry predicates, &lt;code&gt;AbortSignal&lt;/code&gt; support. ~180 lines, 25 tests.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Day 5 — &lt;a href="https://github.com/xiangnuans/claude-pricing" rel="noopener noreferrer"&gt;&lt;code&gt;claude-pricing&lt;/code&gt;&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Calculate Claude API cost from token usage. Bundled pricing table with smart model-alias resolution (strips date suffixes), batch API discount, session &lt;code&gt;CostTracker&lt;/code&gt; with budget alerts. ~180 lines, 19 tests.&lt;/p&gt;

&lt;p&gt;Three packages, ~480 lines of code, 50 unit tests. All public. All on npm.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'm learning
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Match the work to who you are, not who you think you should be.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Indie Hackers and Twitter are full of advice assuming you're a generalist who loves every part of building a company. Many great engineers aren't, and that's fine. Tiny libraries are a perfectly valid path if you optimize for shipping over selling.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Zero-deps is a feature, not a constraint.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Every dependency is a future maintenance burden, a potential security issue, and a reason for someone to skip your package. For 100–500 line utilities, you almost never need anything but Node's standard lib.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Series narratives compound.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Each package on its own is a small thing. But three packages with shared types (&lt;code&gt;TokenUsage&lt;/code&gt; flows from &lt;code&gt;claude-stream-collector&lt;/code&gt; → &lt;code&gt;claude-pricing&lt;/code&gt; cleanly) form a tiny ecosystem. Each new package strengthens the others.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Build in public ≠ build for engagement.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I posted about each release on X and 即刻. Engagement so far: zero likes, zero replies. Downloads on the first package: 129 in 7 days. &lt;strong&gt;People install packages they discover via search and never engage with the announcement.&lt;/strong&gt; That's fine — the artifacts persist longer than any tweet.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Pivoting hurts less when you preserve learning.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;ChatProof isn't dead. The painpoint research, competitive analysis, and architecture thinking from those 7 days informed every package since. Especially: I knew the streaming pain because I'd done the homework.&lt;/p&gt;

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

&lt;p&gt;More small packages: &lt;code&gt;claude-cache-helper&lt;/code&gt;, &lt;code&gt;claude-tools-typed&lt;/code&gt;, maybe &lt;code&gt;claude-prompt-toolkit&lt;/code&gt;. Same shape — small, focused, zero-deps. I'll keep posting about them but won't measure success in likes.&lt;/p&gt;

&lt;p&gt;If you're stuck on an indie project trying to be a "real founder," maybe try shipping a single utility you'd actually use. You might find you ship four more before you'd have written a single landing page.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Three packages:&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/xiangnuans/claude-stream-collector" rel="noopener noreferrer"&gt;&lt;code&gt;claude-stream-collector&lt;/code&gt;&lt;/a&gt; — typed stream events&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/xiangnuans/claude-retry" rel="noopener noreferrer"&gt;&lt;code&gt;claude-retry&lt;/code&gt;&lt;/a&gt; — smart retry + backoff&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/xiangnuans/claude-pricing" rel="noopener noreferrer"&gt;&lt;code&gt;claude-pricing&lt;/code&gt;&lt;/a&gt; — cost calc + tracker&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Building in public: &lt;a href="https://github.com/xiangnuans/ship-log-2026" rel="noopener noreferrer"&gt;ship-log-2026&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>saas</category>
      <category>startup</category>
      <category>testing</category>
      <category>typescript</category>
    </item>
  </channel>
</rss>
