今回のテーマは「動的型付け言語と大規模開発

まつもとゆきひろ氏:こんにちは。まつもとゆきひろです。Matzチャンネル、18回目になりますね。今日は前回の続きで、「動的型付け言語と大規模開発」について話そうと思います。

本当は前回放送リリースした次の日ぐらいに放送できるようにと思っていたんですけど、意外と忙しくてですね(笑)。

今度、フィンランドのヘルシンキで、「Euruko」というカンファレンスが開かれるんですけれども、まだ物理で海外旅行する気にならないので、キーノートを録画しましょうという話になって、そのキーノートの準備をして、スライドを書いて、英語の講演を録画するみたいな作業をしていたら、あっという間に時間が経ってしまって、「Voicy」が後回しになってしまったというのが正直なところですね。でも、前みたいに2ヶ月も空けたりしないので、今後ともがんばろうと思います。

先に決めた型をガイドにプログラミングをしていくスタイルもある

最初に、前回は静的型言語と動的型言語みたいな話と、動的型言語には未来があるみたいな話をちょっとしたんですけど、話し忘れていたことがあって。

静的型言語を書く人たちの中で、プログラミングスタイルとして、まず先に型を決めてそれをガイドにしてプログラミングを組み立てていくというスタイルを取る人がいます。そういうプログラミングスタイルがあるんですよね。

関数型言語とかが特にそんな感じなんですけど、そういうスタイルの場合は、静的型の存在を前提にしたスタイルなので、Rubyみたいな宣言のない言語でエミュレートしようと思っても、静的型のガイドがないのでつらいんですよね。

そういうスタイルの問題は当然あるということは、私も認識しているということをきちんと言っておかないと、関数型キャンプの人たちに対して喧嘩を売っているように聞こえかねないので、そのことははっきり言っておかないといけないなと思っていたんだけど(笑)、前回の放送の原稿に入れるのを忘れていて。

各方面で、もし誤解がある方がいらっしゃれば先に謝っておきますが、Rubyでそういうスタイルを取ってプログラムする人はいないと思うので、Rubyが対象にするプログラミングの範囲内では、型宣言のないプログラミング、かつ、静的型のベネフィットをツールとかの支援によって実現できるよというのが、前回言いたかったことなんですね。

2回目の今回は、「じゃあ、大規模開発はどうなの?」みたいな話をちょっとしていこうと思います。

どんな言語を使うかよりもベロシティを達成することのほうが大事

ソフトウェアを開発していると、どんどん規模が大きくなることは必然的ですよね。規模が大きくなるとだんだんつらくなるわけですよ。

特に、静的な型チェックとかがあると、モジュールみたいなものが型定義によってきっちり書かれるので、その分切り分けが楽になるという側面があるのは否めないですよね。

Rubyみたいなふんわりした言語だと、きちんとドキュメントに書かないと、界面がふらふらと変わってしまいがちなので、それもきちんとテストとか書かないと……テストは書かないと駄目なんですけども(笑)。

でも、ゆるふわで開発していると、モジュールの界面もふんわりしてしまって、規模がデカくなるにつれてつらくなるということは確かにありがちだと思うんですね。

もともとこのテーマを選定されたクラウドサーカスのチームのみんなもそのへんに不安があったり、今開発しているソフトウェアがだんだん規模がデカくなるにつれて、「本当にこれで大丈夫だろうか?」みたいに思ったというのがきっかけだと思うんですけれども。

私、思うんですけど、大規模開発ってそもそも人類にとって難しいと思うんですね。静的な型チェックも、大規模なものに対して、「なんとか対応するにはどうしたらいいか?」みたいなアイデアのうちの1つだと思うんですけれども、そもそも避けることができるなら避けたいなというのが正直なところなんですね。

何かというと、ソフトウェアの規模がデカくなると、ある程度必然的にチームの規模もデカくなる傾向があって、チームの規模がデカくなると開発速度が下がるんですよね。ベロシティって呼んでいますけれど。

そうすると良くないんですよ。関わっている人がたくさんいると調整しなくちゃいけないことや、コミュニケーションのコストがどんどんかかっていく。そうすると、ソフトウェアを作っているんだか調整しているんだか会議しているんだか、だんだんよくわからなくなってくるわけですね。それは駄目かなと思うわけです。

