Kengo's blog

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

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

前回の退職エントリに続けての入社エントリです。

2022年7月1日より株式会社ヘンリーにSREとして入社しています。Java→Kotlin、AWS→GCP、Ansible→Terraformなど技術的には大きな変化を伴う転職でしたが、SREとしての観点や課題解決プロセスへの習熟、そして持ち前のひとがらのよさ(なぜか変換できない)などのポータブルなアビリティに助けられて2ヶ月生きています。このエントリーでは入社の目的と2ヶ月働いてみての転職の感想を残しておきます。

目標

新卒入社のころと違って自分なりの働き方やプロフェッショナルとしての在り方があるためそこまで厳密な目標は定めなくていいのかなと思う反面、振り返りや内省が成長の糧になることも確かなので、3つほど定めています。

1. 顧客と開発現場から学び、製品とチームを継続的に改善する

さすがに14年も経つと、ある程度はITエンジニアとしての基礎や専門性に自信がついてきます。その実態は、自信を持たないといけないという焦りかもしれませんが。。。

ともかく、継続的に仮説を検証して製品やチームに反映させることを通じて成長を積み重ねることや、その実行のための各種棚卸しや優先度付け、情報を整理したうえでの議論などはある程度可搬性のある技術として身についた気がしています。また昔「ソフトウェアを育てるにはユーザが多いほうがいい」と考えていたのとは違うやり方が多数あるということもSaaSやリーンソフトウェア開発手法の文脈で見てきたので、あまり規模にこだわることは本質的ではないのだなという気づきも得ました。ので前回の目標1を単純化して、継続的な学習の実行を新しい目標としています。

2. 製品を通じて社会の課題を解決する

今回転職活動を通じて多くの会社さんと話す機会を作っていただいたのですが、その中には全くときめかないところがいくつかありました。大きく分けると2パターンあり、自らを破壊的イノベーターと位置づけながら「なぜ新技術によってレガシーのやり方を駆逐できるのか」を説明できないところ、「新しいアプローチによって顧客事業を助けられる」と売上機会を説明するだけで「どのような社会的課題を解決するのか」を語ってくれないところには魅力を感じませんでした。

これは新卒で入った会社がブレークスルーによる社会的課題の解決にこだわっていたことの影響だと分析しています。新卒で入る会社が発想や働き方に強く影響するというのは本当なのかもしれません。いまの私は「新しい技術」だけでも「新しいアプローチ」だけでもない、「新しい技術で新しいアプローチを実現することで難しい社会的課題を解決する」働き方がしたいようです。

また同様の理由から、コンサルや請負開発のような自社で製品を持たない働き方を本業にするイメージは湧きませんでした。CD最適化や静的解析、ビルドツールといったスキルだけ見ると、むしろ多数のプロジェクトを実装して渡る働き方はマッチしているとは思っているんですけどね。Canは合ってもWantが合わないという感じなのでしょうか。

3. ドメインの課題を解決する手段とプロセスを理解する

これは俗に言う「チャレンジ目標」です。

私はいままでたくさんの部署や役割を転々とさせていただいて、製品開発だけでなく保守や研究開発、管理統制といった経験を積むことができました。また製品種別だけでもBtoB、BtoC、BtoE、社内ツールと多岐にわたる経験ができました。

が、一方でひとつのドメインに長く留まって業務課題を解決することはしてきませんでした。よく言えば多彩な観点からソフトウェア開発を支えられる人材ではあるのですが、前述したとおり社会的課題の解決に価値を感じている以上、ソフトウェア開発の手前つまり「ドメインの課題を噛み砕いて実行可能な計画に落とし込む」スキルを自分で持っておきたい気持ちもありました。

このスキルを得ることはソフトウェア開発者としての成長にもつながると思っています。ドメイン駆動開発の話です。出典を失念したのですが「昔は言語の表現力や計算機の性能が不足していたのでGoFのようなデザインパターン技法が注目された。現代では多くの障害が取り除かれた結果、変化するソフトウェアによる課題解決という核心に資するドメイン駆動デザインパターンが注目されている」という表現を読んで非常に納得できたことがあり、以降システム設計や保守に加えてドメイン駆動についても学んでいます。

今回の転職で「ドメインの課題を噛み砕いて実行可能な計画に落とし込む」ことを学び、観察し、実践する機会を多く得られそうなので、業務を通じて理解を深めていきたいと考えています。

