Kengo's blog

Technical articles about original projects, JVM, Static Analysis and TypeScript.

続・codexでJVMバイトコード静的解析ツールを書いた

codexでJVMバイトコード静的解析ツールを書いたの続き。その後2週間ほどで6個のマイナーリリースが出せました。 ユーザ体験を考えなければ充分に本番投入できるレベルに来ていると感じています。

v0.5

opentelemetryでtraceをexportできるように改修
SLF4Jのplaceholderの数を検証するruleを追加
ruleを並列で実行することにより高速化

v0.6

SLF4J ruleを多数追加

v0.7

SARIFレポートの安定化
jarを並列で読み込むことにより高速化

v0.8

大きな入力をファイルを経由して受け取れるように修正(Gradleへの組み込みに対応)
guava解析時に失敗する問題を修正

v0.9

EnumSet の利用を推奨するruleを追加
InterruptedException の扱いを検証するruleを追加

v0.10

SLF4J向けrule群をLog4j2向けに調整したrule群を追加
InterruptedException の扱いを検証するruleを追加
ClassPathにSLF4JがないときはSLF4J用ruleを無効化して高速化
ClassPathにLog4j2がないときはLog4j2用ruleを無効化して高速化
GitHub Code Scanningに対応

人間の役目としての意思決定

ルールの実装についてはskillに要件や方法論をまとめることで、ほぼテストケースをレビューすれば実装の詳細は気にしなくて良いという段階まで来ました。またSLF4J向けルールをLog4j2向けに調整して実装する、などはplan作成からほとんど任せられました。このようなルールの横展開ができるケースは多くないものの、codexであればかなり少ない工数でやれそうです。

今回自分が気にするようにしていたのは機能と非機能のバランスです。v0.5でopentelemetryを使えるようにして、どこで遅くなっているのか、どのルールが遅いのかを事実として捉えられるようにしました。これによってjar読み込みの並列化であるとか、ルールの並列化であるとか、特定ルールの最適化であるとかの高速化を適時に実行できました。今回の開発ではコーディングエージェントから気軽に呼び出せるパフォーマンスを重視していますから、そのための観察と意思決定が高い確度で行えることは非常に有効であると感じました。

いまのところOTLPデータからレポートの作成はjaegerを使った手動となっていますが、ちょうど最近JaegerのMCPサーバ対応がリリースされたため、今後はレポートの作成と評価までを自動化できそうです。

こうなると人間にとっては実装の詳細は正直どうでもよく、目指している速度や機能要件を満たせているか、ルールのfalse positiveやfalse negativeを見つけるためのテストケースが書けているかの2点だけを見れば良いことになります。たまに不要なcopyの削減や共通化できそうなコードの発見・対処といった負債返済のためのプロンプトを投げることは必要だと思いますし、そのskill化には価値があると思いますが、人間が真にやるべきは全体のバランスを見て今何をするべきかを決定づけることだと考えます。

このやりかたで踏みそうな地雷としては、OSS実装をコピって持ってきてしまいライセンス違反となることでしょうか。実際に家庭用ゲーム機エミュレータを実装させるとOSS実装をまんま持ってくるようなことはあるようです。また細かい実装まで見ないということは脆弱性を作り込んでしまう可能性も上がるので、SASTの価値も上がりそうですね。Renovateでお世話になっているMendがこの方向で攻めるようなので期待しつつ、それこそJFlogやSonatypeやSonarといった先駆者が多い領域でもあるのでメシ食うのは大変だろうなぁと思うなどしました。

inspequte 1.0リリースまでにやること

ルールを増やしていくことはもう難しいことではなくなったので、ひとまずユーザ体験向上にコストをかけて良さそうです。だいたい次のようなラインナップになるでしょうか:

  • コンパイル済みバイナリを提供する
  • setup-inspequte actionを提供する
  • Gradleプラグインを提供する

あとできればJava標準APIのnullnessデータベースを組み込んでやりたいですが、そこまでこだわるよりは出しちゃったほうが良い気もしています。ちょっと考えます。

