AWS Startup Tech Meetup 関西 #1 で登壇しました

こんにちは、エンジニアのAlvinです!

今年の9月、私は、AWS Startup Communityというイベントに登壇させて頂いたので、当日の発表までを振り返ってみたいと思います。

きっかけ

そもそも登壇のきっかけは、開発部門責任者から「AWSさんが主催する開催したスタートアップコミュニティで登壇をしませんか?」と声を掛けてもらったことです。私は、エンジニアとしての登壇経験がありませんでしたが、思い切って登壇をすることに決めました。

準備

登壇を決めた後、早速準備期間に入っていきました。テーマとしては、技術系のイベントだったので、自分が担当したAWSを使ったサーバーのアーキテクトの話を選びました。

AWSさんのサービスを使い、それで素晴らしい結果を出すことができたので、きっと興味を持ってくださる方がいるのではと思いました。

スライド構成案作りが終わって、エンジニアメンバー数名に集まってもらい、構成案を見てもらいました。リハーサルも兼ねていたので、発表の仕方や技術的な内容について、多くのフィードバックをもらうことができました。Baseconnectのメンバーが背中を押してくれてとても心強かったです。

最後に、デザインチームに頼んで、スライドをデザインしてもらいました。とても綺麗で読みやすくなりました。最終のスライドはこちらになります!

speakerdeck.com

内容は主にAWSのサーバーレスサービス(Lambda、API GatewayAWS Batch)を活用し、高可用性かつ低コストなサービスを作ることについてお話しました。

当日

イベントは大阪で開催されました。ちょうど私はインドネシアにいたため、私以外の登壇者の方は全員現地で参加し、私はリモートで、オンラインとオフラインのハイブリットで開催されました。

エンジニアとしての初登壇だけでなく、他の登壇者の方々は錚々たるメンバーばかりです。プロダクト開発の責任者、CTO、Co-Founderの方が登壇していました。オマケに、いざ自分の登壇の番になると、PCの不調で順番が最後になりました。またしても緊張しました。

ところが、いざ自分の出番の数分前になると、なぜか分かりませんが、落ち着くことが出来ました。 心拍数が低くなって、頭がクリアになり、普通に話せるようになったんです。プレゼンは無事に上手く終えることができました。

こちらは実際の登壇の様子です。

youtu.be

数日後、参加者の方からのアンケートのコメントを共有いただけました。自分のLTに興味を持ってもらえたこと分かってとても嬉しかったです。

終わりに

今回、はじめてイベントに登壇をして、多くの人の前で発表する機会を頂きました。とても緊張しましたし、勇気のいるチャレンジでしたが、このようなチャンスは何回もあるわけではないので、今後もあらゆる機会を最大限に活用していきたいと思います!

色々とお世話になりこのような場を設けて下さったAWSさん、たくさんのサポートをくれた開発チームメンバーの皆さん、本当にありがとうございました!!

herp.careers

レゴを使ったスクラムワークショップを開催しました

こんにちは。プロダクト開発チーム サブマネージャーの富田です! 主に社内の「データ製造部門(DMF)」向けの内部システムの開発を担当しています。

弊社ではMusubuという企業データ情報サービスを提供していますが、この企業データを作成する部門が「DMF」です。 (Data Manufacturingの略です。)

開催したきっかけ

先日、DMFで組織改編が行われ、プロダクトオーナー(PO)が変更になりました。 開発部門でも新しい方が入社されたので、いい機会かと思い、アジャイルのワークショップを開催することにしました。

以前、紙飛行機を使ったワークショップを開催したのですが、今回は、新しいPOの方をメインターゲットにしたかったので、開発よりもプロダクトを意識したものにしたいと考えました。

そこで「レゴスクラム」をやってみることにしました。

どんな感じで開催したの?

レゴスクラムは、LEGOで街を作っていくワークショップです。

このワークショップ、どんな感じのものなのかは知っていたのですが、実際に体験したことが無かったので、lego4scrumやネット上の記事を読んだりしたものの、若干手探りな状態となってしまいました。 (いつかちゃんと参加者として体験してみたい!)

ネットの記事を見ると、チームに分かれて、それぞれのチームが作りたい街を別のチームが作る、という形で進めることが多いようでしたが、今回は私の方で予め作りたい街を用意しておくことにしました。

ずばり「休日に運動が出来る街」です!

街にどんな施設が欲しいかという要望は、下記のような感じで作成しておきました。

お父さんとして、ジョギングできる道が欲しい。
なぜならば、リモートワークの運動不足を解消したいからだ。
会社員として、スキー場が欲しい。
なぜならば、スノボが趣味で、毎年冬は泊りがけでスキー場に行くからだ。

参加者は下記のような感じでした。

  • エンジニアメンバー2名
  • DMFメンバー4名
  • 採用メンバー1名

また、今回は「見積もり」は行いませんでした。 私自身がこのワークを体験したことが無く、あまり要素を盛り込みたく無かったからです。

ワークショップの進め方

最初にアジャイルスクラムの座学を10分程行い、今回のワークショップの狙いについて共有しました。 今回はプロダクトを意識した感じです。

その後、下記のように進めました。

  • どう作っていくのかの検討(リリース計画)
  • 実際に作ってみる(スプリント)
  • ワークショップのふりかえり

リリースを計画してみる

プロダクトのゴールを意識してもらいたかったので、最初にどう作っていくかの計画を考えてもらうことにしました。

作りたい街の要望をどんな順番で実現していくのか、POと開発チームが相談しながら計画を練っていきます。

例えば、↓の要望に関しては、「ラウンドワンは必要なのか?色々なスポーツが出来る施設があれば良いのでは?」というように、目的部分から作るべきものを検討していたりしました。

高校生として、ラウンドワンが欲しい。
なぜならば、色々なスポーツを楽しめるからだ

10分間、色々と検討して、なんとなくまとまってきたようです、、、

作ってみる!

1スプリントは下記のような感じで、3スプリント実施しました。

  • プランニング(3分)
  • 実施(7分)
  • レビュー(3分)
  • ふりかえり(3分)

プランニング

プランニングでは、POから何をスプリント内で実現したいのか開発に伝えてもらい、開発はどうやったら実現出来るのか考えてもらいました。

たとえば、POが↓の要望を出した時、すでにプールは作っていたので、開発チームから「プールの水を凍らせるマシンを設置することで実現すればどうか」という提案が出ました。

高校生として、スケート場が欲しい。
なぜならば、冬にスケートを上達したいからだ。

POと開発チームが話し合い、凍結マシーン(っぽいLEGOブロック)を設置するということで実施内容が決定しました。

実施

レゴの種類を分ける人が出てきたり、レゴ置き場近くで仮組みをしてみたりと、スプリントを進めるに従って、色々な改善を行っていました。

コミュニケーションと距離

また、POと開発チームの「距離」も意識することが出来ました。

最初、POは開発チームが作業している時、少し離れた場所に居たのですが、3スプリント目では開発チームの近くに居るようになりました。

作業中、力を入れすぎて「屋根」が崩れてしまったのですが、POが横で見ていたので「障害が発生した」状況もリアルタイムに知ることが出来ました。 また、開発チームからの質問にもすぐに答えることが出来るようになっていたようです。

リモートベースになって難しくなってしまいましたが、「全員同室」の利点を感じてもらえたのではないかなと思います。

真ん中の後ろ向きの女性がPO。開発チームの様子を見に来ています。

エンジニアメンバー以外の開発体験

今回、エンジニア以外のメンバーが半数以上だったのですが、要件に沿って何かを期間内に作るという体験が出来たのではないかと思います。

欲しい形のLEGOが見つからなかった時「技術的にちょっと厳しいです」と、エンジニア的な会話も飛び出したりしていました!

レビュー

レビューでは、POがプランニング時に立てたゴールを実現出来ているか確認します。 開発チームは、説明やデモ(扉を開けたりとか)を行います。

たいてい要望どおりのものが出来ていたのですが、↓の要望に対して、開発チームが「馬屋」作ってしまうことがありました。

