プログラマと予習 事前学習はどの程度必要か?

ちょっと前の記事で、もう下火になっているから、言及してもいいか。

少なくとも、上記の記事では「プログラミング」を念頭に議論しているようだ。なのでこちらも、プログラミングに的を絞ろう。

この記事では、プログラミングにおける予習の役割・予習すべき量についての考え方・現実における予習のタイミング・予習の優先順位について書き記す。

初めての試みは必ずどこか失敗する――のを軽減のが予習の役割

器用・不器用などによる違いはあれども、全く初めてで未経験の分野に挑戦する場合、大抵はどこかしらで大なり小なり失敗を犯す結果となってしまうものだ。予習とは、未経験の分野に取り組む際に、失敗の数を減らしたり、失敗による被害を抑えるために行う作業だ。

私見だが、プログラミングにおいては、全く初めての分野に挑戦する場合、プログラマが対象について暗黙のうちに頭の中に描いているメンタルモデルが、対象についての適切な概念モデルと比較して、どれほどの差異があるのかが鍵となる。差異が小さければ、開発の初期に構築されたメンタルモデルを流用しつつ、枝葉を選定することで、対象についての適切な概念モデルに転用することが可能となる。これは、経験豊富なプログラマが取りうる手法の一つだ。しかし、差異があまりにも大きかったり、そもそもメンタルモデルを構築することすらできなかった場合には、五里霧中の状態にて手探りで物事を始めなくてはならない。プログラミング初心者が途方に暮れるのと似たことだ。

例えば、今までファームウェアデバイスドライバの開発しかしたことのない人が、急にGUIアプリケーションの開発に放り込まれた場合、少なくとも初っ端は難儀するものだ。一方で、MFCによるWindows GUIアプリケーションの開発経験が豊富な人が、急遽未経験の「QtによるクロスプラットフォームGUIアプリケーション開発」に放り込まれた場合、Qt独特の部分*1に慣れる必要はあるものの、前者ほど難儀することはない。

古典的な「OS上で動作するGUIアプリケーション」は、次のような技術要素で構成されていることが多い。

  • オブジェクト指向プログラミング
    • ツールキットによっては、C言語などで擬似的にオブジェクト指向プログラミングっぽくアプローチすることもある。
  • GUI部品とほぼ一対一で対応する、ウィジェット・クラス
  • ウィジェット・クラスその他を「継承」の視点で整理してまとめあげた、クラス・ライブラリ
    • 既存のウィジェット・クラスを特化させることによる、機能の拡張
  • ウィジェット・クラスのインスタントの包含によってツリー状に構築されたオブジェクト集合と、GUIとのマッピング
  • イベント駆動型とイベントループ
    • 0.1秒ルール順守のために、非同期IOやマルチスレッドなどに踏みこむことも多々ある
  • イベントハンドラによる、ウィジェット・クラスのインスタントとイベントループの連携
  • 状態遷移
    • 複雑なGUI*2では、内部状態の定義と管理が必要となってくる。

これらの要素は、使用する言語やツールキットによって細かな差異があり、それゆえに「最適なアプローチ」が環境によって異なってくる。しかし一方で、抽象化された視点では共通点が多いため、あるプラットフォームでの知見を別のプラットフォームに適用可能であることが多い。例えば「通信に時間がかかるから、UIのイベントループでブロッキングしない実装にしよう。あと、通信中は余分なボタンを押せない状態にして、通信終了したら元に戻そう」という発想は、多数のプラットフォームで通用するだろう。だが、その発想を実現するために最適なアプローチは、各プラットフォームで微妙に異なるはずだ。

全く初めての分野で、しかも近縁分野に触れたこともないとなれば、まずメンタルモデルを構築することすら不可能であるし、仮に構築できたとしても、対象についての適切な概念モデルとは全くもって異なる姿であろう。この状態――「なにも知らない」以前に「『何を知らないか』すら分からない」ままに徒手空拳で突き進むと、慣れてくるまでとんでもないソースコードを量産してしまうだろうし、下手をすると出口にたどり着くことすらできなくなる。

