Quantcast
Channel: TechRacho
Viewing all 2925 articles
Browse latest View live

Rails 6: Docker dev環境のconfig.hostsとwhitelisted_ipsについて

$
0
0

こんにちは、hachi8833です。

Evil Martians流のRails 6 Docker dev環境をやっていて迷った点をメモします。macOS環境でのDocker Desktopが前提です。

Rails 6のDocker開発環境構築をEvil Martians流にやってみた

1. Docker dev環境のためのconfig.hostsの追加は通常は不要

DockerのRails 6 development環境では、一部の場合を除いてconfig/environments/development.rbに以下を追加する必要はありませんでした(後述)。

  config.hosts << 'localhost'

Rails 6にDNSリバインディング攻撃防止機能が追加された(翻訳)

上の記事にあるように、development環境のconfig.hostsには既に"localhost"がデフォルトで含まれていますので、ブラウザからhttp://localhost:3000でアクセスできます。ただしdevelopment環境以外のconfig.hostsは空欄になっているので、少なくとも本番環境では別途config.hostsに自ドメインを追加します。

Rails.application.config.hosts = [
  IPAddr.new("0.0.0.0/0"), # All IPv4 addresses.
  IPAddr.new("::/0"),      # All IPv6 addresses.
  "localhost"              # The localhost reserved domain.
]

ngrokなどを使う場合は必要

config/environments/development.rbにconfig.hostsを追加する必要があるのは、たとえばngrokやserveoで外部からdevelopment環境にアクセスする場合です。
ngrokを使う場合はconfig/environments/development.rbに以下を追加します。

config.hosts << '.ngrok.io'

2. web-consoleを使うならwhitelisted_ipsの追加が必要

一方、DockerのRails 6 development環境でweb-consoleを動かすのであれば、config/environments/development.rbに以下のような行を追加する必要があります。

  config.web_console.whitelisted_ips = '0.0.0.0/0'

web-console gemはRailsのdevelopment環境にデフォルトで入りますが、better_errorsやRubyMineなどをデバッグに使っているのであればなくてもいいかもしれません。ところでweb-consoleって実際どのぐらい使われているのでしょう?🤔

参考: web-console を使えば Rails App のデバックが楽になる - Qiita

追記: config.web_console.permissionsについて

上の記事にあるように、現在のweb-console gemは、config.web_console.permissionsconfig.web_console.whitelisted_ipsの順でホワイトリストを参照していますので、どちらに設定してもいいようになっています。デフォルトではどちらにもホワイトリストは設定されていません。

おまけ

Docker環境に限りませんが、ウイルスチェックソフトのパーソナルファイアウォールにDocker development環境サーバーへのアクセスを邪魔されることもあります。ブラウザからアクセスできない場合はパーソナルファイアウォールの設定もチェックしてみましょう。

自分もESETのライセンスを更新したときにESETの設定が初期化されてアクセスできなくなり、上述の2つの設定をあれこれ変えてみたりしたのですが、結局ESETのパーソナルファイアウォールの設定を変えてやっと解決したのでした😅

関連記事

クジラに乗ったRuby: Evil Martians流Docker+Ruby/Rails開発環境構築(翻訳)

docker-composeを便利にするツール「dip」を使ってみた


週刊Railsウォッチ(20200309前編)Webpackerに乗り換えるべき理由25、Railsのindex_byとindex_withは有能、GCPはやっぱりスゴいほか

$
0
0

こんにちは、hachi8833です。生まれてはじめてRailsにプルリク投げて一瞬でマージいただきました😂


つっつきボイス:「お、ついにRailsにプルリク🎉」「土曜に投げたんですが、数分もしたらkamipoさんがマージしててたまげました😳」「kamipoさんの常駐率スゴそう⛩」「セキュリティガイドのmarkdownに&gt;みたいな生のHTML要素や記号が書き込まれていて、普通なら誰もそういうのは気にしないんですけど、GitLocalizeというツールの原文パースがその箇所でぶっ壊れたので、やむなくプルリクしました」「気づいた人が投げるのが一番☺

ドキュメントのようなトリビアなプルリクのタイトル冒頭には[ci skip]を付けてCIに負担をかけないようにするのがマナーと知りました↓(訳したのは自分ですが😅)。

参考: Ruby on Rails に貢献する方法 - Railsガイド

RailsのCI (継続的インテグレーション: Continuous Integration) サーバーの負荷を減らすために、ドキュメント関連のコミットメッセージには[ci skip]と記入してください。こうすることで、コミット時のビルドはスキップされます。[ci skip] は「ドキュメントのみの変更」以外では使用できません。コードの変更には絶対使用しないでください。
railsguides.jpより

GitLocalizeはGitHub上のmarkdownやhtmlドキュメントの多言語翻訳支援ツール(無料)で、なかなかスグレモノです。こちらについては近々記事にしたいと思います😋


  • 各記事冒頭には⚓でパーマリンクを置いてあります: 社内やTwitterでの議論などにどうぞ
  • 「つっつきボイス」はRailsウォッチ公開前ドラフトを(鍋のように)社内有志でつっついたときの会話の再構成です👄
  • 毎月第一木曜日に「公開つっつき会」を開催しています: お気軽にご応募ください

⚓Rails: 先週の改修(Rails公式ニュースより)

今回は公式の更新情報とコミットリストから見繕いました。

⚓水平シャーディングのサポートが追加

アプリケーションからマルチプルシャーディングに接続してシャーディングを切り替えられるようになった。シャーディングのスワップは引き続き手動である点に注意(今回の変更には自動シャーディング切り替えのAPIは含まれていない)。

# 設定例
production:
  primary:
    database: my_database
  primary_shard_one:
    database: my_database_shard_one
# マルチプルシャーディングへの接続
class ApplicationRecord < ActiveRecord::Base
  self.abstract_class = true

  connects_to shards: {
    default: { writing: :primary },
    shard_one: { writing: :primary_shard_one }
  }
# コントローラやモデルのコードでシャーディングをスワップする
  ActiveRecord::Base.connected_to(shard: :shard_one) do
    # shard_oneから読むようになる
  end

水平シャーディングAPIではreplicaもサポートされる。詳しくはガイドを参照。
ついでにこのPRでは、ドキュメントのないメソッドをいくつかprivate名前空間に移動し、エラーメッセージやドキュメントにも手を加えた。
同PRより大意


つっつきボイス:「たぶんhorizontal付けなくても普通にシャーディングでよかった気がする😆

つっつき後、verticalなシャーディングもあるらしいという指摘がありました。

参考: How Sharding Works - Jeeyoung Kim - Medium


同記事より

🎉❤がいっぱい付いてました」「マルチDBになってからはシャーディングをRailsでやる方法がしばらくなかったからじゃないかな🤔」「そういえばシャーディングは6.1から対応したいってどこかに書いてあったような」

最初に申し上げておきたいのは、現時点のRailsではシャーディング(sharding)はまだサポートされていないという点です。私たちはRails 6.0でマルチプルデータベースをサポートするために膨大な作業をこなさなければなりませんでした。シャーディングのサポートを忘れていたわけではありませんが、そのために必要な追加作業は6.0では間に合いませんでした。さしあたってシャーディングが必要なのであれば、シャーディングをサポートするさまざまなgemのどれかを引き続き利用するのがおすすめと言えるかもしれません。
railsguides.jpより

「シャーディングを待ち構えてた人は結構いるらしいことがわかった😆」「そんな感じですね」「ま自分はシャーディングやりたくないのでシャーディングじゃない方法で実現したいですけどっ🤣

参考: シャーディングとは、テーブルシャーディングという可能性【水平分割】 | SEO対策なら株式会社ペコプラ

⚓スキーマキャッシュで拡張子.gzipをサポート

スキーマキャッシュのシリアライズ戦略使うYAMLとMarshalの両方でgzipをサポートした。
特に巨大なスキーマキャッシュはKubernetesのデプロイで問題になる可能性がある(ConfigMapの上限は1 * 1024 * 1024)。データベースがこれ以上大きくなると上限を超えるかもしれない。
同PRより大意

# activerecord/lib/active_record/connection_adapters/schema_cache.rb#L11
      def self.load_from(filename)
        return unless File.file?(filename)

-       file = File.read(filename)
-       filename.end_with?(".dump") ? Marshal.load(file) : YAML.load(file)
+       read(filename) do |file|
+         filename.include?(".dump") ? Marshal.load(file) : YAML.load(file)
+       end
+     end
+
+     def self.read(filename, &block)
+       if File.extname(filename) == ".gz"
+         Zlib::GzipReader.open(filename) { |gz|
+           yield gz.read
+         }
+       else
+         yield File.read(filename)
+       end
      end
+     private_class_method :read
...
      def dump_to(filename)
        clear!
        connection.data_sources.each { |table| add(table) }
-       File.atomic_write(filename) { |f|
-         if filename.end_with?(".dump")
+       open(filename) { |f|
+         if filename.include?(".dump")
            f.write(Marshal.dump(self))
          else
            f.write(YAML.dump(self))
          end
        }
      end
...
+       def open(filename)
+         File.atomic_write(filename) do |file|
+           if File.extname(filename) == ".gz"
+             zipper = Zlib::GzipWriter.new file
+             yield zipper
+             zipper.flush
+             zipper.close
+           else
+             yield file
+           end
+         end
+       end

先週のatomic_writeが消えたのかと思って焦りましたが、openに移動してたんですね😋


つっつきボイス:「先週スキーマキャッシュでMarshal使えるようにしてたので(ウォッチ20200302)、その続きっぽいです」「なるほど、gzipのCPUコストは安いですし☺」「拡張子もgzになってるからスキーマ自体をgzで保存できるようになったのか」「PRにもKubernetes環境でスキーマキャッシュがあふれないためとありますね: 実際、コンテナではこういうストレージの割り当てがほとんどないので、そこにでかいファイルを押し込まれると困るという要請があったんでしょうね」「なるほど〜」

⚓Kubernetesとボリュームオプション

「自分たちはその辺を考えたくない😆: docker runコマンドの-v/--volumeに相当することをdocker-composeでやれば回避できるんですけど、くばねてだとその辺ができないということなんでしょうね」「Shopifyではdocker buildに含めているとか何とか下の方に書いてありますね: 場合によるんでしょうけど😆

「それそれ: 自分がKubernetesに踏み切れない理由のひとつが、ボリュームオプションをどうしても使いたいからなんですよ」「わかります☺」「ボリュームオプションが使えないとなるとインフラを相当慎重に作らないといけなくなる😢」「GCPのKubernetes(GKE: Google Kubernetes Engine)ならボリュームマウントやれないかな〜?🤔AWS ECS(Elastic Container Service)は既にできていて、AWSのEKS(Elastic Container Service for Kubernetes)もちょっと前にできるようになったと聞いたような覚えが」

「くばねてほとんど使ってないんでわかりませんけど、たぶんAWSで言うEFS(Elastic File System)に相当するサービスがGCPでも使えるならボリュームマウントできそう😋」「GCPのサービスを一気通貫で使っていればできそうな気がしますね🤔」「AWSのECSもEFSならマウントできるはず: EFSは言ってみればマルチAZの同時接続に対応したNFSなので」「GCPにもEFS的なサービスあるのかどうかはまだ知らない😆

参考: 最強のマネージドKubernetesはどれ? スペシャリストが比較検証したGKS/AKS/EKS – G3 Enterprise
参考: Amazon ECS で Amazon EFS を使用して Docker ボリュームを作成する
参考: Amazon EFS を Amazon EKS で使用する
参考: GCP と AWS サービス対応表・比較表(2019年2月版) | apps-gcp.com
参考: Network File System - Wikipedia

⚓GCPとGoogleの底知れなさ

「GCP、そろそろ勉強しないといけないかな〜って気持ちになってきますね: 最近babaさんがSlackのAWSチャンネルでAWSのここが残念とかいろいろ書いてるのを見てると特に🤣」「ああAWSのorganizationとかが残念な件🤣」「あの書き込みはもしかすると社内インフラをGCPに移行するための布石を打ってたりして🤣」「自分もそんな気がしてる🤣

「そういえば最近某所で某インフラエンジニアと呑みながらちょっと話したんですけど『やっぱGCPはつえぇ〜ですよ💪』って言ってた😆」「へ〜強い人もそう言ってるとは😳」「babaさんとも話したことあるんですけど、AWSは言ってみれば下から積み上げていったボトムアップ的なサービス、一方Googleは研究レベルのものをいきなりぽいっと放り込んで二世代ぐらい先の世界を実現する異世界感がヤバい👽」「スゴさのレベルが😆」「亜空間に連れてかれそう😆

「普通の発想ならプロダクトにまず投入しないような技術をぽいっとリリースするGoogleの底力はやっぱスゴくて、GCPじゃないとできないことが結構多いってその人も言ってましたね」「ふ〜む」「AWSは既存のパラダイムで構築されている分、それに縛られることも多かったりしますし☺」「GCPは3年前にお遊びで使ってみただけですけど相当世界変わってそう😅

「KubernetesはGoogleがオープンソースにしたことで今日の隆盛がありますけど、当然ながらすべてをオープンソースにしたわけではない」「たしかに」「オープンになったのはせいぜいインターフェイス周りですし、もちろんそれで問題なく使えますけど、パフォーマンスやコストに直結する部分はそれよりも下のレイヤにあるわけで、Kubernetesのクラスタを動かすネットワークレイヤとか時刻同期みたいな部分にとんでもない謎技術が集中しているはず💪」「ほぇ〜😳」「だから自分はKubernetesを使うならGKEにすべきって思うんですよ😆」「わかります」「Kubernetesのようなものを自前でホスティングするなんてもってのほか🤣」「そうでしょうね🤣

⚓マルチDB向けのrakeタスクを追加

今回の変更で、シングルデータベースに加えて以下のようなコマンドも使えるようになった。
同PRより大意

    rails db:schema:dump
    rails db:schema:dump:primary
    rails db:schema:dump:animals
    rails db:schema:load
    rails db:schema:load:primary
    rails db:schema:load:animals
    rails db:structure:dump
    rails db:structure:dump:primary
    rails db:structure:dump:animals
    rails db:structure:load
    rails db:structure:load:primary
    rails db:structure:load:animals
    rails db:test:prepare
    rails db:test:prepare:primary
    rails db:test:prepare:animals

つっつきボイス:「公式情報のPRリンクが間違ってたので探しちゃいました😆

⚓each_with_objectindex_byindex_withに置き換えた


つっつきボイス:「このプルリクちょっと面白かったです😋」「each_with_objectはRubyのメソッドで、index_なんちゃらはRailsのメソッドでした」「ほほぅ😋」「後者の方が短く書けて速いみたいです」「index_withって知らなかったけど、index_byはたま〜に使ってた」

参考: Enumerable#each_with_object (Ruby 2.7.0 リファレンスマニュアル)
参考: index_by — Enumerable
参考: index_with — Enumerable

# actionview/lib/action_view/renderer/partial_renderer/collection_caching.rb#L92
-     def fetch_or_cache_partial(cached_partials, template, order_by:)
-       order_by.each_with_object({}) do |cache_key, hash|
-           hash[cache_key] =
-             if content = cached_partials[cache_key]
-               build_rendered_template(content, template)
-             else
-               yield.tap do |rendered_partial|
-                 collection_cache.write(cache_key, rendered_partial.body)
-               end
-             end
+       order_by.index_with do |cache_key|
+         if content = cached_partials[cache_key]
+           build_rendered_template(content, template)
+         else
+           yield.tap do |rendered_partial|
+             collection_cache.write(cache_key, rendered_partial.body)
+           end
          end
+       end
      end

参考: Enumerable#index_by() が標準で欲しい - kなんとかの日記 — 2008年の記事です

index_byがRubyにも欲しいというむか〜しの記事↑を見つけました」「たしかにRubyに標準で入ってもおかしくなさそう☺」「どゆこと?」「index_byは以下で言うとUser.find(:all)したものをindex_byすると、user.idがキーになって、それに該当するユーザーのオブジェクトがその先にいるという」「なるほど、ブロックの値をキーにしたハッシュを取れるのか!まさしくindex_byという名前にふさわしい👍」「たまにこういうのを使いたくなりますね😋

# kwatch.hatenadiary.orgより
module Enumerable
  def index_by()   # 名前は to_hash のほうが好み
    hash = {}
    each do |item|
      key = yield(item)
      hash[key] = item
    end
    return hash
  end
end

## example: キーが user id, 値が User オブジェクトであるような Hash を作る
hash = User.find(:all).index_by {|user| user.id }

「まあうかつにallで取ると死にそうだけど😆」「😆」「find(:all)という今は使わないむか〜し昔の書き方をしていた頃からindex_byがあったのがよくわかった☺」「2008年!」

「そしてindex_withはRails 6から入ったのか(ウォッチ20180608)」「むむ、index_withはブロックの中で評価した式がハッシュの値になって、評価する前のキーがハッシュのキーになるってことか!😳」「そういうことか〜!😳」「ちょっとややこしい😅

# activerecord/lib/active_record/associations/preloader.rb#L175
          def records_by_owner
-           @records_by_owner ||= owners.each_with_object({}) do |owner, result|
-             result[owner] = Array(owner.association(reflection.name).target)
+           @records_by_owner ||= owners.index_with do |owner|
+             Array(owner.association(reflection.name).target)
            end
          end

「つまりたとえば配列Aと配列Bがあったとして、index_withでブロックの中でupcaseかけたとすると、元のlowercaseの方がハッシュのキーになって、upcaseかけた結果がハッシュの値になるという感じ」「凄まじいメソッド😆」「こういうのを使いたい瞬間があるのか〜😆

「ざっくりまとめるとindex_withはハッシュのに影響して、index_byはハッシュのキーに影響するのが違いということかな」「逆転の関係🙃」「オプション引数をこねこねしたくなると欲しいヤツ」「知ってたら使いたくなるメソッドですね」「人の書いたコードで見ると一瞬考えちゃいそうだけど😆」「書いた人以外はすぐわからないところはRubyのtapと似てるかも😆

参考: Object#tap (Ruby 2.7.0 リファレンスマニュアル)

「このプルリクを投げた人はRails用のcopを書いたついでに修正をかけたそうで、rubocop-railsにもcopが反映されました↓」「これはとってもいい流れ!😋

⚓ガイドの追加修正

つっつきボイス:「最後はトリビアでガイドの修正: 1つめはパラレルトランザクションのテスト方法」

パラレルトランザクションをテストする
Railsではどのテストケースも自動的に1つのデータベーストランザクションでラップされ、テスト完了時にロールバックされます。これによってテストの独立性を担保でき、データベースの変更が単独のテストに閉じ込められます。
スレッド内でパラレルトランザクションを実行するコードをテストしたい場合、トランザクションがテストトランザクションの配下でネストしているため、互いにブロックしてしまう可能性があります。
以下のようにテストケースのクラスでself.use_transactional_tests = falseを書くことでトランザクションを無効にできます。

class WorkerTest < ActiveSupport::TestCase
  self.use_transactional_tests = false

  test "parallel transactions" do
    # start some threads that create transactions
  end
end

メモ: トランザクションを無効にしたテストによるデータベースの変更は完了時に自動でロールバックされなくなるため、作成したデータテストをすべてクリーンアップしなければなりません。
testing.mdより大意


「もうひとつはActive StorageガイドのサンプルコードにXSSが発見されたので修正されました」「おっと😆」「要らん変数(${file.name})が埋まってた😆

// direct_uploads.js
addEventListener("direct-upload:initialize", event => {
  const { target, detail } = event
  const { id, file } = detail
  target.insertAdjacentHTML("beforebegin", `
    <div id="direct-upload-${id}" class="direct-upload direct-upload--pending">
      <div id="direct-upload-progress-${id}" class="direct-upload__progress" style="width: 0%"></div>
-     <span class="direct-upload__filename">${file.name}</span>
+     <span class="direct-upload__filename"></span>
    </div>
  `)
  target.previousElementSibling.querySelector(`.direct-upload__filename`).textContent = file.name
})
// (略)

参考: クロスサイトスクリプティング - Wikipedia — XSS

⚓Rails

⚓fakeredis: redisサーバーを立てずに開発テスト(Ruby Weeklyより)

# 同リポジトリより
    require "fakeredis"

    redis = Redis.new

    >> redis.set "foo", "bar"
    => "OK"

    >> redis.get "foo"
    => "bar"

つっつきボイス:「これうれしい人いるかなと思って」「なるほど、redisのfaker: CIでぺろっとredisを動かしたいときに役に立つのかな?」「どうだろう〜?今どきはredis立てちゃうんでは?😆」「そう思う😆」「redis立てるのが面倒な人向けかなと」「そのパターンはありそうですが😆

「redisが欲しいけどredisを立てられないときとか?🤔」「BPSのWebチームで内製したSandStarというGitLab CIサーバー↓だとdocker-composeでredis立てられますけど(実際にはredis動かしてませんが😆)、そういうのがない環境だとredis立ち上がらないので、シングルプロセスでredisのfalerが動かせるのはそれなりに価値があるかもしれませんね☺

GitLab 10.6以降でpushイベントのSlack通知が止まったときの対応方法

⚓Webpackerに乗り換えるべき理由25


つっつきボイス:「25も😆」「さよならSprocketsという文字が見えた😆」「Sprocketsからの乗り換えも含んでるみたい☺

「実際Railsプロジェクトの中でJavaScriptのコードも管理するんだったらWebpackerなのかな〜っていう気はしますけど」「今のRailsではJSの管理が公式にWebpackerになりましたね: 画像やCSSの公式な管理はまだですけど」「でも自分はここ最近のRailsプロジェクトではもうSprockets入れてませんし😎」「Sprocketsって今も入っちゃうんでしたっけ?」「rails new--skip-sprocketsって叩かないと入ってきます😅」「ちなみにSprocketsを殺しても、scaffoldするとapp/assets/ディレクトリ作られちゃいました😅


記事ななめ読み:

  • 乗り換えない理由があるとすれば
    • JSを大して使ってない
    • 時間ない
    • 心の準備がまだ
  • 1. WebpackerこそRailsの未来
  • 2. Sprocketsは死んだ、Sprocketsよ永遠なれ
  • 3. JSをうまく書く方法が変わる
  • 4. ESモジュールのパワーを享受できる
  • 5. $JAVASCRIPT_FRAMEWORKが不要になる
  • 6. 別のファイル構成を利用できる
  • 7. 依存関係の管理がしやすくなる
  • 8. jQueryプラグインとおさらばできる(その気があれば)
  • 9. ES2015以降の構文をES5+Babelにコンパイルできる
  • 10. エクスペリメンタル機能もその気になれば使える
  • 11. 特定のブラウザバージョンを対象にできる
  • 12. 新しいブラウザAPIのポリフィルが使える
  • 13. TypeScriptが使える❤
  • 14. 新しい強力なツールの封印が解かれる
  • 15. ソースコードをプログラム的に変更できる
  • 16. require_tree的なこともできる
  • 17. コードの自動静的分割
  • 18. コードの自動動的分割
  • 19. 最新のCSSフレームワークが使える
  • 20. アセットコンパイルがRailsのdevelopmentサーバーの外に出る
  • 21. development環境でページをリロードせずにコードを更新できる
  • 22. source mapオプションが使える
  • 23. パフォーマンスバジェットを実践できる
  • 24. バンドルの中身をチェックできる
  • 25. tree-shaking(不要なコードをビルドから削除する機能)が使える

参考: Google Developers Japan: パフォーマンスバジェットのご紹介 - ウェブパフォーマンスのための予算管理


「お、そろそろゲームにログインする時間なのでおいとまします😆」「😆」「お疲れさまでした〜👋

今回のウォッチつっつき会は、たまたまですが参加者が自分もふくめて全員リモートワーク中だったのでZoomで社内開催しました🔭。Zoomありがたいです😂。なお社内でのつっつき会は途中入場途中退出自由でROM専もOKです。

⚓Let’s Encryptの証明書をRubyで自動更新(RubyFlowより)

# 同記事より
require 'fileutils'
require 'acme-client'
require 'openssl'
apps =
  { 'drgcms' => {
      dir: '/path_to/drgcms', domains: %w[www.drgcms.org tulips.drgcms.org] }
  }

client_key = OpenSSL::PKey::RSA.new( File.read('lets-encrypt.key') )
client = Acme::Client.new(private_key: client_key, directory: 'https://acme-v02.api.letsencrypt.org/directory')

apps.each  do| app, domains |
  p '',"Renewing APP: #{app}"
  order = client.new_order(identifiers: domains[:domains])
  order.authorizations.each do |authorization|
    p ['validating', authorization.domain]
    challenge = authorization.http

    # write challange data to challenge.filename
    FileUtils.mkdir_p( File.join( domains[:dir], 'public', File.dirname( challenge.filename ) ) )
    File.write( File.join( domains[:dir], 'public', challenge.filename), challenge.file_content )

    # validate single domain
    challenge.request_validation
    while challenge.status == 'pending'
      p ['challenge.status', challenge.status]
      sleep(2)
      challenge.reload
    end
    p challenge.status # => 'valid'
  end

  # get certificate
  private_key = OpenSSL::PKey::RSA.new(4096)
  csr         = Acme::Client::CertificateRequest.new(private_key: private_key, names: domains[:domains])
  order.finalize(csr: csr)
  while order.status == 'processing'
    sleep(1)
    challenge.reload
  end
  # save certificate and private key
  if (certificate = order.certificate)
    File.write("#{app}-pkey.pem", private_key.to_pem )
    File.write("#{app}-cert.pem", certificate)
  else
    raise "Error retrieving certificate for application #{app}. Certificate was empty."
  end
end

つっつきボイス:「ACMEv1とかv2って初めて見ました」「これはLet’s Encryptの更新プロトコルです☺」「おぉ、それ用のgemもあるみたいですね」「自分はLet’s Encryptのコマンドで十分ですけど、Rubyでやりたい人もいるんでしょう😆

⚓CarrierWave::Uploader::Baseをうっかり継承しないようにする方法(Ruby Weeklyより)


つっつきボイス:「Uploader::Baseを継承するとすぐに気づかないバグになるみたいですね」「CarrierWaveみたいなでかいライブラリにこういうことするのってあんまりない気がしますけど😆

# 同記事より
module CarrierWave
  module Uploader
    module YourAppName
      class MustInheritFromApplicationUploaderError < StandardError; end
      def initialize(model = nil, mounted_as = nil)
        # これがraiseされると、適用されたアップローダがApplicationUploaderを
        # 継承する必要があることを思い出させてくれる
        # このオブジェクトモデルのファイルではアップローダーを指定していないことがあった
        unless is_a?(ApplicationUploader)
          raise MustInheritFromApplicationUploaderError, "Did you forget to specify an uploader?"
        end
        super
      end
    end
  end
end

「お、上のコードを見るとたしかに直接の継承を明示的に禁止するコードになってる: CarrierWave本体じゃなくてApplicationUploaderを継承せいということね🧐」「MustInheritFromApplicationUploaderErrorってスゴい名前😆」「今のRailsもActiveRecord::BaseではなくApplicationRecordを継承するようになってますし☺


前編は以上です。

おたより発掘

バックナンバー(2020年度第1四半期)

週刊Railsウォッチ(20200303後編)Ruby 2.7で引数のruby2_keywordsフラグを確認する、fake_apiでAPIプロトタイプ、groupdateで日付をグルーピングほか

今週の主なニュースソース

ソースの表記されていない項目は独自ルート(TwitterやはてブやRSSやruby-jp Slackなど)です。

Rails公式ニュース

Ruby Weekly

RubyFlow

160928_1638_XvIP4h

週刊Railsウォッチ(20200310後編)Flutter+Firebaseでモバイルアプリ開発、命名7つの鉄則、Rubyバージョンを切り替えるchruby gemほか

$
0
0

こんにちは、hachi8833です。こちらのサイトの評判がめちゃいいですね。


stopcovid19.metro.tokyo.lg.jpより

以下はつっつき後に見つけたツイートです。尊敬するオードリー・タン氏❤が対策サイトにプルリク投げてくれたとは😂🎉。GitHubで👍がこんなにたくさん付いてるのを見たのは初めてかもしれません。

  • 各記事冒頭には⚓でパーマリンクを置いてあります: 社内やTwitterでの議論などにどうぞ
  • 「つっつきボイス」はRailsウォッチ公開前ドラフトを(鍋のように)社内有志でつっついたときの会話の再構成です👄
  • 毎月第一木曜日に「公開つっつき会」を開催しています: お気軽にご応募ください

⚓Ruby

⚓chruby: rbenvのライバル登場か(Ruby Weeklyより)

# 同リポジトリより
$ chruby 1.9.3
$ chruby
 * ruby-1.9.3-p392
   jruby-1.7.0
   rubinius-2.0.0-rc1
$ echo $PATH
/home/hal/.gem/ruby/1.9.3/bin:/opt/rubies/ruby-1.9.3-p392/lib/ruby/gems/1.9.1/bin:/opt/rubies/ruby-1.9.3-p392/bin:/usr/lib64/qt-3.3/bin:/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/home/hal/bin:/home/hal/bin
$ gem env
RubyGems Environment:
  - RUBYGEMS VERSION: 1.8.23
  - RUBY VERSION: 1.9.3 (2013-02-22 patchlevel 392) [x86_64-linux]
  - INSTALLATION DIRECTORY: /home/hal/.gem/ruby/1.9.3
  - RUBY EXECUTABLE: /opt/rubies/ruby-1.9.3-p392/bin/ruby
  - EXECUTABLE DIRECTORY: /home/hal/.gem/ruby/1.9.3/bin
  - RUBYGEMS PLATFORMS:
    - ruby
    - x86_64-linux
  - GEM PATHS:
     - /home/hal/.gem/ruby/1.9.3
     - /opt/rubies/ruby-1.9.3-p392/lib/ruby/gems/1.9.1
  - GEM CONFIGURATION:
     - :update_sources => true
     - :verbose => true
     - :benchmark => false
     - :backtrace => false
     - :bulk_threshold => 1000
     - "gem" => "--no-rdoc"
  - REMOTE SOURCES:
     - http://rubygems.org/

つっつきボイス:「rbenv的なchruby、なるほど: 今のところrbenvで間に合ってますけど☺」「そういえば最近はもっぱらrbenv使ってるのでrvm使わなくなりました」「rvmは少々お行儀が悪かったんですよね〜😢」「chrubyはcdコマンドにフックかけてないってありますね」「そうそう、rvmはcdにフックかけてた気がする: rbenvはどうだったかな〜🤔」「忘れた頃につっかかったりしそうですね😅」「謎の理由でrvmが死んだことはあった😇

⚓Symbol#to_sの挙動


つっつきボイス:「Noah Gibbsさんの記事で、とりあえず手元で一次訳してみました(近日公開予定)」(眺めてみる)

「Ruby 2.7 previewでto_sがfrozen stringを返すように変更してみたら、思った以上に影響が大きそうだったので最終の2.7には入らなかったという話でした」「ははぁ、これはありそう」「#16150でも"foo".freeze.to_s.frozen?とかいろいろやってみてるし😆」「特に元がstringだった場合に元と同じstringを返すので、改変すると元のも変わっちゃったりすることがあったようです」「object_idが同じになっちゃうんでしょうね🤔

「普通に考えればto_sがfrozen stringを返すのは正しそうなんですけどね」「いろんな使われ方されてると難しいんでしょうね😅

⚓命名7つの鉄則


つっつきボイス:「記事のドメイン名がnamingthings.coですって😆」「ネーミングしか興味なさそう😆

「PronounceabilityとかSearchabilityって、ググラビリティみたいな造語の匂いがしますね」「Austerityって何だろ?🤔」「辞書的には『謹厳な』みたいな感じですけど、ニュアンスとしては『ふざけない』『ひねらない』という雰囲気かなと」「はぁ〜なるほど、一部の人間にしか理解できないようなジョークを混ぜたりすると本質とか意図がぼやけちゃうよね、という主張か」「temporary conceptsを使うなというのもそれっぽいですね」

「don’t be cleverのcleverはネガティブなニュアンスでしょうね😆」「cleverは割と『小賢しい』『猪口才な』という匂いになります😆」「教養を盛り込みすぎた挙げ句、知らない人に伝わらなかったら元も子もないというか🧐」「『これがわかるかねキミ😎』みたいな😆」「そういえば日本ではそういう意味でのcleverすぎる変数名って見ない気がする🤔」「この記事なかなかよさそう👍」「やった😋


記事ななめ読み:

  • 一貫性(consistency)
  • 理解のしやすさ(understandability)
  • 特定性(specificity) — あまりに特定しすぎてもいかん
  • 長すぎず短すぎない(brevity)
  • 検索しやすさ(searchability)
  • 発音のしやすさ(pronounceability)
  • 素直さ(austerity)

⚓その他Ruby


つっつきボイス:「『RubyをWindowsでまともに動かす』ということをそもそもしたくない😆」「😆」「WSLでも結局Windows Subsystem for Linuxで動いてるのでWindowsなのか?というのは疑問ではある」「それLinuxでは感😆」「そういえばこんなツイートも↓」

「ああこれ、自分も大学でRubyInstaller使ってWindowsでRails教えてましたけど、とにかくgemが軒並み動かなくなります😇」「あ〜😳」「Rails本体はぎりぎり動くんですけど、サードパーティgemがたいてい動かなくて😭: pgとかmysql2みたいなアダプタを使おうとするだけでdllを入れないといけないし、SQLite使おうとしてもSQLiteやdllをインストールするところから始めないといけないとか」「何にもできませんね😇」「か、悲しい😅」「これじゃつらすぎですよもう😭

参考: WindowsでRailsTutorialするときに気をつけること - Qiita

⚓DB

⚓Joe Bot: PostgreSQLクエリ最適化をアシストするAI(Postgres Weeklyより)


postgres.aiより


つっつきボイス:「クエリ最適化アシスタント」「普通にCIに常駐するようなボットっぽいですけど」「クエリオプティマイザにシナリオを何回も流し込んで…みたいなのは昔からあるけど、これは外からやるのかな?🤔

(アニメーションGIFのデモ画像を見て)「このJoe Botくんが『こうしてみたらどう?』って教えてくれて、Slackbot形式でやりとりができるというものみたい😋」「おぉ〜」「PostgreSQLの内蔵クエリオプティマイザを改善するというよりは、人間が書くSQL文のオプティマイズをサポートするボットでしょうね☺」「あったら使うかなどうかな〜、DBAな人ならうれしいかも」「これは遅いかもみたいな雰囲気だけでやりとりするんじゃなくて、エビデンスベースでやりとりできて記録がSlackに残るのはよさそう👍

⚓クラウド/コンテナ/インフラ/Linux/Serverless

⚓CloudWatchの改善


つっつきボイス:「お〜複数アラーム組み合わせができるようになってる😋」「今までは単品のアラームしかできなかったんですね」「元々CloudWatchはあまり凝ったことはできませんし、sensing intervalとかも融通が効かないし😆」「ははぁ」

babaさんとも話したことありますけど、CloudWatchを高度な監視のために積極的に使う理由ってあんまりなくて、あるとすればAWSにビルトインされていて何のセットアップもしないで使えるってことぐらい😆」「ないよりはマシなぐらいでしょうか?」「同じものを自分で立てるのはしんどいので、まったくないと困るけど、でも不便😢

「とは言え、複数アラームを組み合わせて条件付けして『こういうときは無視していい』とかできるようになったのは割とありがたい👍

参考: CloudWatch 複合アラームが利用可能になりました!! | Developers.IO

⚓KubernetesのHelmとは

つっつきボイス:「Helmっていうとemacsとかで入力支援するツールを思い出す😆」「VimでもHelm使えるのかな?」「名前ぐらいは聞いたことあるかも(Vimmerなの❤)」「あとはzshとかでシェルを超リッチにしてる人たちが使ってそう」

参考: 初心者〜初級者のためのEmacs-Helm事始め : 前編 - Qiita

「でさくらの記事のHelmは、ほんとにKubernetesのマネージャか」「くばねて、触ってます?」「触ろうと思いつつまだですね〜😆」「Kubernetes入門みたいな本買って読んでるけど、手を動かしながらじゃないとなかなかわからんな〜📕

「ところでKubernetesの対抗馬って今のところなさそうですね」「まあ強いて言うならAWSのECSとか」「たぶんですけど、ECSはくばねてのような方向性にはならないだろうという確信が自分の中では得られました😆」「ECSはコンテナオーケストレーションの先駆けですから😆」「あとDocker Swarm😆」「Docker Swarm!たぶんKubernetesにはもう勝てないでしょう⚔

「今だとどんなオルタナティブがあるんだろか?」「この辺の記事↓かな〜」「PrometheusにDocker Composeに…」「Prometheusは違うのでは😆」「違う気がする😆

参考: 9 Best Orchestration Open Source Docker Tools in 2018

「こっちも見てみよう↓」「ああMinikubeもあるな〜」「Minikubeは割とすんなり入りますね😋: でもKubernetes使いたいレベルの案件だったらKubernetes動かさないと意味ないかもって自分は思いますけど😆」「😆」「そうそうRancherってのもあった」

参考: 14 Best Docker orchestration tools as of 2020 - Slant

「ざっと眺めた感じ、とりあえずKubernetes勉強しとくのがいいのかな〜🤔」「自分たちはDocker Composeで収まる範囲でやってることがほとんどだけど、Kubernetesが必要になりそうなレベルになったら代用品じゃなくてKubernetesでやらないとキツいかも☺

「今更ですけど、Docker Composeってオーケストレーションツールなんですね」「あれは確かにオーケストレーションしてますね🧐: シングルホストだけど」「気づかずに使ってました…😅

⚓その他クラウド

つっつきボイス:「そうそう、これはWSL2の話」「へ〜、Windows HomeでDocker Desktopが使える?」「順番としてはWSL2がWindows Homeに来たからそれを使ってDocker Desktopが動くようになるということかと」「WSL2ってヤツがWindows Professionalじゃなくても動くようになった?」「Hyper-Vのサブセット的なWSL2がWindows Homeでも動くようになるということですね☺」「Professional版とは何ぞや感😆」「まあHyper-Vの機能がすべてWSL2で使えるわけではないらしいので」「あ、そうなのか😅」「Windows Homeを最新に上げれば使えるのかな?まだ上げてないからわからんけど😆

参考: WSL2で劇的に変わるあなたのWebアプリケーション開発環境【その2:導入編】 | SIOS Tech. Lab
参考: Windows 10 での Hyper-V の有効化 | Microsoft Docs

⚓JavaScript

⚓FlutterとFirebase


flutter.devより



firebase.google.comより


つっつきボイス:「FlutterとFirebaseをつい取り違えそうな自分でした😅」「たぶん今の世の中で、iOSアプリとAndroidアプリを最速で公開する最もお手軽な方法といえば、FlutterとFirebaseの組み合わせって感じですかね〜☺」「でしょうね: 何しろサーバーサイドも含めて全部お膳立てされてますし」「後はDartさえ書けば🤣」「それな🤣」「ダ〜トか〜😅

参考: 【Flutter入門】開発言語Dartの特徴を解説するよ | TECHRISE

「あと同じ記事の中で、BLoCとかいう設計方法についてのリンク↓があったので開いてみたら『自分はもうBLoC使ってません』と書いてありました😇」「今はいろいろ過渡期ですから😆

参考: FlutterのBLoC(Business Logic Component)のライフサイクルを正確に管理して提供するbloc_providerパッケージの解説

「ともあれ、個人でモバイルアプリをとりあえず作ってみたいというレベルであれば、FlutterとFirebaseの組み合わせは全然ありだと思います👍」「よ〜し😋」「とにかくこういうのはぱぱっと作って運用してみることですね: 後は投資を集めて会社を大きくするなり売却するなりしてもいいし、運用が回ってくればエンジニアも集まってくるので、じゃあロックインされた部分を切り離そうかみたいなこともできるようになるでしょうし」「ですね☺」「スタートにはこういうのがいいと思います❤

「少なくともReact Nativeでやるよりはよさそう😆」「😆」「React Nativeみたいなゴツいフレームワークは、計画がみっちり立っていてがっつり投資集めて作ることが決まってるような案件でないと、重たそう😆」「React Nativeでないとできないような内容なら別だけど😆


reactnative.devより

追いかけボイス: 「React NativeとFlutterどっちがゴツいといったらどっちも十分ごついと思う人です」(社内Flutter勢より)

⚓CSS/HTML/フロントエンド/テスト

⚓Googleが検索をモバイルファーストへ


つっつきボイス:「お、まだ終わってなかった?」「前からやるぞって宣言してましたけど、まだだったとは」「とりあえずオレオレRailsアプリのモバイル対応がまだ不十分なので気になってました😅

⚓その他フロントエンド

⚓言語・ツール

⚓文字について


つっつきボイス:「昔からあるサイトですが、古い記事もちゃんと更新されているのがいいなと思って」「出たな異体字セレクタ」「DTPわかんない😇」「DTPの世界はWebと背景や文脈が異なるので、Web屋が気軽に手を出しにくいですね☺

⚓その他言語


つっつきボイス:「自分は見かけなかったな〜これ」「理由があるのかないのかもわからん😆」「システム屋がガチガチの要求受けて作ったらこうなりそう」「縦は絶対揃えろとか…ってこれプロポーショナルだから揃ってないし😆

⚓その他

⚓読み方も大事

情報系の用語は英語由来のワードが多いですし、日本語でも「引数」のように日常生活ではなかなか使わない単語が登場します。独学だとどうしても自己流の読み方となってしまい、実際に就職したりプログラミング系のコミュニティに参加したりするときにコミュニケーションのハードルになってしまう可能性があります。(恥ずかしながら、私自身も中学生時代は「引数」を「いんすう」と読んでしまっていた苦い経験があります。)
そのため、N予備校では可能な限り、初出の単語や英単語には括弧書きで以下の画像のように読み方を付けています。
同記事より


つっつきボイス:「ドワンゴのN予備校頑張ってるな〜」「N予備校も今教材を無料開放してるみたいですね」「開校した当初は一部の人たちには注目されつつも一般からは遠巻きに見られていた感じでしたけど、今回の休校騒ぎで評判が変わってくるかも🤔」「私ちょっと興味ありました😋


nnn.ed.nicoより

⚓電子書籍期間限定無償公開の嵐


つっつきボイス:「こちらも続いて無償公開のエントリです」「ko1さんの『Rubyのウラガワ』が期間限定で無償で読める!」

「無償コンテンツ公開はありがたいけど、世の保護者が今最も欲しいのは子どもから目を離さず世話をしてくれる大人でしょうね」「そうなんですよね😢」「自分もマンションの自治会で『家で仕事してるならちょっと見といてもらうことってできますか?』って聞かれましたし😅

「ついでといいますか、以下はZoomで参加可能なイベントです」「Zoomで400人!」「やれるもんなんですね」「Zoomのウェビナーっていう機能を使うと大人数で参加できるんですよ」「へぇ〜!😳」「ただし別料金💰」「パネリストも100人いると排他で話すの大変そう…」


追いかけボイス: 3/19の銀座Rails#19もZoomでのリモート参加形式になったそうです。「出張!Railsウォッチ」もあります。

⚓番外

⚓「密集恐怖症」って呼んでた


つっつきボイス:「ああこれ😆、なんちゃらフォビア系でこういうのありますね」「自分は昔から適当に密集恐怖症って呼んでました: こういう集合体恐怖症のケってあります?」「蓮コラ的な?自分はそんなにないかな😆」「自分も特にないんですが苦手な人はとことん苦手なのを何度も目にしました」「こういう根源的な恐怖は生理的なものだから理由とか関係ないでしょうし」「恐怖って脳のかなり下の層からきてるみたいですし」

参考: トライポフォビア - Wikipedia


後編は以上です。

バックナンバー(2020年度第1四半期)

週刊Railsウォッチ(20200303後編)Ruby 2.7で引数のruby2_keywordsフラグを確認する、fake_apiでAPIプロトタイプ、groupdateで日付をグルーピングほか

今週の主なニュースソース

ソースの表記されていない項目は独自ルート(TwitterやはてブやRSSやruby-jp Slackなど)です。

Rails公式ニュース

Ruby Weekly

RubyFlow

160928_1638_XvIP4h

Postgres Weekly

postgres_weekly_banner

Rails: Active Recordメソッドのパフォーマンス改善とN+1問題の克服(翻訳)

$
0
0

概要

原著者の許諾を得て翻訳・公開いたします。

日本語タイトルは内容に即したものにしました。

Rails: Active Recordメソッドのパフォーマンス改善とN+1問題の克服(翻訳)

Railsアプリケーションのパフォーマンスは多くの変数に依存していますが、その中のひとつは、アクション完了のために実行されるクエリ数です。データベース呼び出しの回数が少ないほどメモリアロケーションが削減され、ひいては操作完了に要する時間も削減できます。

そうした問題のひとつがN+1クエリ問題です。projectsテーブルとcommitsテーブルがあるときに、projectを2件読み込んでからそれらのprojectのすべてのcommitsを読み込むと、projectを読み込むクエリが1件と、projectごとにcommitsをフェッチするクエリがN件生成されます。追加操作は可換なので、1+NともN+1とも書けます。

# projects = Project.where(id: [1, 2])
> SELECT "projects".* FROM "projects" WHERE "projects"."id" IN (1, 2)

# projects.map { |project| project.commits }
> SELECT "commits".* FROM "commits" WHERE "commits"."project_id" = $1  [["project_id", 1]]
> SELECT "commits".* FROM "commits" WHERE "commits"."project_id" = $1  [["project_id", 2]]
....

N+1クエリを解決する一般的な方法は、includesを用いて関連付けをeager loadingすることです。

includesのしくみ

includespreloadeager_loadのショートハンドです。

preloadは2つのクエリを開始します。最初のクエリはメインのモデルをフェッチし、次のクエリは関連付けられているモデルをフェッチします。eager_loadはLEFT JOINを行い、メインのモデルと関連付けられているモデルの両方をフェッチする1つのクエリを開始します。

preloadはメモリ使用量の点でeager_loadよりずっと有利です。eager_loadで使われる他の戦略を強制しない限り、preloadActive Recordのデフォルト戦略です。理由についてはActiveRecord::Associations::Preloaderクラスに記載されています。

rubydocにある昔のRails 4.1.7のPreloaderドキュメントはもっと参考になります。

Authorモデルに’name’カラムと’age’カラム、Bookモデルに’name’カラムと’sales’カラムがあるとします。Active Recordはこの戦略に基づいて、以下のように1件のクエリで1件のauthorとその全bookをすべて取り出そうとします。

SELECT * FROM authors
LEFT OUTER JOIN books ON authors.id = books.author_id
WHERE authors.name = 'Ken Akamatsu'

しかしこれでは余分なデータを含んだ行が大量に返される可能性があります。最初の行が返った時点で、Authorオブジェクトをインスタンス化するのに十分なデータがあります。以後の多数の行で有用なのは、JOINされた’books’テーブルのデータだけあり、JOINされた’authors’のデータは冗長であるにもかかわらずメモリとCPU時間を食います。この問題はeager loadingのレベルが増すに連れてたちまち悪化します(Active Recordがアソシエーションの関連付けもeager loadingする)。

preloadのしくみ

preloadは、preloadするすべての関連付けをループして、関連付け1件ごとにクエリを作成します。

# Project.preload(:commits)
> Project Load (1.8ms)  SELECT "projects".* FROM "projects"
> Commit Load (128.3ms)  SELECT "commits".* FROM "commits" WHERE "commits"."project_id" IN ($1, $2) [["project_id", 1], ["project_id", 2]]

eager_loadのしくみ

eager_loadは、LEFT OUTER JOINでレコードをeager loadingします。JOINのRIGHT側とマッチするかどうかにかかわらずJOINのLEFT側のすべての行を保持するのではありません。

eager_loadJoinDependencyの内部に実装されています。返される結果では、LEFT側のテーブルの各レコードについて1行しか含まれません。

# Project.eager_load(:commits)
> SELECT "projects"."id" AS t0_r0, "projects"."name" AS t0_r1, "projects"."org" AS t0_r2, "projects"."connected_branch" AS t0_r3,
  "projects"."enabled_by" AS t0_r4, "projects"."created_at" AS t0_r5, "projects"."updated_at" AS t0_r6, "projects"."permalink" AS t0_r7,
  "projects"."domain" AS t0_r8, "commits"."id" AS t1_r0, "commits"."author" AS t1_r1, "commits"."committer" AS t1_r2, "commits"."message"
  AS t1_r3, "commits"."sha" AS t1_r4, "commits"."parents" AS t1_r5, "commits"."project_id" AS t1_r6, "commits"."created_at" AS t1_r7,
  "commits"."updated_at" AS t1_r8, "commits"."status" AS t1_r9, "commits"."committed_at" AS t1_r10 FROM "projects"
  LEFT OUTER JOIN "commits" ON "commits"."project_id" = "projects"."id"

# Project.includes(:commits).references(:commits).count
# includesにreferencesを追加することでもeager_loadがトリガされる
> ... (上と同じクエリ) ...

eager_loadcountを組み合わせるとDISTINCTキーワードが自動で追加されることに気づいた方もいるでしょう。

このDISTINCTは、eager loadingおよびCOUNT SQL操作が検出されたときにActive Recordによって追加されます。

# Project.eager_load(:commits).count
# 'DISTINCT'が自動で追加されたことがわかる
  SELECT COUNT(DISTINCT "projects"."id") FROM "projects" LEFT OUTER JOIN "commits" ON "commits"."project_id" = "projects"."id"
> 2

関連付けのサブセットをeager loadingする

場合によっては、キューに乗ったすべてのcommitなどのデータで、データのサブセットだけをeager loadingしたいことがあります。これについては後述の「メソッドをスコープにチェインしてしまう」や「スコープはeager loadingできないので関連付けに変換する」で解説しています。

動的な条件に基づいてeager loadingする

上のセクションの続きです。フィルタの基準が動的に決定されるデータのサブセットをeager loadingしたいときはどうすればよいでしょうか。たとえば、現在ログイン中のユーザーのcommitをすべてeager loadingしたいとします。それには、ActiveRecord::Associations::PreloaderというドキュメントのないAPIを使う必要があります。

# projects = Project.all.to_a
> SELECT "projects".* FROM "projects"

# ActiveRecord::Associations::Preloader.new.preload(
  projects,
  :commits,
  Commit.where("author ->> 'email' = ?", current_user.email)
  )
