Blog::kfly8

prove t/foo/bar/baz.t

勘を減らしてテクノロジーの力でエンジニア組織を応援したい|Findyに入社しました👍

こんにちは。こばけんです。

8月末に約11年半勤めたモバファクを退職して、9月からFindyで働きはじめました。

モバファクでは、エンジニアとして新卒で入社し、プロダクトマネージャー、エンジニア組織開発責任者など任せていただき、たくさんの方にお世話になりました。ここ数年のモバファクでの仕事はエンジニア組織全体を改善する仕事でしたが、こちらをこじらせ、まだやるべきことはありつつも退職をさせていただました。応援の言葉をかけていただいた同僚に感謝してます。ありがとうございます。ありがとう。

Findyは、五反田の会社のよしみで繋がってはいましたが、正直入社することは想像していませんでした。が、今はもっと早く知っていれば良かったと思ってます。技術コミュニティに育てられた恩があるので、技術に関わる人や組織に向けたプロダクトを作りたいと思っていました。また、エンジニア採用も組織も問題を抱えてない会社はないので、ここに貢献できたら、シンプルに世の中よくなって、面白いだろうと。Findyはドンピシャでした。まだ入社して1週間程度なので熱に浮かされてるかもしれないですが、エンジニア組織をあらゆる面で応援をするために、また10年以上取り組めたら嬉しいです。

少し脱線しますが、私はPerlコミュニティが好きでYAPCなどの運営しています。残念ながら(?)、FindyはPerlの会社ではなかったですが、引き続きPerlコミュニティの活動はします。ちなむとFindyの会議室の名前は、TypeScript、Go、Rubyといったプログラミング言語から名前を取っています。Perlとついた会議室がなかったので、CTOのma3tkに相談してみようと思います😉

配属は、エンジニア組織のパフォーマンスを可視化するFindy Teamsです。

Findy TeamsでDevOpsのFour Keysを可視化してる様子 / ©Findy

このエントリーでは、入社のキッカケにもなったFindy Teamsについて、掘り下げて書きたいと思います。

「クエリのチューニングの結果は、どうなった?」

メトリクスのマネジメントに関する話を少し*1書きます。Findy Teamsをなぜ作っているかに直結する話です。

唐突ですが、クエリをチューニングして、CPUの利用率が落ちると嬉しいですよね。「推測するな。計測せよ。」といった金言がありますが、システムのメトリクスは仮説検証、監視などのエンジニアリングのさまざまな場面でなくてはならないと思います。

エンジニア組織のパフォーマンスのメトリクスはどうでしょうか?ちょうど良いメトリクスがあれば、クエリのチューニングのように行動した結果を学べそうです。

定性情報大事。

当たり前かもしれませんが、システムと違い、人や組織の状態の観測は簡単ではないです*2。例えば「大丈夫」といった言葉の意味が、言葉通り大丈夫でなかったりします。もし人や組織の状態を測る定番のメトリクスがあったとしても、そこにいる人たちの感覚とずれているなら、メトリクスの選定や計測方法を疑った方が良いです。人や組織の思いは複雑で難しいです。(そこがうまく噛み合うと生産的ですし幸せだと私は感じるのですが。)もしデプロイ頻度が非常に高かったとしても、駆り立てられ精神的に張り詰めている開発現場であれば、持続的な開発に支障がでることは想像しやすいと思います。この記事は、エンジニア組織で定量的なメトリクスを使うことを薦める記事にはなりますが、定性情報大事。超大事と思ってます。

ヒアリングには信頼関係と経験がいる

ただ定性情報のデメリットはあります。まず定性情報のブレは念頭にいれた方がよいです。先の「大丈夫」のような例です。ヒアリング方法によって回答が変わったり、質問者の主観が入りやすいです。例えば上司に不満がある人が、上司に直接不満を打ち明けるでしょうか?経験豊かでメンバーと良好な関係のエンジニアリングマネージャーであれば、コミュニケーションをしてチームで何が課題か判断していくことができるかもしれませんが、若手や入社したばかりのマネージャーの場合、まず定性的な情報を得ることから難しいと思います。特に急拡大する事業に、組織が適応するにはエンジニアリングマネージャーが肝だと思いますが、中途エンジニアの採用市場は競争が激しく簡単な状況ではないです。そういった背景も踏まえると、エンジニアリングマネージャーの負担を減らすことは価値になるのではないかと思ってます。

ヒアリングは時間もかかる

そして、定性情報の取得に時間がかかることもデメリットです。開発場面の例えですが、ビルドにかかる時間が、5分と30秒だと開発のスピード感は違いますよね。5分かかれば、Twitterをする時間は余裕でしょう👌それと似たことがコミュニケーションで起きがちだと思います。「タスクが忙しいから後にしよう。」「チャットで話してるから十分かな。」と同僚との雑談を避ける(無意識の)障壁はあります。フルリモートになって「廊下」がなくなったことも、障壁の例だと思います。時間がかかることは避けがちで、意識高く保たないとコミュニケーションが成り立たないとしたら、じわじわと問題がおきがちです。

アンケートは手軽だが、頻度高くはできない

ヒアリングと比べ、アンケートは属人性を減らす手軽な調査手段です。例えば、心理的安全性の提唱者のエドモントン氏が、組織の心理的安全性の状態を推し測るために7つの質問を用意しています。Googleのre:Workで参照できます。他には業績や離職率に影響を与える傾向があるeNPSは「親しい知人や友人にあなたの職場をどれくらい勧めたいか」を問うだけのシンプルなアンケートです。エンジニア組織に目を向ければ、DX Criteriaもエンジニア組織について話し合うコミュニケーションの種になります。

しかし、アンケートの形式にもデメリットがあります。アンケート頻度です。アンケート頻度が高すぎれば、回答がめんどうになってテキトウな回答になったり、回答をしなくなる悲しい結末になりがちです。本当は日々開発チームは変化しています。「新しく入ったメンバーは開発に馴染めているか?元気かな?」「イシューの作り方を変えて、サイクルタイムは改善されたか?」など。クエリチューニングほど即結果は出ないですが、ちょっとずつ変化があります。

5月に入社した同僚のアクティビティの推移。開発のオンボーディングは順調そう! / ©Findy

日々の活動を用いて、メトリクスを計測する

LeanとDevOpsの科学の第二章でエンジニア組織のパフォーマンスはどういった尺度が望ましいか記載されています。この本(あるいは調査)では、エンジニアの活動に注目し、DevOpsのFour Keysが採用されていました。この結果指標を用いて、リーンマネジメントや継続的デリバリなどのプラクティスが統計的に有意に働くことを、この本では説明しています。この本は革新的だと思っています。一つは、エンジニア組織のパフォーマンスを日々の活動の事実を用いて計測したこと。もう一つが統計的に良いプラクティスを示したこと。これによって、勘や経験に頼るのではなく、再現をしやすい科学ででエンジニア組織に貢献するプラクティスを示したからです。

少しだけ補足すると、生産性の指標として、注目すべきはプルリクエストのマージされた数などアウトプットです。書いたコード量や働いた時間ではないです。また、開発をしていると、難しいバグに悩まされプルリクエストが思うように作成できないことはよくあります。そんな時、アウトプットが少ないことを窮屈に駆り立てられて良い気持ちになるエンジニアはいないと思います。アウトプットの背景の洞察まで含めてマネジメントの仕事になると思います。エンジニア組織の活動が可視化された後、改善をどう行うかで、組織のカラーが出ますし、頭をひねるところだと思っています。エンジニア組織のパフォーマンスを定量化できたとしても万全ではなく練習が必要で、この改善支援もできたら嬉しいです。

まとめると、エンジニア組織のパフォーマンスの状態を把握する方法は、次の表のようにまとめられます。それぞれ得手不得手があり、定量的なメトリクスで即時に状況を把握し、定性的な情報も組み合わせ洞察を得るのが最適のように感じます。これをより簡単にまとめると「勘を減らしてテクノロジーの力でエンジニア組織を改善する」ということだと思います。

エンジニア組織の状態把握の方法まとめ

Findy Teamsの今

エンジニア組織のパフォーマンスを開発活動に基づき可視化し日々の改善を支援することが、Findy Teamsのコンセプトになります。例えば、先に触れたDevOpsのFour Keysやサイクルタイムなどの可視化をしています。ありがたいことに利用いただいてる企業は順調に増えています。資金調達もありました。入社して一番びっくりしたことは、熱量のこもった大量のフィードバックです。Findy Teamsに課題が沢山ある証拠でもありますが、この期待に応えることで多くのエンジニア組織の貢献になるんだなと。この期待に応えられたときの世界が楽しみです。

📣 協力してくれませんか?

最後に協力をお願いしたいです。

