(これは自身のつぶやきを元に生成された文章です)
日々の開発業務や個人プロジェクトの中で、「この作業、毎回手でやるのは面倒だな」「この情報をサッと確認できるコマンドがあればいいのに」と感じる瞬間は少なくないでしょう。そうした小さな課題意識から、私たちは往々にして自作のコマンドやスクリプト作りに乗り出します。本稿では、そうした「ちょっとしたツール」が、どのような思考の変遷を経て形を変え、また、現代の技術がそのプロセスにどのような影響を与えるのかを、ある開発者の内省を辿る形で探求します。
序章:あの頃、僕らはシェルと戯れていた 📜
かつて、そうした「ちょっとした自動化」の欲求を満たす主要な手段は、シェルスクリプトでした。`grep`、`awk`、`sed`、`find` といったUnixコマンドをパイプで繋ぎ、時には複雑なロジックを組み上げて目的を達成する。一行で書けるものは「ワンライナー」として重宝され、その簡潔さ、即時性に魅力を感じたものです。
このワンライナーという形式は、特にGitHubのIssueやチーム内のチャットで「こうすればできるよ」と共有する際に非常に便利でした。実行環境の差異をあまり気にせず、コピペしてすぐに試せる手軽さは、コミュニケーションを円滑にする一面も持っていました。
しかし、シェルスクリプトでできることには限界も感じ始めていました。少し複雑な条件分岐やデータ構造を扱おうとすると途端に記述量が増え、可読性も保守性も低下します。エラー処理も煩雑になりがちで、再利用可能な部品として切り出すのも一手間です。「もっとマシな方法はないものか」という思いが、自然と湧き上がってくるのでした。
第一章:新しい風、Denoとの出会い 🦕
そんな折、新しいランタイム環境としてDenoが登場しました。TypeScriptを標準でサポートし、デフォルトでセキュアな権限モデルを持ち、URL経由で直接スクリプトを実行・インストールできる (`deno install`) 手軽さは、非常に魅力的に映りました。
これまでシェルスクリプトで四苦八苦しながら書いていたような処理、例えば `git` のコミット履歴から最新の更新日時を取得し、特定のディレクトリ構造を `tree` コマンドのように表示するといった類のコマンドを、Denoで書けばもっと楽に、そして堅牢に作れるのではないか。そんな期待感が湧いてきました。
Pythonももちろん有力な候補ではありました。豊富なライブラリとこなれたエコシステムは強力です。しかし、Denoの持つ権限を明示的に絞れるというセキュリティ思想や、`deno install` による配布・インストールの手軽さは、小さなコマンドラインツールを作る上でのフットワークの軽さに繋がりそうだと感じました。「まずはDenoで書いてみよう」という選択は、ごく自然な流れだったのです。
## 第二章:純粋さを求めて ― 1ファイルという美学 📄
Denoを使い始めると、その快適さに気づくと同時に、ある種の「こだわり」が顔を出し始めました。それは、「できる限り1ファイルで完結させたい」という強い思いです。
ここで言う「こういうやつ」とは、まさに前述したような、特定の目的を持った比較的小さなコマンドのことです。汎用的なライブラリとして開発するわけでもなく、壮大なアプリケーションを目指すのでもない。あくまで「使い捨て」に近い感覚の、しかし手元にあると便利なスクリプト群です。
なぜ1ファイルにこだわるのか。それは、シンプルさを保ちたいからです。ファイルが分割されると、それだけで管理の対象が増えます。小さなツールであればあるほど、全体像を一目で把握できることの価値は大きいのです。そして、このこだわりは依存関係の扱いにも及びます。依存するパッケージは個別に、そして明示的に指定したい。他のライブラリが間接的に引き込んでくる依存(いわゆる推移的依存)は、できる限り避けたいと感じるようになりました。余分な依存関係が紛れ込んだ瞬間に、まるで異物が混入したかのような不快感、言うなれば「ウッ」となる感覚を覚えるほどでした。
この段階では、`deno.json` のようなプロジェクト設定ファイルすら作りたくない、という気持ちが強くありました。スクリプトファイルそのものが全てであり、それ以外の付随物はない、という状態が理想だったのです。
第三章:ツールの成長と、言語選択の岐路 🗺️
しかし、1ファイル主義にも限界は訪れます。スクリプトが少しずつ成長し、機能が増えてくると、どうしても1ファイルでは見通しが悪くなってきます。もっと体系的に整理したい、テストを書きたい、となると、ファイル分割やプロジェクト構造を意識せざるを得ません。
この段階で、改めて言語やツールの選択について考えることになります。Denoのままでももちろん進められますが、例えば「複数ファイルで構成される、もう少し本格的なツール」という段階になった時、Go言語が魅力的な選択肢として浮上してきました。Goの持つ、シングルバイナリとして配布できる手軽さ、比較的シンプルな依存管理 (`go.mod`)、そして `npm` エコシステムのような複雑な依存の連鎖に悩まされにくいという点は、大きなメリットに感じられました。
過去にはOCamlのような関数型言語に触手を伸ばそうとした時期もありましたが、この種のコマンドラインツールにおいては、やはり「インストールの手軽さ」が重要なファクターとなります。開発者自身が使うだけでなく、他の人に「これ便利だよ」と勧める際にも、導入のハードルは低い方が望ましい。その点で、GoやDenoは優位性があると感じました。
かつては、一度選んだ言語でじっくりとツールを成長させていくのが良いと考えていましたが、最近では「状況に応じて別の言語に書き換える」という選択肢も、ごく普通に考慮に入れるようになりました。それぞれの言語やエコシステムには得手不得手があり、ツールの成長フェーズや目的に応じて最適なものを選び直す柔軟性が、現代の開発ではより重要になっているのかもしれません。
第四章:生成AIという追い風 ― 開発スタイルのパラダイムシフト 🤖
この「別言語への書き換え」に対する心理的なハードルが大きく下がった背景には、生成AIの進化があります。以前は、異なる言語への移植はそれなりの時間と労力を要する作業でしたが、今ではAIに既存のコードと移植先の言語を伝えれば、かなりの精度で翻訳してくれるようになりました。Pythonで書かれたロジックをDeno/TypeScriptへ、あるいはシェルスクリプトをGoへと変換する作業が、劇的に効率化されたのです。
さらに、開発の初期段階においても生成AIは大きな影響を与えています。かつてワンライナーで済ませていたような処理や、頭の中にある「こんな感じのスクリプトが欲しい」という雑な仕様をAIに伝えるだけで、DenoやPythonの1ファイルスクリプトの雛形をものの数分で生成してくれるようになりました。冒頭で触れた「gitの更新日時とtreeを組み合わせたコマンド」のようなものも、まさにAIに大まかな要件を伝えて生成させた、という経緯があります。
この変化は、「ワンライナーで試行錯誤する」フェーズから「とりあえず動く1ファイルのスクリプトを作る」フェーズへの移行を格段に早めました。アイデアを形にするまでの時間が短縮され、より本質的なロジックの検討や、ツールの使い勝手の向上に時間を割けるようになったのです。
第五章:依存との終わりなき戦い、そして共存 🔗
ツールの開発において、避けては通れないのが依存関係の管理です。特にJavaScript/TypeScriptのエコシステムでは、`npm` を介して多くのパッケージが利用可能ですが、その一方で、意図せず依存関係が肥大化しやすいという側面も持っています。ちょっとした処理やチュートリアルのコード例を試しただけで、`package-lock.json` が数千行、時には1万行を超えるような状況に遭遇した経験のある方もいるのではないでしょうか。
「依存関係は `lock` ファイルで固定すれば良い」というのは正論です。しかし、そもそもそんな巨大な依存を持ち込みたくない、というのが本音です。依存が増えれば増えるほど、セキュリティリスクの管理は複雑になり、ビルド時間や成果物のサイズにも影響が出ます。依存を小さく保つ努力は重要ですが、そのためにはエコシステムそのものに対する深い知識が求められることもあり、その学習コストに躊躇することもあります。
Denoの `jsr:` スキームによるバージョン管理された標準モジュールや、URLインポートによる依存の明示性は、この問題に対する一つのアプローチと言えるでしょう。また、Goのモジュールシステムも、比較的シンプルで予測可能な依存管理を提供してくれます。こうした「依存の爆発」を避けやすい仕組みは、ツール選定において重要な判断基準の一つとなります。
第六章:自作コマンドの進化の階段 ― ある開発者のモデルケース 🪜
ここまでの思考の変遷を踏まえると、自作コマンドやスクリプトが辿る進化のステップは、以下のように整理できるかもしれません。
1. *ターミナル上でワンライナー*:
目的: 即時性、ちょっとした確認、アイデアの検証。
特徴: `grep`, `awk`, `git` コマンドなどをパイプで繋ぐ。GitHub Issueなどで共有しやすい。
AI活用: 要件を伝えてワンライナーを生成。
2. *1ファイルのコマンドを作成*:
目的: ワンライナーでは複雑すぎる処理のスクリプト化、定型作業の自動化。
特徴: DenoやPythonなどで記述。設定ファイル (`deno.json`, `package.json` など) は作らない。依存は最小限かつ明示的。
AI活用: 大まかな仕様から1ファイルスクリプトを生成。
3. *1つのツールとして作成*:
目的: より複雑な機能、再利用性、保守性の向上。
特徴: ファイル分割、ディレクトリ構造の導入。設定ファイル (`go.mod`, `deno.json` など) を解禁。テストコードの整備。
AI活用: プロジェクト構造の提案、 boilerplateコードの生成、既存コードのリファクタリング支援。
4. *何らかのリポジトリに移動*:
目的: 他者との共有、バージョン管理、継続的な開発。
特徴: GitHubなどのリポジトリで公開。`deno install` や `go install`、あるいはバイナリ配布など、手軽なインストール手順を提供。
AI活用: READMEやドキュメントの雛形生成、リリースノート作成支援。
5. *(WebやGUIで広く公開)*:
これは今回のスコープでは「まずやらない」とされていますが、ツールによっては次のステップとして考えられるでしょう。
この各ステップにおいて、生成AIは単なるコード生成器としてだけでなく、設計の相談相手や、ドキュメント作成のアシスタントとしても機能し得るポテンシャルを秘めています。
終章:これからも続く、道具作りとの旅路 🛠️
小さなスクリプトから始まる道具作りの旅は、尽きることがありません。その時々の課題、利用可能な技術、そして自身のスキルや思想の変化に応じて、最適なアプローチは変わり続けます。
Unix哲学が教えてくれる「小さく、鋭く、うまく協調する道具を作る」という精神は、今もなお多くの開発者にとって指針となるでしょう。その精神を胸に刻みつつも、DenoやGoのような現代的なツール、そして生成AIのような新しい力を柔軟に取り入れていく。そうすることで、私たちはより快適に、より創造的に、日々の課題解決に取り組むことができるはずです。
結局のところ、大切なのは、自分にとって最も心地よく、生産性の高い開発スタイルを見つけ、それを追求し続けることなのかもしれません。この長い内省が、読者の皆様自身の道具作りにおける何らかのヒントとなれば幸いです。
メイキング
この文章は以下のx/twitterの連投から生成されました。
https://x.com/podhmo/status/1935090983148077317
またこの連投を用いた以下のgrokとの対話も参考にしてます。
https://x.com/i/grok/share/eAZK41PZsZnOPKYW3ZnSSEVHz
変換の不自然さ
入力を参照してテキストとして出力させるというタスクを行わせたのですが、参照先への言及を避けるこれは特定の文脈に依存せず広く分かりやすい説明を行うために加えていた制約なのですが、これが災いしているような気がします。どうも特定の何かを参照しての文章表現にまだまだなっているようです。
以下のようなプロンプトで作成しました。
あなたは思慮深い執筆者でそれなりの開発者としての経験を持っています。どちらかといえばunix哲学に親近感を持っていた層です。web開発者としての側面も持っていますがどちらかといえばバックエンドの方がメインのバックグラウンドです。これまでの思考の内容を追体験させるような文章がほしいです。わざと短くまとめようとせずに丁寧に長さを気にせずテキストとして出力してください。
一つの記事にまとめられない場合は同様の想定で2つ目の記事や3つ目の記事と複数の記事に分割して同様の形式で出力して構いません。
----
記事のフォーマットについて
- 記事は#1つのタイトル名からはじめてください
- 記事は幾分かの章で分割してください
- 章タイトルには絵文字を1つ加えてください
- 記事はmarkdownフォーマットを念頭に入れて出力してください
- *2個の太字による強調は禁止します
- 対象読者と狙いをFront matter で簡潔に冒頭につけてください
- 文章は長くなっても構いません。丁寧に記述してください。
## 重要なこと
- 読者は私およびあなたの存在を知りません。伝える必要もありません。文中で参照しないでください。
- 読者は投稿元のSNSの投稿を知りません。それについて触れずに表現してください。
それではお願いします。
この後に以下のようなプロンプトで文章を再度生成してもらいたくなりました。
惜しいですね。文章を見返してください。おそらく何らかのテキストを参照しての言及が多く見られる文章になってしまっています。コレを全て説明する形で書き換えてください。つまり参照先の文章とそれを見て書いた私という関係をなくしてください。一つの文章で簡潔するように説明を加えて表現を整えてください。
あとワンライナーからdenoへの段落が少し急です。昔も1ファイルのシェルスクリプトやpythonのスクリプトは書いていました。
もう一回プロンプトを当てたもの。gemini-nizeされてしまってる気がする。
https://gist.github.com/podhmo/8ddb4df0ee065725f5055f9376878a9e
違和感の感想戦(対象は1回目のこの記事に挙げられてる文章)。悪くないプロンプトなきがする。まさに「こういうやつ」がわりと唐突。
https://gist.github.com/podhmo/713b84bffc21582082a2ba3f000f3466