馬として、乗馬場が欲しい。
なぜならば、人を乗せて全速で走りたいからだ。

POとしては、「馬屋では全速で走れない」ということで、この成果物は「却下」されました。 プランニング時にしっかりゴールを共有する必要があるという気付きになったようです。

街が出来上がりました!


やってみてどうだったか?

ワークショップを終えてみて、簡単にふりかえりを行いました。

よかったこと
  • もっと要件を擦り合わせる必要があるという気付きがあった
  • 1サイクル内で出来る事・出来ない事を試す事が出来た
  • 最初に要件を読み合わせてイメージを共有することが出来た
  • スプリントを進めると、作るものが増えた
もっとよくできそうなこと
  • コミュニケーションをもっと工夫すればよかった
  • 「何のために必要か」をもっと意識する必要があった
  • (今回はオフラインだったが)リモートだとコミュニケーションの工夫が必要に感じた

POをしてみた感想

POからも参加した感想を頂きました!

プロダクトオーナーがやるべきことについて座学では概要を把握していたものの、身近なテーマとレゴを使って進めていくことでよりスクラム開発への理解を深めることができました。

今回は誰でも理解できる身近なテーマだったので、「この要望とこの要望は近い」「この施設はこれに変換できる」といった判断が比較的イメージしやすかったですが、実際の業務においてはステークホルダーの求める像の理解と開発の方と会話できる下地がとても重要で、プロダクトオーナーを務めるには幅広い知識とコミュニケーションを継続して行わなければいけないなと改めて痛感しました。

とても記憶に残るワークショップだったので、時々思い出しながら普段の業務に活かしていきたいと思います!

概ね好評そうだったので良かったです。 狙っていたプロダクトゴールを意識した開発の体験も、ある程度出来ていたのではないかと思います。

ただ、ワークショップ自体のふりかえりとして「リリース計画とスプリントプランニングの棲み分けが分かりにくかった」ということも挙がっていたので、次回開催時はもう少し改善出来ればと思います!

herp.careers

とある言語オタクエンジニアの平凡な1日

おはようございます。こんにちは、こんばんは。 Baseconnectエンジニアの御園です。

今回の記事は、タイトルの通り、私御園が、Baseconnectで働きながら、どのような平日を過ごしているかの1例を文章化してみたものです。

新社会人の皆さんや、歴戦の皆さんも、他の会社で働いている人はどんな暮らしをしているんだろうと気になりがちなのが人間の性というものです。 後は単純に、他の会社のエンジニアはどういう生活をしているんだろう、などと気になっている方もいるかもしれません。

自分以外の人間の生活から得られる刺激、発見、あるいは共感。そういったものは時に、自分の生活のふとした寂しさを紛らわせてくれたり、あるいは単なる笑いを提供してくれたりしますね。

私の生活を覗いてみた時、あなたが感じるものは何でしょうか? 特に何の変哲もない独身男性のエンジニアの1日ですが、これを読み終えた時に少しでもあなたの印象に残るような記録になっている事を祈りながら、文章を書いてみるとします。

起床

私は残念ながら朝が得意ではありません。 おそらく、Baseconnectで働いている人達の朝が苦手ランキング上位に食い込むでしょう。

なので、私の朝は必ず何度かのアラームと一緒に始まる事になります。 「後n分も寝れる」という安心のまどろみが大好きなので、だいたい起きなければいけない時刻の1時間〜20分前にアラームをかけます。

そこから、起きなければいけない時刻に対して、徐々にスヌーズの間隔を狭くアラームをかけていくのです。

9:00
9:20
9:25
9:28
9:30 <= デッドポイント

ポイントは「必ずこれだけは飛び起きる」というアラームの音を自分の中で修業によって作り出し、デッドポイントにはそれで飛び起きるようにしています。 というわけで、いつも9:30頃に私は起床するわけです。

Baseconnectはフルフレックスです。6:00〜22:00の中で自分が働きやすい時間を選択する事が出来ます。極端な話もっと遅めに起きてもよいわけですが、バランスが崩れないように意識しつつ、前職の流れからだいたい10:00には仕事にIN出来るように起床します。 朝が得意ではないとの代名詞の如く、目覚めた直後はめちゃくちゃ眠たい顔をしています。

朝のルーティン

モーニングルーティンなんて動画が流行りましたね。 残念ながら、私はごく平凡な一般独身男性ですから、 has_special_morning_routine()false な訳です。 まず、トイレに行き、洗面台に向かいます。眠たい顔を冷たい水で洗い、少し目を覚ました後、歯を磨くのです。

私は20代前半までは、朝の歯を磨く時間すら睡眠に充てたくてサボっていましたが、死ぬほど虫歯が出来て治療し、そして割とそれが痛かった経験から、ちゃんと朝もしっかり歯を磨くようにしています。

だいたい洗面台の前で歯磨きを始めますが、歯を磨いている間はリビングに戻り、テレビを付けてYouTubeを垂れ流します。内容はほぼ頭に入ってきませんが、歯を磨く手を動かしながら、人の声を聞くことで、徐々に頭を覚醒状態に持っていくのです。

そして歯を磨き終えたら……、おっと今日は特別です。 最近雨続きで洗濯物が溜まっていました。今日は曇りで怪しいですが、洗濯物を干さなければ限界そうです。 洗濯機のスイッチを入れて、洗濯のタスクを洗濯機に委譲します。引数は洗濯用洗剤です。

ここまでが、朝私がいつも行うことです。特に何も面白くなかったでしょ?見事に return falseでしたね。 

午前のワーク

ミニクロワッサンと、健康を意識して豆乳を朝食にとります。 リビングのテーブルにMac Bookを開いたら、仕事モードに頭を切り替えます。

Baseconnectは、リモートベースという、最もパフォーマンスが発揮できる場所を選択出来るという働き方を導入しているので、オフィスもありますが、基本的には自宅の人が多いです。私はオフィスまで徒歩数分の所に住んでいるのですが、本日は洗濯もしたいのでリモートで作業をすることにしました。

Slackの勤怠連絡チャンネルで開始を告げ、freeeで出勤のボタンを押した後、1日のスケジュールをGoogleカレンダーで確認しておきます。また、タスク管理ツールで自身のタスクを再確認し、1日に何をするかを組み立てておきます。

人によって違うと思うのですが、私は午前中はだいたい確認作業や、各種連絡、資料作成の時間に充てることが多いです。あまり午前中にコードを書くことはしません。 この日は、GitHubでレビューを行い、またそれ以外でも技術レビューがあったのでそれを行います。

各種更新されている重要な資料を確認し、連絡が急務なものがないかを振り返ります。 リモートベースでは連絡の取りこぼしがあると仕事に差し支えるので、気をつけないといけないところな訳です。

私の所属している開発チームは、スクラムを取り入れています。1日の始まりの時間に、「デイリースクラム」が行われます。そこでは、開発チームメンバー内で、昨日やったことと今日やること、困っていることを共有します。

場合によっては、困っている事を解消するために、直後ペアプロやモブレビューなどが差し込まれます。この日は11時から。だいたいMeetで行われます。 デイリースクラムに出席し、私も上記の事を共有し、その日は何事もなくデイリースクラムは終了しました。

おっと、洗濯機の音が

デイリースクラムを終えた後、洗濯機の洗濯タスクが end していました。例外を吐くこと無く無事に終了しているようです。離席する旨を勤怠連絡チャンネルに書き込み、freeeで休憩処理をした後、洗濯物を干しに向かいます。

バスタオルを干している時に、外に居る学生が傘をさしているのが見えました。小雨が降ってきたようです……。しかし、今日干さねば限界です。これ以上洗濯物をstackqueue出来ないため、洗濯物を干すことを敢行します。

結果として、干しきった時に雨はやんでおり、曇に戻るどころか晴れ間が見えていたので勝利です。私は全体的に晴れ男なのです。

昼食

洗濯物を干した後、またワークに戻っていましたが、お腹が空いてきました。お昼時です。お腹が空くと人間は能力を十分に発揮できません。なので、私はしっかりとお昼は取るようにしています。