入社2ヶ月時点での所感など

1. 異業界スタートアップで働いてみて

大企業向け基幹システムを提供するメガベンチャーから医療機関向け基幹システムを提供するスタートアップに転職したわけですが、業務内容自体は意外と変わりませんでした。少なくともベンチャー・スタートアップでは、自分で課題を見つけて周囲を巻き込んで解決していく「問題解決の核」としての働き方はかなりポータブルなんだなと感じます。

業界が変わって大きく違ったのは顧客の求めるものでしょうか。前職だとROI(情報投資効率)がひとつの大きな指標だったのですが、医療機関の電子カルテやレセプトコンピュータ(レセコン)だとまた違った話になるようです。またユーザがかなり多様なため、業務想定やユーザ体験設計などの難度が一段上がった気がします。

2. OSS開発ないし専門性と直接の関係がない役割へ転職して

静的解析関連のOSSを長年やっていることもあり静的解析サービスの企業はよく検討していたのですが、ビジネスモデルの問題かどうしても魅力的なポジションがなく、ここは早い段階で諦めていました。 そのため静的解析と直接の関係がない会社に入ったことは特に気にしていません。

またヘンリーはOSSを積極的にはやっていませんが、OSS経験があることは評価してくれていましたし、実際ここ2ヶ月でgradle-gcs-build-cache, tfmigrate, tfaction, gcs-proxy-cloud-runといくつかの貢献を業務として行えました。のでこれも気にせず正解だったと思います、まぁ前職でも言うほど業務ではOSSやってなかったですし。

3. 駐在員としての経験がフルリモートにマッチしているかも

オンライン会議前にアジェンダをまとめておくとか、とにかくドキュメントに落とすとか、カレンダーに不在予定含めすべてのスケジュールを入れておくとか、ソフトだが回りくどくない表現をするとか、Working Out Loudするとか。駐在員として本社の人とどうコミュニケーションするかを突き詰めた結果としてのコミュニケーションスキルはフルリモートで遺憾なく発揮されています。

ちょっと面白いのが「戸田さんはエンジニア以外にもやってることがわかりやすい」「コミュニケーションがいちいち工夫されていて、フルリモートのはずなのに存在感がすごい」というフィードバックをいただくことですね。工夫って……Slack書いてるだけだが?というのは置いといて、もはや意識的にやっていることではなかったので新鮮でした。

VPoEお墨付き↑の存在感🤗

まとめ

とりあえず2ヶ月を走りきり、今後もやれそうな感覚を掴めています。やるべきこと・やれることが山積みの状態ですが、健康に気をつけつつベストを尽くし、挑戦を続けていきます。

ちなみに今回の転職はLaprasさん経由でした。今週インタビューも掲載いただいておりますので、よろしければご覧ください:

note.lapras.com

あとド定番で恐縮ですがWe're hiringなので、戸田が選んだチームや社会的課題ってどんなのだ、というのが気になる方は採用情報も見ていただけると嬉しいです:

jobs.henry-app.jp

Gradleのjvm-test-suiteプラグインがテスト周りの定型コードを排除するのに便利そう

Gradle v7.5の時点ではまだIncubating段階の機能ではあるのですが、Gradleの新しいプラグイン jvm-test-suite がいい感じなので紹介します。

docs.gradle.org

解きたい課題:サブモジュールや統合テストが出てくるととたんに面倒になるビルドスクリプト

Gradleは設定をDSLで記述するので基本的には何でもありなのですが、やはり定形コード(boilerplate)は少ないほうがビルドスクリプトの見通しも良くなります。もちろんGradleは「設定より規約(Convention over Configuration)」の考えを持っているため、ある程度は空気を読んでSourceSetやTaskを自動的に生成してくれます。しかしテスト周りにおいてはこうした自動生成は十分ではなく、次に挙げるような課題がありました:

  1. サブプロジェクト全てに対して実行したタスクのレポートを統合するのが面倒。ここでレポートとは単体テスト実行結果、ないしJaCoCoによるカバレッジ測定結果などを指す。
  2. スモークテストや統合テストといった単体テスト以外のテストを実行する際に、SourceSetやConfiguration(依存管理)、Taskなどを自分で書かなくてはならない

