emahiro/b.log

日々の勉強の記録とか育児の記録とか。

Mastra に入門する

サマリ

Mastra とは

最近話題なので特に詳しくは書きませんが、自分は以下の記事を参考にしてました。

zenn.dev

自分は以下のスライドを見て初めて知りました。

speakerdeck.com

入門してみる

Scaffold する

npx create-mastra@latest すると scaffold なアプリが作成されます。
ドキュメントも丁寧ですし、作られたアプリ(お天気アプリ)の実装を見ると何をどこに書けばいいのか?ということはすぐに分かります。

面白なと思ったのは create mastra-app する過程で MCP の設定シーケンスがあることです。現時点では Cursor と Windsurf の設定のみがシーケンスにありますが、 npx コマンドを設定するだけで VSCode の設定もできます。

MCP 経由で Mastra の Example や Docs を参照でき、やりたいことをプロンプトで伝えるだけで、ドキュメントをベースに実装を提案してくれます。これはすべての言語、フレームワークで備わってほしい標準仕様になってほしいです。ドキュメントを読み漁るあの時間が限りなく短縮されますし、開発者体験として最高でした。

余談ですが、自分が普段開発に使ってる Go にも標準で入る話があるらしく、これはすごく嬉しい話。

github.com

なお自分は Scaffold したアプリを動かすのに Gemini を利用しています。Gemini だと簡単に API Key を発行できて管理も(自分的には)慣れた UI でできるのでこういうときにサクッと使えるのは GCP の良さだと感じます。

各種機能を使う

Mastra は API が生えていて、インターフェースは Swagger で提供されています。自分はまだ触っていないところなのでこの辺は触っていきたいと思います。API を提供してくれるから Scaffold なアプリの UI ではなく独自の Agent や、Slack のようなアプリとも連携できるわけなんですね。

また機能ではないですが Zod を使ったスキーマ定義をしてる部分も興味深かったのでこの辺も深堀りポイントかなと感じています。

MCP と連携する

MCP がサポートされているサービスに対して今回作成した Agent を連携してみました。
とりあえず自分はいちばん簡単な MCP との対話をできるような連携をしてみましたが、Stream という会話を継続させる中で MCP を使うインターフェースもあるみたいで、こちらも試してみる予定です。

手順は Using MCP in Your Code に書いてる通りの流れで試しました。サクッとできてすぐに動かせます。
MCP は認証込みのものを利用すると Client が Connection を確立できずに落ちるケースがあるので自分は mcp/time のような認証のいらない MCP で試しました。

使ってみての感想

ML や昨今の LLM のバックグラウンドに詳しくなくても実装できる

これは価値としては大きいと感じました。
自分も LLM、さらには Agent と呼ばれる流行りの技術に対してはあまり追っかけておらず素人なのですが、まずは Scaffold で作られるアプリケーションを眺めるだけでも、どういう実装なのか?とかどういう作りで行くべきなのか?といった流れが俯瞰できました。

ただ、作ってみて思ったのは LLM で非構造データを扱うことはできても、Agent が依存してるツール群(MCP やその他の実装、 APIなど)に渡す情報はある程度構造化されてる必要があって、入力値の検証や構造化といった前処理が大事になりそうだなという感じも見受けられました。

Typescript で統一できる

Typescript のエコシステムに乗っかっているので、プロジェクト全体の技術統一の観点からも良さそうだなと感じました。昨今技術スタックを Typescript に統一していく流れを感じていますが、そこにも乗っているように思います。

総じて体験は良い

と感じます。まだまだ触り始めで、自分で欲しいもののユースケースがわからないのですが、MCP が公開されてるサービズもあるのでいくつか組み合わせて Agent と呼ばれるものを作ってみようと思います。

またまた余談ですが、この提供されてる API をうまく組み合わせて(マッシュアップして)サービスを作る等には過去に通った道でもあり、時代や世界観が変わってもやってることはあまり変化ないなと感じました(笑)

github-mcp-server を試してみる

サマリ

GitHub MCP Server とは

github.blog

GitHub が公式に公開してくれた MCP 対応した remote server 。このエントリを書いてる時点では現在 preview

あと Go 製。リモートサーバーとしての提供を考えると docker でコンテナを立てたりせずに、バイナリポン置きで動作する Go はツール作るのには向いてる言語だなと思います。

試してみた

設定方法はほぼ https://github.com/github/github-mcp-server?tab=readme-ov-file#installation に書いてあるとおりで動きます。

docker をインストールしてるなら設定を追加するだけで動きます。 Github の PAT が必要なので事前に GitHub の設定で作成しておく必要はあります。

Go 製ということもあって、バイナリを go install github.com/github/github-mcp-server/cmd/github-mcp-server@latest でインストールできます。また VSCode では引数に stdio を設定することで PAT を設定ファイルに渡さなくてもよいと言うのは開発者ツールとしてはよく出来てるなと思います。

以下のような設定になります。

  "mcp": {
    "inputs": [
      {
        "description": "GitHub Personal Access Token",
        "id": "github_token",
        "password": true,
        "type": "promptString"
      }
    ],
    "servers": {
      "github": {
        "args": [
          "stdio"
        ],
        "command": "github-mcp-server",
        "env": {
          "GITHUB_PERSONAL_ACCESS_TOKEN": "${input:github_token}"
        }
      }
    }
  },

実際動かしてみましたが、 get_me で認証者の情報を取得できればほぼすべての tool は動きます。
ただ動かしてみたときにプロンプトが難しくて、手元のターミナルで git コマンド叩いちゃったりするなど、工夫が必要なところがありました。

使い所

git コマンドに馴染んでいたり、GitHub の操作に慣れているとそこまであって有用かと言われると難しいラインですが、一つ、Pull Request の一覧を取ってこれるので半期ごとの振り返り等で半年分の Pull Request を取ってきて要約させるとかそういう用途では有用だなと思いました。

Workflow の操作が MCP のツールとしてサポートされるとわざわざ GitHub Actions の結果見ずとも CI が落ちた理由とかわかってそういうユースケースでは便利になりそうだとという予感がありますが、そもそものインプットする情報量が多すぎる場合はまともに動かないことも今現在はあるのでまだまだ発展途上で各方面のエコシステムが揃うのが待ち遠しいです。

Cursor の @PR でセルフレビューをしている

Overview

Cursor Editor でプルリクエスト作成前に "セルフレビュー" をいれているのですが、これが思っていた以上に効果的だったので、このことについて書きます。

Cursor Editor の @PR エイリアスを利用したコードレビュー

Cursor Editor の @PR というエイリアスで master との差分のみを LLM の解析対象にいれることができます。
これを利用すると、一旦ある程度実装が終わったときに、その実装範囲を対象にした LLM によるコードレビューを入れることができます。

レビュー結果を出力して採用したければそのまま Accept をすれば変更すら自動化できます。

ルフレビューの効用

タイポの軽減

まずいちばんに感じたことはこれでした。
変数名だったりファイル名だったりといったところに潜む ありがちなタイポが劇的に減ります。

プルリクの Description の出力負荷経験

プルリクエストの Description を出力させることもできます。
「書かないといけないとわかっていてもやっぱりめんどくささを感じてしまう」ポイントだと思いますが、自分は極力そうならないようにと意識しつつも込み入った差分になるときは 「 @PR プルリクエストの Description を出力して」というプロンプトで、説明文を LLM に書いてもらっています。

レビュイーの負荷軽減

上記 2 点が改善されるだけで、そもそものプルリクエストのレビュー負荷が減ります。
また、差分においてコメントを残した方が良いポイントやテストケースの追加、より良い実装も提案してくれます。 時折、冗長すぎるコメントを残してしまうこともありますが。

ケアレスミスを軽減できる価値

自分はかなり注意力散漫なタイプで、人生でもケアレスミスに苦しんできたタイプでした。

プルリクエストもフィードバックを取り込むことに躍起になって前述したタイポなりコメント不足なり、テストケース不足といったことに悩まされて来たエンジニア人生でした。

直そうと思ってもやはり日々仕事してる中で事前の「見直し」といった作業が習慣化しなかったり、してもミスっていたりといったことが多くレビュイーに負担をかけ続けてきたなかで、このセルフレビューは個人的には画期的な発明だと感じます。自分と同じようなタイプの方にも刺さるんじゃないでしょうか。

Vibe Coding による出力は期待値のブレも一定ありますが、少なくともその出力において自分が述べている「ケアレスミス(人間的なミス)」はほぼ無いという安心感があります。

