[{"data":1,"prerenderedAt":63},["ShallowReactive",2],{"article-0bs4f42te":3,"prev-article-0bs4f42te":33,"next-article-0bs4f42te":45},{"contents":4,"totalCount":31,"offset":32,"limit":31},[5],{"id":6,"createdAt":7,"updatedAt":8,"publishedAt":8,"revisedAt":8,"title":9,"content":10,"tags":11,"is_no_index":30},"0bs4f42te","2026-04-29T00:46:22.148Z","2026-04-29T01:01:31.544Z","向き不向きの前に、まず手を動かすべきでは","\u003Ch1 id=\"h8d027c8ed3\">はじめに\u003C/h1>\u003Cp>どうも、わたしです。\u003C/p>\u003Cp>最近、というかここ数年、タイムラインを眺めていると、「プログラミングを始めました！」「LLMでアプリを作りました！」みたいな投稿を頻繁に目にするようになりました。それ自体はとても素晴らしいことだと思いますし、新しく何かを始めようとしている人を否定するつもりは毛頭ありません。\u003C/p>\u003Cp>ただ、その後の様子を眺めていると、どうにも釈然としない気持ちになることが多くあります。\u003C/p>\u003Cp>｢動きません｣｢エラーが出ます｣｢分かりません｣｢何もしていないのに壊れました｣、そういう質問が、携わるコミュニティで、Twitterで、Discordで、GitHubで、毎日のように流れてきます。それ自体は別に良いんです。分からないことを聞くのも大事だと思いますから。\u003C/p>\u003Cp>問題は、その質問に何の情報も含まれていないことや、そもそも自分の手元で起きていることを、自分で確認すらしていないことです。\u003C/p>\u003Cp>タイトルは少しばかり煽情的かもしれませんが、自分自身も普段から書いては消し、書いては消しを繰り返している身として、向いてないと切り捨ててしまう前に、もう少し言いたいことがあります。そんな話を、つらつらと書き散らしていきたいなと。\u003C/p>\u003Ch1 id=\"h833a42ac8c\">エラーメッセージは目の前にあるのに何故か読まれません\u003C/h1>\u003Cp>世の中には、エラーが出た瞬間に、画面の前で固まる方々が一定数いらっしゃいます。\u003C/p>\u003Cp>固まったまま、5分、10分、酷い時には30分。エラーメッセージは画面の真ん中にずっと表示されているのですが、その本文を読んでいる気配がありません。読んでいたら、その後の口から｢分かりません｣という言葉が出てくるはずがないからです。\u003C/p>\u003Cp>\u003Ccode>Connection refused\u003C/code>, \u003Ccode>Permission denied\u003C/code>, \u003Ccode>Module not found\u003C/code>, \u003Ccode>Unexpected token\u003C/code>.\u003C/p>\u003Cp>これらは何かの呪文ではなく、コンピュータがあなたに対して、丁寧に問題を教えてくれている、とてもありがたい案内です。\u003C/p>\u003Cp>エラーメッセージを読むというのは、別に難しいことではありません。\u003Ccode>Connection refused 127.0.0.1:5432\u003C/code>と書いてあれば、｢ローカルの5432ポート宛接続が拒否された｣と読めばいいだけですし、5432はPostgreSQLのデフォルトポートなのでPostgreSQLが起動していないか、接続情報が間違っているか、そのあたりに当たりがつきます。1分もあれば原因に辿り着ける問題に何を無駄な時間を費やしているのでしょう。\u003C/p>\u003Cp>｢英語が読めません｣と言う方もいらっしゃいますが、refusedをリフューズドと読み下せれば、それだけで意味は推測できますし、どうしても分からなければ翻訳ツールに通せば済む話です。あなたが普段スマホで観ている海外動画の字幕、あれだって機械翻訳でしょ？同じです。\u003C/p>\u003Cp>そして、信じられないかもしれませんが、エラーメッセージを読まない方々は、何度プログラムを実行しても出続けるエラーに対して、毎回最初から驚きます。｢またこのエラーが出ました｣と言うのですが、そら当然です。書かれていることに対処しない限り、何回実行しても同じエラーが出ますし、コンピュータは、あなたの祈りとかには反応しません。中身を変えず同一の試行を繰り返した挙句、違う結果を望むのはとても愚かなことです。小学生ですらやりません。\u003C/p>\u003Cp>エラーが出るというのは本来ありがたいことです。間違っている箇所を教えてくれるものですから、感謝しながら読んで直せば済みます。逆に、エラーも出ずに動いている、けれど結果が間違っている、という状況の方が、遥かに恐ろしく感じます。エラーは敵ではありません。\u003C/p>\u003Ch1 id=\"h7437e44bed\">｢こう動くはずなんですけど｣って、それあなたの感想ですよね\u003C/h1>\u003Cp>これも本当によく見かけるパターンで、本当に心底嫌いです。\u003C/p>\u003Cp>曰く、｢私は、ここはこういう風に動くはずだと思ってるんですけど、動かないんですよ｣と。\u003C/p>\u003Cp>そうですか。それで、あなたの思いと実際の挙動、どちらが正しいと思います？\u003C/p>\u003Cp>答えは決まっています。動いていないのなら、あなたの思い10割10分が間違っています。\u003C/p>\u003Cp>ところが、自分の認識を譲らない方が一定数居るようで、｢いや、ここはこう動くはずなんですよ｣だとか、｢でも、こういう風に書いたんですから、こう動くはずじゃないですか｣みたいなことを平然と言ってのけます。すごい自信家ですよね、感心します。でも実は違うんです。あなたの認識と、コンピュータの実際の挙動がズレているんです。修正すべきは、コードでも、ライブラリでも、コンピュータでもなく、傲慢なあなた自身です。\u003C/p>\u003Cp>思いをベースに議論しても、コンピュータは1nmも動きませんし、コードを動かすのは、そこに書かれている事実です。実際に出力された値、実際に走った行、実際に発生したエラー等々。それらだけが、原因の手がかりであって、あなたの思いは、原因の特定に役立ちません。\u003C/p>\u003Cp>デバッグの第一歩は、自分の思いを一旦脇に置くことでしょう。｢ここは絶対こう動いているはず｣と思い込んでいる箇所こそ、\u003Ccode>print\u003C/code>でも \u003Ccode>console.log\u003C/code> でも \u003Ccode>dbg!\u003C/code> でも構わないので、実際の値を出力して確認してください。殆どの場合、あなたの思い込みは外れています。本当に、本当に、外れています。騙されたと思って私を信じてください。\u003C/p>\u003Cp>ちなみに、デバッガを使ったことがない方も、かなりいらっしゃるようですが、VSCodeでもJetBrains系IDEでもブラウザのDevToolsでも、ブレークポイントを置いて、ステップ実行して、変数の中身を覗く、ということができます。これを覚えるだけで、デバッグの効率が劇的に変わります。printデバッグも悪くはないのですが、ちゃんとしたデバッガを使えるようになっておくと、人生の何割かを取り戻せます。暇なら覚えておきましょう。\u003C/p>\u003Ch1 id=\"h471a83884c\">詰まったら考えるより先に手を動かそう\u003C/h1>\u003Cp>エラーメッセージを読みました。それでも原因が分かりません。さて、どうしましょうか。\u003C/p>\u003Cp>ここで多くの方が、画面の前で固まる作業に戻ります。けれど、それでは何も解決しません。考え込んでも、コンピュータは何も教えてくれません。\u003C/p>\u003Cp>ここでやるべき事は、手を動かして状況を観測することだけです。\u003C/p>\u003Cp>怪しい箇所の値を出力する。リクエストの中身をダンプする。条件分岐の手前で実行を止めて状態を覗く。データベースに直接クエリを投げて状態を確認する、みたいな。動いているはずと思っている部分が、本当に思った通りに動いているか、ひとつずつ確かめていく必要があります。\u003C/p>\u003Cp>考えてから手を動かすのではなく、手を動かしながら考えるべきです。これは、おそらくベテランの方も全く同じことをしています。違うのは、観測の精度と、観測する場所を絞り込む速度だけです。経験を積むと最初に怪しいと睨んだ箇所が当たる確率が上がっていく、というだけの話に思えます。\u003C/p>\u003Cp>さて、｢闇雲に試して動いたら、それで本当に直ったと言えるんですか？｣と言われてそうです。良い指摘です。実際、闇雲に値を変えていたら偶然動いた、というケースは、本質的な問題の解決には至っていません。\u003C/p>\u003Cp>しかし、観測した結果から｢ここの値が想定と違っていた｣と特定できれば、それは立派な原因究明です。観測のないトライアンドエラーは博打ですが、観測のあるそれは、ちゃんとしたデバッグであると考えます。\u003C/p>\u003Cp>考え込まないでください。手を動かしてください。コードは、あなたの思考の中ではなく、メモリ上で実際に動いています。\u003C/p>\u003Ch1 id=\"hd508ed06f1\">｢LLMに聞いたら違うことを言ってきました｣\u003C/h1>\u003Cp>そんな事を言うくらいなら、はなから私なんかに聞かずとも、永遠に一人寂しくLLMとイチャついてればいいと思うのですが、最近特に増えたのがこのパターンです。\u003C/p>\u003Cp>｢LLMに聞いたらこう書けばいいって言われたんですけど、動きません｣\u003C/p>\u003Cp>そうですか？うんうん、すごいでちゅね〜！それで、あなたはその実装を読みました？\u003C/p>\u003Cp>LLMが生成したコードは、それなりに動きます。それなりに、です。ライブラリに破壊的な変更があれば動きませんし、APIが廃止されていれば動きません。そもそもLLMの学習データに無かったマイナーなライブラリだと、平気でハルシネーションを起こします。実在しない関数を、自信満々で提案してきたりしやがります。\u003C/p>\u003Cp>それを確認もせずにコピペして、挙句、動かなかったら｢LLMが嘘をついたんだ！！！！｣と憤慨するのは、些か筋違いです。\u003C/p>\u003Cp>LLMは便利なツールですが、便利なツールを使うために必要な能力は、依然として人間側に求められています。これは、Stack Overflowが流行った時も、Qiitaが流行った時も、ChatGPTが流行った時も、Clineに全部賭けたときも、Cursorが流行った時も、ずっと変わっていません。\u003C/p>\u003Cp>LLMに頼ること自体は何も悪くありませんし、私も普段からLLMにかなりの出力を強要しています。GeminiにもOpusにも毎日お世話になっています。ただ、LLMが出力したコードは必ず自分で読みます。読んで理解して、おかしいところがあれば修正します。これをやらない人間がLLMを使うと、ただのコピペ職人が爆誕します。それも、自分が何をコピペしているかすら分からない、史上最悪のコピペ職人が、です。\u003C/p>\u003Cp>ちなみに、LLMに｢このコード動きません｣と投げて、LLMが返してきた修正をまたコピペして、それでも動かない、というループに陥っている方も散見されます。それを何時間も繰り返した挙句、LLMはダメだと結論付ける。残念ながら、ダメなのはLLMではなく、あなたの頭です。\u003C/p>\u003Cp>LLMはあなたの状況を完全には理解していません。あなたが理解した上で、適切に指示を出す必要があります。\u003C/p>\u003Cp>以前にも別の記事で書きましたが、LLMがどれだけ優秀になっても、使う人間の側に何もなければ、出力される成果物も同様のものでしょう。\u003C/p>\u003Ch1 id=\"h5cbe4915a0\">自分が何をしたいのか整理して\u003C/h1>\u003Cp>これも本当によく言われます。やめてくださいね。\u003C/p>\u003Cp>｢○○がしたいんですけど、どうすればいいです？｣\u003C/p>\u003Cp>その○○が、抽象的すぎてなんと言うかものすごくふわっとしている。\u003C/p>\u003Cp>｢Webサイトを作りたいんだけど〜｣\u003C/p>\u003Cp>どんなサイトを？静的サイトですか、動的サイトですか？認証認可は必要ですか？どんなコンテンツを載せたいですか？利用者は何人を想定していますか？運用のコスト感は？収益化したいですか、趣味ですか？\u003C/p>\u003Cp>これらはあなたが答えるべき質問です。わたしが答える質問ではありません。\u003C/p>\u003Cp>目的を整理するというのは、プログラミング以前のごく当たり前の力です。これができないと、コードを書く以前に何を書けばいいかすら定まりません。そんなんじゃお話になりません。\u003C/p>\u003Cp>これはエンジニアリングの問題ではなく、もっと手前の自分の思考を整理して言語化する国語の問題です。プログラミングは思考を厳密にコンピュータが理解できる形に翻訳する作業ですから、思考が曖昧なままではそもそもコードに翻訳することすらままならないことは自明でしょう。\u003C/p>\u003Cp>紙でもメモアプリでもLLMでも構わないので、自分が何をしたいのかを書き出してください。やりたいこと、やるべきではないこと、分かっていること、分からないこと等々。整理されていない要望をぶつけられても、こちらは何も答えようがありません。\u003C/p>\u003Cp>そして、もうひとつ。課題文を読めない方も本当に多いです。\u003C/p>\u003Cp>例えば、業務でちょっとしたツールを依頼する場面で、｢Slackに来たメッセージのうち、特定のキーワードを含むものをスプレッドシートに記録してください｣と伝えたとします。これは、｢Slackからメッセージを受け取る｣、｢キーワードでフィルタする｣、｢スプレッドシートに書き込む｣の3つの工程に分解できる、ごく単純な依頼です。\u003C/p>\u003Cp>ところが、これが分解できない方は、依頼文を一塊のまま受け取って、最初の一歩で固まります。｢何から手を付ければいいんでしょうか｣と相談されるのですが、お渡しした依頼文には既に手を付ける順番が書いてあります。一文として読むのではなく、要素ごとに区切って読んでください。それだけで、何をすればいいかが見えてきます。\u003C/p>\u003Cp>これはもうプログラミング以前の、国語の問題です。\u003C/p>\u003Cp>それすらできないようであれば、小学生のお勉強からやり直すことを推奨します。\u003C/p>\u003Ch1 id=\"h0d45d9ccaf\">大きい問題に挑む前に小さい問題に分割して\u003C/h1>\u003Cp>｢○○を作ってください｣という課題が出たとします。例えば、社内向けのちょっとしたダッシュボード、みたいな。\u003C/p>\u003Cp>ここで、できない方は、いきなり全部を一気に書こうとして、数時間悩んだ末に画面の前で固まる作業に戻ります。｢データ取得をどう書いて、それをどう加工して、どうUIに表示して、認証はどうして、エラー処理はどうして…｣と、全てを同時に考えようとして、結局1行も書けないのです。\u003C/p>\u003Cp>わたしならこう書きます。\u003C/p>\u003Cpre>\u003Ccode class=\"language-csharp\">var dashboard = new Dashboard();\nvar data = await dashboard.FetchDataAsync();\ndashboard.Render(data);\u003C/code>\u003C/pre>\u003Cp>3行、終わり。これでダッシュボードが完成です。\u003C/p>\u003Cp>「いや、それじゃ動かないじゃないですか」と思われるかもしれません。その通りです。動きません。\u003Ccode>Dashboard\u003C/code> とかいう存在しない抽象クラスを呼んでいるだけですから、当然です。\u003C/p>\u003Cp>しかし、これで全体像は捉えられます。あとは適当に\u003Ccode>Dashboard\u003C/code>クラスと\u003Ccode>FetchDataAsync\u003C/code>、\u003Ccode>Render\u003C/code>の中身をそれぞれ実装するだけです。中身もいきなり全部書こうとせず、同じように存在しないメソッドを呼んでおいて後から埋めていきます。これを繰り返していると、いつの間にか動くものが出来上がっています。\u003C/p>\u003Cp>トップダウン設計と呼ばれたりもしますが、名前はどうでも良くて、要するに、自分が一度に考えられる範囲まで、問題を細かく細分化するということです。難しい問題を、難しいまま扱える人は、ごく一部の天才だけであって、私を含む殆どの無能は、問題を1ファイルに1,000行で書ける程度の小ささに分解しないと解けません。\u003C/p>\u003Cp>勉強すれば難しい問題も解けるようになると期待されている方もいらっしゃるかもしれません。残念ながら、勉強しても難しい問題は難しいままです。賢くなるのではなく、問題を小さくする技術を身につけるべきです。これは才能の話ではなく、訓練の話なので、正直誰でも身につけられるかと思います。\u003C/p>\u003Ch1 id=\"h89f6c9455f\">動的型付け言語を選ぶ前に考え直しませんか\u003C/h1>\u003Cp>ここから少し、私の個人的な好みが強く入ります。先に断っておきますね。\u003C/p>\u003Cp>私は動的型付け言語が好きではありません。PythonもRubyもJavaScriptも、業務では書きますが、好きでは無いです。理由は単純で、書いた本人が何を扱っているか分からないコードを、後から他人が読んで分かるはずがないためです。\u003C/p>\u003Cp>巷では｢Pythonは書きやすい｣だとか、｢JavaScriptは手軽｣だとか、馬鹿の一つ覚えのように永遠と言われています。確かに、書き始めるまでのハードルは低いでしょう。しかし、それは短期的には楽であるというだけの話であって、長期的には負債が積み上がります。\u003C/p>\u003Cp>引数が何の型か書かれていない関数。返り値が状況によって変わる関数。エラー時に\u003Ccode>None\u003C/code>を返したり、空文字を返したり、例外を投げたりする一貫性のない設計。これらは、動的型付け言語特有の問題ではありませんが、動的型付けの環境では検出されないまま放置されやすい傾向にあります。\u003C/p>\u003Cp>そして、そういうコードを書いた本人は、3ヶ月後には何ひとつ覚えていません。私にも前科があります。\u003C/p>\u003Cp>これからプログラミングを始めるのであれば、最初から型のある言語を選んでください。GoでもRustでもKotlinでもSwiftでも、TypeScriptでも、好きなものを選べば良いと思います。動的型付け言語から始めて後から型に移行するのは苦痛でしょうし、最初から型に親しんだ方が遥かに早いかと思われます。\u003C/p>\u003Ch1 id=\"ha2c9bbc147\">質問とか\u003C/h1>\u003Cp>環境, やりたかったこと, やったこと, 起こったこと, 試したこと, 仮説\u003C/p>\u003Cp>このあたりが揃っていれば、回答する側は、スムーズに原因を絞り込めます。逆に、これらが何ひとつ揃っていない｢動きません｣だけの質問は、回答できません。回答できないというより、回答する前の調査だけで時間が溶けて、結果として誰も答えなくなります。私ならムカついて返信しないこともあるかもしれません。\u003C/p>\u003Cp>｢初心者なので〜｣と前置きする方もいますが、初心者かどうかは関係ありません。初心者であろうと、そうでなかろうと、項目を埋めることはできますし、初心者ほど丁寧に書くべきです。慣れている人なら省略して伝わることも、初心者の場合は丁寧に書かないと伝わらないためです。\u003C/p>\u003Cp>これは偏見ですが、質問が下手な人は概ね実装も汚いです。例外もあるかもしれませんが、私の観測範囲ではほぼ相関しているように思います。質問を組み立てることは、(本質的に)状況を分解して構造化する作業であって、それがプログラミングそのものであるためです。質問が組み立てられないということは、思考が組み立てられないということと同義で、思考が組み立てられなければ、コードも書けないでしょう。当たり前です。\u003C/p>\u003Ch1 id=\"h4eac98b528\">｢ググれ｣が冷たく聞こえるのなら申し訳ないですが\u003C/h1>\u003Cp>わたしは以前、ggrksは最大限の優しさだと書きました。今でも、その考えは変わっていません。\u003C/p>\u003Cp>技術的な質問に対して｢自分で調べてね〜｣と返すのは、決して冷たいわけではありません。\u003C/p>\u003Cp>\u003Cs>答えるのが面倒くさい訳ではないと言ってしまうと大嘘になるのですが、\u003C/s>｢あなたが自分で調べれば、もっと正確で網羅的な情報が手に入りますよ｣の意であり、｢私が中途半端に答えるよりも、検索した方が確実ですよ｣という意味であり、｢自身で調べる力を身につけた方がしあわせになれますよ｣という意味でもあります。\u003C/p>\u003Cp>これを冷たいと感じる方は、自分が答えを与えてもらえる立場だと思い上がっているのかもしれません。しかし、私はあなたの家庭教師でも、メンターでも、サポート窓口でもありません。私は\u003Cs>とても優しいので\u003C/s>聞かれたことにはなるべく答えるようにしていますが、それを当然のように要求されるのは、少し違うように思います。\u003C/p>\u003Cp>ついでに、ググる行為自体にも割とスキルが要ります。｢ECONNREFUSEDが出たんだけどどう治すの？｣みたいな雑な自然言語ではなく、｢ECONNREFUSED 127.0.0.1:5432 postgres:18.3-alpine｣のように、自分の環境に近い具体的なワードを並べる、英語で検索する、公式ドキュメントを優先する、GitHubのIssuesを見る、Stack Overflowは新しい順に並び替える等の工夫が必要です。\u003C/p>\u003Cp>ググれと言われて冷たいと感じる前に、自分のググり方を一度見直してみてください。それだけで、解決できる問題の幅が多分広がるかと思います。\u003C/p>\u003Ch1 id=\"h1afe451c43\">さいごに\u003C/h1>\u003Cp>正直、プログラミングなんてものはどんなに馬鹿でも時間さえかければできるようになります。\u003C/p>\u003Cp>これは、別に綺麗事として書いているのではなく、私自身がソースです。\u003C/p>\u003Cp>私自身、地頭(笑)が良いタイプでも、論理的思考力が突出しているタイプでもなく、どちらかと言えば落ちこぼれ側の人間です。覚えたての頃に書いていたコードなんて、今読み返すと冗談みたいに酷いものですし、未だに簡単なバグで数時間詰まることもざらにあります。\u003C/p>\u003Cp>何とかコードを書いて、何とかお仕事を頂いて、何とか運用できているのは、ただただ触っている時間がそれなりに長いからというだけでしょう。そこに特筆すべき才能なんてものは介在していません。\u003C/p>\u003Cp>なので、自分には才能がないからという理由で諦めるのは、的外れだと思っています。あなたに無いのは才能ではなく、ただの時間です。退屈な作業を、何百時間、何千時間と積み上げる時間が足りていないだけです。\u003C/p>\u003Cp>逆に言えば、これを積み上げる気が無いのなら、永遠にできるようにはなりませんし、｢いつか分かるようになる｣だとか、｢コツを掴めば｣みたいな、そういう商材屋の好きそうな胡散臭い魔法はありません。触った分だけ出来るようになります。\u003C/p>\u003Cp>それが面倒だと感じるのであれば、それは多分、あなたがプログラミングをそこまで好きではない、というだけの話です。それは別に悪いことではありません。世の中には、プログラミング以外にも、楽しい営みがいくらでもあります。プログラミングが特別な何かだと思い込むのは、業界の人間の自意識過剰でしょう。\u003C/p>\u003Cp>何とかしたいと思うのであれば、何とかしてください。何とかしたくないのであれば、別に何ともしなくて構いません。\u003C/p>\u003Cp>埋蔵金探しとかも新鮮で楽しそうですよ。\u003C/p>\u003Cp>それでは。\u003C/p>",[12,18,24],{"id":13,"createdAt":14,"updatedAt":15,"publishedAt":14,"revisedAt":15,"slug":16,"name":17},"wia4slp55q","2025-04-28T07:14:14.541Z","2025-12-03T16:08:13.798Z","thoughts","独り言",{"id":19,"createdAt":20,"updatedAt":21,"publishedAt":20,"revisedAt":21,"slug":22,"name":23},"8q8qyy8sz3","2025-04-29T08:44:23.406Z","2025-12-03T16:07:24.495Z","programming","プログラミング",{"id":25,"createdAt":26,"updatedAt":27,"publishedAt":26,"revisedAt":27,"slug":28,"name":29},"qfey3yw0z1","2025-04-29T08:44:41.687Z","2025-12-03T16:07:14.622Z","technology","テクノロジー",false,1,0,{"contents":34,"totalCount":44,"offset":32,"limit":31},[35],{"id":36,"createdAt":37,"updatedAt":38,"publishedAt":38,"revisedAt":38,"title":39,"content":40,"tags":41,"is_no_index":30,"summary":43},"7xdhut0q03","2026-04-20T22:00:47.483Z","2026-04-20T22:06:59.556Z","個を個として","\u003Cp>どうも、わたしです。\u003C/p>\u003Cp style=\"text-align: start\">どうにも眠れない夜です。仕方がないので、思ったことを色々ぼんやりと書いています。\u003C/p>\u003Cp style=\"text-align: start\">先日、誰かと話していて「属性で括るのは良くないよね」と言いかけました。ギリ踏みとどまって、言いませんでした。世の中的には、たぶん正しい主張なのだと思います。\u003C/p>\u003Cp style=\"text-align: start\">私は、偉そうな初学者が嫌いです。傲慢な人間が嫌いです。知性の乏しい人間が嫌いです。これらをこうして並べている時点で、私はそれらを思いっきり広義の属性で括っています。括ることをやめて、これらの嫌悪を表現する方法を、生憎と私は持ち合わせていません。\u003C/p>\u003Ch1 id=\"hc544c60f77\">カテゴライズから逃れられないらしい\u003C/h1>\u003Cp style=\"text-align: start\">聞くに、人間の認知というのは、結局のところカテゴライズを通してしか働かないようにできているらしいです。目の前にいる存在を猫として認識した瞬間、過去に出会ったすべての猫と、これから出会うかもしれない猫たちと、目の前のその子を、同じラベルの中に押し込めている。猫の名を持つラベルなしに、その存在を指し示そうとすると、認知が成り立たなくなってしまう。\u003C/p>\u003Cp style=\"text-align: start\">であるから、｢属性で括るな」という主張を文字通りに受け取ろうとすると、それは概ね「認知をやめろ」と言っているのと大差無いものだと思います。\u003C/p>\u003Ch1 id=\"h7cbc3267a6\">現象に名前を付けているだけ\u003C/h1>\u003Cp style=\"text-align: start\">こうやって自分を正当化してきました。｢ここで言う偉そうな初学者というのは、特定の属性として括っているのではなく、あくまでも私が観測した振る舞い、それそのものに名前を付けているだけなのだ｣、という感じで。\u003C/p>\u003Cp style=\"text-align: start\">ですが、よくよく考えるとこれは都合のいい言い訳でしかなくて\u003C/p>\u003Cp style=\"text-align: start\">名前を付けてしまったが最期、それはある種のラベルになってしまいます。過去の事例を束ね、未来の事例を予期するための枠組みになってしまいます。概ね構造的には属性で括るのと、全く同じ状態です。\u003C/p>\u003Cp style=\"text-align: start\">極論を言ってしまうと、固有名詞ですら例外ではなく、私が私を呼んだ瞬間、私は私を私として、ひとつの同一性に押し込めているという解釈もできます。昨日の私と今日の私は厳密には違う状態にある(べき)はずなのに、ラベルがそれを連続体として扱わせているためです。指示語ですら、世界を指されたものと指されていないものに分けてしまう。\u003C/p>\u003Cp style=\"text-align: start\">言語そのものが世界を切り分けるための都合のいい道具であると私は考えます。だとすれば、属性で括らない表現というものは、原理的に存在し得ないのではないでしょうか。\u003C/p>\u003Ch1 id=\"h057b7d27d3\">多様性\u003C/h1>\u003Cp style=\"text-align: start\">すごく嫌な世の中になったもので、最近、多様性だとか個性の尊重だとか配慮だとか、そういう言葉が粗雑にあちこちで掲げられているのを目にします。\u003Cs>私には到底理解できませんが、\u003C/s>それ自体は多分良いことなのでしょう。\u003C/p>\u003Cp style=\"text-align: start\">しかしながら、その素晴らしい看板の下で実際に行われていることといえば、結局のところ、望ましいラベルと望ましくないラベルを選り分けて、後者を排しているだけのようにしか思えません。\u003C/p>\u003Cp style=\"text-align: start\">勿論、｢ここで言う多様性が本来は社会制度上の包摂を扱う概念であって、個別の人間関係における受容を保証するものではない｣という趣旨の反論はあり得るのだと思います。それはそうなのでしょう。ただ、私が主張したいのは、その制度的な議論が個人の振る舞いのレイヤにまで降りてきたとき、結局は雑なラベル運用に堕しているという点です。\u003C/p>\u003Cp style=\"text-align: start\">個を個として受け止めるということが本当にできるのであれば、わざわざ多様性なんていう大仰な概念を持ち出す必要すらなく、一人ひとりに対してその人として向き合えばいいだけの単純な話のはずです。\u003C/p>\u003Cp style=\"text-align: start\">しかし、現実としては殆どそうはなっていません。属性で雑に括った上で「あなたのその属性を尊重します」と不誠実な嘘をついているだけ。そんなものは、個の受容ではなく属性の追認に過ぎないのではないでしょうか。個を個として受け止められない多様性は、その時点でもう破綻しています。破綻したものを、そのまま尊重する必要があるとは、到底私には思えません。\u003C/p>\u003Cp style=\"text-align: start\">私は目の前に立つ人間をひとりのあなたとして受け止めたいと思っています。しかし、それは決して簡単なことではなくて、私自身できているとは到底言えません。その辺の何も考えてないようなヘンテコ人間が偉そうに多様性について講釈を垂れる立場にあるのかというと、本当に疑わしく思います。\u003C/p>\u003Ch1 id=\"h0fb99d999f\">無関心\u003C/h1>\u003Cp style=\"text-align: start\">だからこそ、私は他社に対して程よく無関心であろうと思うのです。\u003C/p>\u003Cp style=\"text-align: start\">無関心と書くと冷たい印象を与えるかもしれませんが、ここで言いたいのはそういうことではなくって。粗雑なラベルで他人を括って、そのラベルに基づいて勝手に評価したり、勝手に共感したり、勝手に憐れんだりしないこと。相手のことを本当に知りもしないのに、わかったような顔をしないこと。概ねそんな感じです。\u003C/p>\u003Cp style=\"text-align: start\">前述の話と矛盾しているように見えるかもしれません。｢ラベリングから逃れられないと言っておきながら、無関心であろうとするとはどういうことか｣と。\u003C/p>\u003Cp style=\"text-align: start\">私の中での認識は概ねこういう整理です。ラベル自体は認知の道具として使い続けるしかない。しかし、そのラベルを根拠に他人を裁定しに行かない、評価しに行かない、わかった気にならない。道具として使うことと、道具を振り回して他人をボコボコに殴ることは別の話だと思っています。\u003C/p>\u003Cp style=\"text-align: start\">紛うことなき自画自賛ですが、これはある種の優しさなのだと思います。少なくとも、雑なラベリングで他人を撫でくり回すよりは、余程マシな振る舞いなのではないかと。\u003C/p>\u003Ch1 id=\"h6799f15c52\">返戻\u003C/h1>\u003Cp style=\"text-align: start\">ここまで書いた上で、最初の自分の発言に立ち返ってみましょう。私は偉そうな初学者が嫌いだ、と書きました。これは、粗いラベリングだったのでしょうか。\u003C/p>\u003Cp style=\"text-align: start\">おそらく誰が読んでも粗く感じることでしょう。「偉そう」も「初学者」も、それ自体は個別の振る舞いの束を雑に括った概念に過ぎません。本当に嫌っているのは、自分の理解の浅さに気づかずに断定してしまう態度であったり、他人の指摘を受け入れる構造を持たない振る舞いであったり、上手く言い表せませんが、そういう、もっと具体的な何かだったはずです。\u003C/p>\u003Cp style=\"text-align: start\">それでも、私は今後もこのラベルを使い続けるのだと思います。解像度を上げきった表現は、冗長すぎて使い物にならないので。日常の言葉というのは、ある程度の粗さを引き受けることで、ようやく成立していると考えます。\u003C/p>\u003Cp style=\"text-align: start\">引き受けた上で、自分のラベルの粗さを省みたり顧みなかったりする。(誤字じゃないですよ)\u003C/p>\u003Cp style=\"text-align: start\">実行可能な範疇で誠実であろうと思います。\u003C/p>",[42],{"id":13,"createdAt":14,"updatedAt":15,"publishedAt":14,"revisedAt":15,"slug":16,"name":17},"どうも、わたしです。どうにも眠れない夜です。仕方がないので、思ったことを色々ぼんやりと書いています。先日、誰かと話していて「属性で括るのは良くないよね」と言いかけました。ギリ踏みとどまって、言いませんでした。世の中的には、たぶん正しい主張なのだと思います。私は、偉そうな初学者が嫌いです。傲慢な人間が",62,{"contents":46,"totalCount":62,"offset":32,"limit":31},[47],{"id":48,"createdAt":49,"updatedAt":50,"publishedAt":50,"revisedAt":50,"title":51,"content":52,"tags":53,"is_no_index":30,"summary":61},"j7zvrsp48lb","2026-05-04T22:43:43.362Z","2026-05-05T00:07:59.619Z","Tailscaleやめたい","\u003Ch1 id=\"h8d027c8ed3\">はじめに\u003C/h1>\u003Cp>Tailscaleはネットワークの知識がない人が使うものだと思っていて、内心ずっと冷笑しているのですが、悪い噂をあまり聞かないので本当に悲しいです。用途に応じて使い分けろという話なんだろうとは思います。思いますが、他人の環境にアクセスしたいときに「まずTailscaleを入れて〜」みたいなことを言われると本当に嫌な気持ちになります。\u003C/p>\u003Cp>\u003Cs>ちなみに私も使っているので、あまり人のことを言えた義理ではありません。\u003C/s>\u003C/p>\u003Cp>便利なのは認めます。\u003Ccode>tailscale up\u003C/code>一発で直接インターネットへの疎通性を持たないホストに外から入れるのは素直に便利ですし、ちょっとしたマネジメント用途にはすごく使えるものだと思います。ただ、この便利は、OSのリゾルバ, netfilter, routing table他諸々に対する全力の侵襲と引き換えに成立しています。問題は、その侵襲の質が悪いことで、本当にストレスが溜まります。\u003C/p>\u003Cp>本エントリでは、これまでTailscaleを使ってきた中で頭にきたことを書いていきます。既に修正されたものもありますし、自分の設定が悪いだけのものも混ざっているかもしれません。それでも踏んだ事実は事実なので、忘れないうちに文句を言っておくことにします。\u003C/p>\u003Ch1 id=\"h89f5d63c3c\">resolv.confの扱い\u003C/h1>\u003Cp>tailscaledは起動するたびに\u003Ccode>/etc/resolv.conf\u003C/code>を書き換え、MagicDNS(100.100.100.100)をnameserverの先頭に挿入します。これがsystemd-resolvedとの相性が破滅的に悪い。\u003C/p>\u003Cp>systemd-resolvedはfollower modeで起動すると\u003Ccode>/run/systemd/resolve/resolv.conf\u003C/code>を生成します。tailscaledが事前に挿した100.100.100.100がここに混ざり、それを起動時の状態として読み戻し、upstreamのDNSの一つだと誤認して自身へ向けてDNSクエリを延々と転送し始めます。結果、内部キューが埋まってDNSが無事に死にます。\u003C/p>\u003Cp>これはGitHub上にもAmazon Linux環境での事象が報告されていて、報告者がtailscaleの中の人。タイトルが｢DNS stops working, and everything is very sad｣。very sadなのはこっちなんですけどね。あなたは知っててなぜ直さないんです？？？\u003C/p>\u003Cp>検出ロジックも杜撰で、systemd-resolvedかどうかを \u003Ccode>resolv.conf\u003C/code>のシンボリックリンク先のファイル名だけで判定しています。\u003Ccode>/etc/resolv.conf\u003C/code>が\u003Ccode>stub-resolv.conf\u003C/code>ではなくlegacy側にリンクされている場合、systemd-resolvedの存在に気づかず、direct モードに落ちてしまいます。諸々の組み合わせ全部を正しくハンドルしようとして、当然のように全部カバーできていません。これについて、tailscale公式ブログが“The Sisyphean Task Of DNS Client Config on Linux”と銘打って解説していますが、タイトルで自虐している時点で察してほしいです。\u003C/p>\u003Ch1 id=\"h77d9c93ff7\">RFC違反のDNS実装\u003C/h1>\u003Cp>resolv.confの話については、LinuxのDNS事情が混沌としてるのである程度同情の余地があります。しかし、tailscaleのDNSプロキシ実装そのものがRFCに準拠できていないのはどうにもなりません。\u003C/p>\u003Cp>EDNSのOPTレコードを完全無視します。クライアントがUDPのbuffer sizeを512 byteと広告していても、690 byte返してきます。RFC 6891がresponder MUST NOT exceed the requestor&apos;s buffer sizeと書いているものを、堂々とMUST NOT違反します。Cloudflareの子はちゃんとTC bitセットしてTCPフォールバックを促すのに、tailscale DNSはそれすらしません。\u003C/p>\u003Cp>DNS Flag Day 2020違反もあって、1232 byteを超えてもtruncateせずIPフラグメンテーションを起こします。上流からTC bit付きで返ってきても自分自身がTCPフォールバックできません。\u003C/p>\u003Cp>おまけに、EDNS Client Subnetを勝手にstripして米国IPに置換する挙動もあります。日本から繋いでるのにCDNが米国エッジに飛ばしてきます。MagicDNSを通すだけでレイテンシが100ms乗ります。VPN通すと遅くなるVPN、本当になんなんですか。\u003C/p>\u003Cp>極めつけがEDNS有無でキャッシュキーが分かれるsplit-brain cacheです。digとnslookupで同じドメインに対して別々の結果が(永続的に)帰ってきます。うーん困った。\u003C/p>\u003Cp>ちなみに文句はまだまだあって、tailscaledは \u003Ccode>resolv.conf\u003C/code>のsearch domainは保持するくせに、\u003Ccode>options ndots:5\u003C/code>を完全に剥がす振る舞いをします。Kubernetes環境なんかだとこれが致命的で、Pod内\u003Ccode>ping kuard.default\u003C/code>がbad addressで死んだ記憶があります。\u003C/p>\u003Ch1 id=\"h6d76dc14aa\">100.64/10をハードコードで全部drop\u003C/h1>\u003Cp>これが一番頭に来てる話です。\u003C/p>\u003Cp>ご存知の通り、tailscaleはtailnetのレンジとして100.64.0.0/10を使っています。曰く、「ISP用に予約された帯だから他のネットワークとぶつからない」らしい。確かにRFC 6598の定める100.64/10はService Provider向けのShared Address Spaceで、RFC 1918のものとは区別されています。とはいえ、インターフェース間のNATを介する用途であれば使用可能と明記されており、実際そう使っている方も多いのではないでしょうか。\u003C/p>\u003Cp>10/8と172.16/12がVPNやKubernetes Pod CIDRやDocker bridgeで埋め尽くされてる現状、まともにアドレス計画を立てたネットワークが100.64/10に逃げるのは普通の選択です。うちもそうしてます。自宅も会社もそう。ISPだけが使えるという前提がそもそも成り立っていません。\u003C/p>\u003Cp>それで、tailscaleを起動するとts-inputチェーンに\u003Ccode>DROP all -- !tailscale0 * 100.64.0.0/10 0.0.0.0/0\u003C/code>が挿入されます。tailscale0以外から来た100.64/10宛のパケットを全部drop。こちらが普通に内部で使ってる100.64/10宛の通信をtailscaleが勝手に殺してきます。困りますね。\u003C/p>\u003Cp>しかもこのDROPルール、tailnet policyで自分の使う範囲を100.65.64.0/22のように狭く指定しても、生成されるルールは依然として100.64.0.0/10全域です。コードを見ると\u003Ccode>util/linuxfw/nftables_runner.go\u003C/code>の\u003Ccode>addDropCGNATRangeRule\u003C/code>と\u003Ccode>createDropOutgoingPacketFromCGNATRangeRuleWithTunname\u003C/code>がそれぞれ別の方法で同じ範囲をルール化していて、片方は文字列正規化、もう片方は生の\u003Ccode>netip.Prefix\u003C/code>を利用しているようで。実装が二箇所に分散してる時点で察してほしいです。\u003C/p>\u003Cp>ちなみに、｢100.100.0.0/20で社内SSH運用してたサーバにtailscaleをインストールしたら、無条件で100.64/10全域DROPルールが入ってSSHを含むLAN通信が全切断、リモートからアクセス不能｣という事例がissueに上がっています。\u003C/p>\u003Cp>\u003Ca href=\"https://github.com/tailscale/tailscale/issues/12829\" target=\"_blank\" rel=\"noopener noreferrer\">https://github.com/tailscale/tailscale/issues/12829\u003C/a>\u003C/p>\u003Cp>これ、2024年7月起票で現在もneeds-triageラベルのままopenです。triageもされてない。1年半以上。\u003C/p>\u003Cp>この回避策が\u003Ccode>--netfilter-mode=off\u003C/code>ですが、これはtailscale自身が公式ドキュメントでセキュリティリスクだと書いているフラグで、もうどうしようもないんじゃないの感があります。苦しい。\u003C/p>\u003Ch1 id=\"h729e032ab6\">iptablesの介入がすごく頭悪い\u003C/h1>\u003Cp>\u003Ca href=\"https://github.com/tailscale/tailscale/issues/320\" target=\"_blank\" rel=\"noopener noreferrer\">tailscaleの中の人が起票したissue\u003C/a>の本文がすごいです。\u003C/p>\u003Cblockquote>\u003Cp>The way we add iptables forwarding rules on Linux was an old hack that works for approximately nobody, except by coincidence. Refers specifically to an interface named ‘eth0’ which is very uncommon. Get re-added (but never deleted) every time the app starts. Creates a wildcard MASQUERADE rule rather than tight settings.\u003C/p>\u003C/blockquote>\u003Cp>これが、tailscale製品のLinuxサポートの土台です。中の人がそう書いています。\u003C/p>\u003Cp>それから、tailscaledは起動時にINPUT/FORWARDの先頭に\u003Ccode>ts-input\u003C/code>, \u003Ccode>ts-forward\u003C/code>を挿入し、POSTROUTINGのNATにts-postroutingを挿入します。これも普通の挙動っぽいですが、周期的に再配置して常に先頭にいるよう書き換えてくるのが本当にお行儀が悪い。iptables-restoreでルール順を組んでも、firewalldで管理しても、ufwで管理しても、tailscaleが定期的に後ろから殴って先頭に出てきてしまいます。\u003Ccode>--netfilter-mode=nodivert\u003C/code>を明示しようと周期再配置は止まりません。ユーザはホストのFWを管理してる気になっていますが、実際の支配権はtailscaleにあります。\u003C/p>\u003Cp>おまけにfwmarkも雑で、Calicoの上位16bitの使用とtailscaleの\u003Ccode>0x40000\u003C/code>, \u003Ccode>0x80000\u003C/code>が衝突し、CalicoのeBPFモードで\u003Ccode>Drop malformed IP packets Final result=DENY\u003C/code>が大量発生します。\u003C/p>\u003Ch1 id=\"hce2d3190fc\">MTU 1280ハードコード\u003C/h1>\u003Cp>\u003Ccode>tailscale0\u003C/code>のMTUは1280でハードコードされています。GCPの1460も、AWSの1500も、ジャンボフレームの9000も、全部関係なく1280です。\u003C/p>\u003Cp>WireGuardはICMP Frag Neededを信用しない実装で (DoS耐性のためらしい？)、まともなPath MTU Discoveryがありません。\u003Ccode>tailscale0\u003C/code>をさらにVXLANやWireGuardで重ねると実効MTUがさらに下がります。CiliumのVXLAN MTU 1280 + WireGuard 60 + tailscale 60で実効MTU ~1140になり、1252 byteのTLS Server Helloがちょうど切れる位置に来てdropされます。原因がMTUだと気付くのに半日。おかげさまで貴重な時間を無駄に過ごすことができました。\u003C/p>\u003Cp>私が知る限り、MTUを変える手段は存在しません。VPN製品でユーザにTCP MSS clampを要求するの、設計としてどうかと思います。\u003C/p>\u003Ch1 id=\"he6e50cfa9d\">ACLでDenyを明示できない\u003C/h1>\u003Cp>tailscaleのACLはdefault-denyを謳いながら、actionはacceptしか書けず、個別ルールでdenyを表現できません。\u003C/p>\u003Cp>一般的なFWアプライアンスであれば、\u003C/p>\u003Cpre>\u003Ccode>allow ssh from trusted_hosts\ndeny ssh\nallow * from any\u003C/code>\u003C/pre>\u003Cp>で「SSHは信頼できるホストからだけ、それ以外のSSHは拒否、他のポートは全公開」のようなものが書けます。しかし、tailscale ACLではこれが書けません。これは、最後の\u003Ccode>allow *\u003C/code>がSSH制限を上書きしてしまうためです。\u003C/p>\u003Cp>「\u003Ccode>tag:A\u003C/code>を\u003Ccode>tag:B\u003C/code>以外からブロック」を表現したいだけで、新タグを追加するたびにsrcリストを全書き換えする必要がでてきてしまい、かなりつらい。\u003C/p>\u003Cp>しかも\u003Ccode>acls\u003C/code>フィールドを省略するとdefault allow all。default-denyを謳いながら、書き忘れたtailnetは実質全許可。default-denyって言葉はどこに行ったんでしょうか？\u003C/p>\u003Cp>capability-basedの設計判断として一貫してるのは分かります。ただ、ACLを名乗りながら一般的なFWの動作と全く別物を提供するのはいただけない。\u003C/p>\u003Ch1 id=\"h507404909b\">SSOを強制\u003C/h1>\u003Cp>これは半分私の好みの話ですが。Tailscaleはアカウント作成にSSOを強制してきます。メールアドレスとパスワードでサインアップという選択肢がそもそも存在していません。\u003C/p>\u003Cp>個人で気軽に使う分にはGoogleで入れば済むので困らない〜みたいな主張をされがちですが、一段冷静に考えるとこれは結構な縛りです。tailnet全体のidentityが特定のIdPに紐付くということは、そのIdPアカウントが消えてしまった瞬間にtailnetから締め出されるということに他なりません。Googleアカウントが何らかの理由でロックされたら自分のホストにすら入られなくなる、というのを許容するかどうかは、判断する余地があって然るべきでしょう。しかしながら、Tailscaleはその判断を許してくれません。\u003C/p>\u003Cp>業務で使う場合だと話はさらに面倒で、「組織のGoogle Workspaceでサインアップしたが、退職時に組織のtailnetから個人を切り離したい」のような普通のユースケースがSSO強制のせいで歪な手順になります。tailnetを組織と個人で分けようとすると、別IdPを用意する必要があり、IdPの運用負担がVPNの運用負担に乗ってきます。VPNにIdPの寿命を縛られる、という構造そのものがおかしい。\u003C/p>\u003Cp>｢OIDC対応があるからセルフホストIdPでもいい｣という反論はあると思います。あると思いますが、家庭でVPNを立てたいだけの人間にKeycloakを建てさせるのは、もはやVPNの話ではありません。「VPNを使うために先にIdPを建てろ」という主張はそもそもの順序が逆です。\u003C/p>\u003Cp>そもそもTailscaleのcontrol planeはクローズドソースで、coordination serverに何を握られているかをユーザは検証できません。その上でidentityまでIdP経由で渡すことを強制される。便利の対価として、tailnetの存在条件をTailscale社とIdP事業者の二者に握られるのが現状です。Headscaleを建てれば回避できるのはそう。そうなんですが、それはTailscaleをやめることとほぼ同義でしょう。\u003C/p>\u003Ch1 id=\"h1afe451c43\">さいごに\u003C/h1>\u003Cp>それでも私はたぶん明日もTailscaleを使います。文句を言いながら使うのが一番楽だからです。wg-quickの設定ファイルを書いて鍵を配って繋がらないと喚いている時間より、Tailscaleに苦しめられる時間の方がまだ短いというだけの話であって、これをTailscaleが優れていると言っていいのかはまた別のお話です。\u003C/p>\u003Cp>Tailscaleが約束する“It just works”は、自分のネットワークのどこにTailscaleが侵入しているかを完全に把握した人にだけ成立する魔法のようなものです。そもそも完全に把握している人は態々Tailscaleを使う理由はないでしょう。Tailscaleの“It just works”は、Tailscaleを使う理由がなくなった人にだけ動作する素晴らしい設計です。よくできていますね。\u003C/p>\u003Cp>いい感じの代替を知っている方がいれば是非教えてほしいです。それでは。\u003C/p>\u003Cp>\u003C/p>",[54,55],{"id":13,"createdAt":14,"updatedAt":15,"publishedAt":14,"revisedAt":15,"slug":16,"name":17},{"id":56,"createdAt":57,"updatedAt":58,"publishedAt":57,"revisedAt":58,"slug":59,"name":60},"lte0t59xm8sb","2025-05-02T16:12:38.708Z","2025-12-03T16:06:52.753Z","network","ネットワーク","はじめにTailscaleはネットワークの知識がない人が使うものだと思っていて、内心ずっと冷笑しているのですが、悪い噂をあまり聞かないので本当に悲しいです。用途に応じて使い分けろという話なんだろうとは思います。思いますが、他人の環境にアクセスしたいときに「まずTailscaleを入れて〜」みたいなこ",6,1785520627846]