<?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: shaojie gong</title>
    <description>The latest articles on DEV Community by shaojie gong (@shaojie).</description>
    <link>https://dev.to/shaojie</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%2F4015883%2Ff495cde7-b3bb-432b-8da3-ff6aa5217ebe.png</url>
      <title>DEV Community: shaojie gong</title>
      <link>https://dev.to/shaojie</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/shaojie"/>
    <language>en</language>
    <item>
      <title>A Plant Reminder Is Useless If Android Quietly Stops It: What GrowBook Does About That — Implementation Notes From GrowBook</title>
      <dc:creator>shaojie gong</dc:creator>
      <pubDate>Mon, 21 Sep 2026 01:40:31 +0000</pubDate>
      <link>https://dev.to/shaojie/a-plant-reminder-is-useless-if-android-quietly-stops-it-what-growbook-does-about-that--4k1b</link>
      <guid>https://dev.to/shaojie/a-plant-reminder-is-useless-if-android-quietly-stops-it-what-growbook-does-about-that--4k1b</guid>
      <description>&lt;p&gt;I wrote this down because the implementation only makes sense after the plant-care problem is clear.&lt;/p&gt;

&lt;p&gt;A plant reminder has one job: appear when the plant needs attention. Android can make that surprisingly fragile. Notification permission may be off, exact alarms may be restricted, a reboot clears scheduled alarms, and some phone makers are aggressive about background apps.&lt;/p&gt;

&lt;p&gt;What GrowBook does&lt;/p&gt;

&lt;p&gt;GrowBook explains why it asks for notification access before showing the system prompt. It checks the relevant settings, saves schedules locally, and rebuilds reminders after a reboot or app update. When the operating system refuses an exact time, the app needs to say so instead of pretending everything is fine.&lt;/p&gt;

&lt;p&gt;The boundary I kept&lt;/p&gt;

&lt;p&gt;There is still no universal switch that defeats every battery manager on every Android phone. I would rather be clear about that limitation and guide people to the right setting than promise a reminder that may never arrive.&lt;/p&gt;

&lt;p&gt;A reminder that admits it needs one more system setting is better than a silent reminder that never appears.&lt;/p&gt;

&lt;p&gt;GrowBook on Google Play: &lt;a href="https://play.google.com/store/apps/details?id=com.yuntai.growbook" rel="noopener noreferrer"&gt;https://play.google.com/store/apps/details?id=com.yuntai.growbook&lt;/a&gt;&lt;/p&gt;

</description>
      <category>growbook</category>
      <category>notifications</category>
      <category>plantcare</category>
    </item>
    <item>
      <title>The Plant Care Question I Ask Most Often Is Not Complicated: When Did I Last Water This? — Implementation Notes From GrowBook</title>
      <dc:creator>shaojie gong</dc:creator>
      <pubDate>Sun, 20 Sep 2026 02:08:35 +0000</pubDate>
      <link>https://dev.to/shaojie/the-plant-care-question-i-ask-most-often-is-not-complicated-when-did-i-last-water-this--5e95</link>
      <guid>https://dev.to/shaojie/the-plant-care-question-i-ask-most-often-is-not-complicated-when-did-i-last-water-this--5e95</guid>
      <description>&lt;p&gt;The feature looks simple from the screen. The useful part is the rule behind it.&lt;/p&gt;

&lt;p&gt;Most of my plant mistakes do not begin with rare diseases or mysterious nutrients. They begin with a boring question: when did I last water this one?&lt;/p&gt;

&lt;p&gt;What GrowBook does&lt;/p&gt;

&lt;p&gt;Memory is unreliable because watering is a tiny task. I do it while making coffee, moving laundry or answering a message. Three days later, I remember touching the pot but not whether I watered it. A note with a date settles the argument in a few seconds.&lt;/p&gt;

&lt;p&gt;The boundary I kept&lt;/p&gt;

&lt;p&gt;GrowBook keeps care history beside the plant, so I can see the last watering, fertilizing or repotting entry without digging through a general calendar. It is not expert advice. It is a record of what I did, which is often the missing piece.&lt;/p&gt;

&lt;p&gt;The next time a leaf turns yellow, I would rather check a date than debate my own memory.&lt;/p&gt;

&lt;p&gt;GrowBook on Google Play: &lt;a href="https://play.google.com/store/apps/details?id=com.yuntai.growbook" rel="noopener noreferrer"&gt;https://play.google.com/store/apps/details?id=com.yuntai.growbook&lt;/a&gt;&lt;/p&gt;

