Learning
概要
プログラムの学び方についてまとめる。
ほかのページに比べて主観的なポエムが多くを占める。 プログラムの学び方についてよく考えるのは、すごくできる人と平均的にできる差が非常に大きいためだ。すごくできるプログラマは平均的なプログラマ100人分の仕事をする、なんてざらにある。ほかの職業でここまで明確に差が出ることは少ない。
OSSの99%は、1% ~ 0.1%のプログラマが支えているように見える。そのプロジェクトの作者でもあったのか、ということはよくある。偉大な人はさらに何かを生み出し続け、人類を辞めていくようだ。
この大きな違いをもたらしているのは、まず1つは頭の作りが違うとかそういうことだ。しかしこれは先天的なものが大きいのと、脳の動きを再現できないのでなんとも言えない。 違いをもたらすように見える2つ目は、学ぶ方法だ。これは再現可能だし、希望的・建設的なのでよく観察する価値がある。
Memo
実装との矛盾
わからないものは実装するとどうやって動いているか理解できる。理解していないと実装できない。ただ、それをどうやるかという問題がある。なにかの記事で見たものをなぞるだけでは、それ以上の理解に達しない場合が多い。かといってなぞらないことには、方法がまったくわからない。書いてある以上に何か自分で考えてプラスでやってみないといけない。
説明する
説明することで、どれだけ自分が自明と思っていることに頼っているか確かめられる。自明だと思っていることは、ほかの知識レベルにある人にとっては自明ではない。うまく説明できればなぜそれが必要か、という背景まで理解できているということがわかる。説明できないなら真に理解はできてなく、そういうもんだ、そういう仕様なんだというところで止まっている可能性がある。
具体的な人を思い浮かべて、その人に完全に説明できるようにするんだ。
上達したいなら公開する
公開せず、1人でやるものはなかなか上達しない。できるようになったら見せよう、という種類のものはうまくならないだけでなく、だいたい途中でやめることになる。逆に下手くそでもどんどん人に見せて、フィードバックをもらって、よく準備したものを何か賞にでも送って、という人がプロとして成功するように見える。また、仕事にすると嫌でも向上する。人に見てもらうからだ。一人の天才が狭い部屋で何か革新的なものを作る、というのは稀にみるが、誰かに見せて何らかのフィードバックを得ていて、想像ほど孤独なプロセスではないことが多い。
1人がうまくいきづらいのは、うまくやるプレッシャーが働きにくいからだ。これを相手に見せたらどうなるだろう、という仮定はよくブラッシュアップさせ、意図を言葉で説明できるようにする。
また、人間相手だとかけられる時間に上限がある。聞かれたら、その場で答えられないと議論にならない。他人のコードをその場で理解して何らかのフィードバックをすることが必要になる。逆に時間の制約が全くなく、会ったこともない他人のコード…たとえばOSSの場合は理解の必然性がなく身近でもないので、後回しになりがちだ。なのでわからないままだ。
OSSに取り組む価値があることは明らかだ。仕事ではできない抽象度の高い問題は難しく、根源的な仕組みに対する理解を必要とする。問題が解けることは、理解していることを意味する。貴重な経験が得られる。公開、人の巻き込み、時間制限。これらをうまく使うことで、上達できると考えているが、今のところうまくいっていない。
正しい訓練をやる
- 現状よりも高い具体的な目標のある練習を
- 集中して
- フィードバックを受けながら
- 居心地の悪い状態で
- 継続する
- どんなトップパフォーマンスを弾き出す人でも、限界的練習は苦しい
- 「楽しいことだから、いつまでも没頭して練習を続けられる」というのは誤り
- 「努力しつづけなさい、そうすれば目標を達成できる」は間違っている。正しい訓練を、十分な期間にわたって継続することが向上につながる
早く書く
プログラムを早く書けることは、問題解決、学ぶことにおいて重要だ。
- 思考のサイズ的問題
- 戻るコストの問題
- 質とスピードを選べる
- 経験値
最初から問題を正しく理解して、書くことは難しい。あれこれ考慮するうちに、堂々巡りになって何も進まずにいることはよくある。スコープを絞って不完全でも動くように作っていき、あとから修正するのが、結局一番速かったりする。
動かしてみないとわからない種類の問題もある。そのときはやり直しになるので、それまでにかけたコストが低いほど、失うものが少ない。
早く書けるということは、そこから品質重視か速度重視かによって、選択できるということだ。早く書くことと品質はトレードオフでない。遅い人はその選択ができないので、遅延しやすくなる。
書くのが速いと、成功も、失敗も、経験の数が多くなる。単純にたくさん書いているからそれだけ見覚えのある問題は増えるし、馴染んだ表現が増える。失敗にしたって、得られるものは成功よりも多い。刻みつけられた問題意識や、切り抜けた経験は次の行動につながる。同じ情報に触れても、吸収できる量が増えるため、より吸収する速度に倍率がかかるだろう。
言語の練習方法と習熟段階
- 本を読む
- はじめやすさ:
★
誰でも始められる - 習熟学習ステージ1
- 理解できたかテストできない
- サンプルコードをやるだけではあまり身につかない
- はじめやすさ:
- 個人開発する(新規リポジトリ)
- はじめやすさ:
★
誰でも始められる - フィードバックがないとモチベーションを保つのが難しい
- アピール:
★
少なくとも最低限の能力は持っていることを証明できる - 習熟ステージ2
- ありがちなコードや問題だとググればすぐわかるので、理解できたかテストするのは難しい
- 緊張感、フィードバックがない
- はじめやすさ:
- 有名な開発者の、小さめのリポジトリを覗く
- 有益な情報が多いかつ、ハードル低め
- 仕事にする(メイン/副業)
- 既存リポジトリにコントリビュートする
- はじめやすさ:
★★★
規模によるが、読むのは新規に書くより格段に難しい - アピール:
★★
- 理解できるなら、非常に参考になる
- 理解せざるを得ない状況になる
- コード以外に障壁は存在しない。技術とモチベーションだけが問題
- 難易度が高く、モチベーションを保つのが難しい
- 習熟ステージ3→4
- はじめやすさ:
- 他者との協働の場でないと、正しく把握・理解するプレッシャー、テストが存在しない
常に正しい道を選ぶというより、軌道修正する能力が高い
できるプログラマーの書いたコードを見ていると、常に素早く、正しい選択をしているように見える。 不思議だ。 実際には、途中で方向修正していることが多い。正しい道を選ぶというか、軌道修正できる前提でコードを書いているなど、変更の能力が高いように見える。結果として、素早く実装して運用の中で正しい道を把握していく。 普通の人は、正しい道を選ぼうとしてベストプラクティスを丸々参考にし、運用後の変更は考えていない。期間が経つと負債になることが多い。
ということで、長期的に見て正しい選択をするためにはスピード・後から変更できること、が必要に見える。
Head Firstシリーズの序文
Head Firstシリーズの序文では、学習に関して示唆に富むことを書いている。 ビジュアル、自分で考えること、会話体、繰り返し説明する…。 向き不向きが分かれそうだが、一度試しておくべき本。
メカニズム、理由を解説しているので自分が何かを記録したり書いたりするときにも役立つ。
脳を思い通りにさせるためできること。
- じっくり読みましょう。理解すればするほど、記憶すべきことは少なくなります
- 問題を解きましょう。自分のノートに書き込んでください
- 「素朴な疑問に答えます」を読みましょう
- この本を読んだ後は寝るまで他の本を読まないようにしましょう。少なくとも、難しいものは読まないようにしましょう
- 内容をはっきりと声に出してみましょう
- 水をたくさん飲みましょう
- 脳に耳を傾けましょう
- 感情を持ちましょう
- たくさんのコードを書きましょう
影響範囲の広いリファクタリングのうまいやり方
修正ひとつひとつの難易度は大したことないが、影響範囲の大きいことが先伸ばしにさせている負債。たとえばfactoryを全体的に修正する、というような場合。 広く浅く1種類の問題を解決していく。
- まずネストをやめる(全体)
- traitを使う(全体)
- 不要な記述削除(全体)
- 莫大になるものは適宜ごとでコミットを切る
コーディングスキルは最低限の装備レベル
WEBプログラマーの場合まず身につけることはRubyとかプログラミング言語になる。 新しいProgramming Languageを学ぶときは、まず体で覚えて文法を楽に認識できるようにする。 いちいち考えずとも、自然に認識できる状態にする。先にコードを読み書きする。 本を読んで学ぶのはそれからでいい。 コード例をすばやく理解でき、何度か見た謎の文法がああそういうことだったのかと納得できる。
最初はコードだけできれば仕事が万事できるように見えるのだが、それだけでない。 データ関連とか、配置とか、パスとか、何かを作るうえで無数に決定していくことになるのでそれらをすべて知らないとスピードが出ない。マイグレーションのたびにコマンドを調べたり、 RSpecを書くたびにマッチャーを調べてたら一瞬で時間が溶ける。 そして、脳内だけで展開できないと、開発の会話についていけない。 総合的なRailsでの開発や、データ関係、目の前にあるコードベースへ慣れていく及び慣れるスピードを増やすこと、が必要に なる。
何か難しいことをやるというよりは、地道に数と時間をこなすしかなさそう。 プライベートでなにかしらのWEB開発プロジェクトをやることのが必要だろう。全体を触っておいて、 できる だけでなくスピードも上げていくといい。
仕事の進め方を学ぶ
強い人はその技術だけでなく、進め方に特徴があるように見える。 適切な段階と手順を踏むことで、困難に見えることを実現可能なことに落とし込んでいる。
たとえばWebアプリケーションでデータベースの変更を伴う変更…とくにテーブル名を変更するとか、直接動作に影響しないものはそのままになりがちだ。 この例の場合は単純にいうと面倒くさいということなのだが、その理由の内訳を見ると、データベース関連の変更は事故の危険がある、変更が莫大になるところが大きい。強い人はどう進めるかというと、分割して安全に淡々と進める。そしてみんなが気づいてるおかしなところをブルドーザーのように解決していく。
こういった流れは、本には書いてない。ただ精神的習慣として、獲得していったものに見える。 幸いなことに、典型的なタスクの進め方はリポジトリの過去のPRを読むことで理解できる。 OSSでも良いのだが、日本語のものは少なく、理解できる規模感のものとなるとさらに少なくなる。仕事でやっているリポジトリは、単にお金を稼ぐための情報置き場でなく、過去の偉人が遺した情報の宝庫でもある。
解決スクリプトを書いて解決する
強い人は仕事の梃子としてコードを使う率が高いように見える。 並の人は、たとえばRails開発なら、Railsの機能開発以外でコードを使うことはあまりない。
並の人の例。
- 不整合データを調査する → SQLを使って各条件を調べる。合っているか手動で確かめる
- 不整合データを削除する → 調査する、各環境のコンソールで逐次実行を繰り返す
- 使われてないファイルを削除する → 逐一grepして削除していく
- リレーションの不整合を調査する → 1つ1つ地道に見ていくか、grepで頑張る
- バージョンを上げる → バージョン番号をgrepして置換
強い人の場合。
- 不整合データを調査する → Rakeタスクで1発で必要なデータをすべて出力する。タスク内にチェック処理を入れて検証する
- 不整合データを削除する → 調査 → Rakeタスクで一発で不整合をすべて解決する。各環境でタスクを実行するだけ
- 使われてないファイルを削除する → 使われてないことを検知+削除するスクリプトを書く
- リレーションの不整合を調査する → 1つ1つ地道に見ていくか、grepで頑張る。リレーションを検証するコードを書く
- バージョンを上げる → リポジトリ全体で一括置換
そうやって使うのか、と驚かされることが多い。とても創造的に見える。コードを自由に手足のように使っている。
つまらない単調な仕事があったとき重要なのは、あの人も同じように仕事を進めるだろうか、と考えることだ。強者は100倍の速度で終わらせている、なんてことはよくある。つまらない仕事があるというより、自分でつまらなくしている可能性がある。
具体的にどうすればそうなれそうか考えてみると、1.)2次元的なコードを書くことへの慣れ、2.)道具を作るマインドに思える。 2次元的というのはコードを生成して実行するコード、みたいなことだ。xargsを使いこなせること、といってもいい。1次元的はターミナルに直に打って1つ結果が返ってくるようなこと。またライブラリとかの、ほかのプログラマーが使う用のコードを書く側(使う側ではなく)である側面も技術や想像力といった面で重要なようだ。具体的に思いうかべている超強い人は、Railsの上位のコントリビューターだった。
並のプログラマーは、エンドユーザ(つまりサイトをブラウザで利用してる人)に向けてプログラムしている。だから、具体的な問題な問題に対する解決が多く、梃子がきかない。抽象的な問題の解決に不慣れに見える。 強いプログラマーは、ほかのプログラマーに向けて抽象的な目的や手段を使ってプログラムしているから、抽象的な問題を解決するのに慣れている。梃子がきいて、莫大な成果を上げる。
強い人の特徴
強いプログラマーを観察して感じたこと。共通する特徴を出せるほど知らないので、だいたい特定の一人を思い浮かべる。
- 異常に仕事が早い
- PR出してくるのが異様に早い。例: 38コミット、180Files Changed が一日で来る。普通の人が1週間かかることを1日でやる。単純な変更でも何かしらの技術の梃子がはたらいているように見える(詳細不明)。
- 莫大な変更を恐れずリファクタリングする
- あらゆる分野に異常な状態への感度が高い。 解決法がクール。また、修正のために全体を変えないといけない、ようなことも尻込みせずやる。難易度というよりその影響範囲や変更数のため後回しになっていることも高速で潰していく。
- 一発で理解する量が多い
- 1回で理解し、一度触ったコードなら確度をもって話すことができる。仕様としてほかの人に伝えることもできる。
- 端的な文章 自信がないと長く書きすぎになるが、そういう感じではない。必要な情報が端的にかかれている。素っ気ないともいえる。フランクな書き言葉。
- 明確なスコープ
- スコープをもって1つのPRをやる。1つの目的でも変更が莫大になることはあるが、それを恐れない。
- 既存コード関わらず成果を出す。
- 普通の人は既存コードが汚いから、しょうがない…と言いがち。強い人はその間に既存コードごとすべてを修正してる。環境の側を変える。ひどいコードに直面して何か言いがちなとき、みんなが同じ反応して足踏みする、というわけではない。強い人ならどうするかを考えたほうがよさそう。直面したときの反応は、明確な技術の差にみえる。
コードや環境を完全にコントロールしているように見えた。コンピュータに振り回されるのではなくて、明確に命令する側だった。プログラマーは本質的に何だってできる、ということを確信させてくれる。比較して凹むことも多いだろうけど、すごい人と働くことはおもしろそう。
強い人の語録
いい言葉だったので、強プログラマーの言葉を(勝手に)保存。 主張しつつも相手に決定権をもたせる書き方もいいな。
参考までに、ここでのレビューで着目すべきは、以下かなと思ってます。
- Activehashのバージョンがあがったことでプロダクションに影響がないかを、CHANGELOGなどの差分を見て確認する(主に破壊的変更がないかがだいじ。ただ x.z.y とバージョン番号がついていて、メジャーバージョン.マイナーバージョン.パッチバージョンとがあって、大きな変更がある場合はメジャーバージョンがあがるので、マイナーバージョンの場合はある程度気楽に見ても問題はないです)
- sampleを使っているのがテストコードなので、CIが通っていれば概ね問題ない
- 手元でcheckoutして直し漏れがないか確認。ただ、今回の修正対象がテストコードなのと、 直し漏れがあったところで入出力が変わらないのであれば、そこまで神経質になる必要はない と思ってます(神経質になるほうがかえって生産性を落とすんじゃないのかな、と思います)
注力すべきところ、しなくていいところ、を使い分けてもいいかなと思いますが、経験則もあるので、やっていくうちに身につければいいと思います。なので参考までに、という話。
どちらでも変わらない場合の「重箱の隅」をつつくのがコードレビューではない 、というのだけ共通認識があると嬉しいかもなあ。ミスだったりその変更によって結果が変わる場合はとても大事なんですけど。
という自分も、むかしは「たくさん何かをコメントすれば価値がある」と誤解していたんですけど、 “他人に修正ないし判断を強いる” ことなので「まあ別にここでやらなくてもよくない?」ということに関してはマージしてリリースするのを優先した方が、 “エンドユーザーから見たプロダクトの価値” という視点でみると、価値があるアクションかな〜と思ってます。
そうですね、初学者にありがちなのは、コードレビューを「フォーマットの精査」と認識していることがあるんですが、そういうのはrubocopみたいなのでやればよくて、注目すべきは
- 要求通りか、ちゃんと動くか
- ぶっこわれてないか、ぶっこわれないか
- 入出力が適切か
- 計算量が問題ないか
- 設計や命名に問題がないか、あとで辛くならないか
の5点であって、それ以外は割とどちらでもいいこと(nits)かなと思ってます。
技術本を買うときに気をつけること
- 出版が新しいものを買う
JavaScript, Reactなど、すぐ情報が古くなる分野はちゃんと出版年を確認してから買ったほうがいい。最新のとは変わっていて、あまり役にたたないことがある。ほかのプログラミング言語の本では気にしたことはないが、js関連ではいくつかあった。
- 日本語でないと読めなさそうなとき あまり概念を把握してないとき。新しい概念を他言語で理解するのは大変。まず土台を作っておけば、理解できる。 コードの割合が低い本。コードだと理解できるが、文章の割合が高いと理解できないことが多い。