エンジニア採用や組織のお困りごとや改善の事例などぜひ聞かせてほしいです。生の声はプロダクトに血が通わせるのに欠かせないと思うため、ぜひお願いします!

そして絶賛採用中です!エンジニアだけでなく、プロダクトマネージャー、デザイナー、カスタマーサポート、セールスと全体的に募集してます。求人が出てないポジションでもポジション考えます。まずはカジュアル面談で、Findyのことをお話しできたら嬉しいです。今回、触れられなかったですが、エンジニア採用のサービスを国内にもグローバルにも提供して、技術に関わる人や組織を包括的に応援する会社です。まだまだ触れられてない面白いところあるかなと。

もちろん、今すぐ応募も歓迎です👍

カジュアル面談の窓口はこちら meety.net

採用ページはこちら careers.findy.co.jp

求人一覧はこちら herp.careers

ではでは。

*1:まだ書き足りないです。

*2:システムのメトリクスが取りやすいのは、それに取り組んできた|いる人たちのおかげです。感謝!

エンジニアのためのお祭りの技術カンファレンスをオンラインで実施する|YAPC::Japan::Onlineの場合

もう1ヶ月5ヶ月経ってしまいましたが、YAPC::Japan::Online 2022にご参加いただいた皆さま、関わっていただいた皆さま、ありがとうございました。「楽しかった!」「YAPCだった」といった感想を聞けて、ホッとしました。5ヶ月前に

最高のスタッフの打ち上げの様子

今回、初めてオンラインで大規模にYAPCを運営したのですが、けっこう試行錯誤だったので、その辺をお伝えできればと。

YAPCとは

"YAPCは、Yet Another Perl Conferenceの略で、Perlを軸としたITに関わる全ての人のためのカンファレンスです。 Perlだけにとどまらない技術者たちが、好きな技術の話をし交流するカンファレンスで、技術者であれば誰でも楽しめるお祭りです。"と公式サイトでうたってます。要はエンジニアのためのお祭りです!

YAPC::Tokyo 2019のKeynoteの様子 / ©JPA

しかし、この「お祭り」という言葉。オンラインでどうするよ?無理じゃないか?お祭りにならないならYAPCと名乗れない・・と運営を大いに悩ませました。

自己紹介

申し遅れたのですが、id:kfly8です。こばけんと呼んでもらえると嬉しいです。 普段はエンジニアの組織開発、人材開発、技術広報、採用、事業支援など組織のパフォーマンスをいい感じにしてくためにガンバッテマス。

私とYAPCとの関わりは、裏方での関わりが長く、2013年からスタッフを始めて、2019年度からJapan Perl Association の理事をやらせていただき、前回のYAPC::Tokyo 2019 ではリーダーをしていました。今回のYAPCでは、サブリーダーとして、企画、進行管理、予算管理、スポンサー対応、スピーカー対応、ノベルティ発注、学生交流企画、サイト更新などしていました。まとめると、Perlコミュニティが好きです。

下準備

技術トークをただオンライン配信して、オフラインのコンテンツを踏襲しても、「YAPC」になるとは思えず、スタッフの話し合いは根本的なことから話すことが多かったです。いくつかトピックを紹介していきます。

下準備①:技術カンファレンスへの期待、課題のヒアリング

技術カンファレンスを取り巻く状況が変わっていると感じて、過去YAPC::Japanを主催した人と話す場を2021年4月に設けました。

話し合いのアジェンダ。当時はミニカンファレンスのJapan.pmの建て付けだった。

思い返すと、このヒアリングは成功の鍵だったなと。技術カンファレンスあるいはYAPCに対して、どんなことを期待や課題が見えた場でした。例えばこんな感じ。

  • 機能的な価値
    • 「実践的で具体的な手段が知りたい。例えば、AWSを勉強したければAWSの公式ドキュメントをまず読むのが正解だと思う。登壇資料、ブログがあればインプットはいくらかできる。情報に溢れた今、わざわざ技術カンファレンスに参加する意味は何か?」
    • YAPC::Japanでは、Perlに長く関わる人の先鋭的な話が聞けるが、初学者の目線で見れば難しい。最近はどう書くのが"フツウ"なのかなど、初学者でもわかりやすい、学びやすい話もほしい。」
  • 情緒的な価値
    • 「お祭り感。例えば、Danさんが酔っ払っているのを見ると楽しく感じる。」
    • 「ライブ感。例えば、ライブコーディングが『スラスラできました!』じゃなく、参加している人みんなで『あーだこーだ』と言い合いながら、やっとできるのも楽しい。」
    • YAPCに参加すると人と繋がる。ネット上だけの存在に会える。刺激になる。」

id:onagatani id:shinotra id:anatofuz id:codehex id:itoken417 kawakami 協力ありがとうございました!

下準備②:仲間を集める

YAPCへの期待、課題をヒアリングできたものの、正直、最初の壁はリソース不足でした。 2021年度からJapan Perl Associationの理事体制は代わり、理事はid:karupanerura id:xtetsuji id:papix と私の4人だけとなり、本業も多忙な4人で技術カンファレンスを大規模に開催することは難しい状況でした。そこで、コアスタッフ募集をしました。

blog.yapcjapan.org

結果、最高のスタッフに恵まれました。 id:Pasta-K, id:nyanco15, ariaki, godan09, id:tomcha0079 osima id:nagayama がいなければ、間違いなくYAPCは開催できなかったです。一緒にスタッフができて心から良かったです。感謝。

下準備③:お祭り感を演出する三つの企画

スタッフで話し合いを重ね、お祭り感、参加した感、ざわざわ感を得るための具体的な企画が3つ見えてきました。

1. オンラインの懇親会では参加型コンテンツがあった方が良い

YAPC::Tokyo 2019の懇親会。新型コロナが落ち着いたらやるぞ!/ ©JPA

オフラインの時の懇親会はこの写真のように大勢の人でわーーー!と話します。この一堂に会する懇親会は、オンラインでは同様にできないと早々に諦め、1から企画を考えていきました。

懇親会はYAPCの本編とばかりに、スタッフの懇親会にかける熱量は並々ならぬものがありました。「いいイベントには美味しいご飯」「面白い話、知らなかった話、刺激になる話が聞きたい、話したい」「『憧れのあの人』と話せたら嬉しいね」とこれを実現するアイデアを出していきました。

オンラインだと場の空気感を感じにくく、加え一斉に話されても聞き取れないので、何かしら参加できそうなコンテンツを用意する方向にまとまっていきました。

例えば、こんなアイデアが。

  • クイズ、ビンゴなど参加の敷居を下げられるかも?
  • 登壇者とかの話を聞くラジオがあっても良いかも?
  • 会話に入り込むのは難しいから、何か話すキッカケがほしいかも。順があらかじめ決まると良いかも?

結果、「誰でも好きな話をして良いアンカンファレンス」「初学者でも楽しめる、やんにゃ掛け合いのあるライブコーディング」に繋がりました。

自画自賛ですが、この2つのコンテンツはオンラインと相性が良いなと。オンラインの長所は、画面が見やすく声も通りやすいのでトークの視聴環境として良く、また、今までなら、すでに出来上がってる輪に入るのはむずしかったと思うのですが、オンラインだと輪の人数に制限がないので混ざりやすくなったなと。

オフラインの懇親会で輪に入る難度は高いの図

自分だけのイメージかもしれないですが、オンラインの懇親会は茶の間のテレビなんですよね。テレビでなくYouTubeとかでもいいんですが、一緒にコンテンツを見ながらリアタイでTwitterで感想をつぶやく体験、一斉に「バルス!」と言うちょっとした一体感、インタラクティブ感はオフラインの飲み会のそれとは違うんですが、そんな体験をイメージしてました。(伝わる?)

2. ノベルティ・飲食で、お祭り感がでる

オンラインの勉強会は、勉強会の会場まで移動することなく、ポチッとZoomのURLをクリックすればぬるっと参加することができるのは良いですよね。時短。

ただ、そこがデメリットでもあるなーと。

YAPCは非日常なので、ぬるっと始まるのは違うと。Twitterのタイムラインを見て「そういえば今日勉強会だった!」と急に気づくのも違う。甚平、浴衣を着て、出店で焼きそばを買う非日常が大切で、ノベルティが送られ、チキンが送られ、ビールが送られ、なんかTwitterのタイムラインがざわつき始めて、期待が高まって始まるのがお祭りだなと。

そんなわけで、ノベルティ、飲食を送ることは、すぐに決まりました。「YAPCチキン」「ピザハットさんのピザ」「ノベルティ送付」につながりました。実現できるかどうかが課題でした。

お祭りにはノベルティ

お祭りにはビール

3. ざわざわ感のあるサブコンテンツ

