5
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【個人開発】非エンジニアがClaude CodeとDuckDB-WASMで、100万行サクサクのCSV分析ツールを作った話

5
Posted at

はじめに

個人開発で、ブラウザ完結で100万行のCSVをサクサク処理する「LeapRows」というツールを作りました🎉

特徴はDuckDB-WASMとOPFSを使い、すべての処理がブラウザ内で完結することで、データは一切外部に送信されません。

私はエンジニアではなく、部分的な手直しやデバッグでは少しだけコードを書きましたが、コードの95%以上はClaude Codeに書いてもらっています。

もちろんコードの1行1行を完璧に理解できているわけではありません。だからこそ、AIが書いたコードを無責任にそのまま公開することだけは避けたいと考え、CLAUDE.mdでの実装ルールの策定、OWASP TOP10に沿ったセキュリティ監査のSkills、コミット前の簡易チェックHooksなど、非エンジニアなりに品質管理の仕組みを可能な限り整えました。

十分とは言えないかもしれませんが、少なくとも"祈ってデプロイ"にはならないよう意識しています。

まだBetaリリースの段階ですが、非エンジニアがClaude Codeとの協業でツールをリリースするまでのことを書いていきます。

作ろうとした理由は”Python共有の壁”と”サーバーコスト”

私は普段の業務はSEO担当なのですが、AhrefsやGoogle Search Console、BigQueryなどから落としてきた、数十万行~ほどのCSVを扱うことがよくあります。

大きめのCSVを扱うときにはPython(Polars)を使ってデータを加工したり、集計したりすることが多いのですが、Pythonは環境構築の手間や、コード調整のハードルの高さなどが理由で、非エンジニアのメンバーには共有しづらいと感じていました。

また自分自身でも「こんなちょっとした加工のためにPython書くの面倒だな……」と頻繁に感じていたり、ほかにも「なんで同じツールの同じCSVなのに型推論の結果が違うねん(データの結合でエラー)」みたいなことがあったりと、スプレッドシート並に手軽に数十万~数百万行のCSVを扱えるツールが欲しいとずっと思っていました。

感じていた課題

  • Excelやスプレッドシートだと数十万行超のCSVを扱うのは重たい
  • Pythonは非エンジニアメンバーに共有しづらい
  • ちょっとした作業のためにPythonを書くのは面倒くさい

サーバーでPolarsを動かそうとするも即断念

そこで初めは、「サーバー側でPolarsを動かす仕組みを作れば、超高速に集計できるのでは?」と考えて手を動かし始めてみたのですが、いざ使おうと思うと、

  • そもそも大きなCSVを送受信すること自体が遅くて使う気にならない
  • 使う人数が増えたらサーバーのコストも高くなりそうなのでそれは怖い

などの理由から、サーバー側でのPolars運用は諦めました。

DuckDB-WASM×OPFSという転換点

Polarsをサーバーで動かすのは厳しいし、やっぱり諦めるかーと考えていたときに、たまたまDuckDB-WASM×OPFSで、「1TB規模のログデータをオフラインで扱う」という時雨堂さんの記事を拝見しました。

恥ずかしながらParquet形式というものをこのときに初めて知りましたが、記事に掲載されているデモの検索速度の速さに衝撃を受けました。

そして、「ユーザーがアップロードしたCSVを即座にParquetに変換して、OPFSに保存してしまえば、サーバー処理なしで爆速でデータ加工・集計ができるツールが作れるのでは?」と考えました。

ドキュメントを見てみると、DuckDB-WASMを使って、データを読み込んで表示することだけであれば自分でもコードを書けそうではありました。

ただし「誰でも扱えるようにする」という状態を目指すなら間違いなく作り込まれたGUIが必要。そしてGUIを作り込むのは、2人の育児中の自分にはどう考えても厳しい…。

そこで当時話題になっていたClaude Codeを使ってみようと思いました(これが2025年6月ごろ)

本当に「通信ゼロ」なのか?

余談ですが、非エンジニアが作ったツールとなると、「本当にサーバーにデータが送られていないか?」と不安に思う方もいるかもしれません。

だからこそ、今回は「物理的にデータが外に出ない」アーキテクチャにこだわりました。

実際にLeapRowsに巨大なCSVを読み込ませて処理している最中の、ブラウザのNetworkタブがこちらです。

発生しているPOST通信はVercel Analytics(ページビュー計測)のみです。またVercel AnalyticsはLP側でのみ有効にしています。