codexでJVMバイトコード静的解析ツールを書いた

今年の目標としてRustでCLIを作りたかったのと、coding agentの台頭で静的解析ツールに求められるものも変わるのではないかと想定したことを受けて、RustでJVMバイトコードを解析するコマンドを作りました。

図1 ChatGPTに描かせたロゴ

開発の背景

SpotBugsへの貢献を止めてから後、実は2度ほど静的解析ツールを開発していました。目的はKotlinコンパイラプラグインとして動くものを書くことです。KotlinのIRを静的に解析することに挑戦したかったですし、独立したツールとして提供するよりもIDEやビルドシステムに統合されていた方が体験が良いのではと思ったからでした。差分コンパイルなどのプラットフォームの最新技術の恩恵も得られそうだと思いましたし。

しかし1度目は自分で書き、2度目はエージェントに書かせる形で挑戦したものの、結局はKotlinコンパイラが充分な情報を提供してくれ無さそうという結論になり、いずれも挫折しています。

そこから月日が経ちcoding agentが一般的になったことで、こうした統合の価値がほぼ無くなってきたのでは?むしろ設定や統合をできるだけ減らしてCLIでサクサク実行できる方がいいよね?ということを思いつき、成人の日を使ってcodexといっしょに書いてみた、というところです。もはやJavaやKotlinを書くのにIDEA使わなくなりましたもんね。

なおRustにしたのは知人と話していたときに、高速起動はやっぱRustかGolangだよな、Rustのほうがシステムプログラミング寄りだし一番速そう、みたいな話で盛り上がっていたのがきっかけです。変な苦手意識を取り除くためにも、自分が詳しくて簡単に検証サイクルを回せる開発テーマで使ってみるべきだ、という直感もありました。

codexと開発するうえで工夫したこと

claude codeのplan modeがいいぞという話を多く耳にしたので、codexで開発するうえでもまず how do you plan ~ という質問をまずして計画から入るようにしました。これはControl Flow Graph (CFG)のような静的解析としては一般的な構造や、厚めに書きたいテストケース周りでは特に役立ったと思います。エージェントと2人で仮説を持って開発に臨めること、結果として出てきたものをどんな観点でレビューすべきかをあらかじめ考えて臨めること、特にこの2点がコード変更量に圧倒されないために有用でした。

また経験上必要になるとわかっている性能検証用の実装( --timing オプションやベンチマーク用スクリプト )や実際のJavaコードを使ったテストを書くための spotbugs test harnessのようなハーネスを早期に実装できたのもかなり有効な工夫だったと思います。ひとつひとつの変更を積み重ねると全体最適でなかったり clone が多発するようなコードになったりして、性能改善のための開発を要所要所で入れていく必要があるのですが、その際にベンチマークによって「どこで、どのくらい」遅いのか、変更でどのくらい改善されたのかということがcodexにとって一目瞭然な状態を組み上げられたので、高速化のための方針策定からcodexに任せられるようになりました。たとえば v0.4.0 リリースではcallgraph edge overheadの2倍近い高速化を実現しましたが、このときのリクエストは次のようにとてもシンプルなものでした:

any idea to shorten analysis_load_ms? it is slow even when we load a single jar, so the cause is not storage I/O I guess.

これだけ書くとコードから仮説を立てて、ベンチマークを都度回しながら有効な手段を特定して実装してくれます。もちろん機能に影響はないことは単体テストとスモークテストで確認できる状態です。最終的には関数実行ごとに clone が実行されている点の修正が提案されましたが、その際もパフォーマンスを評価することをこちらから次のように依頼し、実際に実行されました:

reduce per-call cloning in build_edges then rerun the bench-spotbugs.sh to confirm the perofrmance.

ベンチマークを見ながらパフォーマンス確認できるところまでひとりで持ってくるのは何かと時間がかかり、かつ個人OSSではその内容に確信を持つためのレビューをお願いすることも難しいので、coding agentによってスピードと品質とが両方向上したと感じます。

今後の開発について

