エッセイ
プログラマー

ソフトウェアを書くことについて私が学んだ7つの教訓

次の記事

お金の貸し借りメモアプリ「よろペイ」、運営元が4億円調達——ICO関連事業の展開も視野に

それは進行中だ。ちょっとずつ、そして少しずつ、私はエンジニアからある種の…マネージャーに変身している最中だ。ああ、誤解しないで欲しい。私は今でも毎日コードを書いている。しかし気が付くと分析とディスカッション、ミーティングや電話、より高いレベルの意思決定、チームの編成、そして戦術ではなく戦略の策定に費やす時間が増えている。

もちろん、これは悪いことではない。より高いレベルの決定は、個々のクラスおよび機能の詳細よりもはるかに大きな影響を与えることが多い。チームをより生産的にすることは、自分自身をより生産的なものにするよりも、ずっと高いレバレッジ効果を持っている。しかし、私は自分がコードを書いた日々から、いくばくかの教訓を得てきたものと思いたい。個人的にはそれらが、私のマネージャー業の知恵として転換されることを望んでいる。そして、ここでそのいくつかを共有する図々しさを読者に認めて貰いたいと願う:

1. ルールはない。公案があるだけだ。

例を挙げてみよう:DRY(Don’t Repeat Yourself:同じことを繰り返すな)という奴を考える。これはソフトウェアの基本的なルールとしてとてもよく理解されているため、しばしば意思決定を正当化するために使われる:「XをしたのはDRYの原則に従ったからだ」といった具合だ。それは理に適っている筈だ。そう思うだろう?さて、同じことをするコードの断片が2つ以上あったとしよう、それは無駄だし、さらに片方を修正する必要があるときには、おそらく他の部分も修正する必要がある。そしてその修正を忘れることがあり得る。こうなると同期が崩れ奇妙なバグに遭遇し…そして…。同じことを繰り返さないようにするのは明らかなことだ。

だが、それだけではない。その規則を適用するようになって数年後、その普遍的な適用性について不思議に思い始める者が出てくる。いま同じコードブロックを含む2つのメソッドがあるとしよう、そこでその共通ブロックを切り出して別の関数とする。そうしたメソッドが、発散するように進化を始めることは珍しいことではない…そこで関数に引数を追加したり、結果にフラグを付け加えたりするようになる…そしてこれからやってくるコーダーたちはみな、その別の関数に対する、呼び出し側の事情による特定の引数と戻り値を理解するための、認知的オーバーヘッドを強いられる。…そしてようやくここに至って、もし昔「同じことを繰り返して」それぞれのコードブロックが自然に成長するのに任せていたなら、現在得られているコードは遥かにシンプルで直感的なものになっていただろうということに気が付くのだ。

ではDRYが悪いということだろうか?もちろんそうではない!DRYは正しい…通常は、…適切な状況下では、…まあ、たぶん。私の個人的な経験則は「同じことを1度繰り返すことはOK、それ以上繰り返すことはよろしくない…しかしそれは状況によって異なる」というものだ。なにしろ全てのものが状況に依存するのだ。DRYの目的はDRYではない。もしそうだと思っているのなら、若者よ、まだ多くを学ぶ必要がある。DRYの目的は、DRYについて考えさせるようにすることだ。ルールはない、公案があるだけだ。

(繰り返すが、私はここではソフトウェアについて話している。私の経験では、ハードウェアはもっとしっかりしていて、「これがルールだ」というものを持っている。これが私が電気工学を離れてソフトウェアの世界にやってきた理由だ)。

コンピュータサイエンスの世界で、私が気に入っている2つの「原則」について考えてみよう。まず1つめ:「別の抽象化の層を追加することで解決できない問題は、コンピュータサイエンスの中は存在しない」これは本当だろうか?まあ、文字通りの意味ではない。では現象学的にみて、多くの場合に真実だろうか?まあ、実際には、正しいと言えるだろう。ということは抽象化が、どのような問題を解く際にも正しい方法なのだろうか?いや、そんなことはない。それは公案なのだ。よく考えてみよう。

そして次が私の昔からのお気に入りだ:「最適化の第1法則:最適化するな。最適化の第2法則(エクスポートのみ): まだ最適化するな」。もちろん、これは明らかに公案だ。自分自身を法則として参照している。コードをより速く走らせる時だろうか?いいえ。コードをより速く走らせる時だろうか?いや、まだだ。これは何を意味するのだろう?それは、時間、複雑さ、認知的なオーバーヘッド、具体的な結果、全体的な目標、人生の意味、人間の存在の目的を考えることを意味する。それが意味することは:「瞑想せよ」ということだ、若者よ。ただし、それほど長い時間ではなく。何しろ仕事が控えているのだから。