僕自身は LLM によるコーディング支援はものすごくありがたいと感じますし、自分がペインに思っていたところがバチッと現実の業務にも速攻で生きています。出力ガチャばかり注目されますが、現実の業務に生きるポイントというのはこういう(相対的に)地味なポイントなんだなと感じました。

AI Coding を始めた

Overview

最近話題の AI Coding *1を先月末から始めてみました。実際に使ってみて得られた気づきや、個人的に「これは考えを改めないとな」と感じたポイントがあったので、備忘録としてまとめておきます。

AI Coding については、すでにSNSを中心に多くの知見や導入事例が出回っているため、本記事では詳しくは触れません。

個人的には、以下のZennエントリとオライリーのブログ記事が非常に刺さりました。

zenn.dev
www.oreilly.com

AI Coding との付き合い

最初に AI Coding を体験したのは GitHub Copilot でした。本格的に使い始めたのは2年前で、Copilot がローンチされてから半年以上経っていた頃でした。

当時、すでに LLM を内蔵したエディターも存在していたようですが、巨人の肩に乗る感覚で Copilot をメインに利用していました。また、その頃は Devin のような自律型AI Coding Agentもまだ登場しておらず、「ソフトウェアエンジニアはすぐに廃業にはならないだろう」「コードを書く手段として LLM を使うのは一つの選択肢だけど、これが主流になるにはまだ時間がかかるのでは?」と感じていました。

今思えば完全に見誤っていました。僕にとって当時の LLM は「めちゃくちゃプログラミングに詳しいメンター」という位置づけで、やりたいことや実装の方針を伝えたうえで、各種ドキュメントや OSS のコードをベースに提案をもらったり、コードリーディングの伴走をしてもらうような使い方をしていました。

まさに「Copilot(副操縦士)」という役割で、VSCodeとの親和性も高く、自分にとっては生産性を十分に高めてくれる存在でした。

Cursor との出会い

正直、Copilot で満足していたこともあり、「新しいエディターの機能もいずれ Copilot に取り込まれるだろうし、急いで使わなくても良いかな」と思っていました。

そんな中、現職で Cursor のトライアルが始まり参加してみたところ、その感覚が完全に覆されました。

使用感については以下に投稿しています。

実際に触ってみて感じたのは、Cursor は明確に「AI ファースト」である、ということです。

エディター自体は VSCode のフォークなので、若干 UI や機能に差はありますが、GitHub 連携以外の設定はほぼ引き継げるため、VSCode ユーザーであれば違和感なく使えると思います。

VSCode と Copilot だけを使っている人は、ぜひ一度 Cursor や、もう一つの対抗馬である Windsurf のような AI ファーストエディターに触れてみてほしいです。

開発パラダイムの変化と生産性への影響

Copilot のない時代から Copilot を経て、Cursor のような AI First Editor を体験してきた中で、「実際どれくらい業務の生産性が上がったのか?」という点ですが、個人的には Copilot を導入したときの方がインパクトは大きかったと感じています。

Cursor を使った際の生産性向上は、劇的というよりは緩やかな印象です。
これは「0→1」と「1→10」の違いや、LLM に慣れてきたこともあると思いますが、そもそも開発スタイル自体を AI 時代に合わせてシフトしてきたことも要因です。

従来は「書きながら考える」スタイルだったのに対し、現在は DesignDoc や詳細設計を先にテキストで書き、それを Cursor に渡して実装を生成させる、という流れに変わりました。

このやり方では、人間は「書く人」ではなく「出力を調整する人」になります。そして、この手法では事前に関数名やシグネチャレベルの詳細まで設計に落とし込む必要があるため、その準備に時間がかかることで「生産性が劇的に上がった」という実感を得にくい、という仮説を持っています。

実際、現職の他のエンジニアからも似たような感想を聞いています。

ただ、このパラダイム変更の利点は「変更点が確実にドキュメントとして残る」点です。
Pull Request 作成時に丁寧な説明を書く文化がない環境では、PR の意図が不明確なまま進んでしまうことも多いと思います。AI に渡す前提として、差分の全体像を言語化することを強制されるこの手法は、そうしたペインを軽減するソリューションになり得ると感じています。

とはいえ、開発スタイルを大きく変えることになるため、万人にとってフィットするわけではないとも感じています。

プレーンテキストの価値

AI Coding を始めて最も実感しているのは、「プレーンテキストで残しておくことの価値」です。
この点については、t-wada さんのポストが的確に表現してくれていました。

ソフトウェアはテキストで記述された指示や設定に基づいて動作するものであり、現状がテキストで残っていることは LLM 時代において非常に有利な状態だと感じます。逆に、テキスト化されていない情報は、LLM にとってアクセス不能です。IaC 化されていないインフラは、その典型例かもしれません。

かつて「コードがドキュメント」という言説には否定的でしたが、今では「最新の状態を反映したコード」が最良のドキュメントである場面もあると認識が変わりました。

LLM の存在により、コードをドキュメントとして読むハードルが下がり、実装と仕様の接続がしやすくなったと感じています。

MCP に感じた未来

AI Coding において重要なのは、「AI に適切なコンテキストを与えること」だと実感しています。
チームのコーディング規約やガイドライン、開発フローや外部ドキュメントなど、AI に求める出力の質を左右する情報はすべて前提として渡さないといけません。

インターネットを眺めていると全てのドキュメントを markdown に変換して cursor のルールとして定義する、という方針を見かけることもあります。
これはソリューションの1つとしてはあり得ると思いますし、実際そうしてる組織もあると思います。
AI Coding の第一歩として全てのドキュメントを1箇所に集めるという方向性は同意しますが、自分はこの方向性がベターなのかと言われると、結局ルールが肥大化してメンテしきれなる未来が想像できるので少し疑問を持っています。

このルール管理の課題を踏まえたとき、Anthropic が提唱している Model Context ProtocolMCP)は非常に興味深いアプローチだと感じました。

www.anthropic.com
zenn.dev

MCP では、AI に渡すコンテキストを用途ごとに分離・管理しやすくすることで、ルールが肥大化せず、柔軟かつシンプルに保てるのではないかという仮説を持っています。
実際に社内でいくつかの MCP デモを見たとき、コンテキストを渡す手段、及び開発補助ツールとしての活用事例に可能性を感じました。
(※あくまで個人の感想です)

まとめ

AI Coding を通じて、自分の考えを整理してみました。
現職にはこうした取り組みに先進的なメンバーが多く、自分もそのおかげで新しい開発パラダイムをスムーズに学べており、本当にありがたい環境だと思っています。

AI Coding は、開発の常識を根底から変えていく技術だと思います。
個人的には、Go や Google App Engine に出会ったとき以来の衝撃でした。最初は「乗るしかないこのビッグウェーブに!」くらいのノリでしたが、今では「やっておかないとヤバい」くらいの危機感すらあります。

こうした変化をリアルタイムで体験できるのは、自分のキャリアにとって幸運なことだと感じています。

ただし、しんどさもあるのが現実です。
「ソフトウェアエンジニア廃業論」については懐疑的ですが、仕事の中心が「書くこと」から「指示すること」にシフトした今、ある意味では仕事がしんどくなったとも言えます。

ベンダーマネジメントで行っていた「指示出し」を、今はコード実装にも求められている──そんな感覚があります。
これは実際にネット上でも同様の意見を多く見かけます。


それでも、AI Coding は不可逆なパラダイムシフトです。
だからこそ、自分も「やっていかないとな」と思っています。

*1:このブログを書いている時点では「Vibe(雰囲気)Coding」と呼ぶのが正確らしいです

現職で6年目を迎えた

今日で現職6年目を迎えた。 (アルバイト期間を含めているので厳密には今月中旬が正式な6年目ではある)

5年目を迎えたときのブログを書いたが、あっという間に1年経ってしまった。

ema-hiro.hatenablog.com

現在進行系で最長在籍記録を更新中である。毎年思ってるがよく辞めずに続いているな〜と思う。ちなみに去年のブログを読むと自分は「次」を考えるつもりだったらしいが、結局1ミリもそんなこと考えていなかった。

在籍し続けている理由はいくつかあると思うが、現職は働きやすさと業務の難易度のバランスが非常に良く、わかりやすく言えば「飽きない」環境を提供し続けてもらっていると思う。
居続ける理由は人それぞれだと思うけど、自分はトータルで見て「飽きない」ということが在籍し続ける動機として大きいことは間違いなさそうである。(もちろん頑張ろうと思える待遇も出してもらってる)

