蚊帳の中の日記

ゆるく生きてます

Webフロントエンド ハイパフォーマンス チューニング を読んだ

これ読んだ。 https://www.amazon.co.jp/Web%E3%83%95%E3%83%AD%E3%83%B3%E3%83%88%E3%82%A8%E3%83%B3%E3%83%89-%E3%83%8F%E3%82%A4%E3%83%91%E3%83%95%E3%82%A9%E3%83%BC%E3%83%9E%E3%83%B3%E3%82%B9-%E3%83%81%E3%83%A5%E3%83%BC%E3%83%8B%E3%83%B3%E3%82%B0-%E4%B9%85%E4%BF%9D%E7%94%B0-%E5%85%89%E5%89%87-ebook/dp/B0728K5JZV/ref=sr_1_1?s=digital-text&ie=UTF8&qid=1536989264&sr=1-1&keywords=%E3%83%95%E3%83%AD%E3%83%B3%E3%83%88%E3%82%A8%E3%83%B3%E3%83%89%E3%80%80%E3%83%91%E3%83%95%E3%82%A9%E3%83%BC%E3%83%9E%E3%83%B3%E3%82%B9www.amazon.co.jp

なんで読んだかって言うと、今働いてるとこの事業目標の一つに「Webページの表示速度向上」ってのがあったので、「もしかしたらwebフロント層で何かできることあるんじゃないか?でも、何やっていいかわかんないし、とりあえず前知識というか基礎知識というか、そういうのが足りないな〜」と思ったのがきっかけだった。

ウェブサービスのパフォーマンスチューニングって言うと、スロークエリを直したり、ビジネスロジックを直したり、nginxとかでアセットファイルとか色々圧縮したり、レンダリングを早くしたり、CDN使ったり、キャッシュにしたり、、、本当に色々な階層で改善案が浮かぶ。この本はサーバーからのレスポンスとかそういうのを早くするというのではなく、ページのレンダリングなどフロントエンド層の改善案に必要な基礎知識をざっと学べるので良かった。

ブラウザの仕組みがだいたい分かる

恥ずかしながら、ブラウザとかどういった仕組みで動いてて、どうやってWeb ページを表示してるのかよく知らなかった。。。

このブラウザの仕組から学べたので、「リソースを読み込むところが悪いのか?」「レンダリングするときにCSSとかJSが止めてて遅くなってるのか?」とかボトルネックがどこなのか計測して調べやする知識をざっとゲットできた気がする。

ブラウザは

  1. Loading(読み込み)
  2. Scripting(JS実行)
  3. Rendering(ツリー構築)
  4. Paiting(描画)

という流れ。最初のLoadingの章では、TLSハンドシェイクとかDNSの話もでて、どうやってブラウザが通信を確立してリソースを読み込むのかというネットワークプロトコルの話も交えた話が詳しく書いてあってよかった。チューニングのChrome Dev Toolでの見方とボトルネックの見つけ方も書かれていてわかりやすい。

あとは「非同期でjs読み込むの良さそう!」「DNSプリフェッチとかで事前処理しとく!」「事前にリソース取っておく!」...etc 、フロントエンドでの速度改善に役立ちそうなワードや情報をゲットできて、今後の実践する上で足がかりになる知識を大まか網羅できた気がする。 (8章あたりからはCSSアニメーションとかユーザー認知をあげるインジゲータとかの話だったので、まあ実践するときに見ようと思って、さーっと読み飛ばした。)

推測するな、計測せよ

まあ、そうですよね、、、はい。ほんと計測大事。この本で伝えたかったことが、この名言に集約されてると思う。

次は。。。

Rendering(ツリー構築)、Paiting(描画)のチューニングとなるとCSSとかアニメーションとか、普段自分が触る機会が少ない領域の話が多い印象だったので、実践するとしたらLoading、Scripting段階のチューニングを先に実践していくのが良さそうと思っている。やっていき!!!1

次は、この2冊を読んでみようかと思っている。多分、だいたい同じことが書かれてるのではないかと思うが。

コードの意味的な違和感を捉える

この間ペアプロを通して実装していたコードをリファクタリングしていた時に、「そのコードは意味的にわかりやすいか?」的な話が出て、あまり深く意識できてない考え方だったので勉強になった。

どういうことかって言うと。。。

