satoryuの日記

忘れっぽいから覚えてるうちに書いておかないと。

「服従の心理」を読んだ

服従の心理 (河出文庫)

服従の心理 (河出文庫)

先日参加したアジャイルジャパン新横浜サテライトで視聴した基調講演の中で、この本が取り上げられていた。 どういった文脈でこの本に触れていたのかは記憶があやふやだったのだが、フラットな組織についての文脈だったと思う。

この本は、人が権威に服従する仕組みを調査する実験について、その実験方法から被験者の記録、分析について、実験を主導したミルグラム本人によって書かれたもの。ミルグラム実験がどういうものかについては、Wikipediaにも掲載されているのでそちらを読んでください。

ja.wikipedia.org

この本を読んでいて思うのは、人間が恐ろしくもたやすく権威の支持に服従してしまうということだ。 ミルグラム実験では、被害者(実際には実験する側である役者)に対して、被験者が電撃を加える。電撃の電圧を上げていくなかで、被害者が実験の中止を求めたり、体調不良を訴える。もし被験者が実験者に対して実験の中断を申し出ても、実験者は継続するように促す。 ここで被験者はどう行動するのか?

これを実験に参加していない人にアンケートを取ると、被害者が申し出た時点で被験者は実験を中止するだろう、と回答する。 特に、大学生、特に心理学を専攻している学生であるほど、より早く中止するだろうと回答している。 しかし、実験結果は、最悪のもので、被験者のほぼ全員が加えられる最大の電圧まで電撃を与え続ける。

支持の内容が非人道的だとわかっていても、責任が権威にあるとして続けてしまう。社会の中で生きるように進化してきた人間として、権威からの支持に従うことで社会の構成員として存在意義を示す傾向にあるとも言われている。

実験の種類もいくつも試されており、服従しないようにするためにはどのようにすればいいのか調査されている。 被害者と被験者との物理的な距離や、権威を持つ実験者の支持の一貫性が崩れた時や被験者を複数にするなど様々なことを試している。 それぞれの実験結果については、この本を実際に読んでみてほしい。

感想

  • 普段の社会にいたら、権威というものはいたるところに現れる。当然、仕事をしている時なんかは「上司と部下」といったものがそこら辺にある。非人道的な指示ですらあっさり服従してしまうのだから、普段の仕事でも無駄とわかっているようなことをあっさりと何も考えずにこなしてしまうのではないだろうか。
  • 上司の指示が必ずしも正しいわけではないし、それは上司が悪いと言って終わりにしてしまうわけにもいかないと個人的には思う。誤りをどこかで訂正するためには、服従に抵抗しなければならない。そのためのヒントがこの実験にあるんじゃないだろうか。
  • マネジメントは、人に働きかけて全体としてうまくやろう、というものだと思うので、人について知らないといけないだろうから心理学から学べることが多そう。

なんだかホラー映画を見ているような感覚で読んでいた気がする。

VS Code Recipes でRailsのデバッギング環境を構築してみた

MicrosoftVisual Studio Codeの設定のレシピをGitHub上に公開している。 その中に、Ruby on Railsデバッグ環境の構築方法があったので、試しにそれに倣って作ってみた。

github.com

やってみた中で、自分なりに置き換えた部分がありました。

デバッグで使うgemをGemfileに追加

デバッグで使用するruby-debug-idedebaseGemfile に追加する。 理由としては2つある。

  1. それらのgemがデバッグに必須であるので、Railsアプリケーション開発に必要なものとして必要であることを明示するため。
  2. あとで作成するデバッグの設定ファイルlaunch.json 内で、rdebug-ideコマンドのパスを絶対パスとして指定する必要がある。そのパスを開発環境に依存させないようにしたいため。

2については後に出すlaunch.json を見てください。

具体的には、以下のようにGemfileに記述する。

group :development do
  # Access an interactive console on exception pages or by calling 'console' anywhere in the code.
  gem 'web-console', '>= 3.3.0'
  gem 'listen', '>= 3.0.5', '< 3.2'
  # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring
  gem 'spring'
  gem 'spring-watcher-listen', '~> 2.0.0'
  gem 'ruby-debug-ide', require: false # <= Added
  gem 'debase', require: false # <= Added
end

gemrequireオプションにfalseを指定している。これは、Railsが起動時にBundle.require で依存しているgemを全てrequire で読み込むのだが、アプリケーション自体には必要無いので読み込まないようにする。 このオプションを指定しなくてもデバッグはできるのだが、必要ないものはあえて読み込まないでおくほうが健全だと思う。

launch.json

