<?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: Katsutoshi Imanishi</title>
    <description>The latest articles on DEV Community by Katsutoshi Imanishi (@katsuo-chang).</description>
    <link>https://dev.to/katsuo-chang</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%2F3888044%2F08d22823-b875-4ed7-ad5d-ed3323a8611d.jpg</url>
      <title>DEV Community: Katsutoshi Imanishi</title>
      <link>https://dev.to/katsuo-chang</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/katsuo-chang"/>
    <language>en</language>
    <item>
      <title>What I Learned Shipping TightTimeLog: AI Workflow, CSP, AdSense, and Distribution</title>
      <dc:creator>Katsutoshi Imanishi</dc:creator>
      <pubDate>Mon, 20 Apr 2026 02:36:34 +0000</pubDate>
      <link>https://dev.to/katsuo-chang/what-i-learned-shipping-tighttimelog-ai-workflow-csp-adsense-and-distribution-318j</link>
      <guid>https://dev.to/katsuo-chang/what-i-learned-shipping-tighttimelog-ai-workflow-csp-adsense-and-distribution-318j</guid>
      <description>&lt;p&gt;I built and launched &lt;a href="https://tighttimelog.org/" rel="noopener noreferrer"&gt;TightTimeLog&lt;/a&gt;—a small offline-first PWA timer with bilingual UI and on-device logs—as a solo project. Along the way I hit &lt;strong&gt;AdSense rejection&lt;/strong&gt;, underwhelming distribution experiments, and plenty of CSP surprises. This post bundles several shorter notes I published on my &lt;a href="https://katsuo-chang.hatenablog.com/" rel="noopener noreferrer"&gt;Hatena blog&lt;/a&gt; into &lt;strong&gt;one English piece&lt;/strong&gt; for DEV.&lt;/p&gt;




&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;TightTimeLog was my first “ship it” personal project: PWA behavior, EN/JA UI, IndexedDB logs, and a focus on keeping data on the user’s device. &lt;strong&gt;Google AdSense did not approve the site&lt;/strong&gt; (at least on the attempts I made), and marketing pushes did not move the needle as much as I hoped. Still, finishing deployment and seeing &lt;em&gt;where&lt;/em&gt; things break was more valuable than the binary pass/fail. Below I split lessons into &lt;strong&gt;engineering&lt;/strong&gt;, &lt;strong&gt;AI-assisted workflow&lt;/strong&gt;, and &lt;strong&gt;distribution / monetization mindset&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Building with Cursor + Cloudflare Pages (and what hurt)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Why this stack
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Cursor:&lt;/strong&gt; conversational coding, refactors, and research in one loop—huge for a solo dev’s throughput.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cloudflare Pages:&lt;/strong&gt; static deploy + HTTPS + CDN with minimal ops—great default for a hobby site.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  PWA and a little differentiation
&lt;/h3&gt;

&lt;p&gt;I added &lt;strong&gt;haptic feedback&lt;/strong&gt; on supported phones so the app feels slightly more “app-like” than a bare timer page. It’s a small touch, but it matches the goal of a pleasant mobile experience.&lt;/p&gt;

&lt;h3&gt;
  
  
  Design upfront
&lt;/h3&gt;

&lt;p&gt;Using &lt;strong&gt;Figma early&lt;/strong&gt; for icons and layout reduced late rework. For side projects, investing a bit in visuals before features explode pays off.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Practical prompting for i18n and “legal-ish” drafts (Cursor / Gemini)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  i18n
&lt;/h3&gt;

&lt;p&gt;The trick was to pin down &lt;strong&gt;library assumptions, output shape, and file layout&lt;/strong&gt; in the prompt instead of saying “make it multilingual.” For example:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Externalize all user-visible strings; keep English and Japanese keys consistent with the existing &lt;code&gt;app.js&lt;/code&gt; i18n object pattern; don’t leave hard-coded copy in HTML.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Fewer surprises, fewer round trips.&lt;/p&gt;

&lt;h3&gt;
  
  
  Privacy policy drafts
&lt;/h3&gt;