一方で、初めての分野であっても、近縁分野に触れたことがあるのならば、対象についてメンタルモデルを構築することは比較的容易であるし、構築されたメンタルモデルは、対象についての適切な概念モデルとの差異が比較的小さなものとなる。小手先のテクニックでなんとかなるのは、このレベルからだ。とはいえ、前述の例でいうなら、MFC的な発想で突き進んだQtアプリのソースコードは、後世にて「いやいや、なんでシグナルとスロットを使って疎結合にしてくれなかったの?」とメンテナが嘆く代物と化す可能性を秘めている。

Qtを使っているのに「シグナルとスロットを使って疎結合にしてくれなかった」GUIアプリケーションは、失敗といえば失敗だが、「GUIアプリの特性を理解せずに気合いでやり抜いた努力の結晶」よりは、はるかに良い。だがQtによるGUIアプリケーションとしては、少々失敗していると言わざるをえない。

プログラマにとっての予習は、未経験分野に挑戦する際に、失敗の数を減らしたり、失敗による被害を抑えるために行うものだ。

その方法は単純だ。未経験であることによる「知識・経験の欠如」を解消すればよい。それにより、まずメンタルモデルを構築できるようになるし、欠如の解消が進めば、メンタルモデルが対象についての適切な概念モデルに近くなる。典型的な手段としては、以下が挙げられる。

  • 公式ドキュメントや関連書籍による、知識の充足
  • サンプルコードを読んだり改造したりすることによる、知識と経験の充足
  • テスト・プロジェクトなどで実際にコードを書いてみることによる、経験の充足

職業プログラマの作業のうち、公知の技術の利用においては、ドキュメントやサンプルコードを読むことや、実験用のプロダクトで実際に当該技術を使用してみることによる学習は、資料を得やすいこともあり、比較的容易だ。

予習すべき量≒(知らないことの量−近縁分野の知識や経験)×重要度×担当範囲の広さ

当該分野について未経験であることによる「知識・経験の欠如」を解消するのが予習の役割である以上、予習すべき量は「知らないことの量」に比例する。

単純な話、Prototype.jsからjQueryへの過渡期にWebクライアントサイドの開発をしていた人が数年のブランクを経てクライアントサイド開発にカムバックするのと、プログラミングすら初めての素人がクライアントサイド開発に挑戦するのでは、当然ながら前者の方が予習すべき量――まともなコードを書けるようになるために必要な知識・経験の量は少ないはずだ。

ただし、当該分野については知らなくとも、近縁分野の知識や経験があるのなら、予習すべき量は減少する(先に述べた「QtによるGUIアプリケーション開発」の例を参照)。

挑戦する分野により近い近縁分野の経験があるならば、当該分野についての断片的な情報をネットで漁ったり、サンプルコードを眺めるだけでも十分な効果を得られる。だが、今まで経験した分野が挑戦する分野から離れていくほど、書籍や「公式サイトで公開されているドキュメント・サンプルプログラム一式」などのまとまった資料が欲しくなるし、実験用のコードを書いて試してみたくなる。

予習すべき量を左右する別の要因としては、「それがどれだけ重要か?」という点がある。

重要度の1つが「それがシステムの根幹に近いか否か?」だ。例えばjQueryからAngularJS(でもReact.jsでもお好きなものをどうぞ)に移行する場合、要は「フレームワークの導入」である訳で、開発対象のクライアントサイド全体に影響を及ぼす。AngularJSというフレームワークの元での「行儀のよいコード」は、AngularJSの流儀に従ったコードだ。決してjQueryの流儀に従ったコードではない。なので、AngularJSについて重点的に予習するべきだといえる。

もう1つは、開発対象のドメイン固有の視点での重要度だ。例えば「新規開発か否か?」という点は重要だ。最初に築かれた土台が「『誤ったアプローチ』を力技で強引に使える代物に仕立て上げた(でも気づかなかった漏れが多々ある)グダグダ感あふれるもの」だと、後世に禍根を残すことになり、メンテナが発狂するものだ。スピード感は重要だが、同時に腰を据えて「そこそこ理にかなった真っ当な品質」を達成するべきでもある。

