@ledsun blog

無味の味は佳境に入らざればすなわち知れず

RubyKaigiに行く意味とは?

結論をいうと「RubyKaigiに行くと自分が世界最高のエンジニアになる可能性」が得られます。どういうことでしょうか?

目的地を知らないとどうがんばっても目的地にたどりつけません。当たり前の話です。エンジニアとしての成長も一緒です。自分がエンジニアとして成長する上限は「自分の心の中にある理想のエンジニア像」で決まります。自分がどうなりたいか知らないと、それ以上には成長できません。

普通に会社に来て仕事をしていると「自分の心の中にある理想のエンジニア像」は先輩社員です。それ以外の人と会わなければ、知っている人の中から自分に一番合いそうな人を選ぶのが自然です。社内の一番優秀なエンジニアは、日本最高のエンジニアかというと、残念ながらそうではありません。だから、自分の心中にある理想のエンジニア像は、日本全体でみると、「中の上」や「上の下」のエンジニアになります。

さっき言ったように「自分のエンジニアのとして成長する上限は、自分の心の中にある理想のエンジニア像」で決まります。真面目に仕事して、真面目に自習しても、成長の上限は「中の上」か「上の下」のエンジニアです。経験年数によりません。5年やろうが10年やろうが、上限以上に成長できません。

もっとすごいエンジニアに成長するにはどうしたらよいでしょうか?「自分の心の中にある理想のエンジニア像」のレベルを上げます。そのためには何をすればいいでしょうか? 最高のエンジニアに出会って交流すればいいのです。そうすれば「自分の心の中にある理想のエンジニア像」が出会った最高のエンジニアになります。

で、RubyKagiがどういうカンファレンスかというと、世界最高のRubyエンジニアが集まるカンファレンスです。だから、RubyKaigiに参加して、勇気を出して最高のエンジニアに話しかければ「自分の心の中にある理想のエンジニア像」が世界最高のエンジニアになります。

というわけで結論。 RubyKaigiに行くと自分が世界最高のエンジニアになる可能性が得られます。 参加しても必ず「世界最高のエンジニア」になれるわけではありません。 でも、参加すると、その可能性は0でなくなります。

SIerでのプログラマ教育

現状は次の記事に非常にリアルに書かれていると思います。

www.megamouth.info

現状わかったから、じゃあ、どうしようか?という話を書きます。

今回扱わない話

適性ミスマッチについては扱いません。 新卒でIT企業に開発職で入ってみたが、プログラマが向いていないと気づいたときの話です。 これはメンバーシップ型雇用の欠点なので、1企業がどうこうする話ではないので、扱いません。

特に規模の小さな会社では、他の職種への転換が難しいです。 かといって、今すぐジョブ型雇用に転換できるかといえば、新卒の職業訓練する機関が足りてないので難しそうです。

DIVE INTO CODE | プログラミングスクール のような企業が増え、競争によって洗練されると違うのかもしれません*1

社内教育

ブートキャンプ

現在では、私が新卒であった2002年頃に比べ、ブートキャンプ的な新入社員研修の効率はかなり上げれる社会情勢になってきました。

常駐を要求されない案件が増えてきました。 これにより開発チームは社内で働くことができるようになりました。 現役エンジニアが社内で働いていれば、現役エンジニアの労働時間の一部を研修に当てることができます。

エンジニアのが常駐していると、研修に参加するには案件から抜ける必要があります。 そうすると研修に参加するエンジニアの売上が100%減になるため、研修にかかるコストが大きくなります。 そのため研修の教師役には、何らかの理由で現場から引退したエンジニアが当たることが多かったです。 教師役の知識が制約になって、現場で使っている技術を題材できないことがありました。

次に、今は社会人プログラミングスクールが出てきたため、未経験を新卒採用する必要がなくなりました。 新卒採用の問題点は、入社時期が年間で特定の一日に固定されることです。 これが制約となって、研修スケジュールが決まります。 つまり4月〜5月に多くの新人をまとめて研修する必要があります。 複数の教師役を一時期に投入する必要があります。

新卒採用をやめれば、未経験を通年採用できます。 さらに一人ずつ採用することができます。 すると研修内容を特定の一人に対して設計することができます。 研修を複数人に同時に実施すると、どうしても進みが遅い人に合わせて進める必要があります。 研修効率が良くない人が出てきます。 一人ずつ採用すれば、この効率を上げることができます。

常駐案件をやめ、未経験者を通年採用すると、複数人の現役エンジニアが分担して一人の未経験者を教育することができます。 これは15年前の新卒採用一括研修と比較すると、大きなアドバンテージです。

OJT

一方でOJTに関しては、未だに大きな問題があります。

受託開発にて、教育をプロジェクトマネージャーの責務にするのは無理があります。 プロジェクトの利益と教育のコストは矛盾しています。 プロジェクトの利益を上げるには、教育に掛かるコストを削って

ベテランプログラマでも困るような雑な投げ方で案件を振って、出来たらヨシヨシ、出来なかったら、ハイハイとばかりに全部書き直して納品

するほうが効率が良いのです。

それでも最大限の善意で教育はします。しますが、掛けられるコストの上限はプロジェクトの利益です。 その結果

話したりハンズオンしている時に、あっこの子、変数のことわかってないな、と感じたら、ホワイトボードを持ち出してきて、例の"x"と書いた箱の絵に矢印を引いて、値が入っている図を書いて、「わかった?」「あ、はい」みたいなやり取りをして終わり、という程度の「教育」

になります。

これを打破するには、プロジェクト外の人が教育する必要があると思っています。 そのためにも常駐しない案件は大事です*2。 具体的な施策は模索中です。

プログラミング教育の難しさ

ここからは愚痴っぽい話です。興味がない人は、この辺でお帰りください。

プログラミング教育

教育は難しいです。 私の教育のイメージは「100mの壁へのチャレンジを、1mずつにわけて100回のチャレンジに変える」ことです。 教える側の教育スキルが不足していると、チャレンジの分割が上手くできず、100mの壁を見せてそのまま挑戦させます。

ベテランプログラマでも困るような雑な投げ方で案件を振って