一人暮らしだからといって横着する事はせず、自炊していますが、お昼はそんなに手の混んだものはつくりません。この日のメニューは、明太子のお茶漬け、ウィンナー、スクランブルエッグ、チンゲン菜のごま油炒めです。

is_alonetrue なので、見栄えなど特に考えず、洗い物効率を考えて食事をします。この日は炒めものしかしていないので、そのまま菜箸で食べました。だいたい昼食のお供はYouTubeです。

昼食を食べ終えると、しばらくそのままYouTubeを見、時間になると洗い物をします。 そして時間になったら午後のワークを開始します。気分を変えるために、リビングのテーブルから、デスクトップPCを展開しているテーブルに移動するなどします。

午後のワーク

休憩から戻った時に私がやることは、Slackの一通りの確認です。メンションなど以外も、割と細かく確認します。自身に緊急でない連絡なども、他のチームの仕事の流れを掴むのに目を通したり、あるいは他愛もない雑談が出来そうであれば、即座に突っ込んだりします。

リモートベースではFace to Faceで話す事が少ない分、雑談のきっかけも生まれず、結果として絡みが少なくなったりするので、そこは積極的に意識をしていきます。確認が終わると、午後はコーディングを含めた作業に重きを置いてワークしていきます。

技術的知識のインプットや、比較的かなり頭を使う作業を午後に置くことが多いです。 私は最近関わっているプロジェクトの都合上、かなりの量新しい知識を仕入れることが多く、この日もTypeScript関連の知識を膨大に習得しつつ、プロジェクトへのアウトプットを進めていきます。

ところで私は音楽を聞きながらノリノリでコーディングをすることが多いです。 聞いてる歌詞を口パクで歌ったりしてしまうことがあるのですが、オフィスでそれをやると不審者に思われるので、リモート冥利に尽きます。ただ、さらに深部にまで思考が潜る時は、逆に何も聞かず、雨の音等を聞いたりします。

そして空中をただずっと見つめたりする訳です。何も考えてないわけではなく、見つめている空中に様々な情報が空想でビュンビュン飛び交っているわけですから、やはりこれも他の人からすれば不審者です。つまり仕事をしている時の私は不審者な訳です。 セキュリティソフトから言わせれば、 suspicious object な訳ですね。ですが、エンジニアならきっと誰もが経験してるはず。

仕事に詰まった時は...

一人で仕事をしていると、煮詰まってしまってどうしようもない時があります。BaseconnectのSlackには面白い機能があり、「いでよ」と発言すると即席のMeet URLが発行されるのです。

MTGが入っていなければ、チームメンバーが助けてくれます。良く詰まった時はモブプロやモブレビューをします。また、オフィスに居る時は、近くに居るチームメンバーとホワイトボードを使って議論したりします。

チームメンバーと話しながら、考えを言語化するというのはとても良いことです。 頭の中のもやもやが整理され、結論が整頓されます。

合間の休憩

作業の一区切りがついた段階など、作業の合間には小休止を挟むことがあります。 伸びをしたり、水分補給したり。 Baseconnectのオフィスには共有スペースに誰もが飲んで良いコーヒーや紅茶、あるいはチロルチョコなどが用意されています。

乾かぬ洗濯物

日が沈み始めたので、洗濯物を取り入れるときです。晴れというよりかは曇りでしたので、やはり洗濯物が乾ききることはありませんでした。晴れ男敗北です。

statewet ですが、仕方なくCtrl+Cで洗濯物を取り込みます。 私の部屋の浴槽は洗濯乾燥室も兼ねていますから、そこに干して2時間ほど追加で乾燥をかけることにしました。

1日のワークの締め

私の所属している開発チームでは、夕方頃に「夕会」というMTGを行っています。 これはMTGといってもそんなにお硬いものではなく、チームでのコミュニケーションを深める目的のものとなっています。

普段はトークテーマを決め、それについて雑多に話す場になっていますが、週1でこの夕会の時間でCode Golf(いかに短くコードを記述出来るかを競う)を行うことになっています。

そしてこの日はCode Golf、テーマを適当なサイトから持ってきます。チームで業務に関係ないコードを書いて、お互いに見合うのは面白いです。また、汚くてもいいので短く書くという観点は、普段の業務では得られないような気づきを時に提供してくれるので、いい刺激になります。

夕会が終わったら、取り掛かっていたタスクに一区切りついたところで、大体その日のワークを終えます。

勤怠連絡チャンネルに書き込み、freeeで処理をして終了です。だいたい私は19:00〜20:00頃にワークを終えることが多いですが、これも人によってまちまちですし、用事がある日は17:00頃にはもう終えて外出、なんてこともあります。

整骨院

いつもはお腹がもうペコペコなので夕食を作り始めるときですが、この日は整骨院の予定があったので整骨院へ向かいます。私はずっと姿勢が悪い状態で仕事をしてしまっていたので、整骨院で調整をしてもらうのです。私自身がプログラミングコードだとしたら、整骨院はコードフォーマッターですね。

また、この日は鍼も打ってもらいました。私はエンジニアですから、自分の身体が治っていくのを客観的に観察するのが凄く面白いので、私自身に針が刺さっている様子を写真で撮って見せてもらいました。

夕食

整骨院から帰ってきました。もうお腹がペコペコで今私の身体にガーベジコレクションを走らせても何も起こらないでしょう。夕食の支度に取り掛かります。 予め解凍して ready状態にしておいた鶏ささみを使用し、この日は鶏ささみの唐揚げ、豆腐、レタス、トマトを夕食として摂りました。

いつもはトマトはちゃんと切ってるのですが、たまにまるごとかじりつきたくなるときがあり、この日はまさにそれでした。身体の事を考え、よく噛んで食事をします。やはり食事のお供は、だいたいYouTubeです。

夜のルーティン

夜にやることは大体決まっています。

  • プログラミングか技術的な勉強
  • 作曲
  • ゲーム
  • 筋トレ/散歩

私はだいたいこんなものです。この日は整骨院に行った後なので、筋トレと散歩はしません。食器洗いや洗濯物を整理した後、技術ブログ等に目を通すことにしました。

is_alonetrue なので、気兼ねなく一人の時間を過ごせます。プログラミングし始めると楽しくなっちゃって時間が飛ぶので、程々にします。一方、寂しくなった時は時折Discordで通話しながらゲームなどをやります。別に何にも特別な事はなく、Twitterもダラダラと見たりしますし、YouTubeを垂れ流しながら過ごします。 やはり、夜も has_special_night_routine()false でしたね。

就寝

だいたい1時頃には必ずベッドに入っています。寝付きがあまり良くない方なので、早めに就寝体制に入っておくことが重要なのです。これは、Mac Bookを開いた直後にdocker-compose upをするのに似ていますね。

ベットに入った後は、少しだけスマホを触ります。といってもそんなにガッツリは触らず、連絡等の確認や、Twitterを少し見るだけです。私は雑音があった方が寝れるタイプなので、30分ぐらいの動画を音を小さく流しておきます。

寝る前は割と1日のうちにあった反省点を強く反芻しがちなのですが、これだと寝付けないケースがあるので、出来るだけ楽しいことを考えるようにしています。睡眠へのコンパイルが通ったら、後は夢の中。楽しい夢を見れるように願いつつ、私の1日は終わりです。

Outro

どうでしたか? 共感するところ、出来ないところ、はたまたいろいろな感想があったと思います。 少しでも皆さんの脳裏に関心を残せたなら、私も記事にした意味があるというものです。

ここで紹介したのは私の平凡な1日ですが、Baseconnectでは序盤に述べたようにフルフレックスフルリモートなので、実に様々な生活体系があります。理想の生活を実現するためにフィットする職場を探しているエンジニアは、以下の採用ページを覗いて見ましょう。

herp.careers

TDDで開発すべき理由 Pourquoi il faudrait toujours développer en TDD

こんにちは、フロントエンドエンジニアのCouthouis Paulです。メンバーの皆からはクーさんと呼ばれています。