例えば、とあるwebサービスでユーザーの状態によって処理を切り替えるみたいな実装を書いてたとする。こんな感じでcase-whenで(あ、rubyで書いてます)。

class HogeUserExecuter
  user = User.find_by(name: name)

  # ユーザーの状態によって処理を切り替える
  case
  when user.is_blacklist? #=> ユーザーがブラックリストに入っているか?
    ...
  when user.is_not_confirm? #=> ユーザーが仮登録状態か?
    ...
  when user.is_confirmed? #=> ユーザーが登録完了状態か?
    ...
  else
    ...
  end    
end

case文でもif文でもそれ以外でも別に何でもいいが、ここに「ユーザーが論理削除済みの場合の処理」を追加したいとなるとcase文にwhen user.is_deleted?みたいなコードを追加するのではないかと思う(どうかな?違うかな。。。僕だけ?)。

では次に、このサービスがブログサービスだったとして「ユーザーが自身のブログ記事を書いて公開している場合の処理」をこのクラスに追加したいとする(例が良くない気がするけど、あしからず)。 今回だったら、case文に追加するだろうか 🤔

このcase文の一連の処理は「ユーザの状態」から処理を分岐させていると考えることができるかと思う。 しかし、今回追加する要件はブログの記事を書いて公開しているだ。ブログの状態というユーザーとは別の評価対象と考えることもできるのではないだろうか。ユーザーの状態を見て確認する処理をcaseに入れると、違和感が感じる人もいるかもしれない。

class HogeUserExecuter
  user = User.find_by(name: name)

  # ユーザーの状態によって処理を切り替える
  case
  when user.is_blacklist? #=> ユーザーがブラックリストに入っているか?
    ...
  when user.is_not_confirm? #=> ユーザーが仮登録状態
    ...
  when user.is_confirmed? #=> ユーザーが登録完了状態
    ...
  when user.blogs.public? #=> ##### ここを追加 #####
    ...
  when user.is_deleted? #=> ユーザーが論理削除済み
    ...
  else
    ...
  end    
end

ここで自分が言っている違和感というのは、ユーザーの状態を判定する意味を持つ一連の処理に、ブログの公開状態を判定するという違った意味合いの処理が混ざるということだ。このcase文は意味的に伝わりやすいのか??人に説明するとき、「ここでユーザーの状態を見て処理分岐しています。あ、でも一部ブログの公開状態をみているケースもあります」となって、説明を受けてる側が「え?なんで?」ってならないか???、、、こんな感じの違和感につながる。 この違和感は人によって(特に開発者の経験によって)感じ方が違うかなと思っていて、まだまだ経験の浅い自分はこれに対して特に違和感もなく、一連の処理フローに入れたほうがなんとなく流れ的に良さそうだし、とりあえずcase文に入れておけば問題ないっしょ!!!と思っていた。

この書き方が間違っている!ということを言いたいのではない。別にこの書き方は正しく動作するなら間違っていないと思うし、今回の仕様を満たす実装の一つだと思う。ただこういったコードの意味的な違和感(自分はそう呼ぶことにしている)を捉えられるかどうかが、読みやすいコードを書くヒントになるのではないかと、ペアプロで先輩エンジニアに教えてもらえた。

この違和感を解決するためには、そのケースを別箇所に切り出すとかがいいかもしれない。

class HogeUserExecuter
  user = User.find_by(name: name)

  # => 記事を公開済みかどうか?
  if user.blogs.public?
    ...
  end

  # ユーザーの状態によって処理を切り替える
  case
  when user.is_blacklist? #=> ユーザーがブラックリストに入っているか?
    ...
  when user.is_not_confirm? #=> ユーザーが仮登録状態
    ...
  when user.is_confirmed? #=> ユーザーが登録完了状態
    ...
  when user.is_deleted? #=> ユーザーが論理削除済み
    ...
  else
    ...
  end    
end

もしくは処理の順序などの理由でcase文の中で書く必要があるなら、ユーザー情報を入れてる変数名とかクラス名を変えたりして、case文の意味を変えるというのもありかもしれない(いい変更例すぐ思いつかなかった)。

これが正解だ!というのはないと思う。プロジェクトの開発基準とか開発者の考え方、好みとかも関係してくると思うし、細かいかもしれないが議論の余地はありそう。