</description>
      <category>growbook</category>
      <category>plantjournal</category>
      <category>wateringtracker</category>
      <category>houseplants</category>
    </item>
    <item>
      <title>NotebookBloom: 31 installs, 17 opens, 9 successful users — what the funnel changed</title>
      <dc:creator>shaojie gong</dc:creator>
      <pubDate>Fri, 18 Sep 2026 14:13:45 +0000</pubDate>
      <link>https://dev.to/shaojie/notebookbloom-31-installs-17-opens-9-successful-users-what-the-funnel-changed-4kc4</link>
      <guid>https://dev.to/shaojie/notebookbloom-31-installs-17-opens-9-successful-users-what-the-funnel-changed-4kc4</guid>
      <description>&lt;p&gt;My earlier analytics showed 31 extension_installed users, 17 extension_open users, and 9 users with at least one successful action. That changed what I chose to build next.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The biggest issue was not attracting zero users; it was helping users finish.&lt;/li&gt;
&lt;li&gt;AnkiConnect push succeeded 5 times out of 21 starts in that measurement window.&lt;/li&gt;
&lt;li&gt;Citation import and bulk links also failed when prerequisites were unclear.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;These are small-sample observations from the earlier measurement window, not proof of product-market fit and not a claim about 1.1.3 performance.&lt;/p&gt;

&lt;p&gt;The next useful metric is not another install. It is one more person completing the action they came for.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdty2kyd82qixk4jjp6g1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdty2kyd82qixk4jjp6g1.png" alt=" " width="440" height="280"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I built NotebookBloom around this workflow: &lt;a href="https://notebookbloom.com" rel="noopener noreferrer"&gt;https://notebookbloom.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>notebookbloom</category>
      <category>buildinpublic</category>
      <category>productanalytics</category>
      <category>chromeextension</category>
    </item>
    <item>
      <title>Why Watering Every Houseplant on Sunday Is Convenient for You and Rough on the Plants — Implementation Notes From GrowBook</title>
      <dc:creator>shaojie gong</dc:creator>
      <pubDate>Fri, 18 Sep 2026 01:50:43 +0000</pubDate>
      <link>https://dev.to/shaojie/why-watering-every-houseplant-on-sunday-is-convenient-for-you-and-rough-on-the-plants--2gd8</link>
      <guid>https://dev.to/shaojie/why-watering-every-houseplant-on-sunday-is-convenient-for-you-and-rough-on-the-plants--2gd8</guid>
      <description>&lt;p&gt;I ran into this while using GrowBook on my own plants, then had to decide what the app should do.&lt;/p&gt;

&lt;p&gt;A weekly watering day sounds tidy. I used to line up every pot on Sunday, water the lot, and feel unusually responsible for ten minutes. The problem is that a pothos, a fern and a succulent do not care about my calendar.&lt;/p&gt;

&lt;p&gt;What GrowBook does&lt;/p&gt;

&lt;p&gt;A schedule is useful as a prompt to check, not an order to pour water. I look at the soil, the weight of the pot and the plant itself before marking the task complete. Some plants keep a seven-day rhythm. Others change as the room gets warmer or the days get shorter.&lt;/p&gt;

&lt;p&gt;The boundary I kept&lt;/p&gt;

&lt;p&gt;That is why GrowBook stores a separate interval for each plant instead of giving the whole collection one watering day. The reminder says which plant needs attention; the final decision still belongs to the person standing next to the pot.&lt;/p&gt;

&lt;p&gt;Sunday is still on my calendar. It now means “check the plants,” not “pour water into every pot.”&lt;/p&gt;

&lt;p&gt;GrowBook on Google Play: &lt;a href="https://play.google.com/store/apps/details?id=com.yuntai.growbook" rel="noopener noreferrer"&gt;https://play.google.com/store/apps/details?id=com.yuntai.growbook&lt;/a&gt;&lt;/p&gt;

</description>
      <category>growbook</category>
      <category>wateringplants</category>
      <category>houseplantcare</category>
      <category>planttips</category>
    </item>
    <item>
      <title>NotebookBloom: A valid YouTube link can still be a bad NotebookLM source</title>
      <dc:creator>shaojie gong</dc:creator>
      <pubDate>Thu, 17 Sep 2026 13:30:53 +0000</pubDate>
      <link>https://dev.to/shaojie/notebookbloom-a-valid-youtube-link-can-still-be-a-bad-notebooklm-source-1anh</link>
      <guid>https://dev.to/shaojie/notebookbloom-a-valid-youtube-link-can-still-be-a-bad-notebooklm-source-1anh</guid>
      <description>&lt;p&gt;NotebookLM mainly needs usable transcript text. A video can play perfectly and still become a weak source.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Confirm captions exist before importing a full playlist.&lt;/li&gt;