今回、TDDについてお話ししてみたいと思います。私はフランス出身なので、母国語のフランス語を少しまじえながら記事を書きました。 挿絵イラストは同じチームの山本さんが描いてくれています! よければ、ぜひご覧下さい。

introduction

一つに統制された開発手法によって仕様が実現されることが理想的ですが、現実的には開発手法は開発者と同じくらい多くあります。 そんな状況で、過去の開発手法によって仕様の変更に対応することが難しくなる、という問題に直面します。

そしてその変更自体がさらに次の変更を困難にしてしまい、保守がどんどん困難になるコードが引き継がれるという悪循環が発生します。 これは開発者にとってよく知られた問題です。

TDD : Test Driven Development

開発手法としてのTDDで特徴的に感じるのは「テスト」です。 ですからTestが注目され「TDDはテストのための手法である」と言われることがあります。

しかしそれは本質的ではありません。 TDDで一番注目すべき文字は、Test ではなく Drive なのです!

TDDにおいて、テストは開発にありがちな無理や徒労、脱線を防止してくれる、あなたのためのガイドなのです。

原理は「ベイビーステップ(小さなステップ)」です。

  • テストを書く
  • 最初はテストに失敗するコードを書く
  • できるだけシンプルにコードを修正し、テストが成功するように修正します(この時点でコードが美しい必要はありません)
  • テストを通過したら、充分に時間をかけて、コードを美しく、読みやすく、リファクタリングします。

これで次のステップに進むことができ、TDDの輪をさらに広げることができます。

これを実践することで、常にテストカバレッジが100%になります。 (そうでなければ、何かがおかしい)

メリットは他にもあります。

開発者が全体を慮り、予想・想像しながら開発し、結果的に迷走や蛇行運転となり、徒労となっている状態はよくあります(YANGI の原則から逸脱する)。

TDDは、現在の「ベイビーステップ」にのみ焦点を当てます。 いま行おうとしている次のステップは自ずと明らかになっていて、あらかじめ思考しておく必要もありません。

つまり、TDDはあなたのガイドであり、ドライバーでさえあるのです。 まるで、車のGPSが正しい方向か間違った方向かを教えてくれるように、アシストしてくれるのです。

それがTDDの醍醐味です。

TDD:Une discipline exigeante

しかしTDDを実践するには、厄介な問題があります。 TDDを成功させるためには、今までの習慣を捨て、いくつかの必須ルールを守る必要があるのです。

  • テストを合格するためだけに、コードを追加する
  • テストに合格するため以外のコードを書いてはならない。「必要になるかも」等の予想は無用です。
  • テストを失敗したままにしてはいけない。

また、テスト対象のレイヤーが他のレイヤーに依存している時に、記述することが難しくなります。 依存性の注入(DI)と Clean Architecture は、TDDに取り組むために知っておくべき重要な概念です。

お勧めは、TDDの鍛錬に役立つ様々な「KATA」がインターネット上にありますから、参照し練習することです。

※ 「KATA」・・・日本の武道などで伝統的に使われるKATAの意味で使っています。

Développer en TDD dans une base de code existante

また、既存のコードをベースにしてTDDを始めることに難しさを感じるでしょう。私が Baseconnect に入社したとき、まさにそんな状況でした。

ゼロから立ち上げるプロジェクトにTDDで取り組むことが理想的ですが、実際の現場にそんな機会はなかなか恵まれません。対峙しなければならないコードには、独自のテストが実装されていたり、テストが無かったりするのです。

私はTDD無しではやっていけないことに気づき、解決策を模索し続け、ようやくその方法に辿り着きました。 いわば、私が編み出した「KATA」です。

その方法とは最小限のコードを既存のコードに注入することです。

  • まず、新しいスタンドアロンモジュールにTDDを使用して新しいコードを実装します。
  • 完了したら、依存性注入の原則を使用して、このモジュールを既存のコードに注入します。

実際にやってみましょう!

TDDで書かれたコードはすべて「new_src」フォルダに、古いコードは「src」フォルダに収められています。

まずディレクトリ構成です。(Frontendの例で説明します) 古いコードは「src」フォルダに収められており、TDDで書かれたコードはすべて「new_src」フォルダに収められるものとします。 最終的に「src」のコードは「new_src」に移動することを目指します。

> new_src
> node_modules
> src

「new_src」フォルダは、独立したアプリケーションとして機能します。 Reactコンポーネントがあります。

export const Header: React.FC = () => {
  return (
    <x.div data-testid="header" display="flex" justifyContent="space-between">
      <div>
        <Responsibles />
        <TagManager />
      </div>
      <div>
        <StatusManager />
      </div>
    </x.div>
  )
}

また、Redux Toolkitのスライスもあります。

import { createSlice } from '@reduxjs/toolkit'
import { CurrentUserEntity } from './entities'
import { retrieveCurrentUser } from './use-cases'

const initialCurrentUser: CurrentUserEntity = {
  uuid: '',
  authorityName: null,
  branchOfficesStatusOptions: [],
  statusOptions: [],
}

export const currentUserSlice = createSlice({
  name: 'currentUser',
  initialState: initialCurrentUser,
  reducers: {},
  extraReducers: builder => {
    builder.addCase(
      retrieveCurrentUser.fulfilled,
      (_, { payload: { currentUser } }) => ({
        ...currentUser,
      })
    )
  },
})

もちろんテスト用ファイルも。

it('should retrieve status options', async () => {
    const {
      dispatchRetrieveCurrentUser,
      selectStatusOptions,
    } = retrieveCurrentUserSUT().withCurrentUser(fakeAdmin).build()

    await dispatchRetrieveCurrentUser()

    expect(selectStatusOptions()).toEqual<CurrentUserEntity['statusOptions']>([
      {
        label: 'label1',
        value: 'value1',
      },
      {
        label: 'label2',
        value: 'value2',
      },
    ])
  })

最後に、モジュール全体を構築するメインファイルを用意します。 実際のアプリケーションとの唯一の違いは、注入に使用するキーワード 'export' の存在です。

import { Provider as ReduxProvider } from 'react-redux'
import { storeQueries } from './api/queries'
import { AppShell } from './app/AppShell'
import { createStore } from './core/store'

const store = createStore(storeQueries)

export const MainShell: React.FC = ({ children }) => {
  return (
    <ReduxProvider store={store}>
      <AppShell>{children}</AppShell>
    </ReduxProvider>
  )
}

目標は「new_src」のファイルを「src」のファイルに注入できるようにすることです。 そのため、MainShellは "src "のAppShellコンポーネントにインジェクションされます。

import { ThemeProvider } from '@xstyled/styled-components'
import { QueryClientProvider } from 'react-query'
import { NotificationProvider } from '~/components/Notification'
import { queryClient } from '~/queries/queryClient'
import { theme } from '~/style/theme'
import { MainShell } from 'new_src/main'

export const AppShell: React.FC = ({ children }) => {
  return (
    <QueryClientProvider client={queryClient}>
      <ThemeProvider theme={theme}>
        <NotificationProvider>
          <MainShell>{children}</MainShell>
        </NotificationProvider>
      </ThemeProvider>
    </QueryClientProvider>
  )
}

これにより「src」フォルダ内のファイルであっても、すべてのアクションとreduxセレクタにアクセスすることができるようになります。 ただし、既存の「src」テンプレートにReactコンポーネントを注入する必要があることに変わりはありません。

import { Header } from 'new_src/app/design/Header/Header'
~
~
~
 <FormProvider {...methods}>
      <Box backgroundColor={'mainBg'} marginTop="13.5rem" px="1rem">
        <input name="currentFocus" ref={register} style={{ display: 'none' }} />
        <Header />
        ~
        ~

こうすることで、コードのどの部分が100%テストでカバーされているかが分かります。 また、コードをリファクタリングして「src」から「new_src」にコードを移せば、いつか100%TDDのコードを手に入れられるでしょう。

Conclusion

開発者の誰もが、膨大で理解し難い、またメンテナンスが困難な開発を経験しています。