The Perl Conferenceに参加した時の写真。「廊下」にたくさん人がいた。

技術カンファレンスなので、技術トークは軸です。それは間違いないです。ですが、スライドは公開され、ブログも書かれ、YouTubeで動画も公開される時代。ので、今、その場だから感じられる雰囲気、ライブ感が大切だなと。せっかく2年ぶりに開催されたYAPCだから、オンラインであっても、周りの反応、笑い声、ツッコミがあるザワザワ感がほしいとスタッフで話しました。

ちょっと余談ですが、今回、ゲスト対談はゆーすけべーさんとうずらさんにお願いしました。この2人に関する個人的な思い出を少し。YAPC::Asia 2014で、ゆーすけべーさんはリーダーでうずらさんと私はコアスタッフでした。で、この時のベストトークうずらさんがとったんですよね。PHPの話で。

その舞台裏。ベストトークうずらさんだと発表されると、うずらさんは「おれ!スタッフだから!!違うでしょ!!ダメでしょ!!!」と必死に抵抗するんですよね。結構暴れてた。それをスタッフでステージに押し上げ、ニヤニヤと舞台で迎えるゆーすけべーさん。っというのが私にとってYAPC::Asia 2014の一番鮮明な思い出です。

何が言いたいかというと、これはただトークを聞くだけだと体験できないその場にいた人たちの物語だったなと。こんなざわざわがYAPCなのかなと。

結果として、あのゲスト対談、Japan.pmで好評だった大井町.pmによる裏トーク、ネコトーストラボさんのオリジナル絵文字企画、YAPC川柳、YAPCチキンなどに繋がりました。

YAPC::Asia 2014のベストトーク表彰の一幕 ©JPA

下準備④:お金のこと

今回のYAPCは結果数百万円の支出がありました。オフラインで開催していた時より支出は減りました*1が、チケット代だけで賄うなら1枚10,000円は優に超えます。お金の出入りを考えると開催前にお金は必要だったりとお金はカンファレンスの心配事のひとつで、収支が真っ赤っかならYAPCは失敗だと思ってます*2。たとえ、参加者の体験がよかったとしてもです。

この問題は、スポンサー企業のおかげで解決しています。ありがとうございます。スポンサー企業の一覧はこちらです。もし、ぜひご覧いただければと!

yapcjapan.org

今回、オンラインになったことで、これまでオフラインの時に提供できていたバックパネルや立て看板などがスポンサーメニューとして提供できなくなったんですよね。新たにスポンサーメニューを企画、作成しました。

オンライン版のメニューに変更をしました


ノベルティの発送のしくじり。俺みたいになるな!!

ここまで企画準備の話を書いたのですが、1つだけ毛色の違うノベルティ発送のオペレーションについて書きます。カンファレンス運営に関わる人向けの話ですね。

この話を書きたいのも、ノベルティ発送はオンライン開催になって初めて発生したまぁまぁ複雑なオペレーションで、あまりノウハウがこなれてないと思うから。

今回、ノベルティ発送にはオープンロジを利用しました。オープンロジは、普通、ECサイトの商品在庫を管理するようなサービスですが、これをノベルティの在庫管理に使いました。固定費が無料で従量課金だけで済んだので試しやすかったです。

ノベルティまとめはオフライン時代のYAPCスタッフ風物詩だった

※ここでのオペレーションは、2022年1月のオープンロジの仕様を前提にしています。実際にオペレーションを組み立てる時は、現在の仕様をご確認ください。

オープンロジを使うと、倉庫へのノベルティの入庫、出庫をWebのコンパネからポチポチで配送できます。腰に優しくないノベルティ梱包作業が一切なくなり、感動しました。

具体的には、次の4ステップで、ノベルティを配送しました。

  1. カンファレンス運営は、倉庫にいれる商品(ノベルティ)を登録する
  2. カンファレンス運営は、入庫用の商品IDのわかる入庫明細書をスポンサー企業に送る
  3. スポンサー企業には、その入庫明細書の情報をダンボールに貼り付けてもらう
  4. カンファレンス運営は、すべてのノベルティの入庫が確認できたら、イベント参加者に一括送付する手続きをする

ただ、このオペだと、後述の「紙製品の別オペ」のしくじりが発生するので、次のようなオペの方がいいはず。たぶん。

  1. カンファレンス運営は、スポンサー企業にノベルティの予定をヒアリング(紙製品か否か確認)【←追加】
  2. カンファレンス運営は、倉庫にいれる商品(ノベルティ)を登録する
  3. カンファレンス運営は、入庫用の商品IDのわかる入庫明細書をスポンサー企業に送る
  4. スポンサー企業には、その入庫明細書の情報をダンボールに貼り付けてもらう
  5. カンファレンス運営は、すべてのノベルティの入庫が確認できたら、イベント参加者に一括送付する手続きをする

企業向けには次のようなノベルティ送付マニュアルお送りしたけれど、これを読むともう少し具体的なイメージが湧くかなーと思います。

スポンサー企業向けのノベルティ送付方法のご案内

実際にオペレーションして、大きく3つしくじりがありました。

1. 紙製品で別オペ発生のしくじり

まず、シール、チラシ、冊子などの紙製品のノベルティの場合、オペレーションが複雑になりました。

オープンロジでは、商品管理ためにノベルティ一つ一つに原則、管理用ラベルが貼られるんですが、この管理用ラベルを紙製品に貼るとノベルティ破損の恐れがあるので、紙の場合は別扱いにするオペレーションが発生するんですよね。

オープンロジの方に相談して案内されたのが、紙製品を「同梱物」として扱う形。ECサイトなどで商品購入をすると商品購入のお礼のお手紙がついていることがあると思うのですが、そういった「同梱物」として扱えれば管理用ラベルの貼り付けを避けられると。

この「同梱物」に該当するかどうかは、結局オープンロジの人に聞かないとわからず、シール、チラシ、冊子で確認しました。シール、チラシは同梱物として扱ってもらいました。「同梱物」として扱えなかった冊子は、オープンロジに別料金で透明袋に入れてもらってから、管理用ラベルを貼ってもらいました。

カンファレンスを終えて、関わった人みんなこのやりとりが面倒だだったろうなと思いました。そもそも、シールだろうがチラシだろうが透明袋にいれてもらい「同梱物」として扱わない方が、オペはシンプルだったかもしれないなぁと反省。あるいは、スポンサー企業にどんなノベルティを送付予定か事前に聞くなども手だったかなと。ただこれはこれでスポンサー企業がノベルティを練る時間を削る問題もあって難しい。 何にしろ、紙製品でのオペは再考の余地あり。

2. 郵便番号、住所の間違いのしくじり

PassMarketでチケット購入時に入力いただいた郵便番号、住所が市町村の合併前の旧住所の利用していたり、シンプルに誤入力になっているケースがあり、こんなケースだとオープンロジの方のコンパネが住所間違ってるよーとバリデーションしました。

購入された方へのメール確認やわかる範囲での微調整をして難を逃れたのですが、配送先の登録の時期は期限ぎりぎりで調整していたのでとても焦った。ちなむと出庫日は、開催前に必ず届くように余裕を持って10日前だったんですが、離島の船便のケースや吹雪などの悪天候ケースも加味すると丁度良かったと思ってます。

次回は、チケット購入時に住所入力間違いを気を付けるように案内したり、出庫日より前もって配送先の登録ができればと。

3. 「小林謙太」から送付のしくじり

YAPC開催直前、参加者のみなさんにこんな通知があったかなと。

「小林謙太」は私の本名なのですが、「小林謙太」からでなく「YAPC::Japan::Online 2022」から送る形にしたかったんです!不審なお知らせで、すいませんでした!

カンファレンスに関わる皆さんが私みたいにならないで済むように知見?共有をすると、誰から送るかは「依頼主」が利用されるようで「依頼主会社名」や「贈り主」は使われないので要注意を。

「一歩踏み込む」

オープニングトークで伝えた言葉について、少し補足を。

YAPCには、トークを聴いたり話したり、普段会えない人と話したり、場の空気を感じたり、いろんな楽しみ方があって、消化しきれない大量のコンテンツがあります。そんな中、YAPCを楽しむコツは「一歩踏み込む」こと、と話しました。

YAPCを楽しむコツ。オンラインだと余計にかなと。

この言葉をふり返ってみると、当日、不安をふり払いたい気持ちが出てて、言葉もストレート過ぎて、少し恥ずかしいんですが、言ってよかったかなぁと思ってます。感想ブログでこの言葉を言及してくれた人もいて嬉しかったです。

偉そうに言った手前、自分自身がどんな一歩を踏み込んだのか考えたのですが、YAPC当日は、運営のあらゆる準備をしていたので、それが自分の一歩だと思ってました。それはそれで良い経験になったので良いんですが、今考えると、一番背伸びしたことは「一歩踏み込む」という言葉を伝えたことだと思ってます。