&lt;li&gt;Check whether the spoken language is recognized correctly.&lt;/li&gt;
&lt;li&gt;Test two or three videos with a question whose answer is clearly spoken.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Bulk import can save repetitive pasting. It cannot create a transcript that is not there.&lt;/p&gt;

&lt;p&gt;Test a small sample before adding an entire playlist or channel.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fveopc2l7dw3t2pph6c1r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fveopc2l7dw3t2pph6c1r.png" alt=" " width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I built NotebookBloom around this workflow: &lt;a href="https://notebookbloom.com/blog/notebooklm-bulk-import-folders/" rel="noopener noreferrer"&gt;https://notebookbloom.com/blog/notebooklm-bulk-import-folders/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>notebookbloom</category>
      <category>notebooklm</category>
      <category>youtube</category>
      <category>videolearning</category>
    </item>
    <item>
      <title>GrowBook Is Live on Google Play: A Plant Care App for People Who Forget What They Did Last Week — Implementation Notes From GrowBook</title>
      <dc:creator>shaojie gong</dc:creator>
      <pubDate>Thu, 17 Sep 2026 02:02:46 +0000</pubDate>
      <link>https://dev.to/shaojie/growbook-is-live-on-google-play-a-plant-care-app-for-people-who-forget-what-they-did-last-week--5e13</link>
      <guid>https://dev.to/shaojie/growbook-is-live-on-google-play-a-plant-care-app-for-people-who-forget-what-they-did-last-week--5e13</guid>
      <description>&lt;p&gt;This started as a small user-facing problem and turned into a product decision.&lt;/p&gt;

&lt;p&gt;I built GrowBook because I kept watering every plant on the same day. That was easy for me to remember and terrible for the plants. The succulents stayed wet too long, while the thirstier plants were already drooping by the time I noticed.&lt;/p&gt;

&lt;p&gt;What GrowBook does&lt;/p&gt;

&lt;p&gt;The app gives each plant its own photo, care schedule, journal and reminder. The basic flow works without an account and without a network connection. There is also an on-device plant identifier and a light meter that uses the phone's ambient light sensor when the device has one.&lt;/p&gt;

&lt;p&gt;The boundary I kept&lt;/p&gt;

&lt;p&gt;GrowBook is now live on Google Play in 12 languages. It is still a small independent app, so I am watching the first month closely: where people get stuck, which tools they return to, and whether the paid features are useful enough to earn a subscription.&lt;/p&gt;

&lt;p&gt;If you keep forgetting whether you watered something yesterday or last Tuesday, I would like to hear how you handle it now.&lt;/p&gt;

&lt;p&gt;GrowBook on Google Play: &lt;a href="https://play.google.com/store/apps/details?id=com.yuntai.growbook" rel="noopener noreferrer"&gt;https://play.google.com/store/apps/details?id=com.yuntai.growbook&lt;/a&gt;&lt;/p&gt;

</description>
      <category>growbook</category>
      <category>plantcare</category>
      <category>houseplants</category>
      <category>android</category>
    </item>
    <item>
      <title>NotebookBloom: I remembered the quote, but not which NotebookLM notebook it was in</title>
      <dc:creator>shaojie gong</dc:creator>
      <pubDate>Wed, 16 Sep 2026 13:10:10 +0000</pubDate>
      <link>https://dev.to/shaojie/notebookbloom-i-remembered-the-quote-but-not-which-notebooklm-notebook-it-was-in-4bdk</link>
      <guid>https://dev.to/shaojie/notebookbloom-i-remembered-the-quote-but-not-which-notebooklm-notebook-it-was-in-4bdk</guid>
      <description>&lt;p&gt;NotebookLM works beautifully until the notebook list gets long enough that you remember the idea but not its location.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Good notebook names delay the problem but do not remove it.&lt;/li&gt;
&lt;li&gt;Browser history helps only when you remember when the notebook was opened.&lt;/li&gt;
&lt;li&gt;Searching saved sources, citations, cards, and prompts gives you a path back.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Cross-notebook search is retrieval. It does not pretend to generate one combined answer across every notebook.&lt;/p&gt;

