<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>michio</title>
        <link>https://michio.me</link>
        <description>Your blog description</description>
        <lastBuildDate>Mon, 08 Jan 2024 06:07:29 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>michio</title>
            <url>https://michio.me/favicon.ico</url>
            <link>https://michio.me</link>
        </image>
        <copyright>All rights reserved 2024</copyright>
        <item>
            <title><![CDATA[能動的か受動的か]]></title>
            <link>https://michio.me/articles/passive-active-child</link>
            <guid>https://michio.me/articles/passive-active-child</guid>
            <pubDate>Thu, 04 Jan 2024 00:00:00 GMT</pubDate>
            <description><![CDATA[年末は実家に帰省していた。そのとき、子どもにタブレットなどを渡してYouTubeをどれくらいなら見せてもOKにするか、について奥さんと話した。]]></description>
            <content:encoded><![CDATA[<p>年末は実家に帰省していた。そのとき、子どもにタブレットなどを渡してYouTubeをどれくらいなら見せてもOKにするか、について奥さんと話した。
このときの話から、子どものどのような行為については推奨し、どのような行為については推奨しないかの線引きについて一つ判断基準を明確にした。
それは、能動的な行為であればOK、受動的な行為であればNGというものだ。</p>
<h2>能動的行為と受動的行為を定義する</h2>
<p>能動的な行為とは、自らの内発的なモチベーションのもと、&quot;こうしたい&quot;というような目的意識を持って行う行為だと考えた。</p>
<p>反対に受動的な行為は、意図・意思の欠如した行為のことを指す。</p>
<p>例えば、テレビやYouTubeを見るときには単に時間を過ごすためや、習慣的に視聴することが多い。
このような行為は、自動的だし、意識的な決定や選択をしているというよりも単純に退屈や暇つぶしを行っているだけになっている。</p>
<p>ただ、動画を見るという行為であっても、&quot;これを調べたい&quot;、&quot;これどうなっているんだろう&quot;というような内的な興味や好奇心から行うのであれば能動的な行為だと言える。</p>
<p>僕は子どもが没頭することは大事だと思っている。子どもが夢中になって取り組めることがあるなら支援したい。没頭できる環境を作ってあげたい。
では夢中ならなんでも良いのかというと、そうではない。その基準が能動的か受動的かということだ。受動的な行為に没頭することは推奨しない。</p>
<h2>なぜ受動的行為を推奨しないのか</h2>
<p>受動的行為の代表格は、例に上げたようにテレビやYouTubeを見ることだと思う。</p>
<p>僕の子どもはちょうど一歳を過ぎたくらいで、まだ動画を見たいと騒いだりはしない。
しかし、周りの子育て中の親が子どもにタブレットを渡して動画を見せているのを見たことはある。
子どもがずーーーっと動画を見る。動画が止まると泣き叫ぶ。動画が再生されると静かになる。これは中毒である。</p>
<p>こうした様子を見るとなんとも言えない気持ちになる。
大人の自分もダラダラとYouTubeを見たりすることに罪悪感を感じているからである。</p>
<p>自分が罪悪感を感じるような行為を推奨できるだろうか。いや、なんとか一緒になくしていきたいものである。</p>
<p>そんな理由で受動的な行為を推奨しないことにした。道連れみたいなものだ。</p>
<h2>基準を決めたら楽になる</h2>
<p>最近何事も基準を決めておけばその後判断が楽だなぁと思うことが多いので、これも一つの基準として決めてみた。
奥さんとも話し合って決めたので、今後の子育てでこの基準をベースに意思決定していくことになるだろう。
基準があると、判断の軸が明確になるので、判断が楽になる。</p>
<p>最後に、この軸自体は将来ずっと守り通すというものではないと考えている。今時点ではこう決めた、というだけである。
未来は変わるし、子どもも変わる。変わったときにまた考え直せば良い。</p>]]></content:encoded>
            <author>michinobu.shimatani@gmail.com (michio)</author>
        </item>
        <item>
            <title><![CDATA[ChatGPTに大量の文章を学習させる方法]]></title>
            <link>https://michio.me/articles/chatgpt-token</link>
            <guid>https://michio.me/articles/chatgpt-token</guid>
            <pubDate>Mon, 20 Mar 2023 00:00:00 GMT</pubDate>
            <description><![CDATA[最近 ChatGPT を使ったアプリケーションがたくさん出てきていますね。ChatGPT に社内 wiki やブログを読み込ませて、それをもとに質問に回答させるようなアプリケーションにトライしているのもちょくちょく見かけます。同じようなアプリケーションを作ってみたいが、実際どんな原理で実現しているのでしょう。少し調べてみました。]]></description>
            <content:encoded><![CDATA[<p>最近 ChatGPT を使ったアプリケーションがたくさん出てきていますね。ChatGPT に社内 wiki やブログを読み込ませて、それをもとに質問に回答させるようなアプリケーションにトライしているのもちょくちょく見かけます。
同じようなアプリケーションを作ってみたいが、実際どんな原理で実現しているのでしょう。少し調べてみました。</p>
<h2>ChatGPT に学習させる方法について</h2>
<p>ChatGPT では内部で GPT という言語モデルを使っています。</p>
<p>こうした言語モデルに新しく情報を学習させる方法は大きく分けて３つあります。</p>
<ol>
<li>学習段階から自社データを学習データに入れておく方法</li>
<li>学習済みのモデルに自社データを入れてファインチューニングする方法</li>
<li>プロンプトで学習させる方法</li>
</ol>
<p>1 の場合、GPT のモデルは OpenAI が作成しているものなので、学習段階から明示的に自分が追加で学習させたい内容を盛り込むことはできません。このように事前に学習させたモデルのことを事前学習済みモデルと呼び、ChatGPT ではこのモデルをもとに、様々な自然言語タスクに応用することができています。
2 のように事前学習済みモデルに追加で学習させることをファインチューニングと呼び、自分たち独自のデータを追加で学習させる方法として最も一般的だと思います。
しかし、ChatGPT では現在ファインチューニングをサポートしていません。</p>
<p>したがって 3 のプロンプトで学習させる方法が ChatGPT においては最も一般的な学習方法と言えるでしょう。</p>
<h2>プロンプトとは</h2>
<p>プロンプトとは、ChatGPT に与える入力情報のことです。プロンプトには、回答に必要な情報を適切に与える必要があります。以下のような情報が含まれることが多いでしょう。</p>
<ul>
<li>質問文や要求事項の文言</li>
<li>質問文や要求事項の文脈となる情報（例：商品やサービスの名前、地名、期間、数量など）</li>
<li>回答に必要な情報の整形方法（例：データのフォーマット変換、要約、加工など）</li>
<li>回答に必要な情報の出力方法（例：テキスト、音声、画像、動画など）</li>
</ul>
<p>プロンプトエンジニアリングという言葉もありますが、プロンプトに与える情報の考え方は、ChatGPT の性能に大きく影響するため、非常に重要な要素です。社内情報やブログなどの情報を、質問や要求事項の文脈となる情報としてプロンプトに渡すことで、ChatGPT はそれをもとに回答を生成してくれます。
学習させるという単語を使っているので、ChatGPT はプロンプトに与える情報を学習しているように見えますが、実際にはプロンプトに与える情報をもとに、回答を生成させているだけです。</p>
<pre><code># プロンプトの例
# 質問文
〇〇プロジェクトの中で決めた〇〇という仕様ですが、なぜこのような仕様になってましたっけ？
# 質問文の文脈となる情報
{ここに社内の情報を記述する}
# 出力
</code></pre>
<p>上記のような質問を ChatGPT に投げれば、ChatGPT は社内の情報をもとに回答を生成してくれるはずです。</p>
<h2>プロンプトの制約</h2>
<p>では学習させたい全ての情報をプロンプト内に記述することができるのでしょうか。実はそうではありません。</p>
<p>ここでトークンについての理解が必要になります。トークンとは自然言語のテキストデータを分割した最小単位のことを指しており、プロンプトはトークンを並べた文字列として表現されます。プロンプトに渡せる質問文やそれに関連する情報は全てトークンとして処理されるわけです。</p>
<p>ChatGPT では一度に渡せるトークン数に上限があり、GPT-3.5 では最大 4096 トークン、GPT-4.0 では最大 8192 トークンとなっています。上限を超えるとエラーが発生します。
日本語は大体１文字で１トークンと計算されるため、だいたい 4000 文字程度しか渡すことができません。</p>
<p>この最大トークン制約があるため、単純にプロンプトに全ての情報を記述することはできません。プロンプトに与える情報を適切に整理し、必要最低限の情報をプロンプトに渡すことが重要です。</p>
<p>この上限は、ChatGPT が生成する回答の長さに制限があるためです。この上限を超えると、ChatGPT は回答を生成することができないため、エラーが発生します。</p>
<p>少し余談ですが、ChatGPT では会話の履歴を保持しているように思えますが実はそうではありません。
毎回の質問ごとに過去の会話履歴もプロンプトに渡すことで、会話の文脈を加味して返答しているだけなのです。
会話が長くなればいずれ最大トークン数を越えてしまうため、過去の会話を削ぎ落としていくようです。
したがって会話の履歴が長くなればなるほど、ChatGPT は始めの方の会話の文脈を考慮できなくなっていきます。</p>
<p>このように一度に渡せる情報量に上限がある ChatGPT に対して、どのようにすれば大規模な情報を学習させることができるのでしょうか。
いくつか手法はあるようですが、今回はプロンプトに渡す前段にベクトルベースの文章検索を挟む方法を紹介します。</p>
<h2>文章ベクトルを使った検索方法</h2>
<p>OpenAI には<a href="https://platform.openai.com/docs/guides/embeddings/what-are-embeddings">Embeddings</a> API が存在します。
Embeddings では事前学習されたモデルを利用して、単語や文章のベクトル表現を取得することができます。</p>
<p>以下は Amazon のレビュー文章のベクトル表現を２次元にプロットしたものです。プロットの点の色は、レビューの評価を表しています。同じ色の点が近くに集まっていることがわかりますが、これは悪い評価のレビューや良い評価のレビュー文章は近い場所にプロットされていることを示しています。
このことから文章の類似性をベクトル空間上の距離で表現することができることがわかります。
<img src="./embeddings-tsne.webp" alt=""/></p>
<img alt="" srcSet="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fembeddings-tsne.ad063755.webp&amp;w=828&amp;q=75 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fembeddings-tsne.ad063755.webp&amp;w=1920&amp;q=75 2x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fembeddings-tsne.ad063755.webp&amp;w=1920&amp;q=75" width="828" height="580" decoding="async" data-nimg="1" loading="lazy" style="color:transparent"/>
<p>引用元：<a href="https://platform.openai.com/docs/guides/embeddings/use-cases">https://platform.openai.com/docs/guides/embeddings/use-cases</a></p>
<h3>具体的な手順</h3>
<p>Embeddings を使って文章を検索する場合、以下のような手順で行うことができます。</p>
<ol>
<li>検索対象となる社内情報やブログ記事など、全ての文章をベクトル化する</li>
<li>ベクトル化した文章を永続情報として保存する</li>
<li>質問文をベクトル化する</li>
<li>ベクトル化した質問文と保存したベクトル化した文章を比較し、質問文と類似度の高い文章を検索し、取得する</li>
</ol>
<img alt="" srcSet="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2FScaNN_tom_export.fd86ecfc.gif&amp;w=640&amp;q=75 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2FScaNN_tom_export.fd86ecfc.gif&amp;w=1080&amp;q=75 2x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2FScaNN_tom_export.fd86ecfc.gif&amp;w=1080&amp;q=75" width="512" height="245" decoding="async" data-nimg="1" loading="lazy" style="color:transparent"/>
<p>引用元：<a href="https://cloud.google.com/vertex-ai/docs/matching-engine/overview">https://cloud.google.com/vertex-ai/docs/matching-engine/overview</a></p>
<p>質問文と文章の類似度は最近傍法によって計算されるのが一般的なようです。</p>
<p>こうして取得した文章をプロンプトに渡すことで、ChatGPT は検索された文章の文脈を考慮した回答を生成することができます。</p>
<pre><code># プロンプトの例
# 質問文
〇〇プロジェクトの中で決めた〇〇という仕様ですが、なぜこのような仕様になってましたっけ？
# 質問文の文脈となる情報
{検索して取得された、質問と類似性の高い文章をここに埋め込む}
# 出力
</code></pre>
<p>このプロンプトに渡す前に検索を行うという行為は、短期記憶と長期記憶という概念で考えるとイメージしやすいと思います。
長期記憶である文章ベクトル空間から関連する情報を抽出し、短期記憶であるプロンプトに渡すことで、ChatGPT はより正確な回答を生成することができます。</p>
<h2>まとめ</h2>
<p>今回は ChatGPT に情報を学習させることについて説明しました。
現在では一度に渡せる情報量に限りがあるため、利用者自身が渡す情報を工夫する必要があります。
こうした制限は徐々に緩和されていくでしょうし、あるいは今回紹介したような文章ベクトルを使う方法も今後はツール内に内包され、透過的に利用できるようになると思います。</p>
<p>しかし内部でどのように情報が処理されているかを理解しておけば、今後の活用イメージを広げることことができると思います。</p>]]></content:encoded>
            <author>michinobu.shimatani@gmail.com (michio)</author>
        </item>
        <item>
            <title><![CDATA[プロダクトの３つの価値について]]></title>
            <link>https://michio.me/articles/product-three-value</link>
            <guid>https://michio.me/articles/product-three-value</guid>
            <pubDate>Sun, 26 Feb 2023 00:00:00 GMT</pubDate>
            <description><![CDATA[プロダクトの価値というのは大きく分けて３つ存在します。それは限定的価値、合理的価値、体験的価値の３つで、価値の強さ順に並べると「限定 > 合理 > 体験」となります。]]></description>
            <content:encoded><![CDATA[<p>プロダクトの価値というのは大きく分けて３つ存在します。
それは限定的価値、合理的価値、体験的価値の３つで、価値の強さ順に並べると「限定 &gt; 合理 &gt; 体験」となります。
ユーザはこれらの価値をどれだけ感じるかによってそのプロダクトを使うかどうかを決めます。</p>
<p>先日社内のプロダクトマネージャーの方と話をしていて、プロダクトの価値について説明してもらったことがとてもわかりやすかったのでその内容をまとめておく。</p>
<h2>限定的価値</h2>
<p>限定的価値とはプロダクトが扱うコンテンツ・機能がそのプロダクトにしか存在しないという価値を指します。
そのコンテンツ・機能が他の場所では得られない限定的なものなので、そのプロダクトを使う理由になります。</p>
<p>例えば Netflix のオリジナルコンテンツも限定的価値の 1 つです。ストレンジャー・シングスやハウス・オブ・カードは Netflix オリジナルのコンテンツであり、他のストリーミングサービスでは視聴できません。仮に Netflix の料金が高く、UI/UX が使いにくかったとしても、これらのコンテンツを視聴したいなら Netflix を使わざるを得ません。</p>
<p>以前アーティストのファン向けサイトを運営している方がこんなことを言っていました。
「どれだけサイトへのアクセスに時間がかかろうが、サイト内の導線が不親切だろうが、ユーザはこのプロダクトを使う。なぜならグッズやチケットはこのプロダクトからしか買えないから。UI/UXは必要ないんだよね。」</p>
<p>上記は極端な意見ですが、本質を捉えていると思います。ユーザはプロダクトが扱っているコンテンツ・機能が他の場所では得られないということを知っているので、どれだけ体験が悪くても使います。これが限定的価値が最も強い価値といえる理由です。</p>
<p>しかし限定的価値には欠点があります。限定的なものにユーザが惹かれなければ、ただターゲットが狭いニーズに答えるだけのものになります。誰も欲しくないものを限定コンテンツとして提供しても使う理由になりません。
限定的価値は提供する機能・コンテンツを見誤ると最も弱い価値とも言えるので注意が必要です。</p>
<h2>合理的価値</h2>
<p>合理的価値とは、同じような機能・コンテンツを持つ競合プロダクトに比べて、より安く、より多くの価値を提供することで、ユーザ
にとって合理的で魅力的な価値を提供することを指します。</p>
<p>Spotify のプレミアム会員を例にして考えてみましょう。Spotify で配信されている楽曲数は他の音楽ストリーミングサービスよりも豊富です。ユーザは同じ金額を払うとしても Spotify に払ったほうがより多くのコンテンツを楽しめることができるので、Spotify の方が得です。これが合理的価値です。</p>
<p>合理的価値は、価格が安いかどうかだけでは決まりません。価格の他にプロダクトの品質、機能、顧客サポート、信頼性など、多様な側面に基づいて決定されます。ユーザは自分が支払う金額・コストに見合った価値を得ることができるかを判断し、利用するかを決めます。</p>
<p>「支払う金額・コストに対して見合った、もしくはそれ以上の価値を提供できているか」が重要なので、必ずしも安くすればよいわけではありません。コスパが良いかどうかですね。</p>
<h2>体験的価値</h2>
<p>体験的価値は、そのプロダクトを使ったときに感じる楽しさや快適さ、高い満足感を提供する価値を指します。
限定・合理的価値が他と比べて同じである場合、最後はその体験が素晴らしいものかどうかでどのプロダクトを使うかが決まります。</p>
<p>UI/UX の使いやすさ、洗練さやプロダクトのレスポンスの早さなどは体験的な価値と言えます。動画ストリーミングサービスの場合、
同じ動画を視聴するとき動画のロードが早く、高画質で視聴できるサービスを選ぶはずです。
しかしそもそも視聴したいコンテンツがそのプロダクトに存在しなければどれだけ体験が良くてもそのプロダクトを選ばないでしょう。
こうした理由から限定や合理的価値より、体験的価値は弱いと考えています。</p>
<p>デザイナーやエンジニアは持っている専門性から、体験的価値を重視しやすい傾向があります。
しかし体験的価値はプロダクト価値の中でも最も弱い分類の価値であるということを忘れないようにしましょう。こだわることは大変良いことですがね。自戒を込めて。</p>
<h2>プロダクトマネージャーは 3 つの価値をどう考えるべきか</h2>
<p>説明してきたそれぞれの価値の強さを根拠にすると、プロダクトを作る際は限定的価値、合理的価値、体験的価値の順に機能・コンテンツを考えていくことが良いでしょう。
ただしプロダクトの状況や目的によってどの価値を磨いていくのかを変化させていく必要があります。</p>
<p>Netflix も始めはオンラインで動画がすぐ視聴できるところに限定的価値がありました。しかし競合プロダクトが出現し、オンラインで動画を視聴することが当たり前になってくると、オンライン視聴自体は限定的価値とみなされなくなってきます。</p>
<p>そこで視聴可能なコンテンツ数を増やすことで、合理的価値を他プロダクトより高めたり、高画質化で視聴体験を良くすることで体験的価値で競い始めます。そこでも差が出なくなってきたタイミングで、今度は各プロダクトが限定コンテンツを自社で作成し始めました。再度、限定的価値を作り出そうという動きです。</p>
<p>このようにプロダクトに必要な価値は、世の中や競合の動きによって常に変化します。そのため今のプロダクトが何の価値を持っているかを常に考え、磨いていく必要があります。</p>
<h2>価値の重複</h2>
<p>限定的価値、合理的価値、体験的価値は基本的に独立した概念ですが、重複する場合もあります。
例えば限定的価値を持つプロダクトはその希少性が高いため、それ自体が体験的価値や合理的価値につながることがあるでしょう。</p>
<p>そのため、プロダクトマネージャーは、3 つの価値を単独で考慮するだけでなく、相互に関連することも考慮しなければいけません。</p>
<h2>まとめ</h2>
<p>今回プロダクトの価値について考えてみましたが、これらを整理し理解したといって良いプロダクトが作れるわけではありません。
また3つ以外にも価値は当然存在します。(たとえば社会的価値など)</p>
<p>しかしプロダクトの機能開発を進める際、大雑把でも「今回はプロダクトのどの価値を作る目的なのか？」をメンバーですり合わせておくだけで、その後のコミュニケーションが円滑になると感じました。</p>
<p>限定的価値を作る目的なら、その機能が本当に価値を生むのかを検証する必要があるため、捨てても良い前提で開発を進めるといった選択肢を取ることができるかもしれません。
また体験的価値を作る目的なら、徹底して UI/UX やパフォーマンスにこだわりを持って開発する必要があるでしょう。</p>
<p>プロダクトの価値についての解像度があがれば、チームをどのモードに切り替えるべきかを判断する材料の一つとして役に立つはずです。</p>]]></content:encoded>
            <author>michinobu.shimatani@gmail.com (michio)</author>
        </item>
        <item>
            <title><![CDATA[PostgreSQLのEXPLAINではどのようにコストを見積もっているのか]]></title>
            <link>https://michio.me/articles/postgres-exlplain-cost</link>
            <guid>https://michio.me/articles/postgres-exlplain-cost</guid>
            <pubDate>Sun, 12 Feb 2023 00:00:00 GMT</pubDate>
            <description><![CDATA[新しいクエリを発行する機能をプロダクトに追加する際、クエリの実行計画を確認するのが一般的です。PostgreSQL では EXPLAIN による実行計画ではクエリに必要なコスト情報を算出してくれます。]]></description>
            <content:encoded><![CDATA[<p>新しいクエリを発行する機能をプロダクトに追加する際、クエリの実行計画を確認するのが一般的です。
PostgreSQL では EXPLAIN による実行計画ではクエリに必要なコスト情報を算出してくれます。</p>
<p>今回はこのコストがどういったロジックをもとに算出されているかを調べました。</p>
<h2><code>EXPLAIN</code> 実行計画を表示するコマンド</h2>
<p>サンプルとして以下の EXPLAIN コマンドを実行します。</p>
<pre><code>$ explain select * from users
    join user_basic_informations on users.id = user_basic_informations.user_id;

# 実行計画
Hash Join  (cost=4.55..28.62 rows=69 width=274)
  Hash Cond: ((users.id)::text = (user_basic_informations.user_id)::text)
  -&gt;  Seq Scan on users  (cost=0.00..22.17 rows=717 width=110)
  -&gt;  Hash  (cost=3.69..3.69 rows=69 width=164)
        -&gt;  Seq Scan on user_basic_informations  (cost=0.00..3.69 rows=69 width=164)
</code></pre>
<p>実行計画では処理する単位のことを「ノード」と呼びます。上記の例では、Hash Join と Seq Scan などがノードです。</p>
<h2>各項目の説明</h2>
<p>各ノードには、<code>cost</code>, <code>rows</code>, <code>width</code> がそれぞれ表示されます。
以下の実行結果の一つを使って、それぞれの項目が何を表しているか説明します。</p>
<pre><code>Seq Scan on users  (cost=0.00..22.17 rows=717 width=110)
</code></pre>
<ul>
<li>cost: ノードで処理するクエリのコスト</li>
<li>rows: ノードが返却する行数</li>
<li>width: ノードが返却する１行あたりの平均的な行の長さ</li>
</ul>
<p>このうちクエリのコストを判断するのに最も重要なのは <code>cost</code>です。(名前の通りですが)
cost を算出する際に、rows, width も計算式内で考慮されているためです。</p>
<h3>cost=N.NN..M.MM</h3>
<p>cost は<code>N.NN..M.MM</code>のように表示されます。これはそれぞれ始動コストと総コストを表します。</p>
<p><code>N.NN</code>の部分は始動コストと呼ばれ、１件目のデータを返却できるまでにかかるコストを表します。</p>
<p><code>M.MM</code>の部分は総コストと呼ばれます。総コストは全てのデータを返却するまでにかかるコストを表します。</p>
<p><code>cost=0.00..22.17</code>の場合、始動コストは 0 で、総コストは 22.17 です。</p>
<p>シーケンシャルスキャンでは、始動コストは 0 になります。シーケンシャルスキャンはテーブルの先頭から順番にデータを読み込むため、始動コストはかかりません。
インデックススキャンの場合は先に Index を読み込み、その後にテーブルからデータを読み込むため、始動コストは 0 になりません。
また Hash Join や Sort では、データを返却する前にデータをソートしたり、ハッシュテーブルを作成する必要があるため、始動コストが他に比べ大きくなる傾向があります。</p>
<p>始動コストはあくまで１件目のデータを返却できるまでにかかるコストです。始動コストが大きいクエリでも、総コストが小さければ実行時間は短くなります。</p>
<p>したがって、クエリの負荷を判断する上で重要なのは総コストです。実行計画を見て、そのクエリがどれくらいコストがかかるクエリかを判断するためには、総コストをチェックすると良いでしょう。</p>
<h2>どのようにコストを見積もっているのか</h2>
<p>ディスクからデータを読み出し、返却できるまでにかかるコストが総コストと述べましたが、クエリを処理する際 PostgreSQL 内では複数の処理ステップが存在し、それぞれにコストがかかります。
例えば、ディスクからデータを読み取る処理コスト、データを CPU 上で処理するコスト、WHERE 句といった operator を処理するコストなどです。</p>
<p>これらの処理はそれぞれ必要なコストが異なるため、各処理ごとにコスト調整パラメータが存在します。
ディスクからシーケンシャルアクセスで 8KB のデータを読み込む時間のコストを 1.0 とし、他の処理にかかる時間を相対値として表現されています。</p>
<p>各コスト調整パラメータの詳細は公式ドキュメントに記載されています。<a href="https://www.postgresql.jp/document/14/html/runtime-config-query.html#RUNTIME-CONFIG-QUERY-CONSTANTS">20.7.2. プランナコスト定数</a></p>
<p>例を挙げると以下のようなパラメータがあります。</p>
<ul>
<li>seq_page_cost: ディスクからシーケンシャルアクセスで 1 ページ分(8KB)のデータを読み込むために必要な時間コスト</li>
<li>cpu_tuple_cost: ヒープデータ１行あたりの CPU 処理にかかる時間コスト</li>
<li>cpu_index_tuple_cost: インデックスデータ１行あたりの CPU 処理にかかる時間コスト</li>
</ul>
<p>再度、上記の実行計画を見てみましょう。</p>
<pre><code>Seq Scan on users  (cost=0.00..22.17 rows=717 width=110)
</code></pre>
<p>シーケンシャルスキャンでデータを読み込むため、ディスク I/O のコスト(<code>seq_page_cost=1.0</code>)がかかります。
PostgreSQL ではページ単位でデータを読み取ります。コストを計算するためには、users テーブルの合計ページ数が必要です。
また読み取る行単位のデータ処理に必要な CPU コスト(<code>cpu_tuple_cost=0.01</code>)の和で算出することができます。</p>
<p><code>総コスト = (ディスクページ読み取り数 * seq_page_cost) + (スキャンした行 * cpu_tuple_cost)</code></p>
<p>以下のコマンドから users テーブルのページ数を確認できます。</p>
<pre><code>$ SELECT relpages FROM pg_class WHERE relname = &#x27;users&#x27;;
relpages
15
</code></pre>
<p>relpages が 15 であるため、users テーブルの合計ページ数は 15 です。
上記の式に当てはめてみましょう。</p>
<p><code>総コスト = (15 * 1.0) + (717 * 0.01) = 22.17</code></p>
<p>計算結果と実行計画の総コストが一致していることが確認できました。
今回ではシーケンシャルスキャンのみを見てきましたが、インデックススキャンやハッシュジョインなども同様にコストを算出しています。</p>
<p>このように EXPLAIN では、実行されるクエリに必要な処理と、処理される行数を元にコストを算出しています。</p>
<h2>まとめ</h2>
<p>今回は PostgreSQL の EXPLAIN では、どのようにしてコストを算出しているかを紹介しました。
総コストはクエリに必要な処理と、処理される行数をもとに算出されます。これは当然ですが、運用していくことで読み取り対象の行数が増えるとコストも増えることを意味します。</p>
<p>初回の EXPLAIN の実行結果が安心できるものであったとしても定期的にチェックすることで、コストが増加していないかを確認できる仕組みをつくることが重要です。</p>
<h2>参考文献</h2>
<ul>
<li><a href="https://www.postgresql.jp/document/14/html/using-explain.html">PostgreSQL 14.5 文章 EXPLAIN の利用</a></li>
<li><a href="https://www.amazon.co.jp/%EF%BC%BB%E6%94%B9%E8%A8%823%E7%89%88%EF%BC%BD%E5%86%85%E9%83%A8%E6%A7%8B%E9%80%A0%E3%81%8B%E3%82%89%E5%AD%A6%E3%81%B6PostgreSQL%E2%80%95%E8%A8%AD%E8%A8%88%E3%83%BB%E9%81%8B%E7%94%A8%E8%A8%88%E7%94%BB%E3%81%AE%E9%89%84%E5%89%87-Software-Design-plus-%E4%B8%8A%E5%8E%9F/dp/4297132060/ref=pd_lpo_1?pd_rd_w=gEont&amp;content-id=amzn1.sym.d769922e-188a-40cc-a180-3315f856e8d6&amp;pf_rd_p=d769922e-188a-40cc-a180-3315f856e8d6&amp;pf_rd_r=BSV61AH56J8DQWZMPC36&amp;pd_rd_wg=vfsAM&amp;pd_rd_r=b249ab03-56a0-4627-8180-073b805064e8&amp;pd_rd_i=4297132060&amp;psc=1">［改訂 3 版］内部構造から学ぶ PostgreSQL―設計・運用計画の鉄則</a></li>
</ul>]]></content:encoded>
            <author>michinobu.shimatani@gmail.com (michio)</author>
        </item>
        <item>
            <title><![CDATA[npm-package-json-lint で package.json 内のバージョン指定を固定化する]]></title>
            <link>https://michio.me/articles/npm-package-json-lint</link>
            <guid>https://michio.me/articles/npm-package-json-lint</guid>
            <pubDate>Sun, 05 Feb 2023 00:00:00 GMT</pubDate>
            <description><![CDATA[Node.js では、package.json によって依存するパッケージを管理します。package.json には各パッケージごとにバージョンを指定しますが、チルダ記法、キャレット記法のようにバージョンを範囲指定することもできます。]]></description>
            <content:encoded><![CDATA[<p>Node.js では、package.json によって依存するパッケージを管理します。
package.json には各パッケージごとにバージョンを指定しますが、
<a href="https://github.com/npm/node-semver#tilde-ranges-123-12-1">チルダ記法</a>、<a href="https://github.com/npm/node-semver#caret-ranges-123-025-004">キャレット記法</a>のようにバージョンを範囲指定することもできます。</p>
<p>リリース済みのプロダクトの場合、package.json に指定するバージョンは、一般的には固定値で指定するのが良いでしょう。
バージョンを固定することで破壊的な変更が入ったアップデートを意図せずリリースしてしまうことを防ぐことができるからです。</p>
<p>しかし新しくパッケージをインストールした際、うっかりこのバージョンを固定することを(僕は)忘れることがあります。今回はこれを防ぐためのツールを紹介します。</p>
<h2>npm-package-json-lint</h2>
<p>package.json の linter として<a href="https://npmpackagejsonlint.org/">npm-package-json-lint</a>があります。
これは package.json の記法が指定したルールに則っているかどうかをチェックするツールです。</p>
<p><code>npm-package-json-lint</code> には package.json の記法に関する様々なルールを指定できますが、今回はバージョン固定についてのみ紹介します。他のルールについては<a href="https://npmpackagejsonlint.org/docs/rules">公式ドキュメント</a>を参照してください。</p>
<h2>設定</h2>
<p>設定ファイルの構築方法にはいくつかパターンがありますが、今回は一番簡単な<code>npmpackagejsonlint.json</code>を配置する方法を紹介します。</p>
<p>repository のルートに <code>npmpackagejsonlint.json</code> を配置し、以下のように設定します。</p>
<pre class="language-json"><code class="language-json"><span class="token punctuation">{</span>
    <span class="token property">&quot;rules&quot;</span><span class="token operator">:</span> <span class="token punctuation">{</span>
        <span class="token property">&quot;prefer-absolute-version&quot;</span><span class="token operator">:</span> <span class="token string">&quot;error&quot;</span><span class="token punctuation">,</span>
        <span class="token property">&quot;prefer-absolute-version-devDependencies&quot;</span><span class="token operator">:</span> <span class="token string">&quot;error&quot;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre>
<p>今回は以下２つの設定を行いました。</p>
<ul>
<li>prefer-absolute-version は dependencies に指定されたパッケージのバージョンが固定になっているかをチェックします。</li>
<li>prefer-absolute-version-devDependencies は devDependencies に指定されたパッケージのバージョンが固定になっているかをチェックします。</li>
</ul>
<h2>npm-package-json-lint の実行</h2>
<p>今回は以下のような package.json を用意しました。</p>
<pre class="language-json"><code class="language-json"><span class="token punctuation">{</span>
    <span class="token property">&quot;dependencies&quot;</span><span class="token operator">:</span> <span class="token punctuation">{</span>
        <span class="token property">&quot;react&quot;</span><span class="token operator">:</span> <span class="token string">&quot;^18.2.0&quot;</span><span class="token punctuation">,</span>
        <span class="token property">&quot;react-dom&quot;</span><span class="token operator">:</span> <span class="token string">&quot;^18.2.0&quot;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre>
<p>ここでは react, react-dom のバージョンがチルダ表記で指定されています。
この状態だと patch 以下のバージョンが固定化されておらず、パッケージのインストール時に意図しないバージョンがインストールされる可能性があります。</p>
<p>ではこの状態で lint を実行してみましょう。すると以下のようなエラーが表示されるはずです。</p>
<pre class="language-bash"><code class="language-bash">❯ <span class="token function">yarn</span> npmPkgJsonLint <span class="token builtin class-name">.</span>
✖ prefer-absolute-version-dependencies - node: dependencies - You are using an invalid version range. Please use absolute versions. Invalid dependencies include: react, react-dom
<span class="token number">1</span> error
<span class="token number">0</span> warnings

Totals
<span class="token number">1</span> error
<span class="token number">0</span> warnings
<span class="token number">0</span> files ignored
</code></pre>
<p>エラーからは<code>prefer-absolute-version</code> というルールに違反していることがわかります。</p>
<p>それでは以下のようにバージョンを固定しましょう。</p>
<pre class="language-json"><code class="language-json"><span class="token punctuation">{</span>
    <span class="token property">&quot;dependencies&quot;</span><span class="token operator">:</span> <span class="token punctuation">{</span>
        <span class="token property">&quot;react&quot;</span><span class="token operator">:</span> <span class="token string">&quot;18.2.0&quot;</span><span class="token punctuation">,</span>
        <span class="token property">&quot;react-dom&quot;</span><span class="token operator">:</span> <span class="token string">&quot;18.2.0&quot;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre>
<p>これでエラーは解消されます。</p>
<pre class="language-bash"><code class="language-bash">❯ <span class="token function">yarn</span> npmPkgJsonLint <span class="token builtin class-name">.</span>
✨  Done <span class="token keyword">in</span> <span class="token number">0</span>.87s. <span class="token comment"># エラーが出ない</span>
</code></pre>
<h2>CI での実行</h2>
<p>これを GitHub Actions などのワークフローに組み込むことで、バージョンが固定されていないパッケージをリリース前に発見することができます。
以下は GitHub Actions の CI ワークフローに追加した例です。</p>
<pre class="language-yaml"><code class="language-yaml"><span class="token key atrule">name</span><span class="token punctuation">:</span> CI
<span class="token key atrule">on</span><span class="token punctuation">:</span> <span class="token punctuation">[</span>push<span class="token punctuation">]</span>
<span class="token key atrule">jobs</span><span class="token punctuation">:</span>
    <span class="token key atrule">lint</span><span class="token punctuation">:</span>
        <span class="token key atrule">name</span><span class="token punctuation">:</span> lint
        <span class="token key atrule">runs-on</span><span class="token punctuation">:</span> ubuntu<span class="token punctuation">-</span>latest
        <span class="token key atrule">timeout-minutes</span><span class="token punctuation">:</span> <span class="token number">20</span>
        <span class="token key atrule">steps</span><span class="token punctuation">:</span>
            <span class="token punctuation">-</span> <span class="token key atrule">uses</span><span class="token punctuation">:</span> actions/checkout@v3
            <span class="token punctuation">-</span> <span class="token key atrule">uses</span><span class="token punctuation">:</span> actions/setup<span class="token punctuation">-</span>node@v3
              <span class="token key atrule">with</span><span class="token punctuation">:</span>
                  <span class="token key atrule">node-version-file</span><span class="token punctuation">:</span> <span class="token string">&quot;package.json&quot;</span>
                  <span class="token key atrule">cache</span><span class="token punctuation">:</span> <span class="token string">&quot;yarn&quot;</span>
            <span class="token punctuation">-</span> <span class="token key atrule">run</span><span class="token punctuation">:</span> yarn install
            <span class="token punctuation">-</span> <span class="token key atrule">run</span><span class="token punctuation">:</span> yarn npmPkgJsonLint .
</code></pre>
<p>CI のステップ内で<code>npm-package-json-lint</code>を実行することで、package.json のルールに違反しているパッケージがあればエラーが出力されます。</p>
<p>これでコードレビュー時にバージョンが固定されていないパッケージが追加されていないか確認する必要がなくなりました。</p>]]></content:encoded>
            <author>michinobu.shimatani@gmail.com (michio)</author>
        </item>
        <item>
            <title><![CDATA[GitHub Copilotのコツ]]></title>
            <link>https://michio.me/articles/github-copilot</link>
            <guid>https://michio.me/articles/github-copilot</guid>
            <pubDate>Sun, 29 Jan 2023 00:00:00 GMT</pubDate>
            <description><![CDATA[2022 年末にGitHub Copilotを試しに導入しました。これはとても良いですね。関数名や変数を定義すると、このあと自分が書こうと思っていたコードが高い精度でサジェストされる。この「お前わかってるな」感が気に入りました。]]></description>
            <content:encoded><![CDATA[<p>2022 年末に<a href="https://github.com/features/copilot">GitHub Copilot</a>を試しに導入しました。これはとても良いですね。</p>
<p>関数名や変数を定義すると、このあと自分が書こうと思っていたコードが高い精度でサジェストされる。この「お前わかってるな」感が気に入りました。2023 年 1 月時点で Individuals プランであれば、月$10 だけど、体感 1 週間以内には払った金額は回収できると思った。
実際最近のインタビューで GitHub の CEO が<a href="https://xtech.nikkei.com/atcl/nxt/column/18/00677/122100126/">Copilot を使っている開発者は、新規コードの 40％を Copilot で記述している</a>と答えているのを見た。
数字を聞くと驚くが、一度 Copilot を使ってみるとこれくらいの数字でも違和感はない。</p>
<p>導入して 2 ヶ月弱、Copilot を使った実装にも慣れてきた。大分コツを掴んできた気がするので一度今の使い方をまとめてみる。(なおこの文章では Visual Studio Code での開発を想定している)</p>
<h2>Codex: GitHub Copilot の本体</h2>
<p>GitHub Copilot 自体はこの記事では詳しく紹介しないが、このサービスの裏側で動いているモデルについて軽く触れておきます。</p>
<p>GitHub Copilot は OpenAI の <a href="https://beta.openai.com/docs/guides/code">Codex</a> というモデルを利用しています。今話題の ChatGPT で使われている GPT3 系列と同じ系列で、プログラムコードの生成に特化したモデルと言われています。
<a href="https://beta.openai.com/docs/guides/code/best-practices">こちらの公式ドキュメント</a>に Codex を使うにあたってのベストプラクティスが紹介されています。僕が感じたコツも概ねこれに沿ったものなっていたので、なんならこのドキュメントを読むだけ大体の勘所をつかめると思う。
Copilot を使い倒したいと思うなら読んで損はないでしょう。</p>
<h2>Copilot のコツ</h2>
<p>Copilot をペアプロ相手と考え、如何に正確に実装しようと考えている内容を相手に伝えるかを意識していくことが大事だと思います。
どのようにコードを書くことで Copilot に実装したい内容が伝えられるのかがだんだんわかるようになってきます。これが Copilot でのコツと言えるでしょう。</p>
<p>僕は最近 Copilot をペアプロ相手と考えるのが良いかなと思っている。自分が何をやりたいかをできる限り明確かつ具体的に伝えられるかが一番大事なコツと言えると思う。</p>
<h3>明示的なコメントや関数名をつける</h3>
<p>最も重要なコツの一つがどんなコードを書きたいのかを具体的かつ的確にコメントや関数名で表そうということです。</p>
<p>自分が指示を出される側になったと考えればイメージしやすいと思いますが、指示が曖昧であればあるほど何をアウトプットとして出せばよいかの期待がズレてしまうでしょう。
これは Copilot だって同じです。「なんかいい感じの処理をアレで実装しといて」くらい何も言ってないのと同義な指示を出されれば困ってしまいますよね。</p>
<p>このコツは単純に自分のプログラミングスキルを向上させることと同義かもしれません。人間が読んで理解しやすいコメント・関数名であれば、Copilot も正確にサジェストを出してもらう可能性が高まります。
一般的に読みやすいコードとはどういうものかについては、リーダブルコードなどの書籍で説明されている内容が該当するでしょう。</p>
<h3>関数内にコメントを書く</h3>
<p>Copilot は小さく切り出された関数のようにシンプルな処理に対しては精度高くサジェストしてくれるが、複雑な処理に対しては精度が下がる傾向にあります。とはいえ全ての処理を関数に分けるのは現実的ではありません。
関数内のコメントは、こういったケースで有用です。</p>
<p>関数内にコメントを書くことで、その処理内で実行したい処理に関する情報を追加して Copilot に伝えることができます。コンテキストやコードに対する意図が明確になればなるほど、サジェストされるコードの精度が上がります。</p>
<img alt="" srcSet="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2F2023-01-22-22-18-02.3fd81194.png&amp;w=1920&amp;q=75 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2F2023-01-22-22-18-02.3fd81194.png&amp;w=3840&amp;q=75 2x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2F2023-01-22-22-18-02.3fd81194.png&amp;w=3840&amp;q=75" width="1366" height="802" decoding="async" data-nimg="1" loading="lazy" style="color:transparent"/>
<h3>使ってほしい関数・ライブラリなどはコメントで指定する</h3>
<p>特定のライブラリを使ってほしい場合は、そのライブラリを import しておくと、そのライブラリの関数をサジェストしてくれます。これはコメントに記述しておくことも有効です。</p>
<p>ライブラリと書いたが、自分のリポジトリ内の特定コードも同じ扱いです。</p>
<pre class="language-typescript"><code class="language-typescript"><span class="token keyword">const</span> awesome <span class="token operator">=</span> <span class="token keyword">require</span><span class="token punctuation">(</span><span class="token string">&quot;./awesomeFunctions&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">// create wonderful sort function to use awesome.sort</span>
<span class="token comment">// ここ以降にサジェストが表示される</span>
</code></pre>
<p>上記のようにコメント内に利用したい関数などを明示しておくことで、Copilot はその関数を使ったサジェストを表示してくれる可能性が高まります。</p>
<h3>GitHub Copilot はリポジトリのどこまで読み込んでいるのか</h3>
<p>Copilot の <a href="https://github.com/features/copilot">Q&amp;A</a>には以下のように記述されています。</p>
<blockquote>
<p>Code Snippets Data
Depending on your preferred telemetry settings, GitHub Copilot may also collect and retain the following, collectively referred to as “code snippets”: source code that you are editing, related files and other files open in the same IDE or editor, URLs of repositories and files path.</p>
</blockquote>
<blockquote>
<p>お客様が希望する遠隔測定設定に応じて、GitHub Copilot は以下のものも収集・保持することがあります（総称して「コードスニペット」）：お客様が編集中のソースコード、関連ファイル、同じ IDE またはエディタで開いている他のファイル、リポジトリの URL、ファイルのパス。(DeepL による翻訳)</p>
</blockquote>
<p>つまり現在編集中のファイルと、リポジトリ内に存在するそのファイルに関連するファイルを読み込んでいるようです。
ポイントは<strong>そのファイルに関連するファイルをどう判定しているか</strong>だが、確実なのは import などによって読み込んでいるファイルを関連ファイルと位置づけているということだろう。
使ってほしいファイルやライブラリについては、あらかじめ import で読み込んでおいたり、タブで開いておくなどしておくと Copilot がこれらを活用しより良いサジェストをしてくれます。</p>
<h3>コメント内に複数の処理を並べて書くと、その処理をサジェストしてくれる</h3>
<p>いくら関数名を工夫しても実現したい処理が複雑であればあるほど、適切に表現するのが難しいこともあります。これは関数として別で切り出す必要性の証拠かもしれませんね。</p>
<p>例えば処理の内容が数ステップに分かれているような場合は、それをコメントに記述しておくと Copilot はうまく理解し、反映したコードを提案してくれます。</p>
<img alt="" srcSet="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2F2023-01-21-22-26-32.35e6f021.png&amp;w=1200&amp;q=75 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2F2023-01-21-22-26-32.35e6f021.png&amp;w=3840&amp;q=75 2x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2F2023-01-21-22-26-32.35e6f021.png&amp;w=3840&amp;q=75" width="1124" height="548" decoding="async" data-nimg="1" loading="lazy" style="color:transparent"/>
<p>上記の例ではコメントに従い、<code>animals</code>と<code>cities</code>を定義し、<code>stories</code>を返すようなコードをサジェストしてくれています。</p>
<p>実装者が処理の流れをうまくコメントに書けるかどうかで、サジェストの質が変わります。</p>
<h3>コメントで指示を出す</h3>
<p>Copilot は思っていた以上に柔軟にコメントを理解してくれます。
以下の例はコメントで特定の関数のユニットテストを書くように指示をしています。</p>
<img alt="" srcSet="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2F2023-01-22-00-09-58.1fe5a03e.png&amp;w=1920&amp;q=75 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2F2023-01-22-00-09-58.1fe5a03e.png&amp;w=3840&amp;q=75 2x" src="/_next/image?url=%2F_next%2Fstatic%2Fmedia%2F2023-01-22-00-09-58.1fe5a03e.png&amp;w=3840&amp;q=75" width="1918" height="772" decoding="async" data-nimg="1" loading="lazy" style="color:transparent"/>
<p>この例では<code>sorArrayByDate</code>という関数を実装されているという前提で、コメント内でこの関数に対してテストを書けと指示をしています。
Copilot はキチンとコメントの内容を読み取り、コメント上部に記述されている<code>sorArrayByDate</code>のテストコードを提案してくれます。</p>
<p>こういった指示出しはユニットコードの生成以外でも大いに有効です。例えば関数を実装する際に特定の構造体を利用指示したり、内部の処理で使用する関数を指定できたりします。</p>
<h2>AI によるプログラミングのサポート</h2>
<p>最近とにかく話題の ChatGPT ですが、これをエディタに組み込んでコーディングの補佐を担ってもらう拡張ツールも話題になっています。
ChatGPT は Copilot で使われているモデルと違い、対話型のモデルです。これまで触っていた所感だと、ChatGPT はよりリファクタリングや機能の追加といったユースケースで Copilot より融通がききます。</p>
<p>実は Copilot にもまだ β 版ですが GitHub Copilot Labs という experimental 版の拡張ツールが存在し、リファクタリングやテストコードの生成、ドキュメンテーションといった様々な機能を試すことができます。
これらはまだまだ荒削りな部分が多いですが、今後確実に精度を上がっていくでしょうし、AI によるプログラミングのサポートは今後ますます便利になっていくでしょう。
ソフトウェアエンジニアの一人としては、自分は今後エンジニアとしての価値をどこで発揮するかを真剣に考える必要があると感じています。</p>
<h2>どう付き合っていくか</h2>
<p>GitHub Copilot のようなツールは開発者にどのような影響を与えるかについて、公式の Q&amp;A では以下のように回答されていました。</p>
<blockquote>
<p>Bringing in more intelligent systems has the potential to bring enormous change to the developer experience. We do not expect GitHub Copilot to replace developers. Rather, we expect GitHub Copilot to partner with developers, augment their capabilities, and enable them to be more productive, reduce manual tasks, and help them focus on interesting work. We also believe that GitHub Copilot has the potential to lower barriers to entry, enabling more people to explore software development, and join the next generation of developers.</p>
</blockquote>
<blockquote>
<p>よりインテリジェントなシステムを導入することで、開発者のエクスペリエンスに大きな変化をもたらす可能性があります。私たちは GitHub Copilot が開発者に取って代わることを期待しているわけではありません。むしろ、GitHub Copilot は開発者のパートナーとなり、彼らの能力を増強し、より生産的に、手作業を減らし、興味深い仕事に集中できるようにすることを期待しています。また、GitHub Copilot は、ソフトウェア開発への参入障壁を低くし、より多くの人がソフトウェア開発を探求し、次世代の開発者に加わることを可能にする可能性を持っていると考えています。(DeepL による翻訳)</p>
</blockquote>
<p>Copilot は開発者のパートナーであると言っていますが、これは僕個人の実感としても正しいと感じます。
Copilot が細かい実装の補助をサポートしてくれるので、自分は何を作るかにより集中できるようになりました。僕個人はCopilot を使い始めて、実装がなんだか楽しくなったなぁという気がとてもしますね。
なんとなく胡散臭さを感じて Copilot を使わない人もいるかもしれませんが、絶対もったいないです。ぜひ使ってみてください。</p>
<p>今後さらに Copilot のようなサポートツールが発展してくことが間違いないと思います。その中で単純に How を提供するソフトウェアエンジニアの価値は相対的に下がっていく危機感も同時に感じました。
エンジニアとしての価値をどこで発揮するかを考え抜き、自分の価値を高めていくことが大切だと思います。</p>]]></content:encoded>
            <author>michinobu.shimatani@gmail.com (michio)</author>
        </item>
    </channel>
</rss>