image.png

image.png

Payloadを見ても、読み込んだCSVのファイル名やデータの中身は外部に送信されていません。

これにより、サーバーコストをゼロに抑えつつ、安心して扱えるという最大のメリットを獲得できました。

暴走するClaude Codeとの戦い

Claude Codeをインストールし、わくわくしながら「DuckDB-WASMを使って、ユーザーがアップロードしたCSVをParquetに変換してOPFSに保存。データを閲覧できるツールを作って」と依頼しました。

見る見るうちにコードが書き上げられて、最低限の機能を持ったツールが出来上がりました。

楽しくなってきた私は、ピボット集計したい、フィルタ機能をつけよう、列の追加もできるように…と思いつくままに機能を追加していきます。

実装のバグが明らかに増える → イチから作り直しへ

いくつか機能を追加していくと、ある日からClaude Codeが「実装できました→チェックしたらバグだらけ→バグを伝えて直すように依頼→直りました!→直っていない…」という感じで、明らかに実装の精度が下がってくる時期が訪れます。

ひとつの機能を実装するだけでもラリーが非常に多くなり、かつ実装した機能以外の箇所でもバグが頻繁に出てしまう状態になってしまいました。

この時点ではXで拾ってきた「Claude Codeの実装が格段によくなるCLAUDE.md」のような基本原則をまとめたものを使っていました。

しかしツールの仕様もまとまっておらず、依頼もその場その場で行っている状態では、当然のようにCLAUDE.mdの意味もなさず…。最終的には何を実装しようとしてもうまくいかず、どこにバグが出るかもわからないカオスが生まれていました。

そこでこのタイミングで、当初作っていたコード群は一度破棄して作り直しています
(すでに開発開始から1ヶ月くらい経過していました…)

同じ失敗をしないようにGeminiに相談しながら、プロジェクトの作法(設計方針、DuckDBやOPFSへの接続方法の明記、UIの共通ルール)などをまとめることで、かなりスムーズに実装できるようになりました。

現在の CLAUDE.md には、主に以下のようなルールを定義しています。(開発を進めながら継ぎ足していったので少し長いですが、一部を抜粋します)

# 開発哲学
* Incremental progress: 小さな変更を積み重ねる
* Learning from existing code: 既存パターンを学習してから実装
* Pragmatic over dogmatic: 現実に適応する
* Clear intent over clever code: 明確さ優先

# バグ修正の方法論
* 調査→テスト→修正の順序を厳守(推測で修正しない)
* 再現テストを先に書き、失敗を確認してから修正に着手
* 修正は最大3回で打ち切り、収束しなければエスカレーション
* 影響範囲の全数調査(Grepで全使用箇所を検索)
* イミュータブルパターンの遵守

# Skills(再利用可能な実装ガイド)
* 条件に該当する場合、コードを書く前に必ず対応スキルを呼び出すルール
* DuckDB操作、Zustand状態管理、セキュリティ監査、E2Eテスト、UI作成など領域別に整備
* Planエージェントでもスキル参照を必須化(SKILL.mdをReadして計画書に明記)

# アーキテクチャ原則
* DuckDB シングルトン管理 — 接続の一元管理、close()禁止
* Zustand 状態管理 — useShallowによる選択的購読で過剰レンダリング防止
* SQLエスケープ — 専用ユーティリティ関数による安全なクエリ構築
* fileIdの単一ソース — file-context-storeで一元管理
* クエリキャンセル機構 — AbortController + debounceによる論理的キャンセル
* HTMLサニタイズ — escapeHtml + DOMPurifyの2層防御
* 入力検証 — ファイルサイズ制限、ReDoS防止、正規表現パターン検証
* APIレート制限 — IPベースのブルートフォース防止

# トラブルシューティング
* 30件以上のエラーパターンと解決策を表形式で蓄積
* 過去に遭遇した問題の再発防止ナレッジベースとして機能

AIで挑んだ「100万行の壁」

Geminiと相談したおかげでアーキテクチャはそれなりにまとまったのですが、それでも実際に開発していくなかでは、様々な壁にぶつかりました。

DuckDBのクラッシュ(複数接続問題)

初めてDuckDB-WASMを触ったので、DuckDB-WASMのインスタンスに複数接続しようとするとエラーが出るという大前提を知りませんでした…。

もちろんClaude Codeはそんなことを何も考慮せずにコードをガリガリ書いてくれるため、