&lt;p&gt;A useful search result should help you reopen the right source, not hide it behind another summary.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fern2a839yoi4jd3s75qk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fern2a839yoi4jd3s75qk.png" alt=" " width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I built NotebookBloom around this workflow: &lt;a href="https://notebookbloom.com/blog/search-across-notebooklm-notebooks/" rel="noopener noreferrer"&gt;https://notebookbloom.com/blog/search-across-notebooklm-notebooks/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>notebookbloom</category>
      <category>notebooklm</category>
      <category>knowledgemanagement</category>
      <category>pkm</category>
    </item>
    <item>
      <title>NotebookBloom: Check these four fields before trusting a NotebookLM-to-Zotero import</title>
      <dc:creator>shaojie gong</dc:creator>
      <pubDate>Tue, 15 Sep 2026 14:33:27 +0000</pubDate>
      <link>https://dev.to/shaojie/notebookbloom-check-these-four-fields-before-trusting-a-notebooklm-to-zotero-import-587p</link>
      <guid>https://dev.to/shaojie/notebookbloom-check-these-four-fields-before-trusting-a-notebooklm-to-zotero-import-587p</guid>
      <description>&lt;p&gt;BibTeX removes repetitive typing. It does not remove the need to check metadata.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Title — especially when the PDF filename is meaningless.&lt;/li&gt;
&lt;li&gt;Author — organization and personal authors are often mixed up.&lt;/li&gt;
&lt;li&gt;Year and DOI/URL — verify the publication year and route back to the original.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A clean-looking Zotero record can still contain bad source metadata. Import first, verify second.&lt;/p&gt;

&lt;p&gt;Automation should save time without turning unchecked data into a polished-looking citation.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpy8kupxiq3aoc3yk8lmf.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpy8kupxiq3aoc3yk8lmf.gif" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I built NotebookBloom around this workflow: &lt;a href="https://notebookbloom.com/blog/notebooklm-zotero/" rel="noopener noreferrer"&gt;https://notebookbloom.com/blog/notebooklm-zotero/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>notebookbloom</category>
      <category>notebooklm</category>
      <category>zotero</category>
      <category>bibtex</category>
    </item>
    <item>
      <title>GrowBook: shipping in 12 languages, and the boring gauntlet before Google Play</title>
      <dc:creator>shaojie gong</dc:creator>
      <pubDate>Tue, 15 Sep 2026 01:25:02 +0000</pubDate>
      <link>https://dev.to/shaojie/growbook-shipping-in-12-languages-and-the-boring-gauntlet-before-google-play-foc</link>
      <guid>https://dev.to/shaojie/growbook-shipping-in-12-languages-and-the-boring-gauntlet-before-google-play-foc</guid>
      <description>&lt;p&gt;The code was done. The app worked. And then came the part nobody blogs about: the distance between "it runs on my phone" and "strangers can install it from the Play Store." That distance is longer than it looks, and it's paved with paperwork.&lt;/p&gt;

&lt;p&gt;Two things ate this final stretch: languages and compliance.&lt;/p&gt;

&lt;p&gt;First, languages. GrowBook ships in 12 of them. Not "we'll localize later" — twelve, in the box, from launch. That decision reshaped small things everywhere. My plant labels needed translations keyed by scientific name. My notification copy had to make sense in every locale. And Chinese alone is two of the twelve — Simplified and Traditional — which is how I ended up hand-writing a Simplified→Traditional character table for plant names, because I had Simplified data and Traditional users and no budget to translate hundreds of names twice. Android has a localeConfig for declaring supported languages so the system knows what you offer; filling it out honestly is a small file that represents a large amount of "did you actually check this screen in Arabic" work.&lt;/p&gt;