2. 信頼することで信頼を獲得する

これは特にマネージャーにはよく当てはまるものの、マネージャーだけに当てはまるというわけではない。信頼こそがあなたの持つ唯一の価値なのだ。なので、もしあなたの公平性、判断、理解、誠実さなどが信頼されないとするなら、組織の人たちはあなたを欠陥人格とみなし無視するようになる。あなたが有能ではあるが信頼できない開発者の場合は、何らかの価値はあるかもしれないが、あなたの下す決定全てを精査するために使われるコストによって、生み出される価値も随分間引かれることになるだろう。

しかし、より大きなポイントは、チームは互いに信頼する必要があることだ。ナターシャが「そのチケットを担当します」と言ったら、彼女がそうするだろうということを信用しなければならない。もし「ピーターは締め切り前にビルドを終了させることができる」と言うならば、それを本当であると信じなければならない。もし誰かが「聞いてくれよ。すごいアイデアがあるんだ」と言ったなら、チームはそれを真剣に受け止め敬意を持って扱う必要がある。もしそれがある種イカれたアイデアだったとしても。

信頼を築き獲得するには、どのようにすれば良いのだろうか?答はとてもシンプルだ:まずあなたが信頼すること。この新しいライブラリを学んで月曜日までに統合できるという人を、あなたが信じることだ。家族の用事で、明日は早退するので、明日のスタンドアップミーティングには出席できないという人の言葉も信じなければならない。厳しい納期の1ヶ月前に、燃え尽きを感じ始めているので1週間の休みが欲しいという人を信頼しなければならない。難しい問題に挑戦したいという若い開発者を信頼しなければならない。

あなたがいつも正しいとは限らない。時には、実際に悪意のある行動をとるものもいる。そのような時にはそうした悪意ある人びとを暴き、迅速にプロジェクトから放逐しなければならない。そして、成功するために誠実に努力する人たちを信頼して…彼らが失敗してしまうこともあるだろう。しかし、直感には反するかもしれないが、これは通常長期的には勝利なのだ。そうした人びとは、あなたの信頼を覚えているので、それを利息付きで返そうと、できることなら何でもしようとするだろう。

3. シンプルであることは、エレガントであることよりもはるかに重要だ

いや、あなたが言いたいことはわかる。私だってタイトでエレガントなコードは大好きだ。また、抽象度のレベルが何層にも重ねられた柔軟なフレームワークが大好きだ。そのままで、いかなる変更要求も受け止めて扱えるような奴が。私はビットベクトルやビットシフトを使うことが好きだし、やや難解なデータ構造や、あまり知られていないものの、特定の状況下では非常に有用な、変わった小さな言語機能を使うことも大好きだ。

しかし、あなたが仕事で書くコードは自分のために書いているものではない。たとえそれが「単なるプロトタイプ」であったとしても。(私は自分の「プロトタイプ」のうち幾つが、これまでに多少のお化粧レイヤーを被せられて、実製品に組み込まれてきたかをもう覚えていない)。そして、あなたは現在の問題を解決するためだけにそのソフトウェアを書いているのではない。次の開発者が、それを使って次の問題を解決できるように、書いているのだ。その5行のコードがもし10行だったら、より簡単に理解できるというなら、おそらく15行の方が良いかもしれない。

あなたは高度に抽象化された柔軟なフレームワークを使って、次の開発者のために、予めそれらを試し解決しておくことができる。…しかし、預言はあなたの得意分野ではないかもしれない。次の問題が何であるかというあなたの預言は、完全に的外れなものになるかもしれないのだ。おそらく最善の方法は、ただあなたのコードをこれ以上なくシンプルにすることだ。命名規則とコーディングスタイルによって、ほとんど英語のように読めるようにしよう。別のクラスや別のファイルを追加して、次の開発者がコントロールの流れを追うためにそれらを開いておかなければならないようにする代わりに、物事は愚直なやり方で、エレガントではない方法で、シンプルにやろう。

4. 勢いに着目しよう