例えばJaCoCoのカバレッジ測定結果をSonarQubeに渡すために、SpotBugsプロジェクトでは以下のようなタスクを自分で書いて実行しています。一部はプロジェクト固有の課題を解決するためのコードですが、複雑さが伝わればOKです:

task jacocoRootReport(type: JacocoReport) {
  description = 'Merge all coverage reports before submit to SonarQube'
  def reportTasks = project.getTasksByName("jacocoTestReport", true).minus(rootProject.jacocoTestReport)
  dependsOn reportTasks

  executionData.setFrom reportTasks.executionData
  sourceDirectories.setFrom reportTasks.sourceDirectories

  // Only enable class directories related to non-test project
  classDirectories.setFrom files(reportTasks.classDirectories).filter {
    !it.toString().contains("-test") && !it.toString().contains("Test") && !it.toString().contains("junit")
  }

  reports {
    // JaCoCo SonarQube plugin needs a XML file to parse
    // https://docs.sonarqube.org/display/PLUG/JaCoCo+Plugin
    xml.required = true
  }
}

jvm-test-suiteプラグインの新規性

jvm-test-suite プラグインは java-library など既存のプラグインを導入しているプロジェクトであれば既に有効化されています。既存のプラグイン達とのシームレスな統合を前提に開発されていると言っていいでしょう。

また別種のテスト追加やレポートの統合機能もこれに統合されており、例えば前述のJaCoCoレポート統合は test-report-aggregation プラグインによって提供されますが、 jvm-test-suite プラグインの提供するレールに乗っていれば testCodeCoverageReport というTaskが自動的に生成されます。先述の定形コードがまるっと削除できるわけです。

後述するSourceSetやConfiguraitonの自動生成と合わせて、多くの定形コードを排除するのに役立ちそうです。

jvm-test-suiteプラグインの提供するレール

jvm-test-suiteプラグインを適用する上で注意すべきはひとつ、テストの実行に必要な情報を testing Extensionに集約することです。

詳しい説明は公式サイトに譲りますが、例として spotbugs-gradle-plugin で試している設定は以下のようなものになりました:

testing {
    suites {
        val test by getting(JvmTestSuite::class) {
            useJUnitJupiter()
            dependencies {
                implementation(gradleTestKit()) // Gradle v7.6+ が必要。 https://github.com/gradle/gradle/issues/19849
            }
            targets {
                all {
                    testTask.configure {
                        maxParallelForks = Runtime.getRuntime().availableProcessors()
                    }
                }
            }
        }
        val functionalTest by registering(JvmTestSuite::class) {
            useSpock()
            testType.set(TestSuiteType.FUNCTIONAL_TEST)
            targets {
                all {
                    testTask.configure {
                        description = "Runs the functional tests."
                    }
                }
            }
        }
    }
}

これだけで functionalTest Taskと、その実行に必要なSourceSetとConfiguraitonが自動的に生成されます。useJUnitJupiter(), useJUnit(), useSpock(), useTestNG()など依存やバージョンを一括管理してくれる便利メソッドもある(Gradle 7.6からuseKotlinTest() も増えそう)ため、今後は testImplemenentation などのConfiguraitonを自分で設定する機会も激減しそうです。

まとめ

jvm-test-suite プラグインを利用することで、ちょっと複雑なGradleプロジェクトでは頻出だったテスト周りの定形コードの多くを省くことができます。 サンプルプロジェクトがGradle公式から提供されているので、興味のある方は確認してみてください!

github.com

GitHub Actions 最近のやらかし一覧(2022年夏)

2020年のやらかし一覧 に続いて、最近のやらかしも残しておきます。

PRがマージされたときだけPR番号を取得しそこねる

PR番号を取得するのに GITHUB_REF を使いがちですが、マージされたときだけはマージ先ブランチ名が入ってきてしまうので完全ではありません。

# bad
on:
  pull_request:
    types: [ opened, synchronize, reopened, closed ] # closedイベントも拾いたい
  jobs:
    bad-case:
      runs-on: ubuntu-latest
      steps:
        - run: |
            PR_NUMBER=$(echo $GITHUB_REF | sed -e 's/[^0-9]//g')
            echo "PR番号は${PR_NUMBER}です" # merge時には空文字が入ってしまう

