チームの増加に伴いできるようになったこと、やりにくくなったこと

すがわらまさのり氏:ここから本題ですね。「開発生産性について、上から見るか、下から見るか」ということで、よろしくお願いします。過去に私が登壇したもので似たテーマがいくつかあるので、軽く紹介しておきます。もし気になる方がいれば後で見てください。

前提の共有というところで、先ほどもお話ししたように、私が担当したのは「SmartHR」の基本機能というプロダクトなので、この話をしていきます。

これはSmartHRで最初に作ったプロダクトです。モノリシックアプリケーションで、テストコードを含めてコードが60万から70万行ぐらいあって、今は7チームで開発しています。

どんなふうに人が増えていったかを伝えると、2020年、私が入社した時は4チームありました。次の年に5チーム、次の年に6チーム、2023年に7チームと、年々チームが増加しています。

この中でどうやって開発しているかというと、スクラムを基本としているのですが、スクラムを拡張したLeSSというフレームワークでなんとかうまくやれているというのが、SmartHRの基本機能の状況です。

「実際にチームが増えてどう?」というのは、やれることは実際すごく増えていて、必要な機能をどんどんリリースできる速度は速くなっているし、数も質も良くなっています。

一方で、やりにくいことも増えてきたんですよね。これがこれ以上増えていくとヤバそう、というのが最近の課題です。ここが開発生産性を向上させていくためになんとかしていかないといけない部分です。

例えば、7チームあるのでチームを横断してコミュニケーションを取ろうとするとけっこう大変なんですよね。「自分たちのチームでは、うーん、わからないな。じゃあ隣のチームに聞こうか?」とか、そういうふうに、一段クッションが挟まってしまう状態になるので、ちょっと大変だと感じてしまうんですよね。

あとは、7チームいると、どのチームにどういう知識を持っている人がいるかというトランザクティブ・メモリーを把握するのがけっこう難しくなってきます。古くからいる人は把握しやすいのですが、新しく入ってきた人にとっては、誰がなにを知っているかぜんぜんわからない感じになっちゃうことがあります。

それから、機能やコードがすごく増えてくるので、1人の人間が扱える限界を超えてしまうんです。70万行ぐらいあると、さすがに「どういった機能があるか」とか、「ここをいじったらどこまで影響があるか」とかを1人で把握するのは限界があります。

「ここを変えた時にどういう影響があるか」を調べなければいけないのですが、いろいろなところを参照しているものだったりして大変などといった課題があります。

「長期記憶」「短期記憶」「ワーキングメモリ」

こういう状況を、私たちは「認知負荷が高い」と言うんですよね。僕もすごく言うんですよ。

これをどうしたらいいのかはこれから話します。まずは認知負荷がどういうものかという話と、それに対して私たちがどう解決してきたか。解決に向けてなにをどう実践したかを紹介していきたいと思います。

「こういうことをやりましたよ」という話を伝えるのは簡単なのですが、もう少し仕組みを体系化して、認知科学の観点から整理して伝えるのを今日の目的としています。

そうすることで「こうしました」というHowの部分ではなくて、「知識として解決すべき課題はこうだよね」という概念を伝えられるかなと、今日聞いているみなさんも持って帰れるものになるかなと思っています。

ようやく本題です。そもそも太郎、「認知負荷とは何か?」。みなさん、認知負荷がなにかわかりますか? 僕は正直、雰囲気で使っていたんですよ。よくよく調べてみると、認知科学で提案されてきたモデルの1つの話だと「Wikipedia」に書いてありました。

認知負荷の話をする前に、もうちょっと話があって。認知負荷は心理学における記憶の分類において、特定の記憶に対する負荷を表現したものです。なので、まずは記憶の種類について伝えます。

(スライドを示して)これが心理学における記憶の種類です。ほかにももっとあるのですが、よく言われるのは長期記憶と短期記憶とワーキングメモリの3つです。みなさんも聞いたことがあるかなと思うのですが、認知負荷は、ワーキングメモリに対する負荷が高い状態を指しているんですね。

この記憶について、一つひとつ伝えます。長期記憶はいわゆるハードディスクやSSD的なもので、ちゃんと身に染みているものです。慣れ親しんだプログラミング言語だと、予約語や構文というのは、なにも考えずにスッと出てくるものです。

次に短期記憶です。短期記憶はいわゆるメモリと呼ばれていて、見た瞬間に記憶したものです。例えばコードリーディングしていて「このメソッドの名前はなんだ」とか「引数はなんだ」とか「こういう変数名なんだよね」とか、そういったものです。

次がワーキングメモリで、短期記憶と少し似ているので同じように使われる場合もあるのですが、長期記憶や短期記憶を使って脳が処理している間に覚えているものと呼ばれています。