経験豊かなベテランの強みの1つは、近縁分野の経験を流用することで予習量を減らせることだ。ただしこの方法は、取り扱いを間違えると「COBOL臭いJavaのコード」や「BASIC臭いC言語のコード」のような例を生み出してしまう危険性がある。この問題を回避するためにも、対象の重要度を慎重に検討しなくてはならない。「言語の違い」が大きな影響を及ぼす点に気づく必要があるのだ。

担当範囲の広さも、予習すべき量を左右する。ここで興味深いのが、開発対象そのものの規模との関係だ。

開発対象が小規模なら、当然ながら予習すべき量は少なめとなる。ここから、開発対象の規模が大きくなっていくにつれて予習すべき量が増えていくのだが、ある段階より、今度は逆に予習すべき量が減少し始める。なぜなら、規模が増えることで、開発部隊のメンバーの増員と分業化が進むからだ。

そのため、実は大規模開発よりも、中途半端な規模の開発の方が、担当範囲が広く、予習すべき量が多かったりする。中途半端な規模なので増員もできず、少ないメンバーでシステム全体をカバーしなくてはならなかったりするものだ。

さて、「開発対象について知らないことが多く、近縁分野の知識や経験も乏しいのに、比較的重要な部分を任された上に、把握すべき範囲が意外と広い」というワーストケースでは、「本10冊」ではないが予習すべき量が跳ね上がる。

個人的な経験では、少数精鋭で組込みLinux機器の新規開発に関わった時は、明らかに業務経験だけではカバーできない分野が多かった。

開発に加わった時点で、私が業務で関わった主な技術分野は、次のような具合だった。

一方、開発で必要だった知識は、こんな感じ。

言語も違えばツールも違うとなると、徒手空拳で突っ込むのは危険であるし、ネット上の情報だけだと分散していて探すのに手間がかかる可能性がある。

いきおい書籍などの「ある程度まとまった情報源」を求めることになるのだが……ここまで幅があると、棚から一掴みで各分野ごとに2冊ほど買い求めただけで合計10冊になってしまうものだ。

だから、極端なケースでは「本10冊」という可能性はありえなくもない。もっとも「それ、マネジメント(開発者のアサイン)としてどうなのよ?」という視点での問題はあるかもしれないが。

いつ予習するか?

ここまでに述べたように、未経験の分野に挑戦した際の失敗の規模を抑えるのが予習の役割であり、今までの経験から離れていくほど予習の量が増える傾向にある。

で、ワーストケースでは比喩ではなく「本10冊」というのもあり得るのだが……はっきり言って本番直前に全部消化するのは無理である。というのも、実際に「本10冊」が起こりうるケースは、「1分野で合計10冊」よりも「5分野で合計10冊」であることが多い。要するに範囲が広すぎるのである。

ではどうするかといえば――本来は、業務の一環として必要である以上、会社として学習機会を設けるなどの対応をすべきなのだが――現状では直接業務以外の場面で知識・経験を補っている人が多いのが実情だろう。

例えば、先の私の例の場合、社内サーバの管理用にWSH + JScriptでツールを作成していた*3ので、JavaScriptECMAScript 4)の言語本体については知らなくもなかった。また、趣味でMinGWではあるがGCCGDBを触っていたし、愛用のテキストエディタVim(ただしWindows版)だった。一部の技術要素については、直接業務で深く関わったことはなかったものの、間接業務や業務外(趣味)で触れたことがあったのだ。

だから、開発に加わる時点で全く知らなかった部分(予習すべきこと)はある程度減少していたし、DOMやPrototype.jsのように「基礎(JavaScript)を知っているから、リファレンス片手にぶっつけ本番でも何とかなる」レベルに力技で持ち込めた部分もあった。

もっとも、当該業務へのアサインは、そういった個人的事情とは一切無関係に決定されていた。なので、社内サーバの管理ツールをVBScriptで作成していたり、趣味でMinGWではなくVisual Studio Expressを触っていたりしたら、本当に何も知らない状態でコードを書く羽目になっていたはずだ。