本番投入を考えたときに今の実装に足りていないのはルールセットをカスタマイズするための設定ファイルと、ルールをプラグインのような形で増やせる仕組み、そして組み込みルールの数です。そして前者2つについては実装戦略はわかっており、後はやるかやらないかだけという状況です。

ルールを増やすことはcoding agentにとっておそらく容易なことでしょう。既存ツールのルールを並べて評価し、重要そうなものをより本質的なルールへと再定義し、これを実装するだけです。間違いなく人間よりも早く量を用意できると思われます。

のであとはやるだけで普通に既存ツールの土俵で戦えるようになるはず。GradleやIDEAへの組み込みは元々スコープ外ですが、必要なら誰かがやるでしょう。

あと大切そうなのは、いかにdog foodingする場を作ってfalse positiveやfalse negativeについてフィードバックを受けるかという点でしょうか。こればかりはやってみないとわからないですね。ルールセットを増やしてcoding agentを積極的に採用しているOSSを見つけて売り込みに行く必要があるでしょう。

このプロダクトを育てるべきかはちょっとまだ見えてないんですが、継続の意義はあると思うので、しばらく隙間時間に育てていきたいと思います。

2025年の振り返りと2026年の抱負

昨年の振り返りと抱負はこちら。技術書典への個人出展は2回やりましたし、マネジメントスキルの棚卸しや実行も1年間みっちりやりましたので、当初想定はやり切りました。

今年のトピックはだいたいこんなところ↓だと思うので、掘り下げていきます。

  1. 副業をやめた
  2. 執行役員(VPoT)になって成果を出した
  3. 自分の強みと無関心を見つめ直した
  4. 生成AIの使い方が板についてきた
  5. 子どもの親として色々考えた

副業をやめた

今年のはじめ、前職つながりでいただいていた副業をお断りすることにしました。不義理を働いてしまい、当時は結構落ち込んでました。いや業務委託なんてそんなもんでしょという話はあるんですが、それはそれ、これはこれというやつです。

辞めた理由は本業がめちゃくちゃ忙しくなったからで、それは次の理由によるものです。

執行役員(VPoT)になって成果を出した

1月からVPoTを拝命し、3月からVPoEを兼務しました。SREのマネジメントも引き続きやっていたし途中から責務も増えたので、実質4足のわらじを履いてました。

前回の振り返りで書いてた「マネジメントスキルの棚卸し」が必要になっていたのも、転職後に離れていた「マネジメントのマネジメント」を再開する必要があったからです。

自分がマネジメントのマネジメントに思うことは昨年「組織という仕組みで解決することの難しさ、あるいはマネジメントに超人を求めるのは間違っているだろうか」に書いた通りで、助けになるフレームワークやツールはあるものの結局は状況と組織に合わせて様子を見ながら試行錯誤するものであり、とにかくまぁ難しく複雑なものであると思っています。それを兼務なので、明らかにキャパシティを超えた挑戦でした。

とりあえずこの1年ではすべて理想通りにやりきることはできないと早々に諦め、VPoEなのに採用をリードしなかったり、VPoTなのに技術戦略を描かなかったりと、5年後の自分が見たら指さして笑うだろうなという感じの戦術を採りました。ただそのおかげで最も外してはならない権限委譲の実践によるスケールする組織を実現できました。組織は1年間で完成するものではないため引き続きケアは必要ですが、会社として事業戦略をクリアに描けるようになった遠因を作れたことは誇れます。

生成AIが手に馴染んできた

今年もXでは生成AIの話はほとんどしてませんでした。このスピードについていく心理的コストは払えないから、レイトマジョリティになって長いものに巻かれようというスタンスです。実際自分が役割を演じる上で重要なのは応用なので、そういった情報のキャッチアップは同僚に任せて、製品化された事例を主に追っていました。

プロダクトとしてはCursor, OpenAI, Anthropic, Perplexity, Slack AI, Notion AI, Dify に Vertex AIあたりを薄く広く触っていましたが、コーディングに常用しているのはGitHub Copilotです。個人的にいまのMicrosoftとGitHubを信頼しており、技術的には最先端ではないかもしれないが体験を統合させたプロダクトを提供してくれるだろうと期待していて、実際Agent HQは良いものだと思います。