> SELECT "commits".* FROM "commits" WHERE (author ->> 'email' = > 'current_user_email')

メモ: この機能は今のところRails 6では動かなくなっています(#36638

集計クエリをeager loadingする

Railsには組み込みのカウンタキャッシュがあり、COUNT集計関数の問題解決に利用できます。カウンタキャッシュを追加してメンテしたくないのであれば、activerecord-precounterなどのgemを利用できます。

その他のAVGSUMなどの集計関数については、eager_group gemを利用できます。

また、余分な依存関係を追加せずに解決する方法もあります。

# projects.map {|project| project.commits.count}
> SELECT COUNT(*) FROM "commits" WHERE "commits"."project_id" = $1  [["project_id", 1]]
> SELECT COUNT(*) FROM "commits" WHERE "commits"."project_id" = $1  [["project_id", 2]]

# Commit.where(project_id: projects).group(:project_id).count
> SELECT COUNT(*) AS count_all, "commits"."project_id" AS
  commits_project_id FROM "commits" WHERE "commits"."project_id"
  IN (SELECT "projects"."id" FROM "projects" WHERE "projects"."id" IN ($1, $2))
  GROUP BY "commits"."project_id"  [["id", 1], ["id", 2]]

# Commit.where(project_id: projects).group(:project_id).sum(:comments)
> SELECT SUM(comments) AS sum_comments, "commits"."project_id" AS
  commits_project_id FROM "commits" WHERE "commits"."project_id"
  IN (SELECT "projects"."id" FROM "projects" WHERE "projects"."id" IN ($1, $2))
  GROUP BY "commits"."project_id"  [["id", 1], ["id", 2]]

eager loadingのよくある間違い

⚓1. 誤ったオーナーで関連付けがeager loadingされる

次の例をご覧ください。

class Project < ApplicationRecord
  has_many :commits
  has_many :posts, through: :commits
end

class Commit < ApplicationRecord
  belongs_to :project
  has_one :post
end

class Post < ApplicationRecord
  belongs_to :commit
end

Projectでpostsをeager loadingしたのにcommitでpostを呼び出すと、データベースへのクエリが発生します。このposts(ターゲット)はproject(オーナー)で読み込まれるので、このprojectで読み込まれたpostsしかフェッチできなくなります。

# projects = Project.includes(:commits, :posts)
> SELECT "projects".* FROM "projects"
> SELECT "commits".* FROM "commits" WHERE "commits"."project_id" IN ($1, $2, ...)
> SELECT "posts".* FROM "posts" WHERE "posts"."commit_id" IN ($1, $2, ...)

# projects.first.commits.first.post
>

postを個別のcommitで呼ぶと、そのcommitでeager loadingしなければならなくなります。

Project.includes(commits: :post)
> ...

projects.first.commits.first.post

⚓2. selectではなくpluckを使ってしまう

以下の例を見てみましょう。指定の組織にあるprojectのcommitをすべて読み込みたいとします。

# Commit.where(project_id: Project.where(org: 'rails').pluck(:id))
> SELECT "projects"."id" FROM "projects" WHERE "projects"."org" = $1  [["org", "rails"]]
> SELECT "commits".* FROM "commits" WHERE "commits"."project_id" IN ($1, $2)  [["project_id", 1], ["project_id", 2]]

上のコードではクエリが2件発生します。最初のクエリではprojectsのidをSELECTし、次でcommitsをフェッチします。ここでpluckselectに置き換えてみると、サブクエリのおかげでクエリが1件だけになります。

# Commit.where(project_id: Project.where(org: 'rails').select(:id))
> SELECT "commits".* FROM "commits" WHERE "commits"."project_id" IN (SELECT "projects"."id" FROM "projects" WHERE "projects"."org" = $1)  [["org", "rails"]]

メモ: pluckは、行全体ではなく値のサブセットをSELECTしたい場合に使いましょう。

⚓3. メソッドをスコープにチェインしてしまう

あるprojectのすべてのpostsを、読み込み済みのpostsのサブセット(公開済みのpostsのみ、など)に応じて読み込みたい場合、結果セットが小さいのであれば、メモリ上でフィルタすることでデータベースにクエリを2件送信することを回避できます。

projects.posts # DBクエリ1件
projects.posts.published # DBクエリ1件

# 以下ならデータベースへのクエリが1件で済む
projects.posts.select {|p| p.status.eql?('published') }

⚓4. スコープはeager loadingできないので関連付けに変換する

class Commit < ApplicationRecord
  ...

  scope :queued, -> { where(status: :queued) }

  ...
end

projects = Project.includes(:commits)

# キューに乗ったcommitをフェッチするクエリを送信する
projects.commits.queued

commitsをすべてeager loadingするのではなく、キューに乗ったcommits(commitsのサブセット)だけをeager loadingしたい場合は、スコープ付きの関連付けを作成してからeager loadingしなければなりません。

class Project < ApplicationRecord
  ...

  has_many :queued_commits, -> { where(status: :queued) }, class_name: 'Commit'

  ...
end

projects = Project.includes(:queued_commits)
projects.queued_commits

⚓5. プリロードしたデータでexists?を呼んでしまう

exists?を使うと常にデータベースクエリが発生します。データが読み込み済みであれば、レコードが空でないかどうかの確認にはpresent?を使うべきです。別の方法として、存在チェック後にデータが読み込まれることがわかっていれば、単にレコードを読み込んでから存在チェックします。

⚓6. countではなくsizeを使おう

countを使うと常にデータベースクエリが発生します。データが読み込み済みであれば、sizeを呼べば関連付けのサイズを取得できます。常にsizeを使うようにすれば、データベースクエリは関連付けが読み込まれていない場合にのみ発生します。

まとめ

本記事では、よくあるN+1問題を克服するのに使えるActive Recordのメソッドをいくつかご紹介いたしました。シナリオによっては、大量のオブジェクト読み込みやクエリ実行を削減するヒントをRailsに提供できます。Railsアプリケーションのスピードアップについては今後の記事でフォローする予定です。

関連記事

Rails: Bulletで検出されないN+1クエリを解消する

Rails: N+1クエリを「バッチング」で解決するBatchLoader gem(翻訳)

Rails向け高機能カウンタキャッシュ gem ‘counter_culture’ README(翻訳)

Swiftでの文字列置換について

$
0
0

BPSの福岡拠点として一緒にお仕事をさせていただいています、株式会社ウイングドアの岡です。

今回、Swiftでの文字列置換についてご紹介したいと思います。

文字列内でマッチした部分文字列が、正しく置換できるようになることを
目的にやっていきたいと思います。

Swiftで文字列置換させようとしている方へ少しだけでも、
アドバイスになれば、うれしいです。

今回使用したバーションはSwift 5になります。

Rubyでの文字列置換

はじめに、Rubyでの文字列置換を見ていきましょう。
subメソッドを使った方法とgsubメソッドになります。

irb(main):022:0> irb(main):023:0> hogehoge = "ああいうああえお"
=> "ああいうああえお"  
irb(main):026:0> hogehoge.sub("ああ","あか")
=> "あかいうああえお"  

subメソッドはマッチした最初の文字のみを置き換えた状態で文字列を生成して返します。

irb(main):005:0> hogehoge = "ああいうああえお"  
=> "ああいうああえお"  
irb(main):006:0> hogehoge.gsub("ああ","あか")  
=> "あかいうあかえお"  

gsubメソッドはマッチする部分全てを置き換えた状態で文字列を生成して返します。

Swiftでの文字列置換

replaceSubrangeでの方法

後々、何かと便利なのでStringを拡張して実装してみました。

まず、rangeメソッドで文字列中に置換したい文字があるかを
検索し文字列が見つかった場合は、Range型で一致した文字位置が返ってくるので
replaceSubrangeメソッドで返ってきた文字位置に置換後の文字を入れ替えてます。

破壊的メソッドなので使用には注意して使う必要があるかと思います。

extension String {
    func replace(_ from: String,_ to: String) -> String {
        var replacedString = self
        if let range = replacedString.range(of: from) {
            replacedString.replaceSubrange(range, with: to)
        }
        return replacedString
    }
}

では、実際に確認してみましょう。

var hogehoge = "ああいうああえお"  
print(hogehoge.replace("ああ","あか"))  
>  あかいうああえお  

置換自体はできてますが、ヒットした文字の最初だけしか、置換されてないようです。
ヒットした文字が、全て置換されるようにする必要がありますね。

複数の文字を置換できるように変更後

extension String {
    func replace(_ from: String,_ to: String) -> String {
        var replacedString = self

        while((replacedString.range(of: from)) != nil){
            if let range = replacedString.range(of: from) {
                replacedString.replaceSubrange(range, with: to)
            }
        }

        return replacedString
    }
}

ループさせ置換対象の文字がヒットされるまで繰り返すよう変更してみました。
ヒットした文字が、全て置換されるか試してみましょう。

var hogehoge = "ああいうああえお"

print(hogehoge.replace("ああ","あか"))
> あかいうあかえお

ちゃんと置換されてますね。と思ったのですが、
文字列の置換中にrangeでの検索で無限にヒットしている場合が判明。

var hogehoge = "あいうえお"

print(hogehoge.replace("あ", "あか"))
print(hogehoge.replace("あ", "あ"))
> 無限ループしてしまう

これだとダメなので別の方法で解決する必要がありますね。

replacingOccurrencesでの実装

公式ドキュメントを調べていくとreplacingOccurrencesというメソッドでも置換できるという
ことが分かり試しに実装してみました。

同じようにStringを拡張して使いやすいようにしてみました。
replacingOccurrencesは非破壊的メソッドになります。

extension String {
    func replace(_ from: String,_ to: String) -> String {
        var replacedString = self
        replacedString = replacedString.replacingOccurrences(of: from, with: to)

        return replacedString
    }
}

試してみましょう。

var hogehoge = "ああいうああえお"
print(hogehoge.replace("ああ","あか"))
> あかいうあかえお

var hogehoge = "あいうえお"

print(hogehoge.replace("あ", "あか"))
print(hogehoge.replace("あ", "あ"))  
> あかいうえお
> あいうえお

目的通り、複数文字列が置換され、同じ文字があっても正しく置換されました。

まとめ

Swiftを使った文字列置換について紹介しました。
今回は、replacingOccurrencesを使い解決しましたが
用途ごとに、分けて使う必要があるかと思いました。

参考に、公式ドキュメントを貼っておきましたので
使用する際は確認して使ってみてください。


株式会社ウイングドアでは、Ruby on RailsやPHPを活用したwebサービス、webサイト制作を中心に、
スマホアプリや業務系システムなど様々なシステム開発を承っています。

V言語: Goのようにシンプルで、Rustのように小さいバイナリ

$
0
0

こんにちは、hachi8833です。昨年夏に話題になったV言語を半年遅れで知って最近遊んでいます。「主観的には」かなり安定して動く印象です。

なお以下のベンチマークは、そういう結果もあるのねというぐらいに思っていただければと☺

V言語そのものについてはQiitaに半年前のドキュメントの和訳があるのでそちらをどうぞ。READMEのインストール方法も「さほど」難しくありません。

「コード例で学ぶV言語」

V言語を追いかけながら、v-community/v_by_exampleを日本語版に翻訳いたしました。マージありがとうございます!

ざっくり特徴

1. 主にGo言語の影響を強く受けているコンパイラ言語

V言語は当初Go言語で記述されたこともあり、Go言語の特徴の多くを取り入れています(強い型、クラスがない、ループはforのみ、言語レベルのlint、クロスプラットフォーム、goroutineなど)。

現在はC言語固有の部分をごくわずか残している他はほぼすべてV言語自身で記述されていて、Goのコードは残っていません。

Go言語を知ってる人にはとりあえず以下のWikiが参考になると思います。

参考: V for Go developers · vlang/v Wiki

その一方、作者のmedvednikovさんもREADMEにopinionated(独断と偏見の)と記載しているように、Go言語で彼が気に入らなかったであろうと思われる仕様や、Go言語では今更変えられなさそうな仕様をいろいろ変更しています。

特に、変数がデフォルトでイミュータブルかつprivate(変えるにはmutpubを明示的に指定する必要がある)というあたりは好き嫌いが分かれそうなところです。私はpublicみたいな長ったらしいアクセス指定子よりは好きです❤

表記における記号への依存が少なめなところは、どことなくPythonのようでもあります(Pythonの影響については明言されていませんが)。

デフォルトでイミュータブルではありますが、V言語は純粋な関数型言語ではないとのことです。しかし関数型言語からもいくつかアイデアを取り入れていて、個人的にor { }でエラーハンドリングできるところが好きです(Goにこれ欲しい…😢)。

fn main() {
    repo := new_repo()
    // Option 型は`or`ブロックで扱わなければならない
    user := repo.find_user_by_id(10) or {
        return  // `or`ブロックの末尾は`return`か`break`か`continue`でなければならない
    }
    println(user.id) // "10" 
    println(user.name) // "Charles"
}

2. コンパイルが高速、バイナリが小さい

この業界では、速いという言葉はそれだけで耳目を集めがちです。V言語は、少なくともセルフコンパイルは1秒かからず、驚くほど高速です。

コンパイルしてできたバイナリの実行速度は真面目に調べていませんが、まだ最適化を頑張る段階ではないので改良の余地はありそうな雰囲気です。バイナリ実行速度ではGoよりまだ遅いものもあるようです。

Go言語と同じく、デフォルトではワンバイナリになります。オプションを指定することでvlib/を切り離せるので、IoTやOSといった用途も視野に入っています。

Go言語のバイナリはGC(ガベージコレクション)などの機能を内蔵しているので、Hello worldレベルのソースでもコンパイルすると数MBになってしまいますが、V言語はRust的な「GCを使わない」設計を採用していて、vlibが急成長しているVコンパイラ自体のバイナリサイズは1MB未満です。他にもOberonやSwiftなどの影響を受けているそうですが、どこがそうなのか私にはわかりません。

-rwxr-xr-x 1 hachi8833 staff 840K  2 14 14:03 /Users/hachi8833/deve/vlang/v_mac/v

現在でもlibcなどのc言語ライブラリへの依存は少ないのですが、最終的にはlibcなどのライブラリへの依存も完全になくすつもりのようです。

なお現状のV言語はCへのトランスパイラなのでCのコンパイラが必要です。その一方で「いつでもCのコードを生成できる」ことを目標にしているとのことなので、今後ネイティブコンパイルが実現されてもCコードの生成はできるようにしておくそうです。その気になればC経由でWeb Assemblyをビルドすることもできそうですね。

参考: C/C++からWebAssemblyにコンパイルする - WebAssembly | MDN

3. クロスプラットフォームのGUIライブラリ

V言語はもともとVoltというSNSクライアントアプリを書くために作られた言語とのことです(Voltという別の言語もあるのでややこしい😅)。そのためかクロスプラットフォームのGUIライブラリv/uiを最初から備えています。

tcl/tkのようなレガシー感20世紀感とは無縁な、今どきらしいGUIなのがうれしいですね😋

4. 驚異的な開発速度、活発なコミュニティ

V言語の様子を知るにはDiscordを覗いてみるとよいでしょう。土日祝日盆暮れ正月おかまいなしで盛り上がっています。

何しろ発展途上ですので細かいバグつぶしはたくさんあります。近々0.2をリリースする意向のようです。コアメンバーたちが絶えずissueをつぶしていますが、issueもそれを上回る勢いで増えてます。

しかも0.2ではこの期に及んで、今後のメンテをやりやすくするためにVのパーサーをASTベースで完全に書き直すという作業を今年2月の数週間で終えてしまいました😳。道理でパーサーが2つあるわけだ。

目についた課題

以下は記事執筆時点のものです。

1. ドキュメントのキャッチアップ

ドキュメント(docs.md)に載っていない情報がまだあり、最終的にはコミットやソースコメントあたりが頼りです。

特にシンタックスの制約に関する記述がまだ不足しています。

  • 関数名: スネークケースしか許されない(fooはいいがFooはだめ)
  • 変数名: スネークケースしか許されない
  • インターフェース名: PascalCaseしか許されない(CommonDelegatorのような名前)
    • 現在はerで終わる名前しか使えないという制約も課されているが、外される予定とのこと

まだまだ動きが激しいので、しばらく静観することにします。

2. 仕様ファーストではなさそう

これまでV言語を追いかけた限りでは、最初に仕様を固めてから実装するというより、実装先行で仕様が後追いっぽいように見えます(違ってたらすみません)。

何しろ強い人たちなので、ちまちま仕様を書くよりはとにかくエイヤで実装というノリなのかなと思えました。現実にはこうやってできていく言語も多いのでしょう。

しかし現在激しく実装中のv fmtのあたりとかは仕様が頭に入ってる人たちでないと手出しが難しそうですし、どちらかというと緩めのASTの挙動をとりあえずv fmtで無理やり引き締めているような気もします。自分は仕様が気になるたちなので、早いところ仕様が固まってくれるとうれしいです。

3. サードパーティパッケージの安定性

Vで書かれたコードのうち、VそのものとvlibのVコードのみを使うCLIアプリは概ね安定している印象ですが、既存のCライブラリ(JSONやGUIなど)にリンクするアプリには安定してないものもあるようです。

特に、macOSのreadlineはまだ機能が少なく、Linux版ではできているコマンドラインでのショートカット(Ctrl-aで冒頭に移動、Ctrl-eで末尾に移動など)やで履歴を遡る機能が今のところ使えません😭

少なくともmacOSでサードパーティのコードをビルドするには、Command Line Toolsをインストールしておく必要があります。古い場合はアップグレードも必要です。Command Line Toolsのインストール方法はちょくちょく変わるので面倒…😢

参考: macOSでcommand line tools for xcodeとhomebrewのインストール - Qiita

最後に

V言語は発展途上段階なので、今のところ「ただちに何かの業務に役立つ」ものではないと思いますが、言語が急速に成長していく姿をつぶさに観察しているときのライブ感・臨場感が楽しくてたまりません😋

この調子で順調に成長してくれれば、Go言語に似ている分、Go言語の得意なミドルウェアやCLIでの利用が考えられますし、Goがあまり視野に入れていないクロスプラットフォームGUIアプリも期待できます。バイナリが小さいので、Go言語があまり視野に入れていないOS開発やインタプリタ言語の開発にも使えそうです。気の早い人たちはV言語でOSを書き始めています。

関連記事

Goby: Rubyライクな言語(2)Goby言語の全貌を一発で理解できる解説スライドを公開しました!

週刊Railsウォッチ(20200316前編)Webpackerの対抗馬Simpackerはいかが、Hanami::APIは高速性をフィーチャー、N+1と戦うgemほか

$
0
0

こんにちは、hachi8833です。こちらはオンラインイベントや無料コンテンツのまとめサイトだそうです↓。


onlinefesjapan.comより


つっつきボイス:「音楽ライブ系の情報多いですね🎶」「フェスだからいろいろあるかも」「漫画や書籍が無料で読める系も📓」「毎週のようにライブ見に行ってる知り合いも、ここ最近イベント中止が相次いで週末やることなくなったって言ってますし😆」「毎週のようにってスゴい😳」「自分の回りのJリーグファンもみんな気落ちしてますし😭

  • 各記事冒頭には⚓でパーマリンクを置いてあります: 社内やTwitterでの議論などにどうぞ
  • 「つっつきボイス」はRailsウォッチ公開前ドラフトを(鍋のように)社内有志でつっついたときの会話の再構成です👄
  • 毎月第一木曜日に「公開つっつき会」を開催しています: お気軽にご応募ください

⚓Rails: 先週の改修(Rails公式ニュースより)

今回はコミットリストから見繕いました。

⚓perform_enqueued_jobsがリトライしないよう修正

ActiveJob::TestCase#perform_enqueued_jobsは今後リトライしないようになる。
perform_enqueued_jobsをブロックなしで呼び出すと、アダプタは既にキューに入っているジョブを実行するようになった。キューの中にある、後に完了することが決まっているジョブは実行されなくなる。
この変更が影響するのは、perform_enqueued_jobsにブロックを渡さなかった場合に限られる。
Changelogより大意

# activejob/lib/active_job/test_helper.rb#L602
      def jobs_with(jobs, only: nil, except: nil, queue: nil, at: nil)
        validate_option(only: only, except: except)

-       jobs.count do |job|
+       jobs.dup.count do |job|
          job_class = job.fetch(:job)

          if only
            next false unless filter_as_proc(only).call(job)
          elsif except
            next false if filter_as_proc(except).call(job)
          end
          if queue
            next false unless queue.to_s == job.fetch(:queue, job_class.queue_name)
          end
          if at && job[:at]
            next false if job[:at] > at.to_f
          end
          yield job if block_given?
          true
        end
      end

つっつきボイス:「プルリクのreeenqueueってeが一個多い気がする😆」「ホントだ😆」「そういえばGitHubにはスペルチェッカーありませんね☺

問題
perform_enqueued_jobsをブロック無しで実行すると、リトライメカニズムで自分自身を再度キューに乗せるジョブが即座に動き出してしまう。
この動作はperform_enqueued_jobsにブロックを渡すのであれば理解できる。
しかし自分が期待するのは、perform_enqueued_jobsにブロックを渡さない場合は、後でキューに乗るジョブを実行するのではなく、既にキューに乗っているジョブを実行することである。
解決法
ジョブの配列をdupして今後改変されないようにする。
同PRより大意

「あーよくある操作かも: ジョブを実行した後にそのジョブリストを取り出すのか、実行する前にジョブリストを取り出すのかというタイミングをちゃんと使い分けできるようにしたのかな🤔」「ジョブをdupして解決したってありますね」「dupしないとジョブのタイミングによってうまくいかないことがあったのかも🤔

これは#33626の意図にも合致する。
しかしこれは重要な変更ではあるが微妙な変更でもある。元のメソッドは「そのブロックを実行中に有効になったジョブが実行対象となる」というような意味だが、メソッド名にブロックを渡さない場合の文字どおりの意味としては「それまでにキューに乗っていた特定のジョブを実行する」ということになる。そしておそらく「既存のジョブだけではなく後続のジョブも実行してしまう」というバグが存在していた。
これらを別のメソッドに分割して振る舞いを区別できるようにする手もある。つまり「マッチするジョブを、ブロックの実行中に実行する」と「キューで待ち状態のジョブだけを実行する」を区別する。
ジョブのリトライをテストする場合、後続のジョブをジョブ階層的にキューに乗せ、スケジュールされたジョブは第2のブロック形式呼び出しでラップする必要があるだろうか?
同PRコメントより

# 同PRコメントより
# キューに乗ったジョブAを実行する。ジョブAはジョブBもキューに乗せる。
A.perform_later

# ここで実行すればアサーションで期待どおりの出力が得られるはず
perform_enqueued_jobs only: [ A, B ]

# あれ、ジョブAがキューに乗せたジョブBが動かなかったぞ
assert some_b_thing # => failed

# キューに乗ったジョブを実行し、その実行中にキューに乗ったジョブを実行する?
perform_enqueued_jobs do
  perform_enqueued_jobs
end

perform_enqueued_jobsが入れ子になってる😆」「変な書き方😆」「コメントを見た感じではブロック回りの話のようだ」

「ちなみに現場レベルだと1回実行するとうまくいかないのにもう1回実行するとなぜかうまくいくバグってあったりしますよね🤣」「あるある🤣」「実行するとなぜか1個余っちゃって、念のためもう1回実行すると消えるみたいな🤣」「理由わからないけど現場はそれで解決しようみたいな🤣」「# こうするとちゃんと動くとかコメントが付くヤツ🤣」「# たまにうまくいかないけど2回動かせば大丈夫とか🤣」「冪等になってればいいという考え方もありますけど☺

⚓IPAddrをlocalhostと比較したときに毎回例外をrescueしていたのをやめた

# railties/lib/rails/application/configuration.rb#L36
       @hosts                                   = Array(([IPAddr.new("0.0.0.0/0"), IPAddr.new("::/0"), ".localhost"] if Rails.env.development?))
        @hosts                                   = Array(([".localhost", IPAddr.new("0.0.0.0/0"), IPAddr.new("::/0")] if Rails.env.development?))

つっつきボイス:「IPAddrをlocalhostと比較すると例外が発生していたんですね😳」「localhostはIPアドレスじゃないしな〜☺

「比較の順序を変えただけ?」「IPAddr構造体と比較するより先にlocalhostと比較するようにしたようだけど」

「これ見ると↓一番よく使うのがlocalhostなのにlocalhostの比較がトップに来ないのは遅いって😆コメント)」「あ〜そういうことね😆」「リクエストのたんびに例外をrescueするのをやめてパフォーマンスを改善しようってことだと思いました☺」「HostAuthorizationミドルウェアで毎回ここを通ってたのか、わかる☺