例えばコードリーディングをしていて、特定の変数にどういう値が入っているか。「今、stringにSmartHRという文字が入ってきた」と。それに対して、「あっ、ここでは文字を逆順にしているんだな」とか「大文字にしているんだな」とか。

変数の値は、常にアップデートされているものを考えてなきゃいけないですよね。そういった時に使っているものがワーキングメモリです。

(スライドを示して)ここで突然の宣伝ですが、今話していること。長期記憶、短期記憶、ワーキングメモリについては『プログラマー脳 ~優れたプログラマーになるための認知科学に基づくアプローチ』という本に書いてあるんですよね。

この本は、熟練したプログラマーとそうではないプログラマーでどういった違いがあるのか? 例えばコードを読む時に、熟練している人は速く読める、そうでない人は時間がかかりますが、「それってどういう違いがあるんですか?」という話や「どういうふうに学習するとその差分を埋められるんですか?」みたいな話が書いてあるんですね。

(この本は)メチャクチャおすすめで、「自分が無意識にやっていることはどういったことか?」とかもそうだし、「経験の浅い人にこう教えたらいいよね」というところでも役に立つので。本当におすすめなので、みなさんぜひ買ってください。

認知負荷とは何か?

では戻ります。ワーキングメモリと認知負荷の話です。ようやく認知負荷の話にたどり着きましたね。認知負荷はなにかというと、ジョン・スウェラー氏の認知負荷理論によって述べられたものです。

これはなにかというと、学習時にワーキングメモリへの負荷が高すぎると学習ができない。覚えることがたくさんありすぎて、ちゃんと学習できないという話です。より適切に学習できるようにするためにはどういった負荷があって、減らしたほうがいい負荷はどういうことなのかを分類したものです。

(スライドを示して)それがこの3つに分類されています。課題内在性負荷と課題外在性負荷、そして学習関連負荷です。

課題内在性負荷というものは、取り組むタスク自体の難しさを指しています。例えば、これ自体を減らすこと、なくすことはできないのですが、課題自体を小さくして扱いやすくすることはできます。

ドメイン知識が非常に要求されるコードを書く必要がある場合でも、例えば一から十までのすべてを知らなきゃいけないのではなく、例えば小さく分割して3つに分けられたら、その一つひとつはもう少し扱いやすくなりますよね、という話です。

次が課題外在性負荷ですね。これはタスクの外部要因による難しさです。これは慣れによってもけっこう変わってくる部分があります。例えば、初めてRubyのプロダクトに関わるとなると、「Rubyのコード、難しいよね」とか、Pythonのリスト内包表記とかに慣れていない人は、「何だこれ?」となったりします。

そういったものだったり、コード内でコメントが古いとかもありますよね。最初の頃は「こういう理由でこういうコードがあった」と、コメントにもそう書いてありました。でも、今はちょっと別の理由もあってそのコメントが正しくないとか。そういうのは、慣れちゃえば気にしなくてもよくなるので、あまり直ってないけど実は残っていて、初見の人から見ると毎回困る、みたいな。

あとは、コードの書き方が揃っていないとかですね。これもプログラミング言語によってお作法的な書き方やイディオムがあると思いますが、そういったものが揃っていないと、「あれ、なんでこういう書き方しているんだろう?」と思ったりしますよね。

そういう課題自体、課題を解決するものではない部分でワーキングメモリを消費してしまうようなものを、課題外在性負荷と呼びます。

次が学習関連負荷です。これはタスクを遂行する上で必要な負荷ということで、高い状態にするのが望ましいんですよね。高い状態でタスクに取り組めるように課題内在性負荷と課題外在性負荷を取り扱ってあげる必要があるという話が書いてありました。

繰り返しになりますが、課題内在性負荷というのは、扱いやすくするために、小さくてかわいい状態を目指すといいです。

課題外在性負荷は、極力小さい状態を維持できるといいです。余計なことを考えなくてもよくなるとうれしい、という話ですね。

最後が学習関連負荷で、これを高い状態が維持できるようにほかの2つの負荷を減らしていきましょう、と。 認知科学の観点からこういった話がありました。

認知負荷を減らすための、ボトムアップのアプローチ

「これをプロダクトに対する改善活動に体系化できないだろうか?」というのが本題です。

最初に話したとおり、「認知負荷、高い」って言っちゃうんですよね。なので、認知負荷を減らしたいんですよ。では、どのようにアプローチすると良いか。

まずは、課題内在性負荷と課題外在性負荷と学習関連負荷、この3つがありますよね。この3つのうち、過去に私が取り組んだ施策がどれに該当するかを振り返りながら紹介していきたいと思います。

(スライドを示して)例えば、ボトムアップでのアプローチ例で、これは過去にテックブログでも書いたのですが、はてな社での取り組みを参考にして、自分のチームで、チーム内にテックな話題をする場を作ったという話ですね。