自分もMacbook ProのMAXを個人端末として所有しているので手元で色々動かそうと思えばできるはずですが、今のところはGitHubのUIからCopilot使ったり、Codespacesを活用してそもそもコードのチェックアウトすら省いたりとできるだけクラウド側で完結させるようにしています。Codespacesを使い倒せば全部インターネットの向こう側で完結して便利そうなんですが、流石にランニングコスト高そうですね。

あとSRE業務への応用という観点では7月のSRE Nextで見たTopotalさんのデモが良かったのと、SRE自動化ハンドブックで紹介されていた業務自動化がいま想像できる理想だなと思っています。HoneycombとDevelocityのMCPサーバが実装されたようなので、来年色々試してみたいです。

2026年の抱負

子どもが学校でPython3をやっているので、共通の話題としてのPythonをしばらくやっていこうと思います。ひとまずpgzeroで壁打ちゲーム(pong)をフルスクラッチで書くところまで来ました。

またバイナリを作成・配布する基盤としてのRustも再入門が必要だと考えて、今週 rustup で環境のセットアップを終えました。作りたいCLIはまだ見つかってないんですが(ちょいちょい思いつくけどコレジャナイになりやすい) tokio くらいちゃんと使えるようになろうと思います。

2025年に読んでよかった本

前回に続いて、今年読んでよかったなと思った本をいくつか。

医療関係

病院という組織の動きと未来を知るうえで「病院経営の教科書」が良かったです。3版まででていて、定番として信頼されているのでしょうね。実際にはヒトが占めるウェイトがかなり大きいとので現地訪問必須とは思っているんですが、現地訪問時の観察眼や質問を研ぎ澄ませるためにもこういう知識は有効だろうと思いました。

エンジニアリングで地域医療を支えるうえでその構造を理解することが重要なのは疑いないことですが、「地域医療の経済学」は今ある構造や課題だけでなく解決の提案も含まれていて面白かったです。その解決が日本の未来かというとたぶん違うんだろうなぁと思いつつ、かかりつけ医の機能を日本の医療でどう実現していくか?は間違いなく今後のアツい課題のひとつだと思うので継続的に考えていきたいです。

自分には機序が明確な印象のある西洋医学に対して、東洋医学にはあんま根拠が明確でない印象があります。自分は昔読んだインタビューマンガの「絶望に効くクスリ」で東洋医学を研究してエビデンスを作っていく動きがあることは知っていたのですが、このブルーバックス「東洋医学はなぜ効くのか」を読むことでその精度がかなり高まっていて納得できる範囲がかなり広くなっていると感じました。 鍼も漢方も、ここまで判明させるのはかなり大変だったろうなと門外漢でも容易に想像できるところです。特に漢方についてはかなり体質によるという点と、生薬の流通・管理に課題があるとは個人的に感じているところなので、体内で作られる成分そのものの精製や関連する腸内菌とセットで服用できるような工夫が今後一般的になると良いなと思います。

その他

図書館で読んだ古めの本です。自分は社会課題解決をする破壊的イノベーションをもたらすスタートアップで働きたいと思っているヒトなのですが、この本を読んで事業そのものが社会課題解決になるスキームそのものがかなり新しいのかもしれないと思いました。というのもこれに掲載されているのは、従業員に対する福利厚生や売上を利用した寄付を用いて社会課題解決に繋げている話なんですよね。もちろんチョコレート製品という食品を通じて社会貢献していたことは想定されますが、カカオ生産に様々な問題があったことは知られているとおりなので、色々と考えさせられました。

「誰が書いたコードか当てまShow!!」出題者による解説

勤務先のブログで紹介されていますが、Kotlin Fest 2025で出題したクイズに向けてKotlinコードを書いていました。本記事では自分で自分のコードを解説してみます。企画のネタバレを含みますので、先に企画紹介記事を読んでいただければ幸いです。

ヒントは「メソッドチェーン」「Kotlinのプロ、じゃない」