作りたいものに対していかに早く完成させるかというところがソフトウェア開発においては正義で、それを達成するためのツールであれば、たとえRubyを使ってもTypeScriptを使ってもHaskellを使ってもRustを使ってもいい。どんな言語を使うかよりも、ベロシティを達成することそのもののほうが重要だと私は思うんですね。

それを達成することを最初の目標にしなくちゃいけないと私は考えますので、チームサイズが大きくなればなるほど調整が大変になりますし、コミュニケーションに関わる人が増えれば増えるほど、文書化の量や、あるいはルールで縛らなくちゃいけないものが増える。複雑になるんですね。

なので、一遍に考えるべきことを減らし、ソフトウェア開発のために構成要素みたいなものを減らして、ベロシティを上げるのが大事じゃないかなと思うんですね。

ソフトウェア開発のためにはベロシティを優先しなくちゃいけない

Smalltalkを作ったアラン・ケイ先生……尊敬しているので先生と呼びますけど(笑)、アラン・ケイ先生が、「あらゆるソフトウェアは1万行以内で書けるべきだ」とおっしゃったと聞いたことがあります。

裏を取っていないので、本当にそう言ったかどうかはわからないんですけれども(笑)、彼によれば、OSであろうと言語処理系であろうとライブラリであろうとWebアプリケーションだろうと、なんでも1万行以内で書けるべきだと。それ以上のものは、分割するとか、なんらかの方法によってもっと小さいものにならないといけないとおっしゃったんだそうです。

私もだいたい賛成するところがあります。そうは言ってもRubyの処理系は1万行どころじゃない量があるんですけど。1万行ぐらいだったら、1人の人が細部まで含めて理解できるんだけど、これが10万行、100万行になってくると、ちょっと1人では管理できなくなる。

1人で管理できなくなるとチームを導入しなくちゃいけなくなるし、調整が必要になるし、長大なドキュメントを書かなくちゃいけなかったり、コミュニケーションのためのコストがかかりすぎたりするんじゃないかなと思うわけですよ。

ソフトウェア開発のためにはベロシティを優先しなくちゃいけない。ソフトウェアアーキテクチャを考える時にも、いかに膨れ上がるソフトウェアの複雑さと対決するか、みたいなことが重要になります。

例えば最近、モジュラーモノリスみたいな言われ方をするものも、分割することによって複雑さを抑えようとか。マイクロサービスもそんな感じですよね。そんな感じになっていくんじゃないかなという気がします。

「線形的」「指数関数的」2種類の複雑さ

複雑さって言っても、実際は2種類の複雑さがあるんじゃないかなと思っています。1つは、線形な複雑さ、つまり、同じようなレベルのものが並んでいくタイプの複雑さ。それから、指数関数的な複雑さ。組合せ爆発が起こりやすいような複雑さ。この両方あると思うんですね。

線形な複雑さのほうはどうってことないんですよ。単に線形に要素が増えていくだけなので。だけど、組み合わせが発生するような複雑さというのは徹底的に避けなくちゃいけないと思います。

Rubyを作っていてもそうなんですが、「あの機能とこの機能が組み合わさった時に何が起きるの?」みたいなことを考えなくちゃいけない複雑さは、だいぶつらいんですね。

なので、ソフトウェアを作る時には、この機能をこういう作り方をしていったらソフトウェア開発のベロシティが上がるかどうか、あるいは下がるかどうかを基準に考えていかないといけないかなと思うんですね。

もしソフトウェアの複雑さを十分に制御できていれば、なにか問題があった時に、最悪書き換えればいいという発想になるわけです。だけど、過去の資産が積み上がりました。よくわからないコードが100万行ありますというソフトウェアだと、書き直す気にならないですよね。

そういう意味で言っても、ソフトウェアの開発においてベロシティを中心に、評価軸としてアーキテクチャだったりチーム編成だったりを組み立てていく時に、多くの問題を回避できるんじゃないかなと思います。

誰かが、「速度はすべての問題を解決する」というようなことを言っていました。ランタイム・パフォーマンスもそういう傾向があるんだけど、私としては、ソフトウェア開発の速度、ベロシティも多くの問題を解決するんじゃないかなと思っています。