です。多少上手くなっても、50mずつ2つにわけれるとか、せいぜい10mずつの10個にわけれる程度です。

現時点では社会全体で、プログラミング教育のノウハウが蓄積されていません。 おそらく誰も100分割することはできていないのでしょう。 長い伝統があり教えるプロセスが細かく分割されている分野は小学校教育です。 例えば、小学校の算数の授業時間は1011時間*3です。 126営業日です。小学校算数を理解するだけでも、半年ぐらいは掛かるわけです。 それより遥かに複雑なプログラミングの入門が1〜2ヶ月の社内研修でできるわけないのです。

というわけでOJT、さらに主に個人の自習に頼っているのが、現状のプログラミング教育です。 そしてOJTだけで生産されるのは

この術式から得られる人材というのは、どんな要件に対しても、ググり&コピペで向かい撃つ、素手で熊を殴り続ける、よくわからないグラップラーのような存在

です。

プログラムに関するすごい知見を有した向上心の塊みたいな俊英

になるのは、自習したやつだけです。

インプットとアウトプット

ところが、自習できるには自習のスキルが必要です。 インプットとアウトプットです。

を読んでも、インプットとアウトプットができることが前提です。

インプットは、要するに、日本語が読めて、論理が理解できて、応用できることです。 アウトプットは日本語が書けて、その日本語の論理が通っていて、検証できることです。

アウトプットの訓練はやろうと思えばできます。文章を書かせて添削すればいいのです。 コードレビューとそんなに変りません。ソースコードのイディオムが日本語のイディオムに変わるだけです。 議事録の添削であるとか、読書レポートの添削であるとかやればなんとかなります。 多くの場合は、「喋る日本語」と「書く日本語」が違うことを知らずに「喋る日本語」をそのまま書いていることが原因です。 「書く日本語」のテクニックを教えれば、日本語が書けるようになります。 作業量としては、対象者の元のスキルに依存しますが、A4 2枚のレポートを10回ぐらい書き直させれば、それなりの日本語になります。 お互い時間は掛かりますが、やろうと思えばできます*4

インプットの訓練は正直わかりません。 インプットはアウトプットと違い目に見えません。 何に躓いているのか見えません。 文法的に読み取れるかどうかの訓練はできると思います。 おそらく問題はそこではありません。

本の題材と自分の経験がミスマッチだった時に、まったく何も読み取れなくなる人がいます。 私は、そういう場合に、話の抽象度を上げてコンテキストを一致させます。 例えば

を読んだ時に、工場長をやった経験も無ければ奥さんに出ていかれた経験はないので、そのまま役に立つノウハウを得ることはできません。 それでも「ボトルネックを探してそれを中心に最適化していく」概念は理解できます。 そこから「ソフトウェア開発プロセスボトルネックは何か?」という問いを得ることができます。

これができない人がいます。 できないということは未経験の内容の本が読めません。つまり新しい技術を本から学ぶことができません。 この人達は、本を読む時に、その文言をなんのアレンジもせずに、ただひたすら文を暗記しようとするようです。 想像では、娯楽として本を読んだ経験がなく、読んだことある本の90%ぐらいが教科書と参考書だったのではないかと思っています。 そのため「読書とは本の内容を暗記すること」で「想像のために刺激を得るためのもの」とは認識していないようです。 これに対しては読書会のように複数人で一つの本を読んで、読み方の違いを共有するのが良いのではないかと予想しています。 読書会は同期的なイベントで、時間調整の都合があり、今の所試したことはありません*5

論理的思考力

「プログラミングが論理的思考力を育てる」という言説がありますが、あれは半分正解で半分不正解です。 論理的思考力は

  1. 論理を読み取る
  2. 論理を適用する
  3. 適用を検証する

のプロセスで成り立っています。 プログラミングが強いのは3の「適用を検証する」環境を作るのが早いところです。 ただ、早すぎるのです。1と2を飛ばしても作れてしまうのです。

つまり

ググり&コピペで向かい撃つ、素手で熊を殴り続ける

部分だけが繰り返せるのです。

「論理的思考力を育てる」面に関しては、検証環境の作りやすさが、既存の手工業よりは簡単という程度に思えます。

  1. 論理を読み取る
  2. 論理を適用する

が、できる人にとっては、検証環境の作りやすさは大きな武器になると思います。 これはインプットとアウトプットの能力があると同値です。 プログラミングは、自習できる人は大きく力を伸ばせるが、できない人はグラップラーになる分野のようです。

そして自習に必要な、日本語の読み書きの能力はOJTでは伸ばせません。 プロジェクト中の教育で「日本語の読み書きを教える」って、意味わからないですよね? ブートキャンプ的な教育でもそうです。日本語教えるよりプログラミング言語を教えたいですよね? 採用の時に日本語能力でフィルタリングするといいのかもしれません。

*1:現時点は受け入れ側企業としては、プログラミングスクールの卒業生は「即戦力」とは言い難いと感じています。自己投資に一定額以上を投入する気があって、プログラミングに興味がある人のフィルタリングには使えるとは思っています。

*2:一般論として「常駐の多いSIerでは、社内教育制度にある程度制約が掛かる」と判断して良いと思います。

*3:http://www.mext.go.jp/component/b_menu/shingi/giji/__icsFiles/afieldfile/2015/11/09/1363415_006.pdf

*4:ブログをこんだけ書いていて、技術同人誌を3冊書いて、技術同人誌のレビュー経験もある、日本語ライティング強者の感想です。すべてのプログラマが今すぐできることは保証しません。

*5:つまり完全に仮説です。検証していないので、問題設定があっているかすらわかりません。

2018年のふりかえりだとか2019年の目標だとか

2018年のふりかえり

発表

5回発表しました。

speakerdeck.com

Qiitaの記事のテキスト処理をして遊んでいたので、Qiitaのファンミーティングで紹介していみました。 PANQは今でも動いてはいますが、最近は開発を止めています。

speakerdeck.com

RubyKaigiでのLTです。 2018年は10年ぶりぐらいにRubyKaigiに参加しました。 久しぶりの参加なので、自己紹介代わりにLTをしました。 このLTは採用されやすくするため英語で発表しました。 発音で伝わらないと困るのでトークスクリプトをそのまま資料にしました。

