私はリモート環境でオフィスでの働き方を頑張って再現し、今までのノウハウをはめようとする行動に賛同できません。
いきなりのリモートに慣れず、トランジッションの一環としてオフィスでの振る舞いを再現させることは理解できます。
しかし、リモートでオフィス環境を再現させることが無理で可能な限り再現しようとしたらその努力がペイしないし、リモートワークの特徴を生かす効率化が期待できないと考えています。
例えば、全員が常時ビデオ通話のオンライン状態にキープするアプローチがよく聞きますが、社員のストレスが溜まる一向で、作業に集中しやすいというリモートワークのメリットを抹殺してしまいます。
また、オンライン参加が可能だが集まれる人がオフラインで集まって開催するミーティングがいろんな所でやられていますが、コンテキストが共有しにくく、コミュニケーションがスムーズにならないのでオフラインのデメリットとオンラインのデメリットを顕在化させるだけのプラクティスだと考えています。
リモートでやるなら、リモートの特徴にあわせ、同期性の高いワークフローを排除し、非同期をメインにすべきだと思います。
ミーティングの量を最小限にして、コミュニケーションをテキストベースに持っていくことはわかりやすい実践例でしょう。
ただ、非同期優先というのはやり方の話だけではなく、考え方の話も含まれます。どちらかと言いますと、考え方の方が遥かに大事だと実感しています。
まず、相手のことを常にポジティブに解釈するというマインドセットが大事です。テキストがメインになり、ビデオ通話の場合でも同じ物理空間にいるわけじゃないため、五感を生かしてニュアンスを理解することが無理です。故に、ネガティブな気持ちで説明されたことをポジティブに考えることとポジティブに表現されたことをネガティブに解釈することがありえます。経験上、後者の方がトラブルの元になるケースが圧倒的に多いです。なので、相手の本当の気持ちはともあれ、とりあえずポジティブに解釈することは全員のマインドセットに入れないといけないです。
それに付随し、コンテキストを最小限に収めることに心掛けるべきでしょう。前述のように、コンテキストを伝えることがリモート環境において難しいと言えます。曖昧さを回避し、言うことをはっきり言った方が効率的だと考えます。リアルタイムの対話なら参考情報を文脈の中に差し込むことが難しかったりしますが、テキストだとそんな制限がない為、リンクをどんどん貼り付けても構わないのでそういうリモートこその特徴を生かすべきでしょう。
さらに言うと、リモートになることでアプローチできる人材マーケットは日本に限らなくなるはずなので、全世界から優秀な人材を確保できるメリットが出てきます。異文化間のコミュニケーションにおいて、コンテキストを抑えることが大事なので、そういう意味でもコンテキストを最小限にする必要性があります。
また、アウトプットベースで物事を考えるマインドセットが大切になるでしょう。従業員の生産時間ではなく、成果物で従業員の価値を決めるべきという考え方はだいぶ前から言われ始めましたので、ここで強調する必要がありません。
ただ少し脱線しますが、最近大きな反響を呼んでいるGoogleの給料カットの話についてアウトプットベース思考の角度から分析をしてみたいと思います。この給料カットの話を要約すると、Googleは今までのシリコンバレー水準ではなく従業員が住んでいる場所の生活コストに合わせた給料を支払いたいとのことです。従業員が生活コストが低いところに移すなら、給料が減少するわけです。実際にGoogle以外、TwitterやFacebookのようなIT大手も同じ仕組みを導入しています。なぜこうなるかと言いますと、今までの採用戦略がリモートベースじゃなかったからだと考えます。残念ながら会社は従業員のアウトプットで給料を支払っているわけではなく、あくまでもその従業員を確保するために最も合理的な(大体一番安い)コストを支払っているわけです。すなわち、ローカルマーケットの相場が決定的な要素だと考えても良いでしょう。
しかしよく考えたら、これは昔のプライシングのオペレーションに似ていませんか?メーカーが商品の価格を決める際に、商品の生産コストを計算し、そこから業界平均のマージンを上乗せ、定価を決めるやり方でやっていました。一方で、今のマーケティング界では基本的にはコストでなく、商品の価値から逆算して定価を決めるようになってきています。
話を人材業界に戻しますが、リモートが主流になってきますと、ローカルマーケットの相場に合わせることはナンセンスになりますので、時間がかかりますが、従業員に支払う給料が従業員のアウトプットの価値に左右されるようになるでしょう。
戦略的な話の後、戦術的なことについて考えてみましょう。リモートワークの生産性を担保するのに何が必要でしょうか?大きく分けて物理環境と情報だと思います。
まずは物理環境。リモートじゃなければ、会社が従業員の生産性を向上させる為に、オフィスを整備したりしてきたと思います。同様に、会社が社員のリモートワークの環境整備に支援することはリモート時代において欠かせないです。実際にGitLabやShopify、日本だとSmartHRなどの会社がそうしていると言われています。
物理環境と比べ、情報整備の重要性が気づきにくいものの、非常に大事だと考えます。非同期のワークフローが主流になることで、作業に必要な情報が素早く手に入るかによって、作業効率が大きく変わります。
ケーススタディーを一つ挙げます。GitLabが情報整備の重要性に気づき、「Single source of truth」を掲げ、ハンドブックファーストという考え方を実践してきました。私のようなエンジニアが「Single source of truth」を見ると、すぐデータマネジメントのイメージが浮かんでくると思いますが、角度が違うもののGitLabのハンドブックはまさに「Single source of truth」の思想を体現しています。
GitLabは、ハンドブックを静的サイトとしてメンテナンスし、ソースコードをGitLab上のリポジトリとしてホストしています。従業員みんなが常時その内容を確認・更新し、それを信頼できる唯一の情報源として利用しています。そのため、何かわからないことがあったら、迷わずハンドブックを調べますし、何か聞かれたら直接ハンドブック内のリンクを送ります。ハンドブックがGitLabのワークフローの中核にあると言っても過言ではないと思います。
GitLabのハンドブックを100%再現する必要がないと思いますが、今までのリモート経験から見て、信頼できる唯一のレファレンスをメンテナンスすることはリモートワークにおいて非常に価値のあることだと感じています。利用するツールは不問ですが、下記の特徴を備えているレファレンスがあればリモートワークの質が高まるはずです。
辞書と同じ、調べにくいレファレンスは価値がないと思います。その為、カテゴライズを重要視すべきです。わからないことがあったらどこから辿ればいいかがわかる、書きたいノウハウがあったらどのページに書いたら良いかがわかる状態は理想的でしょう。情報が拡張・アップデートされたら、既存のカテゴリーの合理性にも影響を及ぼすかもしれないから、常時見直すことが大事です。
カテゴライズが大事ですが、分類がいくらうまいと言って、レファレンスの情報量が多くなればなるほど、正攻法で調べることが難しくなります。その時、デジタルレファレンスだからこそできる全文検索が役に立ちます(なぜヤフーの代わりにGoogleが主流になったかを考えれば理解しやすい)。ツールによって全文検索の質がかなり異なりますので気をつけたほうがいいと思います。
ObsidianやRoamの台頭によって再び注目され始めた相互リンク(Bidirectional Link)ですが、レファレンスにとっても非常に重要だと思います。そもそも情報というものがお互いに繋がっている為、その構造を再現することは使い勝手の良さに関係すると考えます。
知識を常にアップデートしないと時代についていけないことはみんなが知っていると思います。レファレンス上の情報が更新されたら、人の頭に記憶されている情報を同期しないとトラブります。GitLabではバンドブックの変更がMerge Request経由で実施されていてその変更が常に通知される仕組みになっています。GitLabやGitHub以外、Notionにもバージョン管理機能とそれをチャットツールへ通知する機能があるので、使用するサービスにあわせ変更に気づかせる仕組みを設けるべきだと思います。
個人の思いも込めていますが、これからのIT業界はリモート主流の時代になるべきで、なってほしいと考えています。それにあわせ、今までの働き方により構築してきたノウハウをしっかりと精査し、活かせるべき経験を活かし、捨てるべき経験をちゃんとUnlearnしなければ、リモートワークの価値が最大限まで利用できないと思っています。
「Embrace remote, embrace async-first work style.」
1 | insert into MOCK_DATA (id, first_name, last_name, email, gender) values (1, 'Therine', 'Cawsey', 'tcawsey0@issuu.com', 'Female'); |
氏名を始め、住所や電話番号、IP アドレス等々、いろんなダミーデータが作成できます。作成できたデータは CSV だけでなく、直接 JSON と SQL 等に書き出せる機能は非常にありがたいです。無料ユーザでしたら、1000 件までしか作れませんが、一時的にテストをする目的であれば十分だと思います。ただ、英語しか対応していなく、日本語のデータ作成ができないことは難点として挙げられます。
好みの問題もありますが、アバタ作成ツールの中、こちらの使い勝手が最も良いと考えています。シンプルでありながら、細かい調整ができ、イメージタグまで自動的に作成されるので、コピペの効率がグッと上がります(笑)。使ったことありませんが、React の専用ライブラリもあるようです。
Identicon と言われたらピンっと来ない方が多いと思いますが、GitHub や SlackOverflow をよく利用している方であればこんなアイコンにはきっと出会ったことがあるはずです。
こちらのサービスはシンプルさをきわめたもので、余計な内容が全く入っていないと言っていいほどでしょう。パラメータが少ないゆえ基本的に GET クエリを弄るだけで話が済みますので、こっちの方が使いやすいと思いますよね。
画像プレースホルダーを作成するサービスがいくつかもあり、正直大した差がないと感じています。こちらの方はテキストとそれのフォントや色まで指定できるから、地味に便利だと思い、利用し続けています。パラメータが直感的なので、迷うことがないと思います。
テキストや画像だけではなく、ダミーレスポンスを入れたい場合がよくあります。そういう時はこちらのサービスを愛用しています。例えば、https://httpstat.us/202
をリクエストすれば、202 Accepted
のレスポンスが戻って来ます。
1 | HTTP/1.1 202 Accepted |
sleep
パラメータがサポートされているので、遅い処理をシミュレーションしたい場合だと非常に役に立ちます。
こちらは最近見つけたサービスですが、痒い所に手が届いた感覚でした。Bot サービスを作成やテストする時、ダミーの Webhook が欲しくなったりします。今まではローカルにダミーサーバーを立てて、ngrok でセットアップするやり方を利用して来ましたが、このサービスを使った方がスムーズだったりします。なので、これからも活用していきたいと考えています。
以上私が気に入ったモック用サービスを 6 つ紹介しました。おまけに、GitHub の Octocat を自作するサイトを合わせて紹介したいと思います。モックのプロフィール画像を作成するのに使えるかもしれませんので、興味ある方はぜひ遊んでみてください。
]]>まず wevox values card とコードネームを軽く紹介したいと思います。
wevox values card はアトラエ社より出しているカードで、計 90 枚あって、それぞれのカードに価値観を表す一つの単語が書かれています。詳しい説明は公式サイトを参照していただければと思います。wevox values card のメリットとしてはコンセプト通り価値観の相互理解へ有益だと思います。書かれている単語は全て価値観なので、カード間の優劣が存在していなく否定することは起こり得ないはずです。一方で、既存ルールのゲーム性が弱く盛り上がらない恐れがあります。さらに、チームワークの要素があんまりないので、チームビルディングの目的が果たしにくい面もあります。
一方、コードネームはチェコのヴラーダ・フヴァチルによって 2015 年に考案され、2016 年のドイツ年間ゲーム大賞も受賞した人気ボードゲームです。ルールがシンプルの上、大人数で遊びやすいとの定評があります。Wikipedia のページがあるので、そちらをご参照頂ければわかりやすいと思います。
思った以上にウケがよかったです。みんな楽しそうに遊んで頂きましたし、フィードバックアンケートの中に「面白かった」・「またやりたい」のような声が多く、「リーダーの意外な一面が知れた」みたいなコメントも頂きました。なので、狙い通りだったと思います。昨今のご時世によりオフラインでの開催が難しくなってきたと思いますが、ちょっと工夫すれば、オンラインで実施することも不可能じゃないと考えていますので、活用していただければと思います。
ちなみに、本日は2021年の元日です。明けましておめでとうございます!
]]>まず具体的なタイムラインを紹介しますと、2018 年春の Term 4 から授業を取り始め、今年の Term 3 にて最後の Capstone を修了し、3 月 31 日に卒業申請を出してから 2 ヶ月強を待ち、6 月 8 日に学位記が届き、晴れて Alum と名乗れるようになりました。
UoPeople は University of the People の略称でアメリカの非営利オンライン大学です。ググれば Wikipedia の記事や内容を紹介するブログが一杯出てくるので、説明は割愛させて頂きます。ただ、入学記が多いものの、卒業後の感想を述べる記事はあんまり見当たらなかったので、自分の振り返りを兼ねて軽く綴りたいと思います。
転職して今の会社に入社した後、ビジネス・企画側とコミュニケーションをする機会が増え、技術畑を歩んできた私が自分の知識不足を実感していました。そしてピンポイントで埋められる感覚じゃなかったので、体系的に学べる MBA プログラムの方が良さそうと感じました。
リアル通学も考えていましたが、会社は相対的に寛容な方だとはいえ、業務時間内で大学に通わせる制度がありませんでした。それに対し、Coursera や AI-Class(Udacity の前身)で MOOC 形式の授業をうけることが普通にありましたので、オンライン通学にあんまり違和感を感じませんでした。更に言うと、会社の中では英語が相対的に得意な方ですので、無駄にしたくなかった気持ちもありました。
当時、オンライン MBA の選択肢がそんなにありませんでした。自分が色々調べた結果、選択肢として上がって来たのはUIUC の iMBA プログラムと UoPeople の MBA のみでした。
大学の知名度及びコースの質的には、当時まだ無名に近い UoPeople が新しく出したプログラムより、UIUC のプログラム(iMBA プログラム自体は新しいけれど)の方が圧倒的に強かったと思います。
一方で、安い方だと言われた学費(2 万ドル)は当時の私にとってやはり高いと感じました。そして、mortar-and-brick 的な大学の MBA を取る方々は基本的にソーシャルコネクションを重要視している方だと思い、オンライン環境だとそれが厳しい上、あくまで授業の内容だけ気にしている私にとって、UoPeople の方がコスパがいいかと考えました。
一言で感想を述べますと、思ったより遥かに大変だったと言わざるを得ません。前述した通り、それまでも何回かオンラインで授業を受けていたので、あんまり変わらないイメージでコースを始めましたが、いきなり多量な Material を読まされ、「読んだ資料を踏まえ、Essay 二本を書いて水曜日まで提出してね」みたいな感覚でした。今振り返ってみたら、初週の宿題は楽勝な方だと言ってもいいですが、当時は「無理や、やめよう」と考えていたくらいでした。
その為、基本的には毎週の週末が潰される感覚で二年間を送ってきました。もちろん、Term と Term の間に 2 週間ほど(場合によって 3 週間もあり得る)空きが出るので、そこは一息を入れるチャンスでした。とはいえ、後述しますが、その時間も無駄にしてはいけないと考えています。なので、理解して下さって、この 2 年間支えてくださった家族と同僚に感謝の気持ちで一杯です。
語学力の問題だと考える方もいらっしゃると思います。もちろん英語力が高ければ読み書きのスピードがある程度上がるはずです。とはいえ、英語力が高くても時間がかかると私が思います。普通に Assignment が多いからです笑。MBA の授業を取ってる方の半数以上(今海外での知名度も上がってきているので比率が下がっているかもしれませんが)はアメリカ人です。Yammer(学生同士のコミュニケーションツール)で検索すればわかりますが、思ったより大変だとの投稿がちらほらあります。
かといって、慣れればなんとかなります。自分は 5 つ目の授業までは毎 Term 1 コースしか取っていませんでしたが、5 つ目のところから若干力の余裕を感じ始めました。最初から二年間で修了すると決めていたから、授業を同時に 2 つ取ってみようとチャンレンジしました。同様に初期は全然慣れなくて、宿題を書くために有給をガッツリ使う羽目になってましたが、最終的に有給を取らなくてもやり過ごせるようになっていました。運動と同じく、トレーニングが必要だったと考えています。
当初の期待を裏切ることなく、有益な二年間だと私が評価しています。
NYU Stern の教授が作成に関わったからか、コース全体の設計が合理的だったと思います。財務・マーケティング・組織論・ビジネス戦略など、知識を全面的に学び、段階的に進むことができ、知らなかった知識をたくさん身に付けることができました。使っているケーススタディは基本的に Harvard Business Review や定番教科書から取ってきた代表的なやつでオフラインの MBA で学習する内容と大体一緒じゃないかと考えます。逆に、オンラインである故に、アメリカの話はもちろん、ヨーロッパや東南アジア・アフリカのことを他の生徒の Essay から知ることができ、視野を広げるいいきっかけだったと感じます。
もちろん、コミュニケーションが取りにくいなどオンライン授業としてのデメリットが諸に出ていましたし、宿題の Peer Review を含む授業システムに賛否両論なところもありました。特に入学が簡単だったから、最初の授業で適当に宿題を書いて提出する学生がそれなりにいました。ただ、後半になればなるほど、Discussion と Written Assignment の質が高くなる傾向でした。乱暴に単語数で例をあげますが、最初の授業の Discussion 投稿はせいぜい 200~500 単語くらいでしたが、最後の授業では 1000 単語を下回る投稿が見当たらなく、逆に 2000 以上が散見されていました。
その他、Instructor と Program Advisor が無責任で生徒が放置されているとの意見も見たことありますが、自分は出会ったことがなく、指摘と質問をそのたびに適切に対処してくれていました。
UoPeople の学生は毎 Term 最大で 3 授業をとることができます(Degree Seeking Student になる前もしくは GPA が足りない時は 1 か 2 になります)。自分は毎回最大限までとっていました。もちろん 1 Term で 3 つのコースを学習する余裕がなかったし、やったこともありませんでした。しかし、第一週目で Learning Guide を読み、学生リストを見ればその授業の雰囲気を推測することができます。そこで自分の仕事状況等に合わせて今 Term 取りたいコースを決めて他のコースを Drop することは私の常套手段でした。コース申込時得られる情報と比べ、第一週目で得られる情報量が圧倒的に多いので、判断を毎 Term の始まりに先延ばした方が良いと思います。しかも第一週目の時 Drop する場合、何の記録も残ることなく、費用もかからないので損することが一切ないと思います。
前述した通り私は Learning Guide を読んでから授業を落としたりしました。その場合、読んだ Learning Guide を一旦保存していました。MBA プログラムのコースの選択範囲がそんなに広くなく、「今 Term は取りたくないものの、いずれ取らなければいけない」ケースがよくありました。その場合、Term と Term の間になったら、Learning Guide を読んで予習することができます。さらに余裕があれば、宿題を書き始めることもできます。そうすれば、Term に入ってからのワークロードが減るので、ストレスなくコースをうけることができます。
また、Learning Guide のみならず、自分が提出した Assignment(Written だけじゃなく Discussion や Group Work も)を全部保存すべきかと思います。それらは後続のコース、特に BUS 5117(Strategic Decision Making and Management)と BUS 5910(Management Capstone)の授業で再び閲覧したくなるかもしれません(自分はしました)。
やはりオンライン通学は孤独な旅に例えられます。私は家族と上司の理解を得られて(ありがたいことにMBA 申請用の推薦状も当時の上司に書いて頂きました)、相対的に良い方だと思いますが、それでもしばしば辛く感じていました。人間は社会的動物なので、周りと違う行動を継続的に行うと、違和感を感じたり孤独を感じたりすることは当たり前だと私が思います。その場合、Yammer で投稿を読んだりコミュニケーションを取ることで、自分と同じ道を歩んでいる方と交流でき、その孤独感がかなり解消できます。
その上、Yammer や Reddit の投稿から知らなかった情報を入手することもできます。例えば、MBA の学生は Degree Seeking Student になった後、GPA が一度リセットされる情報は Yammer の投稿から初めて知りました。そういう情報を入手することで、もっとスムーズに計画を行うことができると思います。
Term の Gap を指しているのは、何の授業も取らない Term を挟むことです。今のルールが変わったかわかりませんが、 Term を 3 つまで Gap しても休学扱いにならないルールだったと思います。仕事の激務もあって、自分は一時期あんまりにもしんどくて、宿題をやるたびに吐きそうな感覚だったので、その次の Term を Gap することを決意しました。決断に当たって結構迷いがあって、進捗がさらに遅れる罪悪感と「このままやめてしまうんじゃないか」との心配がありました。でも結果的に元気になってまた気持ちよく学べるようになりました。なので、本当に辛くなったら、無理せず Gap したり、コースを Withdraw したりして、一休みを設けた方が効率的かもしれません。UoPeople のカレンダーに Term が 5 つもあるので、1 Term が空いてもそこまで問題にならないと思います。
2020 年 12 月現在、UoPeople はまだ National Accreditation の大学(Regional Accreditation 申請中)で知名度と評判も発展途上です。とはいえ、周囲の環境に左右されず、自ら目標を定め、純粋に知識を学習したい方にとっては有効な選択肢じゃないかと私が思います。まとまってない文章ですが、今後 UoPeople に通いたい方、今通っている方の参考になれれば幸いです。
]]>まずHTMLファイルに見えない要素を一つ置いておきます。
1 | <p class="invisible-element"></p> |
そしてurl
関数を活用し、こういうCSSを書けばOKです。
1 | @media screen and (orientation: portrait) { |
ユーザのデバイスが縦長の向きになっている場合、orientation=portrait
のGETリクエストがhttps://example.org/analytics
に飛ばされるので、そちらのエンドポイントで集計することが可能になります。メディアクエリで使えばメディア特性なら通用するので、ビューポートの向き以外、スクリーンのサイズやresolutionを検知することも可能です。例えば、@media screen and (min-resolution: 2dppx)
を使えば、ユーザのデバイスがRetinaかどうかは判断できるようになります。
メディアクエリのケースと似ていますが、下記のようなコードを使えば、ユーザが相対的に新しいブラウザを使っているか分かるようになります。
1 | @supports (display: inline-grid) { |
ユーザが使っているブラウザとバージョンによってCSS特性のサポートができていない、もしくは廃止されているかもしれません。よって、条件を組み合わせば、ユーザが使っているブラウザのバージョンまで特定できるでしょう。
下記のような2つのリンクがあるとします。
1 | <a id="product-1" href="https://example.com/product-1">Buy Product 1</a> |
ユーザが実際にクリックした回数だけではなく、興味を示してくれた回数を統計したい要望があり得ます。以下のようなCSSコードを使えば、カーソルが載せた(Hoverの)回数を集計できるようになります。
1 | #product-1:hover::before { |
擬似要素を使っている為、別のDOM要素を設ける必要すらありません。もちろん、:hover
以外に、:focus
や:active
に適用することが可能です。さらに、var()
関数を活用すれば、ユーザを特定するところまで行えます。
やり方は簡単です。まず下記のようにHTMLをレンダリングする側がユーザIDが含まれているURLを作成し、該当要素のstyle属性に埋め込みます。
1 | <a id="product-1" href="https://example.com/product-1" style="--analytics-hover-product-1: url('https://example.org/analytics?user-id=10000&hover-product=1');">Buy Product 1</a> |
それからCSS側で変数として使うだけで完成です。
1 | #product-1:hover::before { |
HTMLとCSSのみで実現でき、JavaScriptを一切介していないので、スクリプトをブロックしても集計ができてしまいます。しかし、流石にここまでやると、わざわざスクリプトをブロックしたユーザに怒られそうですね笑。
使っている技術が別に目新しくなく、実装自体もシンプルだと思います。どちらかと言いますと、こういうアイデアがなかなか思い付かないんじゃないかと思います。ただ、泥臭い部分がどうしてもありますので、Google Analyticsが使えればGoogle Analyticsを使いますけどね。
]]>正確なストーリーポイントが出せていないというのはあくまで事象で、そもそも土台に当たるバックログアイテムが見積もりにくい状態になっていることはよくあります。バックログアイテムの完成度を判断するのによく使用されているのは「I.N.V.E.S.T」という基準です。要するに、取り扱いやすいアイテムは以下の基準を満たさなければいけません。
ネット上「I.N.V.E.S.T」を説明する良い記事がたくさん存在している為、細かい説明は割愛しますが、ここで Independant と Estimatable に注目したいです。例えば、「上のアイテムを完成しないと、下のアイテムが見積もれないよ」という言い方はよく聞きます。それは恐らく独立性の不足を表している証拠です。上手く切ったユーザーストーリーなら、お互いに干渉しないし、不確実性の急増がしません。
また、Estimatable も大事です。ストーリーポイントを出す前、開発チームが見積もる対象のスコープと情報を理解していなければ、正しい見積もりが出せません。もしバックログアイテムが見積もりにくいと感じたら、リファインメントもしくはプランニング 1 のタイミングで開発チームと PO が擦り合わせを行うべきです。
ストーリーポイントを効率よく出す為に、ストーリーポイントの定義をしっかり理解するという前提条件があります。ストーリーポイントと時間の関係性を説明する記事が世の中にいっぱい出回っていますが、理論上ストーリーポイントと時間は換算できないし、すべきではありません。ストーリーポイントはチームの努力を相対的に表している単位で、投資しなければいけない時間はあくまで参考基準の一つに過ぎません。技術的なチャンレンジが含まれているユーザーストーリーは当然ながら大きな努力を要しますので、時間だけで考えるとストーリーポイントの趣旨と異なってしまいます。
正確には、ストーリーポイントを通して見たいことはバックログアイテム間の差です。仮にアイテム A のストーリーポイントは 1 で、アイテム B のストーリーポイントは 5 でしたら、アイテム B を消化させる為に 5 倍ほどの努力をしなければいけないことを意味します。PO はそれを重要な参考材料とした上でアイテム間の順位を決めることになります。さらに、もし前スプリントにおいてアイテム B を消化できたものの今スプリントでは5ポイント消化できなかったら、それを振り返り時の客観的材料として利用することができます。ストーリーポイントの定義を正しく理解していなければ、当然ながら見積もりが上手くいかないはずです。
上記の前提条件をクリアしたとしても見積もりが上手くいかなかったりします。議論の内容を聞けばわかるはずですが、ストーリーポイントを出す時考慮した要素が異なるケースは度々現れます。例えば、A さんが仕様の複雑性を配慮し 3 を出したが、B さんが技術上実現しやすいという考えで 1 を出したりします。無論話し合いにより抜けた観点が補完され、最終的に目線が合わせられますが、効率性を考えれば、最初から合わせた方が有益でしょう。すなわち、ストーリーポイントを見積もる際に考慮したい軸を事前に共有しあい、みんながそれに従って考えることで観点の抜け漏れを未然に防ぐイメージです。ここで二つの考え方を紹介したいと思います。
一つ目はMike Cohn 先生の定義に従った考え方です。ストーリーポイントを見積もる時、以下の三要素から考えるイメージです。
この考え方の実用例として Michael Lant さんの Effort Matrix があります。上記の 3 つめの要素、不確実性を複雑度へマージしたマトリクスです。
この手法に関するモチベーション、やり方およびフィードバックは Michael Lant さんのブログにて詳しく紹介されていますので、詳細はそちらをご参照ください。
もう一つはStacey matrixの思想を借りて、仕様難易度(Requirements)と実装難易度(Methods)二軸で考えることです。下記の図は実用化されているメトリクスを表しています。縦軸は仕様難易度で横軸は実装難易度です。緑は「セーフティゾーン(Safety Zone)」、黄色は「ハザードゾーン(Hazard Zone)」で、赤は「デンジャーゾーン(Danger Zone)」です。
仕様難易度が 2、実装難易度が 3 でしたら、ストーリーポイントが 5 点になり、ハザードゾーンに入ります。すなわち、許容できるものの、できれば避けたい粒度です。もしストーリーポイントの結果がデンジャーゾーンに落ちたら、アイテムの分割が上手くいっていないと考えられます。運用している際に、仕様難易度はちょうどユーザーストーリーの What 部分に当たり、実装難易度は How の部分に当たるため、イメージしやすいと評価されていました。
上記の内容はあくまで経験則に過ぎないので、全てのチームに合致しているとは考えにくいです。例えば、成熟しているチームなら、わざわざ二軸に分けてストーリーポイントを考えることはむしろ過剰だと思います。結局のところ、自分のチームへ最も相応しい方法を見つけ出すことは一番大事ですよね。
執筆時の最新パージョンである ECMAScript 2019(略称:ES2019/ES10)の中に、データ型は全部で 7 種類です。そのうちプリミティブ型は下記の 6 つです。
C 言語のように、integer や double を区別することがなく、数字系なら Number 型一択です。ただ、Number 型に特殊な値が 3 つ存在します:+Infinity
(無限大)、-Infinity
(無限小)およびNaN
(Not a number/数字ではない)です。
Java でしたら、String はオブジェクトになりますが、JavaScript に String というプリミティブ型が存在します。意味は同じく文字列を表現する型です。
Boolean、Null と Undefined の値は限られています。Boolean の値は true と false 二つのみです。Null の値はnullだけで、Undefined の値はundefinedのみです。
Symbol は ES2015(通称:ES6)にて新しく追加されたデータ型です。MDN を含む、Symbol は ES2016 で追加されたとの記述が散見されますが、それは間違い(タイポ?)です。
また、2019 年 6 月に開催された TC39 ミーティングにて、BigInt という新しいプリミティブ型はStage 4 へ進むことが許可されました。よって、ES2020 の言語仕様へ組まれることがほぼ決定になっています。BigInt
は文字通り、今までの Number 型だと取り扱えない大きな数字を処理する為に導入される型です。一部の資料では ES2019 の新機能だと説明されていますが、残念ながらそれは間違いです。確かに一時期 ES2019 へ入れられるんじゃないかと見られていましたが、まだ仕様へ入っていません。ただ、ChromeやFirefox、Nodejs10.8+ではすでに実装されていますので、JavaScript のプリミティブ型としてカウントされていることもあります。
7 種類の中、唯一プリミティブじゃない型は Object 型ですが、恐らく普段のコーディングの中でもっともよく使われている型でしょう。{ age: 1 }
のように作ったのは当然ながら Object ですし、クラス(class)、関数(function)、配列、Date や正規表現はすべて Object です。この記事のスコープはプリミティブ型に絞っていますので、解説は割愛します。
JavaScript において、プリミティブ型は値のみでプロパティがありません。なので、プリミティブ型の変数を識別する時は値を利用するしかありません。値を変えたら別のプリミティブ値になりますので、プリミティブ値は不変(Immutable)なんです。一方、オブジェクトはプロパティを持ち可変(Mutable)です。オブジェクトを識別する際に参照(Reference)を利用します。その為、同じ値を持っているとしても別々のオブジェクトとして識別されるかもしれません。
1 | 1 === 1 // => true |
逆に、値を変えても同じオブジェクトになり得ます。下記のコードは好例です。
1 | let b = a = { i: 1 }; |
上記の記述だけ読むと、JavaScript の型システムは他のオブジェクト指向言語とは大した差がないように感じるかもしれません。しかし、なぜ数多くのプログラマーが JavaScript の型に惑わされ・苦しめられてきたのでしょうか?主な理由は以下の三つだと思います。
JavaScript は多くのスクリプト言語と同じく、動的型付け(Dynamic Typed)という特徴を持っています。その為、型はコンパイル時ではなく、実行時動的にチェックされます。さらに、型に関するチェックが緩く、PHP と一緒に loosely typed
または weakly typed
(弱い型付け)と呼ばれています。Pythonや Ruby が loosely typed と説明している記事もありますが、恐らく動的型付けと混同していたからです。動的型付けと弱い型付け、この二つの特徴により、下記の JS コードは問題なく動作します。
1 | a = "hello"; |
Python や Java の場合、例外が発生するはずですが、JavaScript では型は自動的に判断され、暗黙的に変換されます(implicit coercion)。同じ変数へ別の型の値を代入しても上書きが発生するだけで Runtime に怒られません。そのゆえ、プログラマーにとって型を把握することが難しいです。
1 | false + null == '0' // => true |
例えば、上記の式の評価結果は Boolean 型の true になります。強い型の言語に馴染んでいる方にとってあり得ないでしょう。なぜなら、Boolean 型の値と Null 型の値の和は String 型の値に等しいと示されているからです。
また、プリミティブ型とオブジェクト型の相互変換もあります。先程プリミティブ型はプロパティを持っていないと説明していましたが、下記のコードはエラーになりません。
1 | "hello world".length // => 11 |
なぜなら、String 型の"hello world"
はToObjectという抽象オペレーション(Abstract Operation)により、暗黙的に String ラッパーオブジェクト(Wrapper Object)へ変換され、その後ラッパーオブジェクトの length プロパティへアクセスしに行くわけです。要するに、上記のコードは以下のように解釈されます。
1 | Object("hello world").length // => 11 |
実際に、loosely typed という特徴は非常にパワフルではありますが、上記のようなデメリットが JavaScript のコミュニティにおいても問題視されてきました。それを改善すべく、jshintやeslintのルールで暗黙的な型変換がない ===
が推奨されたり、強い型付けの特徴を持つ TypeScript が流行るようになりました。
その為、ES2015 以降追加された Symbol とこれから追加される予定の BigInt もこのような暗黙的な変換に対し消極的振る舞いを見せています。例えば、1 + 1n
という Number(1
) と BigInt(1n
) 型の足し算すら TypeError になり、Cannot mix BigInt and other types, use explicit conversions
(BigInt 型は別の型と混合することができません。明示的な型変換を使ってください)とのメッセージが表示されます。
typeof
JavaScript では、型を実行中に確認したい場合、typeof
という演算子(operator)を使います。typeof 'hello world'
は string になり、 typeof false
は boolean になります。
しかし、この演算子はいくつか直感に反するところがあります。
まず一番よく指摘されているのは null 問題です。上記のルールに従えば、 typeof null
は当然ながら null になるはずですが、その結果はなんと object になります。いろんな解釈が提唱されていましたが、結局バグ説に落ち着きました。しかもこのバグの存在を前提とした実装がすでに世の中に広がっている為、今後も修正されない予定です。
また、上でも言及していましたが、JavaScript 内のプリミティブ型は全部で 6 つでそれ以外はすべて Object です。よって、typeof
の結果は 7 つしかないと考えられるはずです。しかし、8 つ目、function という結果も存在します。プリミティブ型とオブジェクト型以外に、Function 型が存在するように見えそうですが、そんなことはありません。この振る舞いはただの負債です。ECMAScript の仕様によると、[[Call]]
という内部メソッド(internal method)を持つオブジェクトに限って、評価結果はfunctionになると定義されています。
さらに、各ベンダーの実装違いが火に油を注いでいました。例えば、Chrome の早期バージョンにおいては、正規表現(typeof /s/
)はfunctionになっていたり、IE にunknown と dateが存在していたりしていました。幸い、近年 ECMA-262 の台頭により、このような差分が無くなりつつであります。
NaN
上で紹介されている通り、NaN
は Not a Number(数字ではない)という意味を表している Number 型の値です。しかし、他の Number 型の値と違い、比較・計算することは不可能です。
1 | NaN > 0 // => false |
上記のように比較はすべて false という結果になり、計算結果は全部 NaN です。さらに、多くの JS プログラマーを困惑させたのは、NaN は自分自身とも等しくないことです。すなわち、NaN === NaN
の結果は false です。
実はこれは驚くべきことではありません。ECMA-262 の仕様に記述されている通り、NaN は IEEE 754-2008 標準の Not-a-Number を表していますので、上記の振る舞いはあくまで IEEE の標準を忠実に表現しているだけです。とはいえ、プログラムの中で値は NaN であるかをチェックしなければいけないユースケースはきっと存在するはずです。自分自身と等しくない値は NaN しかないので、ECMAScript5(ES5)まではその特徴を利用するプラクティスが多かったです。この方法は今でも使えますが、黙示的な表現に見えますので、ECMAScript 2015(ES6)の仕様にNumber.isNaN
という専用メソッドが追加されました。値がNaN
の時だけ true になり、それ以外は false を返してくれます。しかし、惑わしさはまだ残っています。なぜなら、グローバルオブジェクト(Global Object)にもisNaN
というメソッドが存在します。両者の関係は下図のようです。
1 | Global Object |
その上、isNaN
はまず引数を暗黙的に Number 型へ変換してから NaN であるかを判断する為、Number.isNaN
の振る舞いと異なります。
1 | isNaN("hello") // => true |
上記の理由によって、NaN
を取り扱う時の惑わしさを減らす為に、グローバルオブジェクトのisNaN
の使用は基本的に推奨されていません。
JavaScript の型チェックが緩く、暗黙的な変換が多い為、型を意識しなくてもコードを触ることができたりします。しかし、型をはっきり認識していなければ、予期に反する実行結果が出る可能性が高いです。特に歴史的な原因などによって、JavaScript の型システムが直感と相反するケースが多いです。そのため、言語仕様をしっかりと理解することが大切だと感じますね。
]]>1 | function calcDuration(date: string) { |
この関数のテストを書くのに、注意しなければいけないことが1点あります。もう気づいたと思いますが、new Date()
の結果を固定値にしないと、テストの結果が実行時の日付に依存してしまいますので、環境依存が発生し許容できないでしょう。Jestにはモジュールモックを含め、いくつのやり方がありますが、標準ビルトインオブジェクトかつコンストラクタの場合、どうモックするか、ドキュメントには詳しく書かれませんでした。ウェブで検索してみた結果、jest.spyOn(global, 'Date')
という感じで書くのは一番直感的で柔軟性が高いと感じました。よって、テストのコードはこうなるかと思います。
1 | test("mock", () => { |
しかし、上記のコードの実行結果は[Jest] Expected value to be (using Object.is): 4, Received: 0
というエラーになるでしょう。理由は簡単です。calcDuration
関数内でDate
コンストラクタは二回も呼ばれ、そして常にdateToUse
が返ってきます。よって、差分が0になるのは正しい振る舞いです。そこを修正する為に、mockImplementation
にて条件分岐を作ればシンプルに解決できます。引数が空の場合dateToUse
を返し、それ以外は元々のビルドインオブジェクトを使わせます。結果は下記の感じです。
1 | test("mock", () => { |
他にmockImplementationOnce
でやる方法も考えられますが、実装を意識しないと書けなさそうと思いましたので、上記のやり方を採用することにしました。
なんちゃらケース系の用語が多すぎるからまとめてみました。以上
]]>最初に参加したセッションはヤフーの森さんによる「Kotlinもう一歩」でした。Kotlinのタイプシステムを用いてGenericsとType Projectionを紹介していました。プレゼンの内容をよくまとめたのは下記の一枚です。
タイプシステムの観点で不変・共変・反変を説明してくれてとても印象的だと感じました。タイプシステムを一言で説明すれば、「タイプAが期待される全ての箇所で、タイプBが使用可能であれば、タイプBはサブタイプだと言える。逆に、タイプAはスーパータイプである。」になります。この一言さえ分かれば、「タイプAは自分自身のサブタイプであり、スーパータイプでもある」ということは理解できるでしょう(ちなみに、これはisSubtypeOf
とisSupertypeOf
で検証できる)。また、「String
はString?
のサブタイプ」というのもわかるはずです。
実例を交えた説明でわかりやすかったです。特に技術選定時に考えたこととハマったポイントの紹介はとても参考になりました。テストコードを書きやすくする為に作られたFactlinとkotlin-fill-classは確かに使いやすそうだなと感じました。
全体のイメージとして、Kotlin製のテストライブラリはまだまだ少ないと感じました。ただ、Javaの資産は基本的にそのまま使えるので、それほど困らないです。クラスがFinal、Immutable、Null許容しないなどの特性によってハックしなければいけないケースがあるものの、その場合はOSSで提供されているラッパー(i.e. mockito-kotlin)かプラグイン(i.e. all-open)、もしくはコード上の設定(i.e. @JvmStatic
)を使えば大体回避できます。
KotlinのLinterについて、一番主流のktlintしか知りませんでした。実際にktlint以外、detekt(detectの意味?w)とアンドロイド用のandroid-lintが存在します。その比較は下記のスライドにてまとめられています。
ktlintと比べてdetektは標準ルールがたくさんありますし、個別の設定ができるし、メッセージ表示も詳しいです。さらに、detekt-formattingを使えばktlintのルールすらサポートできてしまいます(formattingと言いつつも、自動フォーマットと無関係)。整形できないのは状況によって痛いかもしれませんが、選択肢として考えられますね。
また、ktlintのカスタムルールの作成方法は以下のようになります。
detektの場合、issue(サマリー情報)を作成する必要があります。さらに、visitのパターンは複数存在していて選ぶ必要があります。それ以外は大体一緒です。
こちらはFRESHLiveの実例紹介でした。マイクロサービスを採用した理由は以下の2点らしい。
FRESHLiveはほとんどGolangで書かれていましたが、APIをGoでいっぱい書いてくのは辛い(筋肉痛)らしく、gRPCサポート・高階関数/拡張関数/データクラスがある・Null安全のJavaが欲しくなったわけです。よって、Kotlinを採用し始めました。Router Function DSLでRESTful APIを書いているのは印象的でした。マイクロサービスっぽい話は少ししか触れませんでした。パッケージングはhelmを使っていて、prometheus/JVM micrometerを使って監視を行っているそうです。
磯貝さんによるコントリビュートしかたの紹介です。Kotlinのリポジトリをクローンすることや、IDEとプラグインを用意することは特に目新しくないですが、JDK10だけじゃなく1.7、1.6まで必要だそうで、大変だなと思いました。
公式youtrackからup-for-grabsタグがついているIssueをとって作業し、GitHub上でPRを出すのは一般的なワークフローみたいです。わからないことがあったら、Slackの#kontributors
チャンネルで聞けば良いらしいです。
基本的には以下のステップを踏んでコントリビュートすれば挫折しなさそうです。
Kotlinのみならず、他のOSSでも適用できそうな手順ですね。
このセッションは残念ながら聞けませんでしたが、とても気になっていたので、スライドをチェックしてみました。
紹介編かなと思っていましたが思ったより深く触れていたようです。ただ、KotlinConf 2017のプレゼン(Introduction to Coroutines by Roman Elizarov、Deep Dive into Coroutines on JVM by Roman Elizarov)と被ってそうな内容だと感じました。
各スポンサーのノベルティはすごく豪華でした。サーバーエージェントさんとミクシィさんがKotlinのクイズゲームを企画していたのは新鮮で面白かったです。特にミクシィさんから頂いたプランニングポーカーは今後使えそうですね(ここより抜粋した問題らしい)。
食事が美味しかった上、お菓子、デザート、飲み物まで無料で提供されていたのは最高でした。
唯一の不満で言えば、Wifiですかね。こういう大規模なイベントでWifiの接続が不安定になったり、繋がりにくくなったりしますよね。施設より提供しているWifiと別に、イベントの為に用意する必要がありそうと思いました。
今年のKotlinConfはアムステルダムで行うようでチェックしてみましたが、チケットはすでに完売になったみたいです。来年の今、実務でKotlinを使えて、KotlinConfに参加できたらいいなと思いましたね。
1 | const selectAll = el => condition => [el, |
実は、主な作業は2つだけです。
コードを書けばこうなります。(allElements=全ての要素、condition=条件)
1 | const selectAll = (allElements, condition) => allElements.filter(condition) |
関数の再利用を考えたら、コンポジションを採用した方がいいでしょう。
1 | const selectAll = allElements => condition => allElements.filter(condition) |
1つ目の作業はどう実現するかは問題です。DOMはツリー構造なので、親要素の元に要素があり、その要素の中に、さらに子要素が存在する。ツリー構造のケースに対処する場合、再帰を使用することは一般的です。関数型風に書きたかったので、Array.prototye.mapとスプレッド構文を利用することにしました。DOM要素のchildrenはHTMLCollection型で、map関数が備えていません。よって、まずArray.from()
で配列に変換する必要があります。
1 | const selectAll = el => condition => [el, ...Array.from(el.children).map(child => selectAll(child)(condition))] |
しかし、上記のコードだと関数selectAll
の戻り値は配列になります。そのまま再帰してしまうと、配列内に配列が格納されることになります。reduceを使って、flatten効果を果たさせます。
1 | const selectAll = el => condition => [el, |
こんな考え方で、今回のコードを完成させました。
1 | // 全てのdiv要素を抽出する |
やはりJSで関数型プログラミングするのは気持ちいいw
]]>ひとことコメント:ベストセーラー「The Lean Startup」の作者の新作です。
ひとことコメント:(プログラミングではなく)プロダクト開発でよく使われているフレームワークをまとめてくれたサイトです。
ひとことコメント:Intercomから出したスタートアップに関する書籍で、GoodReadsで4.2の高評価をマークしています。
ひとことコメント:画力がなくてもイラストが描けるようになります。
ひとことコメント:あのY Combinatorより出したMOOCです。
ひとことコメント:チートシートが多量に網羅されている便利サイトです。
ひとことコメント:Font Awesome以外の選択肢。
ひとことコメント:有名なテクノロジー企業はどうやって最初のユーザーを獲得できたか、を教えてくれます。
ひとことコメント:因果関係が一目瞭然に。
ひとことコメント:ちょっとした重複作業から解放してくれる、Seleniumより入りやすい自動化ツールです。
ひとことコメント:Wappalyzerより精度が高く、感知内容が多いようです。
ひとことコメント:世界各地の生活コストと注意事項をわかりやすく紹介してくれるサイトです。
]]>JSのオブジェクトを扱う際に、あるプロパティが存在するかをチェックする作業はしばしば発生します。実は、この一見簡単そうな作業を実施する為に、いくつかのやり方が存在します。考え方で分けて見れば、以下の2種類になるかと思います。
よく見かける手法はundefined !== obj[prop]
もしくはundefined !== obj.prop
でしょう(obj
= オブジェクト、prop
= プロパティ)。まずオブジェクトからプロパティを出して値をみます。もしオブジェクトに該当プロパティが存在しなければ、値はundefined
になりますので、この特性を利用する手法ですね。
この手法の異種として、void(0) !== obj[prop]
があります。void演算子が分かればすぐ理解できるでしょう。void(0)
の戻り値がundefined
になるからですね。
また、'undefined' !== typeof obj[prop]
という書き方もあります。値の型を取得し、undefined
型であるかを判別する考えです。
更に、もっとお洒落な書き方も存在します。!!obj[prop]
です。下記のように書くことが可能になります。
1 | if(!!obj.prop) return 'OK'; |
エクスクラメーションマークを2つ先頭に追加することで、結果の反転を二回行います。「False→True→False」みたいに、元の結果に戻すやり方ですね。
undefined !== obj[prop]
がもっと直感的なのに、なぜわざわざvoid(0) !== obj[prop]
や'undefined' !== typeof obj[prop]
を使うか、疑問を感じたので、ベンチマックを行ってみました。実は後二者の効率は前者の3倍程になります。なるほど〜
あともう一つ気を付けなければいけないのは元々プロパティの値はundefined
になっているケースです。上記の手法を使ってしまったら、該当プロパティが存在していても、false
が出てしまいます。
特に!!obj[prop]
を使用する時、もっと注意を払わなければなりません。0
やnull
の反転結果はtrue
になるから、該当プロパティに入る値の型を100%把握していなければ、使用するのをやめておいた方が安全でしょう。
抽出を行わず、直接チェックする方法もあります。in
もしくはhasOwnProperty
の使用です。
1 | // in |
両者の一番の違いはプロトタイプチェーンを辿るかどうかです。in
の場合、プロトタイプチェーンを遡って親のプロパティまで確認する一方、hasOwnProperty
は名前通り、自分自身のプロパティを見るだけです。どっちを使用するかはケースバイケースかもしれませんが、「辿らなくてもいい」・「辿らないでほしい」ケースが多い印象です。
もう一つ細かい違いは実装されたバージョンです。in
はJavaScript 1.4から使用可能になりましたが、hasOwnProperty
は一歩遅れて、1.5になります。ただし、ES5さえ古いと感じている今、ES3時代の違いは流石に考えなくても良いでしょう。
また、underscore.jsのソースコードを読んだことがある方でしたら気づいてたかもしれませんが、hasOwnProperty
の使い方は下記のようになります。
1 | // underscore.jsのソースコードより |
わざわざcall
を使っていますね。理由は簡単です。var obj = {}
の書き方で生成されたオブジェクトでしたら、Object
のメソッドをそのまま継承していますが、var obj = Object.create(null)
みたいに生成すると、そんな継承が発生しません。当然ながら、hasOwnProperty
というメソッドを持ちません。こんなケースに対応する為に、call
を使ったわけです。
じゃ、結局何を使えばいいでしょうか。ケースバイケースのような定番フレーズを避けたいので、できればhasOwnProperty
の利用を推奨します。 理由は三つ。
以上です。
orElse
とorElseGet
の区別は社内でちょっと話題になっていました。これらは同じように見えますが、使い方に気をつけなければハマってしまうかもしれません。まあ、まずJavaDocをみてみましょう。オラクルのドキュメントの中にorElse
の定義は下記のように書かれています。
public T orElse(T other)
存在する場合は値を返し、それ以外の場合はotherを返します。パラメータ:
other - 存在する値がない場合に返される値、nullも可戻り値:
値(存在する場合)、それ以外の場合はother
一方、orElseGet
はこんな感じです。
public T orElseGet(Supplier<? extends T> other)
値が存在する場合はその値を返し、そうでない場合はotherを呼び出し、その呼び出しの結果を返します。パラメータ:
other - Supplier(値が存在しない場合は、これの結果が返される)戻り値:
値(存在する場合)、それ以外の場合はother.get()の結果例外:
NullPointerException - 値が存在せずotherがnullの場合
やはり説明も似ていますね。でも、nullについての説明は違います。前者の説明の中に、nullも可
が入っていますが、後者にはそれがなく、逆にNullPointerException
という例外の説明が入っています。すなわち、Optional.ofNullable(null).orElse(null)
はnullを返してくれますが、Optional.ofNullable(null).orElseGet(null)
は例外をスローしてしまいます。
それはそうでしょう。引数は確実に違うからですね。orElse
の引数は値で、orElseGet
はSupplierというラムダ式を取っています。Supplierのgetメソッドに通じて値を取得するので、Supplier自体がnullになってしまったら、NullPointerExceptionがスローされてもおかしくありません。
でしたら、下記のコードの実施結果は同じように思えますよね。
1 | public class MyTest1 { |
1 | public class MyTest2 { |
実行してみれば、すぐ分かりますが、前者はtest
を出力している一方、後者はtesttest
を出力しています。test
というSupplier変数はラムダ式であることを忘れてはいけません。Cay S. Horstmannが著書Java SE 8 実践プログラミングの中でラムダ式を紹介する時、「すべてのラムダ式の重要な点は、遅延実行(deferred execution)です。」、「ラムダ式を使用する主な理由は、適切な時期までコードの実行を遅延させることです。」と説明しています。上記の違いはまさにそれを証明する実例になります。もしOptional内の値はnullじゃなければ、orElseGet
内の式が評価される必要がありません。その為、System.out.print(result);
が実行されません。当然ながらtest
の出力はありません。
上記のようなコードであれば、まだそんなに問題になりませんが、例えば「Optional内の値はnullの場合、データベースにレコードを挿入し、割り当てられたIDを返す」、「Optional内の値はnullの場合、某APIを叩き、データを取ってくる」みたいなロジックを実装する際に、該当ロジックは常に実行されるので、orElse
を使ってはいけません。間違って使ってしまうと、速度が大幅に遅くなってしまう上、データの整合性が取れなくなってしまうでしょう。
要するに、ラムダの「遅延実行」特性をちゃんと覚えようということですね。
12月13日に開催された「Vue.js Tokyo v-meetup #6」に参加してきました。前回のMeetupがVue.jsを用いたサーバーサイドレンダリングの魅力を見せてくれたと言えるなら、今回のMeetupは2017年、Vue.jsのエコシステムがますます繁栄してきたアピールになった年と言えるのではないでしょうか。開発環境、テストツール、コーディング規約、UIフレームワーク、強力な公式サポートのおかげでWebのフロントエンド開発における欠かせないものを全部用意してくれています。一年前と比べて、今のVue.jsはすでにフロントエンドのギークたちのオモチャから、プロダクションレディで成熟したフレームワークになってきていると実感しています。
Vue.jsのバージョン2.4まで、Vue.jsにおけるTypeScriptのサポートは不足していました。TypeScriptでVue.jsプロジェクトを作成すること自体は問題ないですが、型定義の恩恵は受けにくい状況だったように感じます。@maeharinさんが発表の中で紹介して頂いたように、Vue.js2.5以前、object型構文の内ではthisの型推論はできませんでした。そもそも、TypeScriptはこのような推論をサポートしていませんでしたので、Vue.js側のサポートも厳しかったでしょう。とは言え、Vue.jsの場合、「vue-class-component」を利用しない限り、object型構文は基本なので、かなり致命的な問題だったと考えられます。めでたいことに、TypeScriptのプルリクエスト#14141によって、objectリテラル内のthisの型推論が可能になりました。そして、Vue.jsのプルリクエスト#6391において、型定義の追加を含み、TypeScript向けの改善が行われた為、バージョン2.5以降、Vue.js+TypeScript開発が一気に便利になってきました。例えば、プロパティの補完とタイプミスを避けるために、@maeharinさんがプロジェクトの中にAPIの型定義を入れました。swagger-codegenというツールを使い、RubyクライアントとTypeScriptの定義を自動生成できました。自動生成されたTypeScriptの型定義はフロントの開発で利用されていますので、非常に効率の良い開発を行ったようです。ちなみに、swagger-codegenで使用されるAPI定義ファイルもSpringFoxによって自動生成されているため、無駄な重複作業がかなり排除できたんじゃないかと思います。
Web開発を行う際に、使いやすい開発環境は非常に大事な要素になっています。Vue.js0.1Xの時代からAtomやSublime Text上のプラグインがすでに提供されて来ましたが、自動補完が足りなかったり、Lintができなかったり、とても使いやすいとは言えないでしょう。しかし、これらの問題はVSCodeの素早い進化とプラグインVeturの登場によって解消されました。今回の会場提供者でもある、マイクロソフトの井上さんがデモの中に見せて頂いたように、VSCode+VeturコンビでVue.jsの開発を行えば、デバッグを含み、IDE並の開発体験ができるはずです。VSCodeがデフォルトでサポートしているEmmetと前述のTypeScript(実は、マイクロソフトはTypeScript-Vue-Starterというテンプレートを提供していますよ)を使えば、さらにスムーズな開発が期待できますね。
TypeScriptの話と別に、@maeharinさんがスライドの中で、UIフレームワークであるElementを絶賛していました。Elementは中国のウェブベンチャーElemeが主導して開発したVue.jsのUIフレームワークです。デザインがシンプルで、多くのUIコンポーネントが提供されているため、人気を博し、Vue.jsのUIフレームワークの中で、もっとも人気があるフレームワークのひとつです。Vue.use
の中にロケールの設定と一緒に入れるだけで日本語がサポートされるし、Veturの自動補完にも対応しています。さらに、各コンポーネントの設定項目が豊富で、バリデーションもかなりやりやすいそうです。Vue.jsのプロジェクトを試しに作りたい場合、Elementと組み合わせれば、素早く開発できそうな予感がしますね。当然ながら、Vue.jsのUIフレームワークはこの一つだけではありません。例えばスライドの中に挙げて頂いた、グーグルのマテリアルデザインに従っているvuetifyや、Bootstrapスタイルのbootstrap-vueなどもそれなりに成熟できているので、迷ってしまうほど多くの選択肢があると思います。
複数人による大規模な開発の場合、可読性やメンテナンスのしやすさは重要なポイントです。そのため、ソフトウェア開発工程の中で、コーディング規約は欠かせないものです。当然ながら、プロントエンドのプロジェクトも例外ではありません。純粋なJavaScriptなら、AirbnbとStandard JSは最も高い人気を誇っていますが、フレームワークの場合、Reactが圧倒的な優位に立っています。AirbnbがReactを採用しているため、2015年に「Airbnb React/JSX Style Guide」を公開し、すでに多くの開発現場で使用されているそうです。また、ESlintのプラグインである「eslint-plugin-react」も同時期に登場したため、「eslint-config-airbnb」と合わせて利用すれば、コーディング規約とリンターの設定を心配する必要がありません。それに対し、Vue.js界ではそれほど優れたプラグインや普及できている規約がなく、羨むしかできませんでした。しかし、こんな状況は改善されつつあります。まず、公式スタイルガイドがつい最近リリースしました(まだBeta版ですが・・)。必須、強く推奨、推奨、使用注意、四段階に分けて様々なルールが盛り込まれています。それに、わざわざ静岡からお越しになった@mysticateaさんが自ら開発している「eslint-plugin-vue」も公開になりました。ルールはまだ追加中ではありますが、すでに数十種類を備えているため、十分に使えると思います。例えば、「no-parsing-error」というルールはHTMLやテンプレートの埋込式に構文エラーが無いかチェックしてくれますので、JavaScriptじゃなくてもeslintの恩恵が受けられます。公式スタイルガイド内のルールをサポートする予定なので、かなり期待できますね。ちなみに、現在開発リソースがまだ足りていない状況のようですので、興味ある方ぜひ参加してみてください。
実はVue.jsのユニットテストに関して、「v-meetup #4」の時、@hypermktさんが一回発表を行っていました。しかし、公式テストツールはまだ開発段階だった為、@hypermktさんはavoiazの紹介をしていました。約半年後の今、公式テストツールであるvue-test-utilsはすでにv1.0.0-beta.8になり、だいぶ安定して来ました。@lmiller1990さんが発表の中で、コンポーネントの分離(shallow機能)、イベントのシミュレーション(trigger機能)、データの設定(setData機能)およびvuexのサポートを実演しながら紹介して頂きました。開発の中で、コンポーネント間の依存が複雑になるケースが多々あると思いますので、コンポーネントを切り離すことはユニットテストを行う際に不可欠な一歩だと考えています。これらの機能を使えば、その作業がある程度便利になります。vue-test-utilsの開発はavoiazの作者が主導している為、avoiazの良いところを継承しています。今Vue.jsのユニットテストツールを検討しているのであれば、vue-test-utilsは恐らく一番妥当な選択だと思います。
Vue.jsのエコーシステムはこれだけではありません。上記のプロジェクト以外にも、注目すべきなプロジェクトがたくさんあります。例えば、私が気になっている「vue-component-compiler」はもうすぐ世に出るそうです。今までVue.jsシングルファイルコンポーネントのコンパイルロジックはプロジェクト依存でした。その為、単独利用が難しく、コンパイルロジックを使ったちょっとした拡張がやりにくかったです。「vue-component-compiler」が使えるようになれば、バンドルツールに依存せずコンポーネントのコンパイラの使用が可能になり、Parcelのような新しいバンドルツールにも迅速に対応できるようになるでしょう。また、@kazuponさんの発表の中にもあったように、公式クックブックとvue-cliの刷新プロジェクトもすでに動き出したそうで、Vue.js本体のメインバージョンアップも予定されているようです。Vue.jsのさらなる進歩が楽しみですね。
2017年のウェブ開発において、JSフレームワークの利用はようやく定着化してきたと思います。特に、Vue.jsは直感的で理解しやすいというメリットを持ち、多くの支持を得ることができました。今回のMeetupの中で、@lovalottaplusさんがデザイナーさんにVue.jsを使用してもらう事例を紹介して頂きました。コンポーネントを活用し、内部ロジックをうまく隠蔽すれば、プログラミング知識がそれほど豊富じゃないデザイナーでも開発できることはとても印象的でした。これはまさにVue.jsがずっと提唱して来たプログレッシブの好例だと思います。触りくらいなら、既存コンポーネントを使用すればすぐ開発できる、遠くまで行きたければ、TSなりRxJSなり使ってもよし・・・多種多様なケースに対応できることはVue.jsの長所であり、それに加え、エコシステムのより一層の充実により、2018年のVue.jsには更に期待できそうですね。
今回のMeetupは本当に有益でした、次回のMeetupも楽しみにしています。頑張ってLT枠を取りたいですね(笑)
今回の記事は同僚のこうめいさんに査読して下さいました。本当にありがとうございました。
]]>UX向上の観点により、読み込みが遅いもの(特に大きなイメージファイルなど)が存在する場合、先に枠を表示し、ダウンロードできた後本物に置き換える設計は最近増えてきています。そのうち、下の図で示されているようなFacebookのロード画面は好例の一つだと言えるでしょう。
この設計をVue.jsで実装するのはそれほど難しくないでしょう。いくつかのVue.js製のプレスホルダーはすでに公開されている(その一、その二)ため、それらを条件付きレンダリング(Conditional Rendering)と合わせ、ディレクティブv-if
とv-else
を活用するだけで実現可能です。
1 | <placeholder v-if="content === null" /> |
こういう風に実装してもダメとは言えませんが、コンテンツの切り替えにトランザクションが全く存在しないため、唐突の感は免れません。よって、置き換えが発生した際に、フェードイン/フェードアウト効果を追加した方がUX的に優しいと思われます。
Vue.jsでトランジションを追加するのに、非常に簡単なやり方が存在します。そう、Transition
というラッパーコンポーネントで囲み、CSSで定義する方法です。
1 | <template> |
しかし、これだけでは変な動きが出てきます。一時的にプレスホルダーと本物が同時に画面上に現れるわけです。
理由は単純です。transition
内の子コンポーネントのアニメーションが同時に開始するため、プレスホルダーのフェードアウトアニメーションが再生されている間に、本物のコンポーネントがすでに出てきてしまいました。
これを回避するために、Vue.jsがトランジションモードを用意してくれました。現時点ではout-in
とin-out
2種類が提供されていて、out-in
を利用すれば、コンポーネントAが消失した後、コンポーネントBを出現させることは可能になります。
1 | <template> |
上記の解決策は悪くないと思いますが、もう少しいい表現ができると思いました。今の場合、トランジション効果は一つずつ再生されるため、所用時間が長くなり、入れ替わる感が感じにくくなっています。その為、やはりフェードアウトとフェードインは同時に行って欲しいですね。となると、トランジションモードの使用を取りやめ、その代わりに、CSSのposition
で二つのコンポーネントの位置を同じところに固定すれば、望んでいた効果が出せるはずです。
1 | <template> |
しかし、見た目は完璧でしょうが、この方法はトランジションモードの方法より扱いにくいはずです。なぜかと言いますと、position: absolute;
の使用によって、コンポーネントらがドキュメントフローから出てしまうので、CSS上で配慮しなければいけないものが増えてしまいます。
もっといい方法があるのではないかと思いますが、今回はただの実験なので、これ以上追求しませんでした。一見簡単そうなテーマでしたが、完璧に実現することは意外と難しいということを感じましたね。
]]>デフォルトの設定でHexoのブログを作っている場合、ホームページ(index)がすでに存在しています。かなり便利でありがたい機能ですが、カスタマイズしようとしたら、手数がやや掛ってしまいます。
具体的に言うと、テーマ内のindex.ejs
ファイルに記入されているコードはlayout.ejs
の<%- body %>
に入ります。すなわち、indexページのレイアウトはindex.ejs
のほか、layoutフォルダ内のlayout.ejs
にも左右されます。別に悪い仕様とは全然言えませんが、ホームページだけは通常のテンプレートに適用されたくないというユースケースもあるでしょう。
それを回避するために、HexoのGitHub上のIssueを調査したり、何回か試したりして、ようやく分かりましたので、ここで整理してみたいと思います。
hexo-generator-index
というプラグインを削除します。自分でインデックスページを作るので、当然ながら自動生成の機能は不要になります。Hexoプロジェクトのルートディレクトリに行って、npm uninstall hexo-generator-index
を打てば問題ないはずです。index.md
というファイルを作ります。こちらのファイルはエントリーポイントになります。layout.ejs
内に以下のような条件分岐を加えます。1 | <% if (page.source == 'index.md') { %> |
1 | class MyTest |
上記のコードの実行結果は
答えは
一見複雑そうですが、実はポリモーフィズムをちゃんと理解できれば、すぐ解けるはずです。それでは、main
メソッドの処理を追って見ましょう。
main
メソッドの一行目ではA
型B
実装の変数b
を宣言しました。(A)
というアップキャストの処理が書かれていますが、実はコンパイラが自動的に行ってくれる作業なので、ただの目障りで意味がありません(勿論エラーにもなりませんが・・)。b
のメソッドを利用する際に、コンパイラはまず実装型にそのメソッドがあるかどうかを見ます(二行目)。クラスB
にhello
というメソッドがありましたね。オーバーライドが明記されていませんが、シグネチャに違いがない為、親クラスのhello
メソッドがオーバーライドされていることは分かります。ポリモーフィズムのルールによりクラスB
のhello
メソッドが実行されます。hello
いうメソッドはhello
というコンシューマー関数のフィールドを呼んでいますが、残念ながらA
クラスにしかhello
が存在しません。その為、やむを得ずA
クラス内のhello
関数を利用することになります。(ちなみに、メソッド名とフィールド名の重複は許されますので、コンパイルエラーになりません)hello
という関数はmessage
というフィールドを使います。クラスA
のmessage
はhello
ですので、出力結果はhello world
になります。クラスB
にもmessage
というフィールドが存在しますが、今使っているhello
関数とは無関係ですね。main
の四行目ですが、b
オブジェクトのhello
関数を利用しているように見えますね。しかし、フィールド変数のオーバーライドが存在しないことを忘れてはいけません。hello
関数はhello
メソッドと同じく処理手続きを定義するものですが、本質的にはmessage
のようなフィールド変数です。その為、どのフィールドが利用されるかは宣言時の参照型によります。A
として宣言された為、クラスA
のhello
関数が利用され、二行目の出力結果と同じようになります。