でも、最初から最後まですべきことをシンプルに保ち、メンテナンスの問題も無く、アプリケーション開発を成功させる解決策があるのです。それがTDDです。

最初、TDDはとっつきにくい開発方法です。 しかし、一度マスターすれば、あなたとそのチームを幸せにすることができます。

納得できない?ぜひ一度試してみてください!!最後まで読んでいただきありがとうございました!

herp.careers

React18のAutomatic Batchingを試してみてわかったこと

こんにちは!フロントエンドエンジニアの川瀬です。

少し前に、SuspenseやTransitionなど楽しみにしていたReact18がリリースされたのですが、 自動バッチングというのも新要素としてあったのでどういったものかなと試してみました。

自動バッチング https://ja.reactjs.org/blog/2022/03/29/react-v18.html#new-feature-automatic-batching

バッチングとは React がパフォーマンスのために複数のステート更新をグループ化して、単一の再レンダーにまとめることを指します

試してみたreact18とreact17のソース

// App.tsx 18も17も同じ
import React from "react"

function App() {
  const [count, setCount] = React.useState(0)
  const [flag, setFlag] = React.useState(false)
  const render = React.useRef(0)  //レンダー回数を数えます
  
  render.current++

  const update = () => {
    setCount(c=>c+1)
    setFlag(f=>!f)
  }
  
  React.useEffect(()=> {
    const id = setTimeout(()=>{
      setCount(c=>c+1)
      setFlag(f=>!f)
    } ,1000)
    return ()=> clearTimeout(id)
  },[])
  

  return (
    <div style={{
      display: 'flex',
      height: '100vh',
      justifyContent: 'center',
      alignItems: 'center',
      background: '#777',
      color: '#fff'
    }}>
      <div style={{ display: 'flex', flexDirection: 'column', gap: 10}}>
        <div>Version: (18 or 17)</div>
        <div>render:{render.current}</div>
        <div>count:{count}</div>
        <div>flag:{flag? 'true': 'false'}</div>
        <button onClick={update}>update</button>
      </div>
    </div>
  )
}

export default App;
// index.tsx
import React from 'react';
import App from './App';
〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜
// 18はcreateRootを使います
import ReactDOM from 'react-dom/client';

const root = ReactDOM.createRoot(
  document.getElementById('root') as HTMLElement
);
// StrictModeにすると開発環境ではrenderが2回走ってしまうのでやらない
root.render(
  // <React.StrictMode>
    <App />
  // </React.StrictMode>
);
〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜
//17
import { render } from 'react-dom';

// StrictModeにすると開発環境ではrenderが2回走ってしまうのでやらない
render(
  // <React.StrictMode>
    <App />
  // </React.StrictMode>
,document.getElementById('root') as HTMLElement);

動き確認

ブラウザ表示したばかりでは、どちらもレンダー1回目、countやflagの更新はなしです。

useEffectで、setTime内の処理が1秒後に実行され、countとflagが更新されます。 この時、React18ではrender回数は1回更新されのに対し、React17では2回更新されました。 中でstateを更新している分だけrenderが走ってしまいます。

続けてUpdateボタンで更新してみます。

ボタンで更新した場合はどちらも1回のrenderで済みました。

※公式より抜粋

自動バッチング以前は、React のイベントハンドラ内での更新のみバッチ処理されていました。promise や setTimeout、ネイティブのイベントハンドラやその他あらゆるイベント内で起きる更新はデフォルトではバッチ処理されていませんでした。

参考:https://github.com/reactwg/react-18/discussions/21 より

// promise
fetch(/*...*/).then(() => {
  setCount(c => c + 1);
  setFlag(f => !f);
})

// つまりasync awaitでも起こる
const update = async () => {
  await console.log('promise!')
  setCount(c=>c+1)
  setFlag(f=>!f)
}
// ネイティブイベント
elm.addEventListener('click', () => {
  setCount(c => c + 1);
  setFlag(f => !f);
});

非同期とかaddEventListenerとかよく使ってる、気をつけねば・・

つまり?

React17の場合、上記条件でstateが複数更新される場合、その分renderが走ってしまいます。 onClickなどのReactのイベントハンドラであれば問題なし☆

React18からは、予期せぬrenderが走らない => パフォーマンスがよくなっている、ということですね。

React17でどうしてもsetTimeoutやpromiseの中でstateを更新したい場合は、stateをまとめてみるしか?

const [state, setState] = React.useState({count:0, flag:false})

React.useEffect(()=>{
  setTimeout(()=> setState(v=>{ count: v.count+1, flag: !v.flag}),1000)
},[])

また、色々と試していきたいと思います! 最後まで読んでいただきありがとうございました。

herp.careers

Rubyの「=」について無限に紐解いてみる

はじめまして、Baseconnectエンジニアの御園と申します。 私は2022年の4月1日にBaseconnectにエンジニアとしてJOIN、実務として経験するのは初めてのRubyを使って開発業務を行うことになりました。

そして、入社してから1ヶ月にも満たない4月22日、無茶振りによって 社内向けにLTをすることになり、私はプログラミング言語オタクとしての本領を発揮すべく、Rubyにまつわる些細な疑問を深堀りしたLTを行いました。

結果として割と反応はよく、社内の皆さんに良い感想を沢山頂けたので、今回はそのLTの中から抜粋し、テックブログとして記事を書かせてもらうことになりました。どうぞ、よろしくお願いいたします。

introduction

さて、皆さんがRubyを学ぶ時一番最初に学んだ事を覚えていますか? Rubyでプログラムを書く準備、Rubyという言語について……様々な事を学んだでしょう。 そして、Rubyイカしたプログラムを書く時、誰もが最初に学ぶものがあります。 そう、それは変数に値を格納するための「=」ですね。

プログラマが一生付き合うことになっていく「=」。それを記述した数はもはや数え切れないでしょう。 その「=」がもつ働きは、実はRubyという言語の性質を追求するのに実はもってこいという事を知っていましたか? 一回立ち止まって、あるいは少し後ろを向いてみて、「=」にもう一度目を向けてみましょう。 さぁ、私と一緒にRubyの「=」について追求する旅に、一緒に出かけてみましょう。

motivation

一番最初の些細な疑問は、私がRubyでとある演算子を使おうと思ったときに生まれました。 以下のようなコードです。

value++

++ はインクリメント演算子と呼ばれ、JavaScriptPHPなど、Cに影響を受けた色々な言語で使うことが出来ます。 意味は単純で、"変数の値を1加算する"と言ったものです。よく for と一緒に使用されたりしますね。例えばC++のコードでは以下の様に使えます。

for (int i = 0; i < 10; i++) {
  // iは0〜9まで、1ずつ加算されながら、このブロックの中の処理は反復される
}

さて、Rubyの世界に戻りましょう。Rubyに堪能な読者の人ならば、既に「そんなもの、Rubyには無いよ」と鼻息を荒くしていることでしょう。 まさしくその通りです。Rubyには ++ が定義されていません。なので、私は残酷にもRubyにエラーを吐かれてしまいました。 ( ++を使った直後の行を対象に以下のエラーを出される )。

syntax error 

Rubyにはインクリメント演算子も、"変数の値を1減算する"ところのデクリメント演算子 -- もありません。 例えば1加算する、といった事を実現するためには、以下の様にする必要があります。

value = value + 1

ただし殆どの人がこう書かず、必ずこう書くでしょう。

value += 1

自己代入などと表現されるこの += は、 value = value + 1 と同じ意味を持っています。 これはある意味シンプルです。 変数の値を加算、あるいは減算するという事に対しては、全て同じ演算子を使えば良いということになります。 1を足す時も、何かそれ以外を足す時も+=。1引く時も、何かそれ以外を引く時も -=。 わざわざ ++-- という特別な演算子の事を学習する必要がありません。 しかし、JavaScriptPHPなどに見られるように、”あっても良さそう”という気持ちが生まれるのも確かです。