speakerdeck.com

Node学園の勉強会で発表しました。 PANQの開発中にCPUヘビーな処理が必要になりました。 Node.jsのコアを使い切れない特徴とがっつり遊べる機会だったので、その話をしました。 発表直前にWebWorkerが公開されたのも良いタイミングでした。

特に結論がある発表ではなかったのですが、提示した疑問点で懇親会の議論が盛り上がって楽しかったです。

speakerdeck.com

Tokyo Rubyist Meetupにて発表。 内容はRubyKaigiのLTと同じです。本格的な発表にしました。 これも英語で発表しました。

WebSocketと比較して、SSEを推す人が居たのが新鮮でした。 WebSocketが実用できるタイミングでサーバPushに取り組んだため、僕はSSEの知見がありません。 細かい知見のやり取りができればよかったのですが、英語が・・・・ 発表はまだいいのですが、質疑応答に大苦戦しました。 質問の英語が聞き取れないのが辛いです。

speakerdeck.com

Rails Developers MeetupにてRailsについての発表です。 以前はフロントエンドをやっていて、Railsを本格的に使い始めて2年、発表できるまでの知見が貯めれてよかったです。

トラック数が多かったのでマニアックな話をそのままマニアックに話しました。 それまでの発表で、扱っている要件がレアなことがわかってきました。 20分中6分、要件の説明に多めの時間を取りました。

同人誌執筆

現代webフロントエンドデザインパターン

ledsun.booth.pm

技術書典4向けに書いた新刊です。 今年はサーバーサイドの仕事が増えることがわかっていたので、フロントエンドの知見を棚卸しする意味でデザインパターン形式にまとめました。 パターン形式だと先に目次を使って、中身を後で埋めることができます。 執筆が計画的に進められて良いです。

紙を買ってくれた人に悪いので、無料公開する予定はありません。

お仕事周り

開発

携わっている案件の開発箇所が移動したので、それに合わせてフロントエンドからバックエンドの開発がメインになりました。 また、会社がRailsに力を入れているのもあって、一番使う言語がJavaScriptからRubyに変わりました。

細かい技術的な話は、別に書くかもしれません。

マネジメント

片手手間でやっているマネジメント業の割合が増えてきました。

マネジメントの話をすると、技術の話をしているときより、同僚から辛辣なお言葉*1がいただけるので辛いです。 「面従腹背よりは良いはず」と自分に言い聞かせながら頑張っています。

面談

面談制度を整備しました。 面談のやり方が個人任せだったのを会社として方法を統一しました。

結論としては、面談から「ふりかえり」機能を取り除きました。 「ふりかえり」を実行するには、面談官にコーチング、ティーチングのスキルと、それを使い分けるスキルが必要です。 それらを面談官に要求するのは時期尚早と判断し*2、定期的なコミュニケーションを維持することを主目的にしました。

代わりに2019年は面接官を増やして、社内のコミュニケーションパスを増やす予定です。

RubyKaigi参加支援

参加支援の予算を獲得し、若者を二人連れていきました。 彼らの刺激にはなったようです。 一人にはRubyKaigiで興味を持った発表から、「RubyでつくるRuby」を紹介し、読んで学んだことを社内発表させました。

社内発表支援

2つの発表のお手伝いをしました。

1つ目は前述の「RubyでつくるRuby」の発表の支援です。 初回はすごく伝わらない発表でした。 案件での振る舞いも勘案した僕の分析上、日本語ライティング能力が足りていないようでした。 そこで読書レポート(新人エンジニアにレポートを書かせて技術書の読み方を伝える。 - @ledsun blogの形式)を書いてもらい、それを添削して日本語ライティング能力を伝えました。 添削回数なんと9回。我ながら頑張りました。

それで、自分の考えを整理してもらった上で、再度社内発表してもらいました。 二度目はちゃんと伝わる発表になりました!

2つ目は、社内の新人研修担当者が良い仕事をしていたので、その内容を社内向けに発表してもらいました。 新人研修のような地味な仕事は、社内の同僚から評価されにくにので、良い仕事をしていることを認識してもらうために、発表してもらいました。 研修の内容もかなり良いです。高度な内容を教えているという意味ではなく、受講者にフィットした研修をしているという意味で良いです。

研修内容は会社のHP(採用情報 | 株式会社ラグザイア)に、概要を書いてあります。 体制はめちゃくちゃ手厚いです。 日本で1 of the best 技術研修と言って差し支えないと思っています。

社内発表は好評だったので、2019年はエンジニア全員強制的に発表することにしました。 実は5年ぶり2回目の挑戦です(社内勉強会はヤメだ。自主的はいらん、全員技術発表だ! - @ledsun blog)。

ペアプロ

id:vestige に「面談で相手によって、情報交換が上手く行くときと行かないときがある」と相談をした時に「一緒に作業したらもっと色々わかるんじゃない?」とアドバイスをもらいました。 それで社内でペアプロに挑戦しました。

教育目的で、プロジェクトが違う若手とシニアでペアを組んでみました。