pull_request イベントにちゃんと number が入っているので、これを使用すればOKです。

# good
on:
  pull_request:
    types: [ opened, synchronize, reopened, closed ]
  jobs:
    good-case:
      runs-on: ubuntu-latest
      steps:
        - run: |
            echo "PR番号は${PR_NUMBER}です"
          env:
            PR_NUMBER: ${{ github.event.pull_request.number }}

自分が配布するActionでsemantic tagsを提供しないほうが良いと思っていた

GitHub公式のセキュリティガイドに、クリエイターが信用できるときだけタグに依存して良い=通常はフル長コミットSHAを使って依存せよ、と書いてあります。ので自分を信用するやつなんておらんやろの精神で v1v1.2 のようなsemantic-tagは提供してきませんでした。

ところが同じ公式ドキュメントで、semantic tagsを提供してねと推奨しているんですね。

Add a workflow that triggers when a release is published or edited. Configure the workflow to ensure semantic tags are in place. You can use an action like JasonEtco/build-and-tag-action to compile and bundle the JavaScript and metadata file and force push semantic major, minor, and patch tags. For an example, see this workflow. For more information about semantic tags, see "About semantic versioning."

有名企業でもやらかす世界線で個人開発者を信用するのはやめたほうが良いとは今でも思っていますが、信用するしないはユーザが決めるものなので、Action提供者としては選択肢を残してあげるほうが良さそうです。

13年ぶりにストレングスファインダーをやった

ストレングスファインダー、今はクリフトンストレングス(CliftonStrengths)と呼んでいるそうですが、13年前に新卒入社したときも本を買ってテストを受けたことがありました。

当時は慎重さ・戦略性・規律性・内省・収集心が強みだという結果が出ていました。「あらゆる道のりには、危険や困難が待ち受けていると考えている。日課や秩序正しい計画に従うことを好み、決定や選択を行う時に細心の注意を払う。あらゆる種類の情報を蓄積したり自分の頭の中で考えるのが好きで、知的な討論が好き。」ということで雑に言うと石橋叩いて計画するタイプだったんですね。

さて新しく入社した会社がクリフトンストレングスをまた受けさせてくれました。今回強みとして出た資質は「学習欲・最上志向・収集心・アレンジ・原点思考」でした。内省は7位、慎重さは15位、戦略性は16位、規律性はなんと27位に落ちています。この変化について考えてみたら人生経験がわりとダイレクトに反映されてるかなと思ったので書いてみます。

資質変化の裏側にある人生経験

計画から経験主義へ

「秩序正しい計画に従う」規律性と「決定や選択を行う時に細心の注意を払」う慎重さが落ちて「結果よりも学習すること自体に意義を見出」す学習欲と「一度作り上げた構成にこだわらず、作り変えることをいとわない柔軟性を備える」アレンジが浮上したのは、業務での意思決定において経験主義を採用することが増えたことと関係がありそうです。つまりスクラムの採用と不確実性への理解です。

少なくとも学生時代には、私は「世の中の問いには答えがある」と考えていた節があります。理想のプログラム、理想の情報システム、理想のマネジメント、理想の自分があり、それを探し出してその具体化についてのみ考慮すればよいという考えです。これが正ならば、細心の注意を払って作った秩序正しい計画に従って行動することが最も効率的に理想を実現する方法のはずでした。

実際には答えのない問い、あるいは答えが変わりゆく問いもあります。理想も現状も移ろうので、戦略も継続的に更新しなくてはなりません。そのためには失敗というプロセスから学ぶ準備と姿勢、状況の変化に対応した新しい戦略に作り替えるための柔軟性が必要になります。これを身につけられた13年だったのかもしれません。

歴史・書籍・研究から学ぶ

「過去を調べることにより、現在を理解」する原点思考と「一度作り上げた構成にこだわらない」アレンジが浮上したのは、もともと高かった「あらゆる種類の情報を蓄積」収集心が歴史・書籍・研究から学ぶスタイルとうまくかみ合ったからだと考えています。なお「物事の理由と原因を追求」する分析思考が6位に入ってきたのも関係あるかなと思っています。

歴史・書籍・研究から学ぶというのは、今目の前にある課題や疑問に対して自分の頭で考えるだけではなく、自分の外に情報や発想を求めるということ、自分の向き合っている問題領域について先人が何を考え行動してきたのかを知ることです。