何故Rubyにはインクリメント、デクリメント演算子が無いのでしょう? プログラマは探求をするのが好きです。私もご多分に漏れずその性を持っていますから、こんなちょっとした興味で、Rubyにこれら2つの演算子が無いことを調べ始めました。 そして、すぐさま、その疑問が解消される事については、Rubyという言語そのものの性質を知らなければならないことに気づくのでした。

奇妙なコード

Rubyを学習しようと文献を漁ると、高確率で目にしたり、聞いたりするものがあります。

Rubyでは全てがオブジェクト

これはどういう事なのでしょうか? この事を知るために、まずは次のようなシンプルなコードを実行してみます。

class Hoge
end

i = Hoge.new
puts i.object_id

object_id メソッドは、そのオブジェクトのIDを取得する事が出来ます。 Hoge クラスは new メソッドによってオブジェクトとして実体化され、 i に格納されました。 そして、その i に対して object_idメソッドを使用することによって、実体化したオブジェクトのオブジェクトIDが取得出来るというわけです。 例えば私の環境では、以下のように出力されました。

60

では、もう一つ、次のようなコードではどの様に出力されるでしょうか?

class Hoge
end

puts Hoge.new.object_id
puts Hoge.new.object_id
puts Hoge.new.object_id

このコードは、例えば私の環境では以下のように出力されます。

60
80
100

おそらくどのような人の環境でも、3つとも違うオブジェクトIDが出力されるでしょう。 通常、Rubyではオブジェクトが作成された時、他のオブジェクトと衝突しないようにユニークなオブジェクトIDが割り振られます。 上記のコードでは、3回 Hoge クラスのオブジェクトの作成をしており、それぞれユニークなオブジェクトIDが割り振られた結果、3回とも違うオブジェクトIDが出力されたという事になっています。

今、私達は object_id でそのオブジェクトが持つオブジェクトIDを知る事が出来る事を理解しました。 では、次のようなコードはどうでしょうか?

puts 20.object_id

一見これはとても奇妙です。 もし、これが奇妙に見えない人がいたら、ニヤニヤしながらこの先に進んでください。 しかし、JavaPHP、他のオブジェクト指向の特性を持つプログラミング言語をやったことのある人であればあるほど、このコードがとても奇妙であるということは共感できるはずです。 そして、Rubyではこのコードは問題なく解釈でき、例えば次のような出力を生むはずです。

41

なんと 20 という数字に対して、 object_id メソッドが使えてしまいました。 Rubyでは全てがオブジェクト という言葉がここで脳裏を反芻します。 もう一度先のコードを見てみましょう。

puts 20.object_id

このコードは何が奇妙なのでしょうか? 先程 object_id メソッドは、オブジェクトのオブジェクトIDを取得できるものという説明をしました。 その「オブジェクト」とは、潜在的に「new メソッドを使ってクラスを実体化したもの」という印象があったはずです。ですが、このコードでは new メソッドはおろか、クラスの定義すら出てきません。

プログラマ、特にRubyをやったことのないプログラマ達にとっては、20という数字は「ただの20という数字」でしかありません。それ以上でもそれ以下でも無く、20という数学的性質を表す分量以外の何者でも無いのです。 しかし、Rubyでは、それに対して直接 object_id を使うことが出来てしまいました。 一体何が起きているのでしょうか?

全てがオブジェクトということ

Rubyのリファレンスマニュアルを参照すると、オブジェクト の欄に以下のような記述が出てきます。

オブジェクトとは Ruby で扱える全ての値はオブジェクトです

これはとても興味深いことです。 この事をあえて、他の言語で擬似的に表現することにしてみましょう。 例えば、

hoge = 42

このようなRubyのコードがあったとして、C++では通常このコードと同じような事をするならば

int hoge = 42;

となるはずです。しかし、Rubyでは 扱える全ての値はオブジェクト ということでした。 つまり、実際にRubyで起こっていること を忠実に再現するならば、C++では以下のようなコードになると言えます。

auto hoge = new Integer(42);

42という数字を代入するために、Integer クラスのオブジェクトを42という数字で初期化し、それを変数に代入しています。 これが意味するのは、プログラム中に記述された

= 42

というものが、C++Rubyで決定的に違うということです。 この事を踏まえると、先程起きた奇妙な出来事について、紐解いていけそうです。 もう一度コードを見てみましょう。

puts 20.object_id

直感的に言えば、これはC++ではこのようなコードになりそうです。

std::cout << 20.object_id();

しかし、このコードは大方の予想通り通りません。20 という数字そのものに対して、 object_id などというメソッドは無いからです。 20はあくまで20という数字リテラルでしかなく、それ以上でもそれ以下でも無いのです。 ですが、 実際にRubyで起っていること をまたここでも忠実に再現してみましょう。

std::cout << (new Integer(20))->object_id();

これなら通りそうです。とどのつまり、Integer クラスのオブジェクトを20で初期化したあと、 object_id というメソッドをそのオブジェクトに対して呼び出し処理をしています。

Rubyでの 全てがオブジェクト という事がここまでで具体的に見えてきました。 実際にRubyのリファレンスマニュアルを見ると、Integer という整数クラスの存在を確認する事が出来ます。 その継承を追ってみると

Integer < Numeric < Comparable < Object < Kernel < Basic Object

となっており、これのうち Object クラスに object_id メソッドが定義されていることが確認できます。 Rubyでは、ただの数字に見えても、実際には Integer クラスのオブジェクトであり、それに付随して実装されているメソッドをダイレクトに呼び出せたという訳です。

これはRubyの大事な性質です。何気なくタイピングしている数字、文字列、それらはRubyの中では全てオブジェクトとして顕在化するということに留意しなければなりません。 しかし、この性質を意識していると、奇妙なコードも徐々に"Rubyでは当たり前"という見方に変わっていくでしょう。

もっと踏み込んでいく 〜Integerクラスの工夫〜

ところで、ここで一つ疑問が生まれます。 全てがオブジェクトなのであれば、

・数値を宣言する毎にオブジェクトが生み出されている ・同じ数字を宣言したとしてもオブジェクトが生み出され、パフォーマンスが落ちるのではないか?

という疑問です。先程、Rubyでの次のようなコードを

hoge = 42

C++で擬似的に表すのに次のようなコードを用いました。

auto hoge = new Integer(42);

このコードのままであれば、 同じ数字を宣言したとしてもオブジェクトが生み出され、パフォーマンスが落ちるのではないか? という疑問に対してはYESと答えるしかありません。 例えば、

hoge = 42
huga = 42
hugi = 42

というのはC++での疑似コードでは

auto hoge = new Integer(42);
auto huga = new Integer(42);
auto hugi = new Integer(42);

と表せるはずですから。

ですが、面白いことに実際にはRubyでは工夫されています。 まず、次のようなコードを見てみましょう。

puts 20.object_id
puts 20.object_id
puts 20.object_id

例えばこのコードは、私の環境では以下のように出力されます。

41
41
41

おや?これは少し興味深そうです。冒頭で私は

通常、Rubyではオブジェクトが作成された時、他のオブジェクトと衝突しないようにユニークなオブジェクトIDが割り振られます。

この様に説明しました。とすると、20というオブジェクトは、一回しか作成されておらず、そして同じオブジェクトIDが出ていることから、あたかもそれを使いまわしているかのような挙動をしています。 そのため、このコードのC++での擬似コード

std::cout << (new Integer(20))->object_id();
std::cout << (new Integer(20))->object_id();
std::cout << (new Integer(20))->object_id();

これは真に正解ではなさそうです。

これの種はとてもシンプルです。 Rubyでは全てのIntegerオブジェクトは、シングルトンパターンによってオブジェクト化されます。 つまり、1度登場した数字オブジェクトは、以降同じオブジェクトを使い回すということになります。 次のようなコードで確認してみましょう。

v = 20
puts v.object_id
puts 20.object_id

例えば私の環境では以下の様に出力されます。

41
41

変数 vに代入されている20という数字オブジェクトの object_id と、 そもそもの20という数字の数字オブジェクトの object_id が一致しました。