所管としては、プロジェクトが違う人に対して指導するほうが、プロジェクトの納期のプレッシャーを気にしなくて良いので、教育に集中できるように思います。 (メンバーの教育を(有期の)プロジェクトリーダーの責務に入れてはいけない - @ledsun blog

まだ、1〜2回しか実施できていないので、道半ばです。 2019年はもう少し回数を重ねて、ペアプロスキルを高めたいです。

技術同人誌のレビュー

何年か前にレビューしたときのお礼を今年になって言われる機会がありました。 調子に乗って、技術同人誌のレビューをやりました。

inutetraplus.booth.pm

フロントエンドをやっていたことはありますが、WebComponentsえをやったことはありませんでした。 レビューをするだけで、最新状況にキャッチアップできるというお得な体験でした。

興味が出てきたので、次フロントエンドやるときはWebComponentsも使いたいです。

周りの人を強化する

2018年に気づいた自分の謎能力です。 今の所上手く言語化できていません。

結果から推測すると、僕は自分の周りの人の能力を強化しているように思えます。 「社内発表の支援をする」のような、能動的にやっていることはそれ以外にもあるように思います。 要因は「肯定的なスタイルで人の話を聞く」のような気がしますが、よくわかりません。

案件であれば「要件の整理やヤックシェービングを巻き取って、若手に実装をガンガンやらせる」みたいなアプローチをすることはあります。 ボトルネック発見能力とキャッチアップ能力の合わせ技です。 この辺は自覚している得意分野です。

それとも違うような気がします。

2019年

春か秋かわかりませんが同人誌を一冊は書きたいです。

OSSへのコントリビューション数を増やしたいです。 お仕事でOSS開発しているので、元々コントリビューション数自体は多いのですが、お仕事と関係ないOSSへのコントリビューションを増やしたいです。 目的は

  • 日頃お世話になっているののお礼というのが一点
  • 技術力の研鑽が一点
  • 技術者としてのポゼッションを増やすというのが一点

です。

2018年の発表はニッチな領域に攻めることで価値を高めました。 2019年は、もっと多くの人の役に立つ発表がしたいです。 ニーズと合うかは時の運なので「できれば」ぐらいの目標です。

英語のトーク力を増やしたいです。 これは1年でなんとかなる気がしないので、長い目で鍛えて行こうと思います。

厄年らしいので健康に気をつけていきます。 運動習慣を身につけようと思います。

*1:人の出したアイデアに「無駄」とか「無意味」とか断定的なお言葉をガンガン頂けます。

*2:「ふりかえり」を目指すこと自体にめちゃくちゃ反対されました。それから必要なスキルをトレーニングするリソースも現段階ですぐには確保できなさそうです。

Ruby2.6 に関数合成オペレータが追加されたのでFizzBuzzで遊ぶ

fizzbuzz = -> x { x % 15 == 0 ? 'FizzBuzz' : x % 5 == 0 ? 'Buzz' : x % 3 == 0 ? 'Fizz' : x }
p_fizzbuzz = -> x { p x } << fizzbuzz

1.upto(15){ | n | p_fizzbuzz.call(n) }

pと合成するだけでは面白くないですね。

fizz = -> x { (x.is_a? Integer) && x % 3 == 0 ? 'Fizz' : x }
buzz = -> x { (x.is_a? Integer) && x % 5 == 0 ? 'Buzz' : x }
fizzbuzz = -> x { (x.is_a? Integer) && x % 15 == 0 ? 'FizzBuzz' : x }
do_fizzbuzz = fizz << buzz << fizzbuzz

1.upto(15){ | n | p do_fizzbuzz.call(n) }

条件に一致したら文字に変換、一致しなかったら素通しする関数にわけて合成してみました。

関数合成の一般的な良い使い道は特に思いつきません。

参考:プロと読み解く Ruby 2.6 NEWS ファイル - クックパッド開発者ブログ

#railsdm Rails Developers Meetup 2018 Day 4 Nouvelle Vague で発表しました

Rails Developers Meetup 2018 Day 4 Nouvelle Vague で発表してきました。 趣旨は「ActiveRecordやActiveJobなどのRailsの用意した抽象インタフェースを使うと、アプリケーションのミドルウェア構成、ひいてはアーキテクチャの決定を遅らせる事ができる。その分開発に集中できる」です。

speakerdeck.com

Rails中・上級者向けの話をして良いとのことだったので、Rails開発のレアのシチュエーションの話をしました。 そもそも対象のアプリケーション要件が特殊なので、その説明に20分中5〜6分使いました。

内容が少しマニアックすぎたので、伝わる相手は多くはなかったようです。 その中でも良いリアクションを頂けたので、発表した身としては大変満足でした。

反応

私の前にRail Wayはなく、私の後ろにRail Wayはある*1

補足

Webフロントエンド

Web用フロントエンドとブラウザの間はWebSocketでつないで、サーバから非同期に結果を送っています。 今回はWeb用フロントエンドに関わる話はしないので、端折りました。

Web用フロントエンドでの、WebSocket(とスレッド)に関わる、技術的落とし穴の話は下でしました。 speakerdeck.com アプリケーションの構成は説明していません。

Sucker Punch

qiita.com

発表時に詳細を端折った、Sucker Punchに当てるモンキーパッチの説明をQiitaに書きました。

同じく端折ったDockerの話もどっかーに書きたいです。

ActiveRecord

発表後に聞いた話では、該アプリケーションはSQLiteからPostgreSQLへ、ソースコード修正無しで、乗り換えできたそうです。 ActiveRecordの抽象化の完成度、すごいですね。

その他

動画もあるみたいです。恥ずかしいので自分では、見ていません。

twitter.com

*1:実際は先人が作った巨大なRail Wayの端っこを、ちょっと開拓しただけです。

builderscon tokyo 2018 で得たもの #builderscon

一ヶ月前の話ですが、やはり印象深かったので書きます。

植山類さんのセッション

大変良かったです。 僕は類マニアなので、以上に高いテンションになっている可能性があります。 その辺は差っ引いて読んでください。 新しい知見が得られたというか、目の前で人間が喋っているのが聞けたのが良かったです。

主な内容は以前のブログ記事と大体同じです。 note.mu

動画も公開されているようです。 builderscon.io

ハートが撃ち抜かれた

特に印象的だったのは「カーゴ・カルト・プログラミングを徹底的に避ける」話です。 勝手な想像ですが、カーゴ・カルト・プログラミングを避けた結果、ソースコードを理解していない部分がなくなり、アドバイスをくれる誰よりも自分がソースコードを理解している自信になり、定石破りが(自分のシチュエーションでは定石が有効でないことを喝破)できたのではないかと思います。

それでlldの性能と開発効率が上がって、多くのOSSプロダクトに採用されるようになった実績を、目の前の人間が喋っていることがすごいです。 ハートが撃ち抜かれた。

Turing Complete FMで何度も聞いている声ですが、実際に喋っているのをみると、最初から天才プログラマだったわけじゃなく、本人の努力で今があるのがわかって良いです。ブログ記事には上手く行った話を書くことが多いので、ブログだけ読んでいると「超天才か!」と思うのですが、「人間、天才みたいな属性だけで雑に説明できるわけねえよ」が実感できて良いです。

カーゴ・カルト・プログラミングを避ける

それで、感銘を受けて、「遅い処理を非同期化する」タスクに取り組んでいる時に、

  1. 「遅いのはわかるけど、どこが遅いか計測すべき」と計測し、データ作成でなく、データ送信に時間が時間がかかっていることを確認
  2. データ送信の振る舞いをみるとデータの送信量に依存せずに遅くなることがあるので、厳密にはデータ送信ではなく、コネクション確立に時間がかかっている
  3. ソースコードにコネクションを使い回す修正を入れて計測してみる
  4. 既存のソースコードには修正を邪魔する、不要な抽象が挟まっている。自分で書いたし、書いたときは良かれと思って書いています。まずは、この抽象をはずす

それから「コネクションを使い回す実装をいれ、それで再度計測して効果があるか確認する必要があるな」と、のたうち回っているところです。

当初は「非同期化するだけなら、スレッドかActiveJobを使えばいいだけ」と軽く見ていたタスクでした。それでも、「カーゴ・カルト・プログラミングを避ける」気持ちで見ると、既存の実装の良くない点(コネクションを使い捨てている)と、設計の良くない点(不要な抽象がある)が、見つかり新たな知見が得られます。

まだ調査中なので、本当にこの方針で実装するのが正しいのかはわかりません。例えば、送信先に依存しているかもしれない。そうならば送信先を制限した方がいいかもしれません。それでも、この調査で得られた知見はより正しい判断につながるでしょう。すくなくとも、不要な抽象(本当に何の役にもたってなさそう)が見つかったのは、この先ソースコードに技術的負債を抱えて進んでいくより良いでしょう。この辺は、時間的プレッシャーに負けそうな自分に言い聞かせている理由付けです。

そういうお気持ちでプログラミングを進めていきたい。

メンバーの教育を(有期の)プロジェクトリーダーの責務に入れてはいけない

t.co

を読んで考えた話です。

30歳近くになっても無能、ということは、そいつはほとんどの場合、一生無能だ

と刺激的な言葉が使われています。 状況を限定すれば、一理あると感じる点があります。 表題の「メンバーの教育を(有期の)プロジェクトリーダーの責務に入れてはいけない」です。

結論

プロジェクトリーダーの最大の責務はプロジェクトを成功です。 メンバーの教育は、プロジェクト成功の阻害要因になっているときだけやります。

無能なやつには、意見を聞いても、ろくな話は出てこない。時間がもったいない

の例では、「意見を聞かない」という簡単な回避策があります。 回避策があるときは、教育の手間を掛けてはいけません。

そうは言っても、一緒に仕事する仲間はできるだけ賢くあって欲しい、自分の足を引っ張らないで欲しいと思うのが人間です。

教育は誰がやればいいのか?

ティーチングとコーチン

メンバーの教育は上司(ライン上の上司)がやります。 ここで、教育を大きく2つにわけます。 ティーチングとコーチングです。

プロジェクトに直結したティーチングはプロジェクトリーダーの責務です。 例えば「gitの操作に不慣れなメンバーに、gitの使い方を教える」(または教える人を割り当てる)のはプロジェクトリーダーの仕事です。

コーチングでは、本人に思考を促し、失敗に対して工夫し、何度か失敗を繰り返すのを見守り、成功まで導きます。 要はPDCAサイクルを回すサポートをします。 これは上司の責務です。

コーチの責務

コーチングの目的は、未知の問題にぶつかった時に、自ら工夫してチャレンジしていく能力と自信を身に着けさせることです。 本人が試行錯誤して成功にたどり着く必要があります。 時間が必要です。 またコーチ役は「教えちゃえば簡単なのに」を我慢する必要があります。

一人の人間が同時に、プロジェクトの成功を急ぐのと、メンバーの成長を待つのをやるのは無理です。 人を分けましょう。 プロジェクトリーダーが安心して、教育を手放せるように、他に教育を責務とする人を用意しましょう*1

*1:コーチングを上司の責務にして、プロジェクトリーダーの責務から引き剥がすのが、組織を作る人(CTO、VP of Engineering、人事部門)の責務です。

革ベルトを作る計画を立案せよ

動機

レザーパンチを買ってベルトに穴を開けていたら、革ベルトが作りたくなりました。

予算と手順を立ててみます。

方針

なるべく工数を下げ、可能な限り早く完成にたどり着いて、お手軽に達成感を得ます。

そのため、

  • 手作業での革のカット
  • 手作業での革の縫い合わせ
  • スタッズ等の装飾
  • バックルなどパーツへのこだわり

は放棄します。加工済みの部品で値段が安いものを組み合わせて作成します。

予算

種類 名前 URL サイズ 単価 数量 金額 送料
ベルト 昭南多脂ベンズ ベルト ナチュラ http://l-phoenix.shop-pro.jp/?pid=62568295 4cm 2600 1 2600
バックル固定部分漉き加工 http://l-phoenix.shop-pro.jp/?pid=46123850 200 1 200
バックル バックル 415-35 真鍮 http://l-phoenix.shop-pro.jp/?pid=88940283 680 1 680
ループ ループ35-1B http://l-phoenix.shop-pro.jp/?pid=97645764 340 1 340
カタビス http://l-phoenix.shop-pro.jp/?pid=62568295 9x4x5 70 4 280
トコノール https://item.rakuten.co.jp/lc-palette/s72002/ 324 1 324 220

総額 4,644円

作業手順

  1. バックルを通す用の穴位置を決める
  2. バックルを通す用の穴を開ける
  3. バックルを取り付ける穴の位置を決める
  4. バックルを取り付ける穴を開ける
  5. バックルを取り付ける
  6. 穴の位置を決める
  7. 穴を開ける
  8. 理想の長さを測る
  9. ベルトを切る
  10. トコノールで断面を処理する
  11. ミンクオイルで表面を磨く

11手順、1手順15分として3時間程度で完成できそうです。

参考URL

ミントチョコレートの起源

アイスに限らずにミントとチョコレートの組み合わせの起源を探ります。

1962年 アフターエイト

アフターエイトは初めて知った大人のミントチョコレート : イギリスの食、イギリスの料理&菓子

アフターエイトは、1962年にイギリスで生まれたチョコレート菓子。

単純にチョコレートとミントを組み合わせるだけなので、一番古い組み合わせなのかと予想していました。 1962年で予想外に新しいです。

1945年 チョコミントアイス

チョコミントの発祥はどこ?ミントがトルコならチョコミントは?

1945年にアメリカのカルフォルニアでバスキンさんとロビンスさんによって バスキン・ロビンスという世界最大のアイスクリームのチェーン店が誕生しました。

これがサーティーワンの発祥です。

現地では、この頃からチョコミントフレーバーは人気のアイスでした。

チョコミントと言えばアイスぐらいの王道だけあって、 1945年には既にチョコミントアイスがあるようです。

1900年 グラスホッパー

カクテルガイド|カクテルを知る

現代式のカクテル=「器具を使って作る氷で冷やしたミクスト・ドリンク」となったのは1879年の製氷機の発明以来です。

アイスクリームが大衆化するよりは、カクテルが普及した方が古いので、チョコミントアイスより古そうです。

グラスホッパー・カクテル(Grasshopper Cocktail)のレシピ・作り方| しっぽり...

サンフランシスコにある有名な老舗ホテルのパレス・ホテル(The Palace Hotel)のバーテンダーだったハリー・オブライエン(Harry O'Brien)が1900年頃に考案した

1900年。予想通り、今の所発見できた最古の情報です。

だとすると、ミントチョコレートはもともとカクテルの味として誕生した、大人の味だったのでしょうか? お菓子のチョコレートとミントを組み合わせる方が作るのが簡単そうです。

チョコレートの歴史 - Wikipedia

1875年にミルクチョコレートの販売を始めた。またミルクチョコレート製造には、牛乳から水分を抜く必要があったが、ダニエルは隣りに住んでいたベビーフード生産業者のアンリ・ネスレネスレ創業者)と協力して研究を行った

甘いチョコレートが普及したのが19世紀末なようです。 ミントとチョコレートの組み合わせは、グラスホッパーが起源で、アメリカ生まれの、可能性もありそうです。

Ruby on Railsの開発環境でマルチスレッドでクラス定義を探索すると刺さるが再現できなかった話

現象

Ruby on Railsの、ActiveJob内で起動したスレッドで非同期にDBに書き込もうとすると、ActiveRercordのクラス探索で無限に待って固まります。

わかっている条件

  • ActiveJobで起きる
  • ActiveJobのQueueAdapterにはAsync adapterを使っている
  • DBへの接続以前のActiveRercordのクラス探索で固まる

例えば

Thread.start do
  executor = Lodqa::OneByOneExecutor.new dataset, query, debug: false

  # Bind events to colletc answers
  collected_answers = []
  executor.on(:answer) do |_, val|
    Answer
  end
end

このような処理*1をApplicationJobクラスで実行すると、Answerの探索で固まり、無限に待ちます。

Thread.startでスレッドを開始するほか、executor内でEventMachine#deferを使って更に複数スレッドで処理を行っています。

再現の努力

そこで、再現する最小のソースコードを書いてみます。

class CircularDependencyDetectedJob < ApplicationJob
  queue_as :default

  rescue_from(StandardError) do |exception|
    logger.fatal exception
  end

  def perform(*_args)
    Thread.new { EventMachine.run }

    1.times do
      Thread.new do
        2.times do
          EM.defer do
            Answer
            p 'Goal!!!'
          end
        end
      end
    end
  end
end

エラーが発生

これを実行すると、固まりはしませんが、次のようなエラーが発生します。

/usr/src/myapp # rails c
Loading development environment (Rails 5.2.0)
irb(main):001:0> CircularDependencyDetectedJob.perform_later
Enqueued CircularDependencyDetectedJob (Job ID: 9676f7ff-5c23-411c-84e3-33aca66c5eae) to Async(default)
=> #<CircularDependencyDetectedJob:0x0000562444702420 @arguments=[], @job_id="9676f7ff-5c23-411c-84e3-33aca66c5eae", @queue_name="default", @priority=nil, @executions=0, @provider_job_id="39bf3d7d-051f-4915-a752-17686211c958">
irb(main):002:0> Performing CircularDependencyDetectedJob (Job ID: 9676f7ff-5c23-411c-84e3-33aca66c5eae) from Async(default)
Performed CircularDependencyDetectedJob (Job ID: 9676f7ff-5c23-411c-84e3-33aca66c5eae) from Async(default) in 62.71ms
#<Thread:0x000056244503a970@/usr/local/bundle/gems/eventmachine-1.2.7/lib/eventmachine.rb:1067 run> terminated with exception (report_on_exception is true):
Traceback (most recent call last):
    7: from /usr/local/bundle/gems/eventmachine-1.2.7/lib/eventmachine.rb:1077:in `block in spawn_threadpool'
    6: from /usr/src/myapp/app/jobs/circular_dependency_detected_job.rb:16:in `block (4 levels) in perform'
    5: from /usr/local/bundle/gems/activesupport-5.2.0/lib/active_support/dependencies.rb:193:in `const_missing'
    4: from /usr/local/bundle/gems/bootsnap-1.3.0/lib/bootsnap/load_path_cache/core_ext/active_support.rb:43:in `load_missing_constant'
    3: from /usr/local/bundle/gems/activesupport-5.2.0/lib/active_support/dependencies.rb:534:in `load_missing_constant'
    2: from /usr/local/bundle/gems/activesupport-5.2.0/lib/active_support/dependencies.rb:193:in `const_missing'
    1: from /usr/local/bundle/gems/bootsnap-1.3.0/lib/bootsnap/load_path_cache/core_ext/active_support.rb:43:in `load_missing_constant'
/usr/local/bundle/gems/activesupport-5.2.0/lib/active_support/dependencies.rb:500:in `load_missing_constant': Circular dependency detected while autoloading constant Answer (RuntimeError)
#<Thread:0x00005624446907f8@/usr/src/myapp/app/jobs/circular_dependency_detected_job.rb:9 aborting> terminated with exception (report_on_exception is true):
Traceback (most recent call last):
    7: from /usr/local/bundle/gems/eventmachine-1.2.7/lib/eventmachine.rb:1077:in `block in spawn_threadpool'
    6: from /usr/src/myapp/app/jobs/circular_dependency_detected_job.rb:16:in `block (4 levels) in perform'
    5: from /usr/local/bundle/gems/activesupport-5.2.0/lib/active_support/dependencies.rb:193:in `const_missing'
    4: from /usr/local/bundle/gems/bootsnap-1.3.0/lib/bootsnap/load_path_cache/core_ext/active_support.rb:43:in `load_missing_constant'
    3: from /usr/local/bundle/gems/activesupport-5.2.0/lib/active_support/dependencies.rb:534:in `load_missing_constant'
    2: from /usr/local/bundle/gems/activesupport-5.2.0/lib/active_support/dependencies.rb:193:in `const_missing'
    1: from /usr/local/bundle/gems/bootsnap-1.3.0/lib/bootsnap/load_path_cache/core_ext/active_support.rb:43:in `load_missing_constant'
/usr/local/bundle/gems/activesupport-5.2.0/lib/active_support/dependencies.rb:500:in `load_missing_constant': Circular dependency detected while autoloading constant Answer (RuntimeError)
Traceback (most recent call last):
    7: from /usr/local/bundle/gems/eventmachine-1.2.7/lib/eventmachine.rb:1077:in `block in spawn_threadpool'
    6: from /usr/src/myapp/app/jobs/circular_dependency_detected_job.rb:16:in `block (4 levels) in perform'
    5: from /usr/local/bundle/gems/activesupport-5.2.0/lib/active_support/dependencies.rb:193:in `const_missing'
    4: from /usr/local/bundle/gems/bootsnap-1.3.0/lib/bootsnap/load_path_cache/core_ext/active_support.rb:43:in `load_missing_constant'
    3: from /usr/local/bundle/gems/activesupport-5.2.0/lib/active_support/dependencies.rb:534:in `load_missing_constant'
    2: from /usr/local/bundle/gems/activesupport-5.2.0/lib/active_support/dependencies.rb:193:in `const_missing'
    1: from /usr/local/bundle/gems/bootsnap-1.3.0/lib/bootsnap/load_path_cache/core_ext/active_support.rb:43:in `load_missing_constant'
/usr/local/bundle/gems/activesupport-5.2.0/lib/active_support/dependencies.rb:500:in `load_missing_constant': Circular dependency detected while autoloading constant Answer (RuntimeError)

エラーの原因

該当のソースコードを読むと

if loading.include?(expanded)
  raise "Circular dependency detected while autoloading constant #{qualified_name}"
else

loadingに自身が含まれていたら、循環依存で読み込みをやめる処理です。 マルチスレッドで読み込むと、循環依存していなくても、このチェックをするタイミングで他スレッドで自クラスを読み込んでいることがあるので、発生しているようです*2

対応

この処理は定数の自動読み込みと再読み込み | Rails ガイドのための処理です。 自動読み込みを停止してみましょう。

Rails アプリケーションを設定する - Railsガイド

config.eager_loadをtrueにすると、config.eager_load_namespacesに登録された事前一括読み込み(eager loading)用の名前空間をすべて読み込みます。ここにはアプリケーション、エンジン、Railsフレームワークを含むあらゆる登録済み名前空間が含まれます。

config/environments/development.rb

config.eager_load = true

を指定すると、このエラーは起きなくなります。

やはり、「定数の自動読み込み」と関係がありそうです。

残る謎

  • マルチスレッドであれば起きるはずなのに、Rails consoleで次のソースコードを実行しても発生しない
1000.times { Thread.new { Answer } }
  • 元の現象では固まるのに、再現コードではエラーが発生する

暫定の結論

元のソースコードの、クラス探索で固まる現象もeager_loadを有効にすると発生しなくなります。 これ以上調査時間が取れないので、この記事で供養して完了とします。

*1:発生したソースコードと完全には一致していません。

*2:GILがあるから並列に動かないし衝突しないはず・・・と思ったら、ファイルからクラスを読み込む処理なので並列に動くようです。

<Repository (class)> yielded |nil| to block with

RSpecMockで見慣れないエラーが起こせました。 再現コードをメモります。

class Repository; end

RSpec.describe do
  it do
    expect(Repository).to receive(:doc).and_yield(nil)
    Repository.doc {}
  end
end

実行すると

~ rspec spec.rb
F

Failures:

  1) should receive doc(*(any args)) 1 time
     Failure/Error: Repository.doc {}
       #<Repository (class)> yielded |nil| to block with
     # ./spec.rb:6:in `block (2 levels) in <top (required)>'