これが先ほどの3つの認知負荷の中でいうと、どれにどう効くのかを考えてみました。テックな場は何をしているかというと、SmartHRの基本機能は7チームあるという話をしたのですが、その7チームの中で、自分のチーム以外のプルリクは、けっこう意識しないと見られないんですよね。なので、自分のチーム以外のプルリクを見ることが1つ目です。

それから気になっていること、例えば「このコメント、嘘ついているな」とか「この変数、実は要らないんじゃない?」とか「if文のネスト、不必要に深くなってない?」とか、積み重なって開発していくと、小さい改善点でも直せる場所はいろいろあるんですよね。そういったものを相談したり直したりすること。

あとはテックな雑談をするという、3つのテーマでチーム内で話してみました。

例えば「自チーム以外のプルリクを見る」ですね。これは実際どういったことにつながるかというと、コードと触れ合う時間を増やすことができるんですよね。もちろん、ふだん触っているコードもあるので、触っている時間自体はあると思うのですが、ほかのチームがやっているところは、自分が触っていない部分だったりしますよね。そういったところに触れる時間を増やすことで、長期記憶に留まる機会を増やせます。

そうすると、初見のコードや特定のイディオムが使われているとかそういったことが理解しやすくなっていきます。

例えば、ある日突然別のタイミングに、自分の知らない、ふだん触っていないコードを触らなきゃいけなくなった時も、見て、なんとなく覚えていれば、つまずきが減りますよね。というところで、課題外在性負荷を減らすために一役買ってそうだなとわかります。

次は、気になるコードをリファクタリングするという話です。リファクタリング案を出すことで、コードのパターンを覚えていけます。そうすると長期記憶に留まりやすくなって、さらに、実際にリファクタリングすることで、不揃いなコードや深いネストの解消ができます。

コードの書き方が統一されたり、余計なコードを見なくて済むので、課題外在性負荷を下げることができますね。

あと、テックな雑談をすることです。これは特になにもないんですが、いい話ができれば記憶にも良い影響があるかもしれないですね。

今まで説明した話は、僕ら個人の感覚としては良いと思う行動を推進していたのですが、認知科学の観点から見ても、「こういう効能がありそう」ということがわかります。そういった目的を持つことで、よりソリッドな取り組みができるかもしれないです。

認知負荷を減らすための、トップダウンでのアプローチ

次に、トップダウンでのアプローチですね。課題内在性負荷。本質的な難しさを扱いやすい粒度にするためには、相応のコストが必要になります。

例えばデータベースのリファクタリング、リアーキテクティング、モジュラーモノリスとかマイクロサービスとかです。こういったものはトップダウンでやる必要が出てくるのです。

例えば、SmartHRでは、リアーキテクティングによって考慮事項を減らす作業をしています。SmartHRのデータベースモデルのところに非常に複雑になっている部分があったのですが、そこを少し切り離すことで、タスクの本質的な難しさを減らしたり局所化することを実現できました。

これは2022年に少し発表した内容で、ここらへん(の内容)が実を結んだという感じですね。

最近気づいたこととしては「Four Key Metricsというのは、実は課題外在性負荷の指標になるんじゃないだろうか?」ということを、突然思い至りました。

これ(Four Key Metrics)は『LeanとDevOpsの科学[Accelerate] テクノロジーの戦略的活用が組織変革を加速する』に書いてある4つの指標のことです。

この4つの指標を言い換えると、デプロイが簡単、レビューが早い、不具合を起こしにくいコードベース、障害があってもすぐ戻せるということです。

「これらが整っていない開発体制だったらどうか?」と考えると、これらはメチャクチャ慎重にやらないといけないんですよね。タスクの本質的な課題に立ち向かえないことになるので、このあたりが解消されていることは、課題外在性負荷が少ないということなんですよね。

「こういった課題を見つけるには?」というところで、最近は、「Findy Team+」というものを使うようにしています。まだきっちり運用に乗せられているわけではないのですが、実際に数値を取って、チームでどうやって見ていったらいいかを模索しています。ボトムアップで課題の発見や改善支援ができそうです。

あと、プロダクト全体として見ていくところで、例えばデプロイに時間がかかるといったものはトップダウンでガッと動かしたほうが良い場合もあるので、そういったところはトップダウンで意思決定できる支援ができると良いかなと考えています。

認知科学の観点から課題を整理すると漠然とした「つらい」の輪郭が見えてくる

まとめです。認知科学の観点から課題を整理すると、漠然とした「つらい」とか「こうしたほうがいい」ということに対して輪郭が見えてきます。Four Key Metricsを使うと、「つらい」という部分を定量的に捉えられて良いですね。

課題が見えてきたら、どう適切に対処できるかが検討できます。あとは、課題の大きさや難易度によってボトムアップとトップダウンを使い分けてやると良いですね。

以上で話は終わりです。