自分のキャリア、いつ、どこで、どういうことが起こるかわからないので、離れるときがいつ来ても良いように準備はしてるが、今のところその気配はない。
それどころかちゃんと評価されて今年は Promotion もして頂けた。責任は(多少)増えたが、一方で外資で Promotion を経験する、というのは自分の中で1つの大きな目標としていたところでもあるので、達成できてホッとしている。

自分として掲げていた目標をクリアはしたが、まだしばらくは面白い仕事が続きそうのと、今年も大きな変化のある年なのでもうしばらくは在籍してると思う。

節目の記録なのにエモいことは全くと言っていいほど書けなかった。まぁまた1年ゆるゆると頑張っていく。

Go 1.24 から導入された json の IsZero に触れてみる

Overview

Go 1.24 から encoding/json に入った IsZero のインターフェースについて実際に触ってみて挙動を調べてみました。

IsZero とは?

When marshaling, a struct field with the new omitzero option in the struct field tag will be omitted if its value is zero. If the field type has an IsZero() bool method, that will be used to determine whether the value is zero. Otherwise, the value is zero if it is the zero value for its type. The omitzero field tag is clearer and less error-prone than omitempty when the intent is to omit zero values. In particular, unlike omitempty, omitzero omits zero-valued time.Time values, which is a common source of friction. If both omitempty and omitzero are specified, the field will be omitted if the value is either empty or zero (or both).

tip.golang.org

リリースノートを参考にすると今まで time.Time に入っていた IsZero メソッドを生やしたカスタムフィールドに対して json タグで omitzero を付与することでいわゆる「ゼロ値」の振る舞いを json.Marshal, Unmarshal の中で制御することができるようになるようです。

サンプル実装

まず標準の time.Time を渡してみます。

// You can edit this code!
// Click here and start typing.
package main

import (
    "encoding/json"
    "fmt"
    "time"
)

type X struct {
    A time.Time `json:"a,omitzero"`
}

func main() {
    x := X{}
    b, _ := json.Marshal(x)
    fmt.Printf("%v", string(b))
}

ref: https://go.dev/play/p/nkBKMnKym7i

この出力以下のようになります。

{}

続いてカスタム型に IsZero メソッドを生やしてみます。

// You can edit this code!
// Click here and start typing.
package main

import (
    "encoding/json"
    "fmt"
    "time"
)

type X struct {
    A time.Time `json:"a,omitzero"`
    B Y         `json:"b,omitzero"`
}

type Y struct {
    UserID int
}

func (y Y) IsZero() bool {
    fmt.Println("aaaa")
    return true
}

func main() {
    x := X{}
    b, _ := json.Marshal(x)
    fmt.Printf("%v", string(b))
}

ref: https://go.dev/play/p/sDVeZEzEZlm

この出力は以下のようになります。

aaaa
{}

これは IsZero メソッドの返り値が true (= つまりどんな値をとっても omitzero で消える) なので出力結果は空になります。

ただ少しコードを変更してみます。

// You can edit this code!
// Click here and start typing.
package main

import (
    "encoding/json"
    "fmt"
    "time"
)

type X struct {
    A time.Time `json:"a,omitzero"`
    B Y         `json:"b,omitzero"`
}

type Y struct {
    UserID int
}

func (y Y) IsZero() bool {
    return y.UserID == 0
}

func main() {
    x := X{
        B: Y{
            UserID: 1,
        },
    }
    b, _ := json.Marshal(x)
    fmt.Printf("%v", string(b))
}

ref: https://go.dev/play/p/roo_uNbSGKI

この出力は以下のようになります。

{"b":{"UserID":1}}

Y の中にある UserID が 0 のときは omitzero で消えるようにしてるので、0 以外を指定したときには omitzero の対象になりません。

振る舞いとユースケース

この omitzero (IsZero メソッド)の導入により、今まで json の key から消すには json で Unmarshal するフィールドの定義を参照型にする必要があり、これが意図しない nilpo の温床になっていたり、クライアントアプリから見るとあるはずの key がない、と言ってフロントのエンバグに繋がることもありましたが、今回 の omitzero の導入で「どういうときにゼロとするか?」という仕様を決めることができるようになると思いました。Go の実装としても不要な参照型を排除することで nilpo を踏む可能性を少なくできますし、より堅牢なプログラムを書けるようになりそうです。

また別のユースケースとしては、A/B テストや本番と開発環境(テスト環境等)で特定のフィールドを入れる、入れないを IsZero の中で制御し、クライアントの振る舞いを変更するといったことも実装の深いところではなく、JSON を作るより上層(Converter や View といったレイヤー)で行うことができて不要な依存を深いところに埋め込まなくて済む、といったことも考えられそうです。

まとめ

最初は今更 encoding/json におけるゼロ値の取り扱いを再定義することのメリットは何なのか、ぱっとわかりませんでしたが、実際に実装してみると少しわかりました。使っていきたいと思います。

actions/setup-go で Go のバージョンを指定する

もともと GitHub Actions で Go のバージョンを指定するときは matrix 構文を使って go のバージョンを渡していた*1が、今は actions/setup-go で指定できるようになっていたことを知らなかった。

zenn.dev

Go は下位バージョンとの互換性を保っていてくれるのもあって、大体新しい Go のバージョンが出ると最新に上げてしまうし、それで動いてしまうことも多いので、常に go-version で最新を指定しておけば良さそう。

追記: 自分でもわすていたのですが、過去に全く同じ内容を備忘録として残していました(笑)

ema-hiro.hatenablog.com

*1:Goは下位2version をサポート対象としてるので 2 version 分の Go のバージョンチェックが出来るのは便利は便利

『マネジメントは嫌いですけど』を読んだ

Overview

『マネジメントは嫌いですけど』を読んだのでその備忘録です。
自分はピープルマネジメントに関わる業務をしてるわけではないですが、プロジェクトマネジメントには主たる業務として担当しているので、その勉強も兼ねて読んだのですが、ピープルマネジメントの話よりも後半にあった お金にまつわる組織力 の話が非常に興味深く勉強になったのでその観点について自分の考えてることも含めてつらつらとまとめてみます。

キャリアパスから組織を考える

エンジニアの貢献を評価してもらうのは難しい。

前提として一般的な MBO(Management By Objects) を評価に採用してる企業を念頭に書かれていますが、MBO の評価には以下のような傾向があります。

  • MBO による評価においては、エンジニアの貢献(技術力を上げる、少しずつ改善する、悪い出来事が起こるのを未然に防ぐ)というものが評価されづらい。
  • MBO による評価においてはエンジニアの貢献(何も起きないということにどれだけの労力を割いているか)に価値があるかを評価する術を持たない。

これは実際に仕事をしていても評価のタイミングでたしかにこの傾向はあると感じることは多いです。

報酬は経済が決めている

評価に報いるのは「報酬」です。
ただ、この報酬を決める原資に目を向けると、営利企業に所属してる以上は企業が外部から得ている収益(売上)がそれにあたります。

優秀な社員への報酬が安すぎると見られる場面でも、所属してる企業の得ている収益という原資を超えて報酬を出すことはできません。
これはある程度社会人を経験すると、給料の多寡というものは、その人のスキルではなくその企業の売上(更に行ってしまえば仕事をしている市場)によって決まる、というのは特に不思議なことではないです。
このあたりはインターネット上で良く「椅子取りゲーム」と揶揄されたりするので最近では一般的な考え方として浸透してると感じてますが、本人の努力とは無縁に(ゼロではないにしろ)報酬額が決まるというのはいかにも資本主義経*1だなと感じます。

また別の文脈で、この原資から報酬をどう差配するのかはファイナンスの責任範囲になる、というのも組織の構造があります。
この構造を元にすると、技術者の技術的な貢献に対する配分の枠をある程度確保するには、ファイナンスに技術を分かる人(言ってしまえばエンジニア出身者)をアサインしていると、技術的な貢献に対しての報酬はレバレッジの効いたものもなる可能性があります。
ただ、自分もそうですが、そういった企業に所属したことはありませんし、資本主義というゲームの中では例え技術的な背景のある人がなったとしても難しいのではないかなと思います。そういった技術者の貢献が正当化される世界線というものは自分もあまりイメージができません。あるのかもしれませんが、少なくとも例外的な存在ないなるんじゃないかと思います。

組織の中のお金の理屈

プロジェクトにまつわる「お金」には2種類あり、CAPEX(Capital Expenditure) と OPEX (Operating Expenditure) があり、自分が勉強になったのは OPEX の取り扱いでした。

都度発注ではなく年間の概算で予算を立てる