あんまりいい例ではないけど、このコードはどういった意味を持ってるのだろう???どういった処理をしているのだろう???ということを、もっと深く考える癖をつければ、リーダブルな変数名やメソッド名をかけたりすることに繋がる気がしている。

また良い習慣を教えていただけて嬉しいと思った次第でした🧡 :kansya:

そいつはどんなQuery吐くんや?

RailsActiveRecordとかLaravelのEloquentとか使っていると、たまに「あれ?このメソッドはどんなクエリ吐くっけ?」ってなったり、意図していないけど該当のメソッドを使ってしまって「余計なクエリを発行してもうた〜」ってなることが個人的によくあって、レビューで指摘を受けることが多かった。今でもたまにある。

Railsだとfirstとかlastとかfindとか、ホントよく使うし、Rails Guideでもクエリインターフェースの章で序盤に出てくる基本的なもの。実際使ってみると、お手軽感があり、「あ、余計にfindしちゃった」「あ、firstじゃなくても良さそう」とかなったりする。exist?とかもよく使うけど、クエリ発行するので要注意だったりする。 クエリを吐くってことは、その分DBにリクエスト送っている。余計なリクエストを削減するだけでも、少しだけパフォーマンス向上とかにつながるので、できるだけこういったは抑えることを意識していきたい。