デバッグ時に起動するアプリケーションやスクリプトを記述するためのlaunch.json ファイルも、vscode-recipesにサンプルが記載されている。

サンプル中に出てくる、rdebug-idebundlerコマンドのパスを指定するオプションpathToRDebugIDEpathToBundler は、絶対パスで指定する必要がある。 まず、上でGemfileに依存を記述したので、bundle binstubsコマンドを使って、binディレクトリ配下にコマンドをインストールする。

bundle binstubs ruby-ide-debug bundler

launch.json内では、予め用意された変数を使ってパスなどを生成することができる。 ここで、VS Codeが開いているワークスペース絶対パスを表すworkspaceRootを使い、先程のpathToRDebugIDEpathToBundlerの値を指定する。

具体的には、デバッグのためにRailsサーバーを起動する設定は、以下のようになる。

       {
            "name": "Debug Rails server",
            "type": "Ruby",
            "request": "launch",
            "cwd": "${workspaceRoot}",
            "useBundler": true,
            "pathToBundler": "${workspaceRoot}/bin/bundle",
            "pathToRDebugIDE": "${workspaceRoot}/bin/rdebug-ide",
            "program": "${workspaceRoot}/bin/rails",
            "args": [
                "server",
                "-p",
                "3000"
            ]
        }

同様に他の設定についても、pathToRDebugIDEpathToBundlerの値を置き換えていく。

一通り置き換えてみたものがこちら。

https://github.com/satoryu/ChatterBox/blob/master/.vscode/launch.json

他のレシピも良さそうなので、機会があったら試してみたい。

Agile Japan 2019サテライト 新横浜 に参加してきた。 #agilejapan #デンソー

Agile Japan 2019の企業内サテライトの1つである、デンソー(新横浜)会場に参加してきました。

Agile Japan 2019の基調講演はとても興味があったのでその動画を観れる良い機会でしたし、普段同じ建物で仕事している人たちと*1一緒にOSTしたりと、楽しい時間を過ごすことができました。

基調講演

LOVOTを開発しているGroove X株式会社代表取締役の林さんの講演を視聴しました。

  • ロボット開発はハードウェアとソフトウェアの両輪の開発
  • 新規事業

という点で、新横浜でのMaaS開発と近いものを感じていたので、非常に面白い内容でした。 気になったところをサラッと書きます。

  • 先が見通せないからこそスクラムで、なおかつ組織の体制も階層構造をやめてフラットにしている。フラットにして、複数チームが勝手に独立して動くような船頭が多い状態ではなく、そこを引っ張れるだけのプロダクトオーナーが必要そう
  • 「失敗を早くする」といっても盛大にずっこけることはできないので、リカバリ損切りを判断できるスキルが必要そう
  • 「飽きる」という感覚をポジティブに使うこと。新しいことへ挑戦するモチベーションの現れでもあるので、挑戦にもつながるが、それだけでなくそこに対応できれば離職率を下げられそう。実際に離職率が2%程度とおっしゃっていた。

個人的には、さらっと「100億溶かした」とか言っていて、それだけしてでもやりたいと思える情熱がプロダクトオーナーには必要なんだろうな、と勝手に頷いておりました。

基調講演後の感想戦

基調講演を観ている時に、各自で気になったところや感想を付箋に書き留めていたものを全員で共有し、特に気になるところについてディスカッションしました。

  • フラットな組織って?
  • 組織がフラットになると、2:8の法則の2割がなくなるってほんと?
  • うちらって「洞窟」っぽい?

みたいなことを、ときには脇道にそれたりしながらワイワイと話をしました。

ランチセッション

社内Slackで「どんなセッションをやりたいか?」を公募したところ、「焼きそばを作る」「焼きそばを食べる」という2つがエントリーしており、これが採択されました。 ということで、ハードウェア(ホットプレート)とソフトウェア(焼きそばの材料など)の調達からリリース(調理)まで自己組織的に行いました。

再演: 大企業をリファクタリングしてみる

Agile Japan 2019で登壇した弊社石田さんの再演。 いままでチラッと聞いたことがある、Joy, Inc を参考にした自分たちの秘密基地づくりやインナーソースの取り組みについて聞けたのはとても良かった。 組織と人をハードウェアとソフトウェアのアナロジーで見直すことで、得られる考え方も変わってくる。新しい改革プロジェクトって立ち上がるけど、人が集まらないというのはどこにでもあるもんなんだな、と。

石田さんの講演資料はこちら。

OST

OSTが何かについては、先日優良コンテンツが出たのでそちらをご参照ください。

takaking22.com

せっかく同じイベントに参加してるのだからもっとワイワイと話さなくちゃ! ということで、やってみたところ、短い時間の中でカバーしきれない話題がたくさん出てきました!