これは身近なインフラチームが毎年やっているので馴染みがありましたが、

  • 年間を通した予算調達にすることでのオペレーションコストの削減(都度発注にすると毎回調達のオペレーションコストがかかる)
  • 発注をまとめることで購買価格を下げる作用がある。
  • 予算管理をしてる部門は、年間予算で動くので進め方ともアラインしている。
  • 予算は資産と利益を見通すために必要で、都度発注のような不確実なものは避けられる。

というメリットがあります。

こういった予算を決めるためのメリットと言ったものはその立場にならないと触れられない観点であり、会社の名kでどうしてそう動いているのか?ということがわかるとお金にまつわる解像度が一段上がりますし、自分が交渉するときに知識として持っておいて損はない内容だと思います。

削減する方向ではなく、伸びていく方向に目を向ける

個人的にはこれはコミュニケーションテクニックの類だと思いましたが、「XXX な金額を YYY 円(%) 削減 」という伝え方でなく「同じ金額で ZZZ 倍の性能を手に入れる」という伝え方で評価するというものです。
削った予算は返ってこない or もっと削れるというリクエストに繋がるので、同じ予算を使って効用がどれくらいあったか?という伝え方にするというのは目からウロコでした。実際の仕事でも使っていきたいポイントです。
この伝え方を知っていると「100万円かかるものが50万円になりました」ではなく「100万円で200万円分の効果がありました」となります。確かに自分が予算の差配を握っている立場であれば、どちらも同じくらい勝ちがあると思いますし、何より現場からすると予算が削られないメリットを享受することができて一石二鳥感があるなと思いました。

維持する予算は新しく何かを作る予算より確保しづらい

一度成果が確定したものを維持するコストというのは、予算策定部門から毎年削減してほしいと要求が来ます。これも身近でこのコミュニケーションを見てるのでわかりますし、業務で予算の削減系のタスクをしたことがあるのでよくわかります。
サービスを維持するのに必要な予算なのに、そんな簡単に削られる分けないだろうと思いつつ、そもそも維持コストはそのまんま固定費として計上される事が多く、固定費はまんま削減対象となるというのはわかりやすい帰結だなとは思いました。固定費を削れば会社の売上に対する損益分岐点を手前に持ってくることが出来るので、会社としてその意思決定になるのはしょうがないことでもあるというのは理解できます。

ただ、削減のゴールは究極的には「ゼロにする」ことで、それは廃止(サービス終了)と同義です。
サービス終了する気はないが、コストは限界までゼロにする、というのは結構身勝手なファイナンスの要求だなと思ったりもしますし、これまた現場レベルで考えると当然の考えかなとも思います。

ただこれまたテクニックだなと思ったのですが、この削減を回避するために、「新しく作り直すことで付加価値を見せて、維持する予算も取る」ハックが存在するというのは納得感がありました。自分自身そうしたやりとりは何度か目にしたことがありますし、知らず知らずのうちにやっていた記憶もありました。

承認の負荷の意味とリジェクトの基準

Pull Request などがわかりやすいですが、技術職は日常的に「承認を得る」活動をしています。
そしてこの承認にはいくつかの基準があります。

同じようにマネジメントにも「承認」の活動がありますが、現場仕事とはまた違った「重さ」が存在します。それは当然でマネジメントサイドの承認には関連するステークホルダーが多いので、その承認をされるかされないかで社内外問わず影響を受ける人の数が違うということが挙げられます。

ただ、承認されるものは否認されるものもあり、本書にもありますが、承認ばかりされるよりもきちんと否認されることが文化として整理してることのほうが価値があるということは自分としても確かに、、、と感じるところはあります。
否認にはそもそも承認以上のコストが掛かるものですし、疎まれもします。
ただし、否認するならするでその基準が必要だと最近考えています。個人に属人化したブラックボックスな基準で否認されても、された側はどうして否認されたのかわかりませんし、それがそのままネガティブな感情に結びつくことは想像に難くありません。

組織として承認と否認の基準を明確にし公開しておくと、リクエストしたりプロポーズする側はその基準を学び、その基準を満たすための準備をします。結果として意思決定のレベルは平準化されるので、この基準を作るという行為は、組織全体のレベルアップにもつながると思いました。

予算の仕組みを知る

正直のこの部分が一番本書で勉強になったポイントかも知れません。

予算は会社を守るためのツール

予算には以下の役割があります。

  • なんの根拠もなく会社の資産を使い込みすぎないガードレール
  • 他の部門(財務や経理)とのコミュニケーションツール

特に財務や経理と行った部門は「会社として生き残るためのお金を確保する」のが仕事なのでこの部門とやり取りするのに予算に関する知識が必要になります。

時間を買えるならお金はかけたほうがいい

楽をするためにお金をかけることは割けるべきですが、お金をかける効用が「時間を節約すること」であるならそれはお金をかけたほうがいい場面が多いです。
なぜなら時間は常に有限なので、時間を短縮できるというのは長い目で見たときに一番いいお金の使い方になるからです。
目先の費用削減等に目を向けがちですが、削減した結果将来的に時間というコストを負うかどうか?は現場で持っておいて損はない観点だなと思います。

資金繰りの話

現場仕事で資金繰りのことを考える機会というのはほとんど存在しないと思いますが、資金繰りは非常に大事です。
※ 資金繰り = 期日までに支払いの原資を調達できること。

資金繰りにおいては「とにかく現金を確保すること」「黒字の信用がなければお金は借りられない」という原則があります。

前者は家計の運営等やっているとわかるポイントかなと思いますし、このために「先払い」をやめて「後払い」を選択する等お金周りのテクニックが存在します(ローンはこう考えると非常に良い仕組みだなと思います。)
後者は自分で事業を立ち上げる、もしくは経営陣に入る、ということをしない限りはほとんど意識することはないと思います。僕もそうです。

一方で巷には黒字を割ける事による節税ハック等が流布していたり、そもそも成長を価値としておくスタートアップ等は赤字であることを許容して成長に投資し続けることが正義だったりもします。
どちらの世界を取るかは企業、ひいては経営者の色の世界なのもしれませんが、経営者でない自分としては黒字にする、及び黒字の幅が大きくと株式市場から評価されない、ということをとある経由でまざまざと痛感しており、つい最近アンラーニングした観点でもありました。
黒字にしないと評価の土俵にも登れない、という世界があり、それが非常に身近なところにあると実感したときに、会社の経営状態(特に B/S や P/L) を見て解釈できるスキルというは必要になるんだなと思いました。
同時に、このルールを知っていると今まで理不尽だなと思っていた制約が別の観点(資本主義の世界等々)では合理的な価値基準なんだなと再認識するきっかけもくれました。

その意味ではこの章におけるお金にまつわる組織力学の走りに触れ、街道度を上げることができたことは自分にとって非常に勝ちがありました。

まとめ

冒頭にも記載しましたが、マネジメントの本かと思いきや組織とお金の話まで及んでおり、かつ自分が最近仕事のやりづらかを感じていたポイントの多くでその背景や前提を言語化してくれていた書籍なので本当に勉強になりましたし、自分の中の解像度が少し上がったような気がします。仕事にはお金がつきものなのでお金に関する知識というのはまだまだつけていきたいと思います。

『マネジメントは嫌いですけど』を読んだ。マネジメントの本かと思ってたけど後半のお金にまつわる組織力学の話がとても勉強になった。 予算管理、資産管理、この辺が多少なり業務に関わってきてたので、実感を持って読めたし、今まで提案がリジェクトされた背景も垣間見ることができた。 組織に関するお金の解像度は少し上がったし、そもそもお金の話をするときは普段現場で使ってるものとは異なるプロトコルで会話する必要がある。『Tidy First?』でもお金(これはオプション取引の話だったけど)の話があったので、今まで考えてこなかった観点を少しは固められたかな?と思うなど。

emahiro (@emahiro.bsky.social) 2025-01-19T02:58:25.575Z
bsky.app

*1:ちょうど マイケルサンデル教授の記事 がバズっていてまさしくこの話だなと思ったりもしました。トピックとして出ている職業は技術職のそれとは違いますが

『Read Write Own』を読んだ

Overview

年末年始に 『Read Write Own』 を読んだのでその読書録です。

この書籍は a16z のクリプト関連のトップなのでポジショントークは含まれていますが、LLM が世間を賑わせている昨今にあってブロックチェーンの話はあまり追っていなかったので、ブロックチェーンが今必要とされている話がインターネットの歴史をなぞりながら解説されていて面白さはありました。投資家目線すぎるのでピンとこないというか未来過ぎる話もあったりして現実的じゃないなと思うところもあるにはありました。

