oinume journal Scratchpad of what I learned 2024-01-08T08:30:00+09:00 oinume Hatena::Blog hatenablog://blog/13208692334729889706 Raycastを使い始めて1年経った hatenablog://entry/6801883189072801366 2024-01-08T08:30:00+09:00 2024-01-08T10:06:07+09:00 Raycastを使い始めて1年経ったので、どういうことに使っているかを振り返ってみる。去年書いた AlfredからRaycastに移行した - oinume journal の記事から少し使い方が変わっているところもあるのでメモがてら。 基本的な使い方 Cmd + QをRaycast起動のショートカットとして割り当てている。Pro版は使っていないのでAI機能などは使ったことがない。 ブラウザのブックマーク検索など、よく使うけどHotKeyを割り当てるほどでもないRaycastコマンドはbmのようにAliasを設定している。 Cmd + QでRaycastを起動してbmと入力するとブックマークの… <p>Raycastを使い始めて1年経ったので、どういうことに使っているかを振り返ってみる。去年書いた <a href="https://journal.lampetty.net/entry/alfred-to-raycast">Alfred&#x304B;&#x3089;Raycast&#x306B;&#x79FB;&#x884C;&#x3057;&#x305F; - oinume journal</a> の記事から少し使い方が変わっているところもあるのでメモがてら。</p> <h2 id="基本的な使い方">基本的な使い方</h2> <ul> <li><code>Cmd + Q</code>をRaycast起動のショートカットとして割り当てている。Pro版は使っていないのでAI機能などは使ったことがない。</li> <li>ブラウザのブックマーク検索など、よく使うけどHotKeyを割り当てるほどでもないRaycastコマンドは<code>bm</code>のようにAliasを設定している。 <ul> <li><code>Cmd + Q</code>でRaycastを起動して<code>bm</code>と入力するとブックマークの検索ができるので楽ちん</li> </ul> </li> </ul> <h2 id="アプリケーションランチャー機能">アプリケーションランチャー機能</h2> <p>アプリケーションを起動するときのランチャーとして使っている。よく使うアプリにはHot Key(ショートカット)を割り当ててる。</p> <h2 id="Clipboard-History">Clipboard History</h2> <p>クリップボードの履歴を溜めておいて、後から検索してペーストできる機能。<code>Ctrl + Cmd + C</code>をHotKeyとして割り当てて瞬時に起動できるようにしている。</p> <h2 id="絵文字検索">絵文字検索</h2> <p>正確にはSearch Emoji &amp; Symbolsという機能。これが地味に便利な機能で、こんな感じで英語で絵文字を検索して選択するとクリップボードにコピーされるのですぐにペーストできる。</p> <iframe src="https://drive.google.com/file/d/1yPG77VLTMqdUWEB5-TiGxGS4DUdCZ7yr/preview" width="640" height="480" allow="autoplay"></iframe> <h2 id="スクリーンショットの検索クリップボードへのコピーSlackやGitHubへ貼り付け">スクリーンショットの検索&クリップボードへのコピー&SlackやGitHubへ貼り付け</h2> <p>これも便利な機能で、以下のような流れで使うのだけど、スクショを貼り付けるのがキーボード操作だけでできるようになる。</p> <ul> <li>スクリーンショットを撮った後にRaycastを起動</li> <li><code>screenshot</code>で検索して機能を起動して、スクショをサムネイルで検索して選択</li> <li>するとクリップボードにスクショがコピーされるので、SlackやGitHubなどにペーストするとスクショが貼り付けられる。</li> </ul> <iframe src="https://drive.google.com/file/d/1SrHCEI1sHrbHyB0GTDNv_WQu6aaJtGMg/preview" width="640" height="480" allow="autoplay"></iframe> <h2 id="ブラウザのブックマーク検索">ブラウザのブックマーク検索</h2> <p>正式名称はSearch Browser Bookmarks。ブラウザのブックマークを検索することができる。SafariとChromeがある場合は、Cmd + Kで<code>Select Browsers</code>を選ぶと対象のブラウザを選択することができる(Safari, Chromeの両方を対象にすることも可能)。</p> <h2 id="開いているウィンドウの検索切り替え">開いているウィンドウの検索・切り替え</h2> <p>正式名称はSwitch Windows。今開いているウィンドウをfuzzy検索できる。自分はIntelliJ IDEAを使っていてたくさんウィンドウを開いているので、これで検索してウィンドウを切り替えることが多い。</p> <h2 id="Chromeの履歴検索">Chromeの履歴検索</h2> <p>「あの閉じてしまったタブをもう1回開きたいなぁ」という時に意外と重宝するのがChromeの履歴検索である。<a href="https://www.raycast.com/Codely/google-chrome">Chrome Extension</a>をインストールすると使えるようになる。</p> <h2 id="使わなくなった機能">使わなくなった機能</h2> <p>最初便利だなと思っていたけど使わなくなった機能もある。</p> <ul> <li>Quicklink: ブラウザのブックマークで十分だと感じた</li> <li>カレンダー連携(My Schedule): ChromeでGoogleカレンダーを固定タブで開いているのでRaycastから飛べなくてもいいかなと思った。あと、MacのカレンダーとGoogle Calendarを連携するのが嫌になったというのもある。</li> </ul> <h2 id="Raycastの使いこなせていない機能">Raycastの使いこなせていない機能</h2> <p>Raycast便利に使っているけど、Script CommandsやExtensionを作れるほどのレベルにはなっていないので、今年はそれらに挑戦してみたい。</p> oinume HasuraをDokku上で動かす hatenablog://entry/6801883189070937638 2023-12-30T15:00:00+09:00 2024-01-01T08:54:55+09:00 VPS上にDokkuを構築したので、そこにHasuraを動かしてみるテスト。Hasuraとは簡単に言うとPostgreSQLのテーブルスキーマからGraphQLサーバーを構築してくれるミドルウェア。 前提として、Dokkuはすでに構築済みとする。(自分の場合はUbuntu 22.04 上にDokku 0.32.3を構築済み) 手順 まずは最初にhasuraという名前のアプリケーションを作成する $ dokku apps:create hasura 次に、HasuraのためのPostgreSQL databaseを建てる。 $ dokku postgres:create hasura-db Wa… <p><a href="https://journal.lampetty.net/entry/install-dokku-on-vps">VPS上にDokkuを構築した</a>ので、そこに<a href="https://hasura.io/">Hasura</a>を動かしてみるテスト。Hasuraとは簡単に言うとPostgreSQLのテーブルスキーマからGraphQLサーバーを構築してくれるミドルウェア。</p> <p>前提として、Dokkuはすでに構築済みとする。(自分の場合はUbuntu 22.04 上にDokku 0.32.3を構築済み)</p> <h2 id="手順">手順</h2> <p>まずは最初に<code>hasura</code>という名前のアプリケーションを作成する</p> <pre class="code shell" data-lang="shell" data-unlink>$ dokku apps:create hasura</pre> <p>次に、HasuraのためのPostgreSQL databaseを建てる。</p> <pre class="code shell" data-lang="shell" data-unlink>$ dokku postgres:create hasura-db Waiting for container to be ready Creating container database Securing connection to database =====&gt; Postgres container created: hasura-db =====&gt; hasura-db postgres service information Config dir: /var/lib/dokku/services/postgres/hasura-db/data Config options: Data dir: /var/lib/dokku/services/postgres/hasura-db/data Dsn: postgres://postgres:&lt;redacted&gt;@dokku-postgres-hasura-db:5432/hasura_db Exposed ports: - Id: 272a6f8b722310887317b3d7e10821ed3cfcc88dae7911945c10590be338c09c Internal ip: 172.17.0.4 Initial network: Links: - Post create network: Post start network: Service root: /var/lib/dokku/services/postgres/hasura-db Status: running Version: postgres:15.4</pre> <p>上で構築したhasura-dbをhasuraアプリケーションにリンクする。ただし、この時点ではまだ<code>hasura</code>はアプリケーションの設定がされていないので<code>App image (dokku/hasura:latest) not found</code>のエラーが出るけどOK。</p> <pre class="code shell" data-lang="shell" data-unlink>$ dokku postgres:link hasura-db hasura -----&gt; Setting config vars DATABASE_URL: postgres://postgres:&lt;redacted&gt;@dokku-postgres-hasura-db:5432/hasura_db -----&gt; Restarting app hasura ! App image (dokku/hasura:latest) not found</pre> <p>次にhasura向けの環境変数を設定する。HASURA_GRAPHQL_DATABASE_URLで指定するDSNは上のコマンドで得られた<code>DATABASE_URL</code>をコピーしてくる。また、<code>your_admin_secret</code>はパスワードみたいなものなので、ランダム文字列にするのが良い。</p> <pre class="code" data-lang="" data-unlink>$ dokku config:set hasura \ HASURA_GRAPHQL_DATABASE_URL=&#34;postgres://postgres:&lt;redacted&gt;@dokku-postgres-hasura-db:5432/hasura_db&#34; \ HASURA_GRAPHQL_ADMIN_SECRET=&#34;your_admin_secret&#34; \ HASURA_GRAPHQL_ENABLE_CONSOLE=&#34;true&#34; \ HASURA_GRAPHQL_SERVER_PORT=5000</pre> <p>次にhasuraアプリケーションのためのdocker imageをpullしてくる。</p> <pre class="code shell" data-lang="shell" data-unlink>$ docker pull hasura/graphql-engine:v2.36.1 FAIL</pre> <p>上のコマンドを実行するユーザーが<code>docker</code>グループに所属していないと権限がなくてpullできないので、自分の場合は<code>dokku</code>ユーザーになってpullした。</p> <pre class="code shell" data-lang="shell" data-unlink>$ sudo su - dokku $ docker pull hasura/graphql-engine:v2.36.1</pre> <p>そして最後に <code>dokku git:from-image</code>コマンドでhasuraアプリケーションにpullしてきたimageを指定してアプリケーションとしてデプロイする。</p> <pre class="code shell" data-lang="shell" data-unlink>$ dokku git:from-image hasura hasura/graphql-engine:v2.36.1</pre> <p>これで <code>http://hasura.&lt;your_dokku_domain&gt;/</code> のURLにアクセスするとHasura Consoleが表示されるはず。もしエラー画面が出ている場合は、/var/log/nginx/hasura-error.log を見てみると何かヒントがあるかもしれない。</p> <h2 id="ハマった点">ハマった点</h2> <p>Dokku上のコンテナのlisten portはデフォルトだと<code>5000</code>になるということを知らずに、環境変数<code>HASURA_GRAPHQL_SERVER_PORT</code>を指定していなかったため、Hasuraはデフォルトの8080でlistenしていてDokkuのNginxからDocker containerへのproxyがうまくできていなかった。</p> <p>そのため</p> <blockquote><p>We're sorry, but something went wrong. If you are the application owner check the logs for more information.</p></blockquote> <p>のエラーがずっと出ていたけど(スクショ)、解決方法がなかなかググっても出てこずにハマった。</p> <iframe src="https://drive.google.com/file/d/1OaMdyKQuw2hrAJ09vJKNsoXATwLO5j79/preview" width="640" height="480" allow="autoplay"></iframe> <p>Dokkuの<a href="https://dokku.com/docs/networking/port-management/">Port Managementのドキュメント</a>に書いてはあるのだが、正直そんなの知らんわって思った。でもおかげでDokkuのport mappingの仕組みが理解できたので良かったけど。</p> <h2 id="成功したら">成功したら...</h2> <p>以下のような画面が出るので、<code>HASURA_GRAPHQL_ADMIN_SECRET</code>の環境変数で指定したシークレットを入力するとめでたくHasuraのConsoleが見れるようになる。</p> <iframe src="https://drive.google.com/file/d/1sU2RH-Mqrd541NmV2_54ow93-cU6ckRW/preview" width="640" height="480" allow="autoplay"></iframe> <p>※この記事を書き終わってHasura on Dokkuの構築自体に満足してしまい、肝心のアプリケーションの作成はまだできていない。</p> <p><div class="hatena-asin-detail"><a href="https://www.amazon.co.jp/dp/B0BV8VCCYX?tag=kazzhomeunixo-22&amp;linkCode=ogi&amp;th=1&amp;psc=1" class="hatena-asin-detail-image-link" target="_blank" rel="noopener"><img src="https://m.media-amazon.com/images/I/518YvXHcICL._SL500_.jpg" class="hatena-asin-detail-image" alt="Docker実践ガイド 第3版 impress top gearシリーズ" title="Docker実践ガイド 第3版 impress top gearシリーズ"></a><div class="hatena-asin-detail-info"><p class="hatena-asin-detail-title"><a href="https://www.amazon.co.jp/dp/B0BV8VCCYX?tag=kazzhomeunixo-22&amp;linkCode=ogi&amp;th=1&amp;psc=1" target="_blank" rel="noopener">Docker実践ガイド 第3版 impress top gearシリーズ</a></p><ul class="hatena-asin-detail-meta"><li><span class="hatena-asin-detail-label">作者:</span><a href="https://d.hatena.ne.jp/keyword/%B8%C5%B2%EC%C0%AF%BD%E3" class="keyword">古賀政純</a></li><li>インプレス</li></ul><a href="https://www.amazon.co.jp/dp/B0BV8VCCYX?tag=kazzhomeunixo-22&amp;linkCode=ogi&amp;th=1&amp;psc=1" class="asin-detail-buy" target="_blank" rel="noopener">Amazon</a></div></div></p> oinume squash mergeの環境でCascading PRsでコンフリクトした時 hatenablog://entry/6801883189063458483 2023-12-04T09:00:00+09:00 2023-12-07T15:55:43+09:00 最近以下のような記事を目にすることがあり、ちょうど自分もsquash mergeの環境でひとつ問題を抱えていたので、その話を自分のメモ代わりに書きたくなった。 Git の Squash マージをやめた話 - Mobile Factory Tech Blog squash and mergeしか使ってないけど全く困ってない – Jun Mukai's blog 問題とは、上にある記事のcascading PRsの話で、feature1 -> feature2 というようにbranchを派生して作った場合に、feature1 をmainにマージした後に作業ブランチを feature2 にして gi… <p>最近以下のような記事を目にすることがあり、ちょうど自分もsquash mergeの環境でひとつ問題を抱えていたので、その話を自分のメモ代わりに書きたくなった。</p> <ul> <li><a href="https://tech.mobilefactory.jp/entry/2023/11/29/160000">Git &#x306E; Squash &#x30DE;&#x30FC;&#x30B8;&#x3092;&#x3084;&#x3081;&#x305F;&#x8A71; - Mobile Factory Tech Blog</a></li> <li><a href="https://wp.jmuk.org/2023/11/30/squash-and-merge%E3%81%97%E3%81%8B%E4%BD%BF%E3%81%A3%E3%81%A6%E3%81%AA%E3%81%84%E3%81%91%E3%81%A9%E5%85%A8%E3%81%8F%E5%9B%B0%E3%81%A3%E3%81%A6%E3%81%AA%E3%81%84/">squash and merge&#x3057;&#x304B;&#x4F7F;&#x3063;&#x3066;&#x306A;&#x3044;&#x3051;&#x3069;&#x5168;&#x304F;&#x56F0;&#x3063;&#x3066;&#x306A;&#x3044; &ndash; Jun Mukai&#39;s blog</a></li> </ul> <p>問題とは、上にある記事の<code>cascading PRs</code>の話で、feature1 -> feature2 というようにbranchを派生して作った場合に、feature1 をmainにマージした後に作業ブランチを feature2 にして git rebase main すると、必ずコンフリクトが発生するので悩んでいた、ということ。</p> <p>解決策としては、以下にあるように<code>git rebase --onto main &lt;last commit on feature1&gt; feature2</code> をすればいいらしい。「らしい」というのはまだ自分は試してないけど、同僚がこれで「うまくいけた」と言っていたので。</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fstackoverflow.com%2Fquestions%2F68229841%2Favoiding-conflicts-in-git-when-merging-a-squashed-commit-from-main-into-feature" title="Avoiding conflicts in git when merging a squashed commit from main into feature branch" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://stackoverflow.com/questions/68229841/avoiding-conflicts-in-git-when-merging-a-squashed-commit-from-main-into-feature">stackoverflow.com</a></cite></p> <p>もしくはJun Mukai's blogに書いてあるような以下の方法でもいいのかもしれない。そういえば <code>git rebase --skip</code> って使ったことなかったな。</p> <blockquote><p>PR1に由来するコンフリクトは自分にとっては自明だし、ほとんどの場合にはgit rebase –skipするだけだ。</p></blockquote> <p><div class="hatena-asin-detail"><a href="https://www.amazon.co.jp/dp/4774185930?tag=kazzhomeunixo-22&amp;linkCode=ogi&amp;th=1&amp;psc=1" class="hatena-asin-detail-image-link" target="_blank" rel="noopener"><img src="https://m.media-amazon.com/images/I/516gEDDGmoL._SL500_.jpg" class="hatena-asin-detail-image" alt="【改訂新版】Gitポケットリファレンス" title="【改訂新版】Gitポケットリファレンス"></a><div class="hatena-asin-detail-info"><p class="hatena-asin-detail-title"><a href="https://www.amazon.co.jp/dp/4774185930?tag=kazzhomeunixo-22&amp;linkCode=ogi&amp;th=1&amp;psc=1" target="_blank" rel="noopener">【改訂新版】Gitポケットリファレンス</a></p><ul class="hatena-asin-detail-meta"><li><span class="hatena-asin-detail-label">作者:</span><a href="https://d.hatena.ne.jp/keyword/%B2%AC%CB%DC%20%CE%B4%BB%CB" class="keyword">岡本 隆史</a>,<a href="https://d.hatena.ne.jp/keyword/%C9%F0%C5%C4%20%B7%F2%C2%C0%CF%BA" class="keyword">武田 健太郎</a>,<a href="https://d.hatena.ne.jp/keyword/%C1%EA%CE%C9%20%B9%AC%C8%CF" class="keyword">相良 幸範</a></li><li>技術評論社</li></ul><a href="https://www.amazon.co.jp/dp/4774185930?tag=kazzhomeunixo-22&amp;linkCode=ogi&amp;th=1&amp;psc=1" class="asin-detail-buy" target="_blank" rel="noopener">Amazon</a></div></div></p> oinume VPSにDokkuをインストールする hatenablog://entry/820878482973773817 2023-10-07T20:10:00+09:00 2023-12-30T15:14:53+09:00 VPS上にDokkuというものをインストールして、Dokku上でHello WorldするRailsアプリを動かすまでのメモ。 Why Dokku? DokkuのWebサイトに書いてあるように、Dokkuはオープンソース版のHerokuという立ち位置のソフトウェア。かれこれ7年ぐらいHerokuを使い続けていてとても便利だと思っているのだけど、アプリケーションを1つ動かすのに$5かかるようになってしまった。昨今の円安で750円ぐらいかかるし、アプリを複数作るとなるとそれなりにお金がかかるので、VPSでDokkuを動かしその上でアプリケーションを稼働させるようにしたいと思った。 また、WebAR… <p>VPS上に<a href="https://dokku.com/">Dokku</a>というものをインストールして、Dokku上でHello WorldするRailsアプリを動かすまでのメモ。</p> <h2 id="Why-Dokku">Why Dokku?</h2> <p><a href="https://dokku.com/">DokkuのWebサイト</a>に書いてあるように、Dokkuはオープンソース版のHerokuという立ち位置のソフトウェア。かれこれ7年ぐらいHerokuを使い続けていてとても便利だと思っているのだけど、アプリケーションを1つ動かすのに$5かかるようになってしまった。昨今の円安で750円ぐらいかかるし、アプリを複数作るとなるとそれなりにお金がかかるので、VPSでDokkuを動かしその上でアプリケーションを稼働させるようにしたいと思った。</p> <p>また、<a href="https://web.arena.ne.jp/indigo/">WebARENA Indigo</a>のVPSだと、2vCPUでメモリ2GBで税込814円 / 月という安さなので、これを使うとかなりコスト抑えられそうだなと思ったからだった。</p> <h2 id="インストール">インストール</h2> <p>以下の環境にDokku 0.32.3をインストールする。</p> <ul> <li>WebARENA IndigoのVPS(4vCPU, MEM 4GB, SSD 80GB)</li> <li>OS: Ubuntu 22.04</li> </ul> <p>インストール自体は<a href="https://dokku.com/docs/getting-started/install/debian/">Dokkuのドキュメント</a>の通り、aptを使ってインストールすれば特にハマることはないはず。</p> <h2 id="SSHの公開鍵の登録">SSHの公開鍵の登録</h2> <p>アプリケーションのデプロイ時に<code>git push dokku ...</code>とやる場合、sshの公開鍵が登録されていないと失敗するので、以下のコマンドで登録しておく。</p> <pre class="code" data-lang="" data-unlink>$ echo &#39;CONTENTS_OF_ID_RSA_PUB_FILE&#39; | sudo dokku ssh-keys:add admin</pre> <h2 id="ドメインの設定">ドメインの設定</h2> <p>インターネット上にDokkuを使ってアプリケーションを公開するためには、当然ながらドメインが必要なので用意しておく。自分は dokku.lampetty.net をベースドメインにして、これのサブドメインでアプリケーションを公開していくつもりなので、必要な設定をした。</p> <p>まず、 <code>domains:report</code> コマンドで現状のドメインを確認する。</p> <pre class="code bash" data-lang="bash" data-unlink>$ dokku domains:report --global =====&gt; Global domains information Domains global enabled: true Domains global vhosts: i-xxxxxxxxx</pre> <p><code>Domains global vhosts</code>はデフォルトではホストマシンのホスト名になっているので、これを以下のコマンドで変更する。</p> <pre class="code bash" data-lang="bash" data-unlink>dokku domains:set-global dokku.lampetty.net</pre> <p>次に、<code>dokku.lampetty.net</code> サブドメインがこのVPSのIPアドレスになるようにDNSレコードを定義する。Aレコードの方は別になくてもいいけど、IPとのマッピングは1箇所にしたいのでこうしている。</p> <pre class="code" data-lang="" data-unlink>dokku.lampetty.net A &lt;VPSのIP&gt; *.dokku.lampetty.net CNAME dokku.lampetty.net</pre> <p>digコマンドでDNSレコードを確認して、以下のように返ってくればOK。</p> <pre class="code bash" data-lang="bash" data-unlink>$ dig example.dokku.lampetty.net example.dokku.lampetty.net. 300 IN CNAME dokku.lampetty.net.</pre> <p>これでドメインの設定は完了したので、サンプルアプリケーションをデプロイする。</p> <h2 id="アプリケーションのデプロイ">アプリケーションのデプロイ</h2> <p>サンプルアプリケーションのデプロイは<a href="https://dokku.com/docs/deployment/application-deployment/">ドキュメント</a>通りにやって、最後に<code>git remote add dokku ...</code>のところだけ、自分の環境に合わせて以下のようにやった。</p> <pre class="code bash" data-lang="bash" data-unlink>$ cd ruby-getting-started $ git remote add dokku dokku@dokku.lampetty.net:ruby-getting-started $ git push dokku main</pre> <h2 id="動作確認">動作確認</h2> <p><a href="http://ruby-getting-started.dokku.lampetty.net/">http://ruby-getting-started.dokku.lampetty.net/</a> にアクセスして、以下の画面が表示されればアプリケーションがデプロイされている。(ruby-getting-started 以降のドメインは先ほど設定したドメイン)</p> <iframe src="https://drive.google.com/file/d/1LVfoYbYDJteLO_B5bozLB1S4etm9Sp_l/preview" width="640" height="480" allow="autoplay"></iframe> <p>あとはアプリケーションを https でアクセスできるようにする対応なども必要だけど、いったんこれでVPS上にアプリケーションをデプロイできるようになりましたとさ。</p> <p><div class="hatena-asin-detail"><a href="https://www.amazon.co.jp/dp/4798153222?tag=kazzhomeunixo-22&amp;linkCode=ogi&amp;th=1&amp;psc=1" class="hatena-asin-detail-image-link" target="_blank" rel="noopener"><img src="https://m.media-amazon.com/images/I/5130HDgosML._SL500_.jpg" class="hatena-asin-detail-image" alt="プログラマのためのDocker教科書 第2版 インフラの基礎知識&amp;コードによる環境構築の自動化" title="プログラマのためのDocker教科書 第2版 インフラの基礎知識&amp;コードによる環境構築の自動化"></a><div class="hatena-asin-detail-info"><p class="hatena-asin-detail-title"><a href="https://www.amazon.co.jp/dp/4798153222?tag=kazzhomeunixo-22&amp;linkCode=ogi&amp;th=1&amp;psc=1" target="_blank" rel="noopener">プログラマのためのDocker教科書 第2版 インフラの基礎知識&amp;コードによる環境構築の自動化</a></p><ul class="hatena-asin-detail-meta"><li><span class="hatena-asin-detail-label">作者:</span><a href="https://d.hatena.ne.jp/keyword/WINGS%A5%D7%A5%ED%A5%B8%A5%A7%A5%AF%A5%C8%20%B0%A4%BA%B4%20%BB%D6%CA%DD" class="keyword">WINGSプロジェクト 阿佐 志保</a></li><li>翔泳社</li></ul><a href="https://www.amazon.co.jp/dp/4798153222?tag=kazzhomeunixo-22&amp;linkCode=ogi&amp;th=1&amp;psc=1" class="asin-detail-buy" target="_blank" rel="noopener">Amazon</a></div></div></p> oinume ししまるとしろ hatenablog://entry/820878482971605176 2023-09-30T20:00:00+09:00 2023-09-30T20:00:01+09:00 最近、新しい猫を我が家に迎え入れた。名前は「しろ」。保護猫で年齢は5~6歳。岡山で多頭飼育の崩壊後ゴミ屋敷となった民家から保護された。その後、地元の預かりボランティアのお宅では他の猫と折り合いが悪く、ほぼケージ生活となったため、他に猫のいない家庭に譲渡したいということで東京に移動してきたところで、我が家で迎え入れることになった。 ウチに来て間もない頃のしろ しろの話をする前に、このブログやTwitterにも登場していたししまるの話をしなければ、と思っている。ししまるは昨年の12月に虹の橋を渡ってしまった。昨年の夏から肺炎の症状があり投薬などで治療を続けていたが、(おそらく)間質性肺炎という難治… <p>最近、新しい猫を我が家に迎え入れた。名前は「しろ」。保護猫で年齢は5~6歳。岡山で多頭飼育の崩壊後ゴミ屋敷となった民家から保護された。その後、地元の預かりボランティアのお宅では他の猫と折り合いが悪く、ほぼケージ生活となったため、他に猫のいない家庭に譲渡したいということで東京に移動してきたところで、我が家で迎え入れることになった。</p> <p><figure class="figure-image figure-image-fotolife"><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/o/oinume/20230930/20230930160943.jpg" width="1200" height="1035" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span><figcaption>ウチに来て間もない頃のしろ</figcaption></figure></p> <p>しろの話をする前に、このブログやTwitterにも登場していたししまるの話をしなければ、と思っている。ししまるは昨年の12月に虹の橋を渡ってしまった。昨年の夏から肺炎の症状があり投薬などで治療を続けていたが、(おそらく)間質性肺炎という難治性の病気で治ることなく他界してしまった。自分にとってはペットを飼うことが初めてで、当然その死に立ち会うのも初めてだったのでとてつもなく悲しかった。3ヶ月間は毎日ししまるの写真や動画を見ては泣いてしまうという状態が続いていたし、今でも病気で辛そうだったししまるのことを思い出すと泣いてしまう。</p> <p><figure class="figure-image figure-image-fotolife" title="ししまる"><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/o/oinume/20230930/20230930163317.jpg" width="1200" height="1198" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span><figcaption>ししまる</figcaption></figure></p> <p>その悲しさに一区切りつけられたのは、今年の4月に近所のお寺でペットの合同供養祭に参加したことだと思う。ペットを亡くした飼い主たちがお寺に集まり、お坊さんが供養のお経を唱えて線香をあげるという葬儀。人間と同じように葬儀を行うことで気持ちも少し整理された気がして、その後から「また猫を迎え入れる?」という話を妻とするようになった。そんな中で妻が保護猫のサイトで見つけたのがしろだった。色が白いところや雰囲気がなんとなくししまるに似ている...! ということで、本物を見に行ったところ、体重6.5kgの大きさがししまるっぽく、とてもビビりだけど人懐っこいということで、譲渡の申請+トライアルで2週間ほどわが家に来てもらうことになった。</p> <p>うちに来た当初はずっと机の下に隠れていたけど、2,3日で家の中を探検するようになり、息子含めた全員になでなでをお願いするぐらい懐いていった。ししまるは息子にはあまり甘えなかったので、息子もとても嬉しかったらしい。</p> <p>ただ、ここで一つ問題が発生。ししまるの時は全然問題なかったのに、妻と息子の猫アレルギーが発動して、薬を飲まないとアレルギーの症状が抑えられず、「これだと迎え入れるのは厳しいかも」という状況になっていた。ひとまずトライアルを延長して、猫のアレルゲンを減らすキャットフードを試したり、体をまめにふく&ブラッシングしたりして、なんとか薬を飲まなくても大丈夫な状態にまでなった。一緒に生活していると慣れてきてアレルギーがおさまったりすることもあるそうで、何が効いたのかはいまでもよくわかってないけど、問題が解決したので一旦良しとしている。</p> <p>というわけで、無事に9月にしろを正式に迎え入れることができて、また家に猫がいる生活が始まった。白猫のため、黒いシャツを着ているとしろの抜け毛が目立ってしまい頻繁にコロコロをかけないといけないあの生活がまた戻ってきたと思うと、面倒くさいながらちょっとニヤニヤしてしまう。ししまるは家に知らない人がきても興味を持って挨拶していたりしたけど、しろはビビりで引きこもってしまうところなど、さまざまな違いを発見できて面白い。</p> <p>ししまる、今まで本当に本当にありがとう。しろ、これからよろしくね。</p> oinume Next.js + Auth0でLogin handler failedのエラーを解消する hatenablog://entry/4207112889986049555 2023-05-02T08:30:00+09:00 2023-05-02T08:30:00+09:00 Next.js + Auth0で認証機能を実装しようとして、nextjs-auth0をセットアップしつつ、このドキュメント通りにやっていたら、AUTH0_ISSUER_BASE_URL の設定を間違えていて以下のエラーが出ていた。 LoginHandlerError: Login handler failed. CAUSE: Discovery requests failing for http://localhost:3000, expected 200 OK, got: 404 Not Found 上のauth0のドキュメントでは AUTH0_ISSUER_BASE_URL='https:… <p>Next.js + Auth0で認証機能を実装しようとして、<a href="https://www.npmjs.com/package/@auth0/nextjs-auth0">nextjs-auth0</a>をセットアップしつつ、<a href="https://auth0.com/docs/quickstart/webapp/nextjs/01-login">このドキュメント</a>通りにやっていたら、<code>AUTH0_ISSUER_BASE_URL</code> の設定を間違えていて以下のエラーが出ていた。</p> <pre class="code" data-lang="" data-unlink>LoginHandlerError: Login handler failed. CAUSE: Discovery requests failing for http://localhost:3000, expected 200 OK, got: 404 Not Found</pre> <p>上のauth0のドキュメントでは <code>AUTH0_ISSUER_BASE_URL='https://{yourDomain}'</code> との記述だったので、<a href="http://localhost:3000">http://localhost:3000</a>を指定したのだけど、どうやらこれは<code>https://YOUR_AUTH0_DOMAIN.auth0.com</code>を記述するのが正しいらしい。というわけでAuth0の該当アプリケーションのページから<code>Domain</code>の部分をコピーしてきて<code>https://oinume.us.auth0.com</code>のように指定すればOK。というか<a href="https://www.npmjs.com/package/@auth0/nextjs-auth0">nextjs-auth0</a>にはそのように記載されていたのではじめからこっちを読んでおけばよかった...</p> oinume multipassでAnsibleの動作確認用のVMを作る hatenablog://entry/4207112889979269028 2023-04-12T09:00:00+09:00 2023-04-12T09:00:03+09:00 モチベーション 趣味で開発しているプロダクトのためにVPSを立てていて、Ansibleで各種ミドルウェアの設定をしているんだけど、VirtualBoxのApple Silicon への対応が微妙なのでmultipassを使ってVMを立てる+Ansible Playbookを流してセットアップするということをやってみたのでそのメモ。 multipassでVMを立ち上げる まず初めにssh用の秘密鍵と公開鍵を生成する。multipassでVMを立ち上げるときはデフォルトでSSH用の鍵を生成してくれるけど、Ansibleでsshするときの鍵を作っておく。 ssh-keygen -t rsa -b 4… <h2 id="モチベーション">モチベーション</h2> <p>趣味で開発しているプロダクトのためにVPSを立てていて、Ansibleで各種ミドルウェアの設定をしているんだけど、VirtualBoxのApple Silicon への対応が微妙なのでmultipassを使ってVMを立てる+Ansible Playbookを流してセットアップするということをやってみたのでそのメモ。</p> <h2 id="multipassでVMを立ち上げる">multipassでVMを立ち上げる</h2> <p>まず初めにssh用の秘密鍵と公開鍵を生成する。multipassでVMを立ち上げるときはデフォルトでSSH用の鍵を生成してくれるけど、Ansibleでsshするときの鍵を作っておく。</p> <pre class="code bash" data-lang="bash" data-unlink>ssh-keygen -t rsa -b 4096 -C multipass -f multipass</pre> <p>次に、以下のようなcloud-init.ymlを作成する。</p> <pre class="code lang-yaml" data-lang="yaml" data-unlink><span class="synIdentifier">users</span><span class="synSpecial">:</span> <span class="synStatement">- </span>default <span class="synStatement">- </span><span class="synIdentifier">name</span><span class="synSpecial">:</span> multipass <span class="synIdentifier">sudo</span><span class="synSpecial">:</span> ALL=(ALL) NOPASSWD:ALL <span class="synIdentifier">ssh_authorized_keys</span><span class="synSpecial">:</span> <span class="synStatement">- </span><span class="synConstant">&quot;ssh-rsa &lt;上で生成した公開鍵&gt;&quot;</span> </pre> <p>multipass launchでVMを立ち上げる時に、<code>--cloud-init</code> オプションで先ほどのcloud-init.ymlを指定する。これを行うことでmultipassユーザーの authorized_keys に上で作成したSSHの公開鍵がセットされる。</p> <pre class="code" data-lang="" data-unlink>multipass launch -n vps -c=1 -m=1G -d=5G --cloud-init=cloud-init.yml</pre> <p>次にVMのIPアドレスを確認する。</p> <pre class="code bash" data-lang="bash" data-unlink>multipass info vps Name: vps State: Running IPv4: 192.168.64.13 Release: Ubuntu 22.04.2 LTS Image hash: f6b5b3a980f2 (Ubuntu 22.04 LTS) CPU(s): 1 Load: 0.20 0.11 0.04 Disk usage: 1.4GiB out of 4.7GiB Memory usage: 151.3MiB out of 962.5MiB Mounts: --</pre> <p>Ansibleのinventoryファイルを以下のようにする。</p> <pre class="code" data-lang="" data-unlink>[local] default ansible_host=&lt;上で調べたIP&gt; ansible_port=22 ansible_user=&#39;multipass&#39; ansible_ssh_private_key_file=&#39;multipass&#39;</pre> <p>最後にansible-playbookを実行する。</p> <pre class="code" data-lang="" data-unlink>ANSIBLE_SSH_ARGS=&#39;-o IdentitiesOnly=yes -o ControlMaster=auto -o ControlPersist=60s&#39; ansible-playbook --inventory-file=ansible/inventory/multipass --timeout=30 -v -D ./ansible/hoge.yml</pre> <h2 id="multipassのコマンドメモ">multipassのコマンドメモ</h2> <ul> <li><a href="https://multipass.run/docs/transfer-command">multipass transfer</a> : VMからファイルをコピーする or ホストからVMにファイルをコピーする</li> <li><code>multipass shell</code> : VM内にシェルでログインする</li> </ul> <p><div class="hatena-asin-detail"><a href="https://www.amazon.co.jp/dp/B072K1NH76?tag=kazzhomeunixo-22&amp;linkCode=osi&amp;th=1&amp;psc=1" class="hatena-asin-detail-image-link" target="_blank" rel="noopener"><img src="https://m.media-amazon.com/images/I/51h3XbUE5EL._SL500_.jpg" class="hatena-asin-detail-image" alt="新しいLinuxの教科書" title="新しいLinuxの教科書"></a><div class="hatena-asin-detail-info"><p class="hatena-asin-detail-title"><a href="https://www.amazon.co.jp/dp/B072K1NH76?tag=kazzhomeunixo-22&amp;linkCode=osi&amp;th=1&amp;psc=1" target="_blank" rel="noopener">新しいLinuxの教科書</a></p><ul class="hatena-asin-detail-meta"><li><span class="hatena-asin-detail-label">作者:</span><a href="https://d.hatena.ne.jp/keyword/%BB%B0%C2%F0%20%B1%D1%CC%C0" class="keyword">三宅 英明</a>,<a href="https://d.hatena.ne.jp/keyword/%C2%E7%B3%D1%20%CD%B4%B2%F0" class="keyword">大角 祐介</a></li><li>SBクリエイティブ</li></ul><a href="https://www.amazon.co.jp/dp/B072K1NH76?tag=kazzhomeunixo-22&amp;linkCode=osi&amp;th=1&amp;psc=1" class="asin-detail-buy" target="_blank" rel="noopener">Amazon</a></div></div></p> oinume whisper.cppで文字起こしをやってみた hatenablog://entry/4207112889971374400 2023-03-15T08:30:00+09:00 2023-03-20T16:23:19+09:00 自分の備忘録としてのやってみた系の話。OpenAIが提供するモデルを使って文字起こしをするC++実装のwhisper.cppを試してみた。 環境 Apple M1 MacBook Air 2020 (Memory 16GB) uname -a Darwin hogehoge.local 22.3.0 Darwin Kernel Version 22.3.0: Mon Jan 30 20:39:35 PST 2023; root:xnu-8792.81.3~2/RELEASE_ARM64_T8103 x86_64 whisper.cppのビルド 以下のコマンドを実行してビルドする。 git cl… <p>自分の備忘録としてのやってみた系の話。OpenAIが提供するモデルを使って文字起こしをするC++実装の<a href="https://github.com/ggerganov/whisper.cpp">whisper.cpp</a>を試してみた。</p> <h2 id="環境">環境</h2> <p>Apple M1 MacBook Air 2020 (Memory 16GB)</p> <pre class="code bash" data-lang="bash" data-unlink>uname -a Darwin hogehoge.local 22.3.0 Darwin Kernel Version 22.3.0: Mon Jan 30 20:39:35 PST 2023; root:xnu-8792.81.3~2/RELEASE_ARM64_T8103 x86_64</pre> <h2 id="whispercppのビルド">whisper.cppのビルド</h2> <p>以下のコマンドを実行してビルドする。</p> <pre class="code bash" data-lang="bash" data-unlink>git clone https://github.com/ggerganov/whisper.cpp.git cd whisper.cpp make</pre> <p>ビルドが成功すると、カレントディレクトリに main という名前のバイナリが出来上がる。</p> <h2 id="モデルのダウンロード">モデルのダウンロード</h2> <p>次にモデルのダウンロードをしておく。</p> <pre class="code" data-lang="" data-unlink>./models/download-ggml-model.sh large</pre> <h2 id="文字起こしする音声ファイルのwavファイルを用意">文字起こしする音声ファイルのwavファイルを用意</h2> <ul> <li>whisper.cppはwavファイルしか受け付けないので、ffmpegで動画ファイルをwavにする。</li> <li>ちょうど手元に会社で公開している<a href="https://www.youtube.com/watch?v=k30W2Wz6GQw">勉強会の動画</a>があったので、これを使ってみる</li> <li>あと長いので最初の2分間だけを抽出。</li> </ul> <pre class="code" data-lang="" data-unlink>ffmpeg -i ~/Downloads/2023-02-13.mp4 -ar 16000 -to 120 2023-02-13.wav</pre> <h2 id="whispercppで文字起こし">whisper.cppで文字起こし</h2> <p>先ほど出力したwavファイルからwhisper.cppを使って文字起こしする。</p> <pre class="code" data-lang="" data-unlink>./main -m models/ggml-large.bin -f 2023-02-13.wav -l auto</pre> <p>注意点</p> <ul> <li><code>-m</code> でダウンロードしたモデルファイルを使う。<code>large</code>だと精度が高い</li> <li><code>-l auto</code> を指定しないと日本語の文字起こししてくれないので指定する。もしくは<code>-l ja</code>でもOK</li> </ul> <p>文字起こしの結果。ちなみに2分の音声ファイルを文字起こしするのに82秒ぐらいかかっている。</p> <pre class="code" data-lang="" data-unlink>[00:00:00.000 --&gt; 00:00:01.000] お願いします [00:00:01.000 --&gt; 00:00:02.000] お願いします [00:00:02.000 --&gt; 00:00:11.120] はい 本日 Dpエンジニアリングまで 第60回始めていきたいと思います [00:00:11.120 --&gt; 00:00:12.120] お願いします [00:00:12.120 --&gt; 00:00:13.120] お願いします [00:00:13.120 --&gt; 00:00:14.120] お願いします [00:00:14.120 --&gt; 00:00:21.840] 今日は React 18かな 18から新しく 出たディファードバリューっていう [00:00:21.840 --&gt; 00:00:28.440] やつの紹介ですね もともとデバウンス とかスロットルみたいな感じで [00:00:28.440 --&gt; 00:00:33.840] 各種ライブラリーが似たような やつやってたんですけど そうじゃ [00:00:33.840 --&gt; 00:00:38.800] なくて結構UIのアップデートで 最適化したやつが出ましたよっていう [00:00:38.800 --&gt; 00:00:44.320] ので もともとデバウンスっていう 本当はスロットルも出したかったん [00:00:44.320 --&gt; 00:00:49.200] ですけど React 18でスロットルのやつ がバグっていてとあるライブラリー [00:00:49.200 --&gt; 00:00:54.000] でパッてデモが用意できなかったん ですけど 一応デバウンスだけ紹介 [00:00:54.000 --&gt; 00:01:04.120] しておくと 18入力した後に時間差 で18入力されるという 今これ1秒 [00:01:04.120 --&gt; 00:01:09.680] かな 1秒ってやってるので 最後に 入力した後1秒後に値が更新される [00:01:09.680 --&gt; 00:01:15.400] ってやつですね 連続で入力してる 間はデバウンスバリューは変わん [00:01:15.400 --&gt; 00:01:20.840] ないけれど 止めてから1秒すると デバウンスバリューは変わる これ [00:01:20.840 --&gt; 00:01:27.120] 何使いたいかっていうと インクリメンタル サーチとか オリジンのところを [00:01:27.120 --&gt; 00:01:31.600] 見てもらうと 一文字変わるごとに サーチ検索API叩きまくってると [00:01:31.600 --&gt; 00:01:38.320] 重すぎるし 重すぎるので デバウンス みたいにユーザーの操作が終わった [00:01:38.320 --&gt; 00:01:44.560] 後にAPI一回叩くみたいな感じで やってあげるっていう スロットル [00:01:44.560 --&gt; 00:01:50.280] の場合は1秒ごとに発火される っていうイメージなので 例えば [00:01:50.280 --&gt; 00:01:55.240] デバウンスバリューはこれ 最後 操作終わるまではデバウンスバリュー [00:01:55.240 --&gt; 00:02:00.040] 更新されないですけど スロットル だった場合は1秒ごとに発火されて</pre> <h2 id="最後に会社の勉強会の宣伝">最後に会社の勉強会の宣伝</h2> <p>ドクターズプライムでは、DP Engineering Mondayという名前で毎週勉強会をやってます。月に1回、社外向けに開催したものをYouTubeで配信しているので、よかったらチャンネル登録してください。技術スタックはReact, TypeScript, ChakraUI, Go, GCP, Hasura, PostgreSQLなので、その周辺の話が多いです。</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fwww.youtube.com%2F%40dr.sprimedev8416" title="Dr.’s Prime Dev - YouTube" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://www.youtube.com/@dr.sprimedev8416">www.youtube.com</a></cite></p> oinume 2023年に読みたい本 hatenablog://entry/4207112889957897171 2023-01-30T09:00:00+09:00 2023-01-30T09:00:00+09:00 最近読書量が減ってきたので、今年は読書を頑張ろう!と思ったのでまずは読みたい本をリストアップしてみる。 プロを目指す人のためのTypeScript入門 仕事でTypeScript書いてるんだけど、型システムが難しすぎて詰まっているので、体系的に理解したい。 プロを目指す人のためのTypeScript入門 安全なコードの書き方から高度な型の使い方まで (Software Design plus)作者:鈴木 僚太技術評論社Amazon データ指向アプリケーションデザイン 分厚いけど良書だと噂の本。 データ指向アプリケーションデザイン ―信頼性、拡張性、保守性の高い分散システム設計の原理作者:Mar… <p>最近読書量が減ってきたので、今年は読書を頑張ろう!と思ったのでまずは読みたい本をリストアップしてみる。</p> <h2 id="プロを目指す人のためのTypeScript入門">プロを目指す人のためのTypeScript入門</h2> <p>仕事でTypeScript書いてるんだけど、型システムが難しすぎて詰まっているので、体系的に理解したい。</p> <p><div class="hatena-asin-detail"><a href="https://www.amazon.co.jp/dp/4297127474?tag=kazzhomeunixo-22&amp;linkCode=osi&amp;th=1&amp;psc=1" class="hatena-asin-detail-image-link" target="_blank" rel="noopener"><img src="https://m.media-amazon.com/images/I/510GhCOD1FL._SL500_.jpg" class="hatena-asin-detail-image" alt="プロを目指す人のためのTypeScript入門 安全なコードの書き方から高度な型の使い方まで (Software Design plus)" title="プロを目指す人のためのTypeScript入門 安全なコードの書き方から高度な型の使い方まで (Software Design plus)"></a><div class="hatena-asin-detail-info"><p class="hatena-asin-detail-title"><a href="https://www.amazon.co.jp/dp/4297127474?tag=kazzhomeunixo-22&amp;linkCode=osi&amp;th=1&amp;psc=1" target="_blank" rel="noopener">プロを目指す人のためのTypeScript入門 安全なコードの書き方から高度な型の使い方まで (Software Design plus)</a></p><ul class="hatena-asin-detail-meta"><li><span class="hatena-asin-detail-label">作者:</span><a href="http://d.hatena.ne.jp/keyword/%CE%EB%CC%DA%20%CE%BD%C2%C0" class="keyword">鈴木 僚太</a></li><li>技術評論社</li></ul><a href="https://www.amazon.co.jp/dp/4297127474?tag=kazzhomeunixo-22&amp;linkCode=osi&amp;th=1&amp;psc=1" class="asin-detail-buy" target="_blank" rel="noopener">Amazon</a></div></div></p> <h2 id="データ指向アプリケーションデザイン">データ指向アプリケーションデザイン</h2> <p>分厚いけど良書だと噂の本。</p> <p><div class="hatena-asin-detail"><a href="https://www.amazon.co.jp/dp/4873118700?tag=kazzhomeunixo-22&amp;linkCode=osi&amp;th=1&amp;psc=1" class="hatena-asin-detail-image-link" target="_blank" rel="noopener"><img src="https://m.media-amazon.com/images/I/51T+k4VRzpL._SL500_.jpg" class="hatena-asin-detail-image" alt="データ指向アプリケーションデザイン ―信頼性、拡張性、保守性の高い分散システム設計の原理" title="データ指向アプリケーションデザイン ―信頼性、拡張性、保守性の高い分散システム設計の原理"></a><div class="hatena-asin-detail-info"><p class="hatena-asin-detail-title"><a href="https://www.amazon.co.jp/dp/4873118700?tag=kazzhomeunixo-22&amp;linkCode=osi&amp;th=1&amp;psc=1" target="_blank" rel="noopener">データ指向アプリケーションデザイン ―信頼性、拡張性、保守性の高い分散システム設計の原理</a></p><ul class="hatena-asin-detail-meta"><li><span class="hatena-asin-detail-label">作者:</span><a href="http://d.hatena.ne.jp/keyword/Martin%20Kleppmann" class="keyword">Martin Kleppmann</a></li><li>オライリー・ジャパン</li></ul><a href="https://www.amazon.co.jp/dp/4873118700?tag=kazzhomeunixo-22&amp;linkCode=osi&amp;th=1&amp;psc=1" class="asin-detail-buy" target="_blank" rel="noopener">Amazon</a></div></div></p> <h2 id="モダンソフトウェアエンジニアリング">モダン・ソフトウェアエンジニアリング</h2> <p>これも良書だと噂なので。</p> <p><div class="hatena-asin-detail"><a href="https://www.amazon.co.jp/dp/4798165220?tag=kazzhomeunixo-22&amp;linkCode=osi&amp;th=1&amp;psc=1" class="hatena-asin-detail-image-link" target="_blank" rel="noopener"><img src="https://m.media-amazon.com/images/I/61-mYyDfR1L._SL500_.jpg" class="hatena-asin-detail-image" alt="モダン・ソフトウェアエンジニアリング" title="モダン・ソフトウェアエンジニアリング"></a><div class="hatena-asin-detail-info"><p class="hatena-asin-detail-title"><a href="https://www.amazon.co.jp/dp/4798165220?tag=kazzhomeunixo-22&amp;linkCode=osi&amp;th=1&amp;psc=1" target="_blank" rel="noopener">モダン・ソフトウェアエンジニアリング</a></p><ul class="hatena-asin-detail-meta"><li><span class="hatena-asin-detail-label">作者:</span><a href="http://d.hatena.ne.jp/keyword/Ivar%20Jacobson" class="keyword">Ivar Jacobson</a>,<a href="http://d.hatena.ne.jp/keyword/Harold%20%A1%C8Bud%22%20Lawson" class="keyword">Harold “Bud&quot; Lawson</a>,<a href="http://d.hatena.ne.jp/keyword/Pan-Wei%20Ng" class="keyword">Pan-Wei Ng</a>,<a href="http://d.hatena.ne.jp/keyword/Paul%20E.%20McMahon" class="keyword">Paul E. McMahon</a>,<a href="http://d.hatena.ne.jp/keyword/Michael%20Goedicke" class="keyword">Michael Goedicke</a></li><li>翔泳社</li></ul><a href="https://www.amazon.co.jp/dp/4798165220?tag=kazzhomeunixo-22&amp;linkCode=osi&amp;th=1&amp;psc=1" class="asin-detail-buy" target="_blank" rel="noopener">Amazon</a></div></div></p> <h2 id="A-Philosophy-of-Software-Design-2nd-Edition">A Philosophy of Software Design, 2nd Edition</h2> <p>いい加減読み終わりたい...</p> <p><div class="hatena-asin-detail"><a href="https://www.amazon.co.jp/dp/B09B8LFKQL?tag=kazzhomeunixo-22&amp;linkCode=osi&amp;th=1&amp;psc=1" class="hatena-asin-detail-image-link" target="_blank" rel="noopener"><img src="https://m.media-amazon.com/images/I/51MaNQzHTQL._SL500_.jpg" class="hatena-asin-detail-image" alt="A Philosophy of Software Design, 2nd Edition (English Edition)" title="A Philosophy of Software Design, 2nd Edition (English Edition)"></a><div class="hatena-asin-detail-info"><p class="hatena-asin-detail-title"><a href="https://www.amazon.co.jp/dp/B09B8LFKQL?tag=kazzhomeunixo-22&amp;linkCode=osi&amp;th=1&amp;psc=1" target="_blank" rel="noopener">A Philosophy of Software Design, 2nd Edition (English Edition)</a></p><ul class="hatena-asin-detail-meta"><li><span class="hatena-asin-detail-label">作者:</span><a href="http://d.hatena.ne.jp/keyword/Ousterhout%2C%20John%20K.%20" class="keyword">Ousterhout, John K. </a></li><li></li></ul><a href="https://www.amazon.co.jp/dp/B09B8LFKQL?tag=kazzhomeunixo-22&amp;linkCode=osi&amp;th=1&amp;psc=1" class="asin-detail-buy" target="_blank" rel="noopener">Amazon</a></div></div></p> <h2 id="詳解-システムパフォーマンス-第2版">詳解 システム・パフォーマンス 第2版</h2> <p>めちゃくちゃ評判がいい本なので。</p> <p><div class="hatena-asin-detail"><a href="https://www.amazon.co.jp/dp/4814400071?tag=kazzhomeunixo-22&amp;linkCode=osi&amp;th=1&amp;psc=1" class="hatena-asin-detail-image-link" target="_blank" rel="noopener"><img src="https://m.media-amazon.com/images/I/41HM64PoOdL._SL500_.jpg" class="hatena-asin-detail-image" alt="詳解 システム・パフォーマンス 第2版" title="詳解 システム・パフォーマンス 第2版"></a><div class="hatena-asin-detail-info"><p class="hatena-asin-detail-title"><a href="https://www.amazon.co.jp/dp/4814400071?tag=kazzhomeunixo-22&amp;linkCode=osi&amp;th=1&amp;psc=1" target="_blank" rel="noopener">詳解 システム・パフォーマンス 第2版</a></p><ul class="hatena-asin-detail-meta"><li><span class="hatena-asin-detail-label">作者:</span><a href="http://d.hatena.ne.jp/keyword/Brendan%20Gregg" class="keyword">Brendan Gregg</a></li><li>オライリージャパン</li></ul><a href="https://www.amazon.co.jp/dp/4814400071?tag=kazzhomeunixo-22&amp;linkCode=osi&amp;th=1&amp;psc=1" class="asin-detail-buy" target="_blank" rel="noopener">Amazon</a></div></div></p> <h2 id="LIFESPANライフスパン-老いなき世界">LIFESPAN(ライフスパン): 老いなき世界</h2> <p>最近老いが気になってきたので... <div class="hatena-asin-detail"><a href="https://www.amazon.co.jp/dp/4492046747?tag=kazzhomeunixo-22&amp;linkCode=osi&amp;th=1&amp;psc=1" class="hatena-asin-detail-image-link" target="_blank" rel="noopener"><img src="https://m.media-amazon.com/images/I/51RhzsLtSAL._SL500_.jpg" class="hatena-asin-detail-image" alt="LIFESPAN(ライフスパン): 老いなき世界" title="LIFESPAN(ライフスパン): 老いなき世界"></a><div class="hatena-asin-detail-info"><p class="hatena-asin-detail-title"><a href="https://www.amazon.co.jp/dp/4492046747?tag=kazzhomeunixo-22&amp;linkCode=osi&amp;th=1&amp;psc=1" target="_blank" rel="noopener">LIFESPAN(ライフスパン): 老いなき世界</a></p><ul class="hatena-asin-detail-meta"><li><span class="hatena-asin-detail-label">作者:</span><a href="http://d.hatena.ne.jp/keyword/%A5%C7%A5%D3%A5%C3%A5%C9%A1%A6A%A1%A6%A5%B7%A5%F3%A5%AF%A5%EC%A5%A2" class="keyword">デビッド・A・シンクレア</a>,<a href="http://d.hatena.ne.jp/keyword/%A5%DE%A5%B7%A5%E5%A1%BC%A1%A6D%A1%A6%A5%E9%A5%D7%A5%E9%A5%F3%A5%C8" class="keyword">マシュー・D・ラプラント</a></li><li>東洋経済新報社</li></ul><a href="https://www.amazon.co.jp/dp/4492046747?tag=kazzhomeunixo-22&amp;linkCode=osi&amp;th=1&amp;psc=1" class="asin-detail-buy" target="_blank" rel="noopener">Amazon</a></div></div></p> oinume AlfredからRaycastに移行した hatenablog://entry/4207112889949756410 2023-01-02T08:00:00+09:00 2023-11-07T13:15:41+09:00 最近はRaycastが巷で流行っているので、自分もAlfredから乗り換えてみた。Alfredはv5への移行をずっと保留にしていてタイミング的にも良かったというのが一番大きい。ちなみに自分はAlfredの有料課金のライトユーザーで、以下の機能を使っていた。 通常のランチャー機能 HotKey - よく使うアプリをHotKeyとして登録 Clipboard history - クリップボードの履歴 ブラウザのブックマーク検索 ライトユーザーなので、とてもスムーズに移行できた。 また、Raycastの拡張機能でいいなと思ったのは Screenshotの検索とコピーが簡単にできる Emojiの検索が… <p>最近は<a href="https://www.raycast.com/">Raycast</a>が巷で流行っているので、自分もAlfredから乗り換えてみた。Alfredはv5への移行をずっと保留にしていてタイミング的にも良かったというのが一番大きい。ちなみに自分はAlfredの有料課金のライトユーザーで、以下の機能を使っていた。</p> <ol> <li>通常のランチャー機能</li> <li>HotKey - よく使うアプリをHotKeyとして登録</li> <li>Clipboard history - クリップボードの履歴</li> <li>ブラウザのブックマーク検索</li> </ol> <p>ライトユーザーなので、とてもスムーズに移行できた。</p> <p>また、Raycastの拡張機能でいいなと思ったのは</p> <ol> <li>Screenshotの検索とコピーが簡単にできる</li> <li>Emojiの検索が簡単にできる</li> <li>カレンダー連携すると直近のMeeting URLにRaycastから飛べる <ul> <li>なので「あ、Meetingだ!」って思ったらRaycastを開いておけばいい、という体験が良い</li> </ul> </li> <li>Switch Windowsで開いているウィンドウを選択して開ける <ul> <li>これまではcontextsというアプリを使っていたけど不要になった</li> </ul> </li> <li>Quicklinkの機能で簡単に指定のURLに飛ぶリンクが作成できる</li> </ol> <p>の5点。色んな拡張が<a href="https://www.raycast.com/store">Store</a>にあるので、有用そうなものを日々探してインストールしている。まだ使い始めて3日ぐらいだけど、そろそろAlfredはアンインストールしようかなと思うぐらい馴染んでいる。</p> oinume OpenTelemetryとhttptrace.ClientTraceを使ってHTTPリクエストのlatencyを可視化する hatenablog://entry/4207112889897749712 2022-07-11T09:00:00+09:00 2022-07-11T14:42:28+09:00 この記事は OpenCensusとhttptrace.ClientTraceを使ってHTTPリクエストのlatencyを可視化する - oinume journal のOpenTelemetry版。OpenTelemetryについては OpenTelemetryとは何か、そしてなぜそれが計装器の未来なのか? | New Relic を見てもらうのが手っ取り早くて、httptrace.ClientTraceについては先のブログを見てもらえればと。 OpenTelemetry Tracing + httptrace.ClientTrace 早速本題に入ってしまうと、以下のようなコードを書くことでh… <p>この記事は <a href="https://journal.lampetty.net/entry/opencensus-httptrace">OpenCensus&#x3068;httptrace.ClientTrace&#x3092;&#x4F7F;&#x3063;&#x3066;HTTP&#x30EA;&#x30AF;&#x30A8;&#x30B9;&#x30C8;&#x306E;latency&#x3092;&#x53EF;&#x8996;&#x5316;&#x3059;&#x308B; - oinume journal</a> のOpenTelemetry版。OpenTelemetryについては <a href="https://newrelic.com/jp/blog/best-practices/what-is-opentelemetry">OpenTelemetry&#x3068;&#x306F;&#x4F55;&#x304B;&#x3001;&#x305D;&#x3057;&#x3066;&#x306A;&#x305C;&#x305D;&#x308C;&#x304C;&#x8A08;&#x88C5;&#x5668;&#x306E;&#x672A;&#x6765;&#x306A;&#x306E;&#x304B;&#xFF1F; | New Relic</a> を見てもらうのが手っ取り早くて、<a href="https://pkg.go.dev/net/http/httptrace#ClientTrace">httptrace.ClientTrace</a>については先のブログを見てもらえればと。</p> <h2>OpenTelemetry Tracing + httptrace.ClientTrace</h2> <p>早速本題に入ってしまうと、以下のようなコードを書くことでhttp.ClientでHTTPリクエストを送った際のlatencyを可視化することが可能になる。</p> <script src="https://emgithub.com/embed.js?target=https%3A%2F%2Fgithub.com%2Foinume%2Fgo-http-client-trace-sample%2Fblob%2Fmain%2Fexamples%2Fotelhttp_client_trace%2Fmain.go&style=github&showBorder=on&showLineNumbers=on&showFileMeta=on&showCopy=on"></script> <p>以下のように otelhttptrace.NewClientTraceで生成したClientTraceを<code>ctx</code>にセットし、その<code>ctx</code>をhttp.Requestにセットするだけで良い。</p> <pre class="code lang-go" data-lang="go" data-unlink>clientTrace := otelhttptrace.NewClientTrace(ctx) ctx = httptrace.WithClientTrace(ctx, clientTrace) req, err := http.NewRequestWithContext(ctx, <span class="synConstant">&quot;GET&quot;</span>, url, <span class="synStatement">nil</span>) ... </pre> <h2>Jaeger UIでの確認</h2> <p><a href="https://github.com/oinume/go-http-client-trace-sample/">https://github.com/oinume/go-http-client-trace-sample/</a> のリポジトリをcloneして<code>docker-compose up -d</code>するとJaegerが立ち上げることができる。</p> <pre class="code shell" data-lang="shell" data-unlink>$ git clone https://github.com/oinume/go-http-client-trace-sample $ docker-compose up -d $ go run ./examples/otelhttp_client_trace/main.go</pre> <p>のように、Jaegerを立ち上げてサンプルコードを実行するとJaegerのexporterにトレースの情報が送られる。そのトレースの情報は <a href="http://localhost:16686/search">http://localhost:16686/search</a> にブラウザでアクセスすると見ることができる。以下のスクショを見てもらうとわかるように、<code>http.getconn</code>や<code>http.dns</code>や<code>http.tls</code>など、httptrace.ClientTraceのコールバックメソッドごとにSpanが作成され、それがどのぐらい時間がかかっているのかがわかるようになっている。</p> <iframe src="https://drive.google.com/file/d/1ShXw5MX2YZEg6Qe5K1l4jTsMvATxZzUc/preview" width="640" height="480" allow="autoplay"></iframe> <h2>OpenCensusとOpenTelemetryの違い</h2> <p>httptrace.ClientTraceに関わるところで、OpenCensusとOpenTelemetryで微妙に挙動が違うところがあったので、メモしておく。</p> <p>OpenCensusの場合はデフォルトでは<code>http.getconn</code>や<code>http.dns</code>などの情報がSpanにならないが、OpenTelemetryの場合はそれぞれSpanになっている。この挙動で注意した方が良いのは、例えばGCPのCloud Traceだと<a href="https://cloud.google.com/stackdriver/pricing#trace-costs">スパン単位で従量課金される</a>ので、場合によっては金額が大きくなってしまうかもしれない。しかし、以下のように<a href="https://pkg.go.dev/go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace#WithoutSubSpans">WithoutSubSpans</a>を指定することでSpanを生成しないようにすることができる。</p> <pre class="code lang-go" data-lang="go" data-unlink>clientTrace := otelhttptrace.NewClientTrace(ctx, otelhttptrace.WithoutSubSpans()) </pre> <h2>最後に</h2> <p>OpenTelemetryのTracingについては各言語のSDKも揃ってきて、クラウドベンダーの対応もされてきているので、そろそろ実戦投入していこうと思った。</p> <p><div class="hatena-asin-detail"><a href="https://www.amazon.co.jp/dp/4873118700?tag=kazzhomeunixo-22&amp;linkCode=ogi&amp;th=1&amp;psc=1" class="hatena-asin-detail-image-link" target="_blank" rel="noopener"><img src="https://m.media-amazon.com/images/I/51T+k4VRzpL._SL500_.jpg" class="hatena-asin-detail-image" alt="データ指向アプリケーションデザイン ―信頼性、拡張性、保守性の高い分散システム設計の原理" title="データ指向アプリケーションデザイン ―信頼性、拡張性、保守性の高い分散システム設計の原理"></a><div class="hatena-asin-detail-info"><p class="hatena-asin-detail-title"><a href="https://www.amazon.co.jp/dp/4873118700?tag=kazzhomeunixo-22&amp;linkCode=ogi&amp;th=1&amp;psc=1" target="_blank" rel="noopener">データ指向アプリケーションデザイン ―信頼性、拡張性、保守性の高い分散システム設計の原理</a></p><ul class="hatena-asin-detail-meta"><li><span class="hatena-asin-detail-label">作者:</span><a href="http://d.hatena.ne.jp/keyword/Martin%20Kleppmann" class="keyword">Martin Kleppmann</a></li><li>オライリー・ジャパン</li></ul><a href="https://www.amazon.co.jp/dp/4873118700?tag=kazzhomeunixo-22&amp;linkCode=ogi&amp;th=1&amp;psc=1" class="asin-detail-buy" target="_blank" rel="noopener">Amazon</a></div></div></p> oinume Chapter 5 - Information Hiding (and Leakage) / A Philosophy of Software Design hatenablog://entry/13574176438072554703 2022-03-15T09:00:00+09:00 2022-03-15T09:00:04+09:00 A Philosophy of Software Design, 2nd Edition (English Edition)作者:Ousterhout, John K. Amazon 第5章はInformation Hiding (and Leakage)というタイトル。いいモジュールを作るには情報の隠蔽(カプセル化)を行うことが大事だよ、という話。 5.1 Information hiding Deep Moduleを作る上で最も大事なのは情報の隠蔽 モジュールを実装する上で必要なメカニズムをinterfaceとして表には出さないこと ex) B-treeにどういう情報を保存するか ファイル… <p><div class="hatena-asin-detail"><a href="https://www.amazon.co.jp/dp/B09B8LFKQL?tag=kazzhomeunixo-22&amp;linkCode=ogi&amp;th=1&amp;psc=1" class="hatena-asin-detail-image-link" target="_blank" rel="noopener"><img src="https://m.media-amazon.com/images/I/51MaNQzHTQL._SL500_.jpg" class="hatena-asin-detail-image" alt="A Philosophy of Software Design, 2nd Edition (English Edition)" title="A Philosophy of Software Design, 2nd Edition (English Edition)"></a><div class="hatena-asin-detail-info"><p class="hatena-asin-detail-title"><a href="https://www.amazon.co.jp/dp/B09B8LFKQL?tag=kazzhomeunixo-22&amp;linkCode=ogi&amp;th=1&amp;psc=1" target="_blank" rel="noopener">A Philosophy of Software Design, 2nd Edition (English Edition)</a></p><ul class="hatena-asin-detail-meta"><li><span class="hatena-asin-detail-label">作者:</span><a href="http://d.hatena.ne.jp/keyword/Ousterhout%2C%20John%20K.%20" class="keyword">Ousterhout, John K. </a></li><li></li></ul><a href="https://www.amazon.co.jp/dp/B09B8LFKQL?tag=kazzhomeunixo-22&amp;linkCode=ogi&amp;th=1&amp;psc=1" class="asin-detail-buy" target="_blank" rel="noopener">Amazon</a></div></div></p> <p>第5章は<code>Information Hiding (and Leakage)</code>というタイトル。いいモジュールを作るには情報の隠蔽(カプセル化)を行うことが大事だよ、という話。</p> <h2>5.1 Information hiding</h2> <ul> <li>Deep Moduleを作る上で最も大事なのは情報の隠蔽</li> <li>モジュールを実装する上で必要なメカニズムをinterfaceとして表には出さないこと <ul> <li>ex) <ul> <li>B-treeにどういう情報を保存するか</li> <li>ファイルの中身が物理的なディスクのブロックのどこに存在するかを認識する方法</li> <li>TCPプロトコルの実装の詳細</li> <li>マルチコアCPUでのスレッドの実装</li> <li>JSONをどうやってパースしているか</li> </ul> </li> </ul> </li> <li>情報の隠蔽は2つの方法で複雑性を減らす <ul> <li>情報の隠蔽はモジュールのインターフェースをシンプルにする <ul> <li>具体例) B-tree classを開発者が使うときに、理想的なノードの広がりやツリー内部の均衡化については、利用者は気にしなくて良い</li> </ul> </li> <li>情報の隠蔽はシステムの発展を容易にする <ul> <li>情報がモジュール外には隠蔽されている=(モジュール外からみて)その情報への依存性がなくなっているので、その情報に関する内部的な設計の変更の影響はモジュール内部のみである <ul> <li>例えば、TCPでネットワーク混雑時の制御についてプロトコルの仕様が新しく追加されても、TCPのデータの送受信のコードには影響がないはずである</li> </ul> </li> </ul> </li> </ul> </li> </ul> <h2>5.2 Information leakage</h2> <ul> <li>直訳すると情報の漏洩。情報の隠蔽の反対の概念</li> <li>これはモジュール間の依存を作り出してしまう</li> <li>情報の漏洩はインターフェースだけで起こるものではない <ul> <li>例えば特定のファイルフォーマットを扱う2つのクラスがあり、片方がファイルを読み込み、もう片方が書き込みをするような場合。ファイルのフォーマットが変更された時は両方のクラスを修正する必要がある。</li> </ul> </li> <li>情報の漏洩もまたred flag(危険信号)</li> <li>情報の漏洩をどうやって防ぐか? <ul> <li>先ほどの2つのクラスの例であれば、クラスが小さくて密接に関連しているのであればマージして1つにしてしまうのが良い</li> <li>情報をpull(取得)してカプセル化するクラスを新しく作る、という方法もある。 <ul> <li>Another possible approach is to pull the information out of all of the affected classes and create a new class that encapsulates just that information. However, this approach will be effective only if you can find a simple interface that abstracts away from the details; if the new class exposes most of the knowledge through its interface, then it won’t provide much value (you’ve simply replaced back-door leakage with leakage through an interface).</li> </ul> </li> </ul> </li> </ul> <h2>5.3 Temporal decomposition</h2> <ul> <li>時間による分解</li> <li>Temporal decompositionの考え方で「ファイルを読み込んで、その内容を変更して、新しいバージョンを書き出す」というアプリケーションを考えると、3つのクラスに分解される。 <ul> <li>ファイルを読むクラスと新しいバージョンを書き出すクラスはそれぞれファイルのフォーマットを知っている必要があるので、ここで情報の漏洩が発生する</li> <li>情報の漏洩を防ぐには、ファイルを読むクラスと書き込むクラスは同じものにする必要がある</li> </ul> </li> <li>時間による処理の順番でクラスを分解するのではなく、そのクラスが行う処理が知識として何を持っているべきか(先程の例だとファイルのフォーマット)に着目してクラスを分解するべき</li> </ul> <h2>5.4  Example: HTTP server</h2> <ul> <li>生徒がHTTPプロトコルの実装をした際のデザインの意思決定について、実際の例を交えて考察</li> <li>HTTPプロトコル説明の図</li> </ul> <iframe src="https://drive.google.com/file/d/1FODpff_Nn1qF1DJwc9r7Md83VLPr4veN/preview" width="640" height="480" allow="autoplay"></iframe> <h2>5.5 Example: too many classes</h2> <ul> <li>生徒がよくやってしまう間違いとしては、たくさんのShallow Classに分割してそのクラス間での情報漏洩をもたらしてしまうこと <ul> <li>例) ネットワークコネクションからHTTPリクエストを読み込んでstringに変換するクラスを作り、別のクラスでそのstringをパースする <ul> <li>Temporal Decompositionのダメな例</li> <li>例えばContent-Length headerでbodyの長さが指定されているので、Bodyの長さを計算するにはまずContent-Lengthをパースしないといけない <ul> <li>stringになったリクエストをパースする時にBodyの長さがわからないとパースできない</li> <li>コードの具体例がなかったのでちょっとここはよくわからなかった</li> </ul> </li> <li>結果として、2つのクラスに分割してもそれぞれがHTTPリクエストの構造を知っていないとダメな実装になっている</li> <li>さらに呼び出し側も2つのクラスの使い方を知っている必要が出てくるので、呼び出し側に余計な認知負荷がかかる</li> </ul> </li> <li>よって、上の例ではリクエストを読み込むクラスとパースするクラスは一つにマージした方が良い <ul> <li>HTTPリクエストをパースという処理を1クラスに集約することで情報の漏洩が発生しない</li> <li>クラスの数を減らすことで利用者から見たインターフェースはシンプルになる</li> </ul> </li> </ul> </li> </ul> <h2>5.6 Example: HTTP parameter handling</h2> <ul> <li>HTTP requestがサーバーに来ると、サーバーはリクエストの情報を参照する必要がある</li> <li>例えば、以前のFigure 5.1で言うとphoto_idというリクエストパラメーター <ul> <li>パラメーターはURLのQuery Stringとして送られてくることもあれば、ボディで送られてくることもある</li> <li>Query Stringの場合はURLEncodeされているが、処理する時にはこれはデコードされている必要がある</li> </ul> </li> <li>著者の生徒の大半は以下の良い情報の隠蔽を行なっていた <ul> <li>パラメータはQuery StringまたはBody経由で送信されてくるが、それを利用する人にはどちらから来ているかは重要ではないのでマージしている</li> <li>2つ目の良いところは、パラメーターのデコードについて利用者が意識しなくて済むようにしていること</li> </ul> </li> <li>ただし、生徒が作るモジュールは以下の点でShallow Moduleだった <ul> <li>HTTPRequestというクラスを作ってその中に <code>Map&lt;String, String&gt; getParams()</code> というメソッドを定義していた</li> </ul> </li> </ul> <pre class="code lang-java" data-lang="java" data-unlink> <span class="synType">public</span> Map&lt;String, String&gt; getParams() { <span class="synStatement">return</span> <span class="synType">this</span>.params; } </pre> <ul> <li><code>Map&lt;String, String&gt;</code> という内部のデータ型を利用者に公開するのではなく、<code>String getParam(String name)</code> という形にした方が、仮に内部のデータ構造を変えたいときに利用者に影響を与えずに済む</li> <li>パフォーマンスチューニングのために内部のデータ構造を変更することもあるので、なるべく内部のデータ構造は利用者からは見えないようにした方が良い</li> <li>あとはこのMapを利用者側で変更できないようにImmutableにする必要があるなどの問題もある</li> </ul> <h2>5.7  Example: defaults in HTTP responses</h2> <ul> <li>HTTP Responseに関する、生徒たちがよくやる不適切なデフォルト値について説明</li> <li>HTTP ResponseのHTTPのバージョンに関して <ul> <li>(生徒の)あるチームはメソッドの呼び出し元にHTTPのバージョンを指定させるようにしていた</li> <li>しかし、HTTPのバージョンはリクエストで指定されているものと一致しているべき</li> <li>なので、リクエストのHTTPバージョンをそのままレスポンスのバージョンとすることが望ましい</li> <li>呼び出し元がHTTPのバージョンを指定すると、このHTTPのライブラリと呼び出し元の間で情報の漏洩が発生してしまう</li> </ul> </li> <li>HTTPレスポンスに必要なものとしてDateヘッダーもある <ul> <li>これもHTTPライブラリが気の利いたデフォルト値を提供すると良い</li> <li>もし呼び出し元が何かしらの理由でDateヘッダーをオーバーライドしたい場合は、専用のメソッドを用意する</li> <li>クラス or モジュールはできる限りいい感じのデフォルト値を用意する</li> <li>P.26のJavaのIOバッファリングは、この点でよくない例となっている <ul> <li>IOライブラリのほとんどのユーザーはバッファリングするのでそれがデフォルトになっているべきだが、Javaの標準ライブラリではバッファリングのために別のクラスを使ってラップする必要がある</li> </ul> </li> </ul> </li> </ul> <pre class="code lang-java" data-lang="java" data-unlink> FileInputStream fileStream = <span class="synStatement">new</span> FileInputStream(fileName); BufferedInputStream bufferedStream = <span class="synStatement">new</span> BuffferedInputStream(fileStream); </pre> <ul> <li>Red Flag: Overexposure(過度の露出) <ul> <li>よく使われるAPIが、たまにしか使われない機能もユーザーに覚えさせるようにしてしまうと、認知負荷を増大させてしまう(意味がよくわからなかった)</li> </ul> </li> </ul> <h2>5.10 Conclusion</h2> <ul> <li>情報の隠蔽とDeep Modulesは密接に関連している</li> <li>モジュールがたくさんの情報を隠蔽することで、インタフェースをシンプルにしつつ多くの機能が提供される</li> <li>システムをモジュールに分解するときに、実行時に行われる処理の順番を意識しないようにすること <ul> <li>以前に説明したTemporal decompositionが発生し情報の漏洩やShallow modulesの問題を引き起こす</li> <li>モジュールが知っているべき知識を考慮してそれを各モジュールがカプセル化することで、Deep modulesを作ることができる</li> </ul> </li> </ul> oinume 2022年の抱負 hatenablog://entry/13574176438058108259 2022-01-31T09:00:00+09:00 2022-03-14T08:57:09+09:00 もうすっかり年が明けてしまって1月末ですが、今年の目標というか抱負を書いておこうかなと思います。 健康第一 最近は風邪をひいたりすると仕事でもプライベートでも明らかに悪影響が出るので、健康第一で生きたい。その意味で以下を引き続き習慣として頑張る。 筋トレ 有酸素運動 有酸素運動はジョギングや早歩きの散歩だと膝が痛くなってしまい、色々と試した結果、マンションの階段昇降が一番コスパ良いかつ膝へのダメージが少ないということが昨年分かったので、1日20分 / 週4を目安に頑張る。 GraphQL+Next.jsでWebアプリを作れるようになる 昨年は趣味プロダクトのReact Hooks + Reac… <p>もうすっかり年が明けてしまって1月末ですが、今年の目標というか抱負を書いておこうかなと思います。</p> <h2>健康第一</h2> <p>最近は風邪をひいたりすると仕事でもプライベートでも明らかに悪影響が出るので、健康第一で生きたい。その意味で以下を引き続き習慣として頑張る。</p> <ul> <li>筋トレ</li> <li>有酸素運動</li> </ul> <p>有酸素運動はジョギングや早歩きの散歩だと膝が痛くなってしまい、色々と試した結果、マンションの階段昇降が一番コスパ良いかつ膝へのダメージが少ないということが昨年分かったので、1日20分 / 週4を目安に頑張る。</p> <h2>GraphQL+Next.jsでWebアプリを作れるようになる</h2> <p>昨年は趣味プロダクトのReact Hooks + React Query化が終わったので、今年はGraphQL+Next.jsを導入しつつ学びたい。具体的には以下を使っていきたい。</p> <ul> <li>Next.js</li> <li>GraphQL <ul> <li>gqlgen</li> <li>apollo-client</li> </ul> </li> </ul> <h2>英語</h2> <ul> <li>iKnowを週5</li> <li>Camblyが良さそうなのでトライ</li> <li>TOEICを年に2回受ける</li> </ul> <p>をやる。DMM英会話はやめてしまったけど、値段的にはCamblyが良さそうなので試してみる。</p> <h2>アウトプット</h2> <p>1ヶ月に1つはちゃんとした記事を書く。昨年に引き続き継続は力なり、ということで。</p> <h2>というわけで</h2> <p>2022年もやっていきだ!</p> <p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/o/oinume/20220130/20220130100849.png" alt="f:id:oinume:20220130100849p:plain" width="1200" height="840" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span></p> oinume 2021年の振り返り hatenablog://entry/13574176438048037519 2021-12-31T23:53:43+09:00 2022-01-05T14:52:10+09:00 2021年の抱負 - oinume journalで年初に考えていた抱負がどうだったのか?を振り返ってみる。 習慣化 Habitifyというアプリを1年使い続けてみたけど、習慣化するものとそうじゃないものがあった。ある程度習慣化したものはiKnow(英単語)だけで、その他は習慣化しなかった。Habitify、通知してくれるのはいいんだけど習慣化するにはモチベーションも維持する必要があるので、その点ではあまり役に立たなかったように思う。何かいいアプリがあったら乗り換えたい。 というわけでこれは達成度20%ぐらい。 ReactでSPA作れるようになる 趣味で開発しているプロダクトで、ログイン後の動… <p><a href="https://journal.lampetty.net/entry/resolution-in-2021">2021&#x5E74;&#x306E;&#x62B1;&#x8CA0; - oinume journal</a>で年初に考えていた抱負がどうだったのか?を振り返ってみる。</p> <h2>習慣化</h2> <p>Habitifyというアプリを1年使い続けてみたけど、習慣化するものとそうじゃないものがあった。ある程度習慣化したものはiKnow(英単語)だけで、その他は習慣化しなかった。Habitify、通知してくれるのはいいんだけど習慣化するにはモチベーションも維持する必要があるので、その点ではあまり役に立たなかったように思う。何かいいアプリがあったら乗り換えたい。</p> <p>というわけでこれは達成度20%ぐらい。</p> <h2>ReactでSPA作れるようになる</h2> <p>趣味で開発しているプロダクトで、ログイン後の動的なページはReact化が終わった。まだSPAにはできていないけど、React + React Queryを使って動的なページを作るのはできるようになったので、来年はGraphQL + Next.jsを導入してみたい。</p> <h2>英語</h2> <p>今年は6月にTOEICを受けて自己ベストを更新 &amp; 800点超えしたのが地味に嬉しかった(この時の問題が異常に簡単だったという説もある)。来年も自己ベストを更新できるように頑張りたい。</p> <p><blockquote data-conversation="none" class="twitter-tweet" data-lang="en"><p lang="ja" dir="ltr">TOEIC自己ベストを更新していたので思わずガッツポーズしてしまった。</p>&mdash; oinume@Dr.’s Prime (@oinume) <a href="https://twitter.com/oinume/status/1204629862327021569?ref_src=twsrc%5Etfw">December 11, 2019</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> </p> <p>英語のレッスン自体は週1でやっていたのだけど、DMM英会話時代からお世話になっていた先生が育休に入ってしまうということで3ヶ月ぐらい中断している。来年は再開してこれも習慣化したい。</p> <h2>アウトプット</h2> <p>月に1エントリは書くという目標だったけど、2,3,4,5,10月が達成できず。いつものように仕事が忙しくなってしまうとアウトプットが疎かになってしまうので、月初からちゃんとネタを仕込みつつアウトプットできるようにする。</p> <h2>ライブ</h2> <p>海外アーティストが来れないので、そっちは諦めて邦楽のライブに3回ほど参戦。本当はGWのビバラも行くはずだったんだけどコロナが怖くて断念。これがずっと心残りだったので、デルタ株がピークだったけど散々悩んだ挙句、死ぬ気で8/6にZeppまで足を運んだ。もはや生き甲斐がライブぐらいしかないので、不謹慎で危険な行為だとは思ったけど行って良かったと思う。神経すり減らしながらも爆音が鳴っている中で「生きてる」って感じがして、なんとも言えない感覚だった。</p> <ul> <li>8/6 Straightener &amp; Dragon Ash @ Zepp Tokyo</li> <li>10/14 ROTTENGRAFFTY @ Zepp DiverCity</li> <li>12/28 Creepy Nuts &amp; Dragon Ash @ Zepp Tokyo</li> </ul> <p><blockquote data-conversation="none" class="twitter-tweet" data-lang="en"><p lang="ja" dir="ltr">昨日はZepp Tokyoでライブ納め。Creepy Nutsかっこ良かったしDragon Ashは神セトリで締めにふさわしいライブだった。来年ももっとライブ行きたい <a href="https://t.co/uQDgXqX4vh">pic.twitter.com/uQDgXqX4vh</a></p>&mdash; oinume@Dr.’s Prime (@oinume) <a href="https://twitter.com/oinume/status/1476163912135770112?ref_src=twsrc%5Etfw">December 29, 2021</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> </p> <h2>まとめ</h2> <p>2021年もパンデミックであまり旅行も行けなかったけど、ライブにも映画にも行けたし2020年よりはパンデミックに慣れてきた気がする。2022年はライブハウスでモッシュとかダイブができる世の中になるといいなぁと思っている。海外旅行も行きたいですね。</p> oinume Chapter 4 - Modules Should Be Deep / A Philosophy of Software Design hatenablog://entry/13574176438045404885 2021-12-27T09:00:00+09:00 2022-03-13T16:02:16+09:00 A Philosophy of Software Design, 2nd Edition (English Edition)作者:Ousterhout, John K. Amazon 第4章はModules Should Be Deepというタイトル。 4.1 Modular Design ソフトウェアの複雑性を管理するためのもっとも大事なテクニックの一つとして、全体の複雑性の一部分だけに直面するようにシステムを設計するということが挙げられる。このアプローチは modular design と呼ばれている。 この手法を用いると、ソフトウェアシステムは複数のモジュール(クラス, サブシステム, … <p><div class="hatena-asin-detail"><a href="https://www.amazon.co.jp/dp/B09B8LFKQL?tag=kazzhomeunixo-22&amp;linkCode=ogi&amp;th=1&amp;psc=1" class="hatena-asin-detail-image-link" target="_blank" rel="noopener"><img src="https://m.media-amazon.com/images/I/51MaNQzHTQL._SL500_.jpg" class="hatena-asin-detail-image" alt="A Philosophy of Software Design, 2nd Edition (English Edition)" title="A Philosophy of Software Design, 2nd Edition (English Edition)"></a><div class="hatena-asin-detail-info"><p class="hatena-asin-detail-title"><a href="https://www.amazon.co.jp/dp/B09B8LFKQL?tag=kazzhomeunixo-22&amp;linkCode=ogi&amp;th=1&amp;psc=1" target="_blank" rel="noopener">A Philosophy of Software Design, 2nd Edition (English Edition)</a></p><ul class="hatena-asin-detail-meta"><li><span class="hatena-asin-detail-label">作者:</span><a href="http://d.hatena.ne.jp/keyword/Ousterhout%2C%20John%20K.%20" class="keyword">Ousterhout, John K. </a></li><li></li></ul><a href="https://www.amazon.co.jp/dp/B09B8LFKQL?tag=kazzhomeunixo-22&amp;linkCode=ogi&amp;th=1&amp;psc=1" class="asin-detail-buy" target="_blank" rel="noopener">Amazon</a></div></div></p> <p>第4章は<code>Modules Should Be Deep</code>というタイトル。</p> <h2>4.1 Modular Design</h2> <ul> <li>ソフトウェアの複雑性を管理するためのもっとも大事なテクニックの一つとして、全体の複雑性の一部分だけに直面するようにシステムを設計するということが挙げられる。このアプローチは modular design と呼ばれている。</li> <li>この手法を用いると、ソフトウェアシステムは複数のモジュール(クラス, サブシステム, サービス)に分解される。</li> <li>それぞれのモジュールは相互に依存せず独立しているため、開発者はあるモジュールの開発をしている時に他のモジュールのことを考えなくて良い</li> <li>しかし、上記の独立性の話はあくまで理想で実際にはそんなことはない <ul> <li>例えば、あるメソッドに引数を追加した場合はその呼び出し元のコードも変更しないといけない</li> </ul> </li> <li>依存の認識と管理をするためには、モジュールをinterfaceとimplementationの2つに分けることが大事である</li> <li>Typically, the interface describes what the module does but not how it does it. <ul> <li>interfaceはそのモジュールが「何をするか」を表明し、それを「どうやるか」については表明しない</li> </ul> </li> <li>あるモジュールの開発者は、そのモジュールのinterfaceと実装、およびそのモジュールが依存している他のモジュールのinterfaceを知っている必要があるが、他のモジュールの実装については知らなくても大丈夫である</li> <li>interfaceが実装に比べてとてもシンプルに表現されているモジュールがベスト</li> <li>上記のようなモジュールは2つのメリットがある <ul> <li>シンプルなインタフェースを提供するモジュールは、他のモジュールにもたらす複雑性を少なくする</li> <li>インターフェースが変更されない限り、他のモジュールには影響がない。</li> </ul> </li> </ul> <h2>4.2 What's in an Interface?</h2> <ul> <li>interfaceにはformal, informalな情報がある</li> <li>formal: メソッドのシグネチャ(引数およびその型、戻り値およびその型) <ul> <li>プログラミング言語の機能によってもたらされる</li> </ul> </li> <li>informal: そのメソッドを呼び出した時にもたらされる結果など <ul> <li>eg) 引数で与えられたファイル名のファイルを削除するなど</li> <li>informalな情報はメソッドのコメントで説明されることが多い</li> <li>Method Bを呼ぶ前にMethod Aを呼ぶ必要がある、みたいなものもinformalな情報である。</li> </ul> </li> </ul> <h2>4.3 Abstraction</h2> <ul> <li>Abstractionとは、実体から重要ではない詳細を除外したもの</li> <li>間違った抽象化には以下の2つがある <ul> <li>抽象化したが、重要ではないものを含んでしまう <ul> <li>結果的に他の開発者の認知負荷をあげてしまう</li> </ul> </li> <li>抽象化して重要なものを隠してしまう(false abstraction) <ul> <li>そのモジュールがシンプルなものだと誤解させてしまう</li> </ul> </li> </ul> </li> <li>良い抽象化の例として、ファイルシステムが挙げられる <ul> <li>ファイルにデータを書き込む時に、ユーザーはそのデータがストレージデバイスのどのブロックに書き込まれるかなどは意識しない</li> <li>これはファイルシステムがうまく抽象化されていて、ユーザーにとっての不必要な情報を除外しているから</li> <li>一方で、書き込んだデータのフラッシュについての情報は除外されていない <ul> <li>例えばデータベースのようなソフトウェアだと、「システムがクラッシュしてもファイルに必ず書き込まれているか」を保証するために、「実際のストレージにいつデータが書き込まれるか(=フラッシュされるか)」は知っておく必要がある。そのためこの情報は抽象化されても利用者からわかるようになっている。</li> </ul> </li> </ul> </li> </ul> <h2>4.4 Deep Modules</h2> <ul> <li>モジュールの深さはコストとベネフィットで考える <ul> <li>モジュールのコスト:システムの複雑性。interfaceによって表現される</li> <li>小さくてシンプルなインターフェースだと複雑性は少なくなる</li> </ul> </li> <li>以下がコストとベネフィットの説明の図</li> </ul> <iframe src="https://drive.google.com/file/d/1k9kvw8S3q0_UqWVSY5Yy14btul824jVh/preview" width="640" height="480" allow="autoplay"></iframe> <ul> <li>UnixのFile I/O はdeep interfaceの良い例 <ul> <li>以下の図のようにinterfaceはシンプルだが、実装は以下のような複雑である <ul> <li>ディスク上での効率的なアクセスのためのファイルの実装</li> <li>ディレクトリ構造とその階層構造</li> <li>ファイルのパーミッション</li> <li>interruption, background code、およびこれらのやりとり</li> <li>同時アクセスが発生した場合のスケジューリング</li> <li>アクセスしたファイルのメモリ上のキャッシュ</li> <li>セカンダリのストレージデバイス(ディスク、Flashドライブなど)を一つのファイルシステムに統合する</li> </ul> </li> <li>上記のような複雑なものは利用者からは見えない一方で、何年もの時間を経て大幅に進化している</li> <li>別の例だと、ガベージコレクションもdeep interfaceである <ul> <li> 利用者がほとんど意識しなくていいものだが、実装はとても複雑という意味で。</li> </ul> </li> </ul> </li> </ul> <iframe src="https://drive.google.com/file/d/1UpCMbca1NJOg_1IcqhcOVTZyV9z5_C6E/preview" width="640" height="480" allow="autoplay"></iframe> <h2>4.5 Shallow modules</h2> <ul> <li><p>interfaceが(提供する機能と比較して)複雑であること</p> <pre><code class="``java"> private void addNullValueForAttribute(String attirubte) { data.put(attribute, null); } </code></pre></li> <li><p>上のコードは何も抽象化していない</p></li> <li>もしこのメソッドのドキュメントを適切に書いたとしたら、コードより長いドキュメントになる</li> <li>Shallow moduleはRed Flag <ul> <li>A shallow module is one whose interface is complicated relative to the functionality it provides. Shallow modules don’t help much in the battle against complexity, because the benefit they provide (not having to learn about how they work internally) is negated by the cost of learning and using their interfaces. Small modules tend to be shallow. <ul> <li>Shallow moduleは提供するベネフィットをラーニングコストで打ち消してしまっている <ul> <li>小さいモジュールがshallow moduleになりやすい</li> </ul> </li> </ul> </li> </ul> </li> <li>(個人的に思ったこと)Clean Architectureって割とshallow moduleになりやすいのでは?と思った。例えばinfrastructure層とか、クライアントライブラリのメソッド呼ぶだけみたいなのが多い <ul> <li> クライアントライブラリ自体をinfrastructure層に見立てるというやり方もあるっぽい</li> </ul> </li> </ul> <h2>4.6 Classitis</h2> <ul> <li>昨今だとクラスは小さくする方が好まれている。メソッドも同様で「N行超えたら分割するべき」みたいな風潮がある</li> <li>しかし、Deep Classを目指すのであれば、クラスは大きくなる傾向にある</li> <li>Classitis <ul> <li>The extreme of the “classes should be small” approach is a syndrome</li> </ul> </li> <li>Classitisはたくさんのクラスを生み出し、それぞれのクラスはシンプルになるが全体のシステムとして見ると複雑性が高まる <ul> <li>小さいクラスはそれ単体では十分な機能を提供できない</li> </ul> </li> </ul> <h2>4.7 Examples: Java and Unix I/O</h2> <ul> <li>Classitisの典型的な例としてJavaのIOまわりのクラスの話がある。例えばJavaでオブジェクトをファイルから読むときのコードは以下のようになっている。</li> </ul> <pre class="code lang-java" data-lang="java" data-unlink>FileInputStream fileStream = <span class="synStatement">new</span> FileInputStream(fileName); BufferedInputStream bufferedStream = <span class="synStatement">new</span> BuffferedInputStream(fileStream); ObjectInputStream objectStream = <span class="synStatement">new</span> ObjetInputStream(bufferedStream); <span class="synIdentifier">...</span>. </pre> <ul> <li>バッファリングは基本的にみんな使うので、上記のように専用のクラスを使わないと有効にならないデザインよりかは、よくあるケースに対してシンプルなデザインになっていた方が良い <ul> <li>この例だとデフォルトでバッファリングを有効にして、オプションで無効にできるとか。</li> </ul> </li> <li>対照的な例として、Unix Filesystemはシンプルになっている。 <ul> <li>シーケンシャルIOが最も一般的に使われるので、それをデフォルトの挙動にしている <ul> <li><code>read</code>はシーケンシャルアクセスで、 <code>lseek</code> でランダムアクセスもできるようになっている</li> </ul> </li> </ul> </li> </ul> <h2>4.8 Conclusion</h2> <ul> <li>interfaceを抽象化してシンプルなものにして、複雑な実装はinterfaceから除外することが大事</li> </ul> oinume Chapter 3 - Working Code Isn’t Enough / A Philosophy of Software Design hatenablog://entry/13574176438037377169 2021-12-01T09:00:00+09:00 2021-12-01T09:00:00+09:00 A Philosophy of Software Design, 2nd Edition (English Edition)作者:Ousterhout, JohnAmazon 第3章はWorking Code Isn’t Enough (Strategic vs. Tactical Programming)というタイトル。 Tactical Programming 近視眼的に目の前のタスクを終わらせるためにコードを書く 目の前のタスクを終わらせることが最優先になるので、これだといい設計はもたらされない これが積もり積もってシステムに複雑性をもたらす 開発メンバー全員がこのアプローチで開発すると… <p><div class="hatena-asin-detail"><a href="https://www.amazon.co.jp/exec/obidos/ASIN/B09B8LFKQL/kazzhomeunixo-22/" class="hatena-asin-detail-image-link" target="_blank" rel="noopener"><img src="https://m.media-amazon.com/images/I/51MaNQzHTQL._SL500_.jpg" class="hatena-asin-detail-image" alt="A Philosophy of Software Design, 2nd Edition (English Edition)" title="A Philosophy of Software Design, 2nd Edition (English Edition)"></a><div class="hatena-asin-detail-info"><p class="hatena-asin-detail-title"><a href="https://www.amazon.co.jp/exec/obidos/ASIN/B09B8LFKQL/kazzhomeunixo-22/" target="_blank" rel="noopener">A Philosophy of Software Design, 2nd Edition (English Edition)</a></p><ul class="hatena-asin-detail-meta"><li><span class="hatena-asin-detail-label">作者:</span><a href="http://d.hatena.ne.jp/keyword/Ousterhout%2C%20John" class="keyword">Ousterhout, John</a></li><li></li></ul><a href="https://www.amazon.co.jp/exec/obidos/ASIN/B09B8LFKQL/kazzhomeunixo-22/" class="asin-detail-buy" target="_blank" rel="noopener">Amazon</a></div></div></p> <p>第3章は<code>Working Code Isn’t Enough (Strategic vs. Tactical Programming)</code>というタイトル。</p> <h2>Tactical Programming</h2> <ul> <li>近視眼的に目の前のタスクを終わらせるためにコードを書く</li> <li>目の前のタスクを終わらせることが最優先になるので、これだといい設計はもたらされない</li> <li>これが積もり積もってシステムに複雑性をもたらす</li> <li>開発メンバー全員がこのアプローチで開発すると一気に複雑性が高まる</li> <li>tactical tornado <ul> <li>Almost every software development organization has at least one developer who takes tactical programming to the extreme: a tactical tornado.</li> </ul> </li> </ul> <h2>Strategic Programming</h2> <ul> <li>The first step towards becoming a good software designer is to realize that working code isn’t enough. <ul> <li>working code = 動くコードの意味?</li> </ul> </li> <li>Your primary goal must be to produce a great design, which also happens to work. This is strategic programming. <ul> <li>第一のゴールを素晴らしいデザインでかつきちんと動作するものにするべき</li> </ul> </li> <li>良いデザインに対して投資する、という考え方を持つべき <ul> <li>最初に思い浮かんだ設計のアイデアをそのまま実装するのではなく、少し時間をかけてよりシンプルな設計がないかを模索する時間をかけるべき</li> </ul> </li> <li>どのぐらい良い設計のために時間をさくべきか? <ul> <li>作業の10-20%とこの本ではいっている</li> <li>良いデザインにそのぐらいコストをかけると、数ヶ月後にはそのベネフィットを感じることができる</li> </ul> </li> <li>スタートアップと良い設計への投資 <ul> <li>スタートアップで10-20%コストをかけることは現実的ではないと考えられているため、大抵のスタートアップではTacticalなアプローチが採用される</li> <li>Facebook is an example of a startup that encouraged tactical programming</li> <li>the company’s motto was “Move fast and break things.”</li> <li>Over time the company realized that its culture was unsustainable. Eventually, Facebook changed its motto to “Move fast with solid infrastructure”</li> </ul> </li> </ul> oinume Chapter 2 - The Nature of Complexity / A Philosophy of Software Design hatenablog://entry/13574176438037374365 2021-11-30T09:00:00+09:00 2021-11-30T09:00:01+09:00 A Philosophy of Software Design, 2nd Edition (English Edition)作者:Ousterhout, JohnAmazon 第2章は"The Nature of Complexity"というタイトルで、ソフトウェアのComplexityつまり複雑性についてじっくり説明されている。 Complexityの定義 Complexity is anything related to the structure of a software system that makes it hard to understand and modify the sy… <p><div class="hatena-asin-detail"><a href="https://www.amazon.co.jp/exec/obidos/ASIN/B09B8LFKQL/kazzhomeunixo-22/" class="hatena-asin-detail-image-link" target="_blank" rel="noopener"><img src="https://m.media-amazon.com/images/I/51MaNQzHTQL._SL500_.jpg" class="hatena-asin-detail-image" alt="A Philosophy of Software Design, 2nd Edition (English Edition)" title="A Philosophy of Software Design, 2nd Edition (English Edition)"></a><div class="hatena-asin-detail-info"><p class="hatena-asin-detail-title"><a href="https://www.amazon.co.jp/exec/obidos/ASIN/B09B8LFKQL/kazzhomeunixo-22/" target="_blank" rel="noopener">A Philosophy of Software Design, 2nd Edition (English Edition)</a></p><ul class="hatena-asin-detail-meta"><li><span class="hatena-asin-detail-label">作者:</span><a href="http://d.hatena.ne.jp/keyword/Ousterhout%2C%20John" class="keyword">Ousterhout, John</a></li><li></li></ul><a href="https://www.amazon.co.jp/exec/obidos/ASIN/B09B8LFKQL/kazzhomeunixo-22/" class="asin-detail-buy" target="_blank" rel="noopener">Amazon</a></div></div></p> <p>第2章は"The Nature of Complexity"というタイトルで、ソフトウェアのComplexityつまり複雑性についてじっくり説明されている。</p> <h2>Complexityの定義</h2> <blockquote><p>Complexity is anything related to the structure of a software system that makes it hard to understand and modify the system.</p></blockquote> <p>Complexityの3つの症状</p> <h2>1. Change amplification</h2> <pre><code>- とある機能追加・変更をしたいだけなのに、いろんなところを変更しないといけない。以下の図で言うと、背景色を変更したいだけなのに`bg = "red"`の定義が複数箇所あるため、これらを全て修正しないといけない、みたいな。 </code></pre> <iframe src="https://drive.google.com/file/d/1cMQGOOs6wkuj81xLLU5quG4jrAM4lxQK/preview" width="640" height="480" allow="autoplay"></iframe> <h2>2. Cognitive load</h2> <ul> <li>認知負荷。開発者が機能追加・変更のタスクを完了させるためにどのぐらいシステムの内部を知っている必要があるか、と言うこと。多ければ多いほど把握できなくなり、修正漏れなどのバグが発生することになる。</li> <li>システム設計者は時々コードの行数で複雑性を表そうとするが、1行に処理が集約されてすぎていて逆に認知負荷を高めるケースもある。1行より複数行のコードの方がシンプルになっている場合もある</li> </ul> <h2>3. Unknown unknowns</h2> <ul> <li>開発者が機能追加・変更のタスクを完了させるために、どの部分を変更すればいいのかが明らかではないこと。もしくはどの部分を変更すればいいのかの情報が明らかではないこと</li> <li>上の図だと、「背景色を変更したい」と言うタスクがある時に、bannerBgを変更しても全てのページの背景色が変わらないので、どの部分を変更すればいいのかが分かりにくい</li> </ul> oinume テスト hatenablog://entry/13574176438037763205 2021-11-30T00:00:00+09:00 2021-11-30T00:00:06+09:00 これはテストです。 <p>これはテストです。</p> oinume Chapter 1 - Intruduction / A Philosophy of Software Design hatenablog://entry/13574176438037373329 2021-11-29T09:00:00+09:00 2021-11-29T09:00:00+09:00 A Philosophy of Software Design, 2nd Edition (English Edition)作者:Ousterhout, JohnAmazon A Philosophy of Software Design の第1章を読んだのでそのまとめ。 Intro プログラムは機能が増えるごとに複雑さが増していく。複雑さが増えると、開発スピードが遅くなりバグが増える 開発ツールは複雑性に対処するに役立つが、これには限界がある。一方、シンプルなソフトウェアのデザインはより大きくてパワフルなプログラムを導いてくれる。 複雑性に対処するには2つのアプローチがある。 1つ目はコード… <p><div class="hatena-asin-detail"><a href="https://www.amazon.co.jp/exec/obidos/ASIN/B09B8LFKQL/kazzhomeunixo-22/" class="hatena-asin-detail-image-link" target="_blank" rel="noopener"><img src="https://m.media-amazon.com/images/I/51MaNQzHTQL._SL500_.jpg" class="hatena-asin-detail-image" alt="A Philosophy of Software Design, 2nd Edition (English Edition)" title="A Philosophy of Software Design, 2nd Edition (English Edition)"></a><div class="hatena-asin-detail-info"><p class="hatena-asin-detail-title"><a href="https://www.amazon.co.jp/exec/obidos/ASIN/B09B8LFKQL/kazzhomeunixo-22/" target="_blank" rel="noopener">A Philosophy of Software Design, 2nd Edition (English Edition)</a></p><ul class="hatena-asin-detail-meta"><li><span class="hatena-asin-detail-label">作者:</span><a href="http://d.hatena.ne.jp/keyword/Ousterhout%2C%20John" class="keyword">Ousterhout, John</a></li><li></li></ul><a href="https://www.amazon.co.jp/exec/obidos/ASIN/B09B8LFKQL/kazzhomeunixo-22/" class="asin-detail-buy" target="_blank" rel="noopener">Amazon</a></div></div></p> <p>A Philosophy of Software Design の第1章を読んだのでそのまとめ。</p> <h2>Intro</h2> <ul> <li>プログラムは機能が増えるごとに複雑さが増していく。複雑さが増えると、開発スピードが遅くなりバグが増える</li> <li>開発ツールは複雑性に対処するに役立つが、これには限界がある。一方、シンプルなソフトウェアのデザインはより大きくてパワフルなプログラムを導いてくれる。</li> <li>複雑性に対処するには2つのアプローチがある。 <ul> <li>1つ目はコードをシンプルかつ明らか(わかりやすく)すること。例えば複雑性は特殊ケースを削除することで減らすことができる。</li> <li>2つ目のアプローチはカプセル化でmodular design とよばれている。 <ul> <li>Modular designではソフトウェアをモジュールに分割して管理して(OOPだとクラスとか)それぞれのモジュールは他のものに依存しない。</li> <li>そのため、プログラマーはあるモジュールの開発をするときに、他のモジュールの詳細を知らなくて済む。</li> </ul> </li> </ul> </li> </ul> <h2>ウォーターフォールモデルの紹介</h2> <ul> <li>ウォーターフォールだと基本的には設計フェーズではすべてを設計し、開発のフェーズでは設計をしない。ソフトウェアは物理的なシステムより複雑で目に見えないので、特に大きなシステムであるほど全体を細部まで理解することは難しい。</li> <li>開発フェーズで初めて問題がわかるケースもよくあり、場合によっては設計のやり直しが発生する。ウォーターフォールモデルだとこれは大きな手戻りになってしまう。</li> <li>この問題があるため、最近のソフトウェア開発ではアジャイルのようなインクリメンタルなアプローチが使われている。</li> </ul> <h2>How to use this book</h2> <p>ソフトウェアの設計スキルを向上させるための良い手法の一つは、"red flags"という複雑なソースコードの断片のサインを認識することである。この本ではその"red flags"を、メジャーな設計の問題を通じて説明する。</p> oinume A Philosophy of Software Designを読み始めた hatenablog://entry/13574176438037370237 2021-11-28T14:54:17+09:00 2022-03-15T14:53:50+09:00 タイトル通りで、最近第2版が発売されたのと、いろんなところでオススメされていたのでこの本を読んでいる。やっと第6章まで読み終わったので、それぞれの章のまとめをブログにアップしていく予定。 A Philosophy of Software Design, 2nd Edition (English Edition)作者:Ousterhout, John K. Amazon 以下は読んだ章のINDEX. Chapter 1 - Intruduction / A Philosophy of Software Design - oinume journal Chapter 2 - The Nature … <p>タイトル通りで、最近第2版が発売されたのと、いろんなところでオススメされていたのでこの本を読んでいる。やっと第6章まで読み終わったので、それぞれの章のまとめをブログにアップしていく予定。</p> <p><div class="hatena-asin-detail"><a href="https://www.amazon.co.jp/dp/B09B8LFKQL?tag=kazzhomeunixo-22&amp;linkCode=ogi&amp;th=1&amp;psc=1" class="hatena-asin-detail-image-link" target="_blank" rel="noopener"><img src="https://m.media-amazon.com/images/I/51MaNQzHTQL._SL500_.jpg" class="hatena-asin-detail-image" alt="A Philosophy of Software Design, 2nd Edition (English Edition)" title="A Philosophy of Software Design, 2nd Edition (English Edition)"></a><div class="hatena-asin-detail-info"><p class="hatena-asin-detail-title"><a href="https://www.amazon.co.jp/dp/B09B8LFKQL?tag=kazzhomeunixo-22&amp;linkCode=ogi&amp;th=1&amp;psc=1" target="_blank" rel="noopener">A Philosophy of Software Design, 2nd Edition (English Edition)</a></p><ul class="hatena-asin-detail-meta"><li><span class="hatena-asin-detail-label">作者:</span><a href="http://d.hatena.ne.jp/keyword/Ousterhout%2C%20John%20K.%20" class="keyword">Ousterhout, John K. </a></li><li></li></ul><a href="https://www.amazon.co.jp/dp/B09B8LFKQL?tag=kazzhomeunixo-22&amp;linkCode=ogi&amp;th=1&amp;psc=1" class="asin-detail-buy" target="_blank" rel="noopener">Amazon</a></div></div></p> <p>以下は読んだ章のINDEX.</p> <ul> <li><a href="https://journal.lampetty.net/entry/chapter1-software-design-book">Chapter 1 - Intruduction / A Philosophy of Software Design - oinume journal</a></li> <li><a href="https://journal.lampetty.net/entry/chapter2-software-design-book">Chapter 2 - The Nature of Complexity / A Philosophy of Software Design - oinume journal</a></li> <li><a href="https://journal.lampetty.net/entry/chapter3-software-design-book">Chapter 3 - Working Code Isn&rsquo;t Enough / A Philosophy of Software Design - oinume journal</a></li> <li><a href="https://journal.lampetty.net/entry/chapter4-software-design-book">Chapter 4 - Modules Should Be Deep / A Philosophy of Software Design - oinume journal</a></li> <li><a href="https://journal.lampetty.net/entry/chapter5-software-design-book">Chapter 5 - Information Hiding (and Leakage) / A Philosophy of Software Design - oinume journal</a></li> </ul> oinume JavaScriptの記号の演算子と構文 hatenablog://entry/13574176438008993928 2021-09-07T18:09:10+09:00 2021-11-28T14:50:04+09:00 JavaScript / TypeScript では一見すると「これなんだ?」という記号の演算子や構文がよく出てくるので、自分用の備忘録としてメモしておく。式と演算子 - JavaScript | MDNには演算子の一覧のページがあるため、わからない記号が出てきたら以下のページを見るとだいたい載っているはず。 ?? - Nullish coalescing operator Null 合体 (??) - JavaScript | MDN Null 合体演算子 (??) は論理演算子の一種です。この演算子は左辺が null または undefined の場合に右の値を返し、それ以外の場合に左の値… <p>JavaScript / TypeScript では一見すると「これなんだ?」という記号の演算子や構文がよく出てくるので、自分用の備忘録としてメモしておく。<a href="https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators">&#x5F0F;&#x3068;&#x6F14;&#x7B97;&#x5B50; - JavaScript | MDN</a>には演算子の一覧のページがあるため、わからない記号が出てきたら以下のページを見るとだいたい載っているはず。</p> <h2>?? - Nullish coalescing operator</h2> <ul> <li><a href="https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/Nullish_coalescing_operator">Null &#x5408;&#x4F53; (??) - JavaScript | MDN</a></li> <li>Null 合体演算子 (??) は論理演算子の一種です。この演算子は左辺が null または undefined の場合に右の値を返し、それ以外の場合に左の値を返します。</li> <li>例えば以下のコードだと、aがnull or undefinedの場合は <code>a is null or undefined</code> がvalに代入される</li> </ul> <pre class="code js" data-lang="js" data-unlink>const val = a ?? &#39;a is null or undefined&#39;;</pre> <h2>!! - Double not operator</h2> <ul> <li><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Logical_NOT#double_not_!!">Logical NOT (!) - JavaScript | MDN</a></li> <li>複数の否定演算子を連続して使用することで、明示的にあらゆる値を対応する論理型プリミティブに変換することができます。</li> <li>否定の否定なので肯定ということになり、元の値をbooleanに変換するということらしい</li> </ul> <pre class="code js" data-lang="js" data-unlink>const val = !!a; // val is always boolean</pre> <h2>?. - Optional chaining operator</h2> <ul> <li><a href="https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/Optional_chaining">&#x30AA;&#x30D7;&#x30B7;&#x30E7;&#x30CA;&#x30EB;&#x30C1;&#x30A7;&#x30FC;&#x30F3; (?.) - JavaScript | MDN</a></li> <li><code>?.</code> 演算子の機能は . チェーン演算子と似ていますが、参照が nullish (null または undefined) の場合にエラーとなるのではなく、式が短絡され undefined が返されるところが異なります。関数呼び出しで使用すると、与えられた関数が存在しない場合、 undefined を返します。</li> </ul> <pre class="code js" data-lang="js" data-unlink>const val = a?.b?.c; // aがnull or undefinedの場合 val が undefinedになる</pre> <h2>... - Spread syntax</h2> <ul> <li><a href="https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/Spread_syntax">&#x30B9;&#x30D7;&#x30EC;&#x30C3;&#x30C9;&#x69CB;&#x6587; - JavaScript | MDN</a></li> <li>スプレッド構文 (...) を使うと、配列式や文字列などの反復可能オブジェクトを、0 個以上の引数 (関数呼び出しの場合) や要素 (配列リテラルの場合) を期待された場所で展開したり、オブジェクト式を、0 個以上のキーと値のペア (オブジェクトリテラルの場合) を期待された場所で展開したりすることができます。</li> <li>一番多用されるのは配列やオブジェクトをコピーするときなのではないかと思われる</li> </ul> <pre class="code js" data-lang="js" data-unlink>const arr = [1, 2, 3]; const arr2 = [...arr]; // arr.slice()みたいな</pre> <h2>! - Non-null assertion operator</h2> <p>これはJavaScriptではなくてTypeScriptにしかない演算子。</p> <ul> <li><a href="https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-0.html#non-null-assertion-operator">TypeScript: Documentation - TypeScript 2.0</a></li> <li>以下のようなコードの時に、<code>e</code>がundefinedまたはnullではないことを保証して、<code>name</code>にアクセスできる</li> </ul> <pre class="code js" data-lang="js" data-unlink>// Compiled with --strictNullChecks function validateEntity(e?: Entity) { // Throw exception if e is null or invalid entity } function processEntity(e?: Entity) { validateEntity(e); let s = e!.name; // Assert that e is non-null and access name }</pre> oinume TypeScriptのDestructuring assignment hatenablog://entry/26006613794661507 2021-08-10T10:08:43+09:00 2021-08-10T10:08:43+09:00 最近仕事でfrontendの開発を少しずつやるようになったのだけど、TypeScriptはGoに比べて演算子や記号を使う記法が多くて読むのに一苦労する。なので「これなんだろう?」と思ったやつをメモしておく。 TypeScriptやっていて一番最初に「ん?」と思ったのは、以下のような構文だった。 const obj = { title: 'hello' } const { title: myTitle } = obj // ★ console.log(myTitle) // "hello" ★のコードでは obj というオブジェクトから title という要素を取り出して myTitleという定… <p>最近仕事でfrontendの開発を少しずつやるようになったのだけど、TypeScriptはGoに比べて演算子や記号を使う記法が多くて読むのに一苦労する。なので「これなんだろう?」と思ったやつをメモしておく。</p> <p>TypeScriptやっていて一番最初に「ん?」と思ったのは、以下のような構文だった。</p> <pre class="code lang-typescript" data-lang="typescript" data-unlink><span class="synStatement">const</span> obj <span class="synStatement">=</span> <span class="synIdentifier">{</span> title: <span class="synConstant">'hello'</span> <span class="synIdentifier">}</span> <span class="synStatement">const</span> <span class="synIdentifier">{</span> title: myTitle <span class="synIdentifier">}</span> <span class="synStatement">=</span> obj <span class="synComment">// ★</span> console.log<span class="synStatement">(</span>myTitle<span class="synStatement">)</span> <span class="synComment">// &quot;hello&quot;</span> </pre> <p>★のコードでは</p> <ol> <li>obj というオブジェクトから title という要素を取り出して</li> <li>myTitleという定数を宣言</li> <li>myTitleに 1. で取り出した要素を代入する</li> </ol> <p>ということをやっている。<code>myTitle</code>が定数宣言であるのに対して、キーの<code>title</code>は単純にオブジェクトのキーなので特に何かが宣言されているわけでない、ということが初めはわかっていなかったのでこの構文を見るたびに頭が混乱していた。</p> <p>調べてみると、これはJS由来の構文で<code>Destructuring assignment</code>と呼ぶらしい。</p> <ul> <li><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment">Destructuring assignment - JavaScript | MDN</a></li> <li><a href="https://basarat.gitbook.io/typescript/future-javascript/destructuring">Destructuring - TypeScript Deep Dive</a></li> </ul> <p>上の例ではオブジェクトから要素を取り出す例だったが、当然配列から特定の要素を取り出すこともできる。</p> <pre class="code lang-typescript" data-lang="typescript" data-unlink><span class="synStatement">const</span> array <span class="synStatement">=</span> <span class="synIdentifier">[</span><span class="synConstant">1</span><span class="synStatement">,</span> <span class="synConstant">2</span><span class="synIdentifier">]</span> <span class="synStatement">const</span> <span class="synIdentifier">[</span> first<span class="synStatement">,</span> second <span class="synIdentifier">]</span> <span class="synStatement">=</span> array console.log<span class="synStatement">(</span>first<span class="synStatement">)</span> <span class="synComment">// 1</span> </pre> <p>さらにスプレッド構文 <code>...</code> を使って以下のような代入も可能。</p> <pre class="code" data-lang="" data-unlink>[a, b, ...rest] = [10, 20, 30, 40, 50]; console.log(rest); [30, 40, 50] </pre> <p>Goと違って多値が返せないからこういう構文が必要になるのかなと思ったのだけど、どうなんだろう?</p> oinume Go1.17beta1でGenericsに触れてみた hatenablog://entry/26006613786124214 2021-07-14T08:30:00+09:00 2021-07-14T08:30:00+09:00 Go1.17beta1がダウンロードできるようになったので、Generics(Type Parameters)でStackを書いて軽く遊んだメモ。 Generics (Type Parameters)について 最新の仕様のProposalは以下を見ると良い。 実際にどんな感じのコードになるのかは、GitHubのdev.typeparams branchのコードを見ると良さそう。 Go1.17beta1のダウンロード 普段使っているgoコマンドから以下のようにGo1.17beta1をダウンロードできる。 $ go get golang.org/dl/go1.17beta1 $ go1.17bet… <p>Go1.17beta1がダウンロードできるようになったので、Generics(Type Parameters)でStackを書いて軽く遊んだメモ。</p> <h2>Generics (Type Parameters)について</h2> <p>最新の仕様のProposalは以下を見ると良い。 <iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fgo.googlesource.com%2Fproposal%2F%2B%2Frefs%2Fheads%2Fmaster%2Fdesign%2F43651-type-parameters.md" title="Type Parameters Proposal" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;"></iframe></p> <p>実際にどんな感じのコードになるのかは、<a href="https://github.com/golang/go/blob/dev.typeparams/test/typeparam">GitHubのdev.typeparams branch</a>のコードを見ると良さそう。</p> <h2>Go1.17beta1のダウンロード</h2> <p>普段使っているgoコマンドから以下のようにGo1.17beta1をダウンロードできる。</p> <pre class="code lang-sh" data-lang="sh" data-unlink>$ go get golang.org/dl/go1.17beta1 $ go1.17beta1 download </pre> <p>これで ~/sdk/go1.17beta1 にインストールされる。</p> <p>FYI: Goは標準の機能で複数のバージョンをインストールできる。より詳しくは以下のドキュメントを読むと良い。</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fgolang.org%2Fdoc%2Fmanage-install" title="Managing Go installations - The Go Programming Language" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;"></iframe></p> <h2>Genericsのサンプルコード</h2> <p>Go1.17beta1に付属している以下のディレクトリに結構ある。</p> <pre class="code lang-sh" data-lang="sh" data-unlink>$ <span class="synStatement">ls</span> <span class="synPreProc">$(</span><span class="synSpecial">go1.17beta1 env GOROOT</span><span class="synPreProc">)</span>/<span class="synStatement">test</span>/typeparam absdiff.go fact.go issue45738.go min.go slices.go tparam1.go adder.go graph.go list.go ordered.go smallest.go typelist.go append.go importtest.go list2.go orderedmap.go smoketest.go value.go chans.go index.go lockable.go pair.go stringable.go combine.go interfacearg.go map.go pragma.go stringer.go cons.go issue45547.go maps.go sets.go struct.go double.go issue45722.go metrics.go settable.go sum.go </pre> <h2>GenericsでStackを書いてみる</h2> <p>こんな感じになった。現時点ではType Parametersの型をexportすることができないっぽいので、<code>stack</code>と小文字で定義してmainパッケージに書いている。</p> <pre class="code lang-go" data-lang="go" data-unlink><span class="synComment">// run -gcflags=-G=3</span> <span class="synStatement">package</span> main <span class="synStatement">import</span> <span class="synConstant">&quot;fmt&quot;</span> <span class="synStatement">var</span> ( ErrEmptyStack = fmt.Errorf(<span class="synConstant">&quot;stack is empty&quot;</span>) ) <span class="synStatement">type</span> stack[T any] <span class="synStatement">struct</span> { data []T capacity <span class="synType">int</span> } <span class="synStatement">func</span> newStack[T any](capacity <span class="synType">int</span>) *stack[T] { <span class="synStatement">if</span> capacity &lt;= <span class="synConstant">0</span> { <span class="synStatement">panic</span>(<span class="synConstant">&quot;must be 'capacity' &gt; 0&quot;</span>) } data := <span class="synStatement">make</span>([]T, <span class="synConstant">0</span>, capacity) <span class="synStatement">return</span> &amp;stack[T]{data: data, capacity: capacity} } <span class="synStatement">func</span> (s *stack[T]) Push(v T) { s.data = <span class="synStatement">append</span>(s.data, v) } <span class="synStatement">func</span> (s *stack[T]) Pop() (T, <span class="synType">error</span>) { <span class="synStatement">if</span> s.Size() == <span class="synConstant">0</span> { <span class="synStatement">var</span> zero T <span class="synStatement">return</span> zero, ErrEmptyStack } v := s.data[<span class="synStatement">len</span>(s.data)-<span class="synConstant">1</span>] s.data = s.data[:<span class="synStatement">len</span>(s.data)-<span class="synConstant">1</span>] <span class="synStatement">return</span> v, <span class="synStatement">nil</span> } <span class="synStatement">func</span> (s *stack[T]) Peek() (T, <span class="synType">error</span>) { <span class="synStatement">if</span> s.Size() == <span class="synConstant">0</span> { <span class="synStatement">var</span> zero T <span class="synStatement">return</span> zero, ErrEmptyStack } <span class="synStatement">return</span> s.data[<span class="synStatement">len</span>(s.data)-<span class="synConstant">1</span>], <span class="synStatement">nil</span> } <span class="synStatement">func</span> (s *stack[T]) Size() <span class="synType">int</span> { <span class="synStatement">return</span> <span class="synStatement">len</span>(s.data) } <span class="synStatement">func</span> main() { s := newStack[<span class="synType">int</span>](<span class="synConstant">10</span>) s.Push(<span class="synConstant">1</span>) s.Push(<span class="synConstant">2</span>) s.Push(<span class="synConstant">3</span>) fmt.Printf(<span class="synConstant">&quot;s = %+v</span><span class="synSpecial">\n</span><span class="synConstant">&quot;</span>, s) _, err := s.Pop() <span class="synStatement">if</span> err != <span class="synStatement">nil</span> { <span class="synStatement">panic</span>(err) } fmt.Printf(<span class="synConstant">&quot;s = %+v</span><span class="synSpecial">\n</span><span class="synConstant">&quot;</span>, s) } </pre> <p>コード書いてて少し悩んだのは、<code>func (s *stack[T]) Pop() (T, error)</code> のようなメソッドでerrorを返す時にT型のゼロ値をどうやって返すのか、という点だった。どうやら以下のように <code>var zero T</code> を定義してそれを返すのが良いらしい。<code>return nil, ErrEmptyStack</code>という風に書くと、Tはポインタ型ではないのでコンパイルエラーになる。</p> <pre class="code lang-go" data-lang="go" data-unlink><span class="synStatement">func</span> (s *stack[T]) Pop() (T, <span class="synType">error</span>) { <span class="synStatement">if</span> s.Size() == <span class="synConstant">0</span> { <span class="synStatement">var</span> zero T <span class="synStatement">return</span> zero, ErrEmptyStack } v := s.data[<span class="synStatement">len</span>(s.data)-<span class="synConstant">1</span>] s.data = s.data[:<span class="synStatement">len</span>(s.data)-<span class="synConstant">1</span>] <span class="synStatement">return</span> v, <span class="synStatement">nil</span> } </pre> <h2>実行</h2> <p>main関数があれば普通にgo1.17beta1 runコマンドで実行できる。<code>-gcflags=-G=3</code>を付けないとgenericsのコードはコンパイルできないので注意。</p> <pre class="code lang-sh" data-lang="sh" data-unlink>$ go1.17beta1 run <span class="synSpecial">-gcflags</span><span class="synStatement">=</span>-G<span class="synStatement">=</span><span class="synConstant">3</span> ./typeparam.go s <span class="synStatement">=</span> &amp;<span class="synSpecial">{</span>data:<span class="synStatement">[</span><span class="synConstant">1</span> <span class="synConstant">2</span> <span class="synConstant">3</span><span class="synStatement">]</span> capacity:10<span class="synSpecial">}</span> s <span class="synStatement">=</span> &amp;<span class="synSpecial">{</span>data:<span class="synStatement">[</span><span class="synConstant">1</span> <span class="synConstant">2</span><span class="synStatement">]</span> capacity:10<span class="synSpecial">}</span> </pre> <h2>感想</h2> <p>Type Parametersの機能が入るリリースは2022年の2月のGo 1.18だと思うけど、現時点でもモリモリ実装されているのでとても楽しみである。</p> oinume Apple Silicon向けDocker Desktop for Macでコンテナがハングしないようにする hatenablog://entry/26006613775411285 2021-06-14T09:00:00+09:00 2021-06-16T16:22:18+09:00 TL;DR Apple Silicon(M1) Docker Desktop for Macでgoogle/cloud-sdkのDocker Imageを使ってコンテナ内でCloud Datastore Emulatorを立ち上げると、CPUが100%で張り付いてハングするという問題があった。 解決方法として、該当のDocker Imageをarm64v8アーキテクチャでビルドして、そのイメージでコンテナを立ち上げるようにしたら問題は起きなくなったので、その方法の紹介 問題の詳細 Apple Silicon版のDocker Desktop for MacはRosetta2を使いインテルアーキテ… <h2>TL;DR</h2> <ul> <li>Apple Silicon(M1) Docker Desktop for Macで<a href="https://hub.docker.com/r/google/cloud-sdk/">google/cloud-sdk</a>のDocker Imageを使ってコンテナ内でCloud Datastore Emulatorを立ち上げると、CPUが100%で張り付いてハングするという問題があった。</li> <li>解決方法として、該当のDocker Imageをarm64v8アーキテクチャでビルドして、そのイメージでコンテナを立ち上げるようにしたら問題は起きなくなったので、その方法の紹介</li> </ul> <h2>問題の詳細</h2> <p>Apple Silicon版のDocker Desktop for MacはRosetta2を使いインテルアーキテクチャ(amd64 / x86_64)をエミュレーションして実行されている。エミュレーションが行われているため、ネイティブでarm64v8のコンテナを実行するよりCPUを使ってしまうという問題がある。これは<a href="https://docs.docker.com/docker-for-mac/apple-silicon/#known-issues">Known Issues</a>として以下のように書かれている。</p> <blockquote><p>However, attempts to run Intel-based containers on Apple Silicon machines under emulation can crash as qemu sometimes fails to run the container. In addition, filesystem change notification APIs (inotify) do not work under qemu emulation. Even when the containers do run correctly under emulation, they will be slower and use more memory than the native equivalent.</p> <p>In summary, running Intel-based containers on Arm-based machines should be regarded as “best effort” only. We recommend running arm64 containers on Apple Silicon machines whenever possible, and encouraging container authors to produce arm64, or multi-arch, versions of their containers.</p></blockquote> <p>自分が遭遇した現象としては、google/cloud-sdkのイメージを使ってgcloudコマンドでDatastore Emulatorを立ち上げようとすると、3回に1回ぐらいの確率でCPUが100%になり、コンテナが全く応答しなくなるという問題だった。</p> <blockquote><p>We recommend running arm64 containers on Apple Silicon machines whenever possible</p></blockquote> <p>とDocker Desktop on MacのKnown Issuesに書かれているので、これにしたがってgoogle/cloud-sdkのarm64v8のDocker Imageをビルドしてみた。</p> <h2>google/cloud-sdk arm64v8 Imageをビルドする</h2> <p>公式には提供されていないので、自分でDockerfileを作ってイメージをビルドする必要がある。 <a href="https://hub.docker.com/u/arm64v8">https://hub.docker.com/u/arm64v8</a> には様々なarm64v8のDocker Imageがあるので、debian:buster-slimをベースにして以下のようなDockerfileを作る。ちなみに今回は<code>google-cloud-sdk-datastore-emulator</code>しか使わないのでこれだけをインストールしているが、他のEmulatorが必要であれば<a href="https://github.com/GoogleCloudPlatform/cloud-sdk-docker/blob/master/emulators/Dockerfile">公式のDockerfile</a>を参考にインストールすれば良い。</p> <pre class="code" data-lang="" data-unlink>FROM arm64v8/debian:buster-slim ARG CLOUD_SDK_VERSION=341.0.0 ENV CLOUD_SDK_VERSION=$CLOUD_SDK_VERSION RUN groupadd -r -g 1000 cloudsdk &amp;&amp; \ useradd -r -u 1000 -m -s /bin/bash -g cloudsdk cloudsdk RUN mkdir -p /usr/share/man/man1/ &amp;&amp; \ apt-get update &amp;&amp; \ apt-get -y install \ curl \ gnupg \ sudo \ python3 \ python3-crcmod \ bash \ openjdk-11-jre-headless RUN echo &#34;deb [signed-by=/usr/share/keyrings/cloud.google.gpg] http://packages.cloud.google.com/apt cloud-sdk main&#34; | sudo tee -a /etc/apt/sources.list.d/google-cloud-sdk.list RUN curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key --keyring /usr/share/keyrings/cloud.google.gpg add - RUN apt-get update &amp;&amp; \ apt-get install -y \ google-cloud-sdk \ google-cloud-sdk-datastore-emulator RUN gcloud config set core/disable_usage_reporting true &amp;&amp; \ gcloud config set component_manager/disable_update_check true &amp;&amp; \ gcloud config set metrics/environment github_docker_image_emulator</pre> <p>そしてDocker Imageをビルドする。</p> <pre class="code bash" data-lang="bash" data-unlink>$ docker build -t cloud-sdk-emulators-arm64v8:341.0.0 .</pre> <h2>最後に</h2> <p>今回はGoのユニットテストから<a href="https://github.com/ory/dockertest">ory/dockertest</a>を使ってDatastore Emulatorを立ち上げていたのだが、コンテナを何個立ち上げても全くハングしなくなりとても安定した。今回はDatastore Emulatorだったけど、他のImageでも起こりうる問題だとは思うので、もしApple siliconのマシンでDockerを動かしていて同じような症状で困っている人は参考にしてもらえると良いと思う。</p> oinume 2021年の抱負 hatenablog://entry/26006613685145543 2021-01-30T23:14:54+09:00 2021-01-30T23:14:54+09:00 年初に新年の抱負を書こうと思っていたがずっと後回しになってしまい、1月もそろそろ終わってしまう時期になった。というわけで明けましておめでとうございます。 例によって全ては達成できないと思うけど、今年やりたいことを抱負としてまとめてみる。 習慣化 これは抱負というよりかはよりメタなものだけど、継続していきたいことは習慣化させないと続かないため、習慣化を努力しようと思っている。昨年の11月からHabitifyというiOSアプリを試していてなかなか良いなと思ったので、年初に1年単位で課金してみた。このアプリは、例えば「ジョギング」のような習慣化したいものを登録しておいて、それを通知でリマインドしたり… <p>年初に新年の抱負を書こうと思っていたがずっと後回しになってしまい、1月もそろそろ終わってしまう時期になった。というわけで明けましておめでとうございます。</p> <p>例によって全ては達成できないと思うけど、今年やりたいことを抱負としてまとめてみる。</p> <h2>習慣化</h2> <p>これは抱負というよりかはよりメタなものだけど、継続していきたいことは習慣化させないと続かないため、習慣化を努力しようと思っている。昨年の11月から<a href="https://www.habitify.me/">Habitify</a>というiOSアプリを試していてなかなか良いなと思ったので、年初に1年単位で課金してみた。このアプリは、例えば「ジョギング」のような習慣化したいものを登録しておいて、それを通知でリマインドしたり、実際にやったことを記録して週や月単位で振り返りするようなアプリだ。自分の場合は以下のような習慣を登録している。</p> <ul> <li>プロテインを飲む</li> <li>階段ジョギング</li> <li>筋トレ</li> <li>iKnow(英単語学習)</li> </ul> <p>このアプリ、特に何かすごい機能があるわけではないけど、リマインドを1日に2回するなど割と気が利いているのとストレスなく使えるので気に入っている。今年はこのアプリとともに習慣化に一番力を入れたいなと思っている。</p> <h2>ReactでSPA作れるようになる</h2> <p>仕事では専任のFrontend Engineerがいないため、自分もFrontのコードを触らなくてはいけないし、もともと自分ひとりでサービスを作ったりしたいので、今年はTypeScript + ReactでSPAのサービスを作れるようになりたい。</p> <ul> <li>TypeScript</li> <li>React</li> <li>React Hooks</li> <li>React Context</li> <li>React Router</li> <li>Jest</li> </ul> <p>あたりを学べばいいのかなと漠然と思っている。余裕があったらNext.jsにもトライしてみたい。</p> <h2>英語</h2> <p>毎日iKnowで英単語をやるのと、週に1回はSkype英会話のレッスンをやるようにして、英語力を下げない努力をしようと思っている。昨年まではDMM英会話をやっていたが</p> <ul> <li>値段が高くなってきている</li> <li>英会話をやる時間を捻出するのが難しい</li> <li>単語を発声して覚える方が英語身についてそう(自分が発音できない単語は聞き取れないらしいので)</li> </ul> <p>と思ったのでiKnowをなるべくやるようにして、週1のSkypeレッスンに切り替えた。あと、実力値を安価に測るためにTOEICを年に2回受けるつもり。</p> <h2>アウトプット</h2> <p>1ヶ月に1つはちゃんとした記事を書く。昨年に引き続き継続は力なり、ということで。</p> <h2>というわけで</h2> <p>2021年もやっていくぞい</p> oinume 2020年の振り返り hatenablog://entry/26006613672448185 2020-12-31T23:48:22+09:00 2020-12-31T23:48:22+09:00 早いもので2020も終わってしまう。歳をとるごとに1年が過ぎていくのが早く感じる気がするけど、ここまで早く過ぎ去ってかつ内容が薄い年は初めてなのではないかと思う。ただ、多くの人にとって大変な年になったのは間違いないと思うけど、自分はソフトウェアエンジニアという職業だったおかげで、相対的に見ればCOVID-19の経済的な影響は少なかったので、ラッキーだったとは思う。あらためてテクノロジーの進化とソフトウェアエンジニアになろうと決めた2000年頃の自分に感謝したい。 1月 2019年末からインフルエンザにかかって実家に帰ったりもできず最悪のスタート。しかし、初めて家族でスキーに行ったり、その直後に… <p>早いもので2020も終わってしまう。歳をとるごとに1年が過ぎていくのが早く感じる気がするけど、ここまで早く過ぎ去ってかつ内容が薄い年は初めてなのではないかと思う。ただ、多くの人にとって大変な年になったのは間違いないと思うけど、自分はソフトウェアエンジニアという職業だったおかげで、相対的に見ればCOVID-19の経済的な影響は少なかったので、ラッキーだったとは思う。あらためてテクノロジーの進化とソフトウェアエンジニアになろうと決めた2000年頃の自分に感謝したい。</p> <h2>1月</h2> <p>2019年末からインフルエンザにかかって実家に帰ったりもできず最悪のスタート。しかし、初めて家族でスキーに行ったり、その直後にGoDaysというベルリンで開催されたGoのカンファレンスに参加できたのはとてもラッキーだった。GoDaysのついでにワルシャワに1泊2日で個人旅行してきて、ポーランドという国の大変な歴史を学びつつ、社会主義から資本主義に移行している勢いも感じられたのでよかった。</p> <p><blockquote data-conversation="none" class="twitter-tweet" data-lang="en"><p lang="en" dir="ltr">Got gopher finally. Thank you! <a href="https://twitter.com/hashtag/GoDays20?src=hash&amp;ref_src=twsrc%5Etfw">#GoDays20</a> <a href="https://t.co/Afl5mm6X6u">pic.twitter.com/Afl5mm6X6u</a></p>&mdash; oinume@Dr.’s Prime (@oinume) <a href="https://twitter.com/oinume/status/1220075607289188354?ref_src=twsrc%5Etfw">January 22, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> <blockquote data-conversation="none" class="twitter-tweet" data-lang="en"><p lang="ja" dir="ltr">旧市街を一通り歩いてきた。ここは家族で来たかったなぁ。 <a href="https://t.co/8gCFSjOD3f">pic.twitter.com/8gCFSjOD3f</a></p>&mdash; oinume@Dr.’s Prime (@oinume) <a href="https://twitter.com/oinume/status/1220760446329712640?ref_src=twsrc%5Etfw">January 24, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> </p> <p>振り返ると2020年は私生活ではこの1月が最高に充実していたと思う。スタートアップへの転職について考え始めたのもこの1月からだったと思う。</p> <h2>2月</h2> <p>会社の炎上プロジェクトが本格的に炎上し始めて仕事が大変になってきたのがこの時期だった。ただ、コロナも未だこの頃は猛威を奮っていなくて呑気に出社して仕事をしていた気がする。</p> <h2>3月〜6月</h2> <p>会社が原則WFHに移行し、さらに子供が保育園に行けなくなり精神的に削られる日々が続いた。子供が家にいる状態で集中して作業できる時間が確保できなくなったため、自分でコードを書くのは諦めて、担当しているmicroserviceのproductivity teamのPM的な役割に徹した。コードが書けないのは個人的にはストレスだったけど、とても強い人たちが支えてくれたので、チームとしては結構うまく立ち回れたのではないかと思っている。</p> <p>また、GW前の仕事が一番炎上している時にSoftware Designの記事の締め切りがあり、生きている心地がしないぐらい忙しかった記憶がある。(みんな買ってくれ)</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fgihyo.jp%2Fmagazine%2FSD%2Farchive%2F2020%2F202007" title="Software Design 2020年7月号" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;"></iframe><cite class="hatena-citation"><a href="https://gihyo.jp/magazine/SD/archive/2020/202007">gihyo.jp</a></cite></p> <h2>7月〜8月</h2> <p>メルペイを退職する意向を固めて、本格的に転職活動をしていた時期。コロナ渦での最終出社は寂しかったけど、あまり盛大に送り出されるのも苦手なので、あのぐらいがちょうどよかった。たまたま会社に10人ぐらい人が来ていたので久しぶりに同僚といい話ができた気がする。</p> <p>8月後半にはグラファーとドクターズプライムからオファーをもらい、どちらに行くか決めきれなかったので9月と10月でそれぞれで働いてどちらかに決めることになった。悩みまくってとりあえず横浜のみなとみらいまで海を見に行ったりした。</p> <h2>9月</h2> <p>この1ヶ月はグラファーで業務委託としてお試しで働かせてもらっていた。地方自治体向けのSaaS開発の裏側と急成長しているスタートアップの裏側を何一つ隠すところなく見せてもらい、とても有意義だった。「こういうことをやって欲しい」というお題をある程度与えてもらいつつ、自分的に課題だと感じたところを見つけて直していくようなことをやった。結果としてグラファーからのオファーは断ってしまったけど、CEOの石井さんの考え方がとても好きでファンなので、副業で少しだけ手伝わせてもらっている。</p> <h2>10月</h2> <p>10月はドクターズプライムで1ヶ月働いてみた。グラファーはリモート中心で、こっちはオフィスに出社するのが中心であり、久々にオフィスメインで仕事することもあってか、人と雑談しながら仕事をするのがとても楽しかった。この頃は自分以外にまだエンジニアが誰もいなかったので、どういう風に仕事を進めればいいか悩ましいところもあったけど、他の人々が優しく接してくれたのであまり肩肘貼らずに仕事できたように思う。</p> <h2>11月〜12月</h2> <p>最終的にドクターズプライムに決めて社員として働き始めた。正直コードを書く量は前職に比べて減った気がするけど、プロダクト開発やセキュリティ強化、採用など様々なことをやらなくてはいけないカオスな状況は楽しい。メルペイで働き続けていた方がバックエンドエンジニアとしてのキャリアは積めるだろうけど、社員10名規模で技術スタックが完全一致しているスタートアップで働ける機会なんてそうそうないと思ったので転職してよかったと思う。</p> <p><blockquote data-conversation="none" class="twitter-tweet" data-lang="en"><p lang="ja" dir="ltr">やっていきます💪💪<a href="https://t.co/mgHx9JgpTD">https://t.co/mgHx9JgpTD</a></p>&mdash; oinume@Dr.’s Prime (@oinume) <a href="https://twitter.com/oinume/status/1325954390792916993?ref_src=twsrc%5Etfw">November 10, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> </p> <h2>ライブ</h2> <p>今年はこれらのライブに行くはずだったけど当然全部キャンセル or リスケされた。来年こそはと思うけどワクチン次第なのでどうなることやら。</p> <ul> <li>Knotfest</li> <li>Green Day</li> <li>Avril Lavigne</li> <li>Viva La Rock</li> </ul> <p>しかしストリーミングでDragon Ashのライブを3個も見れたので、これはこれでありなのでは?とも思い始めている。</p> <h2>最後に</h2> <p>何だかんだで今年は激動の1年だった。他にも大事なことがいろいろあった気がするけど思い出せないので気にしないことにします。 新型コロナに負けずに来年もよい年を迎えられると良いなと思っております。皆さんも良いお年を!!</p> oinume O'Reilly Online Learningで日本語の本を読む方法 hatenablog://entry/26006613656825326 2020-11-27T09:00:00+09:00 2021-03-08T11:37:34+09:00 O'ReillyのOnline Learning(旧O'Reilly Safari Books Online)は月額$49でオライリーの本や動画などが見放題になるエンジニア向けのサブスクを提供している。以前は英語の本しか読めなかったが、いつからか日本語の本も読めるようになっていたのでメモ。 www.oreilly.com Sign Inして、左のメニューのSettingsをクリックするとLanguage Preferencesがあるので、ここでJapaneseにチェックを入れて下のUpdate Preferencesをクリックして保存する。 これでHomeに行き、例えばGraphQLで検索する… <p>O'ReillyのOnline Learning(旧O'Reilly Safari Books Online)は月額$49でオライリーの本や動画などが見放題になるエンジニア向けのサブスクを提供している。以前は英語の本しか読めなかったが、いつからか日本語の本も読めるようになっていたのでメモ。</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fwww.oreilly.com%2Fonline-learning%2F" title="Online Learning and Training - O&#39;Reilly Media" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;"></iframe><cite class="hatena-citation"><a href="https://www.oreilly.com/online-learning/">www.oreilly.com</a></cite></p> <p>Sign Inして、左のメニューのSettingsをクリックするとLanguage Preferencesがあるので、ここで<code>Japanese</code>にチェックを入れて下の<code>Update Preferences</code>をクリックして保存する。</p> <iframe src="https://drive.google.com/file/d/1FVUtgiL2Hal2K_a3zj1e9Ctukn143RMa/preview" width="640" height="480"></iframe> <p>これでHomeに行き、例えばGraphQLで検索すると検索結果の画面でBooksのタブがあるのでこれを選択する。そうするとLanguageの選択ができるので、ここで<code>Japanese</code>を選ぶと日本語の本だけに絞ることができる。(手っ取り早く検索結果を表示したい時は<a href="https://learning.oreilly.com/search/?query=*&amp;extended_publisher_data=true&amp;highlight=true&amp;include_assessments=false&amp;include_case_studies=true&amp;include_courses=true&amp;include_playlists=true&amp;include_collections=true&amp;include_notebooks=true&amp;include_sandboxes=true&amp;include_scenarios=true&amp;is_academic_institution_account=false&amp;source=user&amp;formats=book&amp;languages=ja&amp;sort=date_added&amp;facet_json=true&amp;json_facets=true&amp;page=0&amp;include_facets=false">こちら</a>)</p> <iframe src="https://drive.google.com/file/d/1oGjjMfsilThL82V90u4ryzfjgrHeOuVS/preview" width="640" height="480"></iframe> <p>目的の本が見つかったらそれをPlaylistに入れるなりすればOK。なお、AndroidのO'Reillyのアプリからだと日本語の本をダウンロードして読むことができたが、iOSはエラーになって読めなかった。</p> <p>2021年2月時点では合計57冊の日本語の本が読めるらしい。個人的に良さそうだなと思ったものをいくつか書いておく。</p> <ul> <li><a href="https://learning.oreilly.com/library/view/untitled/9784873118703/">&#x30C7;&#x30FC;&#x30BF;&#x6307;&#x5411;&#x30A2;&#x30D7;&#x30EA;&#x30B1;&#x30FC;&#x30B7;&#x30E7;&#x30F3;&#x30C7;&#x30B6;&#x30A4;&#x30F3; &#x2015;&#x4FE1;&#x983C;&#x6027;&#x3001;&#x62E1;&#x5F35;&#x6027;&#x3001;&#x4FDD;&#x5B88;&#x6027;&#x306E;&#x9AD8;&#x3044;&#x5206;&#x6563;&#x30B7;&#x30B9;&#x30C6;&#x30E0;&#x8A2D;&#x8A08;&#x306E;&#x539F;&#x7406; [Book]</a></li> <li><a href="https://learning.oreilly.com/library/view/sre-google/9784873117911/">SRE &#x30B5;&#x30A4;&#x30C8;&#x30EA;&#x30E9;&#x30A4;&#x30A2;&#x30D3;&#x30EA;&#x30C6;&#x30A3;&#x30A8;&#x30F3;&#x30B8;&#x30CB;&#x30A2;&#x30EA;&#x30F3;&#x30B0; &#x2015;Google&#x306E;&#x4FE1;&#x983C;&#x6027;&#x3092;&#x652F;&#x3048;&#x308B;&#x30A8;&#x30F3;&#x30B8;&#x30CB;&#x30A2;&#x30EA;&#x30F3;&#x30B0;&#x30C1;&#x30FC;&#x30E0; [Book]</a></li> <li><a href="https://learning.oreilly.com/library/view/untitled/9784873118888/">&#x30BC;&#x30ED;&#x30C8;&#x30E9;&#x30B9;&#x30C8;&#x30CD;&#x30C3;&#x30C8;&#x30EF;&#x30FC;&#x30AF; &#x2015;&#x5883;&#x754C;&#x9632;&#x5FA1;&#x306E;&#x9650;&#x754C;&#x3092;&#x8D85;&#x3048;&#x308B;&#x305F;&#x3081;&#x306E;&#x30BB;&#x30AD;&#x30E5;&#x30A2;&#x306A;&#x30B7;&#x30B9;&#x30C6;&#x30E0;&#x8A2D;&#x8A08; [Book]</a></li> <li><a href="https://learning.oreilly.com/library/view/cto/9784873118482/">&#x30A8;&#x30F3;&#x30B8;&#x30CB;&#x30A2;&#x306E;&#x305F;&#x3081;&#x306E;&#x30DE;&#x30CD;&#x30B8;&#x30E1;&#x30F3;&#x30C8;&#x30AD;&#x30E3;&#x30EA;&#x30A2;&#x30D1;&#x30B9; &#x2015;&#x30C6;&#x30C3;&#x30AF;&#x30EA;&#x30FC;&#x30C9;&#x304B;&#x3089;CTO&#x307E;&#x3067;&#x30DE;&#x30CD;&#x30B8;&#x30E1;&#x30F3;&#x30C8;&#x30B9;&#x30AD;&#x30EB;&#x5411;&#x4E0A;&#x30AC;&#x30A4;&#x30C9; [Book]</a></li> <li><a href="https://learning.oreilly.com/library/view/go/9784873118468/">Go&#x8A00;&#x8A9E;&#x306B;&#x3088;&#x308B;&#x4E26;&#x884C;&#x51E6;&#x7406; [Book]</a></li> <li><a href="https://learning.oreilly.com/library/view/graphql-webapi/9784873118932/">&#x521D;&#x3081;&#x3066;&#x306E;GraphQL &#x2015;Web&#x30B5;&#x30FC;&#x30D3;&#x30B9;&#x3092;&#x4F5C;&#x3063;&#x3066;&#x5B66;&#x3076;&#x65B0;&#x4E16;&#x4EE3;API [Book]</a></li> </ul> <p>なお、裏技としてACMの会員になると年$99でO'Reilly Online Learningにアクセスできるようになるので、4冊ぐらい読めば元は取れることになる。ACMの登録方法については<a href="https://agnozingdays.hatenablog.com/entry/2018/08/02/235021">ここ</a>によくまとまっているので参考になると思う。</p> <p>それでは素敵なO'Reillyライフを!</p> oinume ドクターズプライムに入社しました hatenablog://entry/26006613648741282 2020-11-10T09:09:46+09:00 2020-11-10T09:09:46+09:00 これはなに? 入社エントリー&会社紹介です。表題の通りで、10月をもってメルカリ/メルペイを退職し、11月からドクターズプライムという会社で働いています。 なにやってるの? Backend Engineerとして、救急車のたらい回しをなくすためのプロダクトを開発しています。救急車のたらい回しが発生する理由としては以下のスライドに書いてある通りなのですが、これを解決するのがDr.'s Primeという医師の採用サービスになります。 自分も子供を持つ親なので何度か病院のお世話になったことはあるし、子供が救急車で運ばれて手術&入院したこともあるので、救急医療に関しては一当事者として良くしていきたいと… <h2>これはなに?</h2> <p>入社エントリー&会社紹介です。表題の通りで、10月をもってメルカリ/メルペイを退職し、11月から<a href="https://drsprime.com/company/">ドクターズプライム</a>という会社で働いています。</p> <h2>なにやってるの?</h2> <p>Backend Engineerとして、救急車のたらい回しをなくすためのプロダクトを開発しています。救急車のたらい回しが発生する理由としては以下のスライドに書いてある通りなのですが、これを解決するのがDr.'s Primeという医師の採用サービスになります。</p> <script async class="speakerdeck-embed" data-slide="9" data-id="942129cb00514737ba067cf1ae49dc94" data-ratio="1.77777777777778" src="//speakerdeck.com/assets/embed.js"></script> <p>自分も子供を持つ親なので何度か病院のお世話になったことはあるし、子供が救急車で運ばれて手術&入院したこともあるので、救急医療に関しては一当事者として良くしていきたいという思いがありました。ドクターズプライムは救急車の搬送を断らないための仕組みを採用サービスとして第三者の立場から提供していて、素直にいいソリューションだなと思っています。</p> <h2>入社の経緯</h2> <p>今年に入ってからスタートアップで働くと言うことを意識し始めて、何社か話を聞かせてもらいました。時系列にするとこんな流れです。退職すること自体は昨年から別の理由で検討していたのですが、今年に入ってからスタートアップ良さそう!と思うようになりました。</p> <ul> <li>1月:スタートアップでDXをやっている会社に興味を持ち始める</li> <li>6,7月:メルペイを退職する意向を固めて、本格的に転職活動を始める</li> <li>8月:2社からオファーをもらって悩み始める</li> <li>10月末:入社を決める</li> </ul> <p>8月は2社からオファーをもらっていて悩んでいた時期で、そのうちの一つがドクターズプライムでした。2社のうちどちらにするかを悩んでいたところ、「両方で働いてみて決めればいいんじゃない?」という提案をしてもらい、ありがたく両方の会社で1ヶ月ずつ働くことにしました。ちなみにもう一つは地方自治体向けのSaaSを提供している<a href="https://graffer.jp/">グラファー</a>という会社です。</p> <p>最終的には、より会社規模が小さいドクターズプライムを選びました。エンジニアが自分を含めて2名しかいないため、必然的にプロダクト開発チームを作るところからスタートになります。1ヶ月お試しで働いてみてこのメンバーならチーム作りを含めて楽しくやれそうだと感じたこと、HRT(Humility, Respect, Trust)をはじめとしてカルチャーを大事にしていて、実際に中で働いてそれを体験できたことが決断の後押しになりました。</p> <p>ちなみにグラファーもいい会社です。こちらはリモート中心の働き方でしたが、相談事があったらすぐにZoom MTGが開催されたり、障害が発生した場合はポストモーテム(振り返り)をきちんと行い再発防止に努めていたり、プロフェッショナルさを感じることが多かったです。たまにオフィスに集まると和気藹々とランチを一緒に食べたり、スタートアップとしての一体感も強いです。</p> <p>取り組んでいる分野に違いはあれど、どちらの会社も日本に存在するペインを猛烈なスピードで解決するスタートアップであり、その両方で働く機会をもらったことはとてもいい経験になりました。両社とも楽しく仕事させてもらったので、分身できるのなら両方でフルタイムで働きたかったぐらいですw</p> <h2>ドクターズプライムでやっていきたいこと</h2> <p>創業時のリーンなプロダクト開発の結果として、GAS + Spreadsheet + Google Forms のシステムで売り上げが立つという、ある意味嬉しい誤算の状態になっていました。しかし、このGASベースのシステムもそろそろ綻びが見え始めており、一般的なFrontend + Backendの構成に置き換えていくことが当面やっていくことになります。</p> <p>これ以外にも</p> <ul> <li>新しい病院向けプロダクトの開発</li> <li>GCPの設定をterraformで管理したい</li> <li>もっとTDDを押し進めたい</li> <li>社内の情報機器の管理をもっと効率的にやりたい</li> <li>勤怠システムの打刻がめんどいのでSlackで打刻できるようにしたい</li> </ul> <p>などなど、やりたいことは山のようにあります。しかし、まだBackend engineer2名という体勢なので採用も頑張らなくてはなりません。特に初期設計から実装までまるっとお任せできる強いFrontend Engineerの助けを必要としています。(マジで助けてくれ!)</p> <p>その他にも多数の職種を募集しているので、興味がある人は以下の採用ページをみてもらえればと。オープンポジションもあるので、事業に興味はあるけど自分がマッチするポジションのイメージがなくても大丈夫です。</p> <p><a href="https://drsprime.com/recruit/">&#x63A1;&#x7528;&#x60C5;&#x5831; | &#x682A;&#x5F0F;&#x4F1A;&#x793E;&#x30C9;&#x30AF;&#x30BF;&#x30FC;&#x30BA;&#x30D7;&#x30E9;&#x30A4;&#x30E0;</a></p> <p>などと採用の話になってしまいましたが、とりあえず私は生きていて浅草で働いているので近くにいる人はぜひランチでも行きましょう!</p> <p><figure class="figure-image figure-image-fotolife" title="オフィス近くから見えるスカイツリー"><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/o/oinume/20201109/20201109182914.jpg" alt="f:id:oinume:20201109182914j:plain" title="f:id:oinume:20201109182914j:plain" class="hatena-fotolife" itemprop="image"></span><figcaption>オフィス近くから見えるスカイツリー</figcaption></figure></p> oinume moq - gomockを使わないMock生成 hatenablog://entry/26006613640596790 2020-10-22T09:00:00+09:00 2023-12-06T23:05:30+09:00 Goでよく使われるMockの生成ツールとしてgomockがある。個人的にはgomockが生成したコードでモックを書くのが好きではないので、代替としてmoqを使うやり方を取り上げてみようと思う。 TL;DR moqは、mockしたいinterfaceで定義されているメソッドと同じシグネチャのメソッドを生成し、そのメソッドを実装することでmockが作れる 純粋にメソッドのモック実装のコードを書けばいいだけなので、gomockのようにライブラリとしての使い方を覚える必要はない 生成されるコードもtype safeであり、gomockのようにanyは登場しない moqには、mock化したメソッドが呼び… <p>Goでよく使われるMockの生成ツールとして<a href="https://github.com/golang/mock">gomock</a>がある。個人的にはgomockが生成したコードでモックを書くのが好きではないので、代替として<a href="https://github.com/matryer/moq">moq</a>を使うやり方を取り上げてみようと思う。</p> <h2 id="TLDR">TL;DR</h2> <ul> <li>moqは、mockしたいinterfaceで定義されているメソッドと同じシグネチャのメソッドを生成し、そのメソッドを実装することでmockが作れる <ul> <li>純粋にメソッドのモック実装のコードを書けばいいだけなので、gomockのようにライブラリとしての使い方を覚える必要はない</li> <li>生成されるコードもtype safeであり、gomockのように<code>any</code>は登場しない</li> </ul> </li> <li>moqには、mock化したメソッドが呼び出された回数を取得するなど、最低限のことはできるようになっている。 <ul> <li>それ以上のことをやりたければ自分で実装する</li> </ul> </li> </ul> <h2 id="題材">題材</h2> <p>GitHubのAPIを使い指定したリポジトリのブランチを出力するServiceを考えてみる。このServiceには以下の引数owner, repoで指定されたリポジトリのブランチを<code>w</code>に出力するというメソッドを持っている。</p> <pre class="code lang-go" data-lang="go" data-unlink>PrintBranches(ctx context.Context, w <span class="synType">io.Writer</span>, owner, repo <span class="synType">string</span>) <span class="synType">error</span> </pre> <h3 id="servicego">service.go</h3> <p>このServiceの実装コードは以下のようになっていて、githubClientを使ってGitHub APIを呼び出し引数のリポジトリのブランチの一覧を取得している。</p> <script src="https://emgithub.com/embed.js?target=https%3A%2F%2Fgithub.com%2Foinume%2Fplayground-go%2Fblob%2Fmain%2Fmock%2Fservice.go&style=github&showBorder=on&showLineNumbers=on&showFileMeta=on&showCopy=on"></script> <h3 id="githubclientgo">github/client.go</h3> <p>次にServiceから参照されているgithub.Client interfaceについて説明する。これは以下のように純粋なinterfaceとして定義している。そして、ユニットテストではGitHub APIへのアクセスをモックにしたいので、このinterfaceが定義している<code>ListBranches</code>を実装するモックを<code>go:generate</code>で生成するようにしている。</p> <pre class="code lang-go" data-lang="go" data-unlink><span class="synStatement">package</span> github <span class="synComment">//go:generate moq -out=client_test.moq.go . Client</span> <span class="synComment">//go:generate mockgen -destination=client_test.gomock.go -package=github . Client</span> <span class="synStatement">import</span> <span class="synConstant">&quot;context&quot;</span> <span class="synStatement">type</span> Client <span class="synStatement">interface</span> { ListBranches(ctx context.Context, owner, repo <span class="synType">string</span>) ([]<span class="synType">string</span>, <span class="synType">error</span>) } </pre> <h2 id="gomockを使ったテストコード">gomockを使ったテストコード</h2> <p>それではgomockを使ってService.PrintBranchesのテストコードを書いてみよう。以下のようにgithub/client.go にgo generateでmockgenを呼び出すコードを追加し、<code>go generate ./mock/github</code> を実行すると、./mock/github/client_test.mock.go が生成される。</p> <pre class="code" data-lang="" data-unlink>//go:generate mockgen -destination=client_test.gomock.go -package=github . Client</pre> <p>そして、以下が実際にgomockが生成するMockを使って書いたservice.goのテストコードである(<a href="https://github.com/oinume/playground-go/blob/main/mock/service_test.go#L75-L88">service_test.go</a>)。</p> <pre class="code lang-go" data-lang="go" data-unlink>ctrl := gomock.NewController(t) <span class="synStatement">defer</span> ctrl.Finish() mockGitHubClient := github.NewMockClient(ctrl) mockGitHubClient. EXPECT(). ListBranches(context.Background(), <span class="synConstant">&quot;oinume&quot;</span>, <span class="synConstant">&quot;playground-go&quot;</span>). Return([]<span class="synType">string</span>{<span class="synConstant">&quot;main&quot;</span>, <span class="synConstant">&quot;feature/xyz&quot;</span>}, <span class="synStatement">nil</span>) s := Service{githubClient: mockGitHubClient} out := <span class="synStatement">new</span>(<span class="synType">bytes.Buffer</span>) <span class="synStatement">if</span> err := s.PrintBranches(context.Background(), out, <span class="synConstant">&quot;oinume&quot;</span>, <span class="synConstant">&quot;playground-go&quot;</span>); err != <span class="synStatement">nil</span> { t.Fatal(err) } fmt.Printf(<span class="synConstant">&quot;out = %v</span><span class="synSpecial">\n</span><span class="synConstant">&quot;</span>, out.String()) </pre> <p>注目して欲しいのは、<code>以下のEXPECT()</code>以降の部分。</p> <pre class="code lang-go" data-lang="go" data-unlink>mockGitHubClient. EXPECT(). ListBranches(context.Background(), <span class="synConstant">&quot;oinume&quot;</span>, <span class="synConstant">&quot;playground-go&quot;</span>). Return([]<span class="synType">string</span>{<span class="synConstant">&quot;main&quot;</span>, <span class="synConstant">&quot;feature/xyz&quot;</span>}, <span class="synStatement">nil</span>) </pre> <p>このEXPECT()が返すのは<code>*MockClientMockRecorder</code>型で、これに定義されているメソッドListBranchesのシグネチャは<code>ListBranches(arg0, arg1, arg2 interface{}) *gomock.Call</code>になっている。これは本来のListBranchesのシグネチャとは異なるものになり、引数の型が<code>interface{}</code>になっているため、オリジナルの引数の型である<code>string</code>以外の型も渡せてしまう。テストコードの実行には問題がないかもしれないが、本来であればClient interfaceに定義されている引数の型と同じにしておいた方が変な誤解を生まなくてすむと思う。</p> <p>また、MockClientMockRecorder.ListBranchesが返す<code>*gomock.Call</code>に対してモックが返す値を<code>Return([]string{"main", "feature/xyz"}, nil)</code>のようにセットする必要があるが、戻り値の数を間違えてもコンパイル時にはエラーにならず、実行時にエラーになることもイケてないと思う。</p> <p>また、Mockのために以下のようなboilerplateのコードを毎回書くのも個人的には好きではない。<s>俺はメソッドをmockしたいだけなのになぜこんなことをしなければならないのか</s></p> <pre class="code lang-go" data-lang="go" data-unlink>ctrl := gomock.NewController(t) <span class="synStatement">defer</span> ctrl.Finish() ... </pre> <h2 id="moqを使ったテストコード">moqを使ったテストコード</h2> <p>では次に、moqが生成したコードとそれを使った場合のコードを見てみる。</p> <ul> <li>moqが生成したコード <a href="https://github.com/oinume/playground-go/blob/main/mock/github/client_test.moq.go#L30-L67">client_test.moq.go</a></li> <li>moqを使う<a href="https://github.com/oinume/playground-go/blob/main/mock/service_test.go#L19-L30">service_test.go</a></li> </ul> <p>まずmoqが生成したコードは以下のようになっている。</p> <pre class="code lang-go" data-lang="go" data-unlink><span class="synStatement">type</span> ClientMock <span class="synStatement">struct</span> { <span class="synComment">// ListBranchesFunc mocks the ListBranches method.</span> ListBranchesFunc <span class="synType">func</span>(ctx context.Context, owner <span class="synType">string</span>, repo <span class="synType">string</span>) ([]<span class="synType">string</span>, <span class="synType">error</span>) <span class="synComment">// calls tracks calls to the methods.</span> calls <span class="synStatement">struct</span> { <span class="synComment">// ListBranches holds details about calls to the ListBranches method.</span> ListBranches []<span class="synStatement">struct</span> { <span class="synComment">// Ctx is the ctx argument value.</span> Ctx context.Context <span class="synComment">// Owner is the owner argument value.</span> Owner <span class="synType">string</span> <span class="synComment">// Repo is the repo argument value.</span> Repo <span class="synType">string</span> } } lockListBranches sync.RWMutex } <span class="synComment">// ListBranches calls ListBranchesFunc.</span> <span class="synStatement">func</span> (mock *ClientMock) ListBranches(ctx context.Context, owner <span class="synType">string</span>, repo <span class="synType">string</span>) ([]<span class="synType">string</span>, <span class="synType">error</span>) { <span class="synStatement">if</span> mock.ListBranchesFunc == <span class="synStatement">nil</span> { <span class="synStatement">panic</span>(<span class="synConstant">&quot;ClientMock.ListBranchesFunc: method is nil but Client.ListBranches was just called&quot;</span>) } callInfo := <span class="synStatement">struct</span> { Ctx context.Context Owner <span class="synType">string</span> Repo <span class="synType">string</span> }{ Ctx: ctx, Owner: owner, Repo: repo, } mock.lockListBranches.Lock() mock.calls.ListBranches = <span class="synStatement">append</span>(mock.calls.ListBranches, callInfo) mock.lockListBranches.Unlock() <span class="synStatement">return</span> mock.ListBranchesFunc(ctx, owner, repo) } </pre> <p>そして、上記のmoqが生成したClientMockを使うコードはこんな感じになる。</p> <pre class="code lang-go" data-lang="go" data-unlink>githubClient := &amp;github.ClientMock{ ListBranchesFunc: <span class="synType">func</span>(ctx context.Context, owner <span class="synType">string</span>, repo <span class="synType">string</span>) ([]<span class="synType">string</span>, <span class="synType">error</span>) { <span class="synStatement">return</span> []<span class="synType">string</span>{<span class="synConstant">&quot;main&quot;</span>, <span class="synConstant">&quot;feature/xyz&quot;</span>}, <span class="synStatement">nil</span> }, } s := Service{githubClient: githubClient} out := <span class="synStatement">new</span>(<span class="synType">bytes.Buffer</span>) <span class="synStatement">if</span> err := s.PrintBranches(context.Background(), out, <span class="synConstant">&quot;oinume&quot;</span>, <span class="synConstant">&quot;playground-go&quot;</span>); err != <span class="synStatement">nil</span> { t.Fatal(err) } fmt.Printf(<span class="synConstant">&quot;out = %v</span><span class="synSpecial">\n</span><span class="synConstant">&quot;</span>, out.String()) </pre> <p>moqの場合はモックにしたいメソッドをstructの初期化時にセットする。これはただのメソッドなので、中身の実装は好きなように書けばよくて、引数の型や戻り値の型も元のClient interfaceと全く同じである。つまり、戻り値の数を間違えてもコンパイル時に検出してくれる。個人的にはこのmoqを使ったコードの方が、boilerplateなコードもなく純粋にモックにフォーカスできるのではないかと思う。</p> <h2 id="まとめ">まとめ</h2> <p>gomockではなくmoqを使うとモックを使ったテストコードがよりわかりやすく、かつ使い方を間違えた時の実行時のエラーもなくなることが伝わったのではないかと思う。個人的にはモック化する対象が少なければ、moqを使わずにmoqが生成するような該当メソッドを置き換えるfuncを手で書くのもありだと思う。というわけで、gomock割といろんなところで使われているけどより良いツールがあるよ、という紹介でした。おしまい。</p> <p><div class="hatena-asin-detail"><a href="https://www.amazon.co.jp/dp/B07VPSXF6N?tag=kazzhomeunixo-22&amp;linkCode=ogi&amp;th=1&amp;psc=1" class="hatena-asin-detail-image-link" target="_blank" rel="noopener"><img src="https://m.media-amazon.com/images/I/51jif840ScL._SL500_.jpg" class="hatena-asin-detail-image" alt="改訂2版 みんなのGo言語" title="改訂2版 みんなのGo言語"></a><div class="hatena-asin-detail-info"><p class="hatena-asin-detail-title"><a href="https://www.amazon.co.jp/dp/B07VPSXF6N?tag=kazzhomeunixo-22&amp;linkCode=ogi&amp;th=1&amp;psc=1" target="_blank" rel="noopener">改訂2版 みんなのGo言語</a></p><ul class="hatena-asin-detail-meta"><li><span class="hatena-asin-detail-label">作者:</span><a href="https://d.hatena.ne.jp/keyword/%BE%BE%CC%DA%20%B2%ED%B9%AC" class="keyword">松木 雅幸</a>,<a href="https://d.hatena.ne.jp/keyword/mattn" class="keyword">mattn</a>,<a href="https://d.hatena.ne.jp/keyword/%C6%A3%B8%B6%20%BD%D3%B0%EC%CF%BA" class="keyword">藤原 俊一郎</a>,<a href="https://d.hatena.ne.jp/keyword/%C3%E6%C5%E7%20%C2%E7%B0%EC" class="keyword">中島 大一</a>,<a href="https://d.hatena.ne.jp/keyword/%BE%E5%C5%C4%20%C2%F3%CC%E9" class="keyword">上田 拓也</a>,<a href="https://d.hatena.ne.jp/keyword/%CB%D2%20%C2%E7%CA%E5" class="keyword">牧 大輔</a>,<a href="https://d.hatena.ne.jp/keyword/%CE%EB%CC%DA%20%B7%F2%C2%C0" class="keyword">鈴木 健太</a></li><li>技術評論社</li></ul><a href="https://www.amazon.co.jp/dp/B07VPSXF6N?tag=kazzhomeunixo-22&amp;linkCode=ogi&amp;th=1&amp;psc=1" class="asin-detail-buy" target="_blank" rel="noopener">Amazon</a></div></div></p> oinume 2020年8月までの振り返り hatenablog://entry/26006613626527082 2020-09-12T00:00:00+09:00 2020-09-12T20:02:42+09:00 ずいぶんと振り返りをサボってしまったので、頑張って7ヶ月分を書く 2月 この頃は各国のコロナの感染者数を毎日チェックしていた気がする。 国別の罹患者数。Cruise Shipが国に格上げされているwhttps://t.co/rp6wUIvCrS— oinume (@oinume) February 17, 2020 そしてコロナの影響で2月終わりから在宅勤務、英語でカッコよく言うとWFHというヤツに会社が全体的に移行した。仕事的にはGitHub Actionsを使って何かいろいろ自動化しようとしていて、そんなに切羽詰まっている感じではなかった気がする。 3月 新型コロナの影響で3月〜5月に行く… <p>ずいぶんと振り返りをサボってしまったので、頑張って7ヶ月分を書く</p> <h2>2月</h2> <p>この頃は各国のコロナの感染者数を毎日チェックしていた気がする。 <blockquote data-conversation="none" class="twitter-tweet" data-lang="en"><p lang="ja" dir="ltr">国別の罹患者数。Cruise Shipが国に格上げされているw<a href="https://t.co/rp6wUIvCrS">https://t.co/rp6wUIvCrS</a></p>&mdash; oinume (@oinume) <a href="https://twitter.com/oinume/status/1229400667837489152?ref_src=twsrc%5Etfw">February 17, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> </p> <p>そしてコロナの影響で2月終わりから在宅勤務、英語でカッコよく言うとWFHというヤツに会社が全体的に移行した。仕事的にはGitHub Actionsを使って何かいろいろ自動化しようとしていて、そんなに切羽詰まっている感じではなかった気がする。</p> <h2>3月</h2> <p>新型コロナの影響で3月〜5月に行く予定だったライブやフェスがことごとく延期もしくは中止になり、個人的な趣味を奪われてしまって、仕事で溜まったストレスが発散できなくなった。仕事はプロジェクトが炎上し始めたのと、WFHになったおかげでこの月から労働時間が著しく増えた気がする。</p> <h2>4月〜6月</h2> <p>保育園から「在宅勤務の家庭は登園を自粛して欲しい」という状態になり、子供の面倒を家で見ながら仕事をするというとてもストレスフルな状態になった。そして仕事ではプロジェクトが炎上し始め、さらにSoftware Designの原稿の締め切りがGW明けとバッティングして苦しい状況だった。</p> <p>仕事では4月にProductivity Teamなるものを立ち上げ、炎上プロジェクトに対して人を増やすと言うアンチパターンにアンチテーゼを投げかけた。自分一人でやっていたものに対してチームで取り組めるようになったので、自分はリーダー業務に徹して実作業はチームメンバーにお願いすることになるのだが、子供の面倒をみなくてはいけないという状況においてはこれはかなり良かった。</p> <p>というのは、子供が家にいるととにかく集中して作業することができない。奥さんが見てくれる時間もあるとはいえ、となりの部屋で叫び声が聞こえてくるとどうしても気になってしまう。なので、集中力が必要なタスクは深夜または早朝にやるようにして、昼間はSlackでのやりとりやMTGに終始するようにした。ただし、労働時間は平常時の1.25倍ぐらいになっているにもかかわらず、アウトプットはほぼ変わらない OR 下がっているという状態で、この期間が精神的にも肉体的にも一番つらかった。</p> <h2>7月</h2> <p>保育園の登園自粛が解除され、平日昼間も集中できる時間が増えた。また、炎上プロジェクトもなんとかリリースが終わり、自分はもう退職することが決まっていたので引き継ぎを徐々に始めた。</p> <p>リリースしたとはいえ、リリースがむしろスタートであり、特にリリース直後のオンコールはつらかった。未知の問題のアラートが多発し、過去のSlackのログなどを漁ってなんとか対応するというやり方で逃げ切った感がある。途中からOperation Reportなるものを作るようにしたおかげで、過去にどういう対応をしたのかなどがわかりやすくなり、手間はかかるがこの仕組みはやって良かったと思う。</p> <h2>8月</h2> <p>8/14が最終(物理)出社日だった。オフィスに珍しく10人ぐらい人がいたので集合写真を撮ったりした。会社のアカウント類は本来は最終出社日で無効化されるはずが、なぜか1週間ぐらい無効化されてなかったのでSlackは毎日のように見ていた。</p> <p>退職エントリーというやつを人生で初めて書いたところ、反響がすごくてこれだけでTwitterのフォロワーが200人ぐらい増えたw メルカリ/メルペイのブランド力というヤツはすごいなーとあらためて思う。</p> <p><blockquote data-conversation="none" class="twitter-tweet" data-lang="en"><p lang="ja" dir="ltr">現況です<br>メルカリ/メルペイを退職します - oinume journal<a href="https://t.co/ThfsB9DEhn">https://t.co/ThfsB9DEhn</a></p>&mdash; oinume (@oinume) <a href="https://twitter.com/oinume/status/1297700172164526083?ref_src=twsrc%5Etfw">August 24, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> </p> <h2>買ったもの</h2> <h3>Dell 4Kモニター 27インチ USB Type-C モデル</h3> <p><div class="hatena-asin-detail"><a href="https://www.amazon.co.jp/exec/obidos/ASIN/B085TWLMF9/kazzhomeunixo-22/"><img src="https://m.media-amazon.com/images/I/515ukqEPSvL._SL160_.jpg" class="hatena-asin-detail-image" alt="【Amazon.co.jp 限定】Dell 4Kモニター 27インチ U2720QM(3年間無輝点交換保証付/広視野角/HDR/IPS非光沢/フリッカーフリー/USB Type-C,DP,HDMI/高さ調整/回転)" title="【Amazon.co.jp 限定】Dell 4Kモニター 27インチ U2720QM(3年間無輝点交換保証付/広視野角/HDR/IPS非光沢/フリッカーフリー/USB Type-C,DP,HDMI/高さ調整/回転)"></a><div class="hatena-asin-detail-info"><p class="hatena-asin-detail-title"><a href="https://www.amazon.co.jp/exec/obidos/ASIN/B085TWLMF9/kazzhomeunixo-22/">【Amazon.co.jp 限定】Dell 4Kモニター 27インチ U2720QM(3年間無輝点交換保証付/広視野角/HDR/IPS非光沢/フリッカーフリー/USB Type-C,DP,HDMI/高さ調整/回転)</a></p><ul><li><span class="hatena-asin-detail-label">発売日:</span> 2020/04/04</li><li><span class="hatena-asin-detail-label">メディア:</span> Personal Computers</li></ul></div><div class="hatena-asin-detail-foot"></div></div></p> <p>この半年の中で買ったものの中ではこれが満足度が一番高い。このモニタからMacBookが充電できるし、場所もそこまで取らない。23.8インチと迷ったけど1万円しか違わなかったのでこっちを購入。</p> <h3>Galaxy Buds+</h3> <p><div class="hatena-asin-detail"><a href="https://www.amazon.co.jp/exec/obidos/ASIN/B084HSPN1X/kazzhomeunixo-22/"><img src="https://m.media-amazon.com/images/I/21IemqU5jSL._SL160_.jpg" class="hatena-asin-detail-image" alt="Galaxy Buds+ / ホワイト [Galaxy純正ワイヤレスイヤホン 国内正規品] SM-R175NZWAXJP" title="Galaxy Buds+ / ホワイト [Galaxy純正ワイヤレスイヤホン 国内正規品] SM-R175NZWAXJP"></a><div class="hatena-asin-detail-info"><p class="hatena-asin-detail-title"><a href="https://www.amazon.co.jp/exec/obidos/ASIN/B084HSPN1X/kazzhomeunixo-22/">Galaxy Buds+ / ホワイト [Galaxy純正ワイヤレスイヤホン 国内正規品] SM-R175NZWAXJP</a></p><ul><li><span class="hatena-asin-detail-label">発売日:</span> 2020/03/12</li><li><span class="hatena-asin-detail-label">メディア:</span> エレクトロニクス</li></ul></div><div class="hatena-asin-detail-foot"></div></div></p> <p>3月〜7月はとにかくオンラインの会議が多くて、AirPodsだと充電が持たなかったので購入。バッテリーは11時間確かにもつし、音もなかなか良いので気に入っている。難点はBluetoothが弱くて満員電車など人が多いところだと混戦して音が飛んでしまうことがある。この点ではAirPodsに軍配が上がるけどそれ以外では重宝している。</p> <h3>Kuman 3.5インチ HDMI 小型モニター 480*320 ラズベリーパイ向け</h3> <p><div class="hatena-asin-detail"><a href="https://www.amazon.co.jp/exec/obidos/ASIN/B07TXTSJBY/kazzhomeunixo-22/"><img src="https://m.media-amazon.com/images/I/51-cp1ql+bL._SL160_.jpg" class="hatena-asin-detail-image" alt="Kuman 3.5インチ HDMI 小型モニター 480*320 ラズベリーパイ3b タッチスクリーン ディスプレイ ビデオ/ゲーム可能 保護ケースセット raspberry pi 3 b+ 2 Model B A+ A に対応 ラズベリー パイ SC6AC" title="Kuman 3.5インチ HDMI 小型モニター 480*320 ラズベリーパイ3b タッチスクリーン ディスプレイ ビデオ/ゲーム可能 保護ケースセット raspberry pi 3 b+ 2 Model B A+ A に対応 ラズベリー パイ SC6AC"></a><div class="hatena-asin-detail-info"><p class="hatena-asin-detail-title"><a href="https://www.amazon.co.jp/exec/obidos/ASIN/B07TXTSJBY/kazzhomeunixo-22/">Kuman 3.5インチ HDMI 小型モニター 480*320 ラズベリーパイ3b タッチスクリーン ディスプレイ ビデオ/ゲーム可能 保護ケースセット raspberry pi 3 b+ 2 Model B A+ A に対応 ラズベリー パイ SC6AC</a></p><ul><li><span class="hatena-asin-detail-label">発売日:</span> 2019/08/20</li><li><span class="hatena-asin-detail-label">メディア:</span> Personal Computers</li></ul></div><div class="hatena-asin-detail-foot"></div></div></p> <p>もともとラズパイは32インチのテレビにつなげていたのだけど、そのテレビをメルカリで処分してしまったので代わりに購入。ターミナルの文字が視力検査ばりに見えないけど、安くてコンパクトだったのでまぁ良いかという気がしている。</p> <h2>個人的な取り組み</h2> <p>今まで通勤中にやっていたこと・習慣がWFHになったため全て消え去ってしまった。特に英会話はオフィスでやっていたため、家でやる時間も習慣も根付かなかった。とりあえずなんとか家でも習慣作りをしていかないと。WFHはあと1年ぐらいは続くと思うので、これは今後の課題。</p> <ul> <li>英語: ほぼやれてない。英会話をする時間がないのでiKnowで単語や文章を発音することだけ毎日やっている</li> <li>アルゴリズム: 全然やれてない</li> </ul> oinume