というのも、Perlコミュニティには偉大な先輩がたくさんいて、育ててもらった恩があり、自分が伝えていいのか、自分の言葉に嘘がないと思えるか、悩むからです。もっと気軽に考えていいとも思うんだけど、超悩む!

それでも一歩踏み込んで伝えてみようと思った理由は2つ。

1つ目は、YAPC::Tokyo 2019のmagnoliaさんのオープニングトークYAPC::Tokyo 2019では「報恩謝徳」(恩に感謝し報いる)をテーマにしていたんですが、「報恩謝徳」というと少し馴染みない言葉を、magnoliaさんはわかりやすく「恩送り」と翻訳して、オープニングトークで伝えていたんですよね。運営のリーダーだったので「報恩謝徳」はいい言葉だなーと思いながら決めたのですが、magnoliaさんのオープニングトークを聞いて、その時初めて言葉に血が通った感覚があり、刺さった。その体験が1個目。

2つ目は、今回のゲスト対談準備で、うずらさんが「最近、若者に遠慮されるようになってきてる笑」と軽く漏らしてたこと。この話のせい(?)で、うずらさんは当日、「本当は今も若いと思っている」背景だった。うずらさんの悩み(?)を聞いて、技術コミュニティの先輩でもこんな悩みを持つならと勇気づけられた。これが言葉を伝えてみようと思った2つ目の理由。

本当は今も若いと思っているうずらさん

だらだらと書いてしまったせいで、一人勝手に感傷的な気分だけど、YAPC::Japan::Online 2022が、人それぞれ自分なりの一歩を踏み込むキッカケになり、思ってもなかったことの発見、新しい出会いや笑いが生まれてたら、嬉しいです。

さいごに

荒削りな部分もあったと思うのですが、また次回のYAPCをお楽しみにいただければと嬉しいです。次回のYAPCは、京都でオンラインとオフラインのハイブリッド開催を予定しています!

謝辞

モバイルファクトリーには、YAPCの準備のために多くの業務時間を使わせてもらいました。また、家族にも協力してもらって運営ができました。この場を借りて、感謝を伝えたいです。

*1:会場費がだいぶ減りました

*2:これまでのご支援のおかげで、多少、赤でも大丈夫です

gihyo.jpで「ISUCONの実装から最近のPerlを学ぶ」という記事が公開されました

先日、WEB+DB Press Vol.126に記事を寄稿したことを書きました。

kfly8.hatenablog.com

こちらの記事がgihyo.jpで公開されました。誰でも見れるので、お気軽に読んでいただければ嬉しいです!

gihyo.jp

そういえば、YAPCがもう少しで開催ですね! 運営として色々仕込みをしているのですが、めちゃくちゃ楽しみです! ワイワイしていきたいですね!

yapcjapan.org

WEB+DB Press Vol.126で「ISUCONの実装から最近のPerlを学ぶ」という題の記事を寄稿しました

f:id:kfly8:20211214082705p:plain

12月24日に販売されるWEB+DB Pressに寄稿しました。21周年おめでとうございます! タイトルが「ISUCONの実装から最近のPerlを学ぶ」で、副題は「わかりやすく変更しやすいコードを実現する考え方と方法」です。 直球ですが、読んだ感想をもらえると嬉しいです!買ってください!!!

ここでは、記事の紹介や紙面都合入りきらなかったことなど補足を書きます。

記事の紹介

記事の内容は、ISUCON11のPerl実装を題材に「今までのPerlはこう書くけど、最近のPerlならこう書ける!」とやや挑戦的?な内容になっていて、正直反応が怖い内容です。

きっかけは、タイトル通り、ISUCON11のPerl実装です。id:papix から声をかけられたとき、仕事もプライベートもかなり忙しい時期で、数年前の初執筆が難産だったこともあり、きちんと書けるか不安でした。ですが、ISUCONの予選が終わった後、ポジティブな感想をもらえたことが素直に嬉しく、せっかく頂いた機会なので書いてみました。つまり、調子に乗って書いた形です。やっててよかったTwitter。予想通り、予想以上の苦労をして、形にすることができました。

具体的な内容ですが、組み込みのtry/catchやisa構文などのPerlの新機能の話を書いています。単に機能紹介だけではなく、わざわざPerlに新しい機能を入れるのは、なぜなのか、何を解決したいのか、問題が明確になるように書きました。

そもそも、これらのPerlの進化はわかりやすさの観点で良いと思っていますが、「わかりやすさとは何か」「わかりやすさをどう実現するか」といった話を、Perlとは一切関係なく、認知心理学の言葉を使って、2ページほどで整理しました。例えば、人間の視覚は、見ているようで見ていない特性があるので、そんな状況で注目を集めるためにゲシュタルトの法則を紹介したりしました。

蛇足ですが「人間の視覚は、見ているようで見ていない」という話を次の動画2分弱で実感できます。お時間が許せば、よかったら見てみてください。知らない人は面白い体験ができます!

www.youtube.com

認知心理学なぞ持ち出した背景を最後に補足すると、普段、私が人材開発・組織開発の仕事をして、プロダクトマネジメントをしていたことが影響しています。どちらも人に関わる仕事で、人を理解することが、仕事の良し悪しに関わると思っています。偉そうに書いていますが、人に関わることで失敗はたくさんしていますし、謝る機会があれば謝って歩き回りたいし、ミーティング、面談、面接が続けば、家でプログラミングして心を整えたいわって人間です。ただ、一方、人や組織の個性がシステム、コードの個性となって現れるのもよくある事実で、人や組織の特性について学んで損はないかなと思っています。

上で紹介した視覚特性の話は、私の場合、データ視覚化と繋げて考える場面が多いです。データ視覚化で基本的な問いのひとつは「引き出したい行動を引き出す視覚化は何か?」で、例えば、信号機、スマホの電池残量などシンプルなデザインでいて、止まれ、充電などの行動引き出し、見本の視覚化だと感じます。

プログラミングの文脈に引き寄せれば「引き出したい行動を引き出すコードは何か?」という問いが、今回寄稿した記事の根っこにあります。

紙面都合、入れられなかった内容

記事タイトルを読んで、ISUCONのPerlに関する話を期待して読んでくださる方には申し訳無いですが、「わかりやすさとは何か」うんぬんかんぬんの説明に前置き2ページも使ってしまったので、Perlに関する話は4ページほどになってしまいました。ここでは、もう少し書いてみたかったPerlの話を簡単に書きます。

perl5.34より先の話

この記事が、いい感じにまとまっています。早速丸投げですいません。レビューで関わったのですが、id:mp0liiu は流石だなーと見ていました。

個人的に推したいのは、Corinna/Object::Padです。Perlには多様なクラスビルダーがありますが、現代的なOOPをコアにビルトインする野心的なプロジェクトです。保守性を上げ、学びの閾値を下げる解決策として良いと思うと同時に、野心的なプロジェクトは、衝突がおきがちなので、少しでも応援したいという感情的な理由です。

JSON文字列の検証

別の記事書きました。

kfly8.hatenablog.com

最後に

今回の記事が、わかりやすさ、変更のしやすさを、ちょっといつもと違った視点で考えるきっかけになれば嬉しいです! 繰り返しの宣伝で、贅沢を言うようですが、内容的に反応も怖い記事ではありますが、そういった反応含めて感想いただけたら幸いです。 よろしくお願いします!!

JSONが正しくエンコードされているか、Cpanel::JSON::XSで手軽に確認する

Perl Advent Calendar 2021 でお送りします。


Perlで、JSON文字列が期待通りか、Cpanel::JSON::XSでテストできます。具体的には、$json->decode($json_string, $json_type)の第二引数にスカラ変数 $json_typeを渡すと、どうdecodeしたか型情報が入ります。以下のコードだと、[false]をdecodeをして、booleanとしてデコードしたことがわかります。

use Test2::V0;

use Cpanel::JSON::XS;
use Cpanel::JSON::XS::Type;

my $json = Cpanel::JSON::XS->new;

$json->decode('[false]', my $json_type);
is   $json_type, [JSON_TYPE_BOOL];
isnt $json_type, [JSON_TYPE_STRING];
isnt $json_type, [JSON_TYPE_INT];

done_testing;

Test2::Tools::JSON の方がテスト用途に最適化されて、使いやすい気がしないでもないですが、Cpanel::JSON::XS::Typeで定義した構造がすでに存在するならば、DRYで良さそう。*1

*1:書いていて思ったけど、Cpanel::JSON::XS::Typeで作った構造から、テスト用のmatcherを生成する方がテストでは便利かもしれない。

Perlのコンテキストと仲良くなるツールを書いた

