<?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: 付小花</title>
    <description>The latest articles on DEV Community by 付小花 (@fuxiaohua).</description>
    <link>https://dev.to/fuxiaohua</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3995049%2F8c9ee398-b015-476b-a498-84d597ff06ca.jpg</url>
      <title>DEV Community: 付小花</title>
      <link>https://dev.to/fuxiaohua</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/fuxiaohua"/>
    <language>en</language>
    <item>
      <title>My SaaS bill grew $24 this year and I never approved it — the silent price hike problem</title>
      <dc:creator>付小花</dc:creator>
      <pubDate>Sat, 12 Sep 2026 00:41:26 +0000</pubDate>
      <link>https://dev.to/fuxiaohua/my-saas-bill-grew-24-this-year-and-i-never-approved-it-the-silent-price-hike-problem-gd5</link>
      <guid>https://dev.to/fuxiaohua/my-saas-bill-grew-24-this-year-and-i-never-approved-it-the-silent-price-hike-problem-gd5</guid>
      <description>&lt;p&gt;Last March, GitHub sent me a renewal email. It looked like every other receipt: one line item, a new amount, a "View subscription" button. My GitHub Pro plan was renewing at $6 per month instead of $4.&lt;/p&gt;

&lt;p&gt;That's the entire announcement. No pricing page update, no banner, no "changes to your plan" email with a clear subject line. A one-line diff inside an email formatted exactly like the twelve before it.&lt;/p&gt;

&lt;p&gt;I didn't notice until two months later, on a bank statement. Twelve months of that increase is $24 — trivial alone. But GitHub wasn't the only vendor doing it. Three other services had similar bumps buried the same way, and together they added up to nearly $100 a year I never budgeted.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why vendors bury price increases in renewal emails
&lt;/h2&gt;

&lt;p&gt;Renewal-email increases are not an accident of formatting. They're the lowest-friction way to raise prices: no public pricing page change to explain, no announcement to publish, and a captive audience that has already demonstrated it pays. Most customers, like me, either accept the charge without reading or miss the diff entirely. The few who notice and complain get grandfathered back — the increase only needs to survive contact with the majority who don't.&lt;/p&gt;

&lt;p&gt;I don't think this is evil. It's rational pricing behavior in a market where switching costs are high and attention is scarce. But it changes where the audit burden falls: onto the customer, every single cycle.&lt;/p&gt;

&lt;h2&gt;
  
  
  The manual defense (what I did for years)
&lt;/h2&gt;

&lt;p&gt;After the GitHub surprise, my system was: a spreadsheet of every subscription, a Google Calendar reminder for each renewal date, and a monthly statement review.&lt;/p&gt;

&lt;p&gt;It worked for a while. Then it failed in the most predictable way possible — I snoozed the calendar reminder for a niche video-editing tool I had already decided to cancel, the annual charge went through in July, and I spent three days emailing support to get it back. The spreadsheet said "Cancel". The calendar said "Renewal". Neither system had the authority to act, and I was the single point of failure between them.&lt;/p&gt;

&lt;h2&gt;
  
  
  The structural fix: read the renewal emails like a machine
&lt;/h2&gt;

&lt;p&gt;This is one of the core reasons I built SaaSClerk (saasclerk.app). It connects to your mailbox read-only — OAuth, no email content stored, AES-256 encrypted tokens — and does two things a human consistently fails at:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;It never misses a renewal email.&lt;/strong&gt; Every message matching billing patterns gets parsed for the service, amount, interval, and next renewal date. There is no "busy Tuesday" version of this; the scan runs the same way every time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It reads for change, not just amount.&lt;/strong&gt; Phrases like "will increase", "new price", or "effective" next to a number get flagged. Old and new prices sit side by side, and anything moving more than 5% lands in the review queue automatically.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The average first-week scan surfaces $4,200 in annual spend the account owner didn't know they had. Some of that is forgotten subscriptions; a meaningful slice of it is exactly this — increases nobody approved because nobody saw them.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to do this week
&lt;/h2&gt;