MRI以外のプラットフォームでは例外のraiseがかなり遅いので、例外を制御フローに使うのは可能な限り許すべきではないと個人的には思う。
同コメントより大意

⚓localhostで使ったファビコンが残ってしまう問題

「Rackミドルウェアは必ず通るし」「まあ最近は開発環境でlocalhostを指定することあんまりありませんけど😆」「それな😆」「localhostを複数の案件で使っちゃうと、ブラウザ表示で他の案件のファビコンが表示されちゃったりしてやりづらいんですよ😇」「そうそう、スクショとか撮りづらくなる😆」「だからなんちゃら.lvh.meとかにして、他の案件のファビコンが出てこないようにしてます😆

「それ気になってました😳: localhostを指定してブラウザ表示すると、普段よく表示しているオレオレアプリのファビコンが他のアプリにも表示されちゃうことが多いのはなぜなんだろうって」「もともとブラウザはファビコンについてはかなりアグレッシブにキャッシュしちゃうんですよ: 本来はメタタグにファビコンパスの定義を書けるんですけど、そういう定義がなくてもブラウザが『ここにファビコンがあるはず』って勝手に取りに行っちゃう変な文化があります」「えぇ〜、そうだったんですか😅

参考: 正しいfaviconの設定方法を対応ブラウザ別にまとめる | ブログ | Glatch(グラッチ) - 夫婦で活動するフリーランスWeb制作ユニット
参考: Google Chromeでブックマークのfaviconがおかしいのを直す方法 | N★Typeブログ

「おすすめの対処法は、localhostの代わりに上で言ってるなんちゃら.lvh.meみたいに案件ごとにドメインを明示的に 分けることでしょうね🧐」「なるほど!」「IPアドレス直打ちとかすると、ごくまれにうまく行かなかったりすることもありますので、lvh.meみたいなループバックドメインのサブドメインにしておく方が余分な面倒を減らせます☺」「自社アプリだけ開発しているなら気にしなくていいんでしょうけど😆」「うちみたいな受託ソルジャーでは必要ですね😆

参考: 【Rails】ローカル環境の開発でサブドメインがある場合「localhost」ではなく「lvh.me」を使う - FujiYasuの日記

⚓RubyのIPアドレス処理

「そういえばRubyのデフォルトのIPアドレス機能って、CIDR構成ネットワークのinclusionチェックはできるけど人間が読めるstring形式にさっと戻せないとか、結構融通効かないんですよね😢」「たしかにイマイチ」「サイダー?」「IPアドレスに続けて/26とか書くアレですね☺」「任意の箇所でIPアドレスを区切るヤツ🧐」「ああ思い出しました😅」「Rubyのデフォルト機能には、サブネットマスクを取ってコネコネするみたいなのがないんですよね〜😢

参考: Classless Inter-Domain Routing - Wikipedia — CIDR
参考: class IPAddr (Ruby 2.7.0 リファレンスマニュアル)

「かといって今どきIPアドレス比較のビット演算処理を手作りしたくありませんし😇」「そんな生々しい処理😆」「現代の俺たちがやるべきことじゃない気がする😆

「いつだったかkamipoさんも『今の時代にIPアドレス処理するはめになるとは』みたいなことをつぶやいてましたね」「そうそう、ipaddressとかいうもっと高機能なgemを入れてました」「引数で渡されたものがサブネットの中にあるかどうかみたいなコードを今どき自分で書きたくない🤣」「同意🤣

以下のgemがそれかどうかわかりませんが参考までに。

⚓stringのアロケーションを削減

# actionpack/lib/abstract_controller/helpers.rb#L60
      def helper_method(*methods)
        methods.flatten!
        self._helper_methods += methods
        location = caller_locations(1, 1).first
        file, line = location.path, location.lineno

        methods.each do |method|
          _helpers.class_eval <<-ruby_eval, file, line