f:id:kfly8:20210508142036p:plain:w600

metacpan.org


Perlのコンテキストに戸惑う場面はあると思います。

my @a = ("apple","banana");
if (@a) { ... }

「あれ?なんでifに配列を入れて動く?」  
「それは、真偽値コンテキストで解釈されるね」  
「なるほど?」 

こういった戸惑いが少しでも減ることを目的に、Contextual::Diagというモジュールを書きました*1

Perlのコンテキストについて

Perlのコンテキストは「内部的に多態なデータを、評価次第で、型解釈を決める」概念で、
例えば、$str eq "hello"は文字列が一致するか比較している場面なので、Perl$strを文字列として評価しようとします。
プログラミング言語によっては、toString, to_sといったメソッドを明示的に呼び出しますが、Perlの場合は、eq に反応しています。

詳しいことは、perldataを読むか、この辺りのスライドがおすすめです。

使い方

コンテキストを知りたい箇所に、contextual_diag 関数を差し込めば、コンテキストを警告してくれます。 次のコードは、ハッシュリファレンスの値を評価する時のコンテキストを探っています。

use Contextual::Diag;

# これを・・・
my $a = { key => 'value' };

# こうする
my $a = { key => contextual_diag 'value' };

# すると、リストコンテキストだと教えてくれる
# => wanted LIST context

もう少し詳しい話

Contextual::Diagでは、まず、wantarrayでSCALAR,LIST,VOIDの大区分のコンテキストを切り分けます。次に、SCALARコンテキストの場合は、返り値をオブジェクトでくるんでやり、overloadを使って、文字列評価、数値評価、配列デリファレンスされた場合の処理をフックしています。オブジェクトリファレンスのケースだけは、overloadでフックしきれないので、AUTOLOADを利用しています。

まとめると、次のコンテキストの診断をします。

診断の一覧

1. SCALAR コンテキスト
   - CASE: Scalar value
      - BOOL評価 e.g. `if ($value)`
      - NUM評価 e.g. `$value + 1`
      - STR評価 e.g. `$value . "hello"`
   - CASE:  Scalar reference
      - SCALARREF e.g. `$$value`
      - ARRAYREF e.g. `$value->[0]`
      - HASHREF e.g. `$value->{key}`
      - CODEREF e.g. `$value->()`
      - GLOBREF e.g. `*{$value}->{CODE}`
      - OBJREF e.g. `$value->hello()`
2. LIST コンテキスト
3. VOID コンテキスト

さいごに

このツールで、Perlのコンテキストと仲良くなる手助けができたら、嬉しいです。 良かったら使ってみてください。

*1:書いたのは1年半前だったけれど、CPANに週末あげた

attributeを用いて、コンパイル時までにチェックに必要な情報を整える

#japanpmのおかげで、最近コードを書けてる。その中で、attribute周りをいじっていたので、そのことについて書きたいと思う。


Perlでattributeを使う時、普通、Attribute::Handlersを使って書くと思う。

package AttrSample;
use Attribute::Handlers;
our @INFO;
sub Stock :ATTR(CODE) {
    my ($package, $symbol, $referent, $attr, $data) = @_;
    push @INFO => { code => $referent, some => 'info' };
}
1;

package AttrUser;
use parent qw(AttrSample);
sub hello :Stock() { }
# => stocked `hello` at INFO

しかし、自分の場合、次の問題点を感じた。

  1. AttrSampleを動的読み込みした場合、Stock attributeは呼び出されない
    • Attribute::Handlersのデフォルトだと、attributeはCHECKブロックで呼び出されるが、CHECKブロックは遅延読み込みした際、発火されなかったりする
  2. Stock attribute実行以前だと、INFOに情報がストックされない
    • attributeを指定していたら、全てストックされている方が良い
  3. 継承が汚れる
    • Attributeを利用できるようにしたいだけであって、継承したいわけじゃない
    • Exporterとの食い合わせで、意図せぬ結果になってハマる(ハマった)

やりたいことを補足すると、自分の場合、詰まるところ、perl -wcによるチェックで問題に気づきやすくしたい。Perlの動的な性質上、静的解析でできることには限度があると思う。かといって、全てのコードを実行してからでは問題に気づくのは遅い。だから、コンパイル時までのチェックを強化すると良いと思ってる。この実現のためには、チェックに必要な情報をコンパイル時までに揃える必要があるが、attributeやkeyword pluginあたりのギミックが都合良い。この記事では、attributeで、コンパイル時までにチェックに必要な情報を揃えるためにどうすると良さそうか書く。

結果としては、次の形に落ち着いた。気になる点があれば、コメントをもらえると嬉しい。より具体的なコードはFunction::Returnにある。

use Attribute::Handlers;
use B::Hooks::EndOfScope;

my @ARGS;

sub import {
    my $class = shift;
    my $target = caller;

    {
        # ③ attributeを、$targetで利用できるように、
        # MODIFY_CODE_ATTRIBUTES, _ATTR_CODE_Stockを差し込む
        no strict qw(refs);
        my $MODIFY_CODE_ATTRIBUTES = \&Attribute::Handlers::UNIVERSAL::MODIFY_CODE_ATTRIBUTES;
        *{"${target}::MODIFY_CODE_ATTRIBUTES"} = $MODIFY_CODE_ATTRIBUTES;
        *{"${target}::_ATTR_CODE_Stock"} = $class->can('Stock');
    }

    # ② 詰め込んだattributeの引数を、コンパイル時に一気に処理
    on_scope_end {
        while (my $args = shift @ARGS) {
            # ... do something by $args
        }
    };
}

# ① BEGINフェーズで、@ARGSにattributeの引数を詰め込んでおく
sub Stock :ATTR(CODE,BEGIN) {
    push @ARGS => \@_;
    return;
}

やっていることは、大きく3つ。①②によって、CHECKブロックでなく、importのコンパイル時に一気に処理。③によって、継承を汚すことなく、指定のパッケージでattributeを利用できるようになる。

詳解

importのコンパイル時に一気に処理

改めて、処理の流れは次の通りで、ここではこの処理の背景を書く。

  • ① BEGINフェーズで、@ARGSにattributeの引数を詰め込んでおく
  • コンパイルフェーズに、詰め込んだattributeの引数を元に一気に処理

まず、CHECKブロックは、requireやevalなどで遅延読み込みされた際、呼び出されないことがある。*1その回避のために、ATTR(CODE,BEGIN)でBEGINフェーズでの実行と、B::Hooks::EndOfScope#on_scope_endを組み合わせる。on_scope_endは、スコープのコンパイルが終わった時にコード実行してくれる君。この2つの組み合わせを素直に実装すると次のようになると思う。

sub Stock :ATTR(CODE,BEGIN) {
     on_scope_end {
            push @INFO => { code => $referent, some => 'info' };
     }
}

しかし、このコードには問題がある。 具体的には、INFOを取り出す処理より後にINFOをStockすると、INFOが期待通り取り出せない。 目的はチェックに必要な情報を全て整えることなので、一部情報が欠けるのは問題になる。

package AttrUser;

use parent qw(AttrSample);
use B::Hooks::EndOfScope;
use DDP;

sub import {
    on_scope_end {
        p @AttrSample::INFO;
        # => EMPTY!!
        # XXX: 本当は、\&helloに関する情報が欲しいが、`sub hello :Stock`はコンパイルまだされていないので、INFOは空
    }
}

sub hello :Stock() { }

1;

結局、attributeごとにon_scope_endを実施するのはやめ、以下コードのように、importに集約して一気に処理する形にする。こうすることで、チェックに必要な情報を揃えやすくなる。もしチェックに必要な情報が取得できないとしたら、このサンプルコードでいうところのAttrSample#importのコンパイルよりも前に取り出そうとしている、と原因を一つに絞れる。

package AttrSample;

my @ARGS;

sub import {
   ...

   on_scope_end {
      while (my $args = shift @ARGS) {
          # do something by $args
      }
   }
}

sub Stock :ATTR(CODE,BEGIN) {
     push @ARGS => \@_;
}

指定のパッケージでattributeを利用

Attribute::Handlersのドキュメントをみると、指定のパッケージで独自定義したattributeを利用するには継承する例が示されている。しかし、use parent qw(AttrSample Exporter)のようなコードを書いているとAttrSampleのimportだけが呼ばれ、Exporter側のimportが呼ばれず困るなんてことがある。あった。*2 また、Attribute::Handlersは、sub UNIVERSAL::Stock :ATTR(CODE) のように、UNIVERSALに生やすパターンも想定しているが、UNIVERSALを使うのは影響範囲が大きいので使っていない。*3