&lt;p&gt;One hour, three steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Search your mailbox for &lt;code&gt;subject:(renewal OR invoice OR receipt)&lt;/code&gt; over the last 12 months, and check every "new amount" against what you thought you were paying.&lt;/li&gt;
&lt;li&gt;Cancel anything you can't remember using in 60 days — before the next renewal date, not after.&lt;/li&gt;
&lt;li&gt;For everything annual, put the renewal date in a system that emails you 14 days ahead whether or not you feel like checking.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Or skip the manual pass and run the read-only scan at saasclerk.app — free for twelve months of history, no credit card. Either way, the point is the same: the price you agreed to a year ago is not necessarily the price you're paying today, and the only person who will catch the difference is you.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>How I built an email-scanning SaaS audit engine with Next.js, Prisma and Neon</title>
      <dc:creator>付小花</dc:creator>
      <pubDate>Sat, 12 Sep 2026 00:34:27 +0000</pubDate>
      <link>https://dev.to/fuxiaohua/how-i-built-an-email-scanning-saas-audit-engine-with-nextjs-prisma-and-neon-5976</link>
      <guid>https://dev.to/fuxiaohua/how-i-built-an-email-scanning-saas-audit-engine-with-nextjs-prisma-and-neon-5976</guid>
      <description>&lt;p&gt;When I audited my own SaaS subscriptions by hand, I found 7 tools I remembered paying for. A scan of my mailbox found 23. The gap between those two numbers is the entire reason SaaSClerk exists, and this post is about how the scanning engine works: the architecture, the trade-offs, and the parts I got wrong first.&lt;/p&gt;

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

&lt;p&gt;An email mailbox contains every receipt, renewal notice, and price change a SaaS vendor ever sent. The challenge is reading thousands of emails efficiently while making strict privacy guarantees — because "paste your Gmail password into my web app" is a non-starter, and so is "let my server read all your mail."&lt;/p&gt;

&lt;p&gt;My first attempt used a traditional email client library with direct IMAP access. Three drawbacks killed it: performance fell apart on large mailboxes, the authentication flows were painful to maintain across providers, and the privacy boundary was blurry — the server could technically see everything.&lt;/p&gt;

&lt;h2&gt;
  
  
  The architecture
&lt;/h2&gt;

&lt;p&gt;The stack I settled on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Next.js&lt;/strong&gt; (TypeScript) for the app and API routes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prisma&lt;/strong&gt; as the ORM&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Neon Postgres&lt;/strong&gt; as the database&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;OAuth read-only&lt;/strong&gt; scopes for Gmail/Outlook — the app can &lt;em&gt;read&lt;/em&gt; messages matching billing patterns and nothing else&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AES-256-GCM&lt;/strong&gt; encryption for every stored token&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The flow: user connects a mailbox via OAuth → the scanner queries only messages that match billing patterns (subjects containing "invoice", "receipt", "subscription", "renewal", plus known billing sender domains) → a lightweight natural-language pass extracts the service name, amount, currency, billing interval, and next renewal date → the result is a structured list the user reviews.&lt;/p&gt;

&lt;p&gt;Nothing outside those matched messages is ever touched. Email content isn't persisted — extraction happens in memory and only the structured subscription record survives.&lt;/p&gt;

&lt;h2&gt;
  
  
  The part I underestimated: renewal emails are where the money hides
&lt;/h2&gt;

&lt;p&gt;The first version only extracted amounts. It worked — the scanner immediately surfaced duplicates and forgotten plans. But the most valuable signal turned out to be &lt;em&gt;change&lt;/em&gt; inside renewal emails.&lt;/p&gt;

&lt;p&gt;Vendors rarely announce price increases on their pricing pages. The increase lives in the renewal notice: "Your plan will renew at $6 per month starting with the next billing cycle." Mine did exactly that — GitHub Pro, $4 to $6, and I caught it two months late on a bank statement.&lt;/p&gt;

&lt;p&gt;So the scanner now parses renewal emails for change signals — phrases like "will increase", "new price", "effective" adjacent to a numeric value. When it detects an increase, the subscription gets a price-change badge with the old and new amounts side by side. If the detected amount differs from the stored one by more than 5%, it goes into the review queue automatically.&lt;/p&gt;