Finished in 0.01831 seconds (files took 0.20957 seconds to load)
1 example, 1 failure

Failed examples:

rspec ./spec.rb:4 # should receive doc(*(any args)) 1 time

ちょっとおもしろいのは

Repository.doc {|a|}

のように、ブロック引数を指定してRepository.docを呼び出すと、このエラーは出なくなります。 RSpecMockは、ブロック引数の個数を見て動きを変えているようです。

Node学園 31時限目でLTしました #tng31

Node.jsの並列プログラミングについて話してきました。 大量のhtmlファイルをパースする処理を、 child_process.forkを使って複数プロセスへ分散した話をしました。 また、worker_threadsを試した話をしました。 めっちゃフィードバックがもらえて楽しかったです。

speakerdeck.com

フィードバック

「並列数を増やした時のボトルネックがわかりません」と発表したところ、 楽しんでいただけだようで、たくさんのアイデアをいただきました。

週末の間、ボトルネックとして成り立つ確率と、検証方法を考えてみました。 一通り検証する予定です*1

ボトルネックファイル仮説

いずれか(複数の可能性もある)のhtmlファイルが巨大であるなどの理由で時間が掛かっていて、ボトルネックになっているのでは?

1ファイル辺りの処理時間は数十ms程度なので、多少ばらついても秒単位のボトルネックにならないようにも思えます。 しかし、現在はラウンドロビンでタスクをサブプロセスに割り振っています。 何回起動しても、同じプロセスに同じタスクが割り振られます。 1ファイルで秒単位のばらつきが出ないとしても、タスク群としては出る可能性があります。