例えばビジネスで出会う課題のいくつかについては、書籍にすでに情報がまとまっていたりします:

また組織がどのように失敗してきたか、だけでも以下のように様々な書籍が出ています:

自分の頭で考えることも重要なのですが、考える材料が揃わないうちに直感ベースで突き進んでしまうと既知の落とし穴に容易にハマるのが人間です。例えばITエンジニアの業務の範疇では、FlickrのDevOpsGoogleのError budgetを知っているのと知らないのとではシステム運用に関する発想が大きく変わるはずと思っています。

単語を知り概念に名をつけるだけでも、考察の幅が大きく広がります。意義や新規性のある意見を持つためにも、まず先例や類似事例について学ぶことは重要ですし。人間や組織がどう考え行動するのかを知ることで人生の意外なところで役立てられるのかなとも思います。

組織とは人だ、ではマネジメントには何ができるのか

内省よりの話が続いたので対外的なところ、マネジメントについて着目してみます。チームとの関係性の築き方に影響しそうなのは、2位の「個人やグループの改善を促す方法として長所に着目」する最上志向と、8位の「各人のユニークな資質に関心を持ちます。異なるタイプの人たちの集団をまとめ、生産性の高いチームを作ることに長け」る個別化です。

実際に生産性高いチームを作れていたかは他の方に評価を譲るとして、尖った個性的な人材をまとめて開発チームを作ること自体は好きでした。残念ながら私は自分大好き人間なので、傾聴すべき場面で自分語りをしたり自分流を押し付けてしまうなどの問題行動もあったのですが。他者の強みを知ること、強みを伸ばすこと、強みが摩擦で損なわれないようにすること、チームの凹みをカバーすることには時間を割き関心を払ってきたと思います。

異なるタイプの人たちの集団をまとめるのに重要なのは、マネジメントの期待を明確にすることだと考えています。最低限の要求を明文化し示すことで、それだけ守れば自由にやっていいのだというあそびが生まれるためです。期待とはアウトプットかもしれませんし、企業文化かもしれませんし、レゾンデートル(存在理由)かもしれません。MVV(Mission, Vision, Value)だとちょっと粗すぎるので、四半期か半年レベルの目標に落とし込む必要があると思います。

課題だと思っていること

経験主義が重要な考え方だとは言え、慎重さが必要なくなったわけではありません。目標を見直し続けるためにどういったデータを残すべきなのか、残したデータをどのように分析するか、失敗したときにどう戦略を切り替えるか、といった細かな内容を「とりあえずやってみて失敗する」前に準備しておかなくてはなりません。

また規律性も同様で、画一的な働き方が不要になった今でも残すべき規律はあります。ITエンジニアで言うならばアジャイルのセレモニーのような、働き方のリズムをつくる仕組みは従来どおり実行していく必要があると考えています。これによって透明性が確保されることで、経験主義が回り始めるからです。

これらはもともと気にできていた部分ですが最近あまりできていないのかなと思ったので、改めて見直していきたいと考えています。

まとめ

日々学習すべきなのは目の前の課題の”正解”ではなく課題解決の基礎体力であること、そのためには過去や外に目を向ける必要があること、マネジメントとして長所に目を注目して期待を伝えることの3点が大切だと学んだ13年でした。

一方で慎重さや規律性も重要なので、かつての強みを手放すことなく活用するべく見直しをかけていきます。

退職エントリ

14年勤めたソフトウェアベンダーを今月末で退職します。私が入社したころは新卒が3年で辞めるという話があって、漠然と自分も似たような感じになるのかもと思っていたので、まさかここまで長く在籍することになるとは想像していませんでした。お世話になった皆様、ありがとうございました。

職場近影(2018年1月)

一生に何度もあるイベントではないので、14年前に立てた入社目的を満足できたのかと、14年を経て自分の何が変わったのかを書いてみます。

私は誰?

手広く働いてきたジェネラリスト寄りのITエンジニアです。研究開発、性能改善、製品開発、要件発掘、品質保証、テクニカルライター、OSPO、セキュリティ、SREなどを色々やってきました。「何やってる人なんです?」と言われてうまく説明できた試しがありません。

OSSプロジェクトではクラスファイル解析ツールSpotBugsSLF4J向け静的解析ツールのメンテナ、actions/setup-javaのdependency cacheの実装もしています。

