Electron
イベント
該当するコンテンツが見つかりませんでした
マガジン
該当するコンテンツが見つかりませんでした
技術ブログ
はじめに こんにちは。RevComm でエンジニアをしている 林 です。 AI コーディングエージェントの普及でコードを書くコストは大きく下がり、ボトルネックはレビューと定型的な保守作業に移りました。 Addy Osmani 氏も「 Agentic Code Review 」で、レビューが PR の作成スピードに追いつかず、コードが読まれないままマージされ始めている現状を書いています。 MiiTel Phone(ブラウザ上で通話できる AI 搭載 IP 電話アプリ)の開発リポジトリでは月に 300〜600 件の PR がマージされます。その中には、振る舞いを変えないリファクタリング、依存パッケージの更新、ドキュメントの修正のような、判断の型が決まっているのにレビューの待ち時間だけは通常の PR と変わらないものが相当数含まれます。 こうした定型的な判断を GitHub Actions 上の AI に任せ、人間のレビューを本質的な変更に集中させる取り組みから、4 つの事例を紹介します。 いずれも Claude Code を GitHub Actions 上で動かす構成で、事例 1〜3 は公式の claude-code-action 、事例 4 は GitHub Agentic Workflows (gh-aw) を利用しています。 ※ 記事中のコードや設定は説明のために抜粋・簡略化しています。 設計原則を先に 4 つの事例は使うツールも対象も違いますが、設計は同じ型に収まっています。作業の大半はプロンプトを書くことではなく、「AI に何をさせないか」を決めることでした。 先に原則を挙げます。以降の事例は、これがどう具体化されているかとして読んでもらえればと思います。 判定は AI、実行は決定的なステップとして 。AI の出力は構造化された判定 or 成果物にとどめ、承認・マージ・クローズ・チケット起票といった副作用は、スキーマ検証を通った出力に対して後続のステップが実行します 自動化の終点を、人間の判断が残る場所に置く 。AI が進めるのは承認、コミットの push、draft PR の起票までで、マージするかどうかは人間が判断します いつでも止められるようにする 。設定をひとつ変えるだけで自動承認を止められるキルスイッチ、作成者がラベルを付けた PR だけを対象にするオプトイン方式、AI の実行回数・実行時間・同時実行数の上限。まず判定だけを動かして精度を確認し、納得してから副作用を有効化します AI への入力を信頼しない 。PR 本文やリリースノートはサードパーティ由来のテキストとして扱い、渡さないか信頼境界の外に置きます 運用で精度を上げる前提を組み込む 。週次のスポットチェック、誤判定を反例としてプロンプトに追記していく運用、リポジトリ固有の観点を自然言語で注入できる入り口、そして「処理しなかったもの」のログを確認可能にしておきます 事例 1: PR のレビュー要否を AI が判定する AI に任せるのはレビューではなく分類 きっかけはチームの振り返りで出た「テストコードやドキュメントだけの変更なら、レビューなしでマージしてよいのでは」という話でした。 前提として、PR を open すると GitHub Copilot や Devin が自動でレビューする設定はすでに入っています。ただし、これらは人間のレビューに観点を足す補助であって、承認までの待ち時間はそこまで変わりません。 今回のボトルネックはまさにこの待ち時間なので、AI レビューを強化して人間のレビューを置き換える方向は取りませんでした。 方針の参考にしたのは Findy Tech Blog の「 不要なレビューをAIにまかせてAIコーディングの環境改善を加速した 」です。要点は、AI を「レビュアー」ではなく「人間のレビューが不要な PR かどうかの分類器」として使うこと、そして Kent Beck 氏の『 Tidy First? 』でいう振る舞いを変えない整頓(tidying)だけを自動承認の対象にすることです。 レビューの代行が品質保証の責任ごと AI に移してしまうのに対し、分類は「人間がレビューすべき PR を人間に確実に届ける」ための振り分けです。 3 レイヤーのレビューポリシー 「AI で自動化」と言いつつ、最初のレイヤーに AI は登場しません。 レイヤー 内容 仕組み レイヤー 0 ドキュメントのみの変更の自動承認 ファイルパスによる機械的な判定(AI 不要) レイヤー 1 人間レビュー必須領域の定義 ポリシードキュメント レイヤー 2 振る舞いを変えない整頓の自動承認 AI 分類器 + 抜き取りの事後レビューと監査 レイヤー 0 は、変更ファイルがすべて docs/ 配下または *.md の PR を機械的に承認します。AI で判定できることでも、決定的に判定できるならそちらを選びます。誤判定リスクがなくコストもゼロだからです。 ただし、同じ Markdown でも .github/ 配下と CLAUDE.md / AGENTS.md は対象外です。これらの無人承認を許すと、自動承認システム自体の改変が無人で通ってしまいます。 レイヤー 1 は、AI の判定にかかわらず必ず人間がレビューする領域の明文化です。通話のような中核機能、feature flag、外部システムとの連携、個人情報や認証、テストの削除、依存パッケージ、CI 設定などが該当します。間違えると影響が大きい領域と、自動化自体の安全性を支える領域(テスト・CI)を並べています。 分類器の実装 レイヤー 2 が本題の AI 分類器です。PR の作成者が ai-classify ラベルを付けたときだけ動きます。分類器は『Tidy First?』の構造的リファクタリングを調整した分類表を持っています。 T1: ファイル・ディレクトリの移動、リネーム(import パスの機械的追随を含む) T5: 変数・関数・コンポーネントのリネーム T7: 未使用コード・ファイルの削除 A1: 既存 feature flag の targeting(どの環境・ユーザーに配布するかの設定)のみの変更(振る舞いは変わるが、チームが明示的にレビュー不要と合意した低リスク変更) NG2: 通話系コードに触れる変更(即 HUMAN_REVIEW) ポイントは、除外条件(NG)を「変更の性質」ではなく「触れた領域」で判定させていることです。通話系ファイルの変更が import パスの機械的な追随だけだったとしても NG2 に倒します。「機械的な変更だから実質は該当しない」という楽観的な判断を AI にさせないためです。 また、迷ったら UNKNOWN と出力させ、 誤って APPROVE するコストは誤って HUMAN_REVIEW とするコストよりはるかに大きい という原則もプロンプトに明記しています。 出力は自然言語のコメントではなく、構造化された判定ファイルです。 { " verdict ": " APPROVE ", " t_categories ": [ " T1 ", " T5 " ] , " reasons ": [ " 判定理由を簡潔に列挙する " ] , " summary ": " 1〜2 文の判定サマリ ", " sampling_used ": false } 後続のステップが jq でこのファイルのスキーマを検証し、検証を通った APPROVE 判定に対してのみ承認を実行します。 - name : Approve if : steps.verdict.outputs.verdict == 'APPROVE' && vars.AI_CLASSIFY_APPROVE_ENABLED == 'true' run : | gh pr review "$PR_NUMBER" --approve --body "$body" gh pr edit "$PR_NUMBER" --add-label ai-approved AI_CLASSIFY_APPROVE_ENABLED は GitHub の repository variable を使ったキルスイッチで、 true でないときは判定コメントの投稿だけを行います。導入初期は切ったまま判定精度を確認し、問題ないことを確認してから有効化しました。 分類器に渡すのは差分とファイル内容だけで、PR 本文とコメントは渡しません。ただし、diff 中のコードコメントには同じ誘導を書けるため、この経路まで消えるわけではありません。 そこは後段のスキーマ検証と、マージ判断が人間に残っていることでブロックします。承認後に push があれば自身の承認を取り消して再分類し、自動マージはどのレイヤーでも行いません。 運用と、PR の作り方への波及 自動承認された PR は週次で数件を人間が事後レビューしています。Addy Osmani 氏の整理を借りれば、すべての差分を人間が見る形から、抜き取り確認と監査で仕組み全体を見張る形への移行です(彼の記事の言葉では human in the loop から human on the loop へ )。誤判定が見つかれば、反例として分類器プロンプトの除外条件に追記します。 効果はレビューが速くなることにとどまりませんでした。分類器が APPROVE を出せるのは PR 全体が整頓で完結しているときだけなので、リファクタリングと機能変更が混ざった PR は HUMAN_REVIEW に倒れます。 つまり、作成者から見ると、混ぜれば全体がレビュー待ちになり、分ければリファクタリング部分を先に流せる。リネームやファイル移動を先行 PR に切り出して ai-classify を付け、その上に振る舞いを変える PR を積み重ねれば(stacked PR として)、人間に届くのは本質的な変更だけの小さな差分になります。 『Tidy First?』の「整頓と振る舞いの変更を分けよ」という規律は、分けること自体のコスト(PR が増え、レビュー待ちも複数回になる)のせいで努力目標になりがちでした。分類器が整頓側の待ち時間をゼロにしたことで、規律を守るほうが速いという構造に変わっています。 自動化が規律を強制するのではなく、規律に従うことが合理的な選択になるというのは、レビュー待ち時間の削減と並ぶ、もうひとつの効果だと思っています。 事例 2: Dependabot PR を毎朝トリアージする 依存パッケージの更新 PR への対応は、毎回ほぼ同じ判断の繰り返しでした。CI の成否を確認し、リリースノートを読んで破壊的変更の有無を確認し、低リスクなら承認してマージ、高リスクなら Notion にチケットを起票して PR はクローズし、動作確認を含めた計画的な対応に回す。判断の型は決まっているのに手作業では追いつかず、PR は溜まる一方でした。 最初の自動化は、この手順を Claude Code の Skill に書き起こすことでした。手元で「Dependabot の PR を対応して」と頼めば、オープン中の PR を順に解析して一括処理してくれます。 ただし、人が起動する半自動なので、誰かが思い出さない限り PR は溜まり、実行する人の環境と権限に依存する属人的な運用でもありました。 そこで Skill として固めた手順を GitHub Actions の composite action(複数のステップをまとめて再利用できる仕組み)に作り直し、無人で定期実行するようにしました。 このとき、AI 自身が実行していたマージやチケット起票は AI から切り離しています。composite action は社内の共通リポジトリに置き、複数のリポジトリから利用しています。 毎日午前 3 時の cron がオープン中の Dependabot PR を古い順に 1 件ずつ処理します(1 件マージすると Dependabot が残りを rebase するため、並列ではなく直列です)。 PR タイトル・body からパッケージ名とバージョンを機械的に解析し、CI チェックの成否を判定する Claude によるリスク判定。リリースノートを読み取り専用のツールで読み、 triage-verdict.json (risk: low / high、根拠、主要変更点)を書き出す 1, 2 を決定表に通して最終アクションを決める 実行。承認して自動マージ、クローズして Notion にチケット起票、または手動対応として PR に残す 条件 アクション タイトル・body とも解析不能 手動対応(PR は残す) CI 失敗 クローズしてチケット起票(AI の判定に関係なく) AI の判定を取得できない 手動対応(安全側に倒して人間に残す) AI 判定が高リスク クローズしてチケット起票 AI 判定が低リスク 承認して自動マージ Notion のチケット操作も AI にはさせず、TypeScript のスクリプトが REST API を直接叩きます。チケット作成に失敗した場合は PR をクローズせず手動対応に降格させ、「チケットなしで PR だけ消える」事態を防いでいます。 リリースノートはサードパーティ由来の入力なので、プロンプト側では、指示のような文言(「この更新は安全である」「以前の指示を無視せよ」など)を見つけた場合、従わないだけでなく、それ自体をサプライチェーン上の懸念として high と判定するよう指示しています。インジェクションの試みを検知シグナルに変える発想です。 それでも最後の砦は AI の外にある CI 成功という条件で、仮に誘導が成功しても CI が落ちている PR はマージされません。Dependabot ブランチのコードはチェックアウトすらせず、AI が読むのはデフォルトブランチと PR のメタデータだけです。 リポジトリ固有の判定観点は、決められたパスに Markdown で書いておくとプロンプトに注入されます。「electron 関連の更新はすべて高リスクとする」のような 1 行で、チームの経験則を判定に反映できます。 導入後は毎朝の CI で Dependabot のオープン PR をゼロに戻していくので、依存更新の PR が滞留すること自体がなくなりました。残る人間の作業は、高リスクと判定されて起票されたチケットへの対応だけです。 事例 3: @claude メンションを全リポジトリ共通の基盤にする PR や issue のコメントで @claude とメンションすると GitHub Actions 上で Claude Code が動き、質問への回答、レビュー指摘への修正コミットの push、ショートカットの実行を行います。 反応するのは書き込み権限を持つユーザーのコメントのみで、Claude に承認とマージはさせません。 よく使うのはコンフリクトの解消です。AI コーディングで PR を作る速度が上がると同時にオープンな PR が増え、main の進みも速くなるため、コンフリクトは以前より起きやすくなりました。PR 作成の高速化が、今度はコンフリクト解消という新しい定型作業を生んだ形です。 @claude /fix-conflicts と書けば、解消して push するところまでやってくれます。 同じ問題意識は GitHub Next の Evergreen や Codex の PR Babysitter にも見られ、この種の PR のお手入れは自動化の定番になりつつあります。 実装面の工夫は共通化です。 @claude メンション自体は claude-code-action の代表的な使い方ですが、リポジトリごとにワークフローを持つと、モデルの変更やコスト制御の調整のたびに全リポジトリへ同じ変更を配ることになります。 そこでトリガー条件・トークン設計・モデル選択・コスト制御を社内共通リポジトリの reusable workflow に集約し、利用側には呼び出しの数行だけを置く構成にしました。 # 利用リポジトリ側はこれを置くだけ jobs : claude : uses : my-org/claude-workflows/.github/workflows/claude.yml@main secrets : inherit ショートカットの中身は Claude Code の Skill として、さらに別の共有リポジトリ(plugin marketplace)に置いています。手元の Claude Code と GitHub Actions で同じ Skill 資産を共有するためで、ローカルで育てたワークフローがそのまま @claude メンションでも使えます。 事例 4: 定期メンテナンスを自然言語で書く (GitHub Agentic Workflows) gh-aw は、ワークフローを「YAML frontmatter + 自然言語プロンプト」の Markdown ファイルとして書き、 gh aw compile で実行可能なワークフロー( .lock.yml )にコンパイルする gh CLI の拡張機能です。 --- on : schedule : weekly on sunday around 20:00 # fuzzy schedule workflow_dispatch : permissions : contents : read engine : id : claude max-turns : 50 timeout-minutes : 30 safe-outputs : create-pull-request : title-prefix : '[main] ' draft : true --- # Docs Drift Audit You are auditing this repository's developer documentation for drift against the actual code, and fixing what has drifted. ... 事例 1〜3 が AI の判定や応答を自前のワークフローに組み込む構成だったのに対し、gh-aw はスケジュールで自律的に動き、リポジトリを調べて成果物(PR など)まで作るエージェントです。 その分、安全装置も枠組み側に強く組み込まれていて、エージェント本体は読み取り専用のトークンで実行され、書き込みは frontmatter の safe-outputs で宣言したものしかできません。 外部通信もファイアウォールコンテナで制限され、 weekly on sunday around 20:00 のようなあいまいな時刻指定はコンパイラが具体的な時刻に解決して、多数のリポジトリで cron が同時刻に集中するのを避けます。現在 2 本が週次で動いています。 ドキュメントと実コードの乖離監査 (docs-drift-audit) 1 本目は、開発ドキュメント( docs/ 配下、 AGENTS.md 、 CLAUDE.md など)と実コードの乖離を監査するワークフローです。参照先のパスが存在するか、記載コマンドが package.json の scripts と一致するか、feature flag 名やワークフロー名が実在するか、規約の記述が設定ファイルと矛盾していないか。確認できた乖離を修正して 1 本の draft PR にまとめ、乖離がなければ何もせず終了します。 これをわざわざ AI にやらせるのは、ドキュメントが人間向けであると同時に AI 自動化の入力でもあるからです。事例 1 の分類器プロンプトも、事例 3 の @claude が参照する開発ガイドも、鮮度が落ちればそのまま自動化全体の精度劣化につながります。 一方でコード側の変化は速く、手動での追従は現実的に漏れます。週次の監査で、月曜の始業前に修正 PR が用意されている状態を作りました。 プロンプトには「何を直さないか」も同じ分量で書いています。意図的に将来の姿を書いているドキュメントは直さない。リポジトリ外への参照は検証できないので触らない。文体の好みは直さない。そして確信の持てない修正は PR から外すのではなく、PR 本文の「要判断」セクションで曖昧さとレビュー時の確認点を説明させます。 期限切れ feature flag の掃除 (feature-flag-cleanup) MiiTel Phone では、新機能や通話まわりのような壊れたときの影響が大きい変更を feature flag で囲い、OFF のまま main にマージしたうえで、QA や社内テナントでの先行利用を経てから全環境で有効化することがあります。問題が起きたときも、コードを巻き戻す代わりにフラグを OFF に戻すだけで引き返せます。 flag は使い捨てで、安定したら flag と不要になった旧コードを削除する決まりで、放置すると誰も触れない謎のフラグが負債として残るため、各フラグに削除期限も設定しています。 期限切れフラグを Slack に通知して CI を fail させるワークフローはあったのですが、通知されても掃除の PR を作る作業が残るため対応が滞りがちで、一時は 15 件が同日に失効を迎える状況になっていました。 そこで通知の一歩先、削除 PR の作成までを自動化しました。対象は「配布設定が全環境で有効化済みで、かつ期限切れ」のフラグだけです。条件付き配布中のフラグの削除はリリース判断そのものなので、AI には触らせず人間に残します。 フラグの削除は定義を 1 行消して終わりではありません。ガードで分岐していたコードの無条件化に加え、それによって死んだコードの連鎖的な削除までがセットです。この作業パターンをそのままプロンプトに落とし込み、最後に grep でフラグ名の残存ゼロを検証させています。 処理しなかった期限切れフラグも、理由つきで実行ログに残させています。自動化が「何をやらなかったか」を隠すと、カバーされているように見えて漏れる領域ができるためです。 claude-code-action と gh-aw の使い分け どちらも GitHub Actions 上で Claude を動かす仕組みで、どちらでも実装できる仕事は多くあります。ただし対等な選択肢ではありません。 gh-aw は安全装置が組み込まれた専用ツールです。Markdown を書くだけで動く代わりに枠が決まっていて、副作用は safe-outputs で宣言できるものに限られます。外部サービスへの起票のような語彙にない副作用や、AI の出力を自分のコードで検証・分岐してから実行する構成は組めず、イベントに反応する対話的な用途も不得意です。 claude-code-action は自前のワークフローに AI ステップを埋め込む部品なので、トリガーも後段の処理も副作用も自由に設計できる代わりに、権限の最小化・出力の検証・キルスイッチはすべて自分で用意することになります。 タスクが gh-aw の枠に収まるなら gh-aw のほうが少ない記述で安全に済み、はみ出すなら claude-code-action で自分で設計する、という使い分けです。 さいごに 4 つの事例は別々のツールに見えますが、やっていることは 1 つです。判断の型が決まっている作業を AI に寄せ、人間のレビューを本質的な変更に再配分する。そしてそれを安全に成立させているのは、モデルの賢さよりも、判定と実行の分離・人間の最終判断・キルスイッチといった CI 側の設計でした。 この記事が、CI と AI の連携を設計する際の参考になれば幸いです。 参考文献 Agentic Code Review - Addy Osmani 不要なレビューをAIにまかせてAIコーディングの環境改善を加速した - Findy Tech Blog Kent Beck『Tidy First?: A Personal Exercise in Empirical Software Design』 anthropics/claude-code-action GitHub Agentic Workflows (gh-aw) Evergreen - GitHub Next
はじめに # Deno 2.9 リリースおめでとうございます。 Deno 2.9 | Deno Electron 大好きな自分としても気になるのはやはり Deno Desktop です。 Tauri と同様 WebView をバックエンドにする構成と Electron と同様 Chromium ベースの構成を選べるとのことで、これは試すしかないと思いました。 公式ドキュメントは以下にあります。 https://docs.deno.com/runtime/desktop/ --> Caution Deno ブログには以下のように書かれており、2.9 時点ではデスクトップ機能は実験的段階です。 deno desktop is experimental in 2.9. The surface described here is stabilizing and some platform features are still landing. 使ってみる # まずは 2.9 にアップグレードしておきます [1] 。 deno upgrade main.ts に Deno.serve を使って普通にサーバープログラムを書きます。 main.ts Deno.serve(() => new Response( "<!DOCTYPE html><h1>Hello from Deno desktop </h1>", { headers: { "content-type": "text/html" } }, ) ); 同じディレクトリで deno desktop main.ts を実行します。 $ deno desktop main.ts ⚠ deno desktop is experimental and subject to change Check main.ts Compile main.ts to hello.dylib Embedded Files hello.dylib └── main.ts (430B) Files: 1.91KB Metadata: 1.38KB Remote modules: 12B Downloading laufey webview backend for aarch64-apple-darwin (v0.4.0) Download laufey-webview-aarch64-apple-darwin.tar.gz 97.44KiB/97.44KiB Codesigning bundle with identity "-" hello.app/Contents/MacOS/laufey_webview: replacing existing signature hello.app/Contents/MacOS/hello.dylib: replacing existing signature Bundle hello.app 最後の出力で、ルートに hello.app (macOS のアプリ実行ファイル)が生成されており、起動できます(Windows の場合は、hello.exe が生成されます)。 Deno のコンセプト通り、追加のモジュールや設定なし(Out of the box)でデスクトップアプリが生成されました。 Deno Desktop の開発体験 # HMR (Hot Module Replacement) オプション付きで起動することで、ローカルの開発サーバを立ち上げ、コード変更を検知してアプリ内容を即時更新してくれます。 deno desktop --hmr main.ts ⚠ deno desktop is experimental and subject to change Compile main.ts to file:///Users/kondoumh/Library/Caches/deno/desktop/5f4a00908e99d886/hello.dylib Embedded Files hello.dylib └── main.ts (422B) Files: 1.9KB Metadata: 1.38KB Remote modules: 12B Running desktop app with HMR (watching /Users/kondoumh/dev/deno-study/desktop/hello) Runtime loaded successfully from: /Users/kondoumh/Library/Caches/deno/desktop/5f4a00908e99d886/hello.dylib Runtime started [desktop] dylib path: "/Users/kondoumh/Library/Caches/deno/desktop/5f4a00908e99d886/hello.dylib" Listening on http://127.0.0.1:52958/ main.ts のコードを書き換えると、保存後すぐに画面へ反映されます。 --> Information Electron では HMR は標準では利用できず、別途 Forge などで開発サーバーを起動する必要があります。 https://developer.mamezou-tech.com/blogs/2024/01/29/electron-forge-introduction/ UI の ローカル HTTP サービスによる実現 # Electron (Forge など) ではローカルサーバーの利用は開発時が中心で、配布後は file:// でアセットを読む構成が一般的です。 これに対し Deno Desktop は、配布後のバイナリでもローカル HTTP サーバーを内部起動し、空きポートを自動割り当てして UI を描画します。サーバーはプロセス内で閉じており、外部公開はされません。ポート衝突を意識せずに済むのも良い点です。 この「開発時もビルド済みバイナリでも、同じ HTTP 実行モデルで UI を提供する」設計により、 開発時とデプロイ時の挙動に差がない コンテンツはブラウザとデスクトップで同じ動きをする Next.js などのフレームワークがそのままデスクトップアプリの中で動く といったメリットが得られます。 https://docs.deno.com/runtime/desktop/serving/ DevTools の起動 # Electron や Tauri と同様、DevTools によるデバッグが可能です。BrowserWindow を起動し、 openDevtools メソッドを呼ぶだけです。 const win = new Deno.BrowserWindow({ title: "My Deno Desktop App", width: 800, height: 600, }); win.openDevtools(); https://docs.deno.com/runtime/desktop/devtools/ --> Information いまのところ、DevTools のフルサポートはバックエンドを cef にしている時のみです。 以下のように、指定して起動する必要があります。 deno desktop --hmr --backend=cef main.ts バックエンドとフロントエンドの通信(Bindings) # Electron の IPC 通信は render.js と main.js を preload.js 経由でブリッジする必要があり、かなり面倒です。Deno デスクトップでは BrowserWindow にバインドした関数を bindings というグローバルオブジェクトにより簡単に呼び出すことができます。 Deno ランタイムとレンダリングバックエンドはスレッドやプロセスとして動作し、呼び出しはプロセス内チャネルを介して行われます。このサンプル構成ではソケットベースの IPC を直接扱わずに済むため、Electron の ipcMain / ipcRenderer、Tauri の invoke と比べて見通しよく書けるのが利点です。 実際のコードで見てみましょう。 const win = new Deno.BrowserWindow({ title: "Bindingsのテスト", width: 800, height: 600, }); // ========================================== // 1. バックエンド側:フロントから呼ばれる関数を登録 // ========================================== win.bind("getSystemInfo", async (userName) => { console.log(`[Deno側] フロントエンドから呼ばれました! 引数: ${userName}`); // Denoの機能を使ってOSの情報を取得 const denoVersion = Deno.version.deno; const os = Deno.build.os; // 少し重い処理をシミュレート(0.5秒待つ) await new Promise(resolve => setTimeout(resolve, 500)); // フロントエンドに返すデータ(JSON化できるものなら何でもOK) return { message: `こんにちは、${userName}さん!`, os: os, denoVersion: denoVersion }; }); // ========================================== // 2. フロントエンド側:画面のHTMLを返す // ========================================== Deno.serve(() => { const html = ` <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Bindings Test</title> </head> <body> <h1>Deno Desktop Bindings</h1> <button id="btn">システム情報を取得</button> <pre id="result">ここに結果が出ます</pre> <script> // ボタンが押された時の処理 document.querySelector('#btn').addEventListener('click', async () => { const resultArea = document.getElementById('result'); resultArea.textContent = "取得中..."; try { // 💡 bindings を使ってバックエンドの関数を呼び出す const data = await bindings.getSystemInfo("kondoumh"); // 結果を画面に表示 resultArea.textContent = JSON.stringify(data, null, 2); } catch (error) { resultArea.textContent = "error: " + error.message; } }); </script> </body> </html> `; return new Response(html, { headers: { "content-type": "text/html" }, }); }); アプリ画面です。 システム情報を取得 ボタンをクリックするとしばらく呼び出し中になり、結果が表示されます。 結果が表示された状態。 アプリを起動しているバックエンドでは次のようにログが出ています。 [Deno側] フロントエンドから呼ばれました! 引数: kondoumh すごくシンプルです。OS のネイティブ機能と Web UI を簡単に連携できるのがいいですね。 https://docs.deno.com/runtime/desktop/bindings/ メニュー の利用 # アプリケーションメニューの実装。 BrowserWindow の setApplicationMenu メソッド内でメニューオブジェクトを定義して渡します。 BrowserWindow にイベントリスナーを登録してメニューがクリックされた時の振る舞いを実装します。 role などは Electron と同じですね。 win.setApplicationMenu([ { submenu: { label: "File", items: [ { item: { label: "New", id: "new", accelerator: "CmdOrCtrl+N", enabled: true, }, }, { item: { label: "Open…", id: "open", accelerator: "CmdOrCtrl+O", enabled: true, }, }, "separator", { item: { label: "Save", id: "save", accelerator: "CmdOrCtrl+S", enabled: true, }, }, { role: { role: "quit" } }, ], }, }, { submenu: { label: "Edit", items: [ { role: { role: "undo" } }, { role: { role: "redo" } }, "separator", { role: { role: "cut" } }, { role: { role: "copy" } }, { role: { role: "paste" } }, ], }, }, ]); win.addEventListener("menuclick", (e) => { const detail = (e as CustomEvent).detail; switch (detail.id) { case "new": console.log("New clicked"); break; case "open": console.log("Open clicked"); break; case "save": console.log("Save clicked"); break; } }); コンテキストメニューの実装。 Deno.MenuItem の配列を作成して、BrowserWindow の showContextMenu に座標とともに渡します。 const contextMenu: Deno.MenuItem[] = [ { item: { label: "Copy", id: "copy", enabled: true } }, { item: { label: "Paste", id: "paste", enabled: true } }, "separator", { item: { label: "Properties…", id: "props", enabled: true } }, ]; // Trigger from a right-click. The webview may not forward the browser // `contextmenu` event, so handle the secondary mouse button on the window. win.addEventListener("mousedown", (e) => { if (e.button === 2) { win.showContextMenu(e.clientX, e.clientY, contextMenu); } }); win.addEventListener("contextmenuclick", (e) => { if (e.detail.id === "copy") { console.log("Copy clicked"); } if (e.detail.id === "paste") { console.log("Paste clicked"); } if (e.detail.id === "props") { console.log("Properties clicked"); } }); --> Information ここではメニューのクリックイベントでログを出力していますが、ログ自体はアプリを起動しているターミナル側に出ますのでご注意ください。 https://docs.deno.com/runtime/desktop/menus/ フレームワークを利用した開発 # Deno.serve() を利用したサンプルを見てきましたが、Deno デスクトップでは、以下のフレームワークとともに利用可能です。これらのプロジェクトのディレクトリで deno desktop を起動すると、フレームワークを自動検出してアプリを構成します。多くのモダンフレームワークがサポートされています。 Next.js Astro Fresh Remix Nuxt SvelteKit SolidStart TanStack Start Vite ローカルで動いてるのに SSR を使うというのがなんとも不思議な感じですが、ちゃんと動いてセキュアであればヨシ!という感じでしょうか。 https://docs.deno.com/examples/next_tutorial/ Next.js のアプリを作成します。 deno run -A npm:create-next-app@latest 作成したプロジェクトディレクトリへ移動して実行します。 cd <project-dir> deno desktop -A Next.js のアプリが、外部サーバーなしでまるっとデスクトップ内で動いてるのは不思議な感じです。 https://docs.deno.com/runtime/desktop/frameworks/ バックエンドの選択について # デスクトップアプリは配布するバイナリのサイズも重要です。小さいに越したことはありません。 Electron は Chromium を内包するため、インストールされたバイナリサイズは300MBぐらいの大きさになったりします。 Deno Desktop の場合、OS にプリインストールされている WebView を使えば70MB程度です。CEF(Chromium) だとやはり300MB程度になります。 OS 依存の WebView だと、Windows と Mac で微妙に CSS や JS の挙動が変わるクロスブラウザ問題が発生するため、そのための対応やテストも必要になります。機能が少ないうちは WebView でもいいかもしれませんが、機能が増えてくるとテストの手間も何倍にもなっていきます。 Deno Desktop の場合、最初は軽量な WebView でスタートし、クロスブラウザが重荷になってきたら、ちょっと配布サイズは大きくなるけど、CEF にスイッチできるのがいいかなと思います。 https://docs.deno.com/runtime/desktop/backends/ --> Information Tauri だとこの辺、Servo ベースの自前 WebView プロジェクト Verso 待ちですが、Deno は既存の Chromium を選択可能にしているあたり、現時点での割り切りが感じられますね。 Electron との比較 # 既存 Web アプリをデスクトップ化したいユースケースでは、Deno Desktop はかなり有力です。 一方で、Electron の WebContentsView のような高度なマルチビュー構成を前提にしている場合は、現時点では Electron のほうが適しています。たとえば VS Code や Figma のように、複数ビューを細かく制御するタイプのアプリです。 ざっくり整理すると次のような感触です。 単一ウィンドウ中心 + 既存 Web 資産活用: Deno Desktop はかなり良い 複雑なウィンドウ/ビュー管理: Electron が依然強い --> Information マルチビュー構成の対応の弱さは Tauri も同様です。 https://developer.mamezou-tech.com/blogs/2025/12/01/porting-an-electron-app-to-tauri2/ Electron の WebContensView 構成については以下の記事をご参照ください。 /blogs/2024/08/28/electron-webcontentsview-app-structure/ https://docs.deno.com/runtime/desktop/comparison/ さいごに # 以上、Deno Desktop 機能を一通り試しました。 Out of the box でここまでデスクトップ開発体験が整っているのは率直に驚きです。タスクトレイやメニュー、Bindings など、アプリらしさを出すための API が最初から揃っているのも好印象でした。 Tauri と違ってアプリ側をすべて TypeScript で書けるため、既存 Web アプリをベースに「メニューやタスクトレイを追加し、OS 機能と連携する」用途ではかなり相性がよいと感じます。最小構成なら、デスクトップアプリ化自体は1時間もかからないはずです。 --> Information Tauri も JS の API を生やして、Rust 知らない勢を取り込もうとしてはいます。 Deno のキラー機能になる可能性もありますね。experimental から安定版へ向けて、今後の熟成がとても楽しみな機能です。 2026年7月6日現在の最新は 2.9.1 です。 ↩︎
はじめに 私が開発ツールに求めることをZedは満たしていた すぐに開けること コードが追いやすいこと 複数画面を上下左右に開けること ディレクトリがツリーで開けること Git関連機能にアクセスしやすいこと VSCode、Ghostty、Zedを比較する Zedの使用感 Zedの微妙なところ ACP経由のエージェント体験はCLIより遅く感じる ファイルパスクリックで開けない VSCode拡張に依存している人は移行しづらい まとめ はじめに 4月にラクスに入社しました。kazuki kanekoです。 研修を受けつつ、開発環境を立ち上げようとVSCodeをセットアップしていました。 すると、AIエージェント開発課のメンバーから「Zedいいですよ」とおすすめしていただきました。 これが、私がZedを知ったきっかけでした。 今は紆余曲折ありながら、Zedに落ち着いています。 以前はVSCodeを中心に使っていて、困っていたわけではありませんでした。 ただ、Zedを使い始めてから、 「VSCodeの動作って、実はけっこう重かったんだな」 ということに気がつきました。 また、Ghosttyについてもおすすめしていただいていたので、一時期はZedとGhosttyを同時に使っていました。 ただ、今は割とZedだけで完結している状態になってきています。 そこで今回は、VSCode、Ghostty、Zedを使ってきたうえで、なぜ今Zedに落ち着いているのかを書いていきます。 まず、前提としてZedについて整理します。 Zedとは、高速性、コラボレーション、AIとの連携を重視して作られているコードエディタです。 公式サイトでは、Zedは「speed」と「humans and AIとの collaboration」のために作られたミニマルなコードエディタとして説明されています。 macOS、Linux、Windowsで利用でき、Rustで一から書かれていて、複数CPUコアやGPUを活用する設計になっています。 Zedを作っているのは、AtomやTree-sitterに関わってきたチームです。 AtomやElectron、Tree-sitterなどの開発経験の延長線上にあるプロダクトとして、Zedが作られています。 Zedの特徴は、単に「軽いエディタ」というだけではありません。 Rust製で、独自UIフレームワークであるGPUIを使い、GPUを活用するような設計になっています。 Zed 1.0の記事では、AtomのようにWeb技術の上に作るのではなく、GPU上のshaderにデータを渡すような、いわばゲームのような作り方を選んだと説明されています。 ソース: https://zed.dev/ つまりZedは、VSCodeのようなGUIエディタの便利さを持ちつつ、より軽く、より高速に動くことをかなり強く意識して作られたエディタだよ、という感じです。 私が開発ツールに求めることをZedは満たしていた 私が開発ツールに求めるのは、以下の5つ すぐに開けること コードが追いやすいこと 複数画面を上下左右に開けること ディレクトリがツリーで開けること Git関連機能にアクセスしやすいこと 順に説明します。 すぐに開けること まず、すぐに開けることです。 GitHubを見ていて、 「このファイル、エディタでちゃんと見たいな」 と思うことがあります。 そういう時にVSCodeで開くと、体感で5秒から6秒くらいかかることがありました。 もちろん数秒なので、めちゃくちゃ遅いというほどではありません。 ただ、開発中の「ちょっと見たい」場面では、この数秒が地味に気になります。 メモ帳みたいにパッと開いて、すぐ見られたらいいのになと思っていました。 Zedはこの「パッと開ける感じ」がかなり良いです。 エディタを開くまでの心理的な重さが少なくて、ちょっと確認したいときにも気軽に開けます。 この軽さは、毎日使っているとかなり効いてきます。 コードが追いやすいこと 次に、コードが追いやすいことです。 コードを読んでいる時には、いろいろな情報を行き来します。 たとえば、 関数の定義元に移動する 型定義を見る クラスやinterfaceの中身を見る 呼び出し元を確認する ファイルをまたいで処理の流れを追う といったことをよくやります。 このときに、定義元にすぐ移動できたり、型ヒントが見られたり、クラス名などにカーソルを当てたときに関連箇所が薄くマーカーされて見やすかったりすると、かなり助かります。 Zedは、このようなIDEの機能を提供してくれています。VSCodeほど充実してませんが、Ghosttyよりは充実しています。 複数画面を上下左右に開けること 複数画面を上下左右に開けることも重要です。 Ghosttyのようなターミナルは、分割の体験がかなり良いです。 上下左右に画面分割できるので、横3、縦2の6画面みたいな形でも開発できます。 Zedでもエディタを上下左右に分割できます。 そのため、Ghosttyのような画面分割の気持ちよさをZedは提供してくれています。 ディレクトリがツリーで開けること ディレクトリをツリーで開けることも大事です。 私は、ディレクトリ構造を見ながら開発したい派です。 たとえば、 controller service repository domain schema migration test のような構成を見るだけでも、そのプロジェクトがどういう責務分割をしているのかがわかるので、どこを変更すればいいのかなどがわかりやすいです。 ZedにはProject Panelがあり、ディレクトリツリーを見ながら開発できます。 Git関連機能にアクセスしやすいこと Git関連機能にアクセスしやすいことも、自分にとってはかなり重要です。 正直、毎回ターミナルで git status を打ったり、commitをコマンドでやったりするのは少しめんどくさいです。 もちろんCLIでやった方が速い場面もあります。 ただ、変更ファイルを一覧で見たり、diffを確認したり、stageする変更を選んだりする作業は、GUIで見たいことが多いです。 VSCodeのGit Graphみたいな感じで、ブランチや履歴を見たり、変更内容を確認したりできるとかなり楽です。 ZedにはGit PanelやProject Diffがあり、変更ファイルを確認したり、diffを見たり、stage / unstageしたりできます。 日常的なGit操作であれば、かなりZed上で確認できます。 VSCode、Ghostty、Zedを比較する ここで、VSCode、Ghostty、Zedを比較してみます。 ただし、Ghosttyはエディタではなくターミナルエミュレータです。 なので、厳密にはVSCodeやZedと同じ種類のツールではありません。 ここでは、以下の3つの開発スタイルとして比較します。 VSCodeのような全部入りGUIエディタ GhosttyでCLIツールを組み合わせてエディタっぽく使うスタイル Zedのような軽量GUIエディタ 観点 VSCode Ghostty Zed 起動の軽さ 重く感じることがある 軽い 軽い コードジャンプ 強い 工夫が必要 強い ファイルツリー ある 工夫が必要 ある 画面分割 できる かなり強い 強い Git UI かなり強い 工夫が必要 強い AIエージェント 拡張+CLIエージェント CLIエージェント前提 ACP+CLIエージェント 自分の印象 全部入りだけど、不要な機能も多く重い 軽いが、使いこなすのに工夫が必要 軽さと機能のバランスが良い VSCodeでも普通に困らないなと思います。 拡張機能も豊富で、Gitも見やすく、デバッグやリモート開発なども含めると、かなり全部入りの開発環境です。 なので、VSCodeが悪いという話ではありません。 ただ、自分の使い方では、少し重く感じる場面がありました。 Ghostty中心のCLI開発は、軽さと自由度がかなり良いです。 ターミナル分割もしやすく、AIエージェントとの相性が一番いいと思いました。 AIエージェントが作業完了すると通知が飛ぶのがいいですね。 ただ、私の場合は、コードを追うときに 定義ジャンプ、ファイルツリー、Git diffなどを自然に見たい場面が多かったです。 比較すると個人的にはZedは、そのVSCodeとGhosttyの中間かなと思っています。 Zedの使用感 ここからは、実際にZedの画面を開きながら、使用感を共有できればと思います。 Zedを立ち上げると以下のような画面が開きます。 初めは何にもないです。VSCodeは初回から何やらたくさん出てきますよね。 何も無さすぎて、初めは戸惑うのですが、 画面底の帯部分(赤で囲った部分)に配置されているアイコンをクリックすると、ターミナルだったり、ファイルツリーを開くことができます。 VSCodeっぽく使いたい場合は、 ファイル、git関連機能、ターミナル、Copilotみたいな感じで開くとそれっぽく使えます。 やろうと思えば、Ghostty風の配置もできます。 Zedでこの配置をするメリットはあまりないので、それならGhosttyがいいかなとか思いますが、一応できます。 私の配置は以下の様な感じです。 エージェントを並列で使いたいので、ターミナルを3枚、git関連情報を右側で見ながら、ファイルも開きながらという感じで開発しています。 ブログ形式でZedの使用感を伝えるのは、難しいなと思いつつ、画面構成の自由度の高さだったり、Copilot、ファイルツリー、git関連機能など、ほしい機能は最初から入っていて、使いやすそうだなというのが伝われば幸いです! 画像では伝わらないですが、今の画面構成を変更したりする操作がサックサクで動く感じです。 Zedの微妙なところ ここまでZedの良いところを書いてきましたが、もちろん完璧ではありません。 特に、AIエージェントまわりはまだ、CopilotやCLIがいいなと感じることはあります。 ACP経由のエージェント体験はCLIより遅く感じる ZedはACP経由で外部エージェントと連携できます。 これは組み込みのAIエージェント機能に依存しないという点で便利なのですが、自分の体感では、ACP経由のエージェント体験はCLIより少し遅く感じることがあります。 CLIでエージェントを使っていると、入力してすぐ反応が返ってくる感じがあります。 一方で、ZedのACP経由だと、UIを挟むぶんレスポンスの出方が少し遅く感じることがあります。 これは設計上ある程度仕方ないのかもしれません。 ただ、CLIの即応感に慣れていると、ここは少し気になります。 ファイルパスクリックで開けない AIエージェントが生成した文章の中に、 src/foo/bar.ts のようなファイルパスが含まれることがあります。 このとき、そのファイルパスをクリックしてそのままファイルを開けると便利です。 VSCodeだと、このあたりの導線が自然に感じることがあります。 一方で、Zedでは、AIエージェント出力内のファイルパスは、ファイルパスのリンクになっていないので、ただの文字列です。 VSCode拡張に依存している人は移行しづらい これはZedというより、VSCodeから別エディタへ移るとき全般の話でもあります。 VSCodeは拡張機能がかなり強いです。 たとえば、 Git関連 Docker Dev Containers Remote SSH デバッグ 各種言語サポート GitLens Copilot など、エディタというより開発プラットフォームに近いです。 なので、VSCode拡張に強く依存している人が、いきなりZedへ全部移行するのは難しいと思います。 私の場合は、VSCodeのすべてが必要だったわけではありません。 だからZedのバランスが合っていました。 まとめ Zedは完璧なエディタではありません。 VSCodeほど何でも揃っているわけではありません。 GhosttyほどCLI開発体験が良いわけではありません。 ただ、色々なバランスを考えると今の私にはZedがかなり合っているという結論になりました。 軽く開ける。 コードを追いやすい。 ファイルツリーがある。 画面分割できる。 Git機能にアクセスしやすい VSCodeに慣れているが、でも、もう少し軽く使いたいという方 Ghosttyの軽さが好きだが、手軽にコードジャンプやGit UIが使いたい方 Zedいいですよ。
動画
該当するコンテンツが見つかりませんでした