プロセス単位で、終了までの時間を出力すれば検証できそうです。

メモリ仮説

プロセス数が増えて消費メモリが大きくなるがボトルネックになっているのでは?

現代では36プロセスが多すぎるとは思えません。 また、EC2はデフォルトでは、swapファイルの割当がなく、メモリが足りなくなると即エラーになりOOM killerが飛んできます。 今の所、OOM killerを観測していません。ただ、すべてのサブプロセスの状態を監視しているわけではないので、知らずに死んでいる可能性はあります。 この場合も、出力データが壊れても、処理時間には影響がなさそうです。

実行時の消費メモリを観測すれば検証できそうです。

物理コア仮説

AWS EC2で試したので、Virtual CPUが36コアだとしても物理CPUが10コアしか無いため、実際は36並列まで伸びないのでは?

クラウド環境にありそうな話です。 ただし、グラフがなめらかな点が気になります。 コア数が足りなくなればそこから水平になると予想できますが、公開したグラフで25コアまでは微減です。 また、機械学習勢の人たちは、僕より早くマルチコア環境を試していそうなので、そういう事実があれば炎上していそうなものです。

物理 CPU、CPU コア、および論理 CPU の数を確認する - Red Hat Customer Portal のコマンドで物理CPU数・コア数と論理コア数を確認すれば何かわかるかもしれません。