私自身はこの書籍に書いてあることはほぼ夢物語みたいなものだと認識してますし、ブロックチェーン自体はまだまだ現実世界への適用は先のように思えつつ、「おもちゃみたいなもの」と日陰に追いやられていたものが急に世界を席巻するイノベージョンとなったりするのでそういう意味では概要や今ある世界のどこに課題があるのか?というのは知っていて損はないのかなと思って読んで良かった本ではありました。

読書 Note

ブロックチェーンブロックチェーンネットワーク

大まかな理屈は頭に入っていつつも、ここが自分が今まで誤解していたポイントではあるんが、ブロックチェーンというのは状態遷移の記録(コンピューティングの記録)というだけなんですね。よく「分散台帳」というような表現をされるので、なんとなく DB 的なイメージをしていたんですが、確かに言われてみると、あるブロックから次のブロックに遷移する(その際に過去の記録はすべて記録されており検証されたものである)仕組みというのは、自分がスタートから間違っていた考えでした。
記録自体は世界のどこかのコンピューティングリソース上に残っており、どこからでも復元は可能であること(そういう意味は分散台帳という表現は正しい)、そして不正な状態遷移を記録しようとしても、遷移するには最終的にそのブロックチェーンに参加してるノードからの投票で決まる、というところから不正が起こりづらい(できないわけでなく過去のブロックすべての計算結果を改ざんして正しいチェーンにして見せれば可能ではあるが、コストが膨大で割に合わない)という仕組みというのは、改めて書かれてみると結構よくできた仕組みだなと思います。

同じネットワークに参加してるノードの投票で遷移の合意を取るというのは、DB 界隈の分散合意アルゴリズム (Raft とか)の話と通じるところがありました。

ただ、分散金融やブロックチェーンゲームにおいてブロックチェーンネットワーク上にアプリケーションというものを作れるというのは知ってるようでよくわかっていなかったポイントでした。ただ正直なところは自分がそれを作ったことがないので例えばイーサリアム上で動くアプリケーションを書く、というのはいまいちどういうものなのかわかっていません。
イメージとしては HTTP のプロトコルを前提にWebアプリケーションを書くみたいな感じなんでしょうか。OSI の参照モデルのアプリケーション層のひとつ下くらいのイメージをしました。こういうのは身近に詳しい方がいれば説明を聞いてみたいポイントです。

インターネット上における所有権

本書でほとんど書かれていたのは既存のインターネットの世界は大企業のネットワークの上に成り立っていて、その大企業のネットワークでは Idenitity は「借りてる」だけで所有していない、ということでした。 (ただ、ブロックチェーンになると「所有」の概念が現実になるということが書いてあってここはあまり良くわかりませんでした)

GAFA に代表される企業が運営してるソーシャルネットワークを利用してインターネットの世界を行き来しているときに使っている「アカウント」というのはこの企業に属したネットワーク上でのみ有効なアカウントであり、そのネットワーク上でしか使えず、かつその生殺与奪の権利を企業に渡してることに等しい(=真の意味で所有していない)ということらしいです。

あまり現在のインターネットにおいて「所有権」を意識したことはなかったので、こういう観点はなるほどな、と思わされました。

そしてこの企業ネットワーク同士には相互運用性がないので、X のアカウントで培った試算をインスタグラムでは使えない(1からアカウントを作り直しになる)という当たり前の事象に対してブロックチェーン同士の相互運用でアカウントの情報を引き継げるみたいなのは確かにそうなると嬉しいポイントかなと思いました。
企業ネットワーク上におけるアカウント(Identity) というのは企業ネットワークそのものの資産に計上されており、ユーザー本人にはその所有権がないので、企業ネットワークを移動する(別のサービスを利用する)ときには別のアカウントになる、というのは当たり前のようでインターネット上における所有を担保されていない、ということになるんですね。

ブロックチェーンで実現できるというのは正直わかりませんが、インターネット上における Identity のポータビリティというのはユーザーからすると個人としてはかなりほしい部類の仕組みだと思いました。SNS を使ってる範囲では特に気にもしませんが例えばゲームなどでは、あるゲームにおける自分の疑似人格(アバター)をカスタマイズした衣装なり武器なりを別のゲームでも利用したいとニーズは想像に難くありません。
同じレベル1だとしてもアバターを1から作り直すことない世界のゲームというのはちょっと体験してみたいなと思いした。(例えるならフォートナイトでこさえた衣装をPUBGでも使えるみたいは感じでしょうか)

企業ネットワークのテイクレート

企業ネットワークのテイクレート(手数料)は99%である、というのはあまりピンときませんでしたが、X を使っていようがインスタグラムを使っていようがその企業ネットワークに属していて様々な投稿という形でネットワークを盛り上げている(ネットワークの価値向上にコミットメントしてる)にも関わらず、企業からそのコミットメントに対してのインセンティブはほぼない、という観点を持ったことはありませんでした。当然のことだと思っていたので。

ただ、ブロックチェーンを基盤にする世界ではこのテイクレートは極めて低く抑えられると書いてあります。

企業ネットワークの不当なテイクレートの高さが保証されるのはその企業ネットワークを維持運営するためのコストはすべて企業が負担してるからであり、テイクレートが高いのはそのインセンティブとして妥当、という考えによるそうです。まぁこれも運営側にいる身としては当然と言えば当然かなと思うんですが、ブロックチェーンネットワーク上においてはそのネットワークへのコミットメント(投稿なのか、何かのアプリケーションを作ることなのか、色々ある)に応じてインセンティブが支払わる(トークンやストレージなどコミットメントに応じてネットワーク上での権利を付与するイメージ)ことで、維持運営コストを支払う価値がユーザー側にあるといいます。
またこうしたコミットメントによってネットワークの価値が高まればネットワーク効果によって新しいユーザーがまたこのネットワークに入ってくることで好循環が働くからとのことでした。まぁ確かにそういうこともあるか、、、とは思います。

ネットワーク維持へのコミットメントとインセンティブ

これを本書では企業ネットワークはテーマパークそのもの提供で、ブロックチェーンネットワークは都市開発の基盤提供と表現していました。
まぁまぁピンとくる表現で、確かにテーマパークにおいては提供されるものを利用するのみですが、都市開発と考えるとその都市に人が増えて盛り上がってほしいので自ら率先してコミットするようになる可能性はたしかにありますし、都市が盛り上がって価値が上がることはそのまま都市に住んでいる自分にいい影響として跳ね返ってきます。

日々使ってるプロダクトから搾取されているんですよ?という筆者の観点は確かに納得感はありつつ、とはいえブロックチェーンの世界ならこうだ!みたいなのはあまりに論理が飛躍し過ぎでは?と思うには思いました。

『Tidy First?』を読んで考えたコードの"整頓"にかかる正味現在価値

Overview

年末に話題になっていた『Tidy First?』を読み、その中の第3部で「整頓」をいつ行うべきか?という話が金融の世界の話と絡めて説明されており、この言語化が今の自分には非常に示唆に富む内容だったので、読書ノートとして記録します。

Bluesky の方に自分の感想を簡単に書きましたが、具体的にどういう部分に響いたのかをこのエントリで詳しく説明します。

話題の『Tidy First?』、読んでみて序盤はまぁそうだよね〜みたいな内容の羅列で薄い本だしサクッと読めるものかと思っていたら、後半の金融絡めたオプション取引、正味現在価値をメタファーとして捉える話が面白かった。 「今日の1ドルは明日の1ドルより価値が高い」ということをソフトウェア開発に置き換えて、整頓(変更までのリファクタリング等の事前改善作業)を先にすることに対する経済的合理性の有無を考えてみるのは今までうまく言語化できてなかったところだったので個人的に刺さったポイント。

emahiro (@emahiro.bsky.social) 2024-12-29T02:40:37.400Z
bsky.app

※ 私は金融のプロではなく、あくまで書籍に書いてあったことを自分なりに解釈しているに過ぎません。そのため厳密な計算や投資判断にかかるような内容は含んでいませんし、仮に間違っていてもご容赦ください。

正味現在価値(Net Present Value: NPV)とは?

そもそも「正味現在価値(NPV)」とは何でしょうか?これはファイナンス用語であり、金融の世界の話で、私は専門外なので LLM(今回は Gemini)に聞いてみました。

--- 以下 LLM の会話

Q: 正味現在価値(NPV)とは何かを教えて下さい。

A:

正味現在価値とは