&lt;p&gt;I listed facts only:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Where data lives (e.g., IndexedDB, not uploaded)&lt;/li&gt;
&lt;li&gt;Third parties involved (hosting, contact form, ads if any)&lt;/li&gt;
&lt;li&gt;How users can reach me&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then I edited the draft manually. &lt;strong&gt;AI reduces blank-page time; it doesn’t replace responsible review.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Cloudflare Pages + CSP: when “it worked locally” dies in production
&lt;/h2&gt;

&lt;p&gt;Production &lt;strong&gt;Content-Security-Policy&lt;/strong&gt; headers blocked third-party scripts and iframes I hadn’t allowlisted—especially after adding AdSense-related domains. Fixing it meant evolving &lt;code&gt;_headers&lt;/code&gt; until the live response headers matched what the browser needed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Mental model
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;/*
  Content-Security-Policy: default-src 'self'; script-src 'self' https://pagead2.googlesyndication.com ... ; frame-src https://googleads.g.doubleclick.net ... ;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your exact directive will differ by which services you embed. I treated the browser console’s CSP violations as the source of truth for what to add.&lt;/p&gt;

&lt;h3&gt;
  
  
  Checklist I wish I had on day one
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Inspect &lt;strong&gt;&lt;code&gt;Content-Security-Policy&lt;/code&gt; on the real origin&lt;/strong&gt; (not &lt;code&gt;file://&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Watch the console for &lt;strong&gt;CSP violations&lt;/strong&gt; after each new script or embed.&lt;/li&gt;
&lt;li&gt;After changing &lt;code&gt;_headers&lt;/code&gt;, &lt;strong&gt;re-deploy&lt;/strong&gt; and confirm headers changed (avoid stale SW caches during testing).&lt;/li&gt;
&lt;li&gt;When using a &lt;strong&gt;service worker&lt;/strong&gt;, bump cache versions or test in a clean profile if HTML responses look “stuck.”&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  4. AdSense rejection: my self-analysis (non-legal, non-official)
&lt;/h2&gt;

&lt;p&gt;Google doesn’t owe us a precise reason, but my working theory:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The product was optimized to be &lt;strong&gt;minimal UI copy&lt;/strong&gt;, which can look like &lt;strong&gt;thin content&lt;/strong&gt; to automated review.&lt;/li&gt;
&lt;li&gt;Policy also cares about &lt;strong&gt;where&lt;/strong&gt; ads appear—&lt;strong&gt;navigation-only or low-value shells&lt;/strong&gt; should not carry ads.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I responded by adding &lt;strong&gt;original explanatory copy&lt;/strong&gt; on main screens and keeping ad tags off purely supplementary pages. Monetization-wise, I’m treating AdSense as &lt;strong&gt;one experiment&lt;/strong&gt;, not the only path.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. Posting on Qiita, X, and Product Hunt: distribution lessons
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Rough outcomes
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;X:&lt;/strong&gt; posting cold from a fresh account barely moved traffic. I skipped &lt;strong&gt;community building&lt;/strong&gt; (hashtags, consistent presence) and paid for it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Qiita &amp;amp; Product Hunt:&lt;/strong&gt; dropping a link alone didn’t create sustained traffic.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Takeaways
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Context beats bare links&lt;/strong&gt;—show up where your audience already discusses problems you solve.&lt;/li&gt;
&lt;li&gt;Even tiny reactions matter; they’re fuel when metrics are flat.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Next, I want to lean into &lt;strong&gt;Build in Public&lt;/strong&gt;: share the process, not only the launch URL.&lt;/p&gt;




&lt;h2&gt;
  
  
  Closing
&lt;/h2&gt;

&lt;p&gt;Shipping still beat not shipping. I’ll keep iterating on &lt;a href="https://tighttimelog.org/" rel="noopener noreferrer"&gt;TightTimeLog&lt;/a&gt; and experiment with other projects too.&lt;/p&gt;

&lt;p&gt;Japanese dev diary: &lt;a href="https://katsuo-chang.hatenablog.com/" rel="noopener noreferrer"&gt;Hatena Blog&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Personal learning log—not legal or financial advice. Verify policies and products for your own situation.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>pwa</category>
      <category>webdev</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