まず自分のコードを再掲します。パッと見でメソッドチェーン風だということがわかり、リアクティブプログラミングの経験があるひとにはAっぽいなと伝わる雰囲気を醸し出しています。

fun solve(input: String): String {
    var prevScore: Int? = null
    var prevRank = 1
    return input.split("\n")
        .map { it.split(" ") }
        .groupBy({ it[0] }, { it[1].toInt() })
        .mapValues { it.value.sum() }
        .toList()
        .sortedWith(compareByDescending<Pair<String, Int>> { it.second }.thenBy { it.first })
        .mapIndexed { index, (name, score) ->
            val rank = if (prevScore == null || prevScore > score) {
                index + 1
            } else {
                prevRank
            }

            (rank to "$name $score").also {
                prevScore = score
                prevRank = rank
            }
        }.takeWhile { it.first <= 3 }
        .joinToString("\n") {
            "${it.first}. ${it.second}"
        }
}

ひとつずつ見ていきましょう。まず目につくのが変数名です:

fun solve(input: String): String {
    var prevScore: Int? = null
    var prevRank = 1

prev は previous の短縮形です。このくらいなら短縮せずに previousScore としたほうが良いかもしれませんね。このくらいの変数なら特にコメントで意図を説明する必要はないかなと思って、ドキュメンテーションコメントは何も書いていませんでした。

なおBのコードでは previous ではなく last が使われており、どちらが良い命名なのかを議論するのも面白そうです。

さて次の行が大きなヒントになっています:

    return input.split("\n")

これとてもJavaっぽい書き方で、実際他の方の回答だと lineSequence() が使われているんですよね。ここで少なくとも「AはKotlinのプロであるタケハタのコードじゃないな」とバレるようになっています。なぜ lineSequence() を使わなかったのかって?だって、知 ら な い そ ん な 函 数!勉強になりました。

なおここで「OSがCRLFのような他の改行コードで動いてたらどうするんだ」ってツッコミは可能なんですが、この手の出題でそこが問題になることはないだろうと判断しました。厳密には System.lineSeparator() あたりを参照したほうが良いですね。

ちなみにJavaで似たような問題を解くと必ずと言っていいほど Scanner が出てくるんですが、さすがKotlin、流れるように処理を書き下せました。便利。

さて次のコードです:

        .map { it.split(" ") }
        .groupBy({ it[0] }, { it[1].toInt() })
        .mapValues { it.value.sum() }
        .toList()
        .sortedWith(compareByDescending<Pair<String, Int>> { it.second }.thenBy { it.first })

ここはまぁ、普通ですね。 Pair を引き回すと可読性が落ちるので、Dのようにクラスを定義して使うのも良さそうですが、今回はまぁいいでしょという気持ちになりました。今見返すと、 .groupBy({ it[0] }, { it[1].toInt() }) がだいぶ厳しいか。

あと compareByDescending() については、3分くらい「GuavaならOrderingなんだけどな〜」って検索してました。無事に便利函数が見つかって良かったです。

さて次は誰からも突っ込まれなかったコードです:

            (rank to "$name $score").also {
                prevScore = score
                prevRank = rank
            }

この also の使い方、だいぶ独特な感じに仕上げたつもりなんですが、見事にスルーされました。クリーンアップ処理を他のコードと違う場所にまとめて書ける、かつ finally と比較したときに例外の発生を想定してないことを伝えられる、ということでわりと悪くない書き方だと思うんですけどね。

最後はこのコードです:

        }.takeWhile { it.first <= 3 }
        .joinToString("\n") {
            "${it.first}. ${it.second}"
        }
}

お題を見たときに takeWhile() が出てくることは決めていました。一番最初に書けたコードだとも言えますね。あとは特に変わったところはなく、普通に joinToString() して終わりです。

他の人のコードへのツッコミどころ

ということで、以上で出題者による解説でした。楽しんでいただけたでしょうか。