共有ログ仮説

書いているログ・ファイルが一つであれば、書き込みがボトルネックになっているのでは?

実際ログは一つのファイルなのでありそうです。 発表中に説明していませんが、重複ログを書きこまない機能があります。 ログを書くかどうかはログの状態によってばらつきます。 この機能が影響しているにしては、グラフがきれいすぎる気もします。

ログ書き込みを止めて計測すれば検証できそうです。

その他

GNU Parallel使うといいのでは?

元々Go lang*2Ruby*3辺りに移植して、多言語の実行環境と性能比較したいとは思っていました。 1から違う言語で書き直すのは大変なので、Node.jsのまま入出力だけ修正して、GNU Parallelを使うというのは、 より短い時間で試せるいいアイデアです。

その他の感想

参考に次の記事を挙げました。 何人か「この記事を参考にしてスクレイピングに挑戦しています」と言ってくれる人がいました。 書いてよかったです。

qiita.com

*1:確率が低くても否定材料を集めておくことは、デバッグ最速理論的に大事なのです

*2:この手の並列はgo langのGoroutinesが強いイメージなので、実際に試してみたいです。

*3:rubexを使うとGILを超えて並列化できるという触れ込みなので実際に試してみたいです。

#rubykaigi 2018でLTしました

会社がお金を出してくれるのでRubyKaigiへの参加が決まっていました。 どうせ参加するなら何かしら発表したいです。 LTに応募したら通りました。