このようなことは誰でも経験しているだろう。ある週には、皆がコードをチェックインして、ビルドは目に見えて形をとり、機能は毎日追加され、テストカバレージは高い値を維持し、Slackには生産性の高いアイデアとソリューションが溢れていた。そして、次の週…どういうわけか…勢いが失われたように見えた。課題Aに関する判断が必要とされたが、その判断は課題B、C、Dに影響を与えるものだった。皆が課題D、E、Fの作業をする一方、それらは論理的な依存関係に従った開発順序ではなかった。そのためさらに仮定を置く必要に迫られて、認知的負荷は高まった。具体的なコードを書くためには、たくさんのモックコード(中身はない仮のコード)を書かなければならなかった。誰かが判断をする必要があったのだ。

あるいは、それは判断力が麻痺したからではなかったのかもしれない。例えば前の週に得られた進捗は、やっつけで作成された技術的負債の偽の基盤の上に構築されたもので、全てを止めて元の場所に戻りリファクタリングをする必要があるのかもしれない。それも今すぐ。なぜなら問題を放置すればするほど事態は悪化するからだ。誰もそんなことは聞きたくない。しかし来月それを聞かされるよりも、今聞いておくべきだったと彼らは考えることになるだろう。彼らにそう伝えて欲しい。

あるいは、先週は少々やりすぎて、今週は皆がただ少々疲れているだけなのかもしれない。どうすれば良いかって?皆を休ませよう。丸一日の休みを。全員に。長期的にはそうすることで時間の節約になる。約束する。

定義するのは難しいし、測定するのも難しく、話すことも難しい。しかし、「勢い」はソフトウェア開発において非常に現実的なもので、それを失っていることは、取り組む必要のある何らかの根本的なトラブルの先行指標なのだ。それを無視してはいけない、勢いが魔法のようにひとりでに戻ってくると期待してはならない。警告のサインを読み取り、すぐに行動しよう。

5. あなたのような人たちとではなく、あなたを補完する人たちと働こう

「職場の文化に合っている」という理由で人を雇うのを目にするたびに、私は目玉がひっくり返る思いをする。大部分の単一文化に何が起こるか知っているだろうか?対処する方法がわからない病原体に遭遇すると、死滅してしまうのだ。

すべての開発者、デザイナー、QA担当者、製品担当者、営業担当者、そして幹部をお互いのクローンにしてはならない。本当に、本当にそうしてはならない。誰もが強みと相対的な弱点を持っている。そして誰もが美徳と欠点を持っている。主要な強みに着目して人びとを雇い、彼らの相対的な弱点は、他の人びとの強みで打ち消すようにしたい。

例えば私。私はコードを速く書く。コミュニケーションに長けていて、散文を驚くような速さで読み書きする、いつでも数十のプログラミング言語やフレームワークに通じていて、物事を素早く徹底的に理解する。また幅広い経験を持っている。…一方特定の分野、フレームワーク、または言語に関して究極的に熟知しているのではなく、広い範囲に対応できるジェネラリストだ。そしてスケルトンが構築されたあとに、追加される必要のある内容や洗練作業を、実際に行なってくれる他の人たちに助けられているアーキテクトの1人である。そしてUXはからっきしだ(「待ってくれ、そのフィールドは揃っていないって言うのかい?」)。同僚の間ではよくジョークのネタにされている。

私のような人は見つけるのが難しく、要求するものも大きい…だが私と私のクローン9人で構成された会社は、最初から破滅を運命付けられているようなものだ。ああ、多くのことは本当にうまくやることが可能だろう。だが会社を殺すには、全員に共通しているたった1つの盲点、たった1つだけの致命的な欠陥があれば良い。ほとんどの人は、自分自身にはうまくやれないことがあること、おそらくそれは他の人が面倒を見る必要があることを、渋々ながらも認めている。それなのにその同じ人たちが、しばしば「職場の雰囲気に合っている」人を探し、自分たちと似たような人たちを雇おうとするのだ。悲劇であり、そしてまた喜劇だ。

6. どんな決断であっても決断しないよりは良い

引き伸ばしはやめよう。確信が持てない時は、とりあえず何かをしよう。もちろん、これはプロダクトのリリース判定時には適用されないが、それ以外のソフトウェア開発の場面では適用可能だ。私たちはこれまでの歴史の中で、最も超加速化された業界で働いている。私たちは指数関数的成長の世界に住んでいるのだ。時間は付き合ってくれない。無駄にしないようにしよう。

これは低レベルの決定と同様に、高レベルの議論にも当てはまる。高レベルの議論でしばしば見られるのは以下のようなものだ「私たちは機能AまたはBのどちらを実装すべきだろうか?X方式でやるべきか、それともY方式か?」極めてありがちな展開は「ではよく考えることにしましょう…このことについてはまた来週話し合うことにして…」といったものか、もしくはもっともらしく「他の人たちがどうやっているかを調査して、もう一度話し合いましょう」とするものだ。こうした対応が正解であることも稀にはあるかもしれない。しかしほとんどの状況での正しい答は、誰かが「どれを試すかを試すかを今日中に私が決めます。そうすれば明日から作り始めることができますから」と言うことだ。