なお他の人のコードにもツッコミどころというか、個性を感じるところは多々あります。ぜひ皆さんも考えてみてください。私が思ったのは次のような感じでしたが、人によって意見は異なると思いますので、ご参考まで:

  • 名前つき引数、いい。わかりやすい。IDEAで書いてるとなかなか気付けない気配りポイント。印刷前提だったので、自分も書いたほうが良かった。
  • String.split(String) は正規表現のコンパイルが走る可能性があるので、ループ内では呼ばないクセをつけたほうが良いかも
  • return@foobar は可読性が落ちるので、個人的には避けたい
  • tailrec すごくいい
  • コメントがいちいち意味ない!(たぶんツッコミ待ちなんだけど気になる)
  • is-a と has-a は違うんですよ!(たぶんツッコミ待ちなんだけど気になる)
  • 函数の引数の型はできるだけ抽象に寄せたいなぁ、なんで HashMap なんだっけ……(たぶんツッコミ待ちなんだけど気になる)

エンジニアに必要な「根拠のない自信」と、挑戦を支えるマネジメント

自分は以前、意思決定というのは「腹をくくる」「清水の舞台から飛び降りる」のように不可逆的かつ一定のダメージを覚悟する・リスクを負う行為だと思っていました。そして意思決定が関わる戦略が長期的であればあるほど、情報を集めても考えを深めても不確実性を下げることはほぼできませんから、意思決定の難度も高くなっていきます。この長期的な意思決定の代表例が企業の経営戦略でありスタートアップの企業理念なので、自分は企業経営に対して強い苦手意識を持っています。不可逆なリスクを負うには覚悟が必要で、その覚悟はどっから持ってくるんだ?というのが未解決問題だったわけです。

ただ最近、不可逆的にリスクを取りに行くだけが経営ではないというか、もうちょっと違う解釈への投影もできるんだなと思ったので、ちょっと書いておきます。

ビジョンは未来予測じゃない

我々はどのような企業になる、弊社の製品はどのような課題を解決する……といったビジョンの策定と発信はマネジメントの大きな仕事です。個人的には創業経営者の仕事はほとんどこれだと思っています。ビジョンに共感する人材を集めて目標をつっこむとアウトカムが生産される、それがスタートアップというやつでしょう。

このビジョンはもちろん想像可能・達成可能なものである必要はありますが、別にそれは「腹をくくる」ようなものである必要はない……というと語弊があるんですよね。当人は腹をくくって強いリーダーシップを発揮するべきですし、考え抜かれたメッセージと行動を通じて課題を実際に個人として組織として解決しなければなりませんので、腹はくくるべきです。投資家が投資先を選ぶうえで必ず見るものとして創業経営者の人柄が挙げられるようですが、それには腹をくくれるか、ビジョンを誰よりも信じて実行に突っ込める人物かを見ている側面は間違いなくあるでしょう。

自分が言いたいのは、この意思決定を100%実現可能だと信じることは重要だが、この意思決定した内容が100%実現されることはそんなに問題ではないということです。自分はここを混同していたのですが、世の中を見るとこうしたビジョンを途中で変えたり、実現する前に退場したりという結果に至った経営は多数あります。 わかりやすいところではGoogleのビジョンでは2000年代に one-click という単語を使っていましたが、今は使っていません。実際音声入力やマルチタッチスクリーンが当たり前になってきた現在において one-click と言っていたら、違和感が強いでしょう(当時もキーボードで全部やる人はいましたが、ここではクリックしない調べ物がより一般ユーザに浸透したと考えます)。しかしこのビジョンの変更について、one-clickだと言っていたじゃないか!と怒るような人は投資家にもいないんじゃないかと思うんですよね*1。クリックが主要なインタラクションではなくなる未来を読めなかったことや、20年使えるビジョンを提示できなかったことは問題ではないわけです。

結局重要なのはビジョンを出して実行することを通じて、従業員や顧客、市場といったものを動かしていくことです。one-clickという言葉を通じて強いシンプルさへのこだわりだとかユーザインタフェースの削ぎ落としだとか、そういったものの重要性を強く示して世界に働きかけることが重要だったわけです。だから今までの自分が「one-clickでできると思うけど、万が一できなかったらどうしよう」みたいな心配をして苦手意識を持っていたのはちょっとズレていたなと思います。