20というオブジェクトは、一回しか作成されておらず、そして同じオブジェクトIDが出ていることから、あたかもそれを使いまわしているかのような挙動

RubyのIntegerは、シングルトンによって工夫したデザインをする事によって、この挙動を実現しています。 つまり、同じ数字を宣言したとしてもオブジェクトが生み出され、パフォーマンスが落ちるのではないか?に対してはNoと返答する事が出来そうです。 私達は、知らずIntegerの工夫によって、こういった細かいパフォーマンスを気にすること無く、コーディングが出来ているというわけです。

「=」が持つ 本当の意味

さて、ここまで読み進めた方は、一つの違和感を覚えるでしょう。 この記事のタイトルは「=」について紐解いてみるというタイトルでした。 しかし、実際言及したのはRubyという言語が持つ、全てがオブジェクトという性質についてでした。 冒頭で私は、

「=」がもつ働きは、実はRubyという言語の性質を追求するのに実はもってこいという事

などと述べました。ここまでの言及で実は先に、Rubyという言語の性質については追求できましたが、肝心の「=」がもつ働きについては追求できていません。 いやいや、と思うこともあるでしょう。 変数に値を格納 という表現を、同じく冒頭で私はしています。そしてあるいは多くの人が同じような認識を持っているかもしれません。 「変数に値を格納する事」こそが「=」の働きである。これは本当なのでしょうか?

まず、次のようなコードを見てみます。

a = "hoge"
b = a 

puts a
puts b

このコードの出力は自明です。どちらの putshoge を出力することでしょう。 しかし問題はもっと深い所にあります。 この時、 ab が参照している hoge という文字列は果たしてどうなっているのでしょうか? 「格納」という表現が合っているのならば、a に 「hogeを格納」した後、 b に 「a の内容を(コピーして)格納 」といった表現が出来るはずです。 これは直感的には ab にはそれぞれ別々に値が入っているイメージを作ります。 しかし、実際にはどうでしょう。次のようなコードを試してみます。

a = "hoge"
b = a
puts a.object_id
puts b.object_id
puts "hoge".object_id

このコードの出力は、私の環境では以下のようになります。

60
60
80

まず、文字列 hoge 自体に object_id を行った時、違うidを参照していることから、文字列はIntegerとは違い、同じ文字列でも新たな文字列オブジェクトが生成され、別のユニークなobject_idが割り当てられることがわかります。

では、 abはどうでしょうか?こちらは同じobject_idを指しています。 つまり、abが指している文字列オブジェクトは同じものであるという事なのです。 その事実を確かめてみましょう。

a = "hoge"
b = a
a.upcase!
puts a
puts b

upcase! は、格納されている文字列を大文字に変換するメソッドです。そして、このコードの出力は

HOGE
HOGE

となります。 b には何ら作用していないように見えるのに、 bの出力も大文字になってしまいました。 これは、

abが指している文字列オブジェクトは同じものであるという事

という事実を裏付けているものに他なりません。 そして、これは「格納」という表現が期待するものとは違うように思えてきます。 とすると

b = a

というコードが持つ意味は

  • aにbの内容をコピーして格納する

のではなく、

  • bはaと同じオブジェクトIDを持つようにする

という事に他ならないのです。 もっと、スッキリする言い方にするのであれば、

a = "hoge"

というのは、 - aという変数は、生成された文字列オブジェクト "hoge" を参照する

となり、

b = a

というのは - bという変数は、aの参照しているオブジェクトを参照する(=aと同じオブジェクトを参照する)

という言い方が出来る事になります。 つまり、「=」が持つ本当の働きは、右辺のオブジェクトの参照先を、左辺に”束縛”する という事なのです。 「=」は、変数にオブジェクトを格納するものではなく、ただ束縛する、結びつけるという働きを持っているのです。 これがRubyにおける「=」の本当の意味なのです。

インクリメント演算子が無いわけ

「=」の紐解きが完了しました。 今こそ、motivationで生まれた疑問を解消するときです。 まず、自己代入 += は何故許されているのか。 これはとても単純な問題です。

value += 1

これは、次のコードのシンタックスシュガーでした。

value = value + 1

このコードが意味することは、value が参照している数字オブジェクトに対して、「1を足した数字オブジェクト」を新たに生成し、それを新たに左辺の value に束縛している。 という事になり、これはRubyの性質から見ても筋が通っています。

value = 1
puts value.object_id
value += 1
puts 2.object_id
puts value.object_id

このコードは、私の環境では以下のようになります。

3
5
5

1という数字オブジェクトを保持していた valuevalue+1 によって新たに生成された2という数字オブジェクトを value = によって新たに参照するようになった。 この事を考えると、 2.object_id と2回目の value.object_id が同じ値を出力するのは、当然のことと言えます。

さぁ、かたや ++ はどうでしょう。 この ++ は "変数の値を1加算する" という意味でした。 では、次のようなコードを考えてみます

a = 1
b = a
a++

もしこれが許されればどうでしょうか? まず、aに数字オブジェクト1が束縛されます。そして、 b も、 a と同じオブジェクトを参照します。 その後、++ によって、 a が参照している数値に対して1を加算しようとします。この操作は破壊的です。 そうすると、どういった事が起きるでしょうか。

a = 1
b = a
a++
puts a
puts b

このputsは両方とも2を返してしまうわけです。もっと言うなれば、 1.object_id を束縛していたものは、全て「2」になってしまうわけです。1が2になる世界。意味が分からなくなってきました。

数値はプログラミングにとって欠かせません。このような意味が分からないことが起きないように、Rubyでは数値オブジェクトには、Immutableという「破壊的な変更」を出来ないようなルールが課せられています。

そして、このルールを破らないように、Rubyには ++ が無いというわけなのです。 同じ様に、"変数の値を1減算する" デクリメント演算子もありません。 これで疑問が解消できました。

outro

長い旅路でした。 私達は、Rubyにおける「=」の持つ意味、そしてその本質であるところのRubyでは全てがオブジェクトである、というものの片鱗を見ました。 これで明日から「=」を書く時も、実際に行われている事を思い浮かべながらコーディングが出来そうです。

いかがだったでしょうか? 生まれた些細な疑問は、時として実に深くプログラミング言語の性質を追求することになる訳ですから、プログラミングは面白いものです。 日常的に生まれた疑問は、是非とも疑問のまま終わらせず、深く追求していきたいですね。

Baseconnectでは、Rubyの「=」のようにデータを結ぶ(Musubu)事に興味があるエンジニアを募集しています。

herp.careers

気持ちをちょっと楽にする工数見積もりの技術

はじめに

こんにちは、Baseconnectでエンジニアをしている米丸です。みなさん、工数見積もりって難しくないですか?しんどくないですか?嫌いじゃないですか?

私も以前「見積もり、やだなーこわいなー」と強く思っていた時期があったのですが、工数見積もりが持つ特徴について学ぶことで、ちょっと気持ちが楽になった経験があります。同じように苦手意識を持っている人向けに、「これを知っているだけでも見積もりがしやすくなる」という、Tips的な話をしたいと思います。

なお、この内容はBaseconnect社内で行っているエンジニア共有会(通称:Developer Closer)でのLTをもとに作成したブログになります。社内エンジニアから役に立ったよーという声をいただけたので、ブログとしてまとめてみました。

工数見積もりはなぜしんどいか?

本題に入る前に、工数見積もりはなぜしんどいかを考えてみます。工数見積もりがむずかしい理由はいくつかありますが、エンジニアが工数見積もりをしんどいと感じる理由としては、「早くしなきゃと遅れちゃいけないの心理的ストレッチ」があると考えます。

早くしなきゃと遅れちゃいけないの心理的ストレッチ

一般論としてですが、ソフトウェア開発において機能のリリースは早ければ早いほど価値につながるため、エンジニアは管理者から「できるだけ早く開発してね!」とプレッシャーがかけられます。一方で、期限に遅れてしまうとユーザーや関係部署に迷惑がかかってしまう場合があるため「絶対に遅れないでね!」とも釘をさされます。