正味現在価値(NPV:Net Present Value)とは、投資の意思決定を行う際に用いられる指標の一つで、将来得られるキャッシュフロー(お金の流れ)を現在の価値に割り引いて計算し、そこから初期投資額を差し引いたものです。簡単に言うと、「この投資は今いくらの価値があるのか?」を数値で表したものです。

なぜ正味現在価値が重要なのか?

お金には時間価値という概念があります。例えば、1年後の100万円は、今の100万円と比べて価値が低いと考えられます。なぜなら、今の100万円は投資などに活用することで、1年後にはそれ以上の価値を生み出す可能性があるからです。正味現在価値は、この時間価値を考慮して、将来のお金の価値を現在の価値に換算することで、投資の収益性を正確に評価することを可能にします。

(計算方法等の話なので中略)

まとめ

正味現在価値は、投資の意思決定において非常に重要な指標です。時間価値を考慮して将来のキャッシュフローを現在価値に換算することで、投資の収益性を正確に評価することができます。ただし、割引率の設定や将来のキャッシュフローの予測など、注意すべき点もあります。

--- ここまで LLM の回答

NPV と "整頓(Tidy)" の関係

本書の中ではこの「整頓(Tidy)」を投資判断として扱っており、その記述は第3部「経済性:時間価値とオプショナリティ」以降にあります。
※ ここでいうオプションとは、「バイナリーオプション」や「ストックオプション」で利用される金融用語としての「オプション」を指します。

正味現在価値を表現することわざとして「今日の1ドルは明日の1ドルよりも価値が高い」というものがあります。これは、今日1ドルを得ることができれば(お金を稼ぐことができれば)、得た利益を元に活動して、1ドル以上の価値を創出できる可能性があるという点で、時間的な価値を付与すると明日得る1ドルよりも価値が高い、という意味です。

これを「整頓(Tidy)」の文脈で考えてみると、整頓するコストを掛けずにすぐに結果を得るためにプロダクトの振る舞いを変更する行為は、先に整頓してから振る舞いを変更するよりも、時間の価値を考えると経済的な合理性がある可能性がある と解釈できます。

もちろん、NPV の値は初期コスト(Tidy にかかるコスト)、割引率、金利、その他のリスクといった変数があるので、必ずしも Tidy せずにすぐに変更を受け入れる方が経済的合理性があるとは限りません。ただし、Tidy を先にするということは、お金を得るまでのリードタイムを伸ばし、後からお金を得ようとする行為となります。

私としては、Tidy 作業をするという意思決定をする際には、NPV の考え方、もしくはディスカウントキャッシュフローの考え方を持っておくと、単なるお気持ち的な改善作業や自助努力によらない「投資判断」としての合理性を説明できるようになると解釈しました。

"ディスカウントキャッシュフロー" と "オプション" から見るソフトウェアの経済的価値

本書において、ソフトウェアにおける「オプション」は「できるであろう振る舞いの変更」とされています。そして、振る舞いのもたらす変更のボラティリティ(不確実性)が高いほど、そもそもこの変更を採用するというオプションの価値が上がります。※ 不確実性が高いほど、上振れたときの利幅が大きくなるので、オプションそのものの価値が大きくなるため。

また、オプションの残存期間(行使期限)が長いほど、不確実性が高くなり、そのオプション自体の価値が大きくなります。つまり、振る舞いの変更を採用するまでの期間を長く取り(かつ期間内であればいつでも権利行使が可能)、行った方が変動幅が大きくなるのでオプションの価値は大きくなります。そして、期間内に変更の価値が下落(もしくはなくなる)としたら、オプションを破棄することもできます(= 流血を未然に防ぐことができます)。

正味現在価値もしくはディスカウントキャッシュフローの観点では、より早く変更を採用し、後から Tidy する方が合理的 で、オプションの観点では 変更を後回しにする(= Tidy など別の作業を先にする)方が合理的 になります。

この観点で考えると、経営をする上では原則ディスカウントキャッシュフローの考え方で意思決定される機会が多いと感じます。それに対して技術サイドからリファクタリングや改善作業が必要であると言うには、「初期コストを掛けたとしても NPV > 0 になる」ことを説明できないといけませんし、オプションとして「できるであろう振る舞いの変更」をより後回しにすることを説明する必要がありそうです。

Tidy First が合理的であるとき

単純に考えれば、Tidy するコスト + Tidy 後に変更するコスト < Tidy なしですぐに変更するコスト であれば、先に Tidy する合理性があります。ただ、こんなことが明確に言える機会はそう多くなく、多くの場合、Tidy First のケースでも Tidy After のケースもどちらにも合理性があると思われます(事実、仕事をしているとそういう場面が多くあります)。

こうなってくると完全に判断の領域になるので、Tidy First を説明できる材料を用意しておくというのが、現場のエンジニアとして望まれる振る舞いなのかなと考えました。

エンジニアとして Tidy First とどう付き合うべきか

ここからは個人的な考えですが、Tidy 作業(変更にかかる事前準備作業)は、ソフトウェアエンジニアとして働いていると必ずやりたくなるものだと思います。自分の書いたコードがクソコードと呼ばれたり、負債と呼ばれたりすることがあるとなおさら、そういう志向やインセンティブが働くものだと思います。私も何度か経験があります。

ただ、Tidy First というのは、ディスカウントキャッシュフローの観点からは経済的な合理性が相対的に小さく、また自分(もしくは自分の関わるプロジェクト)自身への経済的なインセンティブに逆行している、という認識は持っておく必要がありそうです。この観点は過去の私にはなかったので、この書籍で金融のメタファーを知って、考え方が明確になったところです。

私もあるプロジェクトの最中に、やった方がいいと思った大きな Tidy 作業で足が伸びてしまい、想定外の経済的なコストを組織に消費させてしまった経験があります。この書籍に書かれている考え方を用いるならば、

  • オプションとしては残存期間がかなり長かったのでオプションの価値は大きかった(ただ、マイナス方向に振れたために大きな損失を出した)。
  • ディスカウントキャッシュフローの観点で言えば、足の長い Tidy 作業になってしまったので、経済的な旨味はほぼなかった。
  • NPV の観点で言えば、初期コストが膨大で、かつ Tidy 後の変更にかかるコストの削減幅と比較してもマイナスだった。

もちろん、Tidy したことによる副次効果もあるにはありましたが、第一義として経済的なメリットはほぼなかったので、投資判断という文脈で言えば、良くない意思決定だったということになるでしょう。

まとめ

この書籍に書かれている考え方で全ての説明ができるというわけではないですし、投資判断という観点で言えばケースバイケースで考えられるべきものではありますが、ソフトウェアエンジニアの間でよく話される事前改善作業の合理性に関して、新たな視点を提供してくれる良い書籍でした。

2024 年の振り返り

まえがき

年の瀬なので今年1年を振り返ります。

去年の振り返りはこちら:

ema-hiro.hatenablog.com

育児編

忙殺

育児に忙殺された1年でした。多分来年も同じだとは思いますが。 1歳を過ぎて、つかまり立ちから歩くまでの成長が非常に早く、歩き始めてからは本当に目が離せなくなりました。

「育児は常にその時が一番大変」と言われますが、本当にその通りで、「歩き始めたら少しは楽になるかな?」「離乳食が普通食になったら楽になるかな?」と思っていましたが、結局、常にその時が一番大変だという印象は変わりません。

保育園の洗礼

4月から無事に内定をもらった保育園に登園するようになりましたが、すぐに病気をもらってきてしまい、夏が終わる頃まで1ヶ月のうち半分も登園できない状況が続きました。 特にGWにノロウイルスをもらい、それを皮切りに3ヶ月連続で何かしらの胃腸炎にかかりました。さらに、それが家族全員に伝染し、一家全員で嘔吐に苦しむ時期もありました。

私自身、これまでの人生で胃腸炎にかかったのは2、3回程度だったのに、保育園が始まってからの3ヶ月間で4、5回も経験し、本当にしんどかったです。

子どもの成長

保育園に通い始めてから、子どもの成長速度が一段階上がったように感じます。やはり集団生活で学ぶことが多いのでしょうか?

保育園の洗礼が終わって以降は体調も比較的安定し、登園も順調になりました。また、家では体験できないアクティビティを保育園で楽しんでいる様子を見ると、本当に助かります。

一番可愛い時期に100%子どもを見られない寂しさはありますが、保育園に行かせていなかったら育児ノイローゼになっていたと思います。子どもと適切な距離を保つことは、親の精神衛生上も非常に良かったと感じます。

自分の体調管理に気を使うようになる