もう1つ、多くの問題を解決する要素があって、それはお金です。それはまた別の時に話そうと思います。

段階的に手綱をつけていくしかない

じゃあ、どうしようかという話をするんですけども。ゼロからアプリケーションを作る時には、「ベロシティを中心にチームを編成しましょう」「ベロシティを中心にアーキテクチャを設計しましょう」みたいなことになるわけですが、すでに巨大になってしまったアプリをどうするかという問題があって、それはそれで重大です。

これは、「Voicy」みたいな音声メディアで軽々しく答えられるようなことではないような気がするんですね。

あえて言えるのは、段階的に手綱をつけていくしかないということです。簡単に手を下せないからこそ巨大なわけですが、テストを書いていくとか、個別にリファクタリングしていくとかですね、アーキテクチャの変更を検討して、機能の塊ごとに分割してモジュラーモノリスにしていくとか、ライブラリ化でGemにして分離するとか、あるいは、機能ごとに大きく分割して分割統治していくというかたちを取るのがいいんじゃないかなと思います。

いずれにしても、可能であればソフトウェアを作る最初の時点からベロシティを考えるのが大事で、すでにあるものについては、段階的に少しずつ直していくしかないというのが正直なところですね。

ベロシティをケアするために生まれた機能「Hotwire」

「Ruby on Rails」のバージョン7から、Hotwireという機能が入ったんですけれども、これも、たぶんベロシティをケアしているからこそ生まれたテクノロジーなんじゃないかなと思います。

2019年の「RubyWorld Conference」というイベントで、DHH(デイヴィッド・ハイネマイヤー・ハンソン氏)、Ruby on Railsを作った人にインタビューする機会があったんですね。

その時、私は、フロントエンドとバックエンドの分離に対してすごく問題意識を持っていたので、彼に聞いてみたんですよね。

最近はね……最近って言っても2年前ですけど(笑)、フロントエンドをJavaScriptで書いて、バックエンドをほかの言語、例えばRuby on Railsみたいなので書くことが増えているんだけれども、そこに分断があるわけですよね。「その分断についてどういうアプローチを取りますか?」って聞いてみたんですよ。

当時の私は、例えばバックエンドをWeb API化して、分離することによって、フロントエンドとバックエンドの分離をもうちょっときれいにすることによって、問題を解決していくんじゃないかなと思っていたので、「どういうふうにするの?」って彼に聞いてみたんですよね。「APIで切り分ける手もあるよね」という話をしていたら、その時に彼は、ちょっとほのめかすみたいな感じで、「私に考えがある」って。「もうすぐ発表する」って言ったんですよね。何だろうなと思っていたら、その数ヶ月後にRails 7が出て、Hotwireがついてきたんです。

Hotwireっていうのは、今までの、トラディショナルなRuby on Railsアプリケーションを書きながら、なんとなくシングルページアプリケーションのような、動的なアップデートみたいなものができるという謎テクノロジー……謎じゃないんだけど(笑)。

JavaScriptを1行も書かなくても、理論的にはシングルページアプリケーションが書けるというアプローチなんですね。

非常に興味深いことで、昔は、Railsアプリケーションは例えば、「15分でWebアプリケーションを作りました」みたいなデモをしていたわけなんですけれども、極端な話、現代的なシングルページアプリケーションを15分で作ることができるようになるという希望が、これによって与えられたということだと思います。

ソフトウェア開発の大概の問題はベロシティを高めることで解決する

というわけで、もともと「動的型付け言語と大規模開発」というテーマでお送りしていますが、結局は言語とかはどうでもよくて、複雑さを制御してベロシティを高めることによって、ソフトウェア開発の大概の問題は解決するという結論になりそうな気がします。

その時に、ベロシティが低いからRubyは使い物にならないというのは嫌なので、各種ツールや、言語そのものの改善とかによって、Rubyの開発の生産性、あるいはそれによるソフトウェア開発のベロシティを上げることにこれからも注力していきたいなと思います。

それでは、18回目の放送は以上になります。次のテーマは、あまり思いついていないんですが、また思いついたら放送しようと思います。ありがとうございました。それでは、また。