入社理由は満足できたのか

私が今の会社に入社した理由は3つありました。そしてそれらはこの14年間を通じて満足できたと思います。

  1. ユーザ数が多く、得られるフィードバックの質と量が期待できた
  2. 製品を持ち、それが社会に与える影響が大きいと思えた
  3. 経営陣が提唱する理屈・哲学に納得・共感できた

私は何がやりたいのか、あるいは何がやりたくないのかという話 - Kengo's blog

1について。ソフトウェア提供の形がパッケージソフトウェアからウェブアプリケーションに広がり、リーン開発手法の浸透やクラウドの普及、可観測性(Observability)技術などを通じてソフトウェア開発者が得られるフィードバックは大きく変化しています。一方でソフトウェアの向こうに人がいるのは変わらない事実であり、多くの顧客を抱えつつも個別の顧客と深く関わる機会もあるBtoBビジネスを経験できたのはとても幸運でした。顧客とのコミュニケーションを通じて得た経験は、アルゴリズムやデータ構造に関する知識のように今後のキャリアを長く支えてくれると思います。

2について。ソフトウェアには短いライフサイクルを持つものもありますが、私は自分が思っていた以上に長いライフサイクルを持つものが好きみたいです。レガシーと呼ばれるシステムもそうですし、FindBugsみたいなOSSもそうなんですが、長い実績を持つシステムに手を入れて品質を向上し長持ちさせるための工夫を考えて実行するのが性に合っていました。その点では請負開発やコンサルティングではなく、製品を抱えて育てるパッケージソフトウェアはとても適していたと思います。社会的課題の解決に貢献できている実感も定性的定量的に得られ、長期的にモチベーションを保つ支えになりました。

3について。創業経営者から色々学べたのは事実ですが、上司や同僚からも多くのことを学びました。これは当時の自分の想像を大きく越えた体験でしたし、「組織は人」という価値観を醸成するには充分すぎる体験でした。駐在員も経験し、世代や文化や価値観の違いに起因するコミュニケーションの難しさにも幾度となく直面しましたが、この多様性が多様な市場やコミュニティとの交流において価値を生むこともバズワードではなく体感として理解できた気がします。魚座の星占いでよく「清濁併せ呑む」ってワードが出てくるんですが、アレが組織の競争力と魅力の源泉として必要になる時代なのかなとか思います。

仕事に対する解像度が上がった話

この14年で趣味プログラマからプロプログラマ、そしてプロ開発者へと自己認識が変化したと感じています。

まず趣味プログラマからプロプログラマに自分の意識を変えることに、のべ2年はかかった気がします。プログラミングにおけるアマチュアとプロの大きな違いとしては例外処理や保守性、運用容易性への意識がよく語られます。私の場合は、コーディングをする際に機械だけではなく人にも目を向ける必要があるのだという気付きが大きなきっかけとなりました。コードを勝手にフォーマットしてコミットして迷惑を掛けるとか、コメント内容が古いシステムの発掘をするとか、なんでこんなログがここにあるんだと過去の自分に苛立つとか、めちゃくちゃ性能保守両面が考えられている実装に出会って感激するとか、そういう経験が大切でした。一応社会人になる前から他者のコードを読んだり自分のコードを公開したりはしていたはずなんですが、仕事として成果にコミットすることが私には学習効率を上げるために必要だったのでしょうか。フリーソフトなら「気に入らなきゃ使わなくていいよ」と言えてしまいますし、機能性や互換性よりも書き手の手間を減らすことに注力しがちだった気もします。

そして保守性や運用容易性に気を配るプロプログラマになれてさらに数年、自分の仕事がプログラミングでもシステム構築でもシステム運用でもない、課題解決なのだということに改めて向き合う精神的余裕が出てきました。業務におけるマネジメントの割合が増えてきたこととも無関係ではないと思います。

システム実装はミケランジェロが言う「余分なものを取り除くだけで理想の像が現れる」プロセスとは大きく異なります。似たシステムでもチームが違えば理想も変わりますし、要件が同じでも使える資源や技術が変われば自然と適用する技法も変わります。特に重要な特徴は、チームや資源、技術といったこれらの条件がすべて「時間」を変数としていること、つまり「常に正しい正解」が存在しないということでしょう。以前紹介したトリさんのスライドがとても参考になります。