資料です。

speakerdeck.com

結論に被さるぐらいの勢いで銅鑼が鳴りました。

英語で発表

英語での発表にチャレンジしました。 「英語の資料を日本語で発表」はやったことがあります。 「資料も発表も英語」は初めてです。

何人かに、なぜ英語で発表したのか聞かれました。 これにはいくつか理由があります。

某所で「3年以内に海外カンファレンスで発表する」を目標にしました。 海外を国際に置き換えるだけで、ぐっとハードルがさがって実現可能なことに気づきました。

募集要項が全部英語でした。 「参加者は日本人が多いハズだが、国際カンファレンスだったはず・・・」日本語と英語の発表のどっちが良いのか空気が読めませんでした。 英語なら失敗しても「ナイスチャレンジ」と言われるだけなので、英語にしました。

github.com

を見ると、内容は大体伝わっていたようなので一安心です。

二夏続けて、総額90万円、英会話学校に課金しているので、成果が出て嬉しいです。

緊張

5分とはいえ、1000人いる会場で発表するのは緊張します。 そこで敬愛する右角ヒサシを見習いThee Michelle Gun Elephantの曲で己を鼓舞しました。 f:id:ledsun:20180613181640p:plain

さらに気持ちを盛り上げるためにTシャツはLAST HEAVEN TOUR 2003のライブTシャツです。

f:id:ledsun:20180603101351j:plain

15年前の遺物ですが、役に立つのです。

参考資料

R.I.P Abe...

Roy Fieldingの論文

qiita.com

RESTについてRoy Fieldingの論文原著を必ず読んでください

本文は読んでいません。周辺情報を調べてみました。

Architectural Styles and the Design of Network-based Software Architectures が論文全体です。 ネットワークを使ったアプリケーションのアーキテクチャを扱った長い論文の5章 「Fielding Dissertation: CHAPTER 5: Representational State Transfer (REST)」にRESTの話があります。

タイトル「Architectural Styles and the Design of Network-based Software Architectures」でググると6780引用と出てきて驚きます。

Referencesを見ると

  1. Alexander, S. Ishikawa, M. Silverstein, M. Jacobson, I. Fiksdahl-King, and S. Angel. A Pattern Language. Oxford University > Press, New York, 1977.

Alexanderを参照していて、パターンランゲージにも関連があるようです。

英語の論文は読書会でもやらないと読める気がしません・・・。

面白かったけど役に立てられる気がしないセッション #rubykaigi

Hijacking Ruby Syntax in Ruby

クレイジーでした。Binding#local_variable_set や TracePoint を使ってRubyの言語仕様を拡張しようという趣旨です。 CRuby本体を拡張せずに、新文法のProof of conceptが書ける意味はわかります。 とはいえ、いくらなんでもやりすぎなのではという思いで一杯です。

tagomoris.hatenablog.com

TTY - Ruby alchemist’s secret potion

github.com

の紹介でした。

クレイジーでした。CLIを作るのに便利なgem群まではわかります。 とはいえ、なぜそこまでCLIなのでしょうか? プログレスバーやテーブル表示までは理解できるのですが、Markdownの表示までいくと、もうブラウザをUIに使ったほうが簡単なのではという思いです。