&lt;p&gt;Then, compliance — the genuinely unglamorous part. A few things I learned the slow way:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Version numbers are two numbers, not one. Users see 1.0.0. Google cares about versionCode — an integer that must increase with every single upload. My first public version is 1.0.0, but internally it's versionCode 7, because there were six builds before it that only ever existed inside the Play Console. Every test upload burns a number. Nobody tells you this; you just watch the integer climb.&lt;/li&gt;
&lt;li&gt;Signing is forever. Release builds are signed with a keystore, configured out of a keystore.properties file kept out of the repo. Lose that key and you can't update your own app — a new key means a new app. My build falls back to the debug signing config when the keystore is absent, so a teammate (or CI) can still build without holding the release secret. That fallback is a convenience that must never, ever run for a store upload.&lt;/li&gt;
&lt;li&gt;The target API level is a moving deadline. Google requires new apps to target a recent Android API level, and that bar rises every year. Mine has a hard date attached: I have to be on the required target level by August 31, 2026, or updates get blocked. "Ship it and forget it" isn't a thing anymore — staying published is a subscription paid in maintenance.&lt;/li&gt;
&lt;li&gt;Developer verification is now a gate, not a formality. Before your app reaches anyone, your developer identity has to be verified. It's a real checkpoint with real back-and-forth, and it's a step you can't compress by writing better code.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of this is hard the way a light-sensor bridge is hard. It's hard the way taxes are hard: unfamiliar, unforgiving of small mistakes, and impossible to skip. The lesson I wish I'd internalized earlier is that "done" is a developer's word. The store has its own definition, and it includes a pile of forms, an ever-rising API target, a signing key you must guard for the life of the app, and an integer that only goes up.&lt;/p&gt;

&lt;p&gt;GrowBook is 1.0.0 now — version code 7, twelve languages, offline-first, on the Play Store. The code was the fun 80%. This last 20% is why so many finished apps never actually ship.&lt;/p&gt;

&lt;p&gt;If you've launched on Google Play: what caught you off guard in the final stretch — the API level treadmill, the signing paranoia, the version-code accounting, or something worse? I want to know what's still ahead of me.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://growbookcare.com" rel="noopener noreferrer"&gt;https://growbookcare.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;— building GrowBook in public, #6&lt;/p&gt;

</description>
      <category>growbook</category>
      <category>i18n</category>
      <category>flutter</category>
      <category>androiddev</category>
    </item>
    <item>
      <title>NotebookBloom: An AI flashcard without a source is hard to trust</title>
      <dc:creator>shaojie gong</dc:creator>
      <pubDate>Mon, 14 Sep 2026 13:17:04 +0000</pubDate>
      <link>https://dev.to/shaojie/notebookbloom-an-ai-flashcard-without-a-source-is-hard-to-trust-2gn3</link>
      <guid>https://dev.to/shaojie/notebookbloom-an-ai-flashcard-without-a-source-is-hard-to-trust-2gn3</guid>
      <description>&lt;p&gt;A flashcard can sound perfectly clear and still be wrong in a subtle way. The problem becomes harder to notice after the answer feels familiar.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Keep the prompt short enough to review quickly.&lt;/li&gt;
&lt;li&gt;Keep the answer close to the source instead of over-summarizing.&lt;/li&gt;
&lt;li&gt;Attach the source so a doubtful card can be checked later.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The source is not decoration. It is the escape hatch when context is lost during summarization.&lt;/p&gt;

&lt;p&gt;I would rather make a card slightly longer than make it impossible to audit.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9gds859dzri4gvkc0ywf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9gds859dzri4gvkc0ywf.png" alt=" " width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I built NotebookBloom around this workflow: &lt;a href="https://notebookbloom.com" rel="noopener noreferrer"&gt;https://notebookbloom.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>notebookbloom</category>
      <category>notebooklm</category>
      <category>anki</category>
      <category>responsibleai</category>
    </item>
    <item>
      <title>GrowBook: offline-first sync, and the day my wife and I both watered the same plant</title>
      <dc:creator>shaojie gong</dc:creator>
      <pubDate>Mon, 14 Sep 2026 01:26:15 +0000</pubDate>
      <link>https://dev.to/shaojie/growbook-offline-first-sync-and-the-day-my-wife-and-i-both-watered-the-same-plant-5hid</link>
      <guid>https://dev.to/shaojie/growbook-offline-first-sync-and-the-day-my-wife-and-i-both-watered-the-same-plant-5hid</guid>
      <description>&lt;p&gt;GrowBook works fully offline. No account, no signal, no problem — the local database is the source of truth, always. But some people want their plants on two phones, or shared with a partner who also forgets to water things. So I added cloud sync and family sharing. That's where "offline-first" stopped being an architecture diagram and started being a series of small, uncomfortable decisions.&lt;/p&gt;

&lt;p&gt;The setup: every device has its own local database and works alone. Sync is a thing that happens on top — opportunistically, when there's a network — not something the app waits on. The rule I held to was that the app must never block on the network for a core action. You tap "watered," it's saved locally and the UI updates now; the cloud finds out later.&lt;/p&gt;

&lt;p&gt;The hard question isn't uploading or downloading. It's: two devices changed the same thing while offline. Who wins?&lt;/p&gt;