よって「どのようなシステムを目指すのか」「何が余分なものなのか」をチームで議論することは依然重要ではありますが、それ以上に「よし!私達はこれで行く!プランBはこれ、プランCはこれ!いっちょやってみっか!」という自信と勢い、間違ったときに即時修正するための評価判断手段こそが必要です。そしてこれらは目標設定と戦略と迅速性によって、すなわちリーンな組織と情報公開(組織の可観測性)そして迅速に価値を届ける開発体制によって生み出されます。

この体制の実現はマネジメントとかリーダーシップとかソフトウェアエンジニアリングとかが噛み合うとてもおもしろい課題領域なのですが、この「課題と現状をすべて卓上に並べて明らかにし即時対応する」働き方はとても疲れるのですよね。GitLabのHandbookとかAmazonのWorking BackwardsとかGoogleのデザインドキュメントとか、コミュニケーションを円滑化かつ非同期化する手法はいろいろ知られているのですが、この「疲れる」ことに対する心理的抵抗感をいかに下げるか・チームとして越えるか、言い換えるといかに他のチームひいては顧客の信頼を獲得するかが大切なのだな、そしてプログラマやマネジメントとしての知識や技術そして自分自身の生き様が信頼獲得の武器になり得るのだなということが見えて、初めて「プロの開発者」になれた気がします。

まとめ

技術による社会課題の解決にこだわる会社に新卒入社して多くの学習の場を経験できたことが、自分の人生の大きな財産になりました。 関係各位におかれましては、至らぬところの多い私にお付き合いいただき、ありがとうございました。 新しい職場においても、自らの成長と社会課題の解決に向けて工夫して参ります。

オブジェクト指向か関数型か、という話題に私達はどう接するべきか

私がコードを書くときには「オブジェクト指向でいくか、それとも関数型か?」みたいなことはほとんど気にしていません。特にオブジェクト指向については人によって定義から違うこともままあるため、この手の議論がとても遠回りになることも多いと感じます。

ただきしださんのLT資料を拝見して、もしかしたらまだ需要があるのかなということで、この話題にどう接するべきか考えていることを書いてみます。

どう書くべきかはコンテキスト次第

結論から書くと、どのようにコードを書くべきかはチームや解決したい課題、利用言語や既存資産などのコンテキストによって変わります。 ので「何がオワコンでこれからは何が来る」みたいな議論は、チーム内という限られたスコープでのみ有効なはずです。 チームよりも広い場で議論する場合は、「どういったコンテキストにおいてどのような書き方をするか」のように若干抽象的なテーマが適切でしょう。

言い換えると、コードの書き方において絶対善や絶対悪は存在しないはずと考えています。例えばバッチ処理ないしウェブアプリケーションでは、複数スレッドから同一データを共有することで性能を高めるため、メソッドの戻り値をキャッシュしたりメモ化を施したりするかもしれません。このためにはデータが不変であると便利でしょう。 しかしこうした実装に登場するデータすべてが不変であるべきかというとそうではなく、むしろ可変データによって性能や可読性が向上することだってあるはずです。

私の経験した範囲でいうと、Repository内部で扱うデータを完全に不変にした結果コピーコンストラクタやシリアライズ・デシリアライズが頻出する読みにくいコードになったことがあります。 今ならライブラリの力を借りてビルダーを実装するなどもっとうまくできる気もしますが、単体テストによる品質担保を厚めにしつつ可変データを導入する手もあったはずでした。

トレンドの書き方が良いソフトウェアを届けるのに必須ということはない

OSSから例を出すと、OpenJDKやKotlin, Gradleといった著名なプロジェクトで使われているObjectWeb ASMはオブジェクト指向で書かれており、継承や配列といった今ならまず採用を避けるであろう書き方も頻出しています。また異なる意味を持つ intString も多く登場し、「この文字列はクラス名だっけティスクリプタだっけ?」といった注意を払いながらコードを読む必要があります。一部ではTypeTypePathみたいな型が用意されていて取り回すこともできますが、そのAPIはカプセル化やTellDontAskといった近年プログラマが慣れ親しんだものとは程遠いものです。

この面ではObjectWeb ASMは「プログラマの認知負荷を下げる」トレンドからは大きく離れていると言えます。