&lt;p&gt;This one feature generates more "how did it know that" reactions than the core inventory does.&lt;/p&gt;

&lt;h2&gt;
  
  
  The reminder engine
&lt;/h2&gt;

&lt;p&gt;Finding waste is half the job; acting before the renewal date is the other half. My own failure mode: I marked a subscription "Cancel" in a spreadsheet, set a Google Calendar reminder, snoozed it, and paid for another year.&lt;/p&gt;

&lt;p&gt;SaaSClerk's reminder is opt-out instead of opt-in: mark an item "Cancel" and the engine schedules an email 14 days before the next billing date, with the service, the price, and — where known — a direct link to the cancellation page. If you change your mind, clicking "Keep" in that email stops the reminders. The closed loop matters: a reminder you have to configure yourself is a reminder you'll eventually ignore.&lt;/p&gt;

&lt;h2&gt;
  
  
  Trade-offs I'd make again (and one I wouldn't)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Server-side scanning over a client-side extension.&lt;/strong&gt; A browser extension would have solved the privacy perception problem differently, but it can't run when the browser is closed, and renewal monitoring needs to run on a schedule. Server-side with read-only scopes was the right call.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Neon over a self-managed Postgres.&lt;/strong&gt; Branching databases made development measurably faster, and the cold-start behavior is fine for this workload.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No IMAP password flows.&lt;/strong&gt; OAuth read-only only. It cost me compatibility with some providers, but it's the difference between "a tool that reads your mail" and "a tool that can only ever read receipts."&lt;/p&gt;

&lt;p&gt;The one I'm still paying for: parsing non-English billing emails. My pattern set started English-only, and early users in other markets immediately hit the gap. Expanding the phrase matching (Spanish, French, German next) without weakening the no-storage guarantee is the current work in progress.&lt;/p&gt;

&lt;h2&gt;
  
  
  Numbers, since build-in-public posts should have them
&lt;/h2&gt;

&lt;p&gt;The scanner surfaced $4,200 in annual spend across my forgotten, duplicated, and quietly-increased subscriptions. The free tier at saasclerk.app scans your last twelve months if you want to run the same audit on your own mailbox — and if you build anything similar, the one lesson I'd pass on: extract &lt;em&gt;changes&lt;/em&gt;, not just &lt;em&gt;amounts&lt;/em&gt;. The amounts are what people know. The changes are what they're losing.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>nextjs</category>
      <category>prisma</category>
      <category>saas</category>
    </item>
    <item>
      <title>I built a beginner-friendly web app for learning financial reports</title>
      <dc:creator>付小花</dc:creator>
      <pubDate>Sun, 21 Jun 2026 08:23:15 +0000</pubDate>
      <link>https://dev.to/fuxiaohua/i-built-a-beginner-friendly-web-app-for-learning-financial-reports-2boa</link>
      <guid>https://dev.to/fuxiaohua/i-built-a-beginner-friendly-web-app-for-learning-financial-reports-2boa</guid>
      <description>&lt;p&gt;I built ReportWise, a small web app that helps beginners learn how to read financial reports.&lt;/p&gt;

&lt;p&gt;Most people do not need to become accountants. They just want to understand what a company report is saying:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Is revenue growing?&lt;/li&gt;
&lt;li&gt;Are margins healthy?&lt;/li&gt;
&lt;li&gt;Is cash flow strong?&lt;/li&gt;
&lt;li&gt;Is the company operating well?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ReportWise breaks this into simple lessons, mini report examples, and progress levels.&lt;/p&gt;

&lt;p&gt;Demo:&lt;br&gt;
&lt;a href="https://project-20260605-162100.vercel.app" rel="noopener noreferrer"&gt;https://project-20260605-162100.vercel.app&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I would love feedback on:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Is the landing page clear enough for beginners?&lt;/li&gt;
&lt;li&gt;What financial report concept should the first lesson explain?&lt;/li&gt;
&lt;li&gt;Would this be useful for students, founders, or beginner investors?&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>webdev</category>
      <category>startup</category>
      <category>productivity</category>
      <category>sideprojects</category>
    </item>
  </channel>
</rss>