-           def #{method}(*args, &blk)                     # def current_user(*args, &blk)
-             controller.send(%(#{method}), *args, &blk)   #   controller.send(:current_user, *args, &blk)
-           end                                            # end
-           ruby2_keywords(%(#{method})) if respond_to?(:ruby2_keywords, true)
+           def #{method}(*args, &block)                    # def current_user(*args, &block)
+             controller.send(:'#{method}', *args, &block)  #   controller.send(:'current_user', *args, &block)
+           end                                             # end
+           ruby2_keywords(:'#{method}') if respond_to?(:ruby2_keywords, true)
          ruby_eval
        end
      end

つっつきボイス:「なるほど、stringをシンボルに変えたと」「ブロック変数も&blkから&blockに」

⚓Rails.envでStringInquirerのサブクラスを最適化して使うようにした

# activesupport/lib/active_support/core_ext/string/inquiry.rb#L3
require "active_support/string_inquirer"
+require "active_support/environment_inquirer"
# activesupport/lib/active_support/environment_inquirer.rb
module ActiveSupport
  # StringInquirerの特殊用途
  # 環境文字列に基づいてコンストラクション時にデフォルトの3つの環境を定義する
  class EnvironmentInquirer < StringInquirer
    DEFAULT_ENVIRONMENTS = ["development", "test", "production"]
    def initialize(env)
      super(env)

      DEFAULT_ENVIRONMENTS.each do |default_env|
        singleton_class.define_method(:"#{env}?", (env == default_env).method(:itself))
      end
    end
  end
end

つっつきボイス:「すとりんぐいんくわいあら〜?」「初めて見ました」「何をするヤツなんだろう?」「Active Supportなのか」

「ああなるほど: Rails.env.production?みたいなbooleanなカラムをチェックする述語メソッドを生やすヤツか」「なるほど」「StringInquirerからenvチェックを切り離してる」

「たぶんenvのdevelopmentとtestとproductionみたいによく使うものをいちいちmethod_missingで呼んでたら非効率だから、シングルトンメソッドをあらかじめ定義しちゃおうぜということかなと😋」「ははぁ、なるほど」「stagingみたいにデフォルトにないenvはmethod_missingで遅い呼び出しでもええやろと」「パフォーマンスも10倍ぐらい速くなってるぞと⚡」「method_missingの実装は重いからな〜🏋🏻‍♀️

参考: BasicObject#method_missing (Ruby 2.7.0 リファレンスマニュアル)

Warming up --------------------------------------
      StringInquirer   145.978k i/100ms
 EnvironmentInquirer   367.087k i/100ms
Calculating -------------------------------------
      StringInquirer      2.332M (±10.8%) i/s -     11.532M in   5.019755s
 EnvironmentInquirer     27.333M (± 3.4%) i/s -    136.556M in   5.001554s

⚓ドキュメント修正

つっつきボイス:「お、またindex_byウォッチ20200309)」「今度はAPIドキュメントが追加されてました」「ああなるほど☺」「修正後はindex_byとindex_withの説明が相補的になってる😋」「index_byはともかくindex_withの説明は欲しい👍」「サンプルとともに」

index_by: enumerableをハッシュに変換する。ブロックの結果をハッシュのキーとし、そのときのelementをハッシュの値とする。
index_with: enumerableをハッシュに変換する。そのときのelementをハッシュのキーとし、ブロックの結果をハッシュの値とする。
同PRより大意


つっつきボイス:「こちらはGetting Startedガイドの修正で、RailsInstallerの記述が削除されました」「RailsInstaller、懐かしい〜: 随分前に更新されなくなってますよねこれ」「更新後のドキュメントにもありますけど、今はWindowsだとRubyInstaller for Windows使って、さらにSQLiteもインストールする」「まさに先週した話ですね(ウォッチ20200310)」「つらい作業😭

⚓Rails

⚓ Simpacker: Webpackerのオルタナティブ

Webpackerというプロダクトは、Webpackのことを知らなくても簡単に使えるし、他にも便利な機能を提供しているところがよい。Webpackerのつらみは、WebpackをWebpacker固有のDSLやwebpacker.ymlでコンフィグしないといけない点だ。既にWebpackのコンフィグ方法を知っている人は、それをWebpackerのコンフィグに置き換える必要がある。自分はwebpack.config.jsを直接設定したいの!
Simpackerは、webpackのmanifest.json出力を参照してjavascript_pack_tag経由でscriptタグを作成する最小限の機能のみを提供する。その分Webpackの機能を知る必要があるが、Simpackerについてはほとんど何も知らなくてよい。
ただし、Webpackerにあるyarn統合やリクエスト編集のようなお便利機能はSimpackerでは利用できない。
同リポジトリより大意


つっつきボイス:「ああクックパッドさんのSimpacker」「こんなのあったんだ〜😳」「考えてみたら、WebpackerってRailsエンジニア側の発想ですし😆」「😆」「Webpackでやりたいフロントエンジニア側のことはあんまり考えてない感」「WebpackerではWebpackに触らせないんでしたっけ?」「というより、あくまでWebpackerからWebpackを制御するという感覚ですね☺: Webpackを直接触りたいフロントエンジニアはSimpackを使うと直接やれるようになるということらしい」「ふ〜む」

参考: Webpackerはもう要らない〜 Simpacker - Qiita

「ほほぅ、SimpackerではWebpackとwebpacker-cliは入るけど、webpacker-dev-serverは自分で入れないといけないのか」「webpacker-dev-serverは入ってないと結構つらそう」「このQiita記事を見た感じでは、普通にWebpackとReactを設定してる雰囲気👍」「使う機会があったらやってみてもいいかも😋

⚓N+1と戦うツール2点


つっつきボイス:「この間公開した翻訳記事↓でこの2つのgemが紹介されていたので」

Rails: Active Recordメソッドのパフォーマンス改善とN+1問題の克服(翻訳)

「activerecord-precounterはk0kubunさんのgemか」「これもk0kubunさんのactiverecord-precountより設計のきれいな、counter cacheのオルタナティブ」「わかる: Active Recordにパッチを当てるgemって結構コワイし👺

# 同リポジトリより
tweets = Tweet.all
ActiveRecord::Precounter.new(tweets).precount(:favorites)
tweets.each do |tweet|
  p tweet.favorites_count
end
# SELECT `tweets`.* FROM `tweets`
# SELECT COUNT(`favorites`.`tweet_id`), `favorites`.`tweet_id` FROM `favorites` WHERE `favorites`.`tweet_id` IN (1, 2, 3, 4, 5) GROUP BY `favorites`.`tweet_id`

「もうひとつのeager_group gemは集計関数を扱うヤツみたいです」

-- 同リポジトリより
SELECT "posts".* FROM "posts";
SELECT COUNT(*) FROM "comments" WHERE "comments"."post_id" = 1 AND "comments"."status" = 'approved'
SELECT COUNT(*) FROM "comments" WHERE "comments"."post_id" = 2 AND "comments"."status" = 'approved'
SELECT COUNT(*) FROM "comments" WHERE "comments"."post_id" = 3 AND "comments"."status" = 'approved'

-- ↓ 
SELECT "posts".* FROM "posts";
SELECT COUNT(*) AS count_all, post_id AS post_id FROM "comments" WHERE "comments"."post_id" IN (1, 2, 3) AND "comments"."status" = 'approved' GROUP BY post_id;

「なるほど、1つのSQLに集約してくれるのか」「きれいに動いてくれてればいいけど、scopedとかでおかしなことになったりしないかな😅

# 同リポジトリより
class Post < ActiveRecord::Base
  has_many :comments

  define_eager_group :comments_average_rating, :comments, :average, :rating
  define_eager_group :approved_comments_count, :comments, :count, :*, -> { approved }
end

class Comment < ActiveRecord::Base
  belongs_to :post

  scope :approved, -> { where(status: 'approved') }
end

「こういうふうに↑define_eager_groupと書かなければ勝手に動き出さないということらしい↓: 自分はこういうときに生SQL書くことが多いけど、同じようなコードが何度も出てくるような状況ならこういうのを使ってもいいかな😋」「なるほど」「生SQLも似たようなものをいくつも書くことになるの多いですし😆」「たしかに😆」「使うならscopedでは避けたい気がしますね: joinしてscopedなものにこのgemをかけると変なことになったりして😆

「このgemの作者としては、こうやってオブジェクトの属性の1個になるように書けるのがいいということなんでしょうね☺」「たしかにシンプルですね😋」「ハッシュが登場してハッシュをループで回したりするのは気持ちよくないでしょうし」「それにQuery Objectにするにしても何個も似たようなQuery Objectができるのはあんまりうれしくないし」「こう書きたい気持ちはワカル」

⚓Rails 6に入るstrict_loading

「N+1といえば、count系とは違いますけどstrict_loadingっていうN+1を殺す場所を探すのに便利な機能がRailsでデフォルトで入るってこないだのウォッチに載ってましたね(ウォッチ20200302)」「おぉそういえば」「includesみたいなeager loadingメソッドをを叩かない状態でhas_manyリレーションにアクセスすると落ちてくれるので、N+1を絶対殺したいときに使える標準機能😇」「エグい😆: strict_loadingモードで調べられるってことなのね」「lazy loadさせないための機能」

strict_loadingって名前がイマイチな気がしますけど😆: force_eager_loadingとかすればいいのに」「たしかに😆」「#37400にいいねが多いのもわかる気がしました」「みんなN+1に苦しめられてますし😆」「strict_loading使うのはちょいめんどくさいですけど😆」「N+1が出るときはあっても実用上問題ないレベルなら使えるのがRailsのよさという考えもあるので、悩ましい😆」「まあ業務アプリではこういう機能がある方が確実にいいですね👍

⚓Hanami::APIは速度をフィーチャー(Ruby Weeklyより)


つっつきボイス:「この間取り上げようと思って漏れてしまいましたが、HanamiがAPIはじめました〼ということだそうです」「うん、HanamiがAPIサーバーの方向に向かうのは正しい気がしますね👍」「言われてみればAPIの方が向いてるかも?」「Railsは昔から完全にフルスタックな方向に向かっていますし、Railsと棲み分けるのであれば、Hanamiはビューやりませんぐらいのスタンスにしてみるのもありだと思います😋」「自分もHanamiがAPIに向かうのは全然ありだと思います😋

「ところでこのベンチマークのSinatraの遅さにビビったんですけど: Railsより遅いのかと😆」「😆」「ああ、下の方にルーティング10,000とか書いてますけど、Sinatraはルーティング増えるとてきめんに遅くなるんですよ😆」「そういえばそんな話ありましたね😆


同アナウンスより

「メモリーフットプリントはそこまで差は開いてませんね↓」


同アナウンスより

「そもそもルーティングが10,000もあるようなアプリって作りませんよね😆」「😆」「とりあえずこの比較はSinatraに不利すぎてうのみにできないけど、HanamiがAPIの高速化に専念するのは方向性として有望だと思いますし、何ならうちらで使ってみてもいいかも😋

⚓chaskiq: オープンソースのキャンペーンプラットフォーム(Ruby Weeklyより)


chaskiq.ioより

最近conversational marketingという言い方が流行ってるんでしょうか🤔


つっつきボイス:「IntercomやZendeskやDriftのオルタナティブか: Driftは聞いたことある気がするけど」「それっぽいキャンペーンサイトをRailsで立ち上げられる全部盛りキットみたいな感じでした」「ああ、そのまますぐ使えるようなヤツね: ノリとしてはRedmineみたいな感じかな☺」「このchaskiq.ioみたいなLP(ランディングページ)的なサイトが作れるんでしょうね」「だと思います」

「それがRailsである必要がどこまであるかというのは思いますけど😆」「😆」「まあWordPressみたいに、つよつよのエンジニアなしでもとりあえず立ち上げられるところまでやれるなら価値あるかも?」「どうだろう、Railsという時点で割とハードル高かったりして😆」「一応Dockerとか使ってるみたいだし、うまいことパッケージングしてすっと使えるようにしてるかも🤔」「Rails 6.0.2だから最新ですね」「金もリソースもこれからみたいなベンチャーや個人が週末までにRailsでサイト立ち上げたいみたいな用途に使えそうですね」「そういうときはスピードが一番貴重だったりしますし、ダッシュボード的なものも付いててやってます感アピールできますし、スタートアップにはそういうのが本当に大事☺」「やらずに済むことをやらずに済ませるというのが大事😋


前編は以上です。

バックナンバー(2020年度第1四半期)

週刊Railsウォッチ(20200310後編)Flutter+Firebaseでモバイルアプリ開発、命名7つの鉄則、Rubyバージョンを切り替えるchruby gemほか

今週の主なニュースソース

ソースの表記されていない項目は独自ルート(TwitterやはてブやRSSやruby-jp Slackなど)です。

Rails公式ニュース

Ruby Weekly

週刊Railsウォッチ(20200317後編)Strangler Figパターンでリファクタリング、ペアプロ実践記事、イミュータブルデータモデルほか

$
0
0

こんにちは、hachi8833です。以下は東京都の対策サイトをforkして構築したそうです。

  • 各記事冒頭には⚓でパーマリンクを置いてあります: 社内やTwitterでの議論などにどうぞ
  • 「つっつきボイス」はRailsウォッチ公開前ドラフトを(鍋のように)社内有志でつっついたときの会話の再構成です👄
  • 毎月第一木曜日に「公開つっつき会」を開催しています: お気軽にご応募ください

⚓お知らせ: 出張!Railsウォッチ@銀座Rails#19、来週のRailsウォッチお休み

今週木曜19:00よりZoom経由で開催される「銀座Rails#19」に、TechRachoでお馴染みのmorimorihogeが『出張!Railsウォッチ』枠で今回もZoom登壇します。私もZoomで観るつもりです。


ginza-rails.connpass.comより

また、来週の週刊Railsウォッチはお休みをいただきます🙇

⚓Ruby

⚓Strangler FigパターンでRubyコードをリファクタリング(Hacklinesより)

Martin Fowler先生が発祥のようです↓。

参考: StranglerFigApplication


つっつきボイス:「ストレンジャー?」「私も最初同じに読み違えてスペルミスしてしまったんですが、1文字違いのストラングラー(strangle: 絞め殺すの名詞形)でした」「すげー名前😆」「Shopifyの記事なんですね」「Strangler Figか、Martin Fowler先生のところにそんなパターンがあったとは😳」「記事のヘソの部分を取り急ぎ訳してみました↓」

Strangler Figパターンは、コードのリファクタリングにインクリメンタルかつ信頼できるプロセスを提供します。これは、古いシステムの上に新しいシステムをゆっくり成長させ、古いシステムが”絞め殺されて”簡単に除去できるようになるまで新しいシステムを成長させるという手法を表します。このアプローチの良い点は、変更がインクリメンタルであり、常に監視下に置かれ、何かをうっかりぶっ壊す機会が比較的低いことです。新しいシステムが期待どおり動いていることが確信できるまで、古いシステムは温存されます。その後のレガシーコードの削除はシンプルです。

同記事より抄訳

「お〜、移行するときのアプローチとしては昔からあるかな☺」「お金かかりそう😆」「古いシステムをゆっくりと絞め殺していくと😇」「古いAPIをどう殺していくかみたいな🔪」「strangler figで検索したらこんなのが出てきました↓」

参考: 絞め殺しの木 - Wikipedia


その昔にいたStranglersというパンクバンドでこの単語を覚えました。実はちゃんと聴いたことがなかったのですが、腕っぷしの強そうな見た目とうらはらに、やけにピコピコした80年代なサウンドですね。

参考: ストラングラーズ - Wikipedia

⚓Railsで'string'.truncateするよりいい方法(Hacklinesより)


つっつきボイス:「ERBでRubyのtruncateで文字を詰めるよりいい方法があるという小ネタです」

.truncate-title {
  width: 50%;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

「CSSのoverflow: hidden;text-overflow: ellipsis;とかでtruncateしようぜと☺」「width指定やwhite-space: nowrap;も全部動員するヤツですね」「やるかどうかは文字数によるかな」「記事のコメントに『Bootstrapにもそれ用のtext-truncateクラスがあるよ』ってありますね」

参考: Text · Bootstrap

「自分もCSSでやるのがいいと思います: これでtruncateした文字はブラウザのCtrl-Fでも検索できますし😋」「あ〜たしかに!」「HTML上には文字が存在してるからか」「AWSのコンソールでは長い文字の...がこのやり方になってるので、隠れた文字も検索できます😎

「ただし、これがうまくいくのは文字列が1行に収まっているときだけ: 2行以上になるとtext-overflowがどのブラウザでも1行の省略にしか効かなくて、それを承知で対応したことあります😢」「なるほど〜」「なんとかclampというのも使ってみたんですけど、どのブラウザでも動きませんでした😇

こちらかどうかわかりませんが参考までに。

参考: -webkit-line-clamp - CSS: カスケーディングスタイルシート | MDN

「ところで今見ているDev.toの記事の埋め込みカードにも...があるけど、CSSでやってくれてるかな?😆」(ブラウザでinspectしてみる)「おや、HTMLの時点で文字列が切り落とされてる😆」「惜しい、記事で言うところのUsual Way止まり😆」「Dev.toのこの部分は2行になってるから元から断つしかないんでしょうね😆

⚓Rubyのビット演算(RubyFlowより)

# 同記事より
# AND
25.to_s(2)           # 11001
30.to_s(2)           # 11110
(25 & 30).to_s(2)    # 11000

# OR
25.to_s(2)           # 11001
30.to_s(2)           # 11110
(25 | 30).to_s(2)    # 11111

つっつきボイス:「Honeybadgerの記事で、教科書どおりかなと思いつつ」「まさにビット演算」

「Rubyでビット演算ってあんまり使いませんけどね☺」「ぱっと思いつかないですね🤔」「もちろんビット演算はプログラマーが常識的に知っておくべきことではありますけど、Rubyで書くかというと書かないかな〜😆

「この間mrubyの勉強会に参加してきたんですけど、そこではビット演算書きまくりでしたね😋」「mrubyみたいな世界だとメモリのフットプリントを増やさないためにビット演算多用するのはありそう」

Rubyのビット演算子でarrayを操作する

⚓その他Ruby

つっつきボイス:「Rubyにfinalがあったらそうでしょうね〜」「てっきりRubyにfinalが入るのかと思って探しちゃいましたが、どこにもなかったので、もしfinalがあったらというもしもの世界の話だったようです😅」「もし入ったらいろんなものが壊れすぎますし😆」「出てきたのはJavaの記事ばかりでした😆

⚓DB

⚓イミュータブルデータモデル


つっつきボイス:「はてブでバズってた記事です」「おぉ、この視点↓は普遍性があっていいですね👍: 何が同じなのか、どういうときに同じと言えるのか、というような視点」「なるほど!」「こういうエンティティの感覚って人によってブレることがあるんですよね…」

  1. 単一性 (Oneness) 「何が1つのものであるか?」
  2. 同一性 (Sameness) 「2つのものが同じであると、どういうときに言えるのか?」
  3. カテゴリ (Category) 「それは何であるか? どんな分類で識別されるか?」
    同記事より

「自分が昔やってたときは、『もの』と『こと』で分類せよっていう教えられてた気がしますけど、今はリソースとイベントなんですね😆」「言葉はどちらでもいいといえばいいんですけど、日本語の『もの』と『こと』だと意味が広がりすぎるかもしれませんね🤔」「ああたしかに」


「記事では『マスター』『トランザクション』の分類は使わないという記述もありますけど、この分類って、大昔のバッチ処理中心のシステムなら割とマッチしてたんですよ」「そうですね」「そういう世界では、マスターを更新するのは洗い替えのときか、更新されるとしてもせいぜい1日1回ぐらいでしたけど、今はたいていのものがトランザクション的にガンガン更新されるようになってきてますし、『マスター』『トランザクション』分類でやろうとしても結局全部トランザクションになっちゃったりしますし😆」「そういう悩みポイントがこの記事にいっぱい載ってますね😅


Step3. イベントエンティティには1つの日時属性しかもたないようにする

「イベントエンティティの日時属性は1つにする↑、ああなるほど」「これはまさにアップデートを避けるための手法で、データをイベントごとに分けるという感覚ですね」「テーブルにステータスフィールドを増やすとつらくなるのにも通じる」

なんとか日時みたいなフィールドを複数持って更新するんじゃなくて、可能な限りイベントごとに別テーブルにしようという考え方: これはSQLアンチパターンなんかでもよく指摘されていますね☺」「うう、実際にやりがち😅」「実際にやりがちですし、それでOKな場合もないわけではないんですけど、だいたい破綻するんですよね😇」「1つのレコードにいろんな日時が入ってきたりするとわけわからなくなってしまいがち」「取り消し機能を付けた後に取り消しの取り消しをやろうとしたら破綻したり😆

「なので、この記事でやってるように日時ごとにテーブルを分けるのが理想ではありますね」「テーブルをばらした上で必要なときにくっつけると😋

「その一方で、テーブルとして見るときには複数の日時がまとまっている方が見やすかったりするんですよ😆」「システムがどのぐらい複雑になるのか、どのぐらい長く使われるのかにもよりますけど、感覚としてアップデートはよくないというか避けたいというのがテーブル設計のアンチパターンでよく言われてますね」「うんうん」「どんなDBMSを使っていてもアップデートすればフラグメンテーションしちゃいますし」「イミュータブルがアップデートを避けるという意味なら記事タイトルのイミュータブルデータモデルというのは腑に落ちますね😋」「インサートオンリーで考えるとかも」

「この辺のテーブル設計回りのスライドは以前のウォッチでも見たような気がしますね☺」「後で探してみます👀」「記事としてはそれほど目新しくはなさそうでしたが、テーブル設計で何をするとまずいかという点を最初に押さえてから設計を進めるというアプローチはありだと思います: 最終的には同じテーブル設計になるとしても、こうやって着目点を変えてみるのはいいかも👍

後で探しましたがうまく見つけられませんでした。

⚓ユニケージ開発手法

「ユニケージ強者にお尋ねしますけど、『マスター』『トランザクション』分類はユニケージとかなら割とマッチします?」「いえいえ、私ユニケージさんざんやってましたけど、そこは普通のシステムと一緒ですよ😆」「ああやはり: 現代的なシステムだとマスターといえどもいつ更新されるかわからなかったりしますよね😆」「はいそのとおりです😆」「ユニケージでバッチ処理が中心になるのであれば多少整理しやすくなったりします?」「まあユニケージのコマンドの中ではマスターとトランザクションを組み合わせて処理することが非常に多くて、仕様レベルでもこの2つをきっちり区別しておかないといけないんですけど、するとどれがトランザクションなのかというさっきと同じ議論になっちゃいますね」「なるほど」

参考: ユニケージ開発手法 - Wikipedia

「結局『マスター』『トランザクション』分類は不毛ということで: マスターに決めたからといって1日1回しか更新しないかというとそうはなりませんし😆」「そうなんですよ😆

⚓設計・運営・DevOps

⚓ペアプログラミングについて


つっつきボイス:「Martin Fowler先生のサイトに掲載されたかなり長いペアプロの記事で、今翻訳を進めています(近日公開予定)」「なるほど長い😆」「1冊の本になりそうなぐらいペアプロのススメ方についてすごく具体的にみっちり書かれています」(翻訳ドラフトを一同で眺める)

「ペアプロのやり方のひとつとしてストロングスタイルというのも紹介されてて、これは要するに二人羽織だなと思いました😆」「ストロングスタイル😆

「ペアプロはお互いにある程度の信頼関係が必要とか、ペアプロに適した粒度のタスクを選ばないといけないとか、いろいろありますよね☺」「つい長々とペアプロしちゃったりとか」「やってみたけど誰が書いても同じようなものになっちゃったとか」「時間を決めてペアプロするのはありだと思います😋」「記事でも8時間ぶっつづけでペアプロするなと注意してました」

「あとはペアプロで何を得るのかも大事でしょうね☺」「記事の後半でも、ペアプロの目的を見失わないようにときどき思い出せと強調してますね」「誰とペアを組むかも目的によりますし」

「自分はペアプロする機会はあんまりなかったかな」「自分もちょっとやってみたことありましたけど、何かを得たという手応えがあったかというとどうだろう😅」「人がコードを書いているところを横から見て驚いたりそこから学んだりというのはときどきしますけどね☺」「2人で黙々と同じ課題を進めるのは、どちらかというと設計でやる感じかなと」

「一方的に教えるみたいになってもいけないでしょうし」「記事には新人を引き上げるときにはありだって書かれてますね」「あとプロジェクトに新しく加わったメンバーにプロジェクトのルールやツールの使い方を説明しながらリードしていくのにペアプロするとかもありでしょうし」「ツールの使い方とかは横で見せてもらう方が早いし😆」「どこかでやってみてもいいかも😋」「私もペアプロされてみたいです😆」「😆

同記事で以下の記事やツールも紹介されていました。

⚓WIP数に上限を設けることの重要性

上のOn Pair Programmingで紹介されていた記事です。

見出しより:

  • WIPの上限とは
  • WIPの上限が重要な理由 — 非効率な個人マルチタスクを減らすため
  • ScrumでもWIPの上限は使えるか
  • WIPの上限を取り入れてみる

つっつきボイス:「WIPで止まるときってレビュー待ちになることが多いですけど😆」「😆」「WIP塩漬けの件数を超えたらアラートを出すようにするといいかも」「WIPで止まるときは、その本人の問題というより他の問題で止まることが多いですし☺

「自分はカンバン系ツールでタスク管理してますけど、待ち状態になったときは待ち用の板に置いて、本当に作業中のものだけIn Progress板に置いたうえでWIP数を管理するというのは、それはそれでありかなと思います」「ああ、本当の作業中と待ち状態を分けるということですね☺」「In Progress板に置いたものが質問待ちになっちゃったりして😆」「そのときは待ち板にどんどん移動すると😆」「でも待ち板に入ったものって実質WIPじゃありません?😆」「たしかに😆」「脳内からスワップアウトされたタスクをもう一回読み込むのが大変なんですよね😅

「ともあれWIPの数に上限つけるのが重要なのはたしかで、何らかの形でアラートを出すようにして、進めるのか取り下げるのかを相談するとかがいいでしょうね」「ですね」

⚓クラウド/コンテナ/インフラ/Linux/Serverless

⚓sshuttle: 貧者のためのVPN

つっつきボイス:「リモートワークの友として社内Slackで話題になってたツールです」「そうそう、この中にもsshuttle使っているメンバーがいて、ただそれだけだと外部からのブラウザ表示がうまくいかないときがあるのでSOCKSプロキシを使うといいよという話でしたね」「ずっと前にSOCKSプロキシを使ったのをかすかに覚えてます」「sshでdynamic forwardingできればSOCKSプロキシ使えますし、SOCKSはHTTP以外にも何でも通せるのがいいところ😋」「おぉ〜」

「SOCKSがsocketsの略語って初めて知りました😅」「この文脈だとsocketsぐらいしかありませんし😆

以下の記事もはてブで盛り上がってました。

「テレワークでVPNを使わない😆: ゼロトラストの話とか、言ってることはもっともだけど」「日経の記事ってたいてい有料だから途中までしか読めない…😢」「大学にいた頃はボリュームライセンスでバックナンバーも含めて読めましたね😆」「そうそう😆」「いいな〜😭

⚓その他インフラ


つっつきボイス:「Bottlerocket、スリム化したLinuxか」「Amazon Linuxとは別物なんですね」「この記事で言ってるコンテナはDockerコンテナのことでしょうね: EKSのこととか書いてるし」「GoogleのContainer-Optimized OSのアマゾン版という感じ」

参考: Container Linux - Wikipedia
参考: コンテナ最適化 OS  |  Container-Optimized OS  |  Google Cloud

「サポートは3年間か〜😅」「ちょっと短い😆」「こういうのは頻繁に更新したくないし😆

⚓Amazon Linux 2とDockerで構成をシンプルにする

「最近チーム内でよく使う構成は、Amazon Linux 2を使って、amazon-linux-extrasでDockerを入れる、そしてそれ以上のツールはなんにも入れない😆」「なるほど!」「こうすればパッケージ管理でAWSのパッケージしか使われなくなりますし、アプリケーションで必要なものは全部コンテナに入るので、仮にサーバーを再構築しないといけなくなったときにもあんまり怖くない😋」「余計なもの入れないの大事ですね」「ホストOSに何かインストールしちゃうとアップグレードとかセキュリティ対応とかやらないといけなくて、そういうのを考えたくない😆」「😆」「余分なものは極限まで削ぎ落としたいし、そのためにDockerを使う意味がすごくありますね😋

参考: Amazon Linux 2(セキュアなLinux環境でアプリを実行)| AWS
参考: amazon-linux-extras - Qiita

⚓CSS/HTML/フロントエンド/テスト

⚓UXのビジュアルデザイン5つの法則

  • 1. スケール: 相対的な大きさの違いで重要さを表す
  • 2. 階層の「見える化」: 要素が重要な順に並んでいることをひと目でわからせる
  • 3. バランス: 要素が均等であることを示す(あえてバランスを崩してダイナミックにすることも)
  • 4. コントラスト: 文字どおり「対比」で違いを強調
  • 5. ゲシュタルト: 個別の要素の集まりが全体として別のものを表す(だまし絵的)
    同記事より抜粋

つっつきボイス:「BPSデザインチームのチャンネルで話題になってました」「ゲシュタルト😆」「めちゃ堅苦しい言葉ですけど、部分が集まって全体の意味を作るみたいなニュアンスで使ってますね: このNBCのロゴマークに孔雀が隠れてるみたいな↓」


同記事より

参考: ゲシュタルト - Wikipedia

「自分たちエンジニアはこれだけ読んでもデザインはできないけど、他人のデザインにフィードバックするときにはこういう法則ってとっても役に立ちます😆」「😆」「雰囲気でフィードバックするとあやふやになりがちですけど、イケてないデザインはだいたいこういう法則に何かしら違反してたりするので、フィードバックに根拠を与えるという意味でこういう法則を知っておくのは大事ですね☺」「5つなら覚えられそうですし」「あえて法則を外しているのかどうかをデザイナーに問い合わせるときにも使えますし😋

「似たような話は、これまでも何度か話したNon-Designer’s Design Book↓にもあります❤」「へぇ〜😳

⚓その他フロントエンド


つっつきボイス:「フロントもマシンパワー必要なんですね」「もう今はめっちゃ必要😆

「Webpackはファイルアクセスもネットワークアクセスも多いし、動きの多いものを作ってるなら表示が遅いとダメだし」「なるほど」「とはいうものの、あまりハイスペックのPCで作ると、現場の遅いPCで当てが外れることもありますけど😆」「それもありそう😅」「といって遅いマシンで作れとは思いませんし😆

「昔、SSDが今ほど普及してなかった頃って、MacのSSDだとアプリがむちゃくちゃ速く動くのに、デプロイ先のハードディスク環境だとめちゃ遅くなったことが結構ありましたね😆

⚓言語・ツール

⚓repl.it: GitLabリポジトリをブラウザIDEで操作

st0012さん経由で知りました。
たとえば以下を開くとkognise/water.cssをブラウザIDEで開けます。


つっつきボイス:「ありがちかなと思いつつ、GitHubリポジトリをローカルに持ってこないで直接やれるのが面白いかなと思って」「ブラウザIDE的なものはいろんなものが出ては滅びたりしてますけど、その間にちょっとずつ進化してたりするので、追っておくのはいいと思います😋」「repl.itがそのまま使えるプロジェクトならいいけど、うまく動かないプロジェクトがあるかもしれませんし」

「最近社内で新人が使っているCloud9を横から見てると、自分が使ってたしょぼい時代よりだいぶ良くなってるなって思いましたし☺」「おぉ」「いつの間にかERBも補完できるようになってましたし👍

追いかけボイス: 「repl.itでRails引っ張ってみたらデカすぎるってエラーになりました」

参考: AWS Cloud9(Cloud IDE でコードを記述、実行、デバッグ)| AWS

;⚓WebTransport over HTTP/2


つっつきボイス:「HTTPプロトコルの強い人の記事です」「WebTransport over HTTP/2?」「まだ策定中みたいですね」

「へぇ、優先順位やフロー制御がビルトインで使えるのか: これはいいな〜❤」「WebSocketだと自分でやらないといけないんでしょうか?」「WebSocketにそんな機能はありません😇」「あぁやっぱり😅

  • HTTP/2上でWebTransportセッションが確立したあと、クライアントとサーバのどちらからでもWebTransport用ストリームをオープンできる。
  • WebTransport用ストリームでヘッダー圧縮、優先順位付け、フロー制御 の機能が利用できる
  • 中継者がいてもWebTransport用ストリームのデータを正しい相手に配送できる
  • WebTransport用ストリームをグループ化できる
    WebSocket over HTTP/2とは異なり、セッションが確立したあとにサーバ側からWebTransport用ストリームをオープンできる。そのデータをただしくクライアントに配送するための工夫が入っている。
    同記事より

「WebSocketって本当に昔ながらのソケットという感じで結構機能が少ないんですよ😢: 記事にもある『そのデータをただしくクライアントに配送する』なんてまさにWebSocketにない機能」「そんなにプリミティブだったなんて…」「WebSocketはソケットをオープンする以上のことはあまりできないので、WebSocketをつないだ上で、その上にチャネルとかメッセージングプロトコルとかを実装しないとマルチストリームみたいなことができないんですよ」「うーむ」

参考: UNIXドメインソケット - Wikipedia
参考: WebSocket - Wikipedia

「WebTransportはネゴシエーションとかヘッダーみたいな地道にやるのが面倒な機能もあるらしいのがいいですね😍」「しかもWebTransportでは中間装置(プロキシ)も使えるし!」「これでようやっと使えるものになりそう😂」「まあ世の中のブラウザがすべて対応するまではWebSocketもやらないといけませんけど😅

⚓その他

⚓リモートワーク、はかどるのかな?


つっつきボイス:「今流行りのようつべASMR😆」「ASMRって、自分より皆さんの方が詳しいかなと思って」「まあ存在は知ってますけど、自分が見るのはゲーム関係なんで😆」「ASMRのどの辺がリモートワークに関係あるのかと😆」「意味わからん😆」「ASMRって睡眠導入とかそっち方面じゃありませんでしたっけ?」「睡眠導入もありますけど他にもいろいろありますね😆」「睡眠導入しか知らなかったな〜😴

参考: ASMR - Wikipedia

「料理動画なんかで音響がバイノーラル的にすごくリアルになってるのがあったりしますけど、ああいう感じ?」「まあそんな感じ: 人間の頭の形したマイクとか使ったり☺」「今すごく適当に話してますけど😆、卵を割る音とかリアルなの面白いですよね」

参考: バイノーラル録音 - Wikipedia

⚓番外

⚓未来っぽい


つっつきボイス:「まだ光沢平面上ですけど😆」「これまで出てきた光学立体ディスプレイってSF感はあるけどイマイチ流行ってないですね😆」「あとちょっとなのかな〜って」「いろいろ出てきてますけど、表示だけじゃなくてインタラクションや触感なんかも含めた決定版が出てこないと趣味で終わっちゃうかも☺」「やっぱりキラーアプリか」「触ったり掴んだりできるぐらいにならないと」「まだ要素技術研究の段階かな〜」

「こういうのは10年ぐらい前からやってて、Kickstarterかどこかで商品化もやってましたね」「ガラスの中に映すのか〜、スター・ウォーズみたいに何もないところに映せるのが欲しいです👽

参考: まるで光速船、ヘッドギア不要の3Dディスプレイ – Kickstarterで25万ドル集めたチームが挑戦 | TechWave(テックウェーブ)記事内リンク先のプロジェクトドメインはNot Foundでした…

こういうのがやれるのはいつでしょうか。


後編は以上です。

バックナンバー(2020年度第1四半期)

週刊Railsウォッチ(20200316前編)Webpackerの対抗馬Simpackerはいかが、Hanami::APIは高速性をフィーチャー、N+1と戦うgemほか

今週の主なニュースソース

ソースの表記されていない項目は独自ルート(TwitterやはてブやRSSやruby-jp Slackなど)です。

RubyFlow

160928_1638_XvIP4h

Hacklines

Hacklines


Vincentアプリでデザイン/フロントエンドの海外最新記事がまとめて読める

$
0
0
Vincentアプリでデザイン/フロントエンドの海外最新記事がまとめて読める

ニシです。
「Webの技術って海外の方が進んでるらしいよ」という話は耳にするものの、みんなどうやって情報キャッチしてるんだろう?と思ったことはありませんか。私だけでしょうか。
最近、そんな疑問に答えてくれる良いアプリがリリースされたので紹介します。

Vincentとは

Vincentは2020年2月5日にリリースされた海外発のiOSアプリです。
デザイン、フロントエンドのニュースを毎日取得できます。
当時のツイート↓

リリースされたばかりなので、まだまだComming soon…な機能もありますが、ざっと海外記事を読むことができるので大変便利です。
英語の勉強にもなります。

こんな記事が流れてくる

どんな記事が読めるのか、個人的に気になった記事をピックアップしました。

こういったサイトも流れてきます。

余談1:これまでの情報収集

私はTwitterをよく眺めるので、海外記事をツイートしてくれるようなアカウトをフォローしてました。

例えば:

たまにおすすめのサイトをツイートしてくれることもあり、現在でもよく見ています。
あと何の設定をしたのか覚えていないのですが、Twitter公式アプリのサーチ画面をスクロールすると「UX and Web Design」カテゴリのツイートが表示されるので、それも見ています。

Twitterは気軽に眺められるところが良いですね。
VincentAppのおかげで情報収集はしやすくなりましたが、Twitterもまだまだ欠かせない存在です。

余談2:海外記事を読む際に気をつけたいこと

ちょっと自省なのですが、こんなことに気をつけて海外記事を読もうと思ってます。

  • 誤訳することも可能性に入れる
  • 「海外至上主義」に陥らない

誤訳は、Chromeで読む場合はGoogle翻訳がそれなりの品質で訳してくれるのでそこまで気を張らずとも良いかと思いますが、アプリ内で読む場合は翻訳機能がないので若干誤訳して読みがちになります。
お、良い記事だ!と思っても誤訳の可能性を念頭に起きつつ読もうと思います。

また、「海外の最先端デザイナーが言ってるんだからこれが正解だ!」とも思わないようにしたいです。
外国人の感覚と平均的日本人の感覚はまたちょっと違うところがあるので、それも加味して咀嚼した上で情報の価値を考えます。

まとめ

英語がスラスラ読めないと厳しいアプリですが(翻訳機能がないので)、英語の勉強にもなると考えるとかなり良いのではないでしょうか。
他にも「こんな情報収集方法があるよ」と言った情報があれば教えてください!

私が1ヶ月間Vincentを眺めていて感じた感想ですが、海外の方は「かっこよさ」「新鮮さ」を求める一方で「個々人の特性(弱視 等)」をかなり重視している気がしました。人間に寄り添わせるところに論理性が生まれているような。
日本はガラパゴスとも言われていますが、海外の広い知識も借りて自分の技術やデザインをアップデートしたいと思いました。おしまい。

ペアプロを極めて最強の開発チームをつくる(1/4)ペアの組み方(翻訳)

$
0
0

概要

原著者の許諾を得て翻訳・公開いたします。

  • 英語記事: On Pair Programming
  • 原文公開日: 2020/01/15
  • 著者: Birgitta Böckeler — ドイツのThoughtWorks社で開発とコンサルティングを担当しています。カスタムソフトウェアチームのテックリードとして一日を「コーディング」「コーチング」「コンサルティング」「仕事を楽しく続けること」に分けています。
  • 著者: Nina Siessegger — ドイツのハンブルグ出身の開発者/テックリード/コンサルタント。元ThoughtWorks。コーディングの楽しさ以外に、特にソフトウェア開発の人間的な側面にも強い関心を抱き、高品質ソフトウェアは「コミュニケーション」「共同作業」「信頼関係」を重んじるチームによって成し遂げられると強く信じています。
  • サイト: martinfowler.com

日本語タイトルは内容に即したものにしました。画像は元記事からの引用です。
原文が長いので記事を4つに分割し、章立ての階層を浅くしました。原文にない強調やリンクも必要に応じて追加してあります。

  • 第1章: ペアの組み方(本記事)
  • 第2章: ペアプロのメリット
  • 第3章: ペアプロの難しい点
  • 第4章: ペアプロは導入すべきなのか、第5章: それでもペアプロする理由

⚓はじめに

ソフトウェア開発業界で現在働いている人の多くがペアプログラミング(以下ペアプロと略します)について耳にしているにもかかわらず、業界では未だにぼちぼち採用され始めている程度なのが現状です。ペアプロの普及状況がまちまちである理由のひとつは、ペアプロのメリットがすぐには明らかにならないためです。ペアプロの効果が目に見えるようになるまでには中〜長期を要します。しかも「1台のコンピュータに二人が張り付いて作業する」のは簡単ではないので、多くの人がペアプロ中に嫌気が差したあたりで放り出してしまいます。しかし私たちの経験では、チームでの共同作業や高品質のソフトウェア開発は「ペアプロなしでは実現不可能」です。


私は最初の時点からBetty Snyderとペアを組みました。最高のプログラムと設計はペアプロがあってこそ成立すると私は信じています。ペアプロすることによって、お互いに論評しあって相手の間違いを見つけ出し、ベストなアイデアに到達できるからです。
Jean Bartik(ほぼ最初期のプログラマー)


2人で1台のコンピュータを用いてすべての本番プログラムを書く作業。
Kent Beck: Extreme Programming Explained


Jean BartikはENIACの開発チームにいた女性で、最初期のプログラマーのひとりとみなされています。このチームがプログラミングを行っていたのは「プログラム」という言葉すらなかった時代であり、模範とすべきロールモデルもなければ、やり方を教えてくれる本もありませんでした。にもかかわらず、このチームはこの当時に「ペアを組んで仕事をするのがよい」という判断を下したのです。それから50余年を経た1990年代後半に、Ken Beckの『Extreme Programming』にペアプロという用語が登場し、広まりました。同書では「アジャイルソフトウェア開発」を多くの読者に紹介しましたが、ペアプロもその中のひとつに含まれていました。

ペアプロとは本質的に「2人が1台のコンピュータでコードを書く」ことを指します。ペアプロは非常に高い協調を求められる作業方法であり、コミュニケーションが多くを占めます。2人の開発者は1つのタスクを共同でこなしますが、コードを書くだけが作業ではありません。作業計画の立案や話し合いも作業に含まれます。その過程ではアイデアを2人で明確にし、よりよいソリューションを達成するためのアプローチについて2人で議論します。

本記事の第1章「ペアの組み方」では、ペアプログラミングのさまざまな実践的アプローチを概観します。この章は、ペアを組むことになった人や、上手なペアの組み方を知りたい人を対象にしています。

第2章「ペアプロのメリット」と第3章「ペアプロの難しい点」では、ペアプロで目指すべき目的と、ゴールにたどり着くときの障害とどう戦うかについて深堀りします。この2つの章は、ペアプロが自分の書くソフトウェアにもチームにとっても有用である理由を深く理解したい方や、ペアプロの質を高めるためのアイデアを探している方が対象です。

第4章「ペアプロは導入すべきなのか」と第5章「それでもペアプロする理由」では、チーム全体の作業フローや共同作業のスキームにペア作業を取り入れることについて、私たちの考察をまとめました。

⚓第1章: ペアの組み方

⚓どんなペアを組むか

ここでご紹介するペアプロの役割定義は、ある種のペアリングにおいてはもちろん、その他さまざまなペアリングアプローチにも適用できます。

⚓「ドライバー」「ナビゲーター」スタイル

ドライバーはハンドルを握る(つまりキーボードを操作する)係です。ドライバーは、目の前の小さいゴールをクリアすることに集中し、もっと大きな課題についてはさしあたって無視します。ドライバーになった人は、作業中に「今自分はこれこれをやっている」と常に口に出すようにすべきです。

ドライバーが入力担当なら、ナビゲーター(ナビ)はお目付け役です。ナビはコードを絶えずレビューし続け、指示を出したり自分の考えを相手に伝えたりします。ナビ担当になった人は大きな課題やバグにも目を配り、想定される次のステップや障害物をメモします。

この役割分担方式は「同じコードを違う角度で見る」ところがポイントです。ドライバーはなるべく狭く戦術的に考え、目の前のコードの細かな部分について考えることが前提です。ナビはお目付け役として、なるべく広く戦略的に考えるようにします。ドライバーもナビも、それぞれ心のうちに「大きな絵(big picture)」を思い描きながら作業するようにします。

⚓「ドライバー」「ナビゲーター」スタイルでよく使われる進め方
  • 両者が十分納得のいくまで定義されたタスクをひとつ開始する
  • タスクごとに小ゴールを設定して両者で合意を取る。これは単体テストやコミットメッセージで定義してもいいし、定義を付箋にメモしてもいい。
  • 定期的に役割を入れ替えて相手にキーボードを渡す。積極的な参加を共有することでモチベーションも高まり、学びや理解も進む。
  • ナビ担当になったら、細かな点にこだわる「戦術モード」で考えるのを避け、詳細をドライバーに任せること。ナビの仕事は「一歩身を引く」ことと「中長期的な視点で考える」ことでドライバーの戦術モードを補完すること。ナビは「次のステップ」「想定される障害」「アイデア」を付箋にメモして、ドライバーが小ゴールを完了したときに話し合い、ドライバーの作業が中断しないよう心がける。

⚓「ピン」「ポン」スタイル

このスタイルにはテスト駆動開発(TDD)も包含されています。テスト駆動的に実装できるタスクが明確に定義済みであれば完璧です。

  • 「ピン」: 担当Aはfailするテストを書く
  • 「ポン」: 担当Bはテストがパスする実装を進める担当
  • 担当Bは次の「ピン」を開始する(別のfailするテストを書く)
  • 「ポン」の後に共同でリファクタリングを行ってから次のfailするテスト作成に進んでもよい。このようにして「Red」「Green」「リファクタリング」サイクルのアプローチに従う。つまりfailするテストを書き(Red)、最小限の方法でテストをパスさせ(Green)、それからリファクタリングする。

99 Bottles of OOP』(Sandy Mets、Katrina Owen著)には「Red」「Green」「リファクタリング」ワークフローの素晴らしい例がいくつも掲載されています。

⚓ストロングスタイル

ストロングスタイル(二人羽織スタイル)は、知識を伝授するときに特に有用な手法です。詳しくはLlewellyn Falcoの以下の記事をどうぞ。

参考: The way things work in Llewellyn’s world: Llewellyn’s strong-style pairing

ルール: 「自分の考えをコンピュータに入力するときは必ず相手に入力させなければならない」。このスタイルでは、そのセットアップやタスクに長けた経験豊富なベテランがナビを担当し、(言語やツールやコードベースの)初心者がドライバーを務めるのが普通です。ベテランはほとんどの時間をナビに専念して初心者を導きます。

このスタイルで重要なポイントは、「ドライバーはナビを全面的に信頼し、恐れず身を任せる」ことと、「ドライバーは自分の理解がさしあたって不十分でもよしとする」ことです。ドライバーが理由を質問したり問題解決に挑戦するのは、必ず実装セッションが終わってからにすること。ペアの一方がズブの素人であれば、ストロングスタイルの効果はさらに高まります。

ストロングスタイルはマイクロマネージメントすれすれですが、受け身の「見て学ぶ」方式よりも能動的な「身体で覚える」式の新人研修ツールとしても有用です。ストロングスタイルは初歩的な知識の伝授には有用ですが、やりすぎは禁物です。あくまで目的は「役割をすっと切り替えられるようにする」ことであり、マイクロマネージメントは早々に切り上げることをくれぐれもお忘れなく。マイクロマネージメントが終わったときが、知識の伝授がうまくいった合図です。

⚓ペア開発スタイル

「ペア開発」はペアプロ特有の手法ということよりも、ペアプロにおける心がけ、つまりマインドセットの要素が多くを占めています(この言葉を最初に知ったのはSarah Meiの以下のツイートに続くスレッドでした)。ユーザーストーリーや機能を開発するうえでは、コーディング以外にもさまざまな作業が必要です。つまり、あらゆることにペアとして共同で責任を取らなければなりません。

何か新しい名前が必要だ。@wycatsも言ってたように、Tildeでやっている「ペア開発」がいいと思う。
「ペア開発」では機能の開発に2人で共同責任を持つ。プログラミング以外の多くにも共同で責任を持つ。

このマインドセットを身体に馴染ませるために、ペアプロで大きなメリットを得られる(コーディング以外の)ライフサイクルの例をいくつかご紹介します。

⚓プランニング: 目標をはっきり定める

ペアを組んで仕事を開始するときには、いきなりコーディングを始めてはいけません。機能のライフサイクルの立ち上げ期は、無駄な作業を回避する絶好の機会です。初期段階では4つの目玉を駆使して問題を見つめ、思い違いや見落としをつぶしておくことで、後々時間を大きく節約できます。

  • 問題を理解する: 2人がそれぞれストーリーに目を通し、理解した内容を順に復唱します。未解決の問題や思い違いの可能性がある箇所については、プロダクトオーナーにも同席してもらって解決します。自分のチームに「readyの定義」が確立しているのであればそれをチェックし、開始しても大丈夫な状態であることを確認しましょう。
  • 解決方法を模索する: 問題に対してどんなソリューションがありそうかをブレインストーミングします。ブレインストーミングは2人共同でやっても、個別に行った後でアイデアを出し合っても構いません。この作業がうまくいくかどうかは、ソリューションとは何かがどの程度事前にきちんと定義されているかにかかっていますが、個々人の考え方のスタイルにも依存します。ひとりでしばらくじっと考えるのが好きな人もいれば、考え中の内容を声に出すのが好きな人もいます。ペアの一方がそのビジネスドメインや技術にあまり詳しくないのであれば、必要なコンテキストを共有するための時間をある程度確保しておきましょう。

  • 自分のアプローチを計画に落とし込む: 「自分が選んだソリューションを達成するのにどんなステップが必要か」「頭に置いておくべきタスクには特定の順序はあるか」「テストはどうやって行うか」などを考えます。理想は、そうした手順を共有ドキュメントや付箋にメモしておくことです。これによって進捗を追えるようになりますし、タスクを他の人に手伝ってもらう必要が生じたときにも役立ちます。メモしておけば、その時点でわかっているToDoを後で思い出すのにも役に立ちます。メモしておかないと、翌日にはあっという間に忘れてしまうことを甘く考えてしまう人が多すぎます。

⚓調査

不慣れな技術を用いて機能を実装しなければならない場合、最初の段階である程度の調査が必要です。「ドライバ」「ナビゲーター」スタイルや「ピン」「ポン」スタイルはこの作業に合いません。つまり、2人で同じ画面を見ながら共同でググっても、さほど効率は上がらないのが普通です。

ペア開発モードでのアプローチをひとつご紹介しましょう。

  • 自分が答える必要がある疑問のリストを定義し、ふさわしいソリューションを思い付けるようにする。
  • 手分けする: 今抱えている疑問のリストを2人で分け合うか、同じ疑問について2人がそれぞれ解を探ります。疑問を解くために、ネットを探すか社内の書籍や資料に当たってみます。あるいは、2人ともまだ慣れていない概念について調べます。
  • 2人で事前に合意していたタイムボックスの締め切りになったら2人で集まり、お互いが見つけたものを共有します。
⚓ドキュメント作成

コード以外で共同で行う作業として、ドキュメント作成があります。これまで行ってきた作業について必要なドキュメントが揃っているかどうかを2人で振り返ってみましょう。取り組んでいる問題や各人の好みに応じて、共同でドキュメントを作成しても構いませんし、一方がドキュメント作成を担当し、もう一方がレビューや推敲を担当しても構いません。

ドキュメント作成というタスクは、ペアの2人が互いに相手を訓練できるよい例です。ドキュメント作成は後回しにされがちで、しかもドキュメント作成が完了するまでは現在取り組んでいるストーリーを終わらせてすっきりした気持ちになれません。そしてたいていの場合、ドキュメント作成をすっ飛ばすか、やっつけ仕事で間に合わせることになりがちです。ドキュメントをペアで作成することで、面倒ではあるが価値の高いドキュメント作成という作業にある程度率直に向き合えるようになり、ひいてはいつの日か「ドキュメントを作成しておいてよかった」と大いに感謝したくなることでしょう。

⚓タイムマネージメント

これまでご紹介した一般的なペアリングスタイルの他にも、作業を楽にしてくれる気の利いた小物ツールがいくつかあります。

ポモドーロテクニックもそうしたツールのひとつで、時間を小分けにして(25分にするのが通例)合間に休憩を挟むというタイムマネージメントの手法です。この手法は、上述のペア作業のほとんどすべてに適用して集中力を高めるのに利用できます。ペア作業は消耗するので、ポモドーロのリマインダーを合図に休憩を挟み、役割を定期的に切り替えるのに役立ちます。

ポモドーロテクニックを実践するとこういう感じになるという例をひとつご紹介しましょう。

  • 次の作業を決定する。
  • タイマーを25分に設定する。ポモドーロのブラウザ拡張を使うのもよし、何なら台所に転がっているトマト型のキッチンタイマーを使うのもよし。
  • タイマーが鳴るまで中断せずに作業する。
  • タイマーが鳴ったら作業を止め、5〜10分ほど休憩を挟む。
  • ポモドーロを3〜4回繰り返したら、長めの休憩(15〜30分)を挟む。
  • 小休憩では気持ちを切り替えて気力をためることを心がける。水やコーヒーを飲んだりトイレに行ったり、外の空気を吸いに行ったり。小休憩中はメールチェックなど他の業務を避けること。

⚓ペアのローテーション

ペアのローテーション(選手交代)とは、しばらく共同作業を行った後で一方がストーリーから離れ、もう一方が残って、職場にいる他の誰かと作業を続行することを指します。この「残る人」はしばしばストーリーの「アンカー」と呼ばれます。

ローテーションする理由のひとつは、ロジスティクス(調達)です。ペアの一方が病気になることもあれば、休暇を取ることもあります。一方がリモートワークの日のこともあります。ハードウェアのセットアップの関係上、ペア作業は2人が物理的に同じ場所にいる必要があります。

もうひとつの理由は、ローテーションで「淀みを防ぐ」ためです。同じペアでの作業がしばらく続いて一緒にいる時間が長くなりすぎると、ペアの一方にストレスがたまっている兆候が現れ始めます。それに、非常に退屈かつ消耗する作業でもローテーションがあれば一息つけますし、新しく来たメンバーが違う視点を持ち込んでくれたり活気づけてくれることもあります。

最後に、ペアをローテーションする最大の理由は「知識のタコツボ化(knowledge silos)」を避けるためです。すなわち、自分が掌握しているコード(コードのオーナーシップ)が増加して、絶えずレビューしなければならないコード量がさらに積み上がってしまう状態を避けるためです。もちろんペアを組むことで既にある程度対処はできますが、ローテーションも取り入れることで、本番コードを見つめる目玉の平均個数をさらに増やせます。

ローテーションの理想的な頻度については、さまざまな意見が飛び交っています。「知識の伝搬や品質を十分なものにするには2〜3日に一度は常にローテーションすることが重要」だと信じている人々もいます。しかし、ローテーションはある程度の代償を伴います。新人には研修期間も必要ですし、ペアの1人にはコンテキストスイッチのコストも発生します。継続的にアンカーを引き受ける人がいないと、問題に対する暗黙知が増加するリスクや、ソリューションの余地が失われて手戻りにつながるリスクが生じます。若手の開発者であれば、ローテーションの間隔を長めに取り、じっくりトピックに取り組んで知識を身体に馴染ませるのに十分な期間を設ける方がメリットが大きいこともよくあります。

原注: 「show and tell」という用語は、アジャイルのチームでさまざまな意味で使われています。ここでは「定期的に開催される開発者作戦会議」「技術的負債や学習や重要な新しいコードについて週1で集まって議論する時間」を指すことにします。

ローテーションにおける「コスト」と「メリット」のトレードオフについて考えてみましょう。たとえば自分が既に高品質の知識を持ち合わせていて、読みやすいコードやよいドキュメント作成についてチームで「show and tell」するとしましょう。この場合、ローテーションを頻繁に行うことにこだわってしまうと、コードのオーナーシップ(の削減)はわずかに改善されるだけにとどまってしまい、むしろチーム内に軋轢やオーバーヘッドが生じてしまう可能性があります。

⚓その日の作業計画を立てる

ペア作業では、スケジューリングやカレンダーの調整もある程度以上きちんと行うことが求められます。これらを確認して対応する時間を確保しておかないと、その日のうちに悩みのタネとなって我が身に返ってきます。

業務のスタートはカレンダーのチェックから始めること。ペア作業に何時間使うかについてペアの相手と合意を取り、ペア作業以外のミーティングや業務についてもプランニングが必要かどうかをチェックします。ペアの一方がしばらく一人で作業に集中しなければならないことがわかったら、その間はペアの他方が席を外していても作業を続行できるよう準備しておきます(つまりペアの他方のPCを使わないようにする)。

その日に他にもミーティングや立ち会いの必要な作業がある場合は、リマインダーをしっかりセットして時間になったら通知されるようにします(特にペアの他方のPCを使っている場合に重要)。チームがデフォルトでペアを組む体制を取っているのであれば、チーム共通の「コーディング集中タイム」を設けてチーム全員と合意を取ることも検討してみましょう。こうしておくとスケジューリングが遥かにやりやすくなります。

⚓物理的セットアップ

ペアプロでは、1つの机の物理スペースを共有し、2人が顔を突き合わせながら作業することになります。この状態は、自分の机に何でも置いておける状態とかなり勝手が違います。2人が近寄って作業するうえでは、お互いへのリスペクトと、相手のニーズに注意を払うことが求められます。ある程度時間をかけて、ペアのどちらにとっても快適な作業環境を模索しておくことに価値がある理由がこれです。

ペアのそれぞれの机の上に十分スペースを確保したか
必要なら机の上を片付けておきましょう。
机の前に2人が楽に座れるスペースはあるか
ゴミ箱や自分のかばんはしまっておきましょう。
使うキーボード(マウス)は2つにするか1つにするか
これについては一般的にうまくいく明確なルールはないので、最適なセットアップをいろいろ試してみることをおすすめします。「清潔さ」は割とあなどれない要素です。他人のキーボードに触るのはイヤではありませんか?自分のスペースが狭いのはイヤではありませんか?
外部モニタは1台以上あるか
外部モニタがない場合は、リモートでペア作業するときのように、何らかの形で画面共有することを検討しましょう。画面共有のセットアップでは、それぞれ自分のノートPCのキーボードを使うことになるでしょう。
ペアの相手には設定上の特別な好みや注文があるか
フォントは大きい方がいい、コントラストは強めがいいなど。
特殊なキーボードやIDEセットアップを使っているか
そうした特殊なセットアップを相手が使っても大丈夫かどうかをチェックします。その場合、特殊なセットアップをワンタッチで標準セットアップに戻せるかどうかもチェックします。
チームのデフォルトセットアップを統一可能か
セットアップの統一についてチームで合意できるのであれば、各人の好みについて延々と議論せずに済むので話が早くなります。

⚓リモートペアプロ

自分のチームが複数拠点に分散していたり、メンバーの何人かがときどき自宅で作業することはありますか?双方のネット接続が十分安定している限り、ペアプロは十分可能です。

⚓リモートペアプロのセットアップ

リモートペア作業では、自分の見ている画面の他に、キーボードを切り替えて相手のPCの画面も制御する何らかの画面共有ソリューションが必要です。こうした機能は既にさまざまなビデオ会議ツールでサポートされていますので、商用ビデオ会議ツールのライセンスを持っている会社で働いているのであれば、まずそれを使えるかどうか試してみましょう。オープンソースにもjitsiのようなリモート制御機能付きのビデオ会議ツールがあります。通信帯域幅の狭い環境向けには、sshとtmuxの併用VSCodeのLive Share拡張などのソリューションも試してみましょう。

⚓リモートペアプロのコツ

  • 動画でお互いの顔も見えるようにする: 人間同士のコミュニケーションでは仕草や表情が大きな要素を占めるので、画面を共有するときにペアの相手の顔も動画で表示しておくとよいでしょう。そうした機能を持つビデオ会議ソリューションもありますが、自分の使っているビデオ会議にそうした機能がない場合は、別の回線を開いてお互いの顔が見えるようにすることを検討します。
  • 計画立案や設計作業: オンラインコラボレーションツールのビジュアル表示機能を使えば、紙やホワイトボードに共同でスケッチを書き込む作業を再現できます。
  • 音声品質: 周囲の騒音が少ない静かな部屋を選び、品質の良いヘッドセットを使いましょう。指向性マイクもあるとさらによいでしょう。ノイズがある場合はマイクのオンオフ機能も有用です。周囲の騒音で気が散らなくなるノイズキャンセリングヘッドフォンも便利です。

原注: リモートペア作業について詳しく知りたい方へ
Chelsea Troyのブログ記事には、リモートペアプロのノウハウを含むペアプロ上級編記事がまとめられています。

  • ネットワーク遅延と戦う: ネットワークが遅延している状態でリモートPCと作業していると消耗しがちです。定期的にPCを入れ替えて、遅延の少ない方のPCで作業するチャンスを見つけてみましょう。ネットワークが遅延すると、ファイルのスクロールを追うのもつらくなってしまうことがあります。長いファイルのスクロールを避けて、ファイル内を移動するキーボードショートカットや、エディタの折り畳み/展開機能を活用してみましょう。

⚓人的要因

チームの全員が分散しているのではなく、1〜2名だけがリモート作業しているのであれば、オフィス内で行われるすべての話し合いにもリモートメンバーを参加させてみましょう。同じ部屋にいるというだけで、ちょっとしたすり合わせがいかに多く行われているかということを私たちは忘れがちです。

顔を合わせたこともない人と初めてリモート越しで作業すると、通常よりも面倒が増えます。ペア作業はリモートチームのメンバーとお互いに親しくなれるチャンスでもありますが、共同作業の部分を見落としがちでもあります。相手とじかに会う機会がなさそうなら、お互いをよく知るために別の手を考えてみましょう(リモートでコーヒータイムを取ってみるなど)。

最後に、リモートペア作業にはそれなりの困難が伴いますが、ヘッドフォンを付けて気が散りにくくなる分、オフィスでペア作業をするよりも集中しやすくなることもあります。

⚓終わったら喜びを分かち合おう

共同のタスクが完了したら、お互いに「やったね!」「お見事!」「お疲れさま!」などとねぎらったり声をかけたりしましょう。いい大人同士が今さらハイタッチなんてと思うかもしれませんが、実はこういうちょっとした「パワーポーズ」を2人でキメることで元気も出ますし、次のタスクの準備もはかどります。もちろん自分なりのオリジナルなねぎらい方でやってもよいでしょう。たとえばLara Hoganは自分のキャリアで何か達成するとドーナツで自分にごほうびをあげるそうです。

⚓ペア作業で避けるべき落とし穴

ペア作業を快適に進めるのに役立つ、さまざまなアプローチやテクニックがあります。避けておきたい、よくある落とし穴についていくつかご紹介します。

⚓1. よそ見で相手を無意識にないがしろにする

ペアを組んでいる最中は、メールを読んだりスマホをいじったりしないようにしましょう。そのような気晴らしを手元で繰り返していると、相手はだんだん自分がないがしろにされているように感じ、気分が壊れてタスクに取り組む気が失せてしまう可能性もあります。もしどうしてもメールチェックなどが必要であれば、こそこそやらずに「すみません、一瞬メールチェックさせて」というふうに理由も含めて相手に伝えましょう。また、休憩時間や日々の個人的な時間にメールに目を通したりするのは問題ありませんよと伝えて相手を安心させましょう。

⚓2. マイクロマネージメント

マイクロマネージメントモードに陥らないよう注意しましょう。次のように、相手に考える余地も与えずに一挙一動を指示していると相手に不満がたまってきます。

  • 「じゃあsystemドットprintって入力してそれから…」
  • 「つまりxxという名前のクラスが必要かな…」
  • 「コマンドキーとシフトキーを押しながらOキーを押して…」

⚓3. せっかち

5秒ルール」をお忘れなく。ドライバーのやっていることが「間違っている」ことにナビが気づいてツッコみたくなっても、必ず5秒以上待ちましょう。ドライバーが既に自分で間違いに気づいていたら、余計なおせっかいで流れが中断されてしまいます。

ナビを担当するときは、ミスや落とし穴になりそうな点をその場で指摘しないこと。ドライバーが自分で修正するまでちょっと待つか、後で思い出せるように付箋にメモしておきましょう。ドライバーがミスするたびに横槍を入れると、ドライバーが混乱してしまう可能性があります。

⚓4. キーボードを譲ろうとしない

キーボードを握ったら放さないタイプの方はご用心。相手に入力させるのを忘れて自分ばかり操作していませんか?

これをやられた人は心底嫌な気持ちになります。蚊帳の外に置かれた気分ではろくに集中できなくなるかもしれません。既にご紹介したアプローチのどれかを用いて、入力担当をまめに切り替えるようにしましょう。

⚓5. ぶっ通しのペア作業

ペアプロをうまく回そうとチームが真剣に取り組むあまり、1日8時間ずっとペアプロし続けてしまうことがあります。私たちの経験から申し上げれば、こんなペアプロは長続きしません。第1に消耗が激しすぎます。第2に現実の業務でうまくいきません。コーディング以外にもメールチェックや面談や会議や調査や自習など、やることはたくさんあります。一日の計画を立てるときは、コーディングに100%の時間が使えると仮定しないよう、くれぐれもお忘れなく。

⚓「正しい方法」の決定版は、ない

ペアプロのアプローチはさまざまですが、ペアプロに「これこそが正しい方法」というものは存在しません。ペアプロは、スタイルや個人の性格、経験値、状況、タスクそのものなど、さまざまな要因に応じて姿を変えます。最後に、最も重要な問いかけをご紹介しましょう。「それをやれば確実にメリットが生じますか?」もしノーであれば他の方法を試し、見直しや議論や調整を重ねてメリットを得られるようにしましょう。


  • 第1章: ペアの組み方(本記事)
  • 第2章: ペアプロのメリット
  • 第3章: ペアプロの難しい点
  • 第4章: ペアプロは導入すべきなのか、第5章: それでもペアプロする理由

Rails 6/5とRubyのJSON gem向けセキュリティ修正がリリース

$
0
0

当初同じ内容かなと思ったら、RailsとJSON gemのセキュリティ修正は別物でした。

Railsセキュリティ修正

影響を受ける可能性があるのはActionViewヘルパーのescape_javascriptメソッドとそのエイリアスであるjメソッドとのことです。変更点を見た限りでは、バッククォートと$がエスケープされていなかったのを修正したようです。

RubyのJSON gemセキュリティ修正

Rubyそのものではなく、RubyにバンドルされているJSON gemの追加修正です。以下のバージョンが影響を受ける可能性があるとのことです。昨年12月12日にリリースされたJSON gem 2.3.0(Ruby 2.0以降が対象)では修正済みとのことなので、この日より後にJSON gemを2.3.0にアップデートしたかどうかをチェックしてみましょう。

  • JSON gem 2.2.0およびそれ以前のバージョン

おまけ: Rails 5アプリをアップグレードした

オレオレRailsアプリを5.2.4.1から5.2.4.2にアップグレードしました。

アプリのJSON gemは既に2.3.0になっていたのでこちらは変更しませんでした。

  • GemfileでRailsバージョンを5.2.4.2に変更

  • bundle installを実行

実際はbundle installすると依存関係でいくつかつっかかったので以下を実行しました。これはアプリによって異なると思います。

$ bundle update activemodel actionpack railties activerecord

関連記事

Railsアプリで実際にあった5つのセキュリティ問題と修正方法(翻訳)

ペアプロを極めて最強の開発チームをつくる(2/4)ペアプロのメリット(翻訳)

$
0
0
  • 第1章: ペアの組み方
  • 第2章: ペアプロのメリット(本記事
  • 第3章: ペアプロの難しい点
  • 第4章: ペアプロは導入すべきなのか、第5章: それでもペアプロする理由

⚓第2章: ペアプロのメリット

ペアプログラミング(以下ペアプロ)はどんなことに効果があるのでしょうか?考えられるメリットをひととおり押さえておくことは、ペアプロすべきかどうかを決めるうえでも、ペアプロをうまく回すうえでも、困難の中でペアプロするモチベーションを保つうえでも重要です。ペア作業で目指すものは、主に「ソフトウェアの品質」と「チームの作業フロー」です。

さて、ペア作業でどのようにして「ソフトウェアの品質」と「チームの作業フロー」を達成するのでしょうか?

⚓1. 知識を共有できる

ペア作業のメリットとして、まずは最もわかりやすく議論の余地の少ないものから始めましょう。すなわち「知識の共有」です。ひとつのコード片に2人で取り組むことで、チーム内での知識(技術面およびビジネスドメイン面)の伝達が日々促進され、知識のタコツボ化を防げます。しかも、1つの問題を2人が理解して議論することで、よいソリューションを見つけるチャンスも増えます。さまざまな経験や視点があることで、より多くの別案を検討できるようになります。

使えるヒント

ペアを組んでタスクに取り組むときは、タスクに関連する技術やビジネスドメインについての知識がなくても決して尻込みしないこと。自分が一番楽にやれる分野のタスクばかりやっていると、学びの機会がごっそり失われますし、ひいては知識がチーム内に定着する機会も失われてしまいます。

チームメンバーがいつも同じことばかりやりたがる傾向があることに気づいたら、その人の専門知識をチーム内に広めるために他のタスクもやってもらいましょう。それによってチームの「技術力」「業務知識」や「各メンバーの強み」「分野ごとのギャップ」を網羅したスキルマトリックスを作成できるようになります。このスキルマトリックスをチームの部屋の壁に貼っておけば知識の共有も広まって共同作業がはかどります。

⚓2. 振り返りが促進される

ペアプロでは、アプローチやソリューションについて自分の頭だけで考えるのではなく、議論することを強制されます。声に出してはっきり説明することで、理解が本当に正しいかどうか、そのソリューションが本当によいものかどうかを見直せるようになります。これはコードや設計の話のみならず、ユーザーストーリーやストーリーがもたらす価値についても同様です。

使えるヒント

自分がまだわからないことについてお互いが気兼ねなくオープンに話せる空気感を醸し出すためには、2人の間に信頼関係を築く必要があります。チーム内の人間関係構築が、ペアリングでさらに重要度を増す理由がこれです。定期的に1on1やフィードバックセッションの時間を設けましょう。

⚓3. 集中力を維持できる

ペアを組むことで、さまざまな構造的アプローチをずっと楽に取り入れられます。ペアの一方は「今この作業を行う理由」と「今の作業で目指しているもの」を明確に表明しなければなりません。ひとりで作業しているとずっと気が散りやすくなってしまうものです。深く考えずに別のアプローチを「取り急ぎ」やってみた結果、「うさぎの穴」から脱出するのに何時間もかかってしまったりします。ペアのもう一方は、ペアの相方がそうした「うさぎの穴」に陥らないように注意し、今終わらせるべきタスクやストーリーに集中できるよう援護射撃します。順調にタスクが進むようお互いに助け合いましょう。

役に立つヒント

計画は2人で一緒に立てましょう!自分の直近のタスクについて話し合い、ゴールに到達するのに必要なステップを考え、それぞれのステップを付箋にメモし(リモートならチケット管理システムのサブタスクにメモします)、順にひとつずつ取り出して進めます。可能ならポモドーロテクニックも併用して、1ポモドーロに1つのゴールを達成するようにしてみましょう。

「成功と失敗の分かれ目はコミュニケーションにあり」を決してお忘れなく。「今これこれをやっています」と相手に明確に伝え、相手はそれについて説明を求めるという作業をお互いに繰り返しましょう。

⚓4. 書いた端からコードレビューできる

ペアを組んで進めることで、小さな課題についても大きな課題についても4つの目玉で見つめることができ、より多くのミスを(作業完了前ではなく)作業中に発見できるようになります。

コーディング作業にコードのリファクタリングを常に含める点は、ペアプロでも同様です。自分のそばに立ち会う人がいることで、アプローチや名前付けをどうするかなどの問題をすぐ話し合えるようになり、コードの改善がはかどります。

(コードを書きながらではなく)コードを書き上げた後でのコードレビューにはいくつかの欠点があります。これについて詳しくは最終章「ペアプロは導入すべきなのか」をご覧ください。

役に立つヒント

お互いに遠慮なく質問しましょう!質問こそが、自分の作業を理解してよりよいソリューションにたどり着くための最も強力なツールです。コードが相手にとって読みづらく理解しづらいのであれば、もっとわかりやすい別の方法を試しましょう。

ペアプロのコードをもっとみっちりコードレビューして欲しいと感じたら、ペアリング方法を改善する方法があるかどうかを改めて考えてみましょう。ペアセッション中に気になることや心配事を片っ端から質問していませんか?なぜそうせずにいられないのでしょうか?改善のためには、どこを変更する必要がありますか?

⚓5. 2人が「2種類のモード」で考えられる

古典的な「ドライバ」「ナビ」スタイルの項で既にご説明したように、ペアを組むことでコードにさまざまな視点を持ち込めるようになります。通常、ドライバーの脳は「戦術モード」で現在のコードを詳しく追います。一方、ナビの脳はより手広く「戦略モード」で考えます。大きな絵(ビッグピクチャー)を考え、次のステップやアイデアを先回りして付箋にメモします。2つのモードを1人の人間だけで切り替えながらやれるでしょうか?1人ではたぶん無理でしょう。(2人の)戦術的な視点と戦略的な視点を組み合わせれば、心に大きな絵を描きつつ詳細にも注目できるようになり、コードの品質が向上します。

役に立つヒント

キーボードの入力担当、つまり役割を定期的に入れ替えることをお忘れなく。それによってリフレッシュして煮詰まりを防げますし、2種類の思考モードのどちらについても訓練できるようになります。

ナビ担当は、つい「戦術的」に考えてしまわないよう気をつけましょう。コーディングの細かな点はドライバーに任せてください。ナビの仕事は、一歩身を引いて中期的に長い目で見て思考し、戦術モードで作業しているドライバーを補完することです。

⚓6. コレクティブコードオーナーシップ(CCO)を目指せる

チーム全員がコードのオーナーシップを持ってコードを掌握する「コレクティブコードオーナーシップ」(collective code ownership)によって、個別のモジュールのオーナーシップという概念を放棄する。コードベースはチーム全体がオーナーシップを持ち、誰がどの部分を変更してもよい。
Martin Fowler Code Ownership

ペア作業が一定して行われるようになれば、コードのあらゆる行について常に2人以上で作業したことを確信できます。これにより、ほぼすべてのコードをチームメンバーの誰もが気兼ねなく変更できるチャンスが増えます。そして(意外にも)、コードベースの一貫性も1人でコーディングした場合に比べて高まります。

役に立つヒント

ペアプロさえやっていればCCOを達成できる、という保証はありません。CCOを達成するには、ペアリングをさまざまな組み合わせ/さまざまな分野でローテーションして、知識のタコツボ化を防ぐ必要があります。

⚓7. WIPタスク数を制限しやすくなる

WIP(作業中: work in progress)タスク数を一定以下に制限することは、チームのワークフロー改善でおなじみカンバン方式の中核にある法則のひとつです。WIP数制限を設けることで、チームが最も重要なタスクに集中しやすくなります。WIP数制限を取り入れたチームは、マルチタスクを個人が引き受けるという非効率な方法ではなく、チームレベルでマルチタスクが回るようになり、多くの場合生産性が向上します。特に大人数のチームでは、チームで同時並行で進められるタスク数をペアプロで自然に抑えられるので、全体の集中力も増加します。これによって作業が常に滞らずに流れるようになり、作業をブロックする要因も即座に解決されます。

役に立つヒント

チームのWIP上限数は、チーム内のペアの数と同じに設定しましょう。かつ、チームのWIPタスクをチーム内に貼り出すなどして(リモートであればオンラインプロジェクト管理ツールで)見える化しましょう。次のタスクを拾う前にWIP上限数を確認するようにしましょう。WIP上限数を守ることで、ペア作業の習慣を自然に身につけられるという効能も期待できます。

⚓8. 新メンバーの早期研修にも使える

ペア作業によって知識を共有できるので、チームに加わった新メンバーの研修にも役立てられます。新メンバーはペアの助けを得てプロジェクトや業務内容、組織を理解できるようになります。チームメンバーが変動するとチームのフローに影響が生じますし、お互いを知るにはある程度の期間も必要です。ペアプロは、ひとりで作業する場合に比べて遥かに多くのコミュニケーションが必須となるので、メンバー変動の影響を最小限に抑えるのに有用です。

役に立つヒント

新メンバーをペアに組み入れさえすれば、後は「魔法のように」ひとりでに研修がうまく回ってめでたしめでたし、というわけにはいきません。必ず最初のペアリングセッションで「大きな絵」と「背景となる全体コンテキスト」も新メンバーに伝え、研修期間もある程度長めに確保してください。こうすることで新メンバーが脱落せずについていきやすくなり、ペアリングにもチームに貢献できるようになるので、ペアリングを最大限に活用できます。新メンバーには必ず「新しいPC」をペアリング用に渡し、新メンバーが自分で作業環境をセットアップできるよう取り計らってください。

新メンバーの研修計画には、習得すべき項目のリストも付けましょう。項目によっては、その項目のためのセッションスケジュールを押さえておくのもよいでしょう。その他の項目については、ペアを組み替えるときに新メンバーが自分で研修計画に盛り込んでもよいでしょう。ペアリングセッションでこなした項目は、リストでチェックボックスをオンにするなどして消し込みます。このようにしておくと、研修の進捗がチーム内でひと目でわかるようになります。


  • 第1章: ペアの組み方
  • 第2章: ペアプロのメリット(本記事
  • 第3章: ペアプロの難しい点
  • 第4章: ペアプロは導入すべきなのか、第5章: それでもペアプロする理由

関連記事

銀座Rails#19でZoomによるイベント視聴を体験しました

$
0
0

こんにちは、hachi8833です。先週の銀座Rails#19が、リモートイベント視聴初体験でした。

Zoom開催: 【オンライン開催】銀座Rails#19 @リンクアンドモチベーション - connpass


ginza-rails.connpass.comより

Zoomでイベント視聴に参加してみて

Zoomはここ数か月BPS社内で普及しつつあるので、慣れている分使いやすいですね。銀座Rails#19では少なくとも50人以上(一説には80人ほど)がZoomで視聴していましたが、やはり安定性と品質(特に音声)は驚異的でした。終了間際にごくわずか音声が乱れただけで、トラブルらしいトラブルはありませんでした。

Zoomの有料プランでは、たとえば「プロ」なら100人まで参加でき、それ以上はさらに別料金で開催できるようになっています。今回はこれを用いたとのことです。

参考: プランと価格 - Zoom


zoom.usより

発表者の方はだいたいZoomのバーチャル背景機能を使っていました。部屋の中が丸写しにならずに済みますし、視聴者の側も「部屋見えてますけど大丈夫ですか?」と気を遣わずに済むのがいいですね😋。「自分は部屋見られても一向に構わない」という方も、さしあたってバーチャル背景はオンにしておくといいかもと思えました。

銀座Rails#19ではさらにComment Screenというコメントツールも併用されていました。Comment Streenを別タブで開いてコメントを入力すると、ニコ動的にコメントが右から左に流れます。👍❤といったボタンを押せば絵文字が下から花を散らすように漂います。

このときは、Zoomのチャット機能を「発表者や主催者からのお知らせやURL共有」に、視聴者のコメントはComment Screenにという使い分けをしていたようです。Zoomのチャット機能に入力したメッセージにはユーザー名が表示されますが、Comment Screenにはユーザー名が表示されていなかったので、うまい使い分けだと思いました。

自分はこうしたライブものの視聴が初めてだったので、セッション開始で「8888888」がいくつも流れてきたとき何だろう?🤔と思ったのですが、どうやら拍手を表していることにやっと気づきました。そっち方面の文化知らなかった…😅

こうしたビデオカンファレンスでは、発表者の方が「ひとりで喋っていると届いているのかどうか不安になる」という話もちらほら見かけますが、Comment Screenのリアクションがあることで、発表者の方々も視聴者の反応が目に見えてやりやすそうでした😋

なお、20:30にセッションが終わってZoomの会議が閉じられた直後、Comment Screenの方で「しまった、アンケートのURLを開くの忘れてた!」という叫び声を目にしました。アンケートURLはZoomのチャット機能で知らされていたので、Zoomの会議が閉じられると開けなくなってしまうんですね😳

開催スポンサーのリンクアンドモチベーション様、オンライン開催スポンサーのForkwell様とSONY株式会社様、今回オンライン開催で惜しくも出動できなかったビールスポンサー予定のScoutAPM様、ありがとうございました!。

当日発表より

出張!Railsウォッチについては別記事でお送りします🙇

Rubyでバイナリをパース(小林ノエル)

こちらは今のところサンプルコードが公開されていますね。ここでは詳しく述べませんが、バイナリデータを体当たりでパース、考えるだけで恐ろしい…😱

The Majestic MPA(@f_subai)

@f_subaiさんのセッションは、Railsの非SPAをどうやってよくするかというテーマでした。「フロントエンドエンジニアが多い」「アプリごとにさまざまなバックエンド/フロントエンドフレームワークが用いられている」環境では、WebpackerのようにRailsに寄せるしくみだとやりづらくなることが実感できました。

参考: 今日から簡単!Webpacker 完全脱出ガイド - pixiv inside

次回銀座Rails#20

ペアプロを極めて最強の開発チームをつくる(3/4)ペアプロの難しい点(翻訳)

$
0
0

第3章: ペアプロの難しい点

ペアプログラミング(以下ペアプロ)には実に多くのメリットがありますが、ペアプロには訓練が必要ですし、最初からペアプロがうまく回るとも限りません。チームがペアプロで苦労しがちな点のリストと、克服するための助言をいくつかご紹介します。実際にこうした困難に出くわした場合は、ペアプロの数々のメリットと、自分たちがペアプロする理由を頭に思い浮かべてください。ペアプロを軌道修正するには、自分たちが実践で何を達成しようとしているのかを意識しておくことが重要です。

⚓1. ペア作業で消耗する問題

ひとりで作業するのであれば、いつでも好きな時間に休憩できますし、必要ならば少々現実から逃避してうさばらしもできるでしょう。ペア作業では集中の維持を強いられますし、時間が延長される可能性もあります。また、相手の作業のリズムや考え方の癖について共通点を見い出して合わせていくことも求められます。集中時間が増えることはペア作業のメリットでもありますが、その分かなりエネルギーも必要となり、消耗します。

対策

この問題に取り組むうえで重要なのは、十分な休憩を挟むことです。休憩を定期的に入れるのを忘れていたら、アラームで休憩時間を設定してみましょう(1時間あたり10分など)。あるいはポモドーロのようなタイムマネージメント手法も使ってみましょう。昼食を抜いてはいけません。休憩時には画面から離れてしっかり休憩を取ってください。休憩は、ペアプロと関係なく重要であり、生産性を高めてくれるものです。

消耗を防ぐうえでもうひとつ重要なのは、ペア作業を1日8時間もやらないことです。どんなに長くても1日あたり6時間までにします。ドライバーとナビを定期的に交代するのも、消耗を防ぐのに有用です。

⚓2. 共同作業に集中するのがつらくなる問題

相手と顔を突き合わせて長時間作業するのは相当なことです。相手と絶えずコミュニケーションを交わす必要がありますし、相手の気持を汲むなどの対人スキルも求められます。

「技術」「知識」「スキル」「社交性」「性格」「問題解決のアプローチ」は人によって違いがあるものです。お互いの相性があまりよくないと、出だしからペア組みが厳しくなるかもしれません。そのようなときは変にじたばたせず、お互いがこの経験から学びを得られるように共同作業を改善する時間をしばらく設ける必要があります。

対策

ペアリングセッションの最初の話しかけ方を工夫することで、お互いのスタイルの違いを理解してスタイルのすり合わせを計画する手助けになる可能性があります。初めてセッションを開始するときに「どんなスタイルで共同作業したいですか?」「ペアの組み方について希望はありますか?」という具合に最初に質問します。このとき、あなた自身がどのように作業したいかという希望や、自分がどれだけ効率よくやれるかを意識しつつ、焦って他のアプローチに飛びつかないようにしましょう。やっていくうちに何か発見があるかもしれません。

ペア作業した日の終りには、お互いにひととおりフィードバックしましょう。フィードバックするのが気重に感じられるのであれば、むしろちょっとした「振り返りタイム」だと考えてみましょう。ペアリングセッション中に双方が何を思い、どう感じたかを振り返ります。「気配りはできていたか」「疲れがたまっているか」「これがあったおかげでやりやすかったというものはあるか」「逆にこれのせいでやりにくかったというものはあるか」「キーボード入力の交代頻度は十分だったか」「目標は達成できたか」「次回やってみたいことはあるか」。こうした振り返りは、早いうちにルーチンに組み込んでおくとよいでしょう。そうすることで、何か問題が起きたときに相手にフィードバックする訓練にもなります。

人間関係の衝突や困難(うまく会話できないなど)をうまく扱うのに役立つ、優れたトレーニングや書籍はたくさんあります。

問題点にはチームとして取り組んでください。個人に任せきりにしないこと。たとえば、ペア作業の問題点をどう扱うかを議論するためのセッションを別途開催することで対処する方法も考えられます。このセッションでは、最初にペア作業のメリットを列挙することから始め、自分たちがペア作業で何を得ようとしているのかをはっきりさせます。それが終わったら、各個人がペア作業でどんな困難を感じているかを調査します。これで、グループがどのアクションを改善できるかについて考えられる状態になります。また、チームメンバーのホットボタントリガー(=むかつきポイント)を事前に調べておく方法もあります。「『これをやられたら即嫌になる』ことはありますか?」という具合です。

⚓3. ミーティングが割り込む問題

一日中ミーティングで終わってしまい、その日は何にもできなかったという気持ちになった経験はありますか?おそらく、これはどんなデリバリーチームにでも起きることです。ミーティングは、議論や計画立案、これから作るものについての合意形成には必要ですが、ペア作業の流れを中断するものでもあります。チームがペアプロを実践するときに、ミーティングがあまりに多すぎるとむしろ事態が悪化する可能性もあります。ペアの2人がそれぞれ別の時間にミーティングを抱えると、中断の回数が増えてしまいます。

対策

この問題へのアプローチのひとつは、ミーティングの開催を制限するタイムスロットを設けることです。たとえば「ペア作業のコアタイムにはミーティングは入れられないようにする」「ミーティングを開催できない時間帯を設ける(”午後はミーティングを開催してはいけない” など)」といった方法が考えられます。

ミーティングの長さとトータル時間についても再考する価値があります。「どうしても外せないミーティングはどれなのか」「ミーティングの参加者はどんな目標を持っているのか」「目標の質を改善するにはどうすればよいか」などです。改善には、ミーティングの下準備やファシリテーション(議事進行)を適切に行う、明快なアジェンダを用意するといった方法が考えられます。

しかし「ミーティングはいつ入ってもおかしくない」こともまた確かです。ミーティングにペアが対処するにはどうすればよいでしょうか。ペア作業では、ペアセッションの開始時にカレンダーを2人でチェックし、ペア作業を開始する時間が十分確保されていることを確認しましょう。ミーティングがぶつかっているのであれば、ペアの2人で出席することを検討しましょう。ペア作業のコアタイム中は、チームメンバーをミーティングから遠ざけるよう、プロダクトオーナーや、ペアを組んでない他のチームメンバーにお願いしましょう。

⚓4. スキル差が大きい問題

ペアを組む2人の間に、あるトピックについてレベルの差があると、お互いがどの程度貢献できるかについて見積もりを誤ってしまったり、作業のペースが違いすぎて不満がたまってしまうことがよくあります。

対策

そのトピックについて経験豊富なペアの場合
そのペアが持つ知識が万全だという前提を置かないこと。おそらく、「今の作業をこうやって進めている理由」を明確に説明することを義務付けることで、新たな洞察につながるでしょう。(たとえ自明と思えても)作業の進め方や、その進め方を選んだ理由をお互いに質問し合うことで、話し合いも有意義になり、よりよいソリューションにつながります。
そのトピックについて経験が浅いペアの場合
そのペアが問題解決に貢献できるはずがないという前提を置かないこと。自分が目隠ししたまま立ち往生している可能性もあること、そして別の視点によってよりよいソリューションが得られる可能性もあることを思い出しましょう。コンセプトをはっきり説明することを義務付けることは、自分の理解が本物であるかどうかをテストし、改めて考え抜くまたとない機会となることも思い出しましょう。

初心者からエキスパートになるまでの学習プロセスをうまく回す方法を理解するには、学習のステージは人によってさまざまであることを知っておくことも有用です。Dan Northは以下の「Patterns of Effective Teams」というトークで、この点を実に的確に説明しています。Danはこのトークで、学習におけるさまざまなステージや、ペア作業の文脈においてステージの組み合わせが何を意味するのかを理解するために、スキル獲得のドレイファスモデルを紹介しています。

5. ⚓力関係の問題

力関係(power dynamics)は、おそらく本記事の「難しい点」リストの中でも相当厄介な問題です。ペアプロが行われるのは、上下関係のない世界ではありません。目に見える公式な上下関係(上司と部下など)もあれば、水面下にある非公式の上下関係もあります。水面下の上下関係には以下のようなものがあります。

  • 「若手」と「シニア」
  • 「男性以外」と「男性」
  • 「転職組」と「CS学位持ち」
  • 「有色人種」と「白色人種」

これはほんの一例です。力関係は固定ではなく状況によって変動するものですし、部門の壁をも超えます。ペアを組むときには、こうしたさまざまな力関係が働いたり覆いかぶさってきたりします。力関係の差がペア作業にどんな影響をもたらすかを理解するために、いくつか例をご紹介します。

  • ペアの1人がペア作業のセッションを独り占めする(キーボードを相手に渡さない、相手に余裕を与えない)
  • ペアの1人が教える側にばかり回り、先生ぶるのをやめようとしない
  • ペアの1人が相手に耳を傾けようともせず、相手からのサジェスチョンも冷たく却下する

こうしたシチュエーションをいちがいに上下関係のせいにするのは少々微妙です。単に「この人とはやっていけない」と思っているだけというのもよくあることです。しかし、ペアを組む2人の力関係の差が問題の背後に影を落としていることも、これまたよくあるのです。

Sarah Meiは以下の一連のツイートでこの話題をうまく取り上げています。また、アジャイルにおける力関係のより一般的な問題についても以下の動画で触れています。

アジャイルで目につきにくい問題を突き止めるために、異種混合チームで揉める理由を見ていきたいと思います。目に付きやすい問題の裏に何が潜んでいるかを調べてみましょう。
まずは「メンバーのほとんどがペアをうまく組めない」問題から。


私は長年ペアプロの伝道師として普及に努めていた時期がありました。当時からコンサル時代まで、ペアプロを試した挙げ句すっかり嫌気がさしてしまった人々を山ほど見てきました。中には、数か月間さまざまな相手とさまざまなスケジュールでペアを組んでみたにもかかわらず、好みに合う様式をついに見つけられなかった人々もいます。

皆、性別も人種もさまざまでしたし、内向的な人もいればそうでない人もいました。これらの人々に共通していたものが何だったのか、私も長年わからずにいましたが、最近になってやっと気が付きました。
どの人も、ペア内部の(1つまたは複数の)「力関係」で風下に立たされている時間が長かったのです。


ペア作業における力関係は、マクロ(コミュニティでの議論)レベルでもミクロ(チーム内での議論)レベルでもめったに取り上げられません。私から、まず力関係とは何かについて説明し、次に力関係がペアの間でどのように顕在化するかについて説明します。

対策

この問題に取り組むには、力関係において上位の人物が『自分が(図らずも)力関係で相手より上の立場にいる』ことを正面から認識し、自覚することから始めます。それができて初めて、ペアの相手にどんな圧力がかかっていたか、力関係が2人にどの程度悪影響を及ぼしていたかを率直に振り返る準備が整います。そのときの自分の立ち位置や状況を思い返し、「いびつな力関係を是正するために、自分に何ができるだろうか」と考えてみましょう。

こうしたさまざまな力関係を自覚し、振る舞いを正して共同作業を改善する作業は、さまざまな点において反省を求められるため、当人にとってつらいものになることもあります。この問題を抱えている個人やチームを支援するために、「アンチバイアス」トレーニングや「アライアンススキル」などさまざまなトレーニング法が存在します。

⚓6. ペアで未知の課題に取り組むときの問題

ペア作業のスタイルは、ペアのどちらも解決方法の見当がまったく付かないような大規模なトピックに取り組んでいる場合には、思ったように行かないことがしばしばあります。たとえば「まったく初めての技術を使う必要がある」または「新しいアプローチやパターンを試す必要がある」としましょう。調査や実験をペアで行うことについてはうまくいくこともありますが、どう動くかを見極めるためのアプローチもさまざまですし、資料の読み込みや学習をいつもと違うペースで行うことになるので、ストレスがたまる可能性もあります。

対策

新しい技術を使う場合など、わからないことが多すぎるのであれば、実際に作業を開始する前に、トピックの調査や技術の習得を「スパイク」(XPの手法のひとつ)で行うことを検討しましょう。結果をチームで共有するのをお忘れなく。知識交換セッションを開催して図をチームの部屋の壁に貼る方法なども考えられます。

訳注: スクラムのスパイクとは何ですか? What is Spike in Scrum? : warren_lynchのblog

このような場合は「ペアプログラミング」ではなく「ペア開発」マインドセットを思い出しましょう。ペア開発では、調査を2人で手分けするのは「あり」です。疑問点をひととおり洗い出してから、共同でその疑問を解決する必要があるでしょう。

7. ⚓自分の時間が取れなくなる問題

これまでも申し上げたように、絶え間なく会話を続けるのはかなりエネルギーを使う作業です。しかも、多くの人はその日のスループットを達成するための時間も必要とします。特に「内気な人」にとっては切実です(動画参照↓)。

ひとりで作業していれば、必要に応じてトピックを深堀りしたり学んだりする時間はだいたい自然に取れます。しかしペアで作業すると、そうした時間に割り込まれたような気持ちになることもあります。必要なときにひとりの時間や学習時間を確保するにはどうすればよいでしょうか。

対策

繰り返しますが、ペア作業を1日8時間もぶっ通しでやってはいけません。コーディング用のコアタイムについてチームで合意を取り、どんなに多くても1日6時間を上限としましょう。自習のための時間も数時間ほど確保しておきたいでしょう。

問題にアプローチするためのまとまった知識がこのペアに欠けていると感じられたら、資料を手分けして読んだ後で共有し、それから実装を続けましょう。

⚓8. ローテーションでの頭の切り替え問題

知識の共有はペア作業のメリットのひとつであり、ペアをローテーションするとさらに効果が高まります。しかしながら、ローテーションを頻繁にやりすぎると、コンテキスト(つまり頭)の切り替えも頻繁になってしまいます。

対策

「ローテーションの頻度」や「新しいパートナーとペアを組む頻度」については、ストーリーのコンテキストを見失わず、正しい方法で貢献できる落とし所を見つけましょう。「ローテーションのためのローテーション」にしないこと。あるコンテキストを共有することが本当に重要かどうか、そしてコンテキストを共有する理由を今一度考え、そのうえでコンテキストを効率よく共有できる時間を確保しましょう。

9. ⚓ペア作業では「弱みを隠さない」ことが求められる

ペアを組むときは弱みを見せる1ことが求められる。弱みを見せるとは、自分がわかっていることも、わかっていないことも、ガードを下げて洗いざらいさらけ出すことだ。この作業はしんどい。プログラマーは一般に「頭がキレる」、それも「めちゃめちゃ頭がキレる」と思われているし、普通の人々はプログラマーの仕事ぶりを目にして「自分には絶対無理だわ」とため息をつく。プログラマーはそれを見て、ちょっぴり自分が特別な人間だという気持ちが芽生え、その気持がプライドを生み、そしてそのプライドが「弱みを隠そうとする態度」を生み出す。
The Shame of Pair Programming』Hom Howlett

ペアを組んでいるときに「自分はここがわからない」と率直に話すのを強くためらったり、決定を下すときに不安に駆られたりすることがあります。特に「10倍スゴいエンジニア(10x engineer)」伝説が定期的に生まれる業界や、「どんなプログラミング言語を使っているか」「5年前にどんな設計上の決断を下したか」で相手を値踏みする世界ではなおさらです。

自分のわからない点を率直に述べる行為は、ほとんどの文化圏で何かと弱点と結び付けられがちですし、自分の強さを誇示する行為も普通によくあることです。しかしBrené Brownという研究者は「自分の弱みを認める行為は、実際にはイノベーションや変革において極めて重要な要素である」と講演や書籍で繰り返し述べています。

イノベーション、創造性、変革は、自分の弱みをさらけ出したところからこそ生み出されるのです。
Brené Brown(動画↓)

対策

ガードを下げて自分の弱みをさらけ出すには勇気が必要です。そして、みんなが安心して自分の弱みを率直に話せる雰囲気を作り出すことも求められます。繰り返しますが、これはお互いを信頼できるチームを作れるかどうかにすべてかかっています(1on1やフィードバックの機会を定期的に設ける、気後れせずに質問できる文化づくりなど)。

チームの中で他の人よりも権威の高い人であれば、自分の弱みを見せることはそれほど難しくありませんし、リスクもさほどではありません。権威は(既にチーム内で尊敬を集めていれば)自然に備わることもあれば、(テックリードなどの肩書があれば)肩書が物を言うこともあります。つまり、他の人より権威のある人が率先して「わからないことを率直に話す」役割を引き受けてモデルとなることが重要です。それによって「ここはわかる」「ここがわからない」と当たり前に話せるようにし、他のメンバーが自分のわからない点を安心して話せる空気を作りましょう。

⚓10. 経営陣や同僚の説得に手間取る問題

ペアプロを推進しようとすると、多くの場合、チームの毎日のルーチンにペア作業の時間を組み入れることを経営陣や同僚に理解してもらうために駆けずり回らなければならなくなります。

対策

ペアプロの効能を他の人に納得してもらえるシンプルなレシピは、さすがに存在しません。ただし、この場合に常にキーポイントとなるのは、最初に「ペアプロの効能についてプレゼンする時間を確保する」ことと「全員が同じ理解を共有できるようにする(例: 本記事を読んでもらう🙂)」ことです。それが終わってから、ペアをひと組作って試しにペアプロを行ってその体験を他の人と共有するか、あるいは「次回の2スプリントはペアプロをデフォルトとしてみる」というようにチームでの実験を提案します。結果へのフィードバックや振り返りの場を必ず設け、「ペアプロでうまく行った点」「ペアプロで手こずった点」を共有しましょう。

もちろん最終的に実践を頭ごなしに強要するわけにはいきませんし、万人に有効とも限りません。やってはみたもののチームにペアが1組できただけだった、という結果に終わるかもしれません。私たちの経験から申し上げれば、説得するうえで最も効くのは「実践している様子を定期的に他の人に見せる」ことと「チームメンバーにペア作業のメリットや楽しさを体験してもらう」ことです。

説得中によく受ける質問のトップに挙げられるのが「実践が経済的に見合うかどうか」です。「ペアプロしたらコストが倍になるだけでは?」「品質向上やチームのメリットは最終的にそのコストに見合うのか?」といった具合です。これについてはいくつか研究がありますが、その中でも最もよく知られている『The Costs and Benefits of Pair Programming(PDF)』を引用して、ペアプロに価値があるというエビデンスを示しましょう。ただし私たちは、ペア作業が効果的であることを「科学的に証明」しようとする試みに対しては慎重な立場を取っています。ソフトウェア開発は変化と不確定性に満ちたプロセスであり、コードの裏側には比較や測定(分析、試験、品質など)の困難な知見がたくさん潜んでいます。ペア作業に誠実に反対する人々は、開発の生産性の高さを証明するべくセットアップされた再現可能な「科学的」実験のどこかに穴が空いていないかを常に探しています。結局、ペアプロの効果は「自分の身をもって示す」必要があります。ペアプロを実践するには、自分たちの環境でやってみるしかないのです。


訳注

参考: DeepL翻訳


deepl.comより


  1. 本文と直接関係ありませんが、今話題のDeepLでもこれは難しいだろうと思われるパラグラフを機械翻訳させてみました↑。 

Windows 10 Home 対応の Docker Desktop for Windows を一足早く試してみました

$
0
0

こんにちは、 ebi です。
私物 PC で Windows 10 Home に Insider Preview ビルドを入れて、 Docker Desktop を使えるようにしてみたので軽く紹介しておきます。

サラッと前置き

Docker Desktop for Windows は Hyper-V の利用を前提としており、これまでは Professional エディション以上が必要でした。
ところが新しく提供される WSL2 では、動作のために WSL2 向けの Hyper-V の機能サブセットが Home エディションでも開放され(参考記事)、これを利用することで Docker Desktop も Home で利用できるようになるという話が出てきました。

実際のところ、この対応自体は半年前からお知らせが出ていて、いよいよ WSL2 の機能開放が一般に配布される Windows10 の次期バージョン 2004 が間近で改めてリリースされているのでしょうか?よく分かってない 🤔

とにかく先日の Docker Desktop for Windows Home is here! の案内を皮切りに、今月になって試している方も増えているようです。

①Windows 10 Insider Preview ビルドのインストール

公式の案内 に従っていくだけ。
もちろん、バージョン 2004 の配布以降にこの記事を参照する方には必要ない手順になります。

  • Windows Insider Program を開始する。
  • プレビュービルドを受け取る頻度は「スロー」を選択しました
  • Windows アップデート更新プログラムの確認してからの、ダウンロード、インストール、実際のアップデートまでかなり長いかもしれませんが数回に分けて放置して待ちます。
  • バージョン情報を確認すると、OS ビルドが 19040 以上の表示になってます。これで準備は大丈夫そうです

②WSL2 を有効化します

  • まずは「Windows の機能の有効化または無効化」設定から、 WSL を有効化するのですが、「Windows Subysytstem for Linux」 が見当たらない……と思ったら「Linux 用 Windows サブシステム」と表示されてました
    ネット上の記事を見ると、「Windows Subysytstem for Linux」で表示されている例が多かったのでここが一番の難関です。あとは余裕です。
    (管理者権限でコマンド実行する方法も見かけます。そちらでも大丈夫なはずです。)
  • 有効化したら一度再起動が必要です。
  • この時点では wsl コマンドで実行するディストリビューションが何も入ってないのでインストールを薦められます。案内通り、 Microsoft Store を起動して、 Ubuntu のディストリビューションを入れて動かせるはずです。 wsl2 の利用自体がこの辺の Linux システムを動かすことがセットになっているので、 Ubuntu 等を入れるところまでがセットになっている記事が多いと思います。
    ひとまず Docker Desktop を使いたいだけの場合は無視して大丈夫そうだったので無視します。
C:\Users\ebi>wsl -l -v
Linux 用 Windows サブシステムには、ディストリビューションがインストールされていません。
ディストリビューションは Microsoft Store にアクセスしてインストールすることができます:
https://aka.ms/wslstore

③Docker Desktop for Windows をインストールします

  • ここから ダウンロードします。記事執筆時点では、 Edge を選びます。
  • インストーラを実行して、またまた再起動を挟みます。
  • 起動後、エラーの通知ポップアップが右下に出てました。記載通りの URL にアクセスして指示に従います。
Docker.Core.Backend.BackendException:
Failed to deploy distro docker-desktop to C:\Users\ebi\AppData\Local\Docker\wsl\distro: exit code: -1

中略……

  stdout(unicode): WSL 2 を実行するには、カーネル コンポーネントの更新が必要です。詳細については https://aka.ms/wsl2kernel を参照してください
  • (不要かも)気をとりなおして、再度 Docker Desktop のデスクトップアイコンを実行すると、今度は正常動作したような通知メッセージが右下に表示されました。
  • wsl でディストリビューションを確認すると、 docker-desktop が追加されています!
    VERSION が 2 と表示されているのが WSL2 を使っている、と言うことのようです。
C:\Users\ebi>wsl -l -v
  NAME                   STATE           VERSION
* docker-desktop         Running         2
  docker-desktop-data    Running         2
  • hello world できたし多分大丈夫大丈夫
C:\Users\ebi>docker run hello-world
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
1b930d010525: Pull complete
Digest: sha256:f9dfddf63636d84ef479d645ab5885156ae030f611a56f3a7ac7f2fdd86d7e4e
Status: Downloaded newer image for hello-world:latest

Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
    (amd64)
 3. The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
 4. The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
 $ docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker ID:
 https://hub.docker.com/

For more examples and ideas, visit:
 https://docs.docker.com/get-started/
  • ちなみに WSL2 を適用する一環で Hyper-V が適用されることで、 VirtualBox を利用する Docker ToolBox で入れた docker-machine は使えなくなったのですが……
C:\Users\ebi>docker-machine start default
Starting "default"...
(default) Check network to re-create if needed...
Unable to start the VM: C:\Program Files\Oracle\VirtualBox\VBoxManage.exe startvm default --type headless failed:
VBoxManage.exe: error: Raw-mode is unavailable courtesy of Hyper-V. (VERR_SUPDRV_NO_RAW_MODE_HYPER_V_ROOT)
VBoxManage.exe: error: Details: code E_FAIL (0x80004005), component ConsoleWrap, interface IConsole

Details: 00:00:03.219324 Power up failed (vrc=VERR_SUPDRV_NO_RAW_MODE_HYPER_V_ROOT, rc=E_FAIL (0X80004005))

VirtualBox も Hyper-V 環境での利用に対応始めている動きはあり、 最新の 6.1.14 にアップデートすることでそれらしく動くようにはなりました。
(どう考えてもコンフリクトして悪影響しかなさそうなので、 Docker ToolBox をもう使うことはないでしょうが。移行を考えている方は一応 この辺が参考になるかもです
これは業務 PC でも怖がらずに Docker Desktop と VirtualBox を共存させる道が見えているのか……?

おわりに

Windows 機での開発に関する話は度々、他の人も記事にしていますが、 WSL2 の登場や VirtualBox の Hyper-V への対応によって、新たな段階に進むのかもしれませんね……!!

落ち着いたら業務 PC でも Docker Desktop for Windows を導入して、改めて自分なりの開発環境構築を模索していければなぁと思います。
また何かの記事でしれっと Windows ならではの情報は発信していくかもです。よろしくお願いします。


週刊Railsウォッチ(20200330前編)Active Record Doctorで診断、Webpacker 5、GitHubのViewComponentとRails 6.1の3rd-party component frameworkほか

$
0
0

こんにちは、hachi8833です。

つっつき前ボイス:「このところフルリモートで自宅作業していると、つい人と口をきくのを忘れちゃいそうになるので、つっつき会参加します😆」「そんなに😆」「ゴミ捨て以外まったく外に出ませんでした😆」「ほぼパーフェクト😆

  • 各記事冒頭には⚓でパーマリンクを置いてあります: 社内やTwitterでの議論などにどうぞ
  • 「つっつきボイス」はRailsウォッチ公開前ドラフトを(鍋のように)社内有志でつっついたときの会話の再構成です👄
  • 毎月第一木曜日に「公開つっつき会」を開催しています: お気軽にご応募ください

⚓Rails: 先週の改修(Rails公式ニュースより)

今回はコミットリストから見繕いました。

⚓PartialRendererをリファクタリング

今回のコミットリストでtenderloveさんのコミット数がやけに多いと思ったら、これでした。


つっつきボイス:「以下の❌マークの組み合わせはありえないので削除したということのようです」「お〜これは頑張った感ある💪」「パフォーマンス的には速くなった部分と遅くなった部分でプラマイゼロに近いようですが、コードはだいぶすっきりしたようです」

diffがかなり増えて申し訳ない。
PartialRendererが複雑でこのところつらかったので、意を決して手を入れることにした。
ビュー内部で誰かがrenderを呼ぶときにいろんな組み合わせのオプションが渡されるが、PartialRendererはそれらを一手に引き受けていたので、まずはいろんなシナリオを表にまとめてみた。

修正前はPartialRenderer❌も含めて全部やっていたので、ObjectRendererCollectionRendererを切り出した。
同コミットより

「今までPartialRendereroptionsで何でも受けられるようになってて組み合わせが爆発していたのを整理したということかな: renderにパーシャル名を渡す場合、コレクションを渡す場合、オブジェクトを渡す場合…みたいな」「renderにコレクションとオブジェクトを両方渡すとか全然意味わかんないし🤣」「それが❌の場合か」「パーシャル名を渡す場合は普通にレンダリング、パーシャル名とコレクションを渡す場合はコレクション内のオブジェクトをeachで回して云々、パーシャル名とオブジェクトを渡す場合はこう、コレクションだけを渡す場合はこう…というふうに上の表で整理したということですね」

「プルリクにも、optionsハッシュがいろんなケースに対応しすぎてたり使われない組み合わせをわざわざチェックしてたりしたみたいなことが書かれてますね↓」「そういう余分な処理を枝刈りしたということか」「こういうリファクタリングは全容を把握してる人じゃないと大変そう😲」「コミット数45は大きいですね」「これでいいんだろうかと首を傾げながら修正してそう」

名前でわかるように、CollectionRendererはコレクションのレンダリングを担当し、ObjectRendererはオブジェクトのレンダリングを担当する。これで各クラスの実装がシンプルになって理解しやすくなった(し、最適化もしやすくなったと思いたい)。そのためにスタックを少々複雑にせざるを得ず、前述のoptionsハッシュを分解してどのレンダラーで使うか決められるようにする必要もあった。
同コミットより

⚓lookup_storeの互換性が壊れていたのを修正

# activesupport/lib/active_support/cache.rb#L57
      def lookup_store(store = nil, *parameters)
        case store
        when Symbol
          options = parameters.extract_options!
          retrieve_store_class(store).new(*parameters, **options)
+       when Array
+         lookup_store(*store)
        when nil
          ActiveSupport::Cache::MemoryStore.new
        else
          store
        end
      end

つっつきボイス:「これは@kamipoさんによる修正ですね」「lookup_storeって何だっけ?」「ああ、キャッシュストアに入ってるオブジェクトを引っ張ってくるのか」「Active Supportのキャッシュなんですね」

lookup_storeは、シングルトンメソッド的に任意の箇所でキャッシュストアの内容を引っ張ってくるのね」「つまりこのメソッドはシングルトンだからどこからでも呼べる」「なるほど」「Railsでコンフィグされていればそれを取れるし、されてなければメモリストアが取れるみたいな」

「Railsでは、こういうグローバルコンテキストから持ってくるみたいな書き方ってあんまりしませんけど、何かで必要になったら使うのかも🤔

⚓insert_allがenumを型キャストしない問題を修正

# activerecord/lib/active_record/insert_all.rb#L178

          def extract_types_from_columns_on(table_name, keys:)
            columns = connection.schema_cache.columns_hash(table_name)
            unknown_column = (keys - columns.keys).first
            raise UnknownAttributeError.new(model.new, unknown_column) if unknown_column

-           keys.index_with { |key| connection.lookup_cast_type_from_column(columns[key]) }
+           keys.index_with { |key| model.type_for_attribute(key) }
          end

つっつきボイス:「insert_allがenumで型キャストしない、そういえばそうなってたような覚えが🤔」「まだinsert_all使ったことない😆」「これとかupsert_allってRails 6からでしたっけ?」「そうですね」

「たぶんバグかな」「今まではinsert_allでenumをサポートする気があんまりないのかと思ってました🤔」「お?」「だってcreated_atとかも作ってくれないですし😆」「あ〜そういうことか、insert_allって結構挙動違うのかも😳」「もしかすると最新のRails 6のmasterでは作るのかもしれませんけど、少なくとも自分のプロジェクトではcreated_atとかも全部書きましたし、そういうものなのかなと思ってました😆」「とりあえずAPIdockでinsertを見た感じでは、created_atとか設定しなさそうだけど🤔」「コールバックやバリデーションはトリガしないけど、enumをキャストしないとは書いてませんね😆」「どっちなんだろ😆

単一のINSERT文で単一のレコードをインサートする。モデルはインスタンス化されず、Active Recordのコールバックやバリデーションもトリガされないが、渡された値はActive Recordの型キャストとシリアライズ処理を通る。
apidock.comより大意


「今回の修正の方向を今後も推し進めていったら、例のactiverecord_import gem↓とだんだん似てきたりして😆」「ありそう😆」「activerecord_importならやれるんだったかな…?あれはオブジェクト自体を渡すからやれると思います☺」「insert_allではオブジェクトじゃなくてハッシュを渡しますから😆」「activerecord_import gemは結構使ってきたけど、ほぼActive Recordと同じように使えましたし、特に困ることもありませんでしたね😋」「その代わりactiverecord_importはレコードの数だけインスタンスを作るから、困るとすればそこぐらいかなと」

「activerecord_importでActive Recordのバリデーションをスキップしてもスピードが足りなくなったときに、Active Recordを使うのを諦めるという選択肢を取ったことありましたよ😆」「insert_allを使いたい場面ってだいたいパフォーマンスと隣り合わせのタイミングになってるはずだし」「Active Recordを使うのを諦めたときは、ああユニケージのアプローチは正しかったんだなって思いましたし😆」「ユニケージということは私に振ってます?😆」「つまりオブジェクトを作るとか無駄なことするより、テキスト処理でズゴっとやるのが一番速いなと😆」「まあ状況によってはそうかもしれませんね😆」「TSV作ってMySQLのトランザクションをオフにしてcopy fromで突っ込むと、ディスク読み出しと変わらないぐらい爆速になりましたし🚀」「🤣

⚓normalize_keysを高速化

# actionpack/lib/action_controller/renderer.rb#L103
    private
      def normalize_keys(defaults, env)
        new_env = {}
-       defaults.each_pair { |k, v| new_env[rack_key_for(k)] = rack_value_for(k, v) }
        env.each_pair { |k, v| new_env[rack_key_for(k)] = rack_value_for(k, v) }
+
+       defaults.each_pair do |k, v|
+         key = rack_key_for(k)
+         new_env[key] = rack_value_for(k, v) unless new_env.key?(key)
+       end
+
        new_env["rack.url_scheme"] = new_env["HTTPS"] == "on" ? "https" : "http"
        new_env
      end

      RACK_KEY_TRANSLATION = {
        http_host:   "HTTP_HOST",
        https:       "HTTPS",
        method:      "REQUEST_METHOD",
        script_name: "SCRIPT_NAME",
        input:       "rack.input"
      }

-     IDENTITY = ->(_) { _ }
-
-     RACK_VALUE_TRANSLATION = {
-       https: ->(v) { v ? "on" : "off" },
-       method: ->(v) { -v.upcase },
-     }

      def rack_key_for(key)
        RACK_KEY_TRANSLATION[key] || key.to_s
      end

      def rack_value_for(key, value)
-       RACK_VALUE_TRANSLATION.fetch(key, IDENTITY).call value
+       case key
+       when :https
+         value ? "on" : "off"
+       when :method
+         -value.upcase
+       else
+         value
+       end
      end
  end

つっつきボイス:「1.24倍速くなってますね」「何のキーだろう?」「コントローラがリクエストするときのキーとかそのあたりかな🤔」「rack_value_forがあるからRackとかenvがらみっぽい」「プルリク↓に書いてあるように、毎回defaults.each_pairで全部引っ張ってたら遅いから、先にenv.each_pairしてからdefaults.each_pairで必要なものだけ設定するように修正したということか」「なるほど😋

このPRでは以下の2つを行う:
* defaultsをすべて新しい環境ハッシュに設定してからenvのエントリでオーバーライドするのをやめて、envの値をすべて設定した後は既に設定済みのdefaultsの値の設定をスキップする。
* case文でlambdaのハッシュを切り替える
同PRより大意

⚓ドキュメント修正

DEPRECATION WARNING: Dangerous query method (method whose arguments are used as raw SQL) called with non-attribute argument(s): “DATEDIFF(updated_at, created_at)”. Non-attribute arguments will be disallowed in Rails 6.1. This method should not be called with user-provided values, such as request parameters or model attributes. Known-safe values can be passed by wrapping them in Arel.sql().


つっつきボイス:「1つ目は、pluckのAPIドキュメントの書き方が古くて、そのとおりにしたらwarningが表示された↑ということみたいです」「ああなるほど、pluckに直接文字列を渡したらいかんと🚫」「Arel.sqlを通しなさいと」「へ〜、pluckにも文字列渡したらダメなのか😳」「orderとかに文字列渡してはいけないというのが入ったのは何となく覚えてますけどいつだったかな〜?🤔」「同じぐらいのタイミングでpluckもそうなった気がする🤔」「別々に対応する理由もなさそうですし☺」「とにかくSQLインジェクション対策のためにはArel.sqlを使う必要がありますね」

# activerecord/lib/active_record/relation/calculations.rb#L177
-   #   Person.pluck('DATEDIFF(updated_at, created_at)')
+   #   Person.pluck(Arel.sql('DATEDIFF(updated_at, created_at)'))

後で調べるとRails 6から文字列を渡せなくなったんですね↓。


# guides/source/asset_pipeline.md#L44
Using the `--skip-sprockets` option will prevent Rails from adding
-them to your `Gemfile`, so if you later want to enable
-the asset pipeline you will have to add those gems to your `Gemfile`. Also,
+this gem, so if you later want to enable the asset pipeline
+you will have to add it to your `Gemfile` manually. Also,
creating an application with the `--skip-sprockets` option will generate
a slightly different `config/application.rb` file, with a require statement
for the sprockets railtie that is commented-out. You will have to remove
the comment operator on that line to later enable the asset pipeline:

「2つ目はアセットパイプラインのガイドの文面がいろいろイマイチだったので修正したということだそうです↑」「修正も英作文レベルみたい」「ついにCoffeeScriptの記述も消されてる😆」「さらばCoffeeScript👋

⚓Rails

⚓Webpacker 5.0がリリース(RubyFlowより)

必要な最小バージョンがNode.js 10.13.0、Rails 5.2、Ruby 2.4になりました。


つっつきボイス:「Webpackの現在のバージョンは4.42.1ですし、WebpackerはWebpackとバージョン合わせる気なさそう」「Webpackも頑張って更新しないといけないうえにWebpackerまで気にしないといけないのって、どうもね…😭」「Webpackerの5を見た感じでは、必要なRubyやRailsやNodeのバージョンが上がったぐらいで、そんなに大きく変わってなさそうでした」「Multiple files per entryってどういう感じなのかな?🤔」「ファイル名が同じjsやcssごとにバンドルするようになったということのようで、以下で言うとhome.jsとhome.cssは同じhomeにバインドされるということみたいです」

# app/javascript/packs

accounts.js
application.js
home.js
accounts.css
home.css

extract_cssについて書かれてるけど、そもそもextract_cssが何をするのかを知らないと意味わからないでしょうね😆」「😆」「Webpackerのオプション以前にWebpackのプラグインとしてcss extract的なものがあるんですよ」「あ、そういうことですか😳」「Webpackで使うプラグインまでRailsと密結合しにいくのって、それはそれでひとつの未来かもしれないけど、それってどうなのかな〜😅

config/webpacker.ymlのextract_cssオプションもtrueに設定しなければならない。
extract_cssは、出力ファイルをCSSパックごとに分割して生成するようWebpackに指示する。このオプションはdevelopment環境ではデフォルトでfalseなので、development環境でCSSバンドルを生成するには明示的にtrueに変更する必要がある。
同記事より

おそらく以下がそれのようです。

参考: webpack-contrib/mini-css-extract-plugin: Lightweight CSS extraction plugin

⚓Webpackerのあり方

「もしかするとRailsとしてはそういう方向に持っていきたいのかも: Railsとして推奨するWebpackプラグインをバンドルして、みんなこれを出発点にしてくれみたいな🤔」「それありそうですね」「Railsって元々そういうところありますし、レールを敷きたいわけだし」「今まで使われてきたSASSがWebpackerでデフォルトで入っているあたりにそれを感じますね☺」「そうそう、Railsが推奨するWebpackの機能は最初からバンドルして便利に使えるようにしとくから、という世界を目指している感じはちょっとある🤔

「そのレールがいい感じのレールになるなら、Rails専門エンジニアにとってはWebpackerでいいかという気持ちになれるかも」「実際その辺のこと考えたくないですし😆」「考えたくない😆」「ゴリゴリのフロントエンド案件ならともかく、管理画面程度だったら、何を入れるかあれこれ考えるよりデフォルトの組み合わせでいこうぜみたいな」「よほど特殊なプラグインが組み込まれるとかでなければ」

「どっちにしろjavascript_pack_tagとかは自分たちでやらないといけませんし、manifest.jsonまでつながる何かを一気通貫で用意しとかないといけないというのは、そのとおりだと思います☺」「そのあたりが事前に決まっていれば細かいエラーに遭わなくて済みますし」「その分フロントエンドエンジニアにはキラワレそうですけど😆」「まあWebpackerは剥がせるようになってますから☺」「たしかに」

⚓ViewComponent: GitHubのViewComponent(Ruby Weeklyより)

ViewComponentの設計哲学: 「驚き最小の原則」に従ってRailsへのシームレスな統合を目指す
同リポジトリより


ViewComponentは、React Componentsにインスパイアされたビューレンダリングクラスであり、データを受け取って出力安全なHTMLを返す。PresenterやDecoratorやViewModelパターンの進化系と思えばよい。
コンポーネントは、ビューのコードが再利用されるほとんどの場面で効果を発揮し、直接テストできるというメリットもある。
同リポジトリより

ViewComponentは、次に取り上げる「3rd-party component framework」とも互換性を保つそうです。

以下の動画が資料として紹介されていました。


つっつきボイス:「GitHubがガンガン使っているというViewComponentと、次の3rd-party component frameworkについて見落としてたので😅、ViewComponentがどういうものなのかとりあえず上に軽くまとめました」「文字どおりビューのコンポーネントか: inspired by React Componentとあるぐらいだから、Reactと同じぐらいのコンポーネント粒度ということなのかな🤔」「こういうふうに↓renderにコンポーネントを書いて使うと」「なるほど」

<!-- 同リポジトリより -->
<%= render(ModalComponent.new) do |component| %>
  <% component.with(:header) do %>
      Hello Jane
    <% end %>
  <% component.with(:body) do %>
    <p>Have a great day.</p>
  <% end %>
<% end %>

「ViewComponent、入れるのには相当抵抗あるけど、もしかするといいものなのかもしれない🤔」「ビューをコンポーネント単位で単体テストしたいというのが開発の動機だったのね」「たしかにこうなっていればコンポーネント単位で試験できる: フロントエンジニアはこういうのをフロントでコードを書いてテストするけど、同じことをRailsのレイヤでやろうとするとこうなるでしょうね🧐」「やろうとしてることは理解できるけど、これを導入するのは勇気が要るというか、フロントエンドに戻れなくなりそうではありますね🤔」「導入するなら今後すべてRailsでやるという不退転の決意が必要ということになりそう」

「Reactにインスパイアされたんだったら、ERBじゃないスタイルにして欲しかったなんて思ったり😆」「まあERB以外でもできるみたいだし😆」「content_tagとかtag.divとか書く↓みたいですけど、書きたくないというか、ここに生HTML書けるならワンチャンあるかなと思ったり😆」「プロジェクトメンバーが全員Railsエンジニアで、フロントエンドにあんまり興味ないという編成なら、ありかも☺」「GitHubはそういう立ち位置なんでしょうね」

# 同リポジトリより
class TestComponentPreview < ViewComponent::Preview
  def with_default_title
    render(TestComponent.new(title: "Test component default"))
  end

  def with_long_title
    render(TestComponent.new(title: "This is a really long title to see how the component renders this"))
  end

  def with_content_block
    render(TestComponent.new(title: "This component accepts a block of content") do
      tag.div do
        content_tag(:span, "Hello")
      end
    end
  end
end

Q: ERB以外のテンプレート言語も使えるか?
A: ERB、haml、slimでテスト済みだが、ほとんどのRailsテンプレートハンドラーをサポートしているはず。
同リポジトリより

「おそらくGitHubはプロダクトが大きくなりすぎて、JSでテスト回すのがつらくなったのかも: ViewComponentならそれだけ読み込めばテストできるけど、フロントだとひととおり全部読み込まないとテストできないものが多いとか🤔」「次のトピックでそこに言及されてました↓」

⚓Rails 6.1で入る「3rd-party component framework」とViewComponent

Rails 6.1で入る「3rd-party component framework」機能に今頃気づきましたが、これも主にViewComponentのためのようです。以下の2つのPRは昨年のです。

このプルリクでは、ViewComponentを含む「3rd-party component framework」のサポートが導入される。
GitHubには4000テンプレートがあるため、Railsビューで以下のつらみがある:
* テストがつらい: 現在のRailsではビューのテストを結合テストかシステムテストでやることを推進しているが、そのためビューだけをテストすればよいというわけにいかず、ルーティングやコントローラ層のオーバーヘッドがあるためにビューのフルテストがつらいことになっている。それによって同じパーシャルがビューごとに何度もテストされるので、ビューをDRYにしたメリットが帳消しになってしまう。
* カバレッジ: ビューのカバレッジが多くのRubyコードで正しく扱われていないためにテストの監査が難しく、テストスイートとのギャップが開いている。
* データフロー: ビューでは、オブジェクトでのメソッド宣言と異なり、受け取ることを期待する値が宣言されない。そのため、レンダリングに必要なコンテキストを判定するのが難しくなり、1つのビューを複数のコンテキストで使い回すと微妙なバグをしょっちゅう踏むようになる。
* 標準的なRubyコードでもつらい: GitHubのビューは、Rubyクラスで期待される標準的なコード品質であっても、ほとんどがメソッドが長いだの条件ネストが深いだのでfailする。
同PRより大意


ViewComponentのメリット:
* ビューを単体テストできる。単体テストならだいたい25ミリ秒、結合テストでも最大6秒。
* カバレッジ: 少なくともカバレッジツールと一部互換性があり、SimpleCovでは一部うまくいっている。
* データフロー: コンポーネントのレンダリングに必要なコンテキストを明確に定義できるので、パーシャルよりも再利用が楽になった。
同PRより

つっつきボイス:「GitHubのビューテンプレートが4000超えてて、テストが重くてつらくてしょうがなかったそうです」「上の『ルーティングやコントローラ層のオーバーヘッドがあるためにビューのフルテストがつらいことになっている』とか、きっとフロントエンド勢から『フロントとAPIの構成にしとけばよかったのに、密結合してるからそういうことになるんじゃぁ😇』って言われそう😆」「😆」「『そんな無理しなくてもFirebaseでも使えば、フロントエンドでテスト書いてテストできるよ😋』って言われたりして🤣」「🤣

「カバレッジを改善したいという意図、理解できる」「これもフロント勢に言わせれば…(ryってなりそうですけど😆

「まあRailsのビューにもうひとつ別のソリューションが入ることは悪くないと思いますね☺」「言えてる」「3rd-party component frameworkでやれば新しい言語覚えないで済むし😆」「ただGitHubがもし仮に最初からやり直せるとしたら、果たしてViewComponentを選んだだろうかって思ったり🤣」「それな🤣」「歴史的な理由でこれになったんじゃないかと😆

「もしかすると3rd-party component frameworkって、いわゆるActiveとかActionが名前に付かないコンポーネントを導入できるという意味なんだろうか??🤔」「今でもgemを使えば入れられますし😆」「そういえば何が違うんだろう?」「そういう位置づけは詳しく見ないとわからないな〜🤔

「とりあえず#36388の『いいね👍』数がめちゃくちゃ多いし期待されてそうではありますね」「Railsの最大手ユーザーのひとつであるGitHubが使ってるから、という勢いで押されてたりして😆」「サードパーティといいつつ今のところGitHubぐらいしかなさそうですけど😆」「とりあえずViewComponentそのものは、コンポーネント単位でテストできますし、自分は割とキライじゃないですね👍」「今の世の中でスクラッチからRailsプロジェクトを立ち上げるときに使うかというと、ちょっと考えますけど😆

「以下もViewComponent的なものらしいです↓」「コンポーネントを組み合わせてビューを作るという考え方は、それこそいにしえの昔からあるヤツですね: それこそPHPでSymfonyのバージョン0.x系使ってた頃から、コンポーネントを作ってその中にコンポーネントスロットを作ったりとか、そういう名前でありましたし🧐」「いわゆるビューモデルというヤツ」「概念としては新しくない」「まあそんなにキレイに作れるかどうかはまた別😆

参考: ビュー・モデル - Wikipedia

⚓🌟Active Record Doctor: ARの問題を診断🌟Ruby Weeklyより)

Active Record Doctorでやれること:

  • インデックス化されてない外部キーのインデックス化
  • 不要不急なインデックスを検出する
  • 外部キー制約付け忘れを検出する
  • 未定義テーブルを参照しているモデルを検出する
  • uniqueインデックスがないuniquenessバリデーションを検出する
  • non-NULL制約付け忘れを検出する
  • 存在バリデーション付け忘れを検出する
  • booleanカラムに誤って付けられた存在バリデーションを検出する

つっつきボイス:「Active Record周りをチェックしてくれるのね😋」「なるほど😋」「これは何も考えずに入れておいていいgemかな👍」「CIに入れてもよさそうですね😋」「指摘もらって、それが正しいかどうか確認するだけでも意義ありそう」「単にやり忘れてたとか、そのうちやるつもりでそのままになってたなんてことはいつでもありますし」「外部キーを付けることも面倒で忘れやすい😆」「こういうのに怒られたらしょうがないという気持ちになれますし」

「uniquenessバリデーションあるのにuniqueインデックスが張られてないって😆」「ありがち😆」「もう正しさしかない😆」「う、やっちゃってるかも😅」「モデルのバリデーションって、一種気休めというか、本質的じゃないですし」「uniquenessチェックで毎回SELECTするのにuniqueインデックスないのはヤバい😆」「本当に信じられるのはunique制約エラーぐらい: create_or_find_byというメソッドがRailsに生えてるぐらいですし」「下手するとそこでINSERTが詰まる😆

久しぶりに🌟を進呈いたします。おめでとうございます🎉

⚓Facadeパターンでパフォーマンスとメンテナンス性を改善(Ruby Weeklyより)

# 同記事より
module Books
  class IndexFacade
    attr_reader :books, :params, :user

    def initialize(user:, params:)
      @params = params
      @user   = user
      @books  = user.books
    end

    def filtered_books
      @filtered_books ||= begin
        scope = if query.present?
                  books.where('name ILIKE ?', "%#{query}%")
                elsif isbn.present?
                  books.where(isbn: isbn)
                else
                  books
                end

        scope.order(created_at: :desc).page(params[:page])
      end
    end

    def recommended
      # ネストしたfacadeがここにある。
      # ビューの`Recommended Books`の責務は1つなので
      # これを切り出すことでカプセル化とテストしやすさが改善される
      @recommended ||= Books::RecommendedFacade.new(
        books: books,
        user: user
      )
    end

    private

    def query
      @query ||= params[:query]
    end

    def isbn
      @isbn ||= params[:isbn]
    end
  end
end

つっつきボイス:「普通のFacadeパターンかなと思いつつ」「普通にFacadeパターンですね😆

「Facadeって、GoFのデザインパターンの中でも『これってパターンというほどのものかしら、ただの切り出しでは😆?』という気持ちになりますね」「😆」「『ここはFacadeパターンでやりました』とか言うとスゴいことやってるような響きありますけど😆」「オブジェクト指向というほどでもない感☺

[保存版]人間が読んで理解できるデザインパターン解説#2: 構造系(翻訳)

⚓その他Rails


つっつきボイス:「『ロード・オブ・ザ・リング』のセリフなどをいろいろもじってたのが楽しかったので🧝‍♂️」「普通にRuboCopでレガシーコードと戦う記事💣」「3306 files inspected, 12418 offenses detectedとか見るとドキドキする😆」「やべえ😆

「Todo or not Todo😆」「You shall not passはガンダルフの『ここは通さぬ!』↓ですね😆」「😆

参考: ガンダルフ - Wikipedia

つっつきボイス:「*_previously_changed?きたー😆」「dirtyってどこまで使えるのかドキドキしちゃう😆」「from: nil, to: "Tesla"とか書けるようになるとは」

# 同記事より
car = Car.new
car.changed?                                      # => false
car.company = "Tesla"
car.changed?                                      # => true
car.company_changed?                              # => true
car.company_changed?(from: nil, to: "Tesla")      # => true

「何のpreviousなんだろ?🤔」「値がnilから"Tesla"に変わったということか」「dirty絡みっていろんなメソッドがあって覚えにくい😆」「changed?は保存するとfalseに変わるようになったんですね↓」「サンプルコードでfrom:to:付けても付けなくてもbooleanが同じだと違いがわからん😆」「ちょい見づらい😆」「reloadするとクリアされるということか」「reloadに対応するのは無理そうかな☺

# 同記事より
car.save
car.changed?         # => false
car.company_changed? # => false

car.reload
car.company_previously_changed?                               # => false
car.company_previously_changed?(from: nil, to: "Tesla")       # => false

「自分はDocker for Windowsは普通に使う分には今のところそんなに苦労してないかな〜😋


「チュートリアルにさらに解説動画があるという😆」「Railsチュートリアル大きいから😋」「こうやって裾野が広がっていくのはいいですね👍


前編は以上です。

バックナンバー(2020年度第1四半期)

週刊Railsウォッチ(20200317後編)Strangler Figパターンでリファクタリング、ペアプロ実践記事、イミュータブルデータモデルほか

今週の主なニュースソース

ソースの表記されていない項目は独自ルート(TwitterやはてブやRSSやruby-jp Slackなど)です。

Rails公式ニュース

Ruby Weekly

RubyFlow

160928_1638_XvIP4h

週刊Railsウォッチ(20200331後編)Ruby製プロセスマネージャOvermind、pgsyncで手軽にワンショットDB同期、DeepLの機械翻訳を試すほか

$
0
0

こんにちは、hachi8833です。リモートワークもさまざまですね。


つっつきボイス:「リモート環境は人それぞれ☺」「家族と住んでいると制約が発生しがち」「あ〜たしかに」「自分は独り者なのでリビングをオフィス環境にしてます😋」「こうなってくると、家賃が高くて狭い都内より、家賃も安くて広々した地方に引っ越す方がよかったりして😆」「伊豆あたりとかよさそう♨」「家にいる時間が長いと都心に住む意味あんまりないですし😆

「以下のツイート写真はちょっと前に話題になったDHHのリモート環境で、この記事でBasecampの他のメンバーとともにリモート環境を晒してます」

「記事に載ってる皆さんお部屋が随分広そうですけどご自宅なんでしょうか?」「はい、皆さんご自宅でらっしゃるようです🏠」「勇気あるな〜😆」「晒したい人はいますし☺」「日本人でも晒してる人いますヨ😆」「こういうジャンルありますし」「(ここでつっつきに参加)Jason FriedとDHHがいるからBasecampの記事ですか☺」「名前知ってるとはスゴい」「その2人だけですけど😆

このお部屋とかめちゃ狭くてトイレかと思っちゃいました😆」「左右のイボイボは防音材か何か?」「マイクが据え付けてあるし、画面もDTMっぽいからそういうのをやってるのかも🎵」「DHHのところに取り消し線でshow offってあるのは『見せびらかす』という意味です😆」「うらやましい😆」「融通利きやすくなってきてますし、地方ならやれますよ😋」「温泉のあるところにしたいです」

  • 各記事冒頭には⚓でパーマリンクを置いてあります: 社内やTwitterでの議論などにどうぞ
  • 「つっつきボイス」はRailsウォッチ公開前ドラフトを(鍋のように)社内有志でつっついたときの会話の再構成です👄
  • 毎月第一木曜日に「公開つっつき会」を開催しています: お気軽にご応募ください

⚓Ruby

⚓RubyGems.orgが統計情報を公開(Ruby Weeklyより)


stats.rubygems.orgより


つっつきボイス:「RubyGems.orgの統計情報って前から出してたような?」「あ、そんな気もしてきました😅」「ダウンロード数とかはあったと思うので情報が増えたのかも🤔

「Ruby 2.3が一番多いんですね😳」「bundlerで取ってきた数とgemコマンドで取った数とか」「CI vs non-CIはnon-CIが圧倒的に多いけど、これは単に検出できてないのではという気もしますね☺」「野良gemも含めるとnon-CIが多いのかも🤔

⚓Overmind: Evil Martians製プロセスマネージャ(Ruby Weeklyより)

tmuxを用いたプロセスマネージャです。冒頭の記事ではForemanからOvermindに乗り換えたそうです。

参考: tmux - Wikipedia


つっつきボイス:「デバッグに便利という触れ込みみたいです」「tmuxに依存してる?また不思議なものを😆」「ああ、tmuxにアタッチすればいつでも見えるから楽だよという話か」「なるほど〜」「docker logsみたいなことをtmuxでやろうとしてるのかな、なんて😆」「たしかにdocker logsでやると全ストリームが表示されてうんざりしそうですし😆」「ローカル環境前提であれば、これはこれで思い切ったやり方としていいかも😋

「もしかしたら意外と使いやすいかも?」「ほんとに使いやすいかも😍」「ワンチャンあるかも😆」「でも自分の環境は残念ながら未だにGNU screenでやってるからtmux入れてない😇」「😆

参考: 作業がグッと楽になる screen を使おう! - bacchi.me

「この間dev環境staging環境本番環境でまったく同じコマンドを入れようとしてそれぞれの環境変数まで設定し終わってから、こういうときに何かうまいやり方あったよなと思ったら、それはscreenじゃなくてtmuxの機能だったということに気づいてシュンとなっちゃいましたし😭」「それできないとつらいっすね😅

「記事ではForemanからOvermindに乗り換えたとありました」「Overmindはたぶんtmuxがないと実行できなさそう」「インタラクティブなシェルが使えなかったりするとtmux動かないかもしれない😅」「Heroku環境なんかはそうかも🤔」「おそらくですけど、tmuxセッションを使うにはターミナルの環境変数とかを拾えて、cursesでカーソル移動とかを制御できる端末が必要なんじゃないかな〜」「なるほど」「実行環境がそういうのを返せない端末だとtmuxの起動が失敗しそうな予感: まあ普通のローカル環境ならまず大丈夫ですけど☺

参考: curses - Wikipedia


なおOvermindといえば、アーサー・C・クラークの最高傑作との呼び名も高い『幼年期の終り』に登場する、一種の神のような存在の名前です⛩。古典SF好きのEvil Martiansらしいですね😋

参考: 幼年期の終り - Wikipedia

⚓春のコード掃除(Ruby Weeklyより)


つっつきボイス:「Spring cleaning、ちょっとカッコいい❤」「たまにはbundle cleanしようぜと」「yarn autocleanは最近見かけますね☺」「gitのpruneも」「たまにはお掃除してさっぱりしよう☺

「そういえば、gitのマージ済みブランチを削除しないでずっと残しているプロジェクトでgit branch -aしたときの絶望感はヤバい😆」「😆」「あ、ブランチ表示が大量になるんですね😅」「ブランチ残されたまんまはツラい😢」「残すならタグにしましょうと」「最近うちのチームではGitLabでマージリクエストするときにdelete branchesチェックボックスをオンにしてますけど☺


見出しより:

  • 依存関係をお掃除
  • gitブランチをprune
  • 使われてないルーティングやビューを削除
  • 使われてないテーブルやカラムをチェック
  • バリデーションや制約に抜けがないかをチェック
  • migration/フォルダをお掃除
  • ツールでさらにお掃除

なお以下はつっつき後に見かけたツイートです。

⚓str_metrics: Rustも用いた文字列メトリクスgem(Ruby Weeklyより)


つっつきボイス:「何のメトリクスだろう?」「Sørensen–Dice…だと?」「いかにもスウェーデンの人名ですね」「(ググって)ソーレンセン-ダイス係数か!」「統計の手法でしたか😳」「文字列同士の評価方法として、こうした距離とか類似度とかを出せるということみたいですね😋」「レーベンシュタイン距離ってあったわそういえば」「なるほど、そういうメトリクス」

# 同リポジトリより
StrMetrics::SorensenDice.coefficient('abc', 'bcd', ignore_case: false)
 => 0.5

StrMetrics::Levenshtein.distance('abc', 'acb', ignore_case: false)
 => 2

「機械学習とかで使う感じでしょうか?」「統計や自然言語処理なんかでも使うヤツでしょうね」「最初数値の意味がぜんぜんわからなかったけど、そういうことでしたか😳」「文字列と文字列がどのぐらい似てるかを調べられるライブラリということで☺

⚓その他Ruby


つっつきボイス:「これはツボる🤣」「最大10年延ばせる🤣」「そういえばいらすとやさんが早速TOKYO 2021のイラスト出してましたし😆↓」「仕事速!」「商標申請とかされる前に仕上げちゃってるという😆

参考: TOKYO 2021の検索結果 | かわいいフリー素材集 いらすとや

「いらすとやがスゴいのは、TOKYO 2020のイラストがないところ」「ああっ😆」「そうかっ😆」「2020だとたぶんオリンピック委員会の許可が必要でしょうし☺」「実際のオリンピックはTOKYO 2020の名前のままやるらしいですけど」「まあこの先どう変わるかわかりませんし☺


つっつき後に見つけた、今週土曜開催の「Online Ruby Wine」カンファレンスです↓。参加費5ドルで、「ワイン片手に参加しよう🍷」だそうです。レジュメのあちこちにロシア語があったりしますが、「Work language: English」だそうです。

⚓DB

⚓pgsync: PostgreSQLデータベースを同期(StatusCode Weeklyより)

WebOps Weeklyマガジンが、いつの間にかStatusCode Weeklyと名前を変えていました。ツールを紹介するオンラインマガジンという触れ込みです。

よく見たらRubyで書かれていました。ankaneさん作です。


つっつきボイス:「普通のツールかなと思ったんですが、★が1,500個とかなり多かったので」「syncとあるからレプリケーションするのかと思ったけど、どちらかというとrsync的なバックアップとかに使うワンショット同期ツールっぽいですね」「名前のとおりぽすぐれ専用」

「synchronizationという言葉をデータベースの文脈で使う場合、普通は常に同期しっぱなしにすることを指すと思うんですけど、このツールはワンショット的にデータを取るのがメインのようです☺」「ふむふむ」

# 同リポジトリより
groups:
  product:
    products: "where id = {1}"
    reviews: "where product_id = {1}"
    coupons: "where product_id = {1} order by created_at desc limit 10"
    stores: "where id in (select store_id from products where id = {1})"

「こういう設定↑ができるようなので、たとえば最新の本番データを元にステージング環境のデータを整えるときに使うといいかも😋: 特定のデータをスクランブル化するみたいな作業もyamlで書けるみたいですし↓」「おぉ〜😍」「なかなかよさそう👍: こういうツールは書式がきちんと決まっていて誰でも安心してデータを作れるのが大事ですね☺

# 同リポジトリより
data_rules:
  email: unique_email
  last_name: random_letter
  birthday: random_date
  users.auth_token:
    value: secret
  visits_count:
    statement: "(RANDOM() * 10)::int"
  encrypted_*: null

⚓クラウド/コンテナ/インフラ/Linux/Serverless

⚓書籍『Docker/Kubernetes開発・運用のためのセキュリティ実践 ガイド』

https://twitter.com/akihirosuda/status/1242662941331050497


つっつきボイス:「NTTの人が書いたDocker/Kubernetesセキュリティ本で、かなり詳しい目次が記事に載ってました」「たしかにDockerやKubernetesについては基本的なところは固まってきていますし、そろそろ書籍を買って読んでもいい頃かも😋」「セキュリティの話のためにDockerやKubernetesを解説するところから始めてるみたいですね」「必要と思う人なら買ってもいいと思います👍

⚓Lamby: RailsとLambdaを統合(Ruby Weeklyより)


lamby.custominktech.comより


つっつきボイス:「LambdaだからLambyでそのまんま羊の絵というか」「AWSにある公式のインテグレーションツールを使わない理由ってあるんだろうか?🤔」「それもそうですね😳」「デプロイ方法を見た感じではAWSのSAM(Serverless Application Model)でやってるし、独自ツールでやらなくてもよさそうな気がしますけどね☺

# 同サイトより
#!/bin/bash
set -e

export RAILS_ENV=${RAILS_ENV:="production"}
export AWS_DEFAULT_REGION=${AWS_DEFAULT_REGION:=us-east-1}
export CLOUDFORMATION_BUCKET=${CLOUDFORMATION_BUCKET:="lamby.cloudformation.$(whoami)"}

./bin/build

sam package \
  --region ${AWS_DEFAULT_REGION} \
  --template-file ./.aws-sam/build/template.yaml \
  --output-template-file ./.aws-sam/build/packaged.yaml \
  --s3-bucket $CLOUDFORMATION_BUCKET \
  --s3-prefix "APPNAMEHERE-${RAILS_ENV}"

sam deploy \
  --template-file ./.aws-sam/build/packaged.yaml \
  --stack-name "APPNAMEHERE-${RAILS_ENV}-${AWS_DEFAULT_REGION}" \
  --capabilities "CAPABILITY_IAM" \
  --parameter-overrides \
    RailsEnv=${RAILS_ENV}

参考: AWS サーバーレスアプリケーションモデル - アマゾン ウェブ サービス

「もしLambdaでRailsをやるとすると、リクエストのたびにコンテナが起動することになりそう😆: Rails.applicationあたりに流し込むLambdaコードを書くとかすればやれるでしょうけど」「リポジトリを見るとRails & AWS Lambda Integrationって書いてあるから、本当にやってるのかも」「ホントだ」「Quick Start↓を見た感じだと、普通にRailsのルーティングとコントローラを書いて…と、Rails.applicationに流し込むみたいなことをこのLambyがやってくれるということかな?」「じゃコードはRailsのでいいんだ😳」「めちゃ重そうだけど😆」「こういうのを最短でやれる方法がどこかにあるのかもしれませんね」「Railsのフットプリントを相当小さくしないといけないでしょうけど」

# 同サイトより
Rails.application.routes.draw do
  root to: 'application#index'
end

class ApplicationController < ActionController::Base
  def index
    render html: '<h1>Hello Rails</h1>'.html_safe
  end
end

「だからskipをいっぱい付けて少しでも軽くしてるのかも↓」

$ rbenv shell 2.5.5
$ gem install rails
$ rails new my_awesome_lambda \
  --skip-action-mailer --skip-action-mailbox --skip-action-text \
  --skip-active-record --skip-active-storage --skip-puma \
  --skip-action-cable --skip-spring --skip-listen --skip-turbolinks \
  --skip-system-test --skip-bootsnap --skip-webpack-install
$ cd my_awesome_lambda

「ちなみに、LambdaはAWS上で起動するたびにコンテナのデータを取りに行くところから始めてるわけではなくて、ウォームアップさえしてあれば2回目以降は同じコンテナを使うような実装になってます」「なるほど」「この場合ウォームアップって何でしょう?」「ああ、ウォームアップはHerokuとかでやってるようなのと同じで、最初に起動するときは遅いけど、2回目以降は前回のコンテナを破棄していなければ再利用するというモデルですね」「そうでしたか!」「なので最終的に読み込むファイルサイズが大きくても、OSのファイルキャッシュがふんだんに効く実装になっていれば2回目以降は初回起動よりは速いはず🧐

「ただ、Rubyで読み込む処理は速くならないはずなので、DSLみたいに読み込んで処理してから動かすコードをなるべくなくして、ファイルに入っているコードをそのまま読み込めば動くような形にしておいて、OSのファイルキャッシュが効きやすくする方が、おそらく速くなるでしょうね☺」「なるほど!」「そういう工夫をしないと速くならなさそう🤔」「どっちにしろ遅いんじゃないかという気もしますけど☺

「そもそもActive Recordなんかも、キャッシュがない状態でアクセスが始まるとスキーマチェックとかが走り出すから、何をするにも遅くなりがちですし」「ですね」「そういう部分をなるべくスキップするなどの細かいチューニングをしないとなかなか速くならないでしょうね」

「お、Lambyのデータベースコネクションのところを読むとDynamoDBを強くおすすめしてるし😆」「そういう面倒を避けたいんでしょう、きっと😆」「RailsはもともとLambdaで動くように作られていませんけど、データベースアクセスが不要またはデプロイしたSQLite3データにしかアクセスしないような、うんと小さいRailsコードならLambyは案外うまくはまるかも知れませんね☺

⚓その他インフラ


つっつきボイス:「ザンジバルってこういうスペルなんだ」「アマチュア無線でしか見かけたことない地域名🗺」「ガンダムで見たかも」

参考: ザンジバル - Wikipedia
参考: ザンジバル (ガンダムシリーズ) - Wikipedia

「このZanzibarは許認可系を扱うみたい」「Googleの共通認可基盤って、それだけで普通じゃない響き👽」「きっとヤバい😆」「この間のGCPの話に通じてそうですね(20200309)」「秒間100万リクエストをさばくって…😅」「こういうものが既にあるならそれに乗っかりたい😆」「記事は論文を解説してるんですね☺」「大量のリクエストをさばかないといけない認証基盤みたいなものは他にもあって実装もいろいろあるので、論文を見ればどういう部分が難しいのかみたいなことがまとまってそう😋

⚓JavaScript

⚓MicrosoftのReact Native for Windows


つっつきボイス:「BPS社内Slackで盛り上がってましたね」「WindowsのReact Nativeって誰が使うんだろうというのはさておき😆」「マイクロソフトがVSCodeでTypeScriptを推してる流れなのかなと」

「そういえばXamarinってどうなったんでしょうね」「たしかにXamarinどうなったんだ!」「技術的に筋悪じゃなさそうだったけど」「人気の違いなのかな?🤔

参考: Xamarin ドキュメント - Xamarin | Microsoft Docs

「React Native for Windowsも先のことはわかりませんけど☺」「Electronだとちょっと機能が足りないみたいなときにReact Native for Windowsを使うという感じなんだろうか?🤔」「最終的にUWPアプリという位置づけになるのかなという気がしてます」「JavaScriptの共通フレームワークと、プラットフォーム独立のインターフェイスがある、でもあくまでネイティブで動かす、というのがReact Native for Windowsなのかな〜」「Electronだと結局ブラウザエンジンを使ってるだけだから、ネイティブのReact Native for Windowsとは違うんでしょうね🤔

参考: Electron | Build cross-platform desktop apps with JavaScript, HTML, and CSS.
参考: ユニバーサル Windows プラットフォーム (UWP) アプリとは - UWP applications | Microsoft Docs

⚓言語・ツール

⚓DeepL: Google翻訳のライバル登場か


同サイトより

英文を入力すると自動で日本語文が出力され、日本語文を入力すると自動で英文が出力されるなど、細かな点もよくできてますね😋


つっつきボイス:「最近話題のDeepL」「機械翻訳がこうやって進化すると、英語が苦手という人の障壁が下りそうでいいですね😍

「DeepLを試そうと思って、この間出した記事原文の中から、これは難しいだろうと思えるパラグラフを機械翻訳させてみたら、案の定ひっかかりました↓」


deepl.comより

「vulnerabilityは普通ほぼほぼ『脆弱性』と訳されますけど、人間を形容するときは『ガードを下げる』とか『隙や弱みを見せる』とか『いじめられやすい』みたいなニュアンスになります」「ははぁ、しかも原文にProgrammersという言葉も入っているからよけい判定難しそう😆」「invulnerabilityもこの場合は『不屈の精神』というより『弱みを隠そうとする』という流れかなと😆」「こうやってコンテキストから意味を読み取るのはどうやっても難しいでしょうね😆」「まあ変だと思ったら原文見ればいいし😆」「私もDeepLといういいセカンドオピニオンができてうれしいです😋

DeepLに限りませんが、ときどき原文の一部が訳文ですっぽ抜けることがありますね。

「ほほぅ、DeepLに『数え役満』食わせてみたらbeing worthy of the countになった😆」「😆」「以前Google翻訳で『数え役満』を食わせたらcounting officer Mitsuruって出たんですよ🤣」「(爆)」「スゴい🤣」「さすがに今は変わったみたいですけど🤣」「サイコーでした😆」「そういう意地悪問題を思い付ける人もスゴい😆

参考: 麻雀の役の英語一覧はこれだ! | 調整さん

「ディープラーニングの機械翻訳は辞書に載ってない用法も扱えるから、ローカリティの高い文章はむしろ強そうですけどね」「それはありますね: 過学習(overfitting)になることもありますけど」「こういうツールは、自分が6割ぐらいわかっているものを翻訳させる分にはよくて、逆にまったく知らない分野の文章を翻訳させるとドハマリしそう😆」「やっぱり訳文を自分が吟味できないとつらいですね😅」「機械翻訳が一番向いてるのはやっぱりマニュアルかな〜」「業務で使う分にはいいと思います😋

参考: 過剰適合 - Wikipedia

⚓アルゴリズムビジュアル大事典


つっつきボイス:「アルゴリズムビジュアル大事典はどこで見かけたんだっけ…😅」「お、『アニメーション』をクリックするとアニメーションページが表示された」「お〜面白い😋」「よくぞここまで作ったと思います😍


yutaka-watanobe.github.ioより

「こうやっていろんな人があの手この手でアルゴリズムを頑張って説明してくれているんですけど、こくたまにどんなに頑張って説明してもわからない人はどうしてもわからないことがあって、一種思考の断絶みたいなものを感じることはありますね😅」「どちらかというと意欲の方が影響しそうな気がしますけど」「まあそういう人の方が多いですし、そういう人にこういうアニメーションを見せれば多少なりともわかってもらえますし☺

「このアルゴリズムアニメーションは、わかっている人がわかってない人に説明するにはよさそう👍」「後は本人の意欲と😆」「そして本買えと😆

⚓その他言語


つっつきボイス:「プログラミング言語系のSNSで見かけました」「OCaml使ってるのが京大らしいというか」「opamっていうのがOCamlのパッケージマネージャみたい」

参考: OCaml - Wikipedia
参考: opam - opam

⚓その他

⚓ワンツイートでコードを書く(StatusCode Weeklyより)


つっつきボイス:「ワンライナーのTwitter版みたいな感じですね」「140文字縛りでコードを書くのね☺」「140文字でライフゲームってスゲエ😆

「その@bbcmicrobotっていうボットにコードを投げつけると動かしてくれるとかそういう感じなのかな?」「あ、そういうサービスか!」「かなと思って: タイムライン見るとピンク・フロイドのアルバムみたいなドット絵出してたりするし↓」「コードを投げるとGIFアニにして返信してくれるってことかな😋」「いい感じの遊び場🪁

⚓番外

⚓睡眠の科学


つっつきボイス:「いやぁ〜リモートワークの何がいいって、睡眠時間をたっぷり取れること❤」「ホントにホント😍」「今日なんか9時半まで寝てたし😆」「人生における幸福感がしみじみ😂

「自分は意外にも起床時間変わってませんね😋」「何と人間的な😆」「普段でも電車の中でポッドキャストとか聞いてましたし、いつものようにやってるといつの間にか始業時間になってるんですよ😆」「自分は明日何時に起きないといけないときこそ攻めてますけど😆

「記事のアドバイスは『いつもより30分余分に寝てみましょう』でした💤」「あとは運動とかもそうですけど、サウナに行って代謝を促進させるのが効く〜💪」「たしかに😋」「不調だった頃にサウナ行ったら感動的なぐらい体調すっごくよくなりましたし🥰」「デトックスが大事ということですね😋」「何というか体内の水が入れ替わってなかった感じだったし」「サウナ行くと500mlぐらい汗かきますし」


後編は以上です。

バックナンバー(2020年度第1四半期)

週刊Railsウォッチ(20200317後編)Strangler Figパターンでリファクタリング、ペアプロ実践記事、イミュータブルデータモデルほか

今週の主なニュースソース

ソースの表記されていない項目は独自ルート(TwitterやはてブやRSSやruby-jp Slackなど)です。

Ruby Weekly

RubyFlow

160928_1638_XvIP4h

StatusCode Weekly

statuscode_weekly_banner

個別指導塾での「入退くん」の使い方

$
0
0

お久しぶりです。rikaです。
最近Instagramで美味しそうなカフェを見つけ出しては行ってみるという休日の過ごし方でしたが、
コロナの影響で今話題の あつまれどうぶつの森 三昧になりそうな予感です。

さて、2回目の投稿はBPSで運営している個別指導塾での
入退くん実用例をご紹介したいと思います〜! (拍手)

では、スタート

入退くんの利用者別の用途

個別指導塾には以下のような利用者がいます。

  • 当たり前ですが、生徒
  • はたまた当たり前ですが、教師

それぞれ入退くんを利用してもらっていますが、
主な理由は

  • 生徒:保護者への入退室の通知
  • 教師:給与管理

というように、少しだけ用途が違います。
(違う用途でも1つのアカウントで済んでしまうなんて…!)

え、入退くんって入退室の管理だけじゃないの?
と思ったそこのあなたは是非こちらの記事を読んでみてください!

非エンジニアから見た「入退くん」の素晴らしさ

生徒・保護者向けの実用例

さて、いよいよ具体例をご紹介しますが、まずは入退くん生徒バージョンです!

生徒にはQRコードを配布していて、入退室の際にタブレットにかざしてもらっています。
そうすると保護者の方へ入退室の通知が届くようになっているのは皆さんご存じかとか思いますが、
ポイント制度があることはご存じでしょうか…?

退室時にQRコードをかざしてもらうと、今自分が持っているポイントを確認できるようになっています!
また、入退くんでも確認できます!(以下参照)

貯まったポイントは(私たちの塾では)以下のように使えます。

  • 10ポイント:お菓子セット
  • 30ポイント:文房具セット
  • 100ポイント:Amazonギフト券

などなど、楽しみが盛りだくさんです!

ポイントは通常1日1ポイントですが、3時間以上滞在した場合は2ポイント!など
生徒のモチベーションを上げるようなルールを決めています。
※ 夏期講習や冬期講習期間は更に2倍付与しています。

また、急遽休校にします!や、三者面談の案内など
保護者の方に告知したいことは入退くんからメールを送ることも可能です!
実際に、つい先日もコロナの影響で休校になるという連絡を行いました。

このように、

  • 生徒の入退室管理
  • ポイント制度の利用
  • 保護者の方への連絡

を行っています!

特に、保護者の方には入退室時の通知が届くことで
安心できるシステムだと大変ご好評をいただいてます!

教師向けの実用例

教師の方には主に給与計算のために利用してもらっています!
生徒と同じくQRコードを発行して、配布しています。

授業以外の時にそのQRードをかざしてもらって、
月末に入退室簡易集計を見て時給計算をしています。

給与計算は私が行っていますが、1か月間の滞在時間を個人毎に見られるのは
とても助かっています。(ありがとう入退くん)

4月から先生方には授業の際にも入退くんにICカードを
かざしてもらい、給与​​計算をもっと楽にしようと考えています…!

最後に

ポイントの使い方や、給与の計算、勤怠管理…
色々な用途を入退くん1つで解決できてしまうのは
本当に楽だな〜と日々感じています。

生徒に「お菓子以外ないの?」と言われた
のでお菓子セット渡すのはやめようかな…(悩)

関連記事

入退くんのメールが届かないときの対処法

Slackが2020年3月リニューアルでいろいろスゴくなってた

$
0
0

Slackが3月のある日に割と広範囲にリニューアルしたことで、BPS社内でもどよめきと称賛の声がありました。それと同時に、従来からあった機能が再発見されたりもしました。

なお、Slackの新機能の中には有料版でないと使えないものもあります。本記事では基本的に有料版Slackについて書きます。

参考: Slack の料金プラン & 機能 | Slack

新機能

⚓1. サイドバーや検索がリニューアル

リニューアル前がどうだったかを思い出せないので😅以前のSlack公式記事などを見たところでは、以下ぐらいのようです(従来のChannelやDirect Messagesは省略)。

  • 全未読(All unreads)
  • 全スレッド(Threads)
  • 下書き(Drafts)
  • スター付き(Starred)

新しいサイドバー

()内は英語表示です。

全未読(All unreads)
従来どおりの未読まとめ表示
全スレッド(Threads)
従来どおりのスレッドまとめ表示
メンション&リアクション(Mentions & reactions)
自分へのメンションやリアクションのまとめ表示。

メンション&リアクションは、実は有料版には前からある機能だったようですが、私も含めて今回のリニューアルをきっかけに気づいた人も多かったようです。

表示したくない場合は、(Slack全体の設定ではなく)Mentions & reactionsの設定ボタン→をクリックして表示される「フィルタ」で変更できます。

下書き(Drafts)
従来どおりのドラフトまとめ表示

なお、以前はメッセージを書いている最中のチャンネルが勝手にDraftsに移動されてしまいましたが、この動作が廃止されました。書きかけのままチャンネルを切り替えるまではDraftsに移動しません。地味にありがたい変更です。

ブックマーク(Saved)
変更)ブックマークしたメッセージのまとめ表示。

従来「スター付き」だったのが「ブックマーク」に名称変更されました。どうやら「Starredはチャンネルに対してつけるもの」「Saveはメッセージをブックマーク的に保存するもの」という区別に変わったと推測しました。たしかに従来はチャンネルと書き込みの両方がStarredと呼ばれていたので少し紛らわしくはありました。

チャンネル(Channels)
新規)自分がjoinしているorしていないにかかわらず全チャンネルを表示・フィルタできます。

後述のセクション機能でチャンネルを分類するときに、ここで絞り込んだチャンネルをセクションにドロップすると分類がやりやすくなりそうです。

メンバーディレクトリ(People)
新規)Slackにいる全メンバーやユーザーグループを表示・フィルタできます。

App(Apps)
現在利用できるSlackアプリを表示・フィルタできます。

これも前からあったようです。

ファイル(Files)
やりとりしたファイルが表示されます。

⚓2. セクション機能でチャンネルを整理

セクション機能は無料プランにはないそうです。
参考: カスタム項目を使用して会話を整理する | Slack

Slackでjoinしているチャンネルが多くて困っていた方に朗報です。「セクション」を追加してチャンネルを分類できるようになりました。セクションは個人設定なので他のユーザーの表示には影響しません。

なおセクションは従来の「スター付き」(Starred)チャンネルの設定と排他になりますので、セクションに追加したチャンネルにはスターを付けられません。チャンネルにはセクションかスター、どちらか一方のみ設定できます。

セクションの並び順は設定で「アルファベット順(デフォルト)」「利用頻度順」を選べます。

セクションを作成するUIは複数あります。

チャンネル内の🌟をクリックすることで、そのチャンネルをセクションに移動できます。

なお、現時点ではモバイル版(iPhone)のセクションは表示のみで、まだ折り畳みできないようです。Android版はまだセクション自体が表示されていません。

⚓3. 履歴表示とショートカット

ボタン、または時計アイコンで、直近自分が開いたチャンネルの履歴を表示・移動できます。履歴の移動にスレッドも含まれるようになった点がかなりありがたいです。

なお以下のショートカットによる履歴移動は前からあったそうです。

  • Windows: alt + ←alt + →
  • Mac: ⌘ + [⌘ + ]

4. その他

  • サイドバー上部に作成アイコンが追加された
  • ダークモード対応とショートカットでの切り替え
    • OS設定に合わせるオプションも追加予定
  • メッセージボックスのクリップアイコンが稲妻アイコン(ショートカット用)に変わった
    • クリップアイコンはメッセージボックス右に移動しました

⚓参考: BPS社内の声

Pros

  • ブックマーク最高
  • 履歴機能もいい
  • ショートカットで履歴を行き来するときにスレッドも切り替わるようになった
  • チャンネル数多いのでセクションありがたい
  • 前からあったメンション&リアクション機能やキーボードショートカットに気づく機会になった
  • 密かに埋もれていたdraftをまとめて表示して消せるのは良い
  • 書き込み途中でドラフトチャンネルがリストの上に行かなくなった🎉
  • 日本語版UIが前よりよくなったので日本語版に切り替えた

Cons

  • サイドメニューのunread通知が目立たなくなった分、気づきにくくなったかも
  • Android版にもセクションを表示して欲しい
  • 操作が微妙に変わった:
    • チャンネル検索が入力中の補完効かなくなって、Enterキー押さないといけなくなった
    • 検索したチャンネルをクリックしてもプレビューに移動せず、サイドメニューが表示されるだけになった
    • サイドメニューにあるボタン↓はチャンネル切り替え用なのに、ツールチップに「メッセージを送信する」と表示されるのが紛らわしい

  • ミュートや退出をチャンネル領域のチャンネル名クリックでできなくなった
    • サイドバーのチャンネル名右クリックでの操作は従来どおりできます
    • /leave/muteで代替する手もあります

  • サイドバーの通知アイコン(鐘)がなくなった
    • 上述の作成アイコンに置き換えられたためのようです。
    • サイドバーの自分の名前をクリックして「おやすみモード」(Do not disturb)で設定します

欲しい機能

  • セクションを開閉するショートカットが欲しい
  • 未読メッセージがあるチャンネルは、セクションが閉じていても表示されるオプションが欲しい

関連記事

はじめてのSlack(初心者&非エンジニア向け)

Slackのシンタックスハイライト付き「スニペット機能」は使わないと損

契約したばかりのサテライトオフィスを解約します

$
0
0

どうも渡辺です。意気揚々と増床目的で契約した、本社から徒歩1分くらいのサテライトオフィスですが、速やかに解約します。勉強会主催や大人数MTG目的で確保していた本社のスペースがあいたからです。コロナ騒ぎの真っ只中、密室・密集・密接の3条件が揃った環境でやるのもね。

加えて、1月の後半から希望者はリモートワークに移行していたのでオフィスにいる人も減ってきてますしね。全国的に辛い状況ですし、3ヶ月でも過ごしたオフィスを手放すのは寂しいですが、こんな時だからこそ前向きに打てる施策、もろもろ回復するまでに余裕使い切らないように頑張ろう、です。

日経平均が24000円を初めて超えた2018年あたりから、今後絶対不景気になるから準備しておこう、といって2年間たちました。こんなに急激に、しかもウイルスが原因で経済が止まることになったのは想定外でしたね。仕事の対策できてますけど、コロナ対策は多少バタバタしそうです。

こんな状況ですが、皆で健康も、成長も、業績も、前向きに、良くしていきます。

Viewing all 2925 articles
Browse latest View live