たとえAが最終的には間違った答であったとしても、Aの構築を開始するという決断は、おそらく何も決断しないことよりも優れている。これは直観に反するが、多くの場合正しいことなのだ。そしていつでも正しいのは、Aを本質的に理解する良い方法の1つは、実際にそれを作り始めてみるということだ。そしてその理解が良い判断へと導いてくれるのだ。

そしてこれは、低レベルの決定に対してはさらに正しいやり方だ。「仕様では、エラー状態Xをどのように処理すべきか、またはエラーメッセージがどのようなものであるべきかについては定義されていないぞ」(仕様はしばしば、エラー状態が一角獣のように稀であるようなユートピアのために書かれているように見える)「わかってるさ。コメントをここに今書き込んで、ここで実際に彼らが何をしたいと思っているのかを質問してくるよ!」

これはやりがちだ。もしこう答えたとしても、誰もあなたを責めることはできない。しかしこのやり方は間違っている。
より良い方法はその問題に対して自分自身で何らかの決断を行うことだ。たとえそれが何もせずに問題を持ち帰って質問をすることに比べて、荒削りで醜いものであったとしても。彼らを何もない状態から考えさせるのではなく、たとえそれが素晴らしいものでないとは知っていたとしても、既にあなたが作ったもの、学んだ地点から考えさせるようにしよう。彼らにとってもプロジェクトにとっても、結果はより良いものになるだろう。素早く実験を行い…そして素早くコースを変えることができるようにしよう。

7. 謙虚であれ、しかし堂々とせよ

全ての答を持てる訳ではない。も、自分自身がこう言うことは本当に気が進まないことなのだが、すべての答えを持っている訳ではない。それどころか、ほとんどの答さえ持っていないのだ。まあ十分な時間と労力をつぎ込むことができれば、大抵のことは理解できる自信があるとは言いたいのだが…。

…あなたもそうだろう。私たちは皆、ジェフ・ディーン(Googleの分散システムの基盤を構築した天才プログラマー)にも、サトシ・ナカモト(ビットコインを考案した人物)にも、あるいはマーガレット・ハミルトン(アポロ計画で活躍した天才プログラマー)にもなることはできない。私たちは、本当の天才と自己宣伝のうまい偽の天才で溢れた世界で働いている。そこでは全てを知る者はおらず、皆が自分たちが知らない物全てをひどく意識している。

幸いなことに、私たちの大多数は科学者ではない。私たちの仕事は、画期的な発見をすることではなく、そうした発見を実用化することである。望むらくは皆が本当に欲しているサービスとして。おそらくあなたがブルームフィルターやマークル木のようなデータ構造を発明することは決してないだろう。そして一緒に働く大多数の人たちによっても。しかしそれは問題ではない。本当に大事なことはブルームフィルターやマークル木を使って(場合によってはより抽象的な使いやすいレイヤを使って)実際の問題を解決することなのだ。

なので、あなたがテーブルの向こうにいる人以上のものを知っていると仮定すること、彼らの直感に反するアイデアが間違っていると即断すること、そして彼らの言語選択が酷いと思うことは全て間違っている。そして相手が自分よりも知っていると仮定することも間違っているのだ(もしそれが結果的に本当だったとしても)。世界は、スマートで知識が豊富でありながら、なぜか神秘的な理由で実際に物事を成し遂げられない人びとで満たされている(これはまるで安っぽい冗談だが、書いてしまおう:これが私たちの世界に学者がいる理由だ)。

だから、もしあなたが物事を成し遂げる人ならば、目がくらむような理論や知識に直面しても畏れ入ることはないし、同様にあなたよりもすごいことをたまたま成し遂げることができた人を前にしても気後れする必要はない。1日が終わって、コードを書き、テストをし、そしてデプロイしたのは、お馴染みの現場に身を投じて実際に物事を動かした開発者たちに他ならない。そしてその現場から引き離されつつある者の立場から語るなら、一緒に現場で穴を掘っていない人の事はあまり気にする必要はないということだ。そして、そうした人には上司というよりも協力者として接すれば良い。

[ 原文へ ]
(翻訳:Sako)

(訳注:トップの画像は映画「卒業」からのものである)。