ですが、ObjectWeb ASMは事実上オワコンでなく、JVMエキスパートからの支持を集めて止まないわけです。 加速したJavaのバージョンアップにも速やかに追随し、コミュニティからの貢献を受け付けて修正をデリバリするとともに、リファクタリングや性能改善も行っています。今日SonarQubeを見たところではカバレッジ96.6%でした。まさに「質とスピード」を地で行くプロジェクトです。 *1

この面ではObjectWeb ASMは「ユーザに高い品質とかけがえのない価値を継続的に提供する」ソフトウェアの理想像に限りなく近いと言えます。 だいぶ極端な例ではありますが、トレンドの書き方が良いソフトウェアを届けるのに必須ということはないことを説明する良い事例だと思います。

曖昧な定義や由来を明らかにすることが重要

何がいいかはコンテキストによるのでコンテキストを明らかにしないまま議論をするのはやめましょう、というのが私の主張ではありますが、コンテキストを限定せずとも行うべき重要な議論・問題提起はあります。不明瞭な定義や由来に補足をしてただすものがそうです。言葉が曖昧だと議論が噛み合わず、建設的な議論になりません。例えばまさに今日読んだ Value Objectについて整理しよう - Software Transactional Memo はまさにこの貢献をするもので、とても勉強になりました。

定義や由来を明確にすることは、コンテキストが明らかなチームにおいても重要です。 例えばオブジェクト指向だと 2021年の「オブジェクト指向」を考える で指摘されるように、様々な定義が想起されます。 今話しているオブジェクト指向が何を意味しているのか、議論に参加する各々がきちんとすり合わせる必要があります。

まとめ

コードの書き方は結局、チームが望む働きを実現する道具のひとつです。チームの中で合意が取れているか、コードの理解と変更が容易か、APIや性能が利用者にも受け入れられているか……そういった要件に目を向けるべきです。オブジェクト指向や関数型も私達の道具箱に入っている道具のひとつとして、それぞれ尊重して理解につとめていきたいです。

*1:またAPIの認知負荷が高いのも、言い換えれば他のことに特化していると言えます。これは想像ですが、クラスファイルとプリミティブとの架け橋に徹し高い性能を実現することが設計の主目的かなと感じます。認知負荷については、各利用者が自分のコンテキストに最適化されたAPIを設計しそれでObjectWeb ASMを包むことで解決できます。ObjectWeb ASMはその用途が幅広いため、いたずらに抽象化してしまうと特定ユーザにとって使いにくいものになるでしょう。今のVisitorベースとTreeベースのAPIを提供するくらいがちょうどいいという判断かもしれません。

「非nullのint配列」をアノテーションで表すのは `@NonNull int[]` ではない

正解は int @NonNull [] です。な、なんだってー!

本当です。Java言語仕様書にも記載がありますが、配列を修飾する場合は [] の手前にアノテーションを書く必要があります。JVM仕様書に記載の例のほうがわかりやすいかもしれません:

@Foo String[][]   // Annotates the class type String
String @Foo [][]  // Annotates the array type String[][]
String[] @Foo []  // Annotates the array type String[]

組み合わせて考えると、「要素も配列自体も非nullのString配列」は @NonNull String @NonNull [] になります。コレクションは @NonNull List<@NonNull String> みたいにわかりやすいんですけどね。JavaのRecordでは配列を使わないほうが良いという話の時にも思いましたが、Javaは配列周りに非直感的な挙動が多い気がします。

なお配列だけでなく内部クラスでも同様で、パラメータが非nullの内部クラスを要求することをアノテーションで表現する場合は @NonNull Outer.Inner param ではなく Outer. @NonNull Inner param内部クラス名の手前にアノテーションを書く必要があります。これTwitterで11名にアンケートご協力いただいた限りでは、正答を答えられたのは1名だけという難問でした。

他にアノテーション周りで驚く機能としては、 this を修飾する方法も提供されています。receiver parameterと言います。Pythonのように第1引数にthisを書く形です:

class MyClass {
  void method(@Foo MyClass this, String param) {
    // ...
  }
}

いずれもJava8(2014年)からあった機能ですが、自分は今日まで知りませんでした。クラスファイルパーサを書くことが無ければ、このまま気づかなかったかもしれないです。Java、奥が深い。