<?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: Rupesh Sappata</title>
    <description>The latest articles on DEV Community by Rupesh Sappata (@rupesh_sappata_1d51b0ef24).</description>
    <link>https://dev.to/rupesh_sappata_1d51b0ef24</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%2F3825750%2F9d8c2d91-9113-4ac1-842d-18e499ae1dbb.png</url>
      <title>DEV Community: Rupesh Sappata</title>
      <link>https://dev.to/rupesh_sappata_1d51b0ef24</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/rupesh_sappata_1d51b0ef24"/>
    <language>en</language>
    <item>
      <title>I Built a Soulver Clone for Android Using Only Claude — Here's the Stack and the Lessons</title>
      <dc:creator>Rupesh Sappata</dc:creator>
      <pubDate>Mon, 18 May 2026 13:00:00 +0000</pubDate>
      <link>https://dev.to/rupesh_sappata_1d51b0ef24/i-built-a-soulver-clone-for-android-using-only-claude-heres-the-stack-and-the-lessons-46pl</link>
      <guid>https://dev.to/rupesh_sappata_1d51b0ef24/i-built-a-soulver-clone-for-android-using-only-claude-heres-the-stack-and-the-lessons-46pl</guid>
      <description>&lt;h2&gt;
  
  
  I shipped CalcBook — an offline natural-language calculator for Android — by pair-programming with Claude end-to-end. No bootcamp, no team. Here's what worked, what didn't, and the prompts that mattered.
&lt;/h2&gt;

&lt;h2&gt;
  
  
    &lt;div&gt;
    &lt;iframe src="https://www.youtube.com/embed/QSiWIlHrErI"&gt;
    &lt;/iframe&gt;
  &lt;/div&gt;

&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;I built and shipped a Soulver-style calculator app on the Play Store using only Claude (no Copilot, no Cursor, no human collaborators). 4 months. 100% offline. Free. Here's the honest breakdown of how AI pair-programming actually works at scale — and where it breaks.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  The bet
&lt;/h2&gt;

&lt;p&gt;Soulver is the legendary "notepad that computes" on Mac and iOS. For Android, the category didn't exist. I wanted to find out two things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Can a solo dev with &lt;strong&gt;zero Flutter background&lt;/strong&gt; ship a polished offline app to the Play Store?&lt;/li&gt;
&lt;li&gt;Can &lt;strong&gt;Claude alone&lt;/strong&gt; carry that project — design, architecture, debugging, polish — without it falling apart?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The answer to both: &lt;strong&gt;yes, but not the way Twitter makes it sound.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The app is live: 📱 &lt;a href="https://play.google.com/store/apps/details?id=app.pixanddev.calcbook" rel="noopener noreferrer"&gt;CalcBook on Google Play&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This post is the unvarnished version of how it got built.&lt;/p&gt;




&lt;h2&gt;
  
  
  What "built with AI" actually means here
&lt;/h2&gt;

&lt;p&gt;I want to be precise because this phrase has been beaten into a marketing slogan.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What I did:&lt;/strong&gt; I wrote every product decision, every UX choice, every requirement. I tested every screen. I rejected ~30% of what Claude generated on the first pass. I shipped, marketed, and supported the app.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What Claude did:&lt;/strong&gt; Wrote ~95% of the actual Dart code. Designed the recognizer pipeline that powers the parser. Caught bugs I would never have found. Refactored mercilessly when I asked. Wrote inline docs and tests.&lt;/p&gt;

&lt;p&gt;The split that worked: &lt;strong&gt;I owned product. Claude owned implementation.&lt;/strong&gt; Every time I tried to flip that — letting Claude pick the feature, or me writing code without it — quality dropped immediately.&lt;/p&gt;




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

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;Choice&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Framework&lt;/td&gt;
&lt;td&gt;Flutter 3.5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;State&lt;/td&gt;
&lt;td&gt;Riverpod 2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Storage&lt;/td&gt;
&lt;td&gt;Hive (local-first, offline)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Parser&lt;/td&gt;
&lt;td&gt;Custom recognizer pipeline (no petitparser, no regex soup)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cloud sync&lt;/td&gt;
&lt;td&gt;Google Drive &lt;code&gt;appDataFolder&lt;/code&gt; (optional, opt-in)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Analytics&lt;/td&gt;
&lt;td&gt;Firebase Analytics + Remote Config&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Monetization&lt;/td&gt;
&lt;td&gt;AdMob + one-time Pro IAP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IDE&lt;/td&gt;
&lt;td&gt;VS Code&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AI&lt;/td&gt;
&lt;td&gt;Claude (chat + Claude Code CLI)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Zero servers. Zero backend code. The only network calls in the app are Firebase, AdMob, and the user's own Drive — everything else runs on-device.&lt;/p&gt;




&lt;h2&gt;
  
  
  The hardest problem: the parser
&lt;/h2&gt;

&lt;p&gt;Soulver's magic isn't the UI. It's that you can type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;coffee $4.50 and muffin $3.00
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;…and get &lt;code&gt;$7.50&lt;/code&gt; back. A traditional parser errors on &lt;code&gt;coffee&lt;/code&gt;. Soulver doesn't.&lt;/p&gt;