DuckDB-WASMまわりで頻出したエラー

  • DuckDBの初期化が終わっていないのにクエリを実行しようとしてエラー
  • CSV→Parquetへの変換が終わっていないのにSQLを実行してエラー
  • 前回のクエリが実行し終わる前に新しい処理をしようとしてエラー

というようにバグが多発しました。

エンジニアの皆様からすれば基礎中の基礎かと思いますが、ここでGeminiに相談して、はじめて「シングルトンパターン」というものを学びます。

ZustandによってDuckDBのインスタンスを管理をすること、さらにCLAUDE.mdにDuckDB接続時には必ずシングルトン管理のDuckDBインスタンスを使うこと、を明記するとエラーの数は劇的に減りました。

「_setThrew is not defined」の多発

DuckDB-WASMを使っていると、本当にものすごい数の「_setThrew is not defined」というエラーに遭遇しました。

当初はエラーの意味が全くわからず、Geminiに聞いても、Claudeに聞いても、Googleで検索しても解決しなかったのですが、WASM側で起こっていたエラーだと気づき、WASM側のエラーをキャッチしてconsoleに書き出す仕組みを作ってもらってから、デバッグができるようになりました。

ちなみに大半は先述した、複数接続しようとしたり、初期化前に接続しようとしたり、加工前後のデータの整合性が取れていない等の初歩的な理由でした。

待ち時間ゼロを目指した「動的CTE」と、そこから生まれた「レシピ機能」

当初は列加工や列削除などのたびに、クエリを実行し、実行結果はOPFS上のParquetに上書き保存していました。

DuckDB-WASMの処理は高速なのでParquet保存は1秒未満でも終わります。しかし、Excelのようにサクサク操作したいユーザーを想定すると、「毎回1秒待たされる」というのは非常にストレスが大きなUXになっていると考えました。

Before:操作のたびに物理保存していたので、Parquet保存の待ち時間が生まれる

どうすれば待ち時間をゼロにできるのか。ふと「毎回データを物理保存するのをやめて、UIの操作履歴をJSONで保持しておき、表示する瞬間にSQLのWITH(CTE)で数珠つなぎにして1発で実行すればいいのでは?」と思いつきました。

-- 💡 内部で動的に組み立てているCTEのイメージ

WITH step1 AS (SELECT * FROM source_data WHERE category = 'A'),
     step2 AS (SELECT * FROM step1 WHERE price > 1000)
SELECT * FROM step2;

これによりユーザーが作業をするたびに待ち時間が発生する問題を解決できました。

そして、「操作履歴がJSONで綺麗に整理されているなら、保存しておけば、そのまま操作の自動化が出来るのでは?」と気づき、ここからLeapRowsの特徴であるレシピ機能の発想に繋がります。

After

  • 待ち時間はファイルロード時のみ
  • JSONから作業工程を再実行可能

ただ、正規表現や複雑な構成比計算などが重なると、どうしても動的CTEの実行自体が遅くなるケースが出てきました。

自分には、実行計画(EXPLAIN)を見て高度なクエリチューニングを行うようなSQLのスキルはありません。そのため、まずは「ユーザーを待たせないこと」を最優先とし、計算結果を一時的に物理保存するキャッシュ機能を実装してカバーしています。

AIが迷走した時の最終兵器は、結局「大量のconsole.log」だった

とはいえ、それでもAIが「直しました!」と言いながらも、エラーがループしてしまうケースはまだまだありました。

最終的には怪しいコードの周辺にconsole.logを大量に記載することで、「ここでデータがundefinedになってる」「ここのコードの前後でSchemaが変わってる」などを泥臭く調査し、Claude Codeに「〇〇なのでここの挙動がおかしいのではないか」と言ったように伝えることで、迷走していたAIが正しいコードを書き出してくれる確率がかなり上がりました。

vibe-coding的ではありますが、どこがおかしいのかをこちらで特定したうえで改善依頼を出すことは、非エンジニアなりのデバッグの勝ち筋だったのかもしれません。

Claude Codeとの協業で学んだこと

育児中の時間がない中でも個人開発ができる

過去にも個人開発で何かを作りたくて、少し作り始めては、時間がなくなり投げ出して、また少し作り始めては時間がなくなり投げ出して…、ということを何度も繰り返してきました。

特に本業はコードを書く仕事ではないので、新しく何かを始めようとしたときには、前回に学んだことはほとんど忘れてしまっていたりで、尚さらアウトプットできるレベルに達していませんでした。