根拠のない自信の効用

自分が打ち立てたビジョンを信じられるのは、決して楽観的だからではないと思っています。まぁそういう人もいるでしょうが、どちらかというとまぁなんとかできるやろ的な根拠のない自信が多いんじゃないかと思うんですよね。そして根拠のない自信を持つことはシニアエンジニア、というか人を巻き込んで動いていく開発者には結構大事なんだなと思います。

根拠のない自信というと深く考えないことや痛みを気にしないことのような「鈍感力」のイメージもあると思いますが、自分が今回考えているのは「全く同じじゃないけど、似たようなことは経験してきた」「失敗しそうなポイントをざっと洗って、あらかじめ対処を考えられた」という、確実ではないけど一定の工夫はした、でも根拠と言うには薄くないか?みたいなそういうやつです。経験者の肌感とか、職人の感覚とか、そういうやつです。テスト駆動開発がすべてのバグを防ぐわけじゃないけど、前に進む自信をくれるのと似ているでしょうか。

一応テクニックとして「失敗してもダメージを許容できるようにする」、つまり撤退条件をあらかじめ定めるなどのコンティンジェンシープランの検討は使えます。使えるし、使うんですが、コンティンジェンシープランは次善の策であって本丸の確度を上げるものではないので、自分で打ち立てたビジョンを頭から信じる根拠にはならないと思うんですよね。のでテクニックとしてつくるコンティンジェンシープランの向こう側に、マインドとしての「まぁなんとかできるやろ」的な根拠のない自信は一定必要になるはずです。そして「根拠がない」ことを気にしすぎないでまず一歩踏み出すことが必要なんだと思います。

生存バイアスを作ればいいんだよ、というアドバイスをしないために

この根拠のない自信は理詰めでは身につきません。ではどうするのか?一番シンプルな答えは「とりあえずやってみて、転んだらそこから学べばいい」というものです。確かにこれは短期的には有効で、コードを書いて壊して直して覚えるような開発者の学び方とも相性がいい。

ただ、この考え方には落とし穴があります。生き残った人だけが「転んで学んだ」と語れるのであって、再起不能になるケースも多いのです。これがいわゆる生存バイアスというやつですが、ビジネスや組織運営では持続可能な方法が求められることから、「千尋の谷に突き落とす」ようなコミュニケーションは避けるべきです。

levtech.jp

だからこそ大事なのは、「安全に転べる環境を用意する」ことです。歴史から学ぶのが理想ですが、それが難しいときには上司や仲間とのコミュニケーションでセーフティネットを作る。例えば「まず1週間自由にやってみて、うまくいかなければこのプランに切り替える」といった撤退条件を決める。「失敗したら一緒に謝りに行こう」と保証する。そうすることで挑戦のリスクを許容でき、持続可能に第一歩を踏み出せるのだと思います。

blog.kengo-toda.jp

こうしたセーフティネットと合わせてもうひとつ大切なのが、周囲からのフィードバックによる“外付けの自信”です。根拠のない自信を持たない部下は、新しい一歩を踏み出しにくい状況にあるかもしれません。その場合は「君ならできると思うよ、だってあのときもああやって解決できたじゃん」などと、普段見ているからこそできるフィードバックを通じて背中を押しましょう。

根拠のない自信が自分で作れないなら、まぁアイツが言ってたからやってみるか……という外付けの自信を持たせてあげる。それで第一歩を踏み出せたらしめたものでしょう。

まとめ:リスクはゼロにできないから、根拠のない自信をうまく使う

リスクをゼロにすることは必要は多くの場合不可能ですし、不要です。企業経営のような難しい意思決定であっても、リスクをゼロにすることではなく行動を通じて課題解決を個人として組織として前に進めることが求められます。これは楽観的になってリスクを軽視しろという意味ではなく、自分ならなんとかできるだろうという根拠のない自信をうまく使うことが大切だという意味です。