この「早くしてね」「遅れないでね」というメッセージングがあるため、工数を短めに見積もりたくなる気持ちと、長めに見積もりたくなる気持ちという相反する想いが同時に生じ、心理的ストレッチがかかってしまいます。

くわえて、工数見積もりは非常に難しく、実装経験さえあればできるというものではありません。心理的ストレッチがある中で見積もりを大きく外してしまい、トラウマになっているエンジニアは少なく無いのではないでしょうか?

個人的には、工数見積もりは技術を要するものだという認識をもっと広めたいです。

気持ちをちょっと楽にする工数見積もりの技術

ここから本題です。工数見積もりに苦手意識を感じていた私が、知るだけでもだいぶやりやすくなった見積もりの技術を紹介します。

1.ぶれ幅の特徴を掴む

工数と確率のベータ分布

工数見積りにはぶれ幅があります。

ソフトウェアの開発期間のばらつきを、縦軸に確率、横軸に時間軸を置いたグラフに表すと、このようなベータ分布になるといわれています。うまくいけば左端の短い期間(楽観値)で開発が終わるが、問題が起きれば右端の期間(悲観値)まで開発は長引いてしまう、という開発期間のぶれ幅を表しています。山になっている最頻値は、もっとも着地する確率の高い期間です。

まずは、見積もりには幅があるものなので、神様でも点での見積もりを正確に出すことはできないのだと、安心してください笑。また、自分が出した工数見積もりが、楽観値、最頻値、悲観値のどれにあたるのか意識するだけでも精度は上がると思いますし、受け取り手にも、それが伝わるようにしてみてください。

工数と確率のベータ分布 - 50%ライン

さらに、このグラフが面白いのは、左右対象ではなく後ろに尾をひく形になっていることです。非対称の理由としては、開発期間の短縮には限界があるが、開発を遅らせる問題には制限がないためと言われています。

グラフに赤線でひいていますが、50%の確率でここまでには開発が終わるという、50%ラインというものが存在します。左右非対称のため、この50%となるラインは、最頻値よりも少し右側にあります。そのため、一見妥当そうに見える最頻値の見積もりを基準にスケジュールを引いてしまうと、確率としては 2回に1回以上、スケジュール遅れになってしまうことになります。必ずしも最頻値の見積もりを出せばよいというわけではないのです。

もう少し言うと、大きなプロジェクトでは、50%ラインをベースにスケジュールを引きつつ、「各タスクの50%ラインと悲観値の差の標準偏差」をバッファとして持つ、といった方法もあったりします。ここでは詳細は言及しませんが、楽観値、最頻値、悲観値から50%ラインを割り出す計算式もあったりします。

2.場合分けする

いつもきれいなベータ分布になるとは限らない

先程のベータ分布のグラフはあくまでモデルであり、実際にはいつもきれいなベータ分布となるとは限りません。 例えば、ライブラリ更新だけで終わると思っていたら、思ったように動かずスクラッチでつくることになった・・・という悲しい経験が個人的にはあるのですが、そのような事例では、うまくいく場合といかない場合とで、開発工数の差は大きく乖離ができ、確率と工数のグラフもフタコブラクダのようになります。このような見積もりを大きく左右する要素が最初から分かっている場合は、それがうまくいく場合とそうでない場合とで、場合分けをして見積もりを立てるのが一つの手だとおもいます。

3.相対的に見積もる

「相対的に見積もる」とは、とあるタスクを基準として2倍/3倍/4倍といった比較値をだしたり、アバウトにS/M/L/XLといったTシャツのサイズでタスクの大きさを表す手法を指します。このとき比較するのは、過去に実施したタスクとの比較、あるいはこれから実装するタスク同士で比較をします。

個人的には、絶対時間の見積もりが必要な場合も、まず相対的な見積りをだして考えるようにしています。なぜ相対的な見積もりを出すかと言うと、まず、絶対時間の見積もりよりも相対的な見積りの方が人間は出しやすいからです。実際に、人は10倍以内のものならうまく見積もれる、という研究結果もあるそうです。あとは、絶対的な見積もりは、実装者のレベルなど諸条件によって変わるところがありますが、タスク間の相対見積もりは比較的影響をうけないので、管理がしやすいことがあげられます。

フィボナッチ数列での見積もり

アジャイルの文脈では、相対見積もりであるストーリーポイントとして、「1, 2, 3, 5, 8, 13, …」というフィボナッチ数列が使われることがあります。ソフトウェア開発は規模が大きくなるにつれて不確実性が増していくため、数が大きくなるにつれて間隔が大きくなっていくフィボナッチ数列は見積もりを表すのに相性がよいと言われています。

個人的な理解では、「うーん、このタスクの見積もりは、10、11、いや11.5かな…」などと細かい数字の違いに気をとられることなく、大きく外さないレベルまでの見積もりを意識せず出せることが、フィボナッチ数列を使うメリットだと理解して、アジャイルな開発ではなくても使える技術だと思っています。

4.理想時間と現実時間を区別する

理想時間と現実時間とは?

  • 理想時間

    • 何者にも邪魔されない、精神と時の部屋のような理想の環境で作業した場合の、開発にかけられる時間
  • 現実時間

    • 予定されていない差し込みのタスクや、切り替えのオーバーヘッドの影響を受けた、実際に開発にかけられる時間

例えば、とあるタスクを3日程度でできそうだと見積もった時に、その3日は8時間フルでタスクにあてた場合の見積もりでしょうか?カレンダーに入っている予定は考慮して差し引いているかもしれませんが、差し込みで入る小タスクや、slackで送られてくる連絡や質問へのリアクション、カフェスペースでの同僚との雑談などにかかる時間は考慮されていないことが多いのかなと思います。特にマルチタスクをしているような場合は、タスクを切り替えるときのスイッチングコストもそれなりに掛かっています。

理想時間と現実時間の活かし方

見積もりを出す時に、理想時間と現実時間を明確に分けずに考えると、失敗しがちです。理想時間での換算なのか、現実時間を考慮したものかを区別する事がまずは大事です。

ただ、現実時間をとらえることは難しいです。計測することも一つの手ですが、細かいタスクの積み重ねやスイッチングコストがどれくらいかかっているかは計測しにくいところです。一つの手として、極力理想時間と現実時間の差分が無くなるように予定を組むのは有効です。ミーティングをまとめたり、複数のタスクを持たないようにしたり、差し込みをシャットアウトする集中時間を設けたり、などです。

5.複数人で見積もる

プランニングポーカーというチームで見積もる技法があります。

  • プランニングポーカーの手順
    • 開発に携わる全員で集まる
    • それぞれで見積もりを考え、数字が書かれたカードを伏せた状態で自分の前に出し、せーので公開する
    • 自分が立てた見積もりの根拠を議論する。ギャップがあればすり合わせる

この方法の良いところは、一人よりも数人で考えた方が正確性が増すことと、開発内容について認識ずれがあった時に見積もり段階ですり合わせができることにあります。

そうはいってもチームで毎回集まるのは難しい場合があります。プランニングポーカーまでは行かなくとも、コードレビューのように見積もりをレビューしてもらう方法は、効果もあり取り入れやすいと思います。

実際にプランニングポーカーをおこなった経験では、人によって出てくる見積もりはおもしろいほど異なり、正解は無いものだと実感することができました。

むすび

工数見積もりと仲良くなるための技術をご紹介しました。どうでしょうか。ちょっとでも工数見積もりへの苦手意識がなくなったら幸いです。

本来、工数見積もりとは不確実な未来に対して道筋を建てるための有効な武器のはずですが、出す側も受け取る側もコミットメント(期限の約束)と捉えてしまうことで、エンジニアを苦しめる足かせになっていることがあります。

正確な見積もりを出すことは不可能だと認めた上で、見積もりの特性を理解し精度を上げ、足かせではなくプランを建てる武器としてうまく使って行きたいものです。

Baseconnectでは不確実性と戦うエンジニアリングマネージャーを絶賛募集しています。興味を持たれた方は、是非カジュアル面談で一度お話しましょう。お気軽にご応募ください。

herp.careers