結局、attributeを生やすために必要な最低限の関数を生やせれば良い。 code attributeを定義するには、初心に帰るとMODIFY_CODE_ATTRIBUTESを生やせば良いが、これを独自に書くのは面倒*4。なので、Attribute::Handlersが定義しているMODIFY_CODE_ATTRIBUTESを利用する。具体的には、_gen_handler_AH_をみると良い。中身をみると、次のことがわかる。

  • 行152-173で、ATTR attributeに関する処理をしていて、どのフェーズで実施するかを記録し、_ATTR_CODE_Stockといった関数を生やす。
  • 行175-196で、_ATTR_CODE_Stockといった関数が見つかれば、その実処理をする。

つまり、MODIFY_CODE_ATTRIBUTESと_ATTR_CODE_Stockを指定パッケージに生やせば、Stock attributeは利用できる。 内部実装に依存した書き方になっていることは心配だけれど、Attribute::Handlersなら、そんなに壊れることはないだろうと賭けている。

まとめ

  • attributeを用いて、コンパイル時までにチェックに必要な情報を揃えるためには、まずBEGINフェーズで適当な変数にattributeの引数情報を詰め込み、次にimportのon_scope_endで、詰め込んだ引数を元に一気に処理する
  • attributeを指定のパッケージで利用できるようにするには、継承ではなく、Attribute::HandlersのMODIFY_CODE_ATTRIBUTESと、Attribute::Handlers用の命名で関数を指定のパッケージに生やす

フィードフォワードをしてから、フィードバックをする

 HHKB HYBRID発売されましたね。欲しいですね。私は間が悪くHHKB追加購入したばかりなので泣いています。id:kfly8です。

 この記事はEngineering Manager vol.2 Advent Calendar 201920日目の記事です。

 伝えたいことはタイトル通り「フィードフォワードをしてから、フィードバックする」ことが良いという話です。場面はピープルマネジメントを想定していますが、コードレビューがフィードバックの1つなのでエンジニアにとって身近な内容だとも思います。

 ここでのフィードバックは、目標達成のための軌道修正のことです。 ロケットの軌道修正から来た言葉だそうです。ロケット機内にいることを想像してみると、何の手助けなしに自分がどこを飛んでいるのか把握するのは難しいと思います。プログラミングであれば、自分が書いたコードを客観的に見るのは難しいと感じませんか?少なくとも自分には難しいです。なので、コードを一晩寝かせたり、あるいは人に見てもらうことで気づきを得ようとします。自分がロケット機内にいたら問題に気づけないので、ロケットの外に出たり、周りに教えてもらって、ようやくどこにいてどこに向かっているか認識できます。自己認識を調整するために、フィードバックは必要だと思います。

 ただ対人間のフィードバックでおこりがちな失敗はあると思います。正直自分は失敗だらけです。例えば、フィードバックされる側からしたら「なんでそんなこと言われなきゃいけないんだ」と抵抗を感じることはあるあるです。原因は、フィードバックを求めていない場面だったり、そもそも目標の認識が合わせられてないってことがあると思います。フィードバックが、自分では変えようがない過去の出来事に対しての指摘になるので、批判的なニュアンスがどうしても伴います。

 フィードバックは、自己認識の調整のために必要だと思いますが、どうしても抵抗感を作りやすい側面があります。

f:id:kfly8:20191220074008j:plain:w600

 フィードバックの抵抗感を緩和する手段はないか?

 今回の自分の提案は「フィードフォワードしてから、フィードバックする」ことです。フィードフォワードとは、これから起こる出来事を予測し、予告することです。例えば、開発をする際、性能か、堅さか、はたまたさくっと書くことか、期待を話し合っておけば、あとでコードレビューする際、どんな観点でのレビューになるのか予想がついていてある程度開発をする人は準備をしているので、抵抗感は減ると思います。準備の期待の合わせだけなく、仕事を進めている最中も「次はこんなこと起きそうだねー。あそこが落とし穴になりそう。」といった話が、「あそこどうだった?」とフィードバック時の伏線に繋がります。

 まとめると、フィードフォワードとフィードバックの関係を業務に当てはめると次のようなイメージです*1。フィードバックだけの片割れ運用をしていたら、フィードフォワードを意識して取り入れてみると良さそうです。

f:id:kfly8:20191221010311p:plain
フィードフォワードとフィードバックの関係

参考

部下の強みを引き出す 経験学習リーダーシップ

部下の強みを引き出す 経験学習リーダーシップ

*1:詳しくは松尾睦先生の本参照

Perl5で関数のメタ情報を扱うSub::Metaをリリースしました

Sub::Meta - handle subroutine meta information - metacpan.org

Sub::Metaは、関数の名前、関数の入出力といったメタ情報を扱うためのモジュールです。 Sub::Identify,Sub::Util,Sub::Infoといった類似モジュールはありますが、関数の入出力のメタ情報を扱えないためSub::Metaを書きました。

基本的な使い方は次の通りです。

コードリファレンスを元にメタ情報を抽出できます。

use Sub::Meta;

sub hello($) :mehtod { }
my $meta = Sub::Meta->new(sub => \&hello);
$meta->sub;         # \&hello
$meta->subname;     # hello
$meta->fullname;    # main::hello
$meta->stashname;   # main
$meta->file;        # path/to/file.pl
$meta->line;        # 5
$meta->is_constant; # !!0
$meta->prototype;   # $
$meta->attribute;   # ['method']

コードリファレンス渡すことなく、メタ情報を扱うこともできます。

my $meta = Sub::Meta->new(subname => 'foo');
$meta->subname; # foo

関数の入出力のメタ情報を付加できます。

# 入力
my $parameters = Sub::Meta::Parameters->new(
  args => [
    { name => '$a', type => 'Int' },
    { name => '$b', type => 'Int' },
  ],
  nshift => 1,
);

$meta->set_parameters($paramters);
$meta->parameters->args;
# [
# Sub::Meta::Param->new({ name => '$a', type => 'Int' }),
# Sub::Meta::Param->new({ name => '$b', type => 'Int' })
# ]

# 出力
my $returns = Sub::Meta::Returns->new(
  scalar => 'Int',
  list   => 'Int',
);
$meta->set_returns($returns);
$meta->returns->scalar; # 'Int'

関数の入出力のメタ情報を扱う動機をもう少し掘り下げて、説明します。

Perl5には、関数の入力のバリデーションをするモジュールがParams::Validate,Data::Validator, Smart::Args,Function::Parameters,Type::Paramsなど数多くあります。 モジュールごとに使い勝手が異なり、それは多少ストレスだと思いました。 また、せっかく型アノテートしているので、静的解析したいと思いました。 そのために、関数の入力のメタ情報を取り扱うオブジェクトがあれば、統一されたI/Fで取り扱いしやすくなるのではないかと考えました。

関数の出力についても同様です。

また、拙作のFunction::InterfaceといったPerl5でJavaのようなInterfaceを表現しようとした時に、関数の入出力が取り回ししやすいと便利です。現状のF::IFunction::Parameters,Function::Returnの実装にべたつき、多くの利用用途に対応できず柔軟性に欠けると考えました。

そういったわけで、関数の入出力含めたメタ情報を取り扱えるSub::Metaを作ってみました! よかったら試してもらえると嬉しいです! 以上です!

Perlのインタフェース実装をした

吉祥寺.pm #17 で話した内容です。吉祥寺.pm 楽しかったです! ちなむと吉祥寺.pmのフィードバックはこちらです。楽しかった。

本題

Perlのインタフェース実装をしました。雰囲気はJavaとかのインタフェースです。 次のキャプチャにあるように、コンパイル時に色々教えてくれて嬉しいです。

asciicast

使い方

まず、インタフェースの定義は次のようにします。 例えば、fun hello(Str $msg) :Return(Str); と書けば、引数はStr $msg、返り値の型はStrな関数helloを宣言します。

package IFoo {
    use Function::Interface;
    use Types::Standard -types;
 
    fun hello(Str $msg) :Return(Str);
 
    fun add(Int $a, Int $b) :Return(Int);
}

次に、インタフェースの実装は次のようにします。 Function::ParametersFunction::Returnを用いて関数の実装をします。

package Foo {
    use Function::Interface::Impl qw(IFoo);
    use Types::Standard -types;
 
    fun hello(Str $msg) :Return(Str) {
        return "HELLO $msg";
    }
 
    fun add(Int $a, Int $b) :Return(Int) {
        return $a + $b;
    }
}

そして、ImplOf['IFoo']のようにインタフェースの型指定します。 実装に依存しなくなって、嬉しいですね。

package FooService {
    use Function::Interface::Types qw(ImplOf);
    use Function::Parameters;
    use Function::Return;
    use Mouse;
 
    use aliased 'IFoo';
 
    fun greet(ImplOf[IFoo] $foo) :Return() {
        print $foo->hello;
        return;
    }
}
 