f:id:satoryu:20190813200418j:plain

答えは出なくても、普段モヤモヤっとしてることを出してみて、思考を整理したり、他の人の意見を聞けたり、新しい情報を得られたりできるので、こういう時間はとてもいいですね。

ちなみに、引き続きオススメ映画(Amazon プライムで観れるもの)は募集中です。

Fun! Done! Learn!

楽しい時間は過ぎるのが早いというもので、その楽しさをFun! Done! Learn!で振り返りました。

おわりに

7月に入社して、まだ右も左も分からない感じなんですが、大企業だけどポジティブに色んなことをやっていこうとしている人たちがいる感じがしました。 しかし、良くも悪くも皆さん真面目な空気を感じてもいます。

今日みたいな楽しいことが普段からやれていけるといいな、と思った次第です。 これからも楽しむぞ!

*1:と書いておきながら自分は秋葉原にいることが多かったりするのではありますが。

Railsでウィザード形式のフォームを実装する時はテーブルを分けよう

f:id:satoryu:20190811081742j:plain unsplash-logoEstée Janssens

ユーザーへの入力を便利にしようと、ウィザード形式を採用しようと思って、試しに雑に作ってみた時にハマってしまった。

実際のコードは晒せないので、サンプルとして以下のような状況を考える。

例えば、プロフィールサービスを作っていて、ユーザー登録後にユーザーのニックネームやアバター画像、TwitterFacebookなどSNSのアカウント情報を入力させたくて、ウィザード形式を採用しようとしている。とする。

ここで、プロフィール入力を2ステップで構成されるウィザード形式にしたい。

  1. ニックネームと誕生日を入力
  2. SNSのプロフィールのURL

また、各プロフィールは必須入力としたい。

当初はウィザード形式は考慮していない実装だったので、ユーザー登録後に入力する情報は、いずれもユーザーのプロフィールということでprofilesテーブルに登録する。 テーブルのスキーマは以下のようになる。

ActiveRecord::Schema.define(version: 2019_08_11_000716) do
  create_table "profiles", force: :cascade do |t|
    t.string "nickname"
    t.date "birthday"
    t.string "twitter_profile_url"
    t.string "facebook_profile_url"
    t.integer "user_id"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.index ["user_id"], name: "index_profiles_on_user_id"
  end

  create_table "users", force: :cascade do |t|
    t.string "email"
    t.string "password_digest"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.index ["email"], name: "index_users_on_email", unique: true
  end
end

良くない実装

必須入力の項目のチェックするために、Profileモデルにバリデーションを設定する。

class Profile < ApplicationRecord
  validates :nickname, presence: true
  validates :birthday, presence: true
  validates :twitter_profile_url, presence: true
  validates :facebook_profile_url, presence: true
end

これだけだと、ウィザードの最初のステップでニックネームと誕生日を入力し、その時点の情報をprofilesに保存する時にその後に入力するtwitter_profile_urlなどのバリデーションによって止められてしまう。 validatesメソッドのonオプションで、そのバリデーションを有効にするタイミングを制御することができるので、それを使うことで一応は解決できそうではある。

class Profile < ApplicationRecord
  validates :nickname, presence: true, on: [:step1, :step2]
  validates :birthday, presence: true, on: [:step1, :step2]
  validates :twitter_profile_url, presence: true, on: :step2
  validates :facebook_profile_url, presence: true, on: :step2
end

このようにしておけば、モデルを保存する時に、save(context: :step1) などとすればステップごとに有効にしたいバリデーションを指定できる。

問題点

on オプションでバリデーションを追加すると、指定されたコンテキストでしかバリデーションが実行されないので、通常のsaveやupdate時にも有効にさせたい場合との区別が難しい。 このウィザード以外にProfileを保存や更新する場合に、必須入力であるということがチェックされない。チェックするにはコンテキストを指定する必要がある。

良さそうな実装

profilesテーブルを分割する。 そもそもウィザード形式で分ける時に、各ステップでの入力項目には意味がある単位にわかれているはず。そうでなければ、何の入力の助けにもならない。

ということで、ニックネームと誕生日、TwitterFacebookのURLとそれぞれの組でテーブルを分ける。

ActiveRecord::Schema.define(version: 2019_08_11_134450) do
  create_table "profiles", force: :cascade do |t|
    t.string "nickname"
    t.date "birthday"
    t.integer "user_id"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.index ["user_id"], name: "index_profiles_on_user_id"
  end

  create_table "social_profiles", force: :cascade do |t|
    t.string "twitter_profile_url"
    t.string "facebook_profile_url"
    t.integer "user_id"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.index ["user_id"], name: "index_social_profiles_on_user_id"
  end

  create_table "users", force: :cascade do |t|
    t.string "email"
    t.string "password_digest"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.index ["email"], name: "index_users_on_email", unique: true
  end
