10xプログラマーという神話
10xプログラマー、それは「一流のプログラマーは、普通のプログラマーの10倍の生産性を持つ」という、ソフトウェアエンジニアの世界における神話です。
「多くの人に使われるものを創れるようになりたい。」
そんな想いから、これまでGunosy、Mercari、LINEなどでエンジニアとして働いてきましたが、最近、自分の進むべき道を見つめ直す機会があり、「良いエンジニアとは何か」について考える時間がありました。
そんな中で、Redisの開発者である Salvatore Sanfilippo が書いた「The mythical 10x progrmmer」という記事に出会い、その内容が非常に参考になったため、翻訳させてほしいと本人に申し出たところ、「Sure!」と快諾していただけたので、僭越ながらここで共有させていただきます。
10xプログラマーという神話
10xプログラマー、それは「一流のプログラマーは、普通のプログラマーの10倍の生産性を持つ」という、ソフトウェアエンジニアの世界における神話です。
ここで言う「普通」とは、自分の仕事を十分にこなすことができるものの、10xプログラマーが持つ魔法のような能力は持たないプログラマーのことを指します。より正確に言うと、「普通のプログラマー」とは、その分野のプロフェッショナルの中で平均的な生産性を持つプログラマーのことです。
このような「獣(beast)」が存在するか否かについては、プログラマーの中でも意見が分かれています。ある人は「10xプログラマーなんて存在しない」と言い、別の人は「探すべきところを知っていれば100xプログラマーでさえ存在する」と主張します。
プログラミングを「線形的」な分野として捉えると、10xプログラマーの存在が不合理に見えるのは明らかでしょう。たとえば、あるランナーが他の選手よりも10倍速く走れるか、ある作業員が同じ時間内に他の人の10倍の建設を行えるか、と考えれば、その不合理さは明白です。
しかしながらプログラミングはある種の「デザイン」の分野です。たとえ、プログラマーがアーキテクチャの設計に参加していなくても、実装を行う際には、その実装に関するサブデザインが必要です。
プログラミングの設計と実装が非線形的な能力である場合、コーディング、知識、無駄の判別などの能力も非線形的であり、これらが相乗的に機能することでプログラムは作成されます。プログラマーがプログラムの設計と実装の両方を担当できる場合、この現象はより顕著になります。
10xプログラマーは、タスクが「目的指向」であればあるほど、少ない労力で自らの能力を発揮できます。一方、目的を達成するために使用するツールや実装方法に関するガイドラインが厳格であると、10xプログラマーはその能力を十分に発揮することが困難になります。
彼らは、そのような「ローカル」なタスクにおいても優れた仕事を行うかもしれませんが、たとえば「到達可能な目的がほぼ同じだが、コストが大幅に削減される場合、その仕様を完全に削除する」など、目的を達成するための方法を大きく変更することはできません。
私が20年間プログラマーとして働いてきた中で、Redisやその他のプロジェクトを通じて多くのプログラマーたちと仕事をしてきました。そして、多くの人々は私を「非常に実装の速いプログラマー」と信じています。私がワーカホリックではないことを考えると、私は私を「生産性の高いプログラマー」の一例として挙げることができるでしょう。
以下は、私がプログラマーの生産性において大きな違いを生み出すと信じている資質のリストです。
基本的なプログラミング能力
経験
集中力
設計の妥協
単純さ
非完璧主義
理論的知識
低レイヤの理解
デバッグ能力
基本的なプログラミング能力
プログラマーの明らかな長所または短所の一つは、プログラムの一部を実装するというサブタスク、つまり関数やアルゴリズムを扱うことです。
驚いたことに、基本的な命令型のプログラミングを効率的に使用する能力は、思ったほど普及していません。
私は、単純なソートアルゴリズムさえ知らない非常に無能なプログラマーが、大学卒で理論的には非常に有能だが実装力に欠けるプログラマーよりも多くの仕事をこなしているのを目にしたことがあります。
経験
ここでいう経験とは、よくあるタスクに対して、すでに検討された解決策を知っていることを意味します。
経験豊富なプログラマーは、さまざまなサブタスクを効率的に処理する方法を知っています。これにより、設計作業が大幅に省けるだけでなく、設計エラーに対する非常に強力な武器となります。設計エラーは単純さの最大の敵の一つです。
集中力
コードを書くために費やした時間は、その時間の質を考慮しなければ意味がありません。
集中力の欠如は、内的要因と外的要因によって生じます。
内的要因とは、怠惰、運動不足、睡眠不足、幸福感の欠如、プロジェクトへの興味の欠如などです(好きでないことをうまくやることはできません)。
外的要因としては、頻繁な会議、適切な作業環境の欠如、同僚による頻繁な妨害などが考えられます。
集中力を改善し、妨害要因を排除することがプログラミングの生産性に良い影響を与えるのは自然なことです。
集中力を得るためには、極端な手段が必要な場合もあります。例えば、私はメールを時々しか読まず、ほとんど返信しません。
設計の妥協: 90%を得るために5%を捨てる
プロジェクトにおいて本質的でない目標が設計に大きな複雑性をもたらし、他のもっと重要な目標を達成することを困難にしていることを積極的に認識しようとしない限り、複雑さが発生してしまいます。
設計者にとって、解決が容易でない部分、つまり工数とリターンが比例しない部分を認識することは非常に重要です。
アウトプットを最大化するために実行されているプロジェクトは、重要かつ適切な時間内に実装できる側面に正確にフォーカスしています。
たとえば、私はメッセージブローカーであるDisqueを設計した際、メッセージにベストエフォートの順序付けを提供することで、可用性、クエリ言語、クライアントとの対話性、単純さ、パフォーマンスなど、他のすべての側面が大幅に改善されることに気づきました。
単純さ
単純さを理解するためには、複雑さがどうやって生じるかを観察するのが良いでしょう。
私は、複雑さを生み出す要因は主に二つあると信じています。それは、設計の妥協を避けること、そして、設計作業におけるエラーの蓄積です。
設計が誤った方向に進めば進むほど、私たちは最適解からどんどん遠ざかります。初期の設計エラーは、誤った手法を用い続ける限り、同じシステムを再設計しても解決されることはありません。むしろ、初期のエラーを対処するために、別の複雑な解決策を設計する必要が生じます。プロジェクトはすべてのステップで、より複雑で非効率的になっていきます。
単純さを実現する方法としては、小さな「プルーフ・オブ・コンセプト(PoC : Proof of Concept)」を繰り返すことが有効です。プログラマーは、頭の中で単純な設計を何度も検討し、最も実行可能で直接的なアプローチから実装を始めることができます。その経験と能力は、設計を改善し、サブデザインのための賢明な解決策を見つける助けとなるでしょう。
しかし、複雑な解決策が必要な場合もあります。複雑さを避けるために時間をかけて熟考し、すべての選択肢を検討しても他の方法が見つからない場合にのみ、複雑な解決策を選択することが重要です。
非完璧主義
完璧主義には二つのタイプがあります。一つは、可能な限り最高のパフォーマンスを追求するエンジニアリング文化であり、もう一つは個人的な特性です。
どちらの場合も、完璧主義はプログラマーが迅速に物事を実現する最大の障壁の一つです。
完璧主義と外部からの評価に対する恐れがあるプログラマーは、心理的または明らかに測定可能なパラメーターにのみ従って設計するため、堅牢性、単純さ、期限内に完成させることなどが軽視されがちになります。
理論的知識
複雑な問題を扱うときは、データ構造や基本的な計算量、あるタスクに特化したアルゴリズムに関する知識が適切な設計を見つける上で重要です。
すべての分野でエキスパートになる必要はありませんが、少なくとも、一つの問題に対して複数の潜在的な解決策を認識できることが望ましいです。
たとえば、一定のエラー率を許容する設計の妥協をし、確率的な濃度の推定値を用いることで、ストリーム内の一意の項目を数えるための複雑で遅い、メモリ効率の悪い解決策を避けることができます。
低レイヤの理解
高級言語を使用していても、プログラムにおけるいくつかの問題は、コンピュータが特定のタスクを実行する方法に対する誤解から生じます。使用するツールやアルゴリズムに根本的な問題があった場合、そのプロジェクトを最初から再設計して再実装する必要が生じることさえあります。
C言語の能力を把握し、CPUやカーネルがどのように動作し、システムコールがどのように実装されるかを理解することで、プロジェクトが予期せぬ問題に陥ることを防ぐことができるでしょう。
デバッグ能力
バグを見つける作業は、容易に時間を浪費させます。合理的なステップを踏んでバグの原因を特定し修正する能力、そしてバグを含まない単純なコードを書く習慣は、プログラミングの効率に大きく影響します。
最後に
上記の資質がプログラマーの生産性に10倍以上の影響を与える可能性があるというのは、驚くべきことではありません。これらの資質を組み合わせることで、実行可能なモデルから始め、他のものより数倍単純な優れた設計を実装することが可能となります。
私は、このような単純さを重視する手法を「opportunistic programming」と呼んでいます。
基本的にすべての開発ステップにおいて、ユーザーに対して最小限の労力で最大の影響を与える機能と実装を選択すべきです。