これまで気を使っていなかったわけではありませんが、子供の持ってくる病気が一家全体の脆弱性となるため、夫婦のどちらかが必ず動ける状態を維持する必要が出てきました。そのため、手洗いやうがいなど衛生面を意識する機会が増えました。ただし、手洗いを頻繁に行うようになった結果、手が荒れてしまい、衛生管理の難しさを実感しています。

早起きになる

8時より前に起きたことがほとんどなかった人生でしたが、保育園の準備のために早起きする生活に変わりました。 自分自身が一番信じられませんが、人間の生活リズムは変えられるものですね(笑)。

私生活編

筋トレ続ける

気づけば筋トレを5年継続していました。 ここ数ヶ月は頻度も週2~3回に増え、非常に良いペースです。 育児をしていると子どもの食事時間などに合わせる必要があり、ジムに行くタイミングが難しかったのですが、最近はリモートワークの日に始業前にジムに行くルーティンを確立しました。朝からトレーニングをして仕事を始めるという、意識の高そうな生活を送っています。

プラモデルを始める

子どもの頃に好きだったプラモデル作りを再開しました。 きっかけは、20年ぶりに「ガンダムSEED」の新作が公開され、すっかり感化されてしまったことです。人間は案外単純ですね(笑)。

なお、「ガンダムSEED FREEDOM」は「顧客が本当に求めていたもの」そのもので、映画を見た後は力が抜けるような、これまでにない体験をしました。

仕事編

ベンダーマネジメントの業務を始める

今年から開発のアウトソース先のマネジメントを担当するようになりました。 いわゆる「外注管理」の業務ですが、この業界ではポジティブに捉えられることが少ないかもしれません。それでも、実際にやってみると学びが多く、スキルの幅も広がりました。また、自分が手を動かさなくてもプロダクトが形になっていくことの価値を改めて実感しました。

さらに、この業務は案外自分に合っていると感じました。

テックリードに戻る

テックリードの役割に戻りました。大きく変わった点は、上述のベンダーマネジメント業務が追加されたことですが、定期的にロールを行き来することで新たな視点を得ることの重要性を実感しました。

来年の話

育児が生活の大部分を占めており、プライベートも仕事も大きなトピックが少ない1年でした。 この年齢になると、日々の生活に能動的な変化をつけるのは難しいと感じますが、子どものおかげで強制的にカオスな生活を送ることができ、飽きることなく過ごせています。

来年は35歳になり、いわゆる「エンジニア35歳定年説」の年齢になります。もちろん、自分が定年になるとは全く思いませんが、区切りが良い年齢なのでキャリアの棚卸しをし、ここ数年あまり積極的でなかったカジュアル面談など外を見る機会を増やしていこうと思っています。

2024年 買ってよかったもの

Overview

今年も終わるので、買ってよかったものをまとめていきます。

買って良かったもの

ガジェット

M2 Mac mini

5年ぶりに個人用のPCを購入しました。 これまで個人で開発することはほとんどなかったのですが、昨年育休中に育児の大変さの反動からか、個人開発(主にポートフォリオサイトのカスタマイズ)にハマりました。また、生成AI関連でローカル環境でLLMを動かしたいという思いもあり購入を決めました。

正直、M2のスペックであれば個人開発レベルでは十分です。ローカルでLLMを動かす際には若干パワー不足を感じる場面もありますが、それでも概ね満足できる性能です。

ただし、今年の秋にM4 Mac miniが発売され、それこそが「求めていたミニPC!」と言える素晴らしいプロダクトだったため、近いうちに買い替える予定です。きっと2025年の「買ってよかったもの」にランクインすることでしょう(笑)。

www.apple.com

AirPods4

AirPods Pro2から乗り換えました。 カナル型ではないにもかかわらずノイズキャンセリング機能が搭載されており、その性能には驚かされました。装着すると、不思議なほど静かな環境が得られます。

ジムでカナル型イヤホンを使っていた際に「耳がこもる」感じが気になっていたため、インイヤー型イヤホンを探していました。この製品は装着感が軽く、小型で使いやすく、AirPods Proとは比べものにならない快適さがあります。特に大江戸線での通勤中、十分な外音遮断を体感でき、ノイズキャンセリング性能は本物だと実感しました。

Proモデルに比べると性能は若干劣るものの、小型・軽量という特性だけで十分な価値があります。今年のベストバイは間違いなくこれです。

www.apple.com

今年のAppleは「小型」プロダクトへのこだわりが際立っており、この特異性こそがAppleの魅力だと感じます。

Sonyのオープンイヤー型イヤホンです。 リモートワーク時に「電池持ち」と「外の音(子どもの泣き声や宅配便のインターホン音)が聞こえること」に重点を置きたくて購入しました。

期待していた用途に十分応えてくれ、AirPods4との使い分けもできているので、少々値段は高めですが購入してよかったと思っています。

iPad mini7

「地味にAirPodsに次ぐAppleの良プロダクト」と思っているiPad miniが、3年ぶり?にリニューアルされたので購入しました。 使ってみた感じではiPad mini6と大きな違いは感じませんが、それでも素晴らしい製品だと思っています。

www.apple.com

生活用品

サンワサプライ 電源タップ

サンワサプライの電源タップです。この製品の推しポイントは、タップ数を増やせるだけでなく、重さで抜けてしまったり、刺さりが甘くなるといった問題がないことです。背面のダミープラグのおかげで安定感があり、非常に使いやすいです。

SHARP プラズマクラスター ハイブリッド加湿器

子ども部屋用に購入しました。 これまでは象印の蒸気式を愛用していましたが、子ども部屋で使うにはやけどの危険があり、新しい加湿器を選びました。この製品の最大の特徴は、上部給水が可能な点で、給水のしやすさが本当に革命的です。

加湿器選びでは「手入れのしやすさ」と「給水のしやすさ」を重視するのが間違いないと改めて感じました。


ヒップシート Hugoo

子どもとの外出時に使えるおしゃれなヒップシートを探していたところ、この製品を見つけました。 黒色のデザインは男性にも合い、抱っこが楽になったことで外出がしやすくなりました。購入して本当に良かったです。

書籍

エレガントパズル

直接マネジメントに関わる仕事ではありませんが、マネジメントの視点を持っていて損はないと思い、読んでみました。 他にもいくつかマネジメント関連の書籍を読みましたが、この本を通じて「例外を許さずポリシーに従う」という一節が特に心に残り、現在も大切にしています。

スタッフエンジニアの道 ―優れた技術専門職になるためのガイド

エンジニアの上位職としての役割や期待値を理解するために読みました。 この本を通じて、ハードスキルだけでなく、抽象的な課題への取り組みや、仮説を立てて進める能力、さらにはソフトスキルの重要性を改めて実感しました。読んでよかった一冊です。

local で Cloud Pub/Sub の動作をエミュレートする

Overview

ema-hiro.hatenablog.com

上記で書いた gRPC サーバーを local で立ち上げる方法を下に、local で起動したその gRPC サーバーを利用して pubsub の動作(今回は subscriber の動作)をエミュレートするテストを書いてみます。

local でテスト用の pubsub クライアントを実装する

// grpc サーバーを起動する
listener, err := net.Listen("tcp", "localhost:0")
if err != nil {
    t.Fatal(err)
}

ts := grpc.NewServer()
go func() {
    if err := ts.Serve(listener); err != nil {
        panic(err)
    }
}()


// 略

// テスト用の pubsub の Fake サービスを用意する。
type testSubscribeServer struct {
    pubsubpb.UnimplementedSubscriberServer
}

// subscriber の fake サーバーを起動した gPRC のサーバーに登録する
tss := &testSubscribeServer{}
pubsubpb.RegisterSubscriberServer(ts, tss)

// local の gRPC に向けた grpc のコネクションを作成し、そのコネクションを使って pubsub クライアントを生成する。
conn, err := grpc.NewClient(listener.Addr().String(), grpc.WithInsecure())
if err != nil {
    t.Fatal(err)
}
client, _ = pubsub.NewClient(ctx, "test-project", option.WithGRPCConn(conn), option.WithoutAuthentication())

subscriber のサーバーを用意する場合以下のようにエミュレートしたい RPC のプロセスを mock で実装しておく必要があります。

type testSubscribeServer struct {
    pubsubpb.UnimplementedSubscriberServer
}

func (s testSubscribeServer) StreamingPull(r pubsubpb.Subscriber_StreamingPullServer) error {
    r.Send(&pubsubpb.StreamingPullResponse{
        ReceivedMessages: []*pubsubpb.ReceivedMessage{
            {
                AckId: "ack-id",
                Message: &pubsubpb.PubsubMessage{
                    Data: []byte("test-message"),
                },
            },
        },
    })
    return nil
}