もちろんリスクを管理するために、コンティンジェンシープランを定めたりダメージコントロールしたりすることは必要です。これらが自分でできない部下に対しては、上司としてこれを助けることが欠かせないでしょう。加えて根拠のない自信を外付けで持たせてあげることで、リスクを伴う挑戦に一歩踏み出せるように支援できると理想的だなと思いました。

*1:don't be evil の方は知らん

医療向け基幹システムを提供するスタートアップに入社して3年経った

医療向け基幹システムを提供するスタートアップに入社して丸3年経ちました。入社した目的はどの程度達成されたのか、今後どうしていくのかをまとめておきます。

入社した目的の振り返り

入社エントリに書いてるのですが、今回の入社には大きく分けて3つの目的がありました:

  1. 顧客と開発現場から学び、製品とチームを継続的に改善する
  2. 製品を通じて社会の課題を解決する
  3. ドメインの課題を解決する手段とプロセスを理解する

リーンに社会課題を解決するワザを研ぎ澄ませること、新しい技術で新しいアプローチを実現することで難しい社会的課題を解決すること、ドメインの課題を噛み砕いて実行可能な計画に落とし込むことがだいたいの理由というところです。そして前職の先輩方からさんざん聞いていた創業時のカオスを楽しみたかった、というのも大きかったですね。

リーンに社会課題を解決するという点で期待以上の動きができている

これは直近の記事でも公共政策について触れていましたが、リーンに社会課題を解決するための動きは当初の期待を超えた広がりを見せてくれています。スクラムをちゃんとやる、組織を大きくする、社会課題に真正面から取り組むといった機会が山のようにあります*1

医療という社会課題を選んだのも結果的に良かったと感じます。課題の大きさや市場としての複雑さはもちろんですが、2040年という節目を国が挙げていることもあり*2ここから15年間に何が問題になるかが考えやすく、少なくともマクロでは傾向や課題は掴みやすい領域です(と思っていたんだけどそうではない領域がたくさんある、という話が今読んでる本に書かれているようなので楽しみ)。

お金と生命を預かるシステムなので難しさは当然あるのですが、そこは前職から変わらずです。ただ要配慮個人情報を扱う一方で医療機関側に専門家が少ないという業界の課題については、事業での解決は難しいなと思っています。

ドメインの課題を解決する手段とプロセスに驚きはない

過去記事「組織という仕組みで解決することの難しさ、あるいはマネジメントに超人を求めるのは間違っているだろうか」で書いたことほぼそのままです。ドメインの課題を解決する手段にも、それを適用するプロセスにも、大きな驚きはありません。たぶんどこに行ってもこれは変わらないんだろうと思いますし、生成AI時代でも使えるスキルというのはこういうものを指すのかもしれません。

結局これをどこまで泥臭く、丁寧に、飽きずに情熱を持って徹底できるかどうかで課題が解決されるか決まります。内発的動機づけが重要な理由はだいたいこれだと思ってますし、創業経営者が重要な理由もだいたいこれだと思ってますし、社会課題解決の現場にしばしば継続可能性が怪しい活動が出てくるのもこれだと思ってます。だからこそ信念を持って継続できる、興味と関心を保ち続けられる社会課題を探すことが大切だと思いますし、中間管理職としては理想と継続可能性の両方に目を配らなければなりません。ロイヤリティとかエンゲージメントとかの横文字が出てくるのも、この複雑さに起因するのでしょう。

結論、やりたいことはだいたいできてる

連結ウン千人のメガベンチャーで社員番号1000番台だった3年前と比べて、100数人のスタートアップで社員番号20番台をやってる今のほうが事業と社会課題解決にまっすぐ向き合えている感覚があります。前職の先輩からさんざん聞かされていた体験をいままさにできていて、良かったと思います。

もちろん全て理想的に事が運んでいるわけではなく、色々とやりきれてない部分はまだ多々あるのですが、ひとつずつ解決していきます。

*1:具体的な内容については個人ブログの範囲を超えるので、勤務先のカンパニーデックに譲ります

*2:最近公開された医療提供体制等についてという資料がわかりやすい