end

当然だが、こうするとバリデーションは普通の記述になる。

class Profile < ApplicationRecord
  belongs_to :user

  validates :nickname, presence: true
  validates :birthday, presence: true
end

class SocialProfile < ApplicationRecord
  belongs_to :user

  validates :twitter_profile_url, presence: true
  validates :facebook_profile_url, presence: true
end

おわりに

ウィザード形式を採用する場合に、テーブルを意味のある単位に分割することでバリデーションが複雑にならずに済む。 データの設計がコードに影響することがわかる例だと思う。

分割することで悪い影響はまったく無いのだろうか。 1つあるとすれば、分割されたテーブルの情報を使ってユーザーを検索する場合にJOINするコストが発生することくらいではないだろうか。 例えば、上の例で、social_profilesテーブルのtwitter_profile_urlprofilesテーブルのnameusersテーブルのSELECT文のWHERE句で使う場合だ。 2つのテーブルくらいなら大丈夫そうだが、増えていった場合はパフォーマンスの影響も考慮した分割の方法を検討すると良いのかもしれない。

また、既に稼働しているサービスの場合、分割に際してデータ移行が必要となる。

参考

2年前くらいに読んだ「システム設計の原則」の中で似たようなことをやっていたのを思い出したのがきっかけ。 あらためてその部分を読もうと思ったのだけど、失くしてしまったのか、後輩にあげてしまったようなので、また買うかな…

38歳になりました&今年上期を振り返ってみた。

f:id:satoryu:20190728144415j:plain
Photo by Lidya Nada on Unsplash

本日で38歳になりました。

37歳のあいだも色々ありました。 色々な方に支えられての1年で、様々なことを経験させていただいており、周囲の方々への感謝の気持ちでいっぱいであります。

37歳の振り返りではありませんが、ちょうど良い頃合いなので、上半期の振り返りをしてみました。

TL;DR

  • 38歳になりました。
  • イベントで登壇したり、転職したり、色々ありました。
  • 最後の重要な連絡事項はこちら
続きを読む

Google AssistantのバックエンドをAzure WebAppで作った。

Google Homeで音声を入力とした何かを今後作るかもしれないので、試しにGoogle Assistantを作るためのActions on GoogleとDialogflowを使った開発をしてみた。

初めて触れる技術のことなので、アプリケーションのネタはとてもシンプルにこれまで作ってきた駄洒落の自動生成にした。 で、実際にできたものがこちら。

www.youtube.com

バックエンドを、Firebase Functionsで作ろうと思ったのだが、無料版だと外部へのネットワーク接続ができないため諦めた。バックエンドは、Webhookの呼び出しを受けられれば何でも良いので、今回は普段から慣れているAzure WebAppを使うことにした。本当は、Azure Functionsにしたかったのだが、npmパッケージのactions-on-googleがサポートしていなかったので、WebApp上で動くnode.jsアプリケーションとして作った。

ソースコードは、こちら。

github.com

ユーザーの認証や、リソースへのアクセス権限を取得したりできるようなので、できることは色々ありそう。

インスタンス名を取り締まるためのRuboCop拡張をリリースしました。 #rubocop

インスタンス名を取り締まるためのRuboCop拡張 rubocop-instance_variable_name をリリースしました。

github.com

何ができるの?

この拡張がやっているのは、とてもシンプルなことで、インスタンス変数名の長さが一定以下(デフォルトだと2)のインスタンス変数に警告を出します。

@ms = 'foo' # bad code

@message = 'foo' # good code

作った動機

最近、他人の書いたコードをレビューしたり、他人の書いたコードを修正する機会が増えてきました。 その中で、いつも悩まされるのが変数名がわかりづらい時でした。 特に、インスタンス変数名は重要だと思います。 メソッド名やattr_readerなどで定義されるゲッターが分かりづらかったり、複雑なコードだとしても、変数名が何を表しているのかがわかると読み解くことができます。 特にクラス変数名は重要です。

読みづらさの中で最も辛いのは、略語を使ったものです。 普段使わない単語を使っているのも読みづらいのですが、英和辞典やググれば理解することはできます。 ですが、略語になると、それを推測するのが非常に難しくなります。

ということで、インスタンス変数名の長さに注目し、受け入れられない長さの変数名を指摘するRuboCop拡張を作りました。

是非ご活用ください。 また、何か不具合等ございましたら、Issueを作っていただけると助かります。