my $foo_service = FooService->new;
my $foo = Foo->new; # implements of IFoo
 
$foo_service->greet($foo);

奇妙ですが、面白いですね!

仕組み

簡単ですが、次のような感じです。

  • Function::Interface は、Keyword::Simple + PPR で文法を拡張しています。Deparseすると、抽象関数のメタ情報を保存していることがわかります。
  • Function::Interface::Impl は、保存したインタフェースのメタ情報と、Function::Parameters、Function::Returnを用いて取得できる実装のメタ情報を付き合わせて、エラー判定しています。
  • ImplOf は、Type::Tinyでparameterizeして型を定義しています。

Perlの自由さが感じられて楽しいですね。機会があれば、詳解したいです。

さいごに

遊んでくれる人がいると嬉しいです。以上です!

github.com

型に厳しい言語とCpanel::JSON::XS::Typeで仲良く

この記事はPerl Advent Calendar 2018の10日目の記事です。

JSONをdecodeする時、例えば、123"123"は型に厳しい言語であれば、別々に扱いたいものです。 ですが、Perlでぼやっとencodeすると、これらをいっしょくたにしてしまいます。

例えば、次のコードは123とencodeされることを期待しても、"123"とencodeします。 これは、123perl内部の値のフラグが意図せず、文字列になり*1、encodeされてしまったからです。

use JSON::XS;

my $row = { id => 123 };
# ぼやっと hash key として触る
my %fg; $fg{$row->{id}} = 1;

print encode_json($row);
# => { "id":"123" }

これでは、型に厳しい言語と仲良くできませんね..! 型指定無くencodeしできるのは手軽ではありますが、decode側の言語が期待する型通りencodeできれば、異なる言語間で仲良くできそうです *2

ここでは、PerlCpanel::JSON::XS::Type を用いて、期待する型を定義し、encodeする方法について紹介します。

Cpanel::JSON::XS::Typeで型指定をしてencodeする例

早速ですが、型指定をしてencodeする例を示します。 次のコードは、{ id => JSON_TYPE_INT }という型指定をし、値を"123"という文字列でなく123という数値にencodeしています。

use Cpanel::JSON::XS;
use Cpanel::JSON::XS::Type;

# 型指定
my $type = { id => JSON_TYPE_INT };
print encode_json({ id => "123" }, $type);
# => { "id":123 }

encode_jsonの第二引数に型を指定しなければ、従来のencodeを行うので取り入れやすいのではないかと思います。

Cpanel::JSON::XS::Typeで型を定義する

JSONで期待される型は、基本型は数値、文字列、真偽値、nullの4種で、また構造を表現するため、配列、ハッシュがあります。 基本型は、それぞれ、JSON_TYPE_INT|JSON_TYPE_FLOAT、JSON_TYPE_STRING, JSON_TYPE_BOOL、JSON_TYPE_NULLという定数で表し、もし基本型がNULLを取りうるのであれば、_OR_NULL というsuffixを付けます。 配列、ハッシュは、[],{}というリテラル表現か、json_type_arrayofjson_type_hashofといった関数を用います。 詳細は、ドキュメントを確認ください。

例えば、このようなデータをencodeする例を考えてみます。

# http://dist.schmorp.de/misc/json/short.json
my $data = {
    'id' => undef,
    'params' => ['user1','we were just talking'],
    'array' => ['1',11.01,234,-5,1e5,1e7,!!1,!!0],
    'method' => 'handleMessage'
};

この場合、型は次のように定義できます。見たままの型定義で、特に理解に支障はないと思います。

my $type = {
    id     => JSON_TYPE_INT_OR_NULL,
    params => json_type_arrayof(JSON_TYPE_STRING),
    array  => json_type_arrayof(JSON_TYPE_INT_OR_NULL),
    method => JSON_TYPE_STRING,
};

この型を指定し、encodeすると次のようになります。

print encode_json($data, $type)
# => {"array":[1,11,234,-5,100000,10000000,1,0],"id":null,"params":["user1","we were just talking"],"method":"handleMessage"}

まとめ

  • decode側が期待する型通りに、Perlがencodeできない時がある
  • そこで、Cpanel::JSON::XS::Typeを用いて期待する型を指定する方法を紹介した

宣伝

YAPC::Tokyo 2019 が1/26(土) に開催されますね! 最高に楽しくなると思う!! ので、来て欲しいです!!!!

チケット販売期限が、今週末予定なので、買い逃しなく!!!!!!

yapcjapan.org

*1:SVが、IV→PVIVに変わった

*2:実際、JSON Schema、OpenAPI、Protcol Buffers、GraphQL といった中間表現を用意し、encode/decodeするコードを利用言語ごとに自動生成するアプローチで、言語間の境界を意識する経験をした人もいるかもしれません。

WEB+DB PRESS vol.107 「Perl Hackers Hub」 第52回「Perlで堅牢な開発」に寄稿しました

本日10月24日(水) に販売されるWEB+DB PRESS vol.107 で Perl Hackers Hub 第52回「Perlで堅牢な開発」を書かせていただきました。

技術評論社の稲尾さん、監修いただいた牧さん、応援してくれたモバファクの同僚、そして家族に協力してもらい、執筆することができました。本当にありがとうございました。

大それたお題になってしまっていますが・・ Perlでの構文チェックや型制約の活用方法などをいくらかまとめることができたのかなと思っています。 少しでもお役に立てれば幸いです。

f:id:kfly8:20181010160652j:plain

WEB+DB PRESS Vol.107

WEB+DB PRESS Vol.107

  • 作者: 大竹智也,浦井誠人,平野朋也,村田紘司,上野学,末永恭正,久保田祐史,吉川竜太,上野博司,牧大輔,西郡卓矢,桑原仁雄,小林謙太,竹馬光太郎,池田拓司,はまちや2,竹原,長谷川智希,北村壮大,WEB+DB PRESS編集部
  • 出版社/メーカー: 技術評論社
  • 発売日: 2018/10/24
  • メディア: 単行本
  • この商品を含むブログ (1件) を見る

宣伝

来年の1/26(土) に YAPC::Tokyo 2019 が開催されますね! 微力ながら運営をやらせてもらっていますが、着々と準備が進んでいて、ワクワクしています!! 楽しみですね!!!

yapcjapan.org

About Dancer2 (ja)

この記事は、Perl5 Advent Calendar 2015 の20日目の記事です。

Web Application Framework の Dancer2 について紹介したいと思います。

まずは、YAPC::Asia 2014 の LT から。 こちらは、Dancer2 のリリースマネージャーの SawyerX による、Dancer2の超速紹介です。 Dancer2 の要点が凝縮されたLTです。


LT / Dancer2 / Sawyer X

目を凝らすとこんなスライドが見えます。DSLで、ルーティングを記述します。

package MyApp;
use Dancer2;

get '/' => sub {
   template index => {
      name => 'Sawyer X',
      event => 'YAPC::Asia'
   }
};

JSON を返却するようなAPIも簡単に用意できます。

package MyApp::API;
use Dancer2;

set serializer => 'JSON';

get '/' => sub {
    return {
        hello => 'world',
    }
};

Dancer2 は Plack化されているので、plackupできます。

# app.psgi
use MyApp;
my $app = MyApp->psgi_app;
plackup app.psgi

ここまでが、LTの抜粋です。

Dancer2 は、元々、Ruby のSinatra を Perl に port した Dancer の次バージョンです。 Dancer の DSL はほぼそのままに、設計面での調整が大幅に入っています*1

例えば、以下のような対応が行われています。

  • Plack 対応
  • DSL 実装を、Top 空間から、Dancer2::Core::DSL に分解
  • Moo 採用
  • デザインパターンの調整(デメテルの法則...)

この対応に関して、2011 年の Dancer Advent Calendar にて、(超訳ですが)「全部書き換えるぞい」という記事があります。

Dancer 2, or Why I Rewrote Everything | PerlDancer Advent Calendar

利用者が多数のプロダクトにおいて、こういった完全な書き換えは、個人的な興味をそそりました。 今現在も活発に開発され、Dancer という名前を冠するPerl カンファレンスも開かれています。

Dancer を作った sukria のポスト。 https://blog.sukria.net/2015/10/22/perl-dancer-2015-reportblog.sukria.net

最近の開発状況の話。


SawyerX - State of Dancer

(Dancer2::XS ってあって、すごいってなった)