&lt;p&gt;Every row in GrowBook carries an updatedAt timestamp and an isDeleted flag (soft deletes, so a deletion can sync like any other change instead of a row silently vanishing). When I sync, I compare timestamps and the later write wins. Standard last-write-wins. Simple, predictable, and wrong just often enough to think about.&lt;/p&gt;

&lt;p&gt;Here's the case that made me pick a direction on purpose. It's a shared plant. You water it and mark it done on your phone. Your partner, not knowing, waters it too and marks it done on theirs. Two "completed" events, same plant, roughly the same time. What should the app show?&lt;/p&gt;

&lt;p&gt;I decided: on sync, download first, and let the remote win on conflicts. My reasoning was specifically about family sharing — if someone else in your household already recorded a completion, that's real information, and clobbering it with your local copy erases something a real person did. In my sync code the comment just says: "remote wins on conflicts (e.g., family member completions)." That one parenthesis is the whole philosophy. It's not that remote is technically more correct — it's that in a shared household, the other person's action is not noise to be overwritten.&lt;/p&gt;

&lt;p&gt;The stuff nobody warns you about, in a bulleted heap because that's how it arrived:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Deletes are the scary ones. Without soft deletes, "I deleted this plant" is indistinguishable from "this plant hasn't synced yet," and you either resurrect dead rows or delete live ones. The isDeleted flag exists entirely to make deletion a normal, syncable event.&lt;/li&gt;
&lt;li&gt;Clocks lie. Timestamps assume devices agree on what time it is. They mostly do. "Mostly" is doing a lot of work in that sentence and I know it.&lt;/li&gt;
&lt;li&gt;Family sharing is a permissions problem wearing a sync costume. Who's allowed to see and edit whose plants runs on Supabase row-level security — the rules live in the database, not in my Dart code, so a bug in my app can't hand you someone else's data.&lt;/li&gt;
&lt;li&gt;Offline-first means your happy path is the offline path. The network is the edge case. That inversion is the whole mindset, and it's backwards from how most tutorials teach you to build.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What I keep coming back to: sync isn't a technical feature you add, it's a set of value judgments you encode. "Last write wins" sounds neutral, but choosing whose write wins when two humans in the same house both did the right thing — that's a product decision, and pretending it's just engineering is how apps end up quietly deleting things people care about.&lt;/p&gt;

&lt;p&gt;If you've built sync: what's your conflict rule, and did you pick it deliberately or inherit it from whatever the library did by default? I picked mine, and I'm still not sure it's right — only that it's mine.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://growbookcare.com" rel="noopener noreferrer"&gt;https://growbookcare.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;— building GrowBook in public, #5&lt;/p&gt;

</description>
      <category>growbook</category>
      <category>supabase</category>
      <category>buildinpublic</category>
      <category>plant</category>
    </item>
    <item>
      <title>NotebookBloom 1.1.3: fewer dead ends after you click export</title>
      <dc:creator>shaojie gong</dc:creator>
      <pubDate>Fri, 11 Sep 2026 13:19:24 +0000</pubDate>
      <link>https://dev.to/shaojie/notebookbloom-113-fewer-dead-ends-after-you-click-export-5bfi</link>
      <guid>https://dev.to/shaojie/notebookbloom-113-fewer-dead-ends-after-you-click-export-5bfi</guid>
      <description>&lt;p&gt;NotebookBloom 1.1.3: fewer dead ends after you click export&lt;/p&gt;

&lt;p&gt;This release is deliberately small. I focused on the places where users had already decided what they wanted to do, clicked the button, and still got stuck.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Push to Anki now checks the local AnkiConnect connection first.&lt;/li&gt;
&lt;li&gt;The failure message explains the desktop Anki and add-on requirement.&lt;/li&gt;
&lt;li&gt;.apkg is shown as the no-AnkiConnect fallback, and citation selection is easier to understand.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The update also repairs panel_open measurement so I can distinguish opening the extension from reaching the working panel. It does not collect notebook content.&lt;/p&gt;

&lt;p&gt;The goal of 1.1.3 is not more features. It is a higher chance of completing the first useful action.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxhuvjhykh6ayvcsr8oqk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxhuvjhykh6ayvcsr8oqk.png" alt=" " width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I built NotebookBloom around this workflow: &lt;a href="https://notebookbloom.com" rel="noopener noreferrer"&gt;https://notebookbloom.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>notebookbloom</category>
      <category>notebooklm</category>
      <category>chromeextension</category>
      <category>anki</category>
    </item>
  </channel>
</rss>