&lt;p&gt;My first three attempts at the parser used a single monolithic grammar. Each one collapsed under its own weight — every new feature broke the previous one. After throwing ~4,000 lines of grammar code in the trash, Claude proposed the approach that actually worked:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Don't write one parser. Write N tiny recognizers, each owning one domain, and run them in priority order. Let unrecognized tokens silently fall through."&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DateRecognizer       (priority 80)
TimeRecognizer       (priority 80)
PercentageRecognizer (priority 70)
CurrencyRecognizer   (priority 65)
UnitRecognizer       (priority 60)
MathRecognizer       (priority 50)
FuzzyScanner         (priority 10 — last resort)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each one implements a single method:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kd"&gt;abstract&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Recognizer&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="n"&gt;priority&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;/// Try to recognize a pattern starting at [startIndex] in [tokens].&lt;/span&gt;
  &lt;span class="c1"&gt;/// Returns null if it doesn't apply — caller skips to the next position.&lt;/span&gt;
  &lt;span class="n"&gt;RecognizerMatch&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;tryRecognize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="kt"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Token&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;startIndex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;DocumentScope&lt;/span&gt; &lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The pipeline walks the tokens. At each position it asks recognizers (high priority first) to claim a window. &lt;strong&gt;Anything not claimed is just skipped.&lt;/strong&gt; That's the entire trick.&lt;/p&gt;

&lt;p&gt;Adding a new domain — say, temperatures — is now isolated work. Write a &lt;code&gt;TemperatureRecognizer&lt;/code&gt;, register it in the pipeline, ship. No existing recognizer changes.&lt;/p&gt;

&lt;p&gt;I'd love to take credit for this design. I can't. Claude proposed it. I evaluated it, accepted it, and steered the implementation. &lt;strong&gt;That's the actual workflow.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Where Claude got it right
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Architecture.&lt;/strong&gt; Recognizer pipeline. Riverpod state shape. Hive serialization layout. All Claude.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Boilerplate.&lt;/strong&gt; Settings persistence, syntax highlighting, theme switching. I described what I wanted; it produced clean code on the first try.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Refactors.&lt;/strong&gt; "Rename this. Extract this. Split this file." Claude does this with surgical precision. Far better than any human reviewer because it has no ego about the existing code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Inline documentation.&lt;/strong&gt; Every comment in the codebase is Claude. Reads like a senior engineer wrote it.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Where Claude got it wrong (and how I caught it)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Over-engineering.&lt;/strong&gt; Default Claude wants to add abstractions, base classes, factories, and "future-proofing" everywhere. &lt;strong&gt;You have to fight this constantly.&lt;/strong&gt; My most-used prompt fragment became: &lt;em&gt;"No backward-compat shims. No defensive null checks for impossible cases. Just write the change."&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Confident hallucinations on Flutter APIs.&lt;/strong&gt; Claude is occasionally certain about a &lt;code&gt;TextField&lt;/code&gt; parameter that doesn't exist. Always run the build before trusting "looks good."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;UX taste.&lt;/strong&gt; Claude can implement a UI spec flawlessly but cannot tell you the spec is bad. Every padding, every color, every animation curve needs human eyes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Don't let it choose features.&lt;/strong&gt; "What should we build next?" produces generic answers. Product decisions stay with you.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Prompts that mattered
&lt;/h2&gt;

&lt;p&gt;The four prompt patterns I used most:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. "Here's the bug. Here's the file. Don't refactor — just fix the bug."
2. "Add feature X. Don't add error handling for cases that can't happen.
    Don't add base classes. Don't introduce abstractions."
3. "Audit this file for dead code, unused params, redundant null checks.
    Remove without changing behavior."
4. "Read these three files. Plan a change. Don't write code yet —
    Describe the diff you'd make and why."
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The fourth one is the multiplier. Forcing Claude into "plan first" mode catches 80% of the over-engineering before any code is written.&lt;/p&gt;




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

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Start with the AI workflow on day one.&lt;/strong&gt; I spent the first two weeks trying to "learn Flutter properly" before letting Claude write. Pointless. The fastest way to learn Flutter was to read Claude's code, ask why, and iterate.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Write the test cases as English sentences first.&lt;/strong&gt; &lt;code&gt;"coffee $4.50 and muffin $3.00 → $7.50"&lt;/code&gt;. Every test was authored by me, every test implementation by Claude.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keep one source-of-truth file&lt;/strong&gt; (&lt;code&gt;CLAUDE.md&lt;/code&gt; in the project root) that documents the architecture. Update it whenever an invariant changes. For future Claude sessions, read this and get up to speed instantly.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  The honest takeaway
&lt;/h2&gt;

&lt;p&gt;AI pair-programming is &lt;strong&gt;not "describe the app, get the app."&lt;/strong&gt; It's a senior engineer who never gets tired, never argues, has perfect recall, and sometimes invents APIs that don't exist. You have to lead it. But if you can describe what you want clearly, you can ship things you couldn't before.&lt;/p&gt;

&lt;p&gt;CalcBook is the proof of concept I needed. Whether you use Claude, Cursor, or whatever else, the bottleneck is no longer "can I write the code." It's "do I know what I want?"&lt;/p&gt;




&lt;h2&gt;
  
  
  Try the app
&lt;/h2&gt;

&lt;p&gt;📱 &lt;a href="https://play.google.com/store/apps/details?id=app.pixanddev.calcbook" rel="noopener noreferrer"&gt;CalcBook on Google Play&lt;/a&gt; — free, fully offline.&lt;/p&gt;

&lt;p&gt;I'm shipping more on &lt;a href="https://www.linkedin.com/in/rupesh-sappata-6a328b185/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Builders using AI to ship: what's your prompt-pattern that changed everything? Drop it in the comments.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>flutter</category>
      <category>showdev</category>
      <category>buildinpublic</category>
      <category>ai</category>
    </item>
  </channel>
</rss>