――ところで、こういう話はあくまで「(酒の席での)オッサンの武勇伝」に留めておくべきだ。会社組織としては、理想的には、当該分野を多少なりとも経験している人をアサインするとか、経験者と未経験者をセットでアサインしてフォロー体制を整えるとか、そういう配慮をするべきだろう。事前に何らかの形で学習機会を設けてもらえると、なおよい。趣味のような「個人の事情」に左右されることを是としていては、業務の属人化がより一層進んでしまうものだ。その人が倒れてしまったら、目も当てられない。

実のところ、ある程度ベテランになってくると、未経験の分野であっても、多少なりとも近隣分野の経験があることが多いこともあり、コードを書いているうちに段々と慣れてきて、最終的にはそこそこ理に適ったコードを書けるようになる。ではあるものの、スケジュール等の関係で「慣れる前に書いてしまっためためたなコード」は書き直されないままリリースされてしまうことが多いし、そのコードが将来の災いとなることは結構多いものだ。そういう、目に見えにくいコストも考慮すべきなのだが、そうではないのが実情だったりする。

予習の優先順位を設ける

現状では、未経験の分野について「すみません、事前に予習させてください……」と要求することが難しいことが多い*4。仮に何とか時間を捻出できたとしても、予習すべき分量をとてもまかないきれないことが大半だろう。

そうなると、予習に優先順位をつけて、優先度の高いものから手を付けることになる。

優先順位をつけるには、重要度・影響範囲・難易度・必要となる時期のバランスを勘案する必要がある。

重要度
例えばシステムの肝の部分(そこがうまくいかないと、システムとして落第点となってしまう)の予習に時間を割くのは、間違っていないだろう。
影響範囲
例えば、使用する言語・フレームワークについて誤った認識を持ったままコードを書き始めてしまうと、後で問題に気づいたときには「不適切なソースコード」が大量に存在していて、修正が大変になるだろう。事前準備に時間をかけることは、間違いとは言い難い。
難易度
時間の余裕があるなら、難易度の高い部分の予習に時間を割くべきだろう(難易度の低い部分は、最悪でも「リファレンス片手にぶっつけ本番」で何とかなる可能性が高い)。一方、時間の余裕がないのなら、難易度の低い部分を予習で総ざらいしてしまい、精神的余裕のある状態で残りの難易度の高い部分に取り組む、という方法もある。
必要となる時期
開発スケジュール後半に必要となる内容なら、優先度を下げてもよいだろう。

まとめ

  • 予習の役割は「未経験分野に取り組む際に犯してしまうだろう失敗の量の低減、失敗による被害の抑制」である。
    • 予習ですべきこと≒未経験であることによる「知識・経験の欠如」を解消することで、第一に対象システムについてメンタルモデルを構築できる状態に到達し、第二にメンタルモデルと「対象システムについての適切な概念モデル」の差異を減らす。
  • 予習すべき量は、知らないことの量に比例するが、近縁分野の知識や経験によって抑制できる余地がある。また予習すべき内容の重要度や、担当範囲の広さも影響を与える。
  • 本来は未経験分野に取り組む前に予習期間を設けるべきだが、現実には間接業務や業務外(趣味)の時間に充足している人が多いと思われる。
  • 本来は十分な予習期間を設けるべきだが、現実には期間が限られるので、予習の優先順位を設けることになる。その際には「重要度・影響範囲・難易度・必要となる時期」を考慮することになる。

予習すべき具体的な量はケースバイケースだが、一つ目安として「対象システムについてメンタルモデルを構築できること」と、「構築されたメンタルモデルがそこそこ妥当である(≒『対象システムについての適切な概念モデル』との差異が少ないこと)」あたりが参考になるのではないかと思う(少々主観的な目安だが)。

*1:「シグナルとスロット」などのQtの作法(≒Qtらしいアプローチ)、Qt独自のクラスライブラリ群、Qt Creatorやqmakeなどの各種ツール。

*2:例えば、動作モードや「選択されているアイテムの種類」などによって、メニュー表示の内容を変化させるようなものなど。

*3:しかも、なぜかオライリーの『JavaScript 第5版』や『JavaScript: The Good Parts ―「良いパーツ」によるベストプラクティス』を買って言語本体の勉強をしていた。

*4:個人的には、もうちょっと融通を利かせてほしいところではあるが……。