あと、モデルの関連で指定するオプションでdependent :delete_allとか:destroyとかあるけど、どっちのほうがパフォーマンスがいいクエリを吐くのか??とか、each回してfindとかせずにwhereでUser.where(id: user_ids.pluck(:id)とかして該当のレコードを一括で取得するようにするとか、、、。そのメソッドがどういったクエリを吐くのか?どうしたらもっとパフォーマンスの良いクエリを発行するのかを意識する必要もありそう。

対策

開発環境でbundle exec rails sして、ちゃんとどんなクエリが発行されているのか?余計なクエリは発行していないか?を確認・意識することが重要。

ペアプロをしてもらって自己の問題のボトルネックがわかってきた

ペアプロをしてもらったのだが、その時の先輩エンジニアにフィードバックを色々問題の指摘やフィードバックをもらって自分の課題を浮き彫りになって面白かったので、鉄は暑いうちに打て精神でブログに書き留めておきたくなった。 自分の開発の仕方や思考プロセスをレビューしてもらって、フィードバックをもらえる機会はなかなか少ないと思うので、今後もこれらを意識して問題克服に努めた(←具体的にどうやって??)。

思考系

  • どんな粒度で考えれば良いか、思考の粒度や問題の切り出しが自分でできていなかった。
  • ある問題点を見つけて解決する最中に、他の問題点も見つけてそちらも考え出し、色々なことを考えながら進めて混乱してしまうケースが多々ある。問題や思考の粒度を小さく切り分けて、「いま自分はどの問題について取り組んでいるのか?もしくは取り組むべきなのか」をよく確認する癖をつける(問題のフォーカス)。人間の考えられる量には限界があるので。
  • 思考を分離して考える。上の項目とも近いものがあるが、今自分はどの問題を解決しようとしているのか??他の問題も持ち込んでいないかよく考える。そしてその問題について思考を集中する。思考を集中できるので、気持ちも楽になる。実装でも一部に集中できれば良さそう。一部修正して、その一部のRspecだけ通すように務める。全体のスペックや実装を見るのはその後。そのほうが気持ちが楽になる。
  • 説明などをする時、考える事と文章を組み立てることが一緒になってしまって、結果的に読み手が分かりづらい文章になっていることが多いので、一緒にやらない。コツとしては事実ややったことを羅列して、その後に論理的にくっつけると、思考も分けられて良い。良いコミットメッセージや説明力向上につながるはず。
  • 何が問題なのか?わからなくなったら、書き出してみるのが良い!頭の中で考えるだけでは整理できない。少なくとも自分はそこまで器用なことはできないことがわかった。
  • 図解などは思考や理解の助けになる
  • 悩みすぎずに休憩やリフレッシュを入れることは良いこと。煮詰まりすぎずに、5分とか考えて途方に暮れてしまったら、相談したり、リフレッシュした利するのが良さそう。
  • 言語化し説明できることが、自分の理解の基準とできるだろう。この基準は自分でも一つの指標として持っておくと良いだろう。

開発の進め方系

  • Time.zon.atってどんな形式の日時が返ってくるんだっけ??となった時に、「Time.zon.at」の用法を覚えなきゃ!ではなく、この処理は気をつけないことがあったな!!!と思い出す癖をつける。詳細な処理などはgoogle先生に聞いてみるのが良い。人間の覚えられることなんて限られているので。
  • typoすることが多いので、変数名などをコピペや補完機能を使って余計なミスを減らそう!努力ではなく、方法でカバーする。
  • 自分がなぜそういう実装をしたのか”結論”を自分の中に落とし込む。落とし込む過程で根拠があることが重要。根拠が会って結論が落とし込めたら、それらを文章に組み立てたり、説明する(説明責任)。説明できたことによって、聞き手が「なぜ?」と考えることが少なくなる。この根拠・理由を言わずに結論だけ話しても「なぜ?」と聞き返され、最悪別の議論に話がずれ込むこともある。”速”を出した開発をするには、そのあたりのコミュニケーションの省略にもつながるので説明責任を果たすことは大事なこと。コミットメッセージに説明責任があればレビュワーは納得感がある。
  • 一発で覚える癖は大事そう。その場でできる限り理解するクセをつける。
  • 木を見て森をみず状態にならないこと。開発のコツとして、まず実装の全体を見て、どういった処理フローになるのか想像し俯瞰する。そうすると実装のイメージも付きやすくなってくる。特に処理が複雑なものについては有効かも。「クラス名どうするとか??」とか細かいところにフォーカスする前に、全体を見渡すことは特に設計でも重要。大きい粒度で考えて、小さくしていく問考え方にも近い。
  • 細かいところに気が行き過ぎるのは設計の段階では良くないかも。細かいところは実装段階で。
  • 「エラーに出くわす → エラーを見る → ググる」はあまり良くない。「エラーに出くわす → エラーを見る(場合によってはstacktraceも読みとく) → ドキュメントやコード・テストを見る → それでもわからなければググる」。いきなりググるのは課題や問題を把握せずにgoogle先生に丸投げしている。思考放棄。処理は大体コードに落とし込まれているはずなので、ドキュメントやコードを見て仕様を確認するのが、正しい対策や対応を身につける近道。
  • ドキュメント英語は辛いけど見ましょうよ!ね!(問題を細分化→何がわからないのか出来るだけ切り出す→ドキュメントやソースを見に行く→それでもわからなければググる
  • あとドキュメントもおかしい場合は大いにある。PRを出そう!くらいの気持ちで読んで見る。
  • Rspecは先にテストしたいことをcontextやdescribeから書いて、そのあとに中身を書いてみる!何をテストしたいのかを考えないといけない。そしてテストは仕様書なので、そこがおかしいのは良くない。読み手が理解しづらいものになる。
  • ペアプロみたいに話しながら進められると、思考の整理になるので良い。
  • 変数に何が入っているとか、ハッシュの構造はどうなっているとか考えてもしょうがなかったら、考えるのめんどくさくなってきたらbinding.pryを使って見てみる!

雑なので、個々についてはまた別の記事で詳細を書こうと思う。

"相談できる"って素晴らしい

仕事のことで先輩や上司、同僚に相談して、結果、問題を解決できないこともあるけれど、話したことによって自分の頭の中の整理になったり、意外と足したことがないかもって思えたり、なにか対応策を練ることができたりして、自分の心が穏やかになることがこんなにも素晴らしく、気持ちのいいこなのかとココ最近になって再認識できた。そして、そういう環境に身を置かしてもらっているということに改めて感謝したい気持ちになった。

まだ転職して4ヶ月くらいだけれど、会社の風土としても相談しやすい雰囲気が出ているし、相談したら先輩も色々教えてくれる。上の方々も色々とそういった環境づくりに気を配ってくれていて雰囲気があって、とてもいいな。やりやすいなと思った。今日もちょっと困り事をあって相談をさせてもらったのだが、他のエンジニアの方なども巻き込んで色々と解決方法などを講じてくれたりして、とても有難かった。先輩エンジニアの方々も自分の問題点を解決するための提案や今後の進め方を提示してくれたし、本当に感謝だし、同時にもっと頑張らねば!と思えて、なんか勝手に活気が湧いた。

前職で働いていた会社は正直そういった環境はなくて、個人的には仕事の進め方を考えたりすることもきつかったし、メンタル面でも結構きつかった(その会社では新卒で入社仕立てで色々青二才なところもあって、自分の相談の仕方が悪かった面もあるのかも知れないけれど)。メンタル面でキツかったというのは、相談をしても「え?何お前そんな事も知らないの?」「そのくらい自分でなんとかしろよ」という雰囲気が蔓延(少なくとも自分はそう感じた)していて、実際に相談や質問をしにいっても今表現した言葉をそのまま浴びせられることが多かった。個別で言われることもあったのだけど、これを部署会議や全体会議のような人が多くいる前でやられることもちょくちょくあった。
そのくらいで弱音はいてんじゃねーよと思う人もいるかも知れないけど、自分にはそれがひどく耐えられなかった。で、よくあるダメ新卒の負のパターンみたいになって、相談しにくい→抱え込む→仕事が滞る→怒られる→メンタルダメージみたいなことが毎日とまでは行かないけれど、ちょくちょくあった。まあそれ以外のこともあったりして、今の会社に転職を決意したわけ。。。。

ちょっと話がそれた。。。。相談できるって素晴らしい!って話に戻る。本当に相談ができて、みんなで問題点などを共有し、一緒に解決するために考え、行動できるということが如何に心強いと言うか、心を穏やかにするのか再認識する日がここ最近多かったので、言葉に書き留めたくなった次第。これは人任せにするということで楽になるとかではなくて、(一緒に働いている凄いエンジニアの方や上司の方の受け売りなのだが)責任をみんなで共有してみんなで解決の道を探り実行するみたいなイメージ。「一人で抱え込まないで、みんなでやってるんだから相談しましょう!」とか「わかんなかったら聞きましょう!数秒考えてわかんなかったら、すぐ質問したり意見するくらいが良い。意見したり質問することは悪ではない」とか、みんなそういった話を熱を込めて言うし、「こういった考え方ができるのか。。。」と正直、カルチャーショックだった。(転職仕立てだから今だけ優しいだけかな。。。とか思ってしまってたけど(笑)、他の人の雰囲気とかを見てもそういった感じではなさそう。)

こういった相談できたりする環境ができるのは、相談できる人がいるなど環境が整っているからとも言えると思っている。今の自分はほとんど相談して助けてもらっている側の人間。早く実力をつけて、みんなから相談され、解決策を提案したりして指南できるオジサンにならねば!と思った次第である。

render :json でmodelメソッドの処理結果をjsonに追加して返したい

railsでは一般的なやり方なのかどうかわからないけれど、とあるrails apiの処理を読んでいる時にちょっと見慣れないコードがあったのでブログに書いてみる

例えば

def index
  user = User.find(id)
  render json: user
end

これはよく見るコードで、こんな感じでActiveRecordで見つけてきたuserオブジェクトの結果をjson形式で返す。
この場合、Userでnameやらidやら定義されている(?)パラメータしかjsonで返すことができず、「定義されているAttributesだけだとレスポンス受け取り側がわかりづらい事があるかもしれないので、他のAttributesを追加して返したい」とか、ちょっとjsonをイジってから返したいという時にちょっと困る。

この問題を、今回見つけた見慣れないコードが解決しいた。以下、サンプル。

def index
  user = User.find(id)
  render json: user, methods: [:auth?]
end

Userのモデルにdef auth?が定義されていて、その結果をrenderで返すjsonに加えている処理になっていた。

こちらのサンプルだと、auth?で真偽値を返すメソッドで{.....user attribute..... , auth: true }みたいに追加される。 最初見たときのこのメソッドがfalseだったらrenderが実行されないとか、そういう処理なのかな???とか思っちゃたりして挙動がはっきりわからなかったので困惑した。 自分をメンターしてくれてる先輩エンジニアにも相談して、railsのコード読んでどこに定義されてるのか探してみたけど、ぱっと見つからなかった(TODO: これは後で探そう)。

ググったりrailsドキュメントでもrenderにmethodsオプションはない。stackoverflowにはちょっと関連の記事はあったけど。

stackoverflow.com

こっちはちょっと話題が違うかもだけど、同じような感じでmethodsオプションを利用している。

stackoverflow.com