もう少し、Dancer2 の特徴を触れて、記事を終わりにしたいと思います。
(と言っても、ほぼ、2014 年の dancer advent calendar *2 からの抜粋です。

fatpack、つまり一枚スクリプトにするのをサポートしています。 (なので、XSモジュールは、recommend 扱いです)

ポータビリティを考えると良い選択になるかもしれません。

コマンドラインで利用も考慮されています。

use Dancer2;

warn config->{environment}; # => development
warn dancer_version;        # => 0.165000

複数のアプリケーションの同居が考慮されています。 Dancer2 に appname を渡すことで、config やdbh などが、appname ごとに管理されます。

以下の例だと、 MyApp::Foo1, Foo2 で、一つの Dancer2::Core::App obj が作られ、 Bar にもう一つの Dancer2::Core::App obj が作られます。

package MyApp::Foo1 {
  use Dancer2 appname => 'foo';
  get '/' => sub { ... }
}

package MyApp::Foo2 {
  use Dancer2 appname => 'foo';
  get '/' => sub { ... }
}

package MyApp::Bar {
  use Dancer2;
  get '/' => sub { ... }
}

まとめ

  • なんだか Dancer2 楽しそうだぞ

明日は、akihiro_0228 さんです。

*1:http://advent.perldancer.org/2014/2

*2:SawyerX の一人アドベントカレンダー。つよい。

設定とFixtureのテスト

この記事は、モバイルファクトリー Advent Calendar 2015 2日目の記事です

昨日は、nekobato さんの superagentとaxiosの使い分け でした  

Perl Advent Calender 2014 の最高の記事の ソースコード以外もとにかくテストする。もしくはカバレッジだけではダメだという話 から、 アプリケーション設定ファイルのテストFixtureのテスト の例を書いてみたいと思います

アプリケーション設定ファイルのテスト

本番環境、開発環境、テスト環境で設定が過不足がないかチェックするため、 Test::Deep, Test::Deep::Matcher を利用しています

テストの流れとしては

  1. 本番環境の設定値を、Test::Deep::Matcher のmatcher に変換
  2. 開発環境、テスト環境の設定値が、matcher にあうか比較

です

例えば、以下のような本番設定があったとして..

# production.pl
{
  servers => [
    'XX.XX.XXX.XXX:11211',
    'XX.XX.XXX.YYY:11211',
  ],
  options => {
    utf8 => 1,
  },
}

再帰的にmatcherに変換してあげます

use Test::Deep::Matcher;
use Test::Deep qw//;

sub convert_matcher {
    my $v = shift;

    if (!ref $v) {
        return Data::Util::is_integer($v) ? is_integer
             : Data::Util::is_number($v)  ? is_number
             : Data::Util::is_string($v)  ? is_string
             : Data::Util::is_value($v)   ? is_value
             : undef;
    }
    elsif(ref $v eq 'ARRAY') {
        return Test::Deep::array_each(convert_matcher($v->[0])) # XXX: 仮定
    }
    elsif(rev $v eq 'HASH') {
        return +{ map { $_ => convert_matcher($v->{$_}) } keys %$v }
    }
}

あとは比較するだけです

my $expect = convert_matcher($production_conf)
cmp_deeply $development_conf, $expect;

(余談)

  • 上の例では、本番環境の設定から(雑な)型推定をしました
  • 設定の型定義を用意しておけるなら、その方が良いかもしれません
    • 異常値などの定義をしやすいですし

以下、crystal の例です

require "json"

class Config
  JSON.mapping({
    servers: String,
    options: { type: ServerOptions },
  })
end

class ServerOptions
  JSON.mapping({
    utf8: Int32
  })
end

Fixture のテスト

リレーションチェックのため、 DBIx::Schema::DSL, SQL::Translator::Schema を利用しています

チェックには、外部キー制約を利用します
実際のアプリケーションでの利用は、諸事情 でしていないですが、 テストでの利用では最高に便利です
(この辺の外部キーありなしのスイッチを、no_fk_output/output だけで簡単にできるのが、DBIx::Schema::DSL めっちゃ便利だーってなって便利です)

流れは、

  • 外部キー制約つきのスキーマを用意して、INSERT する

だけです。 ただ、エラーメッセージを、人間が読みやすい形にするため、地味にループを回すようなコードにしています:p

# Schema
use 5.014002;
package MyProj::DB::Schema {
    use DBIx::Schema::DSL;

    create_table 'module' => columns {
        integer 'id', primary_key, auto_increment;
        varchar 'name';
        integer 'author_id';

        add_index 'author_id_idx' => ['author_id'];

        belongs_to 'author';
    };

    create_table 'author' => columns {
        integer 'id', primary_key, auto_increment;
        varchar 'name', unique;
    };
}
# belongs_to.t

$db->execute(MyProj::DB::Schema->output);

my $schema         = MyProj::DB::Schema->context->schema;
my $module_schema  = $schema->get_table('module');
my $constraints    = $module_schema->fkey_constraints;

for my $fkey (@$constraints) {
    my $ref_table = $fkey->reference_table;

    my @errors = try_load($ref_table);
    ok !@errors
       or note explain @errors;
}

sub try_load {
    my ($table) = @_;

    my @errors;
    for my $row (@{$FIXTURE_DATA{$table}}) {
        try {
            $db->insert($table => $row);
        }
        catch {
            push @errors => shift;
        };
    }
    return @errors;
}

まとめ

  • アプリケーション設定ファイルのテストFixtureのテスト の実装例を紹介しました

明日は、 nekobato さんです!

Hello, Crystal

この記事は Crystal Advent Calendar 2015 の 2 日目の記事です

昨日は、pine613 さんの これから Crystal を始める方へ: Crystal 日本語情報まとめ でした。 公式ドキュメントの日本語訳が整っているの素敵ですね!! http://ja.crystal-lang.org/

今日は、crystal の setup から、heroku に deploy するまで、です。
完全、後発です。厳しいです。詳しいことは、↓を見ると良いのではないでしょうか!?

subvisual.co

zephiransas.github.io

setup

  • *env 最高
% anyenv install crenv
% crenv install 0.9.1
% crenv rehash
% crystal -v
Crystal 0.9.1 [b3b1223] (Fri Oct 30 03:26:53 UTC 2015)

initialize app

% crystal init app app # `app` という名前のapplication の雛形用意
% cd app
% git commit -m 'initial commit'

create app

  • crystal-lang.org の冒頭のHello返すだけのHTTP server に OptionParserでport食わせられるようにしただけ
% cat src/app.cr

require "http/server"
require "option_parser"

port = 8080
OptionParser.parse! do |parser|
  parser.on("-p PORT", "--port=PORT", "Set server port") { |p| port = p.to_i }
end

server = HTTP::Server.new("0.0.0.0", port) do |req|
  HTTP::Response.ok "text/plain", "Hello"
end

puts "Listening on http://0.0.0.0:#{port}"
server.listen
  • 試しに実行してみる
# 即時実行
% crystal run src/app.cr
Listening on http://0.0.0.0:8080

# ビルドしてあげる
% crystal build src/app.cr
% ./app -p 5000
Listening on http://0.0.0.0:5000

deploy to heroku

  • Procfile とりあえず用意して、手元の環境で、ps 動かしてみる
% cat Procfile
web: ./app -p $PORT
% heroku local
forego | starting web.1 on port 5000
web.1  | Listening on http://0.0.0.0:5000
% git add .
% git commit -m 'update'

heroku create

  • ↓のカスタムビルドは、適宜・・好きなのを用意してください
    • 今回は手抜きで、crystal version が、手元では、0.9.1 だけど、0.9.0 でビルドされるカスタムビルドパックを利用しました:p
    • 今回のカスタムビルドの要件は、0.9.0 でのビルド。shard.yml があること。./src/app.cr がソースファイルで、 ./app にリリースビルドされて、Procfile./app を呼び出している感じですね。
% heroku create --buildpack https://github.com/scaint/heroku-buildpack-crystal.git
% git push heroku master

Counting objects: 16, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (10/10), done.
Writing objects: 100% (16/16), 306.50 KiB | 0 bytes/s, done.
Total 16 (delta 0), reused 0 (delta 0)
remote: Compressing source files... done.
remote: Building source:
remote:
remote: -----> Fetching set buildpack https://github.com/scaint/heroku-buildpack-crystal.git... done
remote: -----> Crystal app detected
remote: -----> Installing Crystal 0.9.0
remote: -----> Installing Shards 0.5.3
remote: -----> Installing Dependencies
remote: -----> Compiling src/app.cr
remote:
remote: -----> Discovering process types
remote:        Procfile declares types -> web
remote:
remote: -----> Compressing... done, 409K
remote: -----> Launching... done, v3
remote:        https://arcane-harbor-8307.herokuapp.com/ deployed to Heroku
remote:
remote: Verifying deploy... done.
To https://git.heroku.com/arcane-harbor-8307.git
 * [new branch]      master -> master
% heroku open

反省

アドベントカレンダー事前に準備する

明日は、 5t111111 さんです!