しかし、Claude Codeが登場したことによって、自分の頭のなかにある「こんなものが欲しい」というイメージがあれば、それをベースに仕様をまとめておくことで、実装自体はClaude Codeが進めてくれます。

作り直しの期間も含めて8ヶ月もかかってしまいましたが、1日1時間半の作業の積み重ねだけで、Beta版のリリースまで進められたことは、ここ数年間ずっと「育児期間中には何もできないし、成長を諦めるしかない」と思い込んでいた自分にとっては、とても大きな経験となりました。

非エンジニアでも開発のセオリーを知っておくべき

Claude Codeを使っているのに開発に8ヶ月もかかってしまった理由は、エンジニアの方々では当たり前に考えられるような、アプリ全体のアーキテクチャに対する設計や、事前知識がなかったことが一番の理由だと感じています。

※シングルトンパターンとか操作ステップを保存する考え方とか…

Claude Codeが本当に賢いので、非エンジニアもツールやアプリを作れる時代になってきていると思います。

しかし、作りたいものに対して最適なデザインパターンやアーキテクチャの前提知識を持っているほうが、遠回りも避けられますし、何倍も良いものを作れるようになりそうだなと感じています。

SkillsやHooksはやっぱり便利

当初はSkillsの使い道がよくわかっておらず、ほとんど使っていなかったのですが、しかし

  • DuckDBへのクエリを作るとき専用のSkill
  • バグが出たときにテスト駆動で修正するまでループするSkill
  • E2Eテストを書くためのSkill
  • OWASP TOP10に沿った監査Skill
  • コミット前に簡易的なセキュリティチェックをするHook

など、少しずつ充実させていくことでかなり開発効率が良くなったと感じています。

もちろん専門家による脆弱性診断には及びませんが、非エンジニアなりに"AIがやらかすリスク"を減らすガードレールを敷いています。

スラッシュコマンドだけで、特定の作業をしてくれるので本当に便利ですね。
以下は個人的によく使っているスキルです。

スキル名 用途
build-in-public gitコミットからX投稿文(#BuildInPublic)を生成
claude-md-organizer CLAUDE.md肥大化防止(完了済み計画→docs/specs移動)
documentation-update 実装変更後のドキュメント更新(明示的な依頼時のみ)
duckdb-singleton-safe DuckDB接続操作、_setThrewエラー対策
duckdb-sql-standards SQLクエリ作成、列名エスケープ
e2e-scenario-creator E2Eテストシナリオ作成
e2e-test-fixer E2Eテスト失敗の構造的診断・修正(4フェーズ、最大3イテレーション)
security-audit-api-security API認証・レート制限・CSRF監査
security-audit-data-exposure データ漏洩(ログ、エラーメッセージ)監査
security-audit-dependency 依存関係の脆弱性監査
security-audit-headers セキュリティヘッダー監査
security-audit-input-validation 入力検証(ReDoS、ファイルサイズ)脆弱性検出
security-audit-sql-injection SQLインジェクション脆弱性検出
security-audit-xss XSS脆弱性検出
security-vulnerability-checker アプリ全体のセキュリティ監査(OWASP Top 10準拠)
tailwind-ui-patterns UIコンポーネント作成(ボタン・テーブル等)
test-first-bug-fix テスト駆動バグ修正(再現テスト→修正→検証ループ)

あと開発以外の普段の業務においても、繰り返し行われるようなタスクなら色々な使い道がありそうなので、ここは少しずつ学んでいきたいと思っています。

おわりに:AIは魔法ではないけど能力を拡張してくれる

作り始めるまでは「実際、どのくらい実用的なものを作れるんだろう…?」と半信半疑ではあったのですが、Claude Codeは想像以上に優秀で、自分の可能性を大きく広げてくれました。

特に「なんとなくは知ってるけど、自分で手を動かせるレベルではないこと」に対して、前向きに動けるようになるのが大きいと感じています。

一方でAIはなんでも作ってくれる魔法のようなものではなく、むしろ作り手のイメージを具体化する役割のようにも感じています。そのため、作り手が詳細にイメージできていればいるほど、良いアウトプットに繋がりそうだなと感じました。

まだBeta版リリースになんとかこぎ着けたところなので、これからコツコツとマーケティング活動を進めていこうと思います。

もしご興味を持ってくださった方がいれば、使ってみて、フィードバックをいただけると泣いて喜びます:sob:

5
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?