今回は streaming-pull の API をエミュレートするために StreamingPull の interface を Fake サービスに実装しました。 pubsub 含め GCP の各種サービスでエミュレートしたいプロセスがあるときは Fake サービスに interface を実装すればよいです。
なお、StreamingPull のプロセスをエミュレートできるとこの Receive 処理の検証が local で可能です。

pubsub の場合は https://github.com/googleapis/google-cloud-go/blob/main/pubsub/apiv1/pubsubpb/pubsub.pb.go にある Unimplemented{$Hoge}Server に生えている interface を見ればエミュレートしたいプロセスをテストサーバーに生やせばよいです。
GCP のサービス全般、gRPC が必要なことが多いのでこの google-cloud-go の中にあるサンプルを探してみるのは良いかもしれません。

まとめ

pubsub に限らず gRPC サーバーが必要になるととたんにテストが多少難解になります。
pubsub のようにそもそもリモートでもテストがしづらいサービスなどを利用してるときは簡易的な動作検証は local で出来ると開発効率は上がることもあるので覚えてて損はない手法かなと思いました。

単体テストのために local で gRPC サーバーを起動する

Overview

タイトルのとおりです。
このエントリは local で簡単に gRPC サーバーを起動して GCP のライブラリの簡易的な動作検証環境を整えたときの備忘録になります。
モチベーションとしては、単体テストと書いてますが、主に GCP のライブラリの動作を local で簡単にエミュレートしたいとき、GCP のライブラリには gRPC サーバーがセットで必要なことが多く、良く HTTP サーバーのエミュレートで利用される httptest package がそのまま利用できません。
リモート環境にデプロイすることなく local で簡易的な動作チェックが出来ると開発も捗るのでその手順を記載します。

local での起動手順

listener, err := net.Listen("tcp", "localhost:0")
if err != nil {
    t.Fatal(err)
}
ts := grpc.NewServer()
go func() {
    if err := ts.Serve(listener); err != nil {
        panic(err)
    }
}()

これだけです。
※ port を 0 にするとその時空いている適当な port を利用してくれます。

実はこれと同じように gPRC のサーバーを起動するサンプルコードは GCP のライブラリの internal の中に testutil としてコードは公開されていました(これをあとから知りました...)

github.com

local で gRPC サーバーを起動して GCP のライブラリの挙動をテストするときはこのサーバーの実装をまるっと fork してくるほうが早いかもしれません。

また grcp-ecosystem というリポジトリがあって、ここで簡易的な local での gRPC サーバーの立ち上げ方も書かれているのでこういうとこを見て実装方法を調べるのは良いですね。 -> https://github.com/grpc-ecosystem/grpc-cloud-run-example/blob/master/golang/server/main.go

ISUCON14 に参加し、惨敗した

Overview

  • isucon 14 に参加した。
  • 今年の目標は去年の点数を超えることと最終30位に入ることだった。
  • 結果: 未達(惨敗)

やったこと

事前準備

  • SRE のメンバーにインフラ関係のワンパン設定 playbook 及び監視環境、デプロイスクリプト等々を準備してもらう。
  • 「イスワン(1時間等制限の中で初期準備を高速化するisucon)」を実施して環境設定周りの手順の統一化。
    • pprotein 等メトリクス系のライブラリ導入練習もここで済ませる。
  • LLM にコードや改善点を吐き出させるためのプロンプト用意。

speakerdeck.com

当日

  • アプリケーションコードの改善
  • DB 分割 (web server 2台構成はうまくいかず撤退)
  • インフラパラメータチューニング
  • アプリケーション側のパラメータチューニング一部
  • 一部データのキャッシュ化
    • ベンチマーカーの整合性チェックが合わず減点対象に。
    • この結果環境が壊れてしまい直せず。

結果

  • 最終300点台。初期スコアより落ちる。
  • 一時は上記改善をやって 14000点台くらいまで伸びて結構いい感じだったが、キャッシュ周りの実装をしたときに本番環境が壊れてしまいベンチマーカーが走ってもスコアが上がりきらなかった。

振り返り

良かった(と思ってる)こと

  • 過去問解く時間を捻出はできなかったが、1時間でやることをチームで共通認識持ってすり合わせる 「イスワン」 はとても良い訓練だった。
    • 業務時間中でも1時間程度であれば取りやすく思いの他効果があった(と思う)
  • 最初から DB を分割しなかったこと。ある程度1台でチューニングしきれるところまでやりきってお昼すぎくらいにDB分割したらスコアが伸びた。もともと MySQL のプロセスが支配的だったのはわかっていたけどその状態である程度チューニングして分割することでちゃんと因果を意識した改善ができた点は良かった。(反省点でも一部触れる)
  • LLM を実戦投入できたこと。
    • アプリケーションコード(と SQL ファイル)をまるっと食わせて Index 提案して、というあたりは精度高くそのまま出力された create index を適用してもスコアには反映された。
    • N+1改善も Copilot と話しながらコード生成してもらったらそこそこいいコードが吐き出す事ができた。このへんは普段の業務でも Copilot に頼ってるいい面が出た。クエリ改善 -> コード改善系も難しいコードがなかったのも今年は良かったと思うけど Copilot は本当に偉大。

反省点

  • アプリケーションコードであまり大きなボトルネックじゃないところを潰すのに時間を割いてしまっていた。特に全体のワークロード(アプリの仕様とも言えるけど)を理解せずにとりあえず非効率なクエリ直したり N+1 を改善しても数百点~1000点位の積み上げにしかならなかった = ボトルネックじゃないところを直しても意味はなかった。
  • MySQL が支配的というのは最初にベンチ流したタイミングでわかっていたので今回に限っては DB を先に分割しても良かったかもなと思った(アプローチ自体が間違っていたとは思わないのでケースバイケースだけど)
  • 「台数不足」のアプローチとして先に owner 側に手を付けず、ユーザーの体験を改善させる(レビューが悪いのはベンチマーカーのログからもわかっていた)方を先に対応するべきだった。
    • notification の改善が終わったタイミングで台数が足りないことがわかっていたので owner 登録改善でいけるのでは?と思ったけど、仕様には売上 UP -> 台数追加と書いてあるので売上 UP のための配車・乗車の改善にアプローチするべきだった。
      • ここはしたにはしたけど効果的な改善方法が見つからなかったというのはある。仕様の理解不足。
  • マッチングの改善以外にもレギュレーションに記載されている内容の精査や理解の時間を取るべきだった。
  • ベンチは常に回せるようにする。
    • 積極的に revert する。
    • ベンチ壊れた状態を長時間保持しない(改善を merge してもベンチ落ちるので意味がない。手持ち無沙汰の時間を減らす)
    • 可能であれば複数台構成にするまでは1台を dev 環境として使う等のルールを決めておく(デプロイスクリプトはそうしてもらってたけどちゃんとは使えなかった)
  • パラメータチューニングもう少し頑張るべきだった。
    • 終盤、ちょっと行き詰まって次の一手が浮かばない時間があってもったいなかったので、実装側のパラメータチューニングにはもう少し意識を向けるべきだった。
      • retry interval を調整したことでスコアが伸びた箇所があったがそこしかチューニングしてなかった。
      • MySQL にしろ nginx にしろスコアに目を向けるのであればパラメータチューニングは王道なので、チューニングの余地があるところはもう少し手を出しても良かった。実際話を聞く限り 5000 点程度の上積みにはなったっぽい。

その他 (感想戦)

当日終了後社内の isucon 参加メンバーで打ち上げをした際にも話したし、Discord の会話も眺めてましたけど DB 分割と web サーバー複数台構成をもう少し早いタイミングでやっても良かったなということと、仕様(アプリケーションのレギュレーション)を理解する時間をちゃんと取るべきだったなと感じた。
仕様の理解については毎年課題を感じているし、今年も取っていなかった訳では無いが、8時間という短い時間の中でルールから加点ポイントを導き出すスキルはまだまだ足りてないと感じる。
ルール理解のプロセスも実際やっていたから一部のパラメータをチューンングしてみようという発想に至ったのは良いことでもあるけどそれ以外にも目を向けるべきだった。
この辺りは ISUCON14 が終わったあとメンバーとも話したけど、ベンチマーカーのログが大きなヒントになっているので、ベンチマーカーのログとレギュレーションを突き合わせて、スコアに効きそうなポイントを洗い出す時間は環境構築してる間にやったりとか、8時間全体のマネジメントには改善の余地があるなと思った。