Quantcast
Channel: メドピア開発者ブログ
Viewing all 210 articles
Browse latest View live

ActiveModelSerializersを使った所感

$
0
0

こんにちは。メドピアエンジニアの保立です。

メドピアでは、ドクター向けに運営している「MedPeer」のiOSアプリを3月15日にリリース致しました!!!
今回は、iOSアプリ開発の過程で、APIの実装にActiveModelSerializersを使ったので、そこで得た知見を書きます。

ActiveModelSerializers を使った理由

json形式のレスポンスを返却する場合、jbuilderを使うケースも多いのではないでしょうか。
メドピアでも、今まで外部へのAPIにはjbuilderを使用していました。
しかし、iOSアプリ用のAPIでは、以下のメリットを考慮して、ActiveModelSerializersを使うことにしました。

メリット① 複雑なjsonを返す際に、ActiveModelSerializersの方が、レスポンスが早い

スマートフォン用のAPIでは、なるべく少ないリクエストで画面の描画に必要なすべての情報を返却したいため、何重にも入れ子になるjsonを返却することが多いと思います。そのため、viewをレンダリングするjbuilderでは汎用的なオブジェクトはpartial化することが求められますが、partialを呼び出すのに時間がかかってしまいます。
一方、ActiveModelSerializersでは、レスポンスの内容を定義するserializerにhas_manyhas_oneを使って、関連するオブジェクトを指定することができます。これにより、jbuilderよりも素早くレスポンスを返すことができます。

メリット② DSLな書き方をしなくてよい

jbuilderの書き方は、直感的に分かりづらい記法になるケースがあります。

json.title  @post.title
json.body @post.body
# {"title": "タイトル", "body": "本文"}


json.post @post, :title, :body# {"post": {"title": "タイトル", "body": "本文"}}}# これも同じ内容を返す(こっちの方が少し分かりやすい)
json.post do
  json.title @article.title
  json.body @article.body
end

さらに、partialを読んだり、if文などの分岐が入ると、より分かりづらくなります。
ActiveModelSerializersでは、Rubyの記法に則って書けるため、分かりやすいコードになります。

以上の点から、メドピアのiOSアプリ用APIとして、ActiveModelSerializersを採用しました。

使用例

ActiveModelSerializerは以下のように使用します。

classPostsController< ApplicationControllerdefshow@post = Post.find(params[:id])
    render json: @post, serializer: PostSerializerendend


classPostSerializer< ActiveModel::Serializer
  attribute :title# 別名をつけたい時はkeyを使用する
  attribute :created_at, key: :timestamp# has_manyやhas_oneで関連するオブジェクトを指定する
  has_many :comments,  serializer: CommentSerializer# メソッドを呼び出すことも可能
  attribute :publisheddefpublished
    object.published_at.present?
  endendclassCommentSerializer< ActiveModel::Serializer
  attribute :bodyend# レスポンス
{  
  title: "タイトル",
  timestamp: "2018-01-01T00:00:00+09:00",
  published: true,
  comments: [
    {
       body: "コメント" 
    }  
  ]
}



並列関係の複数Modelに紐づくAPIを返却したい場合もあります。 以下の例は、アプリ起動時など、Masterデータを取得する時のコードです。

classMasterDataController
  render json: MasterData.new, serializer: MasterDataSerializerend


classMasterDatadeffirst_master_data@first_master_data ||= Master::FirstMasterData.all
  enddefsecond_master_data@second_master_data ||= Master::SecondMasterData.all
  endend


classMasterDataSerializer
  has_many :first_master_data
  has_many :second_master_dataclassFirstMasterDataSerializer
    attributes :id, :nameendclassSecondMasterDataSerializer
    attributes :id, :nameendend

使ってみた感想

書いていて、ものすごくつまずくということがなかったです。
ActiveRecordの延長のような作りなので、つまずくことなく書くことができました。
またGitHubのドキュメントが豊富なので、基本的にわからないことがあってもドキュメントをみれば解決しました。
コード量はjbuilderより多くなるので、「すぐに実装したい」「複雑なjsonを返す必要がない」場合はjbuilderでもいいと思います。 逆に、長い間運用したり、拡張する可能性があるAPIを用意する場合は、ActiveModelSerializerおすすめです^^

おまけ(Active Model Serializer と jbuilder のパフォーマンス比較)

posts#showアクションにて、postsを1件とhas_many関係のcommentsを20件をレスポンスに設定するケースで比較します。

jbuilder

# app/controllers/posts_controller.rbclassPostsController< ApplicationControllerdefshow@post = Post.find(params[:id]).preload(:comments)
  endend# app/views/posts/show.json.jbuilder
json.title  @post.title
json.page_views do
  json.partial! partial: 'comment', collection: @post.comments, as: :commentend# app/views/posts/_comments.json.jbuilder
json.body comments.body

実行結果は以下のようになります。

Processing by PostsController#show as JSON
Posts Load (1.0ms)  SELECT `posts`.* FROM `posts` LEFT JOIN `comments` ON `posts`.`id` = `comments`.`post_id` 
Rendered posts/_comments.jbuilder (0.3ms)
Rendered posts/_comments.jbuilder (0.3ms)
Rendered posts/_comments.jbuilder (0.3ms)
(commentsの数だけ続く)
Rendered posts/_comments.jbuilder (0.3ms)
Rendered posts/show.json.jbuilder (47.2ms)
Completed 200 OK in 87ms (Views: 49.6ms | ActiveRecord: 7.5ms)

Active Model Serializers

# app/controllers/posts_controller.rbclassPostsController< ApplicationControllerdefshow@post = Post.find(params[:id]).preload(:comments)
    render json: @post, serializer: PostSerializerendend# app/serializers/posts_serializer.rbclassPostSerializer< ActiveModel::Serializer
  attribute :title# has_manyやhas_oneで関連するオブジェクトを指定する
  has_many :comments,  serializer: CommentSerializerendclassCommentSerializer< ActiveModel::Serializer
  attribute :bodyend

実行結果は以下のようになります。

Processing by PostsController#show as HTML
Posts Load (1.0ms)  SELECT `posts`.* FROM `posts` LEFT JOIN `comments` ON `posts`.`id` = `comments`.`post_id` 
[active_model_serializers] Rendered PostSerializer with ActiveModelSerializers::Adapter::Attributes (11.23ms)
Completed 200 OK in 41ms (Views: 9.7ms | ActiveRecord: 7.9ms)

と、いうことで2倍以上の速度でレスポンスを返すことができました。
実際のMedPeerアプリでは、3重4重に入れ子にしたjsonを返却することがあるため、Active Model Serializers の恩恵は計り知れないものがあります。


(☝︎ ՞ਊ ՞)☝︎是非読者になってください


メドピアでは一緒に働く仲間を募集しています。 ご応募をお待ちしております!

■募集ポジションはこちら

https://medpeer.co.jp/recruit/entry/

■開発環境はこちら

https://medpeer.co.jp/recruit/workplace/development.html


RubyKaigi2018に参加してきます!!

$
0
0

こんにちは!4月から参画したエンジニアの森田です。

RubyKaigi2018、いよいよ明日からですね!

rubykaigi.org

※メドピアもGoldSponsorとして、協賛しています。

私を含めて総勢11人(希望者全員参加!🎊)のエンジニアで参加費、交通費、宿泊費、懇親会費と、なにからなにまで会社からの補助で参加します🙏
(なんとメドピアRailsエンジニアの参加率80%!👀留守を守ってくれるエンジニアの皆様、ありがとうございます…!) f:id:akinorifukumura:20180530001233j:plain

当日は、ゆるきゃら(?)であるメドベアをプリントしたパーカーを着ていますので懇親会等でお会いした際には、ぜひお話しましょう! f:id:madogiwa0124:20180529163312j:plain

会場ではステッカーも配ってますので、よろしければもらってください🐻 f:id:madogiwa0124:20180530104601j:plain

またRubyKaigi中に各セッションの速報をリリース予定です📝
当日はこちらも、チェックしてみてください !

それでは、当日みなさんにお会いできることを楽しみしています🙌


(☝︎ ՞ਊ ՞)☝︎是非読者になってください


メドピアでは一緒に働く仲間を募集しています。 ご応募をお待ちしております!

■募集ポジションはこちら

https://medpeer.co.jp/recruit/entry/

■開発環境はこちら

https://medpeer.co.jp/recruit/workplace/development.html

RubyKaigi2018 速報 (5/31 1日目)!!

$
0
0

(※こちらのエントリを随時アップデートしていきます)

RubyKaigi2018の5/31 (1日目)の速報です!

RubyKaigi2018に参加中のメドピア エンジニアが、セッションの内容や感想を更新していきます。

f:id:motsat:20180531132717j:plain

A parser based syntax highlighter (5/31 16:40〜)

Masataka Kuwabara @p_ck_

スライド:

speakerdeck.com

  • 既存のHighlighterは正規表現を使用している。
    • atomではcson内に正規表現が記載されている。
  • 既存のSyntax Highlighterの問題
    • コードを読むのが辛い
      • 正規表現を読むのが辛い。。。
    • ハイライトが完璧じゃない
      • editorによっては、ハイライトが壊れてしまう。。。
  • gemIro
    • Iroの概要
    • 今後について
      • ヒアドキュメントのハイライト(SQL等)
      • 多言語対応(Slim, Markdown)
    • よくある質問への回答
      • 3000行程度であれば、パフォーマンスは問題ない
      • Ripperのおかげで編集途中のファイルでも上手くハイライト出来る

RubyGems 3 & 4 (5/31 15:50〜)

スライド:

www.slideshare.nethttps://www.slideshare.net/tagomoris/hijacking-ruby-syntax-in-ruby
  • 今の最新バージョンは2.7
    • rubygems単体で2.5,2.6をリリースされることはないのでrubyのアップデートが必要
    • 2.7に関しては、メンテナンスがされるのでrubyのアップデートは不要
    • ruby1.8をサポート対象としてたり、bundlerとの兼ね合いがあるのでサポートが結構たいへん。。。 *Ruby 2.6.0には3.0、2.7 or 3.0には4.0が入る予定
  • RubyGem3
    • 非推奨メソッドを削除
      • Gem::Duplicateを使うと非推奨の警告を簡単に出せて便利
    • サポートがruby2.2以上に
    • bundlerとdependency resolverのversionをあわせたい
    • gem codesearchでgem内の検索(def thenがけっこうある…)
    • all-ruby 世の中のすべてのrubyであるコードを実行できテストや調査
  • RubyGem4
    • 現状あまり機能していないdefault optionを使いやすくする
    • --user-installをdefaultにするかも
    • update rdocでconflictが発生しないように修正する

Fast Numerical Computing and Deep Learning in Ruby with Cumo (5/31 15:50〜)

Naotoshi Seo @sonots

スライド:

speakerdeck.com

感想

  • pythonで良いのでは?なんて思っても言ってはいけない
  • 楽しそうではあるが、pyth.....

memo

  • cumoとはcudaで数値計算するライブラリ
  • scientific computation in ruby
    • pythonだとnumpy, cupy, pycudaとかあります
    • rubyだとnumo, cumok, rbcudaとかができてきている
      • pycallというのもあり
  • CUDA基礎
    • GPU速い(30日(cpu)→4日(gpu))
    • GPUはコア数が多いので
    • 行列演算とか強い
  • CUDA architecture
    • GPU memory確保
    • GPU memoryに転送
    • 計算
    • GPU memoryから転送
    • なんか使う
  • 非同期な命令が多く、同期が必要な命令のところで待ち合わせる
  • cumo
    • numo compatibility
      • コードの一括置換でそのままGPU化できる
    • cuBLAS by NVIDIAで内積計算をする
      • fortlanベースなので、メモリレイアウトが微妙に違う(行/列)
    • Memory pool
      • cuda mallocが遅いので
      • best fit algorithm
        • 単純なfree listではなくfree listをsizeで分けている
      • best fit with coalescing (BFC)
        • みんなのmallocはこれを使っている
      • JIT compiling
        • NVRTC (NVIDIA Runtime Compilation)を使ってる
  • numo vs cumo
    • element-wise 40倍速い
    • dot product 158倍速い
    • red-chainerで試したら…
      • 20倍速い(memory poolなし)
      • 75倍速い(memory poolあり)
  • GPUとGCと相性悪いのでは?
    • すぐ解放したいメモリがあってもGPUを待たなければならない
      • GPU側の使用メモリ量はGC発動に影響を及ぼさない
      • pythonだと参照カウントだから問題ない!
    • #freeメソッドを追加してごまかす
  • mkmfの機能たりない
    • コンパイラを複数種類作れない
    • 拡張子によってコンパイラを分けるproxy的なものをコンパイラに指定して回避
  • broadcast operation遅い
    • kernelを叩く回数をへらす必要がある
    • でかい行列を作って1回で済ませて回避(?)
  • Inplace math operations
    • a += ba = a + bでやりたくない(一時オブジェクトを作りたくない)
    • pythonだと参照カウントを見てうまく一時オブジェクトを作らないようなことをしているらしい

All About RuboCop (5/31 14:50)

Bozhidar Batsov @bbatsov

内容

  • Lintはcommon senseの置き換えにはならない
  • Rubocopの歴史
    • 初期rubocopは正規表現でチェックしていた
    • 2013年4月の怒涛のアップデート
    • ripperからparser gemへ
  • 過去の話から未来の話
    • 1.0に向けてrailsとかパフォーマンス関連のcopをコアから外す予定
  • 他の話
    • Emacsはいいぞ
    • vimmerはspacemacsを使おう

感想

  • ついにRubocopが1.0になりそうな雰囲気!
  • spacemacesいいよね

TTY - Ruby alchemist’s secret potion(5/31 14:40〜)

Piotr Murach @piotr_murach

内容

感想

  • 簡単に綺麗なCLIのインターフェースを作れそう。
  • 機能毎にgemが別れているので、プログレスバーだけ等必要なものだけ使うとかも出来そうだから導入ハードルが低そう。
  • 英語はあまり分からなかったけど、コードが表示されていてなんとく内容が理解出来てよかった・・・!
  • ライブコーディングあったけど、コードもわかりやすい(not crazy!!!)
  • CLIで色々面白いことを簡単にできるので、ちょっと触ってみたくなった
  • 外人がさらりと日本語で自己紹介していて、かっこいいと思った

Hijacking Ruby Syntax in Ruby(5/31 13:50〜)

事前知識

  • Binding
    • 変数の設定(名前と値)をとっておく機能
    • 既存の変数を上書きすることも出来る。
  • TracePoint
    • Rubyの組み込みクラス
    • 例外の情報を集めるのが便利とあるが、他にもたくさんの情報を集められる。
    • Bindingが呼び出せるので、任意の行の変数の上書きが出来る!
  • Refinement
    • 安全な局所的なメソッドを追加出来る仕組み、安全にモンキーパッチやクラス拡張が行える。

BindingやTracePointを使ってRubyをHackした話

www.slideshare.net

RubyをHackするのは楽しい!でもデバッグがとてもつらい・・・! 「覚悟は出来てるか?俺は出来ている!」

A practical type system for Ruby at Stripe(5/31 13:50〜)

  • ストライプではpureなRubyを使っている

  • 型チェックのツールをオープンソース(にする予定)プロジェクトとして作ってる。

  • いろんな型チェック機能の詳細の説明

  • いろんなユースケースとか知りたいから興味ある人は連絡してね

BlockChain * Ruby の話(Bancorというgemを作成したよって話) (5/31 13:00〜)

Yuta Kurotaki @kurotaky
https://github.com/kurotaky/bancor

  • Ethereum(イーサリウム)などの仮想通貨は、「Smart Contract」という仕組みを基盤にしている(Smart Contractを使って、自動で売買契約の実行を行えるようになる)
  • 「仮想通貨A * 1コインを15$で売りたい」という人がいた時、「15以上で仮想通貨Aを書いたい」というユーザーがいないと、取引が完結しない。(流動性が下がる。) 特に、マイナーな仮想通貨間の取引では、なかなか流動性が上がらない。
  • ユーザーの売買の欲求、総発行量・準備金から、価格を流動的に算出するものが「Bancor Protocol」(Bancorはバンコールと読む)。これを、なるべく早く正確に計算できるようにしたい。
  • 「Bancor Protocol」をもとに、売買できる仕組みをRubyで作るためのgemを作った。そして、RailsのWebアプリで動かしてみるまで実施した
  • 小数点以下の演算とかまだまだざっくりなので、きちんと計算できるようにしてサービス化していきたい

感想

  • Rubyでブロックチェーンを活用したアプリは、他の言語に比べて少ないので、どんどん盛り上げていきたい(いってほしい)。

Analyzing and Reducing Ruby Memory Usage(5/31 13:00〜)

Aaron Patterson @tenderlove
Rubyによるメモリ使用量の減少。

  • コードを読む
  • mallocをスタックトレースする
  • Objectspace、allocation_tracerはRubyオブジェクトしか判定できない
  • Malloc Stack Loggingでrubyプログラムが行ったmallocを判定できる → 対象プログラムに対し行うとファイルにalloc freeなどの情報を出力できる → 誰がmallocしたかはわからない…
  • ファイル読み込みの改善
    → 一度読みこんだファイル情報をキャッシュしておく
    → 「同じファイル」は相対パスの関係でいろいろな文字列になる
    → それらをそれぞれ文字列を作りmallocしないで、全体文字列からのポインタ?のようなものだけ持って削減する
  • あなたならこの機能にいくらはらいますか?5000円?10000円?5000兆円?
  • Ruby2.6にアップグレードすれば無料!
  • なんと5年前に同じようなpatchが書かれていた…(patch書くときはissueの調査から!)
  • バイトコードの命令、オブジェクトをパラメータを切り分け、オブジェクトをバイトコードにpushされたときにバイトコードをGCにマークさせる(これをしないと命令とオブジェクトの切り分けが難しい)
  • あなたならこの機能にいくらはらいますか?5000円?10000円?5000兆円?
  • Ruby2.6にアップグレードすれば無料!
  • Ruby2.6にアップグレードしてください!

感想

メモリ改善、こういった細かいところで知恵がしぼられて行われているんだとしみじみ思った。 日本語でのセッションでしたが全く違和感なく聞けてすごい…

Keynote Yukihiro "Matz" Matsumoto@yukihiro_matz ( 5/31 10:30〜)

  • 名前重要。命名にこだわることで開発効率をよくすることを支えている。
    パフォーマンスカイゼン、重要。
    プログラミングは物理的な制約がないので概念で構成される
    名前付けには「振る舞い」、「プロジェクト、コミュニティ」の2パターンがある。
  • メソッド名の悪い例
    yield_self(会場笑) メソッドを使って何がしたいかがわからない
    →then
    Rubyという名前にしてよかったけど、それはbefore googleだったから
     悪い例:
      Go(会場大爆笑)
      Swift

  • メソッド名のいい例:
    Ruby on Rails(スペースで単語)
    Jupyter(ちょっといじる)
    Hanami, Nokogiri, Kaminari(会場笑)

  • いろいろなエンジニアが実行速度のカイゼンを続けている。

  • コミュニティ重要。非互換性を生むような進化はコミュニティの停滞に繋がり、言語の繁栄の弊害となる
  • Rubyは上記3点を大事にしながら進化し、Rubyistをハッピーにしていく。  
  • Rubyistの生産性・幸福度を重視した松本さんの思いを感じたKeynoteでした。

  • 今回のテーマはことわざ

  • 塞翁が馬、名は体を表す等のことわざを用いてRubyの「いままで」と「これから」をMatzから話がありました。
  • JITコンパイラを使って高速化やバージョンアップアップによるコミュニティの分断を防ぐ等の今後のRubyの見通しが伺えました
  • 塞翁が馬=人生いいことも悪いこともある
    2013-2018のRubyはハイプ・サイクルでいう安定期のようなものだが、Ruby is Deadと言われるようになった。毎年言われている。Ruby is Dead Every Year(会場笑)
  • ポール・グラハム「簡潔さは力なり」というエッセイ 豆知識:MatzはRubocopのデフォルトルールが嫌い(笑)

  • 名は体を表す=名前重要

  • 時は金なり
    Rubyはパワフルで簡潔で〜なので時間を有効活用できる

  • パワフル:便利なメソッドがたくさんある、ライブラリ、gem、フレームワーク、2.days.ago
    コミュニティが大きくなっているので助けてくれる人が多い

tech.medpeer.co.jp


(☝︎ ՞ਊ ՞)☝︎是非読者になってください


メドピアでは一緒に働く仲間を募集しています。 ご応募をお待ちしております!

■募集ポジションはこちら

https://medpeer.co.jp/recruit/entry/

■開発環境はこちら

https://medpeer.co.jp/recruit/workplace/development.html

RubyKaigi2018 速報 !! ( 6/1 - 1つ目 )

$
0
0

RubyKaigi2018の6/1 (2日目)の速報 (6/1 - 1つ目)です!

他の「RubyKaigi2018 速報!!」まとめ
RubyKaigi2018 速報 (5/31 1日目)!! - メドピア開発者ブログ
※最新のまとめはこちら
RubyKaigi2018 速報 !! ( 6/1 - 2つ目) - メドピア開発者ブログ

RubyKaigi2018に参加中のメドピア エンジニアが、セッションの内容や感想を更新していきます。

extend your own programming language ( 6/1 13:50〜)

http://rubykaigi.org/2018/presentations/m_seki.html#jun01

for DSL

  • rubyを使ってやるよりも機能が制限されている分安全

Extend MinRuby

  • 拡張して遊ぶ!(末尾最適、rinda eval、
  • requireで自分で作成したライブラリを読み込むことが出来る。
  • 自分で演算子を追加したりも出来る。クラスの演算子をまねっこすれば簡単に出来る。
    • でもrubyに存在しない演算子を追加するのはRipperに無いので難しい。。。

感想

MinRubyを使って僕の考えた最強のRubyを作るのは楽しそう、かつ勉強になりそうでした。

Guild Prototype

new concurrency model for ruby 3 ( 6/1 13:00〜) Koichi Sasada @ko1

Guild Prototype - RubyKaigi 2018

感想

  • guildがどんなものかイメージができてよかった
  • GILから逃れられる?方法ができるのは単純にありがたい

memo

  • 2016のときはまだアイディアだったguildの進捗をお伝えします
  • マルチスレッドプログラミングは人類には早すぎた
  • motivation
    • rubyではプログラミングを簡単にしたい
    • CPUコアを有効利用したい
  • Guildのコンセプト

    • mutableなものは共有しない(raceが起こらない)
    • threadをguildに置き換える
  • 共有不可オブジェクト

    • 1つのguildにのみ所属
    • mutable objects(String ,array)
    • ruby2とcompatible
  • 共有可能オブジェクト

    • immutable objects(symbol, numeric, true, false, nil, etc)
    • Class/Moduleオブジェクト
      • 共有できないとさすがに辛い
      • C::Const = [1, 2, 3]みたいなのは特別扱い
    • special mutable objects
      • Shared hash, array
      • Guild objects
      • 変更するには同期が必須
    • Isolated Proc
      • 外のmutableな変数にアクセスできない
  • 40 virtual CPUのマシンでデモ

    • fib(23)を100000回
      • 早くなるに決まっています
      • fib(10)くらいだとあまり変わらない
    • File.read(file).b.upcase.split(/\W/).uniq.size
      • Guildが増えると遅くなる
        • GC/object allocationでロックが発生する
  • GIL(Giant Interpreter Lock)がguildごとに適用される?

    • GGL(Giant Guild Lock)
  • Guild間通信

    • Actor model, send/receive semantics
      • Guild.parent << 'hoge'
      • hoge = Guild.receive
    • Guild間で共有不可オブジェクトを受け渡すときはコピーするか移動する
  • 実装

    • VM -> 1+ Guilds (NEW!) -> 1+ Threads -> 1+ Fibers
    • GCは全Guildを止める(Guildの中のスレッドも)
  • Guildという名前はmembershipを意識している

    • ただしまだコードネーム扱い

Improve Ruby coding style rules and Lint ( 6/1 13:00〜)

http://rubykaigi.org/2018/presentations/koic.html#jun01

スライド:

speakerdeck.com

LintとStyle違い

  • Style
    • いろいろなスタイルガイドがあるがrubocopのdefaultはRubyStyleGuide
    • それは正解不正解ではなく、文化の違いのためRubocopを変更するのはいいこと。
  • Lint
    • Lintはバグとなり得るもの。
    • ERB.newが2.6でキーワード引数で呼び出すように変更となったものを取り込んだりしている。
    • ruby -woptionでも検知出来るものもあるが、rubocopの方が検証範囲を細かく設定できるため、かゆいところに手が届く。

実際の業務からrubocopにつなげる

  • 実際に現場で発生したバッドケースをCopとして定義している。
  • それが他の人に役に立つ。

他のOSSからrubocopにつなげる

  • railsのrubocop.ymlは基本的にすべてfalseにして、有効にしたいものだけtrueとする運用としている。
  • railsのminitestのcopをrubocopのrails部署に置き換えることを行った。
    • railsの新機能のcopが集まってきたりするようになった!

It's Rubies All The Way Down ( 6/1 10:50〜)

http://rubykaigi.org/2018/presentations/wyhaines.html#jun01

  • アプリケーション(rails等)、環境構築(itamae等)、デプロイ(capistrano等)、ロガー(Fluentd等)等のRubyで実現されている様々な機能
  • Rubyで作られたKey/Value Storeがあるの知らなかった・・・! https://roma-kvs.org/

Faster Apps, No Memory Thrash: Get Your Memory Config Right ( 6/1 10:50〜)

github: noahgibbs Noah Gibbs @codefolio

Faster Apps, No Memory Thrash: Get Your Memory Config Right - RubyKaigi 2018

スライド:

docs.google.com

感想

  • 他でも良いという話は聞くのでjemallocは一度試してみたい

memo

ruby memory system

  • tiny objects
    • no memory allocations
  • small objects
    • 40-byte slot
    • 408 slots / page
  • big objects
    • slotに入らない

GC

  • generational
    • major GC
      • 全部チェック
      • GC.start
    • minor GC
      • 新しいのだけチェック
      • GC.start(full_mark: false)
  • mark/sweep

GC cycle: grow, collect, expand

問題: garbageが増えると遅くなる

省ける無駄は省く destrictive ops (gsub!, concat)はやさしい

質疑 Q) immutableなデータ使いたいんだけど? A) といっても、immutabilityを利用した最適化はrubyでは難しいから…

GC.stat useful parts - heap__available__slots, heap__live__slots, heal__free_slots - major__gc__count, minor__gc__count

環境変数 - RUBYGCHEAPINITSLOTS - RUBYGCHEAPFREESLOTSGOALRATIL - RUBYGCMALLOCLIMIT - RUBYGCOLDMALLOCLIMIT

適切な環境変数により、startup timeを改善できる

EnvMem: A Memory Tool

gem install env_mem

crubyはwarmupに時間がかかるが、EnvMemを使ってみたらwarmupの影響がなかった

Allocators

なんか難しいんだけど、簡単に早くなるものはないの? => YES!

mallocの代替 → jemalloc

linuxだったら特にjemalloc良い

./configure --with-jemallocRUBY_CONFIGURE_OPTS="--with-jemalloc" rbenv install ...

10-12% speedup

まとめ what helps?

  • LESS WASTE
  • Good Env variable
  • jemalloc
  • latest ruby

その他

Slotは動かしづらい → fragmentation

GC::Profiler

GC::Profiler.enable

...

puts GC::Profiler.report

(☝︎ ՞ਊ ՞)☝︎是非読者になってください


メドピアでは一緒に働く仲間を募集しています。 ご応募をお待ちしております!

■募集ポジションはこちら

https://medpeer.co.jp/recruit/entry/

■開発環境はこちら

https://medpeer.co.jp/recruit/workplace/development.html

RubyKaigi2018 速報 !! ( 6/1 - 2つ目)

$
0
0

※こちらのエントリを随時アップデートしていきます

RubyKaigi2018の6/1 (2日目)の速報 (6/1 - 2つ目)です!

会場内で突然始まったペアプロの様子! f:id:motsat:20180601163227j:plain

他の「RubyKaigi2018 速報!!」まとめ
RubyKaigi2018 速報 (5/31 1日目)!! - メドピア開発者ブログ
RubyKaigi2018 速報 !! ( 6/1 - 1つ目 ) - メドピア開発者ブログ

Journey of a complex gem upgrade

http://rubykaigi.org/2018/presentations/Edouard-chin.html#jun01

概要

  • Shopifyの人。Shopifyは1つの大きなRailsアプリケーションで、今までフルリライトはされたことがない
  • Gemのアップグレード時のベストプラクティスとはなにかを探っていく

やり方について

  • 通常のフローはCHANGELOGを見る、Bundle updateする、CI叩いて問題なかったらDeploy
  • だが、「Bleeding」をやめよう(Lessons learnt : Stop the bleeding first)

具体的には下記のような順で行っていく

  • 環境をデュアルブート可能にする(新バージョンGemを使う環境と旧バージョンGemでENV分け、Gemfile内でENV分岐でGemバージョン書き換えするなど)
  • Gemfileを分ける
  • アプリケーションを走らせて問題を修正する
  • CIを通す

問題の修正のやっていき方

  • バージョン切り分けで問題となるソースをコンポーネント化 (Rails.rootにcomponentsフォルダを作る)
  • どのコンポーネントがどこに依存しているかを切り分ける
  • ActiveSupport.Deprecation.warn('This code is deprecated')でDeprecationを出す

まとめ

  • bleedingをしないようにしよう
  • ツールを活用しましょう
  • Deprecationの修正に追従していきましょう
  • スライドは下記のツイッターアカウントにあとでアップしていただけるとのことでした! https://twitter.com/daroudedudek

Type Profiler: An analysis to guess type signatures

http://rubykaigi.org/2018/presentations/mametter.html#jun01

ruby 2.6の新機能

  • Endless range:(1..)
  • Beginless range(..1)も出るかも??

RDL: https://github.com/plum-umd/rdl

  • 実装が公開されている注目プロジェクト

使い方

  • rdlをrequire
  • 型修飾を書く
    • typecheck: :callとすると、メソッド呼び出された際に型チェックが実行される。他にも:nowを渡して、定義時にチェックすることも出来る。

特徴

  • 半静的な型チェックツール
  • メタプログラミングをサポートしている。
  • Implementationが、とても良い
  • 型情報をたくさん書かないといけない。。

他との比較

f:id:madogiwa0124:20180601172551j:plain

Ruby3の型システムの話

型システムのアイデアと課題

  • Static Analysis 1:定義されているメソッドから型を推定する。
    • 静的にチェック出来るけど、引数や返り値の解析は難しい。。。
  • Static Analysis 2:n + 3だとNumelic等、式に使われている値から型を推定する。
    • 静的にチェック出来るけど、StringArray<<等の解析が難しい。。。
  • Dynamic Analysis3:TracePointを使って動的に型を推定する。
    • 実装は分かりやすい、テスト実行が必要かつPerformanceに難あり。。。

Scaling Teams using Tests for Productivity and Education ( 6/1 13:50 )

Julian Nadeau @jules2689

チーム開発で起こる失敗と問題解決方法について

失敗

  • 失敗を避けるためにチーム内で「これをすべき、これをすべき」のマナーを作ってもにしてもすぐに古くなる、覚えられない、どうやって自動化しよう?

問題の解決方法

  • 問題を明文化するIdentify the problem
  • 修正が自動化できるかを判断するdetermine if a fix can be automated
  • テストを実施するimplement a test to detect and educate

問題の明文化

例:メンテナンスタスクでのファイル名 メンテナンスタスクで読み込むべきファイルの名前が間違っていたとしたら、ただ「間違っています」というエラーメッセージ出すのでは不親切 同ディレクトリ内を読み込んで、開発者にファイル名がどう間違っているかを知らせる

前のエラーメッセージが:「あなたの読み込んだファイルが間違っています」だったとしたら… 「test.rbというファイルを期待しましたが、test_test.rbファイルが読み込まれましたよ。test.rbというファイルを読み込んで下さい」と教える

間違っている可能性のあることすべて伝える(suggest any possible)

修正が自動化できるかを判断する

ユニットテストを書くこと

  • ユニットテストの実行時のメッセージをできるだけ書く
  • MiniTestだとしたら、asssert時のオプションmessageに上記のような細かいメッセージを書く
  • 問題が常に覚えておくべきことかどうかにかかわらず、検知と教育のためにテストを書こう

rubocopを導入すること

  • rubocopは便利、初心者でもとっつきやすい
  • shopifyでもruby-stydy-guideのリポジトリを作っている

just in time な教育をすること

  • JIT educationのJITとは、Just In Timeのことで、JITコンパイラのJITとコンテキストは近い
  • 開発者が問題を認識したのを検知して、 そのタイミングで問題をと知識を共有、修正に向けて動く

Other solution

bundlerのエラー時のメッセージがわかりにくかったので、実際に上記のように修正のGemを作った https://github.com/jules2689/extended_bundler-errorsたとえばbundle installに失敗したときに 「could not create Makefile due to some reason」と言われることがあるが extended_bundler-errorsを使うと 「何が問題か?どのバージョンならOKか?何をすべきか?」 を提示してくれる

感想

  • 現場感のあるとてもいい発表でした。テストを書くことには動作の保証やヒューマンテストのコストを減らすだけではなく、教育の意味もあるのだなと思いました
  • 問題になりやすいテストに、なぜそれが落ちるのかをメッセージで表示していくのは取り入れてみたいです

Ruby Programming with Type Checking ( 6/1 14:40〜)

概要

発表者が作った型チェックライブラリ「Steep」(前回のRuby会議で発表した)は、実験的すぎたためプロダクションでは全く使用されなかった。 https://github.com/soutaro/steep

より実践的なものにするべくここ9ヶ月に渡る改良と、この型チェックライブラリがどれだけRubyプログラミングを助けるかを発表する。

Steepの機能

type annottaiion type definition local type inference structural subtyping

昨日v0.3.0リリース

いろんな機能追加。 typing polymorphic methodなど

Sorbetとの比較

写真撮ったのであとであげる

Demo

基本的な使い方の説明から、ここ9ヶ月で実装した機能などを紹介。

ここ9ヶ月で実装した機能

インスタンス変数をattr_reader, attr_accessorで扱う場合があるが、signature対応していなかったため対応 case分の型チェック 型チェックのstrict modeを追加 ...and so on

型づけのステップ

write signatures write ruby program with annotation rub type checker

型付けの困難

ライブラリの型がない 型付けの表現が難しい メタプロへの対処

感想

RubyKaigi全体を通して型付けのセッションが多い。みんなRubyに静的型付欲しいの? このセッションでは、1日目のセッションで紹介されていた「Sorbet」との比較もあり(Sorbetの開発者もこのセッションを聞いていた)、お互い意識しているのだろうか?


(☝︎ ՞ਊ ՞)☝︎是非読者になってください


メドピアでは一緒に働く仲間を募集しています。 ご応募をお待ちしております!

■募集ポジションはこちら

https://medpeer.co.jp/recruit/entry/

■開発環境はこちら

https://medpeer.co.jp/recruit/workplace/development.html

RubyKaigi2018 速報 !! ( 6/2 最終日)

$
0
0

他の「RubyKaigi2018 速報!!」まとめ ( 6/2 13:50 ) RubyKaigi2018 速報 (5/31 1日目)!! - メドピア開発者ブログ
RubyKaigi2018 速報 !! ( 6/1 - 1つ目 ) - メドピア開発者ブログ
RubyKaigi2018 速報 !! ( 6/1 - 2つ目) - メドピア開発者ブログ

Design pattern for embedding mruby into middleware

speakerdeck.com

Cで書かれたソフトウェアの拡張や設定をRubyで書くことが出来るのでは?と考え様々なミドルウェアについて、mrubyで拡張等を行ってきた。

なぜmruby?

  • rubyで書きたい、mrubyの限定された機能が都合が良かった。
  • 今までC言語で書かないといけないため難易度が高かったが、敷居が低く効率が良い。

どうやって組み込むか?

注意点

  • メモリ管理
  • パフォーマンス
  • 使いやすさ

Design Pattern

multi process, multi thread

  • ミドルウェアのスレッド単位でmrb_stateを組み込むことが大事
  • でもそんな単純じゃない、mrb_stateを作る処理が重いのでパフォーマンスに影響が出てしまう。。。
  • mrb_stateとbyte_codeの共有方式が重要

non-blocking

  • ミドルウェア側でnonblockな実装がされていてもmrubyがnonblockになっていないため、そこでパフォーマンスに影響が出てしまう。
  • できるだけmrubyの処理をnonblockで実装させる、blockする処理はミドルウェアのイベントループを優先して、その処理のコールバックによって再開するようにしてあげる。

Build your own tools

http://rubykaigi.org/2018/presentations/shugomaeda.html#jun02

スライド:

slide.rabbit-shocker.org

内容

自由なソフトウェアの話

  • FLOSS (Free Software and Open Source Software)
  • Web ApplicationにはFLOSSが多く含まれている
  • SaaSS (Service as Software Substitute) はあまり好きではない
  • ローカルでできるものはローカルでやりたい

自作したツールの数々

様々な自作アプリに関する紹介でした。最近キーボード自作する人多いですね!

ツールを自作するために必要なもの

  • モチベーション、時間、スキル
  • 自分に自信を持つ
  • 好きなテーマ(自分が使いたいもの)を見つける
  • 飽きる前に動く状態に持っていくために小さく作ろう
  • EmacsはEditorMACroS、つまり拡張可能なエディタ、そういう意味だとvimも同じ。仲良くしよう!

感想

テストは変更しやすくするためのものなので、書きすぎて逆に変更に弱くならないように書きすぎないというのはそのとおりだと思いました。

Deep into Ruby Code Coverage ( 6/2 13:50 )

  • テストが無いコードやデッドコードを検出するのにカバレッジは重要
  • DeepCoverageを使うと分岐のカバレッジをかなり細かくとれる(ruby本家のカバレッジよりも細かい!)
  • 実装は結構大変だった

感想

  • とりあえず試してみたい
  • そもそもruby2.5のカバレッジをまだ使っていなかった…

The Method JIT Compiler for Ruby 2.6 ( 6/2 13:00 )

http://rubykaigi.org/2018/presentations/k0kubun.html#jun02

スライド:

speakerdeck.com

現状のJIT状況

  • すごいバグってる! 
  • 並列にCIが走るとこけてしまう…

RubyのJITコンパイラ

バイトコード->Cのコード->コンパイル->バイトコード

ポータビリティ

Mingwで最適化をおこなったらなんかうごくようになった

パフォーマンス

  • ベンチマーク
  • 去年 : 5倍
  • 今(preview2): 5.7倍
  • 遅くなる場合がある。
    ファミコンは早くなるのにRailsだと遅くなるケースがある…

おそくなる仮設

  • longjump c言語のraiseみたいなもの call cache メソッドキャッシュが聞かない時(キャンセルされた時。再定義など。railsだとけっこうある)

  • JITコンパイルのオーバーヘッド thread間の転送(ロックなどのコスト) そんなに気にしなくて良さそう

  • JIT-ed
    全部nilを返すだけのmethodでなぜか遅くなる… 1メソッドあたり2Mバイト消費する(soファイル。なぜ2Mも) 1個のsoファイルにメソッドをいっぱいつっこめば速くなるがどうやるか

現状

Rails遅くなるから控えて。いろいろ改善しつつはある

navive code

  • GCCがもろもろめんどさを引き受けてくれる
  • われわれはインライン化に精を出せる

インライン化

  • Cコンパイラに最適化をしてもらうためにインライン化が必要
  • 条件
    メソッド定義
    呼び出しコードをJITコンパイラが更新できる
    cacheのinvalidation

インライン化のターゲット

  • Rubyから呼び出されたものはまだ簡単
  • Cから呼び出されたものは難しい
    やればできるけど保守性が下がる

もしRubyがcより早かったら?

  • プロトタイプはある 「C language is dead」

f:id:motsat:20180602133351j:plain

感想

JITによる高速化をめざしたもの。言語の中の人はバイトコードまでおっかけていろいろなアプローチで高速化を試みていてありがたいと思った。 また、Cよりも速くなるケースを作れるという話は単純にすごい。

Reirb: Reborn Irb ( 13:00〜)

概要

irbを作り直している話。

最近のirb

  • 開発が長く停止
  • 似たようなアプリが出てきた
  • Reish(URubyistのためのnixシェル)を開発している過程で、irbを作り直すためのアイデアを思いついた!

Reishの説明(機能)

  • job control
  • reidline
  • smart completion

↑Reirbは上の機能を使える。というかソースコードをかなり再利用してる。

Reirbの字句解析

Ripperを使っている。 irbではRipperを使っていないため、字句解析が不十分。

字句解析とは

irb(main):002:1> p 1
irb(main):003:1> end
1
=> 1

上記のようにstatementを解析し、宣言の終わり(式の終わり)に実行するようにしてくれるやつ?

Ripperとは

  • ruby の Parser

Job-Control

  • ^Z
  • fg, bg, jobs
irb(main):002:1>  p 1
irb(main):003:1>  sleep1
irb(main):003:1> end

上記の時にZで止める機能

reidline

  • readlineの複数行編集可能版
irb(main):002:1>  p 1
irb(main):003:1>  sleep1
irb(main):003:1> end

上記を1行ずつだけでなく、複数行に渡って同時編集できる

  • history
  • messages

Completion

現在実装中

  • bash/zsh風のcompletionを実現したい (edited)

Grow and Shrink - Dynamically Extending the Ruby VM Stack ( 6/2 10:50- )

http://rubykaigi.org/2018/presentations/sugiyama-k.html#jun02

  • 最近Goとか並列処理が得意な言語が増えてきたので、RubyVMのスタック拡張の改修を行っている

  • スタック拡張についてStretchCallStackChainringという2つの手法の紹介

  • 成果として、メモリ使用量の40%程度の削減に成功
  • 今後の課題としては、さらなる性能向上と幅広いテスト環境での検証をしていく

Ruby code from the stratosphere - SIAF, Sonic Pi, Petal ( 6/2 10:50- )

成層圏から来たRuby code http://rubykaigi.org/2018/presentations/kn1kn1.html

スライド:

github.com

  • Sonic Pi 大人から子供まで、Rubyで音楽を簡単につくれるソフトウエア
  • 内部でpetalという言語(Rubyライク)
  • Rubyコードでマリオの名曲を再現できている…
  • petal
    cps 2 # 1秒に2サイクル
d1 'bd*8' # bdという音を8回 * 1鳴らす
d1 'bd*64 [bd]' # 入れ子もできる
d1 'v' , n: 'irand 2 8' # 乱数もある
d1 'v', rate: 0.5 # 音の高さや逆回転などの制御
  • Euclidean rhythm
d1 'bd(5,8)' # 全体を8として、5を分散させる(太鼓っぽい!)
  • 音声ログから、その音声を再現することも可能
    それに音を追加したり

感想

Rubyコードの簡潔な部分が、曲作りや設定にすごく生かされていると感じた。 GUIな作曲ツールと、このツールで作った曲で作りたいものや出来上がるものがけっこう変わってきそう。

tech.medpeer.co.jp


(☝︎ ՞ਊ ՞)☝︎是非読者になってください


メドピアでは一緒に働く仲間を募集しています。 ご応募をお待ちしております!

■募集ポジションはこちら

https://medpeer.co.jp/recruit/entry/

■開発環境はこちら

https://medpeer.co.jp/recruit/workplace/development.html

swift4.1へのバージョンアップとつまづいたこと

$
0
0

こんにちは。メドピアのエンジニアの保立です。
メドピアでは、医師専用コミュニティサイト「MedPeer」のiOSアプリを3月26日にリリースしました。
詳しくはこちら

メドピアのiOSアプリは、swift4.0で開発を始めましたが、先日swift4.1にバージョンアップしたので、今回のブログ記事では、swiftのアップグレードのためにやったことについて紹介します。

swift4.0から4.1の変更点

3月29日にswift4.1がリリースされました。変更内容とメドピアでの対応について記載します。

変更1) SE-0157 Support recursive constraints on associated types (関連する型の再帰的制約をサポート)

変更内容

swift4.0以前では、「プロトコル内で、プロトコル自身を再帰的に参照する」ことを制限していましたが、swift4.1以降では、その制限が解除されました。

// swift4.0以前では、コンパイルエラーとなるprotocolSequence {
    // `SubSequence` が、再帰的に `Sequence` を参照しているassociatedtype SubSequence:Sequencewhere Iterator.Element == SubSequence.Iterator.Element, SubSequence.SubSequence == SubSequence

    funcdropFirst(_ n:Int) ->Self.SubSequence { ... }
}

ちなみに、swift4.0以前では以下のように実装する必要がありました。わかりづらい。。。

protocolSequence {
    associatedtype SubSequence

    funcdropFirst(_ n:Int) ->Self.SubSequence { ... }
}

structSequenceOfInts:Sequence {
    funcdropFirst(_ n:Int) ->SimpleSubSequence<Int> { ... }
}

structSimpleSubSequence<Element>:Sequence {
    typealiasSubSequence= SimpleSubSequence<Element>typealiasIterator.Element= Element
}
対応

プロトコル内でassociatedtypeを使って再帰的に参照している箇所を修正。

変更2) SE-0185 Synthesizing Equatable and Hashable conformance (EquatableとHashable適合性の合成)

swift4.1化のメインで、最もメリットを享受できる変更だと思います。

変更内容

Equatable / Hashableプロトコル内に実装する必要があった決まり文句を、暗黙的に実装してくれるようになりました。

structQuestion:Equatable {
    letid:Int64lettitle:String// swift4.1以降では、 '=='の実装が不要になる。便利!publicstaticfunc== (lhs:Question, rhs:Question) ->Bool {
        return lhs.id == rhs.id && lhs.title == rhs.title
    }
}

今までは、public static func == (lhs: Element, rhs: Element) -> Bool { ... }を明示する必要があったため、プロパティの追加、削除、または変更があった場合、この演算子を更新しなければならないことと、手作業で記述する必要があるため、漏れや誤植で間違ってしまう可能性がありました。
Hashableの場合、以下のようになります。

structForumTag:Hashable {
    letid:Int64letname:String// swift4.1以降では、 '=='も 'var hashValue: Int{} ` も実装が不要になる。超便利!!varhashValue:Int {
        return id.hashValue
    }

    staticfunc== (lhs:ForumTag, rhs:ForumTag) ->Bool {
        return lhs.id == rhs.id
    }
}
対応

Equatable / Hashableプロトコルに実装された ==hashValueを使うように修正。

変更3) SE-0187 Introduce Sequence.compactMap(_:) (Sequence.compactMap(_:)の導入)

変更内容

Sequence.flatMapを廃止し、 同じ機能として、 compactMap(_:)を導入する。

対応

コンパイルエラーになったら対応。(MedPeerアプリでは Sequence.flatMapを使っている箇所はありませんでした)

変更4) SE-0188 Make Standard Library Index Types Hashable (標準ライブラリのインデックス型をHashableに)

変更内容

swift4から KeyPathという機能が実装されました。 KeyPathについては Qiitaが参考になると思います。

対応

MedPeerアプリでは、KeyPath使ってないので対応不要。

(おまけ) SE-0143 Conditional Conformance (条件付き適合)

この変更は、swift4.2以降で実装されます。

変更内容

URL: https://github.com/apple/swift-evolution/blob/master/proposals/0143-conditional-conformances.md

型引数が特定の要件を満たす場合にのみ、ジェネリック型が特定のプロトコルに準拠するという概念を表現できるようにします。

例えば、Arrayに対して、その要素がEquatableである場合に、Equatableプロトコルを実装することができることを表したい時、以下のように実装します。

extensionArray:Equatablewhere Element:Equatable {
  staticfunc==(lhs:Array<Element>, rhs:Array<Element>) ->Bool { ... }
}

swift4.1以前で、上記のような実装をすると、「 'Array'自体がEquatableでない」という理由で、コンパイルエラーになります。

error: extension of type 'Array' with constraints cannot have an inheritance clause
extension Array: Equatable where Element: Equatable { }
^                ~~~~~~~~~

これが解決されます。 Standard Library Adoptionに記載されている通り、 OptionalArrayEncodable / Hashable適合など、既存のライブラリにはすでに内部で使われています。

swift4.1に移行する時につまづいたこと

swift4.1への移行はほぼスムーズにできましたが、1点だけ修正が面倒なことがありました。Firebaseなどのサードパーティーツールで、以下のようなWarningが出ることです。

/path-to-app/vendor/FirebaseCoreDiagnostics.framework/FirebaseCoreDiagnostics: Failed to parse executable: Unknown header: 1918975009

メドピアでは、ライブラリ管理にCarthageを使っていたのですが、FirebaseなどはCocoaPodsにのみ対応していたので、FirebaseSDKを直接ダウンロードしていました。それにより、Firebaseのバージョン管理が適切にできていないのが原因でした。
結局、Carthageで管理できないライブラリは、CocoaPodsで管理することで解決しました。 この修正により、以下のライブラリをCocoaPodsで管理するように修正しています。

- Firebase/Core
- Fabric
- Crashlytics
- Repro
- R.swift

まとめ

今回、メドピアでは初めてswiftのバージョンアップを実施しましたが、今後もバージョンアップ等で得た知見をブログに執筆していきます。
iOSアプリはまだリリースしたばかりで、開発したいこと/すべきことが山ほどあるので、メドピアに少しでも興味を持っていただけたら遊びに来てほしいです。


是非読者になってください(ง `ω´)ง


メドピアでは一緒に働く仲間を募集しています。 ご応募をお待ちしております!

■募集ポジションはこちら

https://medpeer.co.jp/recruit/entry/

■開発環境はこちら

https://medpeer.co.jp/recruit/workplace/development.html

RubyKaigi2018に参加してきました

$
0
0

こんにちは!メドピア歴1年、Railsエンジニアの小林です。

今回のブログでは、5月31日〜6月2日に仙台で開催されたRubyKaigi2018に全日参加してきましたので、その感想をお届けしたいと思います。

f:id:marikokobayashi:20180605163917j:plain

このブログを書いた人の背景

私はメドピアに入社することになって初めてRubyを書き始めました。

昨年9月に開催されたRubyKaigi2017時はまだRubyを書いて数ヶ月という状態だったのですが、実はそのときも参加しており、今回のRubyKaigi2018は2回目の参加になります。

なぜ前回、まだ右も左も分からない状態で参加したかというと、自分が仕事で一番使っているプログラミング言語の大規模な国際会議が日本国内であるのであれば、外から盛り上がりを見ているよりは実際に体験してみたいと思ったからです。

また、Rubyを書いている仲間を見つけて交流したいという気持ちもあり、参加しました。

今年は、去年のそんな私の姿が評価されたこともあってか(?)、RubyKaigiへ行きたいと表明したメンバー全員分の参加費・交通費・宿泊費が会社で補助していただけることになりました!

tech.medpeer.co.jp

本当は見たセッションすべての感想を書けたら良いのですが、長すぎてまとまりがなくなってしまいそうなので、本記事では特に印象が強かった部分だけを書かせていただきます。

個々のセッションについてもう少し知りたい方は、RubyKaigi中に参加したメンバーがそれぞれ見たセッションを速報としてブログにまとめましたので、こちらをご参照いただければと思います。

tech.medpeer.co.jp

(2日目以降のブログは上の記事にリンクがありますので、辿って下さい!)

速報性を大事にしていたため、突っ込みどころもあるかと思いますが、これはおかしいだろうという箇所がありましたら、ご指摘いただければと思います!

Matz基調講演について

1日目で特に印象に残ったことは何と言ってもMatzのキーノートです。今回は諺についてのお話。

Matzが大事にしている諺として下記の3つが挙げられていました。

  • 名は体を表す
  • 時は金なり
  • 塞翁が馬

名は体を表す

Rubyのカンファレンスに参加するようになってから、Matzのお話を聴講したことは何回かありますが、そのたびにMatzは毎回「名前重要」という話をしているということに気づきました。

名は体を表す、といいますが、確かにRubyがRubyという名前ではなかったらその言語に対して持つ印象も違っただろうと思います。持つ印象が違えば、コミッターやユーザーがその言語をどのように扱うかも変わってくると思います。そうであれば今のRubyとは全く別の言語になっていたかもしれません。

正しい命名付けを行うことは、正しく概念を理解していることでもあります。良いプログラマーは、良い命名者でもあります。

「概念がどういう局面で使われるか十分に理解していれば比較的簡単に名前をつけられる」「よいプロジェクトの名前はコミュニティの旗印になったり、求心力になったりする」、とMatzは説いていました。

ちなみに、もし今のネット社会の中で新たに言語の名をつけるとしたら、Rubyはググラビリティが悪いのでつけないだろうということでした。

確かにMatzが2014年に作ったもう一つの言語であるStreemは、実在する単語を少しいじったものであるので、ググればすぐMatzのリポジトリが出てきてググラビリティが良いですね。ちなみに某言語…おっとだれか来たようです。

時は金なり

時間は、誰にでも平等に1日24時間与えられています。時間をどう使うかは、どういう人生を歩むか、を決定づけます。

プログラミングで自動化できることは自動化していったほうが生産性の高い人生を歩むことができると思います。

Rubyには便利なメソッドがたくさんあるので、時間を有効活用するのを助けてくれる傾向があります。

また、プログラミングにおいては、問題が発生したときに早く解決できたほうが時間の短縮になります。問題が自分ひとりでは解決できないこともあると思いますが、そういった意味では「ほとんどの人にとって『質問に答えてくれる人が身近にいる言語』が一番いい言語」でしょう。

Rubyはコミュニティが活発なので、質問に答えてくれる人を比較的見つけやすい環境であると言えると思います。

処理速度については、Rubyは遅いと言う人も居ます(Rubyでググると「Slow」が候補として上がっています)が、現在もコミッターの方々がパフォーマンス改善を続けています。

書きやすさと処理速度についての良いバランスを取っている言語だと思います。

塞翁が馬

塞翁が馬というのは、中国の故事『准南子』に出てくる小話のことで、「人間万事塞翁が馬」とも言われているかと思います。

内容は、人生には良いことがあってもその後悪いことが起きることもある、悪いことがあってもその後に良いことが起きることもある、先のことはわからない、というお話です。

読む人によっては、良いことがあっても浮かれすぎるな、悪いことがあっても落ち込みすぎるな、という解釈もあるかと思います。

Rubyは多くの人に使われる言語となりましたが、先のことはわかりません。

他の言語に取って代わられ、Rubyユーザーが縮小してしまう未来もないとは言い切れません。

そんな中でも今後も時代に合わせた進化を続けることで、時代に合わせたRubyのあり方を模索していきたい、といった趣旨のお話でした。

私も実は個人的に以前からこの諺を気に入っており、人生そのものだと感じているため、座右の銘の一つとしておりました。

Matzの考え方には深く頷ける部分が多くあり、そのように強い信念、理念を持った言語設計者が作った言語だから多くの人に広まったのだなあと改めて思いました。

--

また、セッションの締めにはMatzは「Rubyに『言語仕様として』型が入ることはない」とおっしゃっていました。型推論についてが今議論されていますが、それはあくまで推論であるということです。

Rubyを使う人は型を使いたくない人も一定数いると思うので、もし言語仕様としてそこを変えてしまうとしたらかなりの既存のユーザーが置いてけぼりになるのではないかと思います。

時代の流行を気にしながらも、Rubyとしてそぐわないところは無理に採用しないというMatzの意志を感じました。

Ruby Committers vs the Worldについて

2日目の締めはRuby Committers vs the World。

壇上にRubyコミッターが勢揃いする光景は圧巻です。

コミッターたちが、寄せられた質問などについて答えてくれました。

このようにRubyコミッターが勢揃いすると、Matzの意見が必ずしも通っているわけではないということに気付かされます。

特にMatzが最近Ruby2.6に追加したというエイリアス「then」の命名の良し悪しについては熱い議論が交わされていました。

普段Rubyコミッターたちがどのように議論してRubyを改善・機能追加しているかの様子が垣間見れたような気持ちになります。

Matzが愛されて(いじられて?)いる様子には、ただのいちRubyユーザーである自分も何だかつられて笑ってしまいました。

f:id:marikokobayashi:20180604155312j:plain

セッションの見かたについて

今回2回目の参加でしたが、去年の1回目の参加後は「なにがなんだかわからない…参加費を無駄にしてしまったのでは…」と絶望して帰ってきたのですが、2回目に参加してみると「あれ、もしかしたら前回の反省がちょっと生きているかも、少しは成長したかも」と思いました。

具体的には、「すべてを1回でわかろうとしない、絶望しない」というところが大事なポイントかもしれません。

イベントに参加する人は、Rubyの内部実装やC言語を深くわかっている人だけでなく、私のようなRailsを使っているだけというWeb系エンジニアも多くいると思いますが、そんな中でも「知識不足ゆえにまったく理解できなそうなセッション」と「Web寄りやビジネス寄りで、現時点での知識でも少しは理解できるところがありそうなセッション」があると思うのです。

割り切って「現時点で少しは理解できるセッション」に絞って聞いてみるのも戦略としてありかなと思いました。

(もちろん自分の知識不足に喝を入れるために全然わからないセッションを聞きたい、という人はそれもアリかと思います)

また、セッションは全部の時間で聞こうとすると1日に6~7個聞くことになると思いますが、どれも全部内容が濃く、それぞれを深く理解するためには、無理に全部の時間に聞かなくても良いと思いました。

ありがたいことに、最近はセッションの動画がのちに公開される(今年も公開していただけるようです)ので、会場では体調を整え、自分の体力と相談しながら適度に休憩を入れるのも、長い3日間を乗り切るポイントかなと思います。

ステッカーについて

メドピアは今回ゴールドスポンサーとして参加したので、ステッカー置き場にステッカーを置かせていただきました。メドピアのキャラクター、メドベアちゃんがRubyと戯れている様子がステッカーになっています。

今後Ruby関連のイベントでスポンサーする機会があればそのときにも持っていくと思うので、今回もしもらい損ねたという人がいましたら、その機会に是非!

f:id:marikokobayashi:20180604154754j:plain

英語について

セッションの大半は英語なため、英語力に課題を感じました。RubyKaigi直前は英語力を高めるため下記のPodcastを聞いてみた*1のですが、それでもやはりセッションを1回聴講しただけで理解するのは難しいなと感じました。

Ruby Rogues Archive | Devchat.tv

5by5 | Ruby on Rails Podcast

のちにセッションの動画が上げられたら、繰り返し見て、理解を深めたいと思います。

観光について

RubyKaigiは、東京開催以外だと地元の美味しい料理を楽しんだり観光を兼ねられるのが1つの魅力でもありますね。

2日目の夜は、会社のメンバーみんなで牛タンを食べてきました。

予約していたお店は行ったことがある人はおらず不安でしたが、結果的にとても美味しく皆満足していました!

f:id:marikokobayashi:20180604154814j:plainf:id:marikokobayashi:20180604154845j:plain

ちなみに最終日翌日は日曜でしたので、個人的に仙台周辺を観光してきました。

時間のある方は松島まで行かれた方もいらっしゃるようですが、私は以前から行ってみたかった仙台城跡を見てきました。

城跡なので実際にお城の中に入れるわけではないのですが、伊達政宗公の騎馬像の実物を目の前にしたときは改めて「仙台に来たんだな〜!」と感じました。

f:id:marikokobayashi:20180604154920j:plain

仙台城跡まではバスで行くこともできましたが、国際センター駅から歩いていくと傾斜がある山を少し登ることになり、運動不足気味なエンジニアには適度な運動になるかもしれません!(女性や子供でも登れていたので、それほど苦しいレベルではないと思います)

全体を通して

今回も、RubyKaigiにはコミッターからRubyを最近書き始めた人まで、たくさんのRubyを使う人が参加していました。

そんな中で、自分がお話させていただいた中では「RubyKaigiに一度参加してみたけれど、自分の目指す方向性はRubyを極めたいわけではなくビジネスに使いたいだけだった」と改めて認識する方がいらっしゃったり、「参加してみてもっとRubyの内部実装を知りたくなった、C言語を勉強したくなった」という方がいらっしゃったり、参加した方の分だけそれぞれの感想があり、それぞれ素敵だなと思います。

セッションを聞くことも楽しいですが、このようにたくさんの方がそれぞれのRubyとの関わり方をしているのだということに思いを馳せられるのもRubyKaigiの良いところだなあと思いました。

f:id:marikokobayashi:20180605163304j:plain

セッション以外にも、休憩時間にもブースを見て回ったり、ペアプロの様子を見たり、サイン会があったり、朝食やお弁当を各スポンサーさんに出していただいたり、セッション終了後は1-3日目それぞれPartyに参加できたりと盛り沢山な内容で、Kaigiでもあり、お祭りでもあるような3日間でした。

個人的には、去年は知り合いが少なく少し寂しかったのですが、今年は会社のメンバーがたくさん参加したので、あまり寂しい思いをすることがなく過ごすことができました!

f:id:marikokobayashi:20180605163344j:plainf:id:marikokobayashi:20180605163835j:plain

また、同じコミュニティに顔を出し続けると少しずつ知り合いが増えてきて、いろいろな方とお話できるようになり、前回参加したときより少しではありますが自分がRubyistな方々と近づけたのかなと思いました。

来年は福岡、4月18〜20日の開催とのことです。来年も多くのメドピアメンバーが参加し、また多くのRubyistと交流できる会になれば良いなあと思っています。

*1:理解したとは言っていない


Rails × ECS でオートスケーリング&検証環境の自動構築

$
0
0

マリオカートでカーブを曲がるときに体を傾斜させてしまうCTO室 kenzo0107です。

今回は 2018/04/02 にリニューアルしたイシコメの Rails × ECS についてです。

イシコメとは?

「イシコメ」は、医師10万人の声でつくるヘルスケアメディアです。
医師と一般の方々をつなげることで、医療情報格差を埋めることを目指しています。

MedPeerの10万人の医師会員に協力いただいたアンケート結果をもとに編集部で記事を執筆し、医師監修の上で配信。多くの医師の声を反映することで、より正しい情報を提供しています

ishicome.medpeer.jp

リニューアル経緯

リニューアル前は以下のような構成でした。

f:id:kenzo0107:20180618120335p:plain

  • フロントに Laravel 5
  • バックに Drupal
  • Docker on EC2
  • コンテナイメージの S3 でのプライベート管理

Docker がまだ出てきて間もない頃、当然、AWS ECS がリリースされておらず
果敢に技術的にチャレンジした痕跡が多々ありました。

ですが、 当時の構成では以下の問題がありました。

  • イメージの管理やデプロイがし辛い
  • スケーリングが考慮されてない

また、
Google 検索結果の医療や健康に関する検索結果の改善が追い風となりトラフィックが伸び、スケーリングへの配慮が重要になりました。

webmaster-ja.googleblog.com

上記の問題を AWS ECS・ECR の恩恵を受けることで解決しようと考えました。

また、
開発促進をすべく、弊社で開発の知見のより多い Rails へのリプレイスを行う運びとなりました。

まず結論

f:id:kenzo0107:20180618121513p:plain

※ 左側がユーザサイド、右側が管理画面になります。

  • イメージ管理は ECR
  • デプロイは ecs-cli
  • CloudFront > ALB > Nginx > Rails というルーティング*1
  • Tasks, EC2 をオートスケーリング
  • RDS Aurora MySQL へリプレイスし Read Replica オートスケーリング

メディアサイトというサイト特性もあり、アクセスが集中しスパイクすることを鑑みて CloudFront でのキャッシュを有効にし、オートスケーリングできるところはする様にしました。

リニューアル直後の 2018年4月頃、麻疹が流行した際に麻疹関連の記事へのアクセスが急増した際は、本当にこの構成にしておいてよかったと思いました。

ishicome.medpeer.jp

続いて ECS でサービス構成するに当たって考慮したことをまとめました。

ECS への準備で考慮したこと

  • コンテナ設計
  • デプロイ
  • ロギング
  • オートスケール
  • バッチ処理
  • 検証環境

コンテナ設計

Nginx

Nginx をルーティングに挟んだのは以下の理由からです。

  • Rate limit の設定を細かくコントロールしたかった。*2
    • AWS WAF では最低でも 5分間に 2000 リクエスト以上でアクセス制限可能
  • IP 直指定回避

Rails

Rails コンテナについて以下の点を設計考慮しました。

secrets.yml

yaml_vaultで暗号化・復号するようにしました。*3

  • KMS のエイリアスキーを作成し
  • そのキーでの暗・復号権限を IAM Group で管理する

こうすることで
権限の付与時には IAM Group に含める、
権限の剥奪時には IAM Group から除外する、
と管理が楽になりました。

Task Definition には基本秘密情報を載せない様にしました。*4

  • 実行コマンド
// secrets.yml の暗号化 (env: production)
yaml_vault encrypt \
  config/secrets.yml \
  -o config/encrypted_secrets.yml.production \
  --cryptor=aws-kms \
  --aws-region=ap-northeast-1 \
  --aws-kms-key-id=<kms alias> \
  --aws-profile <profile>

// secrets.yml の復号 (env: production)
yaml_vault decrypt \
  config/encrypted_secrets.yml.production \
  -o config/secrets.yml \
  --cryptor=aws-kms \
  --aws-region=ap-northeast-1 \
  --aws-kms-key-id=<kms alias> \
  --aws-profile <profile>
Dockerfile (Rails)

RAILS_ENV を渡してビルドし各環境毎に処理分けさせてます。

FROM ruby:2.5-alpine

RUN apk update \
  && apk upgrade \
  && apk add --update build-base mysql-dev nodejs tzdata git \
  && rm -rf /var/cache/apk/*

# TZ JST
RUN cp /usr/share/zoneinfo/Asia/Tokyo /etc/localtime

ENV app /work/app
WORKDIR $app

COPY . $app
RUN bundle install -j4 --retry 6 --without test development --no-cache \
  && npm install --production --no-progress \
  && mkdir -p tmp/sockets \
  && mkdir -p tmp/pids

ARG RAILS_ENV

# docker build 時に db 接続しようとする為、接続しない様、nulldb 指定
RUN chmod +x docker/on_build.sh \
  && sync \
  && docker/on_build.sh

CMD ["bundle", "exec", "puma", "-C", "config/puma.rb"]

docker/on_build.sh

  • assets:precompile, assets:sync 実行時に S3 にアップロード, CloudFrontで配信 by asset_sync, fog-aws
#!/bin/sh

set -e

# docker build 時に db 接続しようとする為、接続しない様、nulldb 指定
DB_ADAPTER=nulldb bundle exec rake assets:precompile assets:sync RAILS_ENV=${RAILS_ENV}

デプロイ

ecs-cli を採用しました!

採用理由は以下の通りです。

  • ECS 向けの AWS オフィシャルのツール
  • 既存の設定した AWS credential 利用可
  • Task 定義が docker-compose.yml 形式
  • Fargate 対応可

Fargate Tokyo region が待ち遠しいです♪*5

デプロイフロー

f:id:kenzo0107:20180618125321p:plain

  1. master, develop, qa/*に merge をトリガーに CircleCI ビルド
  2. テストがパスしたら CodePipeline 開始
  3. CodePipeline で cap (production|staging) deploy実行

CircleCI には CodePipeline の開始・更新権限のみの AWS Access Key ID, Secret Key を設定しています。

CircleCI 上からデプロイしないの?

はじめに検討したのですが以下理由により上記構成を選択しました。

  • CircleCI とビルドサーバを分けることで CircleCI はテストに専念できる。
    → デプロイが終わるまでリソースを手放さず他のビルドが遅延するのを避ける。

ゆくゆくは CodePipeline でテストも実行し全てを完結させてみてコスト比較する検証をしたいと思っています。
既にあったら教えてください

ロギング

f:id:kenzo0107:20180618130832p:plain

  1. コンテナから awslogs で CloudWatch に出力
  2. Lambda で日次で CloudWatch のロググループを S3 に保存

Rails コンテナでは環境変数に RAILS_LOG_TO_STDOUT: 1を設定することで Rails のログを標準出力し、CloudWatch に流す様にしています。

また、お好みですが
logrageで CloudWatch 上のログの視認性が高まりました。

CloudWatch のイベントフィルターで

{$.db >= 1000}

とすることで DBで 1000 msec 以上時間を要したイベントを抽出することができます。

オートスケール

f:id:kenzo0107:20180618131419p:plain

スケールアウト時に考慮したことと

  • まず EC2 インスタンスを増やして、その後、Task を増やす
    → Task が一方に偏ってしまう等の事象が発生してしまう為です。

CloudWatch の監視設定で evaluation_periods を EC2 < Task の様に設定しました。

  • インスタンス単体の CPU 使用率等のメトリクスでなく、Service でのメトリクスで考えること
    → 一時的な偏りでなく、全体的に負荷が高い場合にスケールする、という様にする為です。

  • RDS Aurora MySQL のオートスケールは Read/Write を switch_pointで参照先を切り替える様にしました。

バッチ処理

クラスタを指定し ECS Scheduled Tasks で one-off container で定期実行します。

以下 Sitemap 定期更新タスク例です。

f:id:kenzo0107:20180618132440p:plain

検証環境の自動構築

f:id:kenzo0107:20180618133934p:plain

qa/*というブランチ名で push するとそのブランチに紐づくポートで検証環境を自動で構築する様にしました。

これによりエンジニア・ディレクターの検証回数が飛躍的に増え、画面を通じてコミュニケーションすることで認識の齟齬が減りプロジェクトがより円滑に進む様になりました。

現状、インスタンスタイプ m5.largeの EC2 インスタンスをスポットで利用することで価格を抑えつつ 3~5 程度の検証環境が動作しています。

構築手順

  1. ALB の空いている Listener Port 取得
  2. 空き Port を元に target group 作成
  3. target group に branch 名をタグ付け
  4. インスタンス登録
  5. ALB Listener 作成
  6. ECS Service 作成
  7. service 名を branch 名がわかるようにラベリング
  8. QA 環境へ deploy

現状不要となった QA は cap で削除用コマンドを用意してますが ブランチ削除時に自動削除される等、検討中です。

ちなみに、どのブランチがどのポートを使っているかは Slack bot が教えてくれます。

f:id:kenzo0107:20180618143727p:plain

まとめ

Rails × ECS へのリプレイスにより自動化できた部分が多く本当に運用が楽になったと感じます。

特に検証環境の自動構築は他プロジェクトでも望まれ導入を進めています。

また、Fargate の検証を実施しておりますので、その折には本ブログにまとめていきたいと思います。

以上です。
参考になれば幸いです。


是非読者になってください(︎ ՞ਊ ՞)︎


メドピアでは一緒に働く仲間を募集しています。 ご応募をお待ちしております!

■募集ポジションはこちら

https://medpeer.co.jp/recruit/entry/

■開発環境はこちら

https://medpeer.co.jp/recruit/workplace/development.html

インフラ構築・運用や開発プロセスの改善に携わっていただける SRE 募集中です!

SRE(サイトリライアビリティエンジニア) - 採用情報 - メドピア株式会社

Rails未経験/経験年数が2年以内のポテンシャルエンジニア絶賛募集中です!*6

Rails開発してみたい人、新しい技術に意欲的な人、リードエンジニアに教育されてみたい(?)人、医療に関わるサービス開発を行ってみたい人、その他メドピアに興味を持った方などは、是非、コンタクトを取って頂ければと思います!

*1:Nginx を挟んだ理由はコンテナ設計で!

*2:時折、 /phpmyadmin のようなパスでアクセスしてくるような攻撃が1分間に 200 リクエスト程きますが、 AWS WAF の based rule では防げません。

*3:イシコメ リニューアル直後に Rails 5.2 credentials が出た為、secrets.yml での秘密情報管理をしています。

*4:EC2 上で ssm で値を取得し環境変数に設定する方法もありますが Fargate の時どうするのか検証仕切れず、一旦 secrets.yml に寄せる様にしました。

*5:本記事執筆時に Fargate Tokyo Region の 2018年7月 予定がアナウンスされる神タイミングでしたので、執筆を急ぎました

*6:リードエンジニアも募集中です!

poltergeistからheadless chromeへ移行する時に気をつけること

$
0
0

こんにちは。メドピアのRuby(Rails)化をお手伝いしている@willnetです。最近は子育てに忙しくしています 👶

先日、メドピアで利用しているcapybaraのjavascript driverをpoltergeistからheadless chrome(selenium-webdriver)に移行しました。driverを変更するにあたって既存のテストコードをいくつか修正する必要があったので、そこで得た学びを共有したいと思います。

なぜ移行したのか

ここ数年、Railsでエンドツーエンドのテストを書くときにはpoltergeistを使う、というのがデファクトスタンダードだったはずです。それ以前はみんなcapybara-webkitを使っていましたが、poltergeistはバックエンドにPhantomJSを使っており、Qtに依存しているcapybara-webkitと比べてビルドが簡単だったことから徐々にシェアを増やしていったように思います*1

そんな中、だいたい1年ほど前にChromeのバージョン59からヘッドレスモードが実行できるようになりました。これをうけて、PhantomJSの開発が停止されました。PhantomJSをメンテするのはだいぶ大変だったみたいです。

これは当然、依存しているpoltergeistにも影響します。PhantomJSの依存をやめる構想もあったようですが、リポジトリを眺めている限りではあまり進捗しておらず、さらにpoltergeistの開発自体あまりアクティブではないように見えます。

ここまでの経緯を考えると、headless chromeに切り替えるのは無難な選択と思われるのですが、移行時期には考慮が必要でした。headlessモードが使えるchromeがリリースされた当初は、chromedriver(chromeを別プロセスから動かすのに必要なもの)に大きめのバグがあり、すぐに移行するには不安な状況でした。しかしそれから1年ほど経過した今なら問題なく移行できるのではないか?となったのでした。

そんなわけで、フィーチャスペックのときに使うドライバをpoltergeistからheadless chromeに変更しました。

f:id:willnet:20180619182309p:plain

移行に必要なこと

リリースから1年経ってこなれたと言っても、poltergeistからheadless chromeへの変更は単純に入れ替えただけでは完了しません。テストコードの変更も必要です。

僕たちはcapybaraのインターフェースを経由してheadless chromeやpoltergeistを利用しているので、移行してもテストの書き方は基本的には変わりません。ただ、ドライバによって対応しているメソッドとしていないメソッドがあるため、poltergeistのときは動いていたコードがheadless chromeでは動かないケースがあるのでした。そういうところは動くように書き換えてあげる必要があります。

大抵の箇所は機械的に置換していくようなやり方でOKなのですが、テストの内容によってはもっと頑張らなければならないところがあるのでそれを紹介します。

ファイルダウンロードのテストを変更する

例えばリンクをクリックするとcsvがダウンロードできるようなページがあったとします。poltergeistを利用している場合、次のようにレスポンスヘッダを見て、Content-Typeが text/csv であることをもってテストを成功をみなすという方法があります。

context 'CSVダウンロード用のページに遷移したとき'do
  before { visit csv_download_path }

  it 'かつ"CSVダウンロード"をクリックしたらCSVファイルがダウンロードできること'do
    click_on 'CSVダウンロード'
    expect(page.response_headers['Content-Disposition']).to eq("attachment; filename=\"download.csv\"")
    expect(page.response_headers['Content-Type']).to eq("text/csv")
  endend

上記のコードは、headless chromeに切り替えるとうまく動きません。どうやらheadless chrome(正確にはselenium webdriver)はレスポンスヘッダを見るAPIを提供していないようです。バグとかではなくそういう設計方針の模様

レスポンスヘッダを見れないとしたら、実際にダウンロードしたファイルの内容を確認するしかありません。こちらのエントリを参考にしつつ、実際にダウンロードしたファイルの中身をチェックする方法に変更しました。

まず、次のようなヘルパーメソッド群をモジュールとして定義し、フィーチャスペックで使えるようにします。

moduleDownloadHelperTIMEOUT = 10PATH    = Rails.root.join('tmp/downloads')

  module_functiondefdownloadsDir[PATH.join('*')]
  enddefdownload
    downloads.first
  enddefdownload_content
    wait_for_download
    File.read(download)
  enddefwait_for_downloadTimeout.timeout(TIMEOUT) do
      sleep 0.1until downloaded?
    endenddefdownloaded?
    !downloading? && downloads.any?
  enddefdownloading?
    downloads.grep(/\.crdownload$/).any?
  enddefclear_downloadsFileUtils.rm_f(downloads)
  endendRSpec.configure do |config|
  config.include DownloadHelper, type: :feature
  config.before(:suite) { Dir.mkdir(DownloadHelper::PATH) unlessDir.exist?(DownloadHelper::PATH) }
  config.after(:example, type: :feature) { clear_downloads }
end

これにより、先程のテストを次のように変更することができます。

context 'CSVダウンロード用のページに遷移したとき'do
  before { visit csv_download_path }

  it 'かつ"CSVダウンロード"をクリックしたらCSVファイルがダウンロードできること'do
    click_on 'CSVダウンロード'
    expect(download_content).to eq "row1,row2\n"endend

さてこれで解決…と思いきや、もうひとつ対処が必要だったりします。どうやら、 headless chromeはデフォルトでファイルダウンロードをしないようです。そこでstackoverflowの回答を参考に、ファイルダウンロードを許可するようにしました。具体的には次のようにconfig/rails_helper.rbに記述しています(bridge変数以下がファイルダウンロードを許可しているコード)。

Capybara.register_driver :headless_chromedo |app|
  driver = Capybara::Selenium::Driver.new(
    app,
    browser: :chrome,
    desired_capabilities: Selenium::WebDriver::Remote::Capabilities.chrome(
      login_prefs: { browser: 'ALL' },
      chrome_options: {
        args: %w(headless disable-gpu window-size=1900,1200 lang=ja no-sandbox disable-dev-shm-usage),
      }
    )
  )
  bridge = driver.browser.send(:bridge)
  path = "session/#{bridge.session_id}/chromium/send_command"
  bridge.http.call(
    :post, path,
    cmd: 'Page.setDownloadBehavior',
    params: {
      behavior: 'allow',
      downloadPath: DownloadHelper::PATH.to_s,
    }
  )
  driver
endCapybara.javascript_driver = :headless_chrome

これでファイルダウンロードをheadless chromeでテストできるようになりました。

ブラウザ上で見えない要素に対応する

headless chromeは、poltergeistと比べて「ブラウザ上で見える要素であるか否か」にシビアなようです。例えば、position: fixed; left: 1000pxとしているDOM要素があるとします。このとき、ブラウザのウィンドウサイズが(800, 600)のように小さく要素が画面外になる場合は、その要素は見えないという扱いになります。

この場合は単にウィンドウサイズを大きくしてあげればよいのですが、それでは対応できない場合は次のようにvisible: falseをつけて、見えない要素の中で指定のDOMが存在するかを確認する必要があります。

expect(page).to have_css('.super-right-dom', visible: false)

このように「画面外にあるので見えない」というのはわかりやすいのですが、なにをもって「見える」「見えない」と判断しているのか難しいなと感じるケースがあります。例えばあるページではbxSliderを利用して画像をカルーセル表示しているのですが、スクリーンショットで見えていることが確認できるDOM要素もheadless chrome的には見えない扱いをされていました。

やむを得ずこれもvisible: falseとして対応しました。しかしこのあたりはまだ深く調査できてないので、詳しい方いたら教えていただけると嬉しいです。

細かい変更点

次のような細かい点にも対応しました。このへんは単に置換するだけなので軽く箇条書きで済ませます。

  • triggerメソッドがない
    • trigger('click')を使わず、clickメソッドを使うようにした
  • ウィンドウのリサイズのお作法が違う
    • before: Capybara.page.driver.browser.resize(320, 580)
    • after: Capybara.current_session.current_window.resize_to(320, 580)
  • ブラウザのダイアログが表示される部分は、polgergeistでは自動でacceptされるが、headless chrome(というかselenium webdriver)ではどのように対応するかをaccept_alert {}みたいに書く必要がある

まとめ

polgergeistからheadless chromeへの移行において、気をつける点と変更が必要な点について紹介しました。headless chromeに移行することで、今後メンテナンスが続いていくであろうという安心感が得られます。しかし、セットアップには今回紹介したようにコツをつかむ必要があります。

この記事がこれからheadless chromeへ移行する人や、移行に苦労している人にとって参考になれば幸いです。


是非読者になってください(︎ ՞ਊ ՞)︎


メドピアでは一緒に働く仲間を募集しています。 ご応募をお待ちしております!

■募集ポジションはこちら

https://medpeer.co.jp/recruit/entry/

■開発環境はこちら

https://medpeer.co.jp/recruit/workplace/development.html

Rails未経験/経験年数が2年以内のポテンシャルエンジニア絶賛募集中です!*2

Rails開発してみたい人、新しい技術に意欲的な人、リードエンジニアに教育されてみたい(?)人、医療に関わるサービス開発を行ってみたい人、その他メドピアに興味を持った方などは、是非、コンタクトを取って頂ければと思います!

*1:PhantomJSもQtに依存しているけど、PhantomJSはバイナリが配布されているのでビルドせずにインストールできるというのが大きかった

*2:リードエンジニアも募集中です!

開発合宿@伊豆大島に行ってきました!

$
0
0

こんにちは。メドピアのサーバサイドエンジニアの小林です。

まずはじめに、この度の西日本豪雨で被災された方々にお見舞い申し上げますとともに、被災地の一日も早い復興を心よりお祈り申し上げます。

今回のブログでは、メドピアでは恒例となっているエンジニア・デザイナー開発合宿に参加してきましたので、レポートさせていただきます。

メドピアでは、これまでにも年2〜3回ぐらいのペースで開発合宿を開催しています。

この夏は、東京の離島、伊豆大島へ…!メドピアとしても合宿で島へ行くのは初めてのことでした。

1日目

東京港竹芝ターミナルから船に乗っていざ、出発! 船は予想通り(予想以上に?)揺れるので酔い止め薬必須です。

船に揺られること2時間、着いたのは伊豆大島の北の玄関口、岡田港です。

f:id:marikokobayashi:20180711194653j:plain

ここから更に三原山に登り、車で30分程経った頃、標高500mほどに位置する大島温泉ホテルさんに到着。

【公式サイト】伊豆大島・三原山温泉/大島温泉ホテル

f:id:marikokobayashi:20180711195031j:plain

到着…したは良いものの…あたりは一面の霧でした…!肌寒い…!

私はあたり一面を山に囲まれた田舎出身者ですが、今回は事前にちゃんと標高を調べていなかった中、実家の標高より高い場所に来てしまい *1、軽装で来た(薄手のカーディガンは持っていましたが…)ことを若干後悔しました。

しかし、今回は観光に来たのではなく合宿。山を降りようにもこの霧の中ではいまいち降りる気も湧いてきません。 合宿所にこもるにはうってつけではないでしょうか…!?!?

f:id:marikokobayashi:20180711210044j:plain
名物の椿フォンデュの天ぷらが美味しかったです。ところてんが食後のデザートのあとに出てきました

今回は、ホテル内に全員が集まれる会議室や広間のような場所は借りていないため、借りた客室の中で一番広い部屋にいったん集まって、それぞれが何をやるか発表しあって合宿スタートです!

f:id:marikokobayashi:20180711210732j:plainf:id:marikokobayashi:20180711210218j:plain
1部屋に集まってやること発表会

2日目

2日目も、特に皆出かける予定もなく、宿でもくもく…。

f:id:marikokobayashi:20180711211007j:plainf:id:marikokobayashi:20180712153054j:plain
もくもくしている様子
ちなみに温泉は昼は3時から夜の12時まで入れるようになっていましたので、3時になるとすぐにお風呂に入り出す人もいました。
f:id:marikokobayashi:20180711210927j:plainf:id:marikokobayashi:20180711211022j:plain
2日目のご飯の様子

最終日

最終日はすぐにやってきます。3日間あるとはいえ、1日目の夕方に宿に到着して3日目の朝には宿を出発。 ご飯・睡眠・お風呂の時間も考えると、開発にあてられた時間は実質1日強といったところです。

最終日の朝、宿を出発したあと、帰りの船に乗るまで時間がありましたが、あいにくの雨でしたので、一旦皆で元町港の近くにある御神火温泉さんに立ち寄り、休憩所で開発をしたりそれぞれ好きなように過ごしていました。

f:id:marikokobayashi:20180712201548j:plainf:id:marikokobayashi:20180711211250j:plain

その後、出発1時間ほど前になってやっと雨が止んできたので、岡田港にバスで向かい、近辺でお土産を購入したり、アイスを食べたりと束の間の夏気分を味わいました。

f:id:marikokobayashi:20180711211439j:plainf:id:marikokobayashi:20180711211411j:plain
べっこうずしが美味しかったそうです(私は食べれなかった)

成果発表会

成果発表会は後日会社で行いました!FirebaseやGraphQL、Google Cloud Speech APIなどのライブラリを試した人、資格取得に向けた資料作りをした人、パフォーマンスチューニングに挑んだ人などがいて様々な種類の発表がありました。エンジニア・デザイナー以外のメンバーにも聞いていただけました!

f:id:marikokobayashi:20180711212134j:plainf:id:marikokobayashi:20180711212238j:plainf:id:marikokobayashi:20180712152154j:plainf:id:marikokobayashi:20180711212207j:plain

まとめ・感想

今回は伊豆大島ということで、最初は「夏の海!観光!」といった期待をしていた人が少なくなかったようですが、実際は天気もあまり優れず山の上にこもりきりということになりました…!! そのぶん開発自体に集中できたという人も多いのではないかと思います。

私個人としては、今回は3人1チームで1つ新サービスを立ち上げるということにチャレンジしました。

私は合宿に参加するのが1年前から数えて3回目になりました。 これまでにも合宿中に新サービス作りにチャレンジしようとしたこともありましたが、今まではRails開発経験の浅さもあって(と言ってしまうと言い訳じみていますが…)人に見せられるような形での新サービスを3日で作り上げるということはできなかったのですが、今回は3人のチームで、各々の分担を決めて限られた日程の中でスケジュール立てして作っていったので、発表のときには「新サービスこんな感じで作ったよ!」と紹介することができ、達成感がありました。

合宿に参加する意義としては、新しいライブラリを試したり、普段業務で後回しになって手を出せずにいるようなリファクタリング等に取り組める場として使えるというのももちろんですが、「短期間でどうやって人に見せられるような成果を出せるか?」の経験を積むことが出来るという側面もあるように感じました。

今後も開発合宿を定期的に開催していただける予定のようなので、次回あたりには是非私の地元の県での合宿の開催を検討してもらえないかな〜と密かに狙っています。

以上、メドピア開発合宿@伊豆大島の様子をお伝えしました!

f:id:marikokobayashi:20180711213126j:plain


是非読者になってください(︎ ՞ਊ ՞)︎


メドピアでは一緒に働く仲間を募集しています。 ご応募をお待ちしております!

■募集ポジションはこちら

https://medpeer.co.jp/recruit/entry/

■開発環境はこちら

https://medpeer.co.jp/recruit/workplace/development.html

Rails未経験/経験年数が2年以内のポテンシャルエンジニア絶賛募集中です!*2

Rails開発してみたい人、新しい技術に意欲的な人、リードエンジニアに教育されてみたい(?)人、医療に関わるサービス開発を行ってみたい人、その他メドピアに興味を持った方などは、是非、コンタクトを取って頂ければと思います!

*1:長野県民は実家の標高を言えるらしい(詳しくはググってください)(私も急に聞かれたけどおおよそ正確な値を言えました)

*2:リードエンジニアも募集中です!

開発環境のレンダリング完了時間を1/10にした話(Rails)

$
0
0

ビール大好きですが、3回に1回は年齢確認をされる26歳エンジニアの村上(pipopotamasu (pipopotamasu) · GitHub)です。

今回はリクエストからレンダリング完了までの時間を減らした取り組みについて書こうと思います。

f:id:ec0156hx39:20180820162919p:plain

きっかけ

メドピアではメインプロダクトをRuby on Railsで開発しています。 Railsで開発を始めてから2年半、すくすくとアプリケーションが成長してきています。 しかし成長していくにつれ問題が出てくるのが世の常、以下のような問題が生じてきました。

「ページ読み込みが遅い...」 f:id:ec0156hx39:20180816143017p:plain

Webアプリケーション開発者は開発環境で1日に何十回とページ読み込みをさせると思いますが、Railsの成長と共にそれがめちゃくちゃ遅くなってきたのです。

環境

メドピアのフロントエンド環境はassets pipeline(browserify-railsを用いてビルドしている)で管理している部分と、Webapckerで管理している部分があります。 今回の取り組みは、JavaScriptをassets pipelineからWebpackerへ移行している過渡期に行いました。

問題は何か?

さて、ページの読み込みがめちゃくちゃ遅いという問題にぶちあたりましたが、これだと問題が大き過ぎるのでもうちょっと細分化してみましょう。 以下はリクエストからページレンダリング完了までの流れになります(超ざっくりです)。

f:id:ec0156hx39:20180817113222p:plain

①~⑤まで確認してみたところ、②と④、⑤に時間がかかっていることがわかりました。

まずdev toolの計測を見てみましょう。 f:id:ec0156hx39:20180817131401p:plain

青色が待機時間、サーバーから応答が返るまでの待ち時間です。最初のHTML取得で結構時間がかかっていることがわかります。 ピンク色はブロック時間、ネットワーク接続の待ち行列で費やした時間を表しています。

最初のHTML取得までの時間、つまり②の時間が遅いことについては、開発環境であるためRailsのconfig.cache_classesをfalseにしていてコードのキャッシュをさせないようにしているので、遅いのはある程度仕方がないのですが、それにしても遅い...。サーバ側でのViewのレンダリングにかなりの時間を費やしています。

# config/environments/development.rb

config.cache_classes = false

f:id:ec0156hx39:20180820005252p:plain

またdev toolの計測を見てみると、CSSやJavaScriptといったアセットを取得しに行く時間、つまり④、⑤も異常に遅いです、全リソースを読み込むのに15秒もかかってるorz。そもそも読み込もうとしているJavaScriptが多すぎますね。

原因は何か?

②が遅い原因

アプリケーションログを遡っていくと、あることに時間を費やしているのがわかりました、browserifyのビルドです。 JavaScriptに何かしらの変更を加えた時に、browserifyのビルドが走っていてそれが遅く感じる原因のようです。 f:id:ec0156hx39:20180820005453p:plain

どうやらbrowserifyはWebpackなどと違い、デフォルトで差分ビルドができず(watchifyというプラグインが必要)、JavaScriptを変更した後にエントリーポイントで読み込むファイルを全ビルドしていたためにかなり遅くなっていました。 読み込んでいるJavaScriptの種類にもよりますが、約4~6秒ほどかかっていました。

④と⑤が遅い原因

読み込もうとしているJavaScriptが多すぎて、ブラウザの同時接続数(Chromeは6)をオーバーしているためです。前述したdev toolのピンク色がブロック時間、つまり接続待ちを表しています。

そもそも何故こんなにも読み込むJavaScriptが多いのでしょうか?

どうやらdevelopment環境のデフォルトで、assets pipelineで本来一纏めにされるJavaScriptがデバック用にバラバラで読み込まれる設定がされているようです。

# config/environments/development.rb

config.assets.debug = true

これにより読み込むJavaScriptが多くなりすぎて、接続待ちに時間が多く費やされていました。

対策

browserifyのビルドが遅い問題

watchifyを導入して、差分ビルドをさせるようにするという案もありましたが、最終的にWebpackに置き換えるという形にしました。 これについては別に記事を書いているのでよろしければご覧ください。

tech.medpeer.co.jp

最終的にbrowserifyの全ビルドから、Webpackの差分ビルドに置き換えたことにより、 4~6秒 → 1.5秒 に短縮することができました。

f:id:ec0156hx39:20180820010534p:plain

読み込むJavaScriptが多すぎる問題

デバックモードもそれはそれでデバックしたい時には便利なので、単純にconfigの値をfalseにするだけだと切り替えが面倒です。

# config/environments/development.rb

config.assets.debug = false # デバッグしたい時にtrueに書き換えてappサーバを再起動する必要がある

しかしconfig.assets.debugがfalseの時、Railsのjavascript_include_tagヘルパーのdebugオプションをtrueにすることで、デバックモードに切り替えることができます。

railsguides.jp

このオプションを利用して、debugしたい時だけURLに特定のパラメータを付与するとdebugモードに切り替わるヘルパーを作成しました。

  def javascript_include_tag_with_debug_option(js, options = {})
    javascript_include_tag js, options.merge(debug: params[:js_debug].present?)
  end

このヘルパーを利用することで、通常時のリクエストはdebugモードはOffですが、以下のように特定のパラメータを付与するとOnにできるようになりました。 https://hostname.jp/articles?js_debug=on

終わりに

今回は開発環境の話でしたが、本番環境でもリソース取得時間の改善の余地がまだまだあります。 「サイトの読み込み時間を爆速にする」ことに興味がある方は是非一度メドピアに遊びに来てください。

また9月12日にメドピアでRailsの技術的負債をテーマにした「MedBeer」というイベントを開催します。

medpeer.connpass.com

負債の予防・返却に興味がある人、ビールが飲みたい人は是非参加してください。


(☝︎ ՞ਊ ՞)☝︎是非読者になってください


メドピアでは一緒に働く仲間を募集しています。 ご応募をお待ちしております!

■募集ポジションはこちら

https://medpeer.co.jp/recruit/entry/

■開発環境はこちら

https://medpeer.co.jp/recruit/workplace/development.html

「MedBeer -Rails開発での技術的負債との付き合い方-」を開催しました!

$
0
0

身長体重が変わっていないにも関わらず、5年前より体脂肪率が4.5%増加したエンジニアの村上(@pipopotamasu)です。

本日は9/12(水)に開催したMedBeerというイベントを報告したいと思います。

medpeer.connpass.com

f:id:ec0156hx39:20180921184837j:plainf:id:ec0156hx39:20180921165847j:plain @GINZA SIX 12F 株式会社リンクアンドモチベーション内 イベントスペース

MedBeerとは?

MedBeerとは、年1回くらいのペースでメドピアが主体となって行なっている技術イベントになります。 基本的にRailsに関することをテーマに発表を行い、今回で4回目の開催となりました。

第1回から着々と規模が大きくなっており、前回は約60名、今回は約120名程が参加されました。

また「Beer」という名をつけているので、10種類ほどのビールとそれにおつまみ(今回はからあげエンジニアが参加するということでしたので、日本で初めて外食メニューとして唐揚げを出した三笠会館の唐揚げを用意しました)を用意していました。

MedBeer -Rails開発での技術的負債との付き合い方-

サブタイトルにも書いてある通り、今回のテーマは「Rails開発における技術的負債」です。 このテーマでメドピアからは2名、またClassi株式会社CTOの佐々木さん、クックパッド株式会社の小室さんをお招きし、計4名で上記テーマに沿って発表を行いました。

Rails Good Parts, Bad Parts

まず初めのトップバッターはメドピアの技術顧問、前島真一(@willnet)さんです。

f:id:ec0156hx39:20180921183445j:plain

発表では「負債の増加を防ぐにはどうしたら良いか?」という観点から、Railsで負債の増加を防ぐ方策を提示していただきました。 重要な要素を一部抜粋すると以下のようになります。

  • Railsが用意する便利機能と、要注意な機能の扱い方を知ることで負債を貯めずに開発することができる
  • そのためにはレビューや社内教育などを通してRailsに習熟することが大事

また発表の中では、前島さんの主催するクリーンなRailsのコードを議論するコミュニティ「clean-rails.org」を紹介していただきました。 皆さんもRailsの設計や実装で悩んだ時など、こちらで相談したらいかがでしょうか?

発表資料は以下になります。

小さな成功体験を積み重ねてチームで負債に立ち向かう

2番目は、Classi株式会社でCTOである佐々木 達也(@sasata299)さんに、負債返却についてClassiで行なった事例について発表していただきました。

f:id:ec0156hx39:20180921184318j:plain

負債返却に必要なものは「負債返却のリソース」と「負債返却いけるいけるという気持ち」の2つが必要で、後者を中心に話していただきました。 既存コードの改修のためにモブプロ/ペアプロの導入や、整地部というコードをリファクタリングする活動を設けたりと、とても参考になる事例を含む発表でした(ちなみに整地部はメドピアで取り入れようとする動きがあります)。

発表資料は以下になります。

メドピアにおけるライブラリアップデート

3番目は私、メドピアの村上大和(@pipopotamasu)が発表させていただきました。

f:id:ec0156hx39:20180921183639j:plain

「メドピアにおけるライブラリアップデート」という題の通り、メドピアでどのようにライブラリのアップデートを行なっているかを発表しました。 メドピアではGemをアップデートするためのフローがあり、1ヶ月毎にそのフローに沿ってアップデートを行い、バージョンが最新と乖離しないように(=負債をためない)しています。具体的にどんなフローでアップデートを行なっているのかの説明と、そのフローをJavaScriptのライブラリに適用させようとしていることをメインで話しました。

発表資料は以下になります。

クックパッドの巨大 Rails アプリケーションの改善

最後に発表したのは、クックパッド株式会社の小室 直(@hogelog)さんです。クックパッドの巨大なRailsアプリケーションの改善について発表していただきました。

f:id:ec0156hx39:20180921184340j:plain

お台場プロジェクトというクックパッドの巨大レポジトリの改善プロジェクトのお話がメインでした。「巨大なRailsアプリケーションの改善は計測と可視化から」という言葉通り、CIにかかった時間・アプリのロード時間・コード量・依存Gem数など詳細にデータをとり、改善結果がそれらの数値にどう影響あったかを逐次確認できる という状態にして、プロジェクトメンバーのモチベーション向上やメンバー以外への成果の共有にも繋がるため、とても参考になりました。

発表資料は以下になります。

終わりに

冒頭にも書きましたが、今回のMedBeerは今までで最も規模の大きいのものになりました。 多くの皆さまに参加いただき、他社の事例も知ることができ、実りの多いイベントでした。 また、急遽会場を貸していただき、当日の準備も手伝っていただけたリンクアンドモチベーション社の皆さまも本当にありがとうございました。 様々な人の協力もあり、第4回MedBeerも良いイベントにすることができました。

今回参加された人もされなかった人も、次回のMedBeerでお待ちしてます!!!


(☝︎ ՞ਊ ՞)☝︎是非読者になってください


メドピアでは一緒に働く仲間を募集しています。 ご応募をお待ちしております!

■募集ポジションはこちら

https://medpeer.co.jp/recruit/entry/

■開発環境はこちら

https://medpeer.co.jp/recruit/workplace/development.html

メドピア に整地部ができたってよ!

$
0
0

はじめに

こんにちは、メドピアの駆け出しエンジニアの川﨑です。

最近我が広島カープが日本シリーズ進出を決めて機嫌が良いのでブログ書きたいと思います。

今回僕がお届けするのは、先月の9月14日 「Rails開発での技術的負債との付き合い方」 をテーマに開催された 「MedBeer」 にて、 Classi株式会社のCTOである佐々木 達也(@sasata299)さんの発表にて紹介された「整地部」がMedPeerでもできたよ!というお話です。

整地部とは?

整地部の定義に関しては、以下にある佐々木 達也さん の興奮する資料が分かりやすいです。

一言で言うと

技術的負債となっているコードを少しずつでも良くしたい人たちの集まり

です。

どんな風に進めたか?

記念すべき第一回は、弊社Railsの技術顧問である前島(@willnet)さんがいらっしゃる水曜日に開催されました。

進め方としては、僕が前々から直したいと思っていた箇所を自分なりに修正したPRを作っていたので、そのPRを元にモブプロで洗練させる形をとりました。

今回の整地対象は?

MedPeerでは製薬企業様から医師の方々へ向けた講演会をWeb上で行えるサービスを展開しており、講演会サービスのTOPページではオススメの講演会をスライドで表示しています。

そのスライドに表示する講演会の情報を抽出するロジックが今回の整地対象となりました。

対象となった理由は以下の通りです。

  • 講演会の情報を抽出するロジックが少し複雑
  • ロジックが書かれているモデルが300行を超え肥大している
  • ActiveRecord のメソッドを使えば良いところが散見される
  • メソッド/変数/定数の名前がわかりにくい

どんなことをしたのか?

前提として

・ロジックが書かれているモデルが300行を超え肥大している

という問題に辛さを感じていたので、以下前島さんの記事を参考にしつつ、対象の処理を単機能として一つのクラスへ切り出す方針で進みました。

tech.medpeer.co.jp

クラスに切り出した後は主に以下のことを行いました。

  • 1メソッドでやるべきでない処理を分けてわかりやすくする
  • メソッド/変数/定数の名前をいい感じにする
  • ActiveRecord のメソッドを使った方が良いところは使うようにする

実例

ここで実際に整地された例を一つご紹介します。

今回クラスへ切り出したコードの中で次のようなメソッドがありました。

deftoday_conferences(conferences)
    newly_start_at(conferences.find_all { |c| c.start_at.today? }, TODAY_RECOMMENDATION_COUNT)
  enddefnewly_start_at(conferences, limit) 
    conferences.sort_by(&:start_at)[0..limit] 
  end

このメソッドでやりたいことは次の通りです。

  1. 取得した講演会から当日開催のものだけ抽出
  2. 開催日の昇順ソート
  3. 先頭からlimit取得

やりたいことは実装できていますが辛いですね。

今回は ActiveRecord のメソッド where,order,limitを使って次のように修正を行いました。

deftoday_conferences
    conferences.where(start_at: Time.zone.today.all_day)
               .order(:start_at)
               .limit(TODAY_RECOMMENDATION_COUNT)
  end

Rubyの世界でのごちゃごちゃした処理がなくスッキリ書けているので、やりたいことが明確になり可読性が上がったかと思います。

実際やってみた感想

実際やってみて特にデメリットは感じないほど良い活動だったと思います。

メリットはたくさんありましたが、個人的に強く感じたことは次の2点です。

  • レベルが高いエンジニアの方と一緒にモブプロするのすごい勉強になる
  • プロジェクトへ貢献するチャンス(特に僕のような新人エンジニア)

一つ目はそのままの意味で、実装するときの考え方や気をつけることを学べる場にもなると思います。

二つ目についてですが、新人エンジニアは実力的にプロジェクトへ大きく貢献することが難しいと思います。

そういった状況でも、整地部の活動へ積極的に参加することで、技術的負債を作らない方法を学びながらプロジェクトに貢献できる点がメリットだと感じています。

今後はどんな感じで進めて行くのか?

実際に活動してみて割と大きな修正が必要なものが見えてきたこともあり、今後は以下のような方法で活動を進めて行く予定です。

・活動頻度は1回1.5h/隔週
・大きな修正は技術力の高いエンジニアでモブプロ
・新人エンジニアは上記モブプロを見て勉強したり、自分で修正できる箇所があれば整地する(必要であればペアプロ依頼)

また、工夫ではないですが、整地活動をする上でやっておいた方が良いこととして、以下2点を意識すると良いと思いました。

・新人エンジニア場合、余裕があれば事前に自分なりに修正したPRを作っておく
・小さいもので良いので、1つはPRを作るところまで対応する

今後の活動へ向けた気持ち

当然ですが大きな成果が出たりする活動ではないので、地道に続けていくことが重要だと思います。

佐々木 達也さんの発表でもありましたが、小さな修正を続けて成功体験を増やしていくことがとても重要だと感じておりますので、僕自身も積極的に活動へ参加したいと思っております。

おわりに

今回はMedPeerで始まった新たな取り組みについてのご紹介でした。

技術的負債は作らないのが一番ですが、長くシステムを運用していく中で浮き彫りになってくるものもあるかと思います。

技術的負債に目をつぶらず、積極的に立ち向かうチームが社内にできたことそのものがとても素晴らしいことだと感じています。

今後も負債に負けない柔軟で堅牢なシステムを維持するべく整地部は突き進みます!!!


(☝︎ ՞ਊ ՞)☝︎是非読者になってください


メドピアでは一緒に働く仲間を募集しています。 ご応募をお待ちしております!

■募集ポジションはこちら

https://medpeer.co.jp/recruit/entry/

■開発環境はこちら

https://medpeer.co.jp/recruit/workplace/development.html

RubyWorld Conference2018にRubyスポンサーとして参加しました

$
0
0

こんにちは。メドピアのRailsエンジニアの小林です。遅くなってしまいましたが、11/1, 11/2に島根県松江市で行われたRubyWorld Conferenceに行ってきたので、レポートをお届けいたします。

2018.rubyworld-conf.org

多くの興味深いセッションが行われましたが、内容はRubyWorld Conferenceの公式YouTubeチャンネル*1から動画で見れるため、本記事では基調講演の大枠のまとめと、ブース出展の様子をお伝え致します。

今回、メドピアは初のブース出展(Rubyスポンサー)でした!そもそもRuby関連のイベントへのブース出展することが初なので、準備に手間取ることを予想して前日入りしました。

f:id:marikokobayashi:20181115235854j:plainf:id:marikokobayashi:20181116000544j:plain
f:id:marikokobayashi:20181115235910j:plainf:id:marikokobayashi:20181116000516j:plainf:id:marikokobayashi:20181116002319j:plain

1日目は開会挨拶と来賓挨拶からスタート。まずは島根県知事代理・松江市市長代理がいらっしゃってのご挨拶。島根県・松江市がRubyを使ったIT企業の支援に力を入れていることが伝わってきます。

Matzの基調講演 The power of the community

  • Rubyのこれまで歩んできた道
  • ピッケル本(正式名: Programming Ruby: The Pragmatic Programmer's Guide、英語で初めて出版されたRuby本)の出版社のAddison-Wesleyから本を書きたいと連絡が来たときのお話
  • 最初のRubyConfのときのお話
  • Rubyのユーザー数の増加
  • Rubyコミュニティについて
  • Rubyのハイプサイクル
  • 「あきらめない」こと
  • 一歩踏み出すこと
  • Matzの引退とRuby4について(!)

などについて語っていただきました。

特にRubyコミュニティのお話が印象的でした。 もしRubyコミュニティが存在しなかったら、Rubyは今のような形では存在していなかっただろうとMatzはおっしゃいます。

Matzが発案したものではない機能が今ではRubyに欠かせないものとなっていますし、周囲の人がRubyのカンファレンスや勉強会の開催、Rubyの本の執筆等の活動をしたことでRubyは発展してきました。

RubyコミュニティはNiceなコミュニティです。

ですがRubyコミュニティの良いフィードバックをより長く強く維持していくために、Matzは「もう一歩だけ先に行ってほしい」と言っていました。

コンフォートゾーン、居心地が良い環境にいつづけるだけでは進歩がなくなってしまう。

新しいカンファレンスに出てみる、友だちを作ってみる、新しいことを試してみる、他の人を褒める、目を見て握手してみる、アイディアを出し合ってみる。そういったことをしてみて下さい、とのことでした。

「宿題を出します。カンファレンス全体で感じたことをなにかでシェアして下さい。ブログでもTwitterでもFacebookでもMediumでもQiitaでもいいのでシェアして下さい。皆さんにも一歩踏み出していただけたらいいなと思っています。それが未来を良くすると思います」とのことでした。

f:id:marikokobayashi:20181116000215j:plain

メドピアのスポンサーセッション(1日目のお昼)

メドピアのスポンサーセッションは1日目のお昼!CTOの福村が登壇しました。

f:id:marikokobayashi:20181116000247j:plainf:id:marikokobayashi:20181116000328j:plain

発表資料はこちらです。

メドピアの事業紹介、メドピアの働きやすい制度、それからエンジニア発信のしくみである「プルリク振り返り会」と、そこで過去に上がったTipsについて話してくれました。

今メドピアでは数多くのプロジェクトが走っており、PRが上がってきたら同プロジェクトのメンバーがPRをレビューするという体制になっています。

しかしそれだとプロジェクトを横断して全エンジニアが知っておいたほうが良い知識が共有されないので、そのようなPRを振り返って話すのが「プルリク振り返り会」です。この取り組みは他社のエンジニアさんとお話すると「良いね」と言ってもらえることが多いです。

気になるという方は、是非取り入れてみてください。また、取り入れた際は感想を共有してもらえると嬉しいです!

レセプション(懇親会)

1日目の最後にはレセプションがありました。夕食や美味しいお酒とともに、和気あいあいと、島根の学生や各地からの参加者と交流していた弊社メンバーたちでした。 しかし突然の出来事が…!

レセプションの中でTシャツのプレゼント企画を行っていたのですが、弊社参加メンバーの名前が突然壇上から呼ばれ、Tシャツが当たってしまいました!しかもMatzがそれにサインしてくださいました!

喜びと驚きで興奮状態の我々、写真を撮ったりしてはしゃいでいたうちに、近くにいらっしゃったRuby/Railsコミッターの松田さんにお声がけしたところ、松田さんもサインをくださり、そして松田さんがさらに他のRubyコミッターを呼び寄せ、その場にいらっしゃったRubyコミッターによるサインリレーが始まってしまい…

f:id:marikokobayashi:20181116001032j:plainf:id:marikokobayashi:20181116001056j:plain
(弊社メンバー大はしゃぎだったため失礼をしていたら大変申し訳ありません)

…最終的にこのようなTシャツになっていました。Rubyistからしたら完全にお宝ですね。

f:id:marikokobayashi:20181116001418j:plain

今回参加したメドピアメンバーにとって、思い出に残る1日となりました。

Chad Fowler氏基調講演 Don't Stop Moving

2日目のスタートは、RubyConfやRailsConfの共催者、The Passionate Programmer: Creating a Remarkable Career in Software Development(邦題: 情熱プログラマー ソフトウェア開発者の幸せな生き方)の著者として知られるChad Fowler氏による基調講演でした!

私は彼の著書「情熱プログラマー」は読んだことがあり、感銘を受けた人間の一人ですが、講演を聴くのは初めてなのでわくわくしながら聞きました。

今回は、モチベーションの保ち方や、プログラマーとして生き抜くための戦略について教えてくださいました。

深さと広さ

テクノロジーの仕事をするときは、色々なテクノロジーを広く少しづつ学ぶか、一つのことを深く極めるか、どちらかの戦略を取ると思います。まず、どちらのキャリアを選ぶか決めましょう、という話でした。Chad氏は色々なテクノロジーを広く学ぶ方を選びました。

その道を進んだChad氏は、いろいろな人が新しいテクノロジーを追いかけていることを目の当たりにしますが、「こんな新しいものがいっぱいあるのだ」と驚いてしまったときに、自分のキャリアの中での初心を思い返そうと考えたそうです。

テクノロジーに興味を持ったきっかけとなったビデオゲームである World War Craftのことを思い出してみたら、キャラクター育成の際に最初に「魔術師」「戦士」などを選び、スキルツリーからスキルカテゴリを選んで、どのように育成するかを選んでいました。

それと同じことで、テクノロジーの仕事をするときも、ひとつひとつの細かいことを気にせず、スキルカテゴリを見つけ出すことが大事だとのことです。

1996年頃にメンターの先輩から教わったことがあったそうです。当時は、ディレクトリサービス/UNIX/プログラミング言語という3つのカテゴリを軸に理解すれば、良いキャリアのスタートとなると言われたとのことでした(それぞれのカテゴリについて理解すればその中で具体的に何を選んで学ぶかは重要ではないとのこと)。

2018年の今、同じことを考えた場合、Chad氏が見出した3つのカテゴリは

  • オペレーティングシステム
  • データベース
  • プログラミング言語

とのことです。 さらに、それぞれのカテゴリの中でさらに、3つのカテゴリを見つけると良いとのことです。例えば言語であれば

  • オブジェクト指向言語(Ruby、Pythonなど)
  • Haskell/OCamlのような関数型言語
  • LISP

を挙げていました。これらは、「必要だから学べ」ということではなく、自分の知っている部分と別のカテゴリにおいては考え方の違いを知るのに頭を使うので、実際にはRubyだけで食べていくことができても、 違うカテゴリのものを学ぶことはテクノロジーを広く知るためには役に立つということをおっしゃっていました。

Technology is a fashion

  • ファッションは進化し続けています。全てに付いていくは大変です。テクノロジーもそれと同じ。全部追いかけ回すと全部やらなければいけない、全部最新の流行を理解しないといけないと思うとやる気が無くなってしまう。それよりも自分が安定して取り組めるものにきっちりついていったほうが良いというお話がありました。

自発的な動機づけと外的な動機づけ

  • 外からアメやムチで人を動かそうとしても、結果は出せず、自発的な動機があって初めて結果が出せます。ダニエル・ピンク氏の著書 Drive(邦題: モチベーション3.0)でも、モチベーションを持つには「自発的であること」「熟練すること」「目標があること」が大事と書かれています。

  • Chad氏も、最初にDave Thomas氏に「情熱プログラマー」を書いてくれと言われた時は、正直大変だなと思ったらしいです。本を書いて有名になりたい気持ちもあったが、それは外的動機づけなのであまり気持ちが乗らなかったとのこと。 しかし、この本を書くことでだれか1人の人間でも幸福にする事ができるかもしれないという自発的なモチベーションを持ったことで、書き上げることができたというお話でした。

「情熱プログラマー」を読んだことでソフトウェア開発者としての生き方を見つめ直したという人も多いのではないでしょうか。その著者自身の執筆のモチベーションのお話を伺ったことで、私ももっと自発的な動機づけをもって物事に取り組もうという気持ちが高まりました。

ブース

ブースでは、弊社のメインサービスである医師向けコミュニティサイト「メドピア」の紹介の他に、子会社Mediplatの遠隔診療サービス「first call」と、同じく子会社FitsPlusの管理栄養士による食事アドバイスアプリ「DietPlus」のご紹介をさせていただいておりました。

f:id:marikokobayashi:20181116002650j:plain

また、PeerWaterというお水とメドベアのステッカー(メドピアの熊キャラクターなので「メドベア」です)を無料で配布していたのですが、ステッカーの評判が良かったですね。ちなみにMatzにも1日目にブースにいらっしゃった時に渡したのですが、気に入っていただけたのか、2日目にもブースに来てくださって数枚持って帰ってくださっていたぐらいです。(メドベアグッズ出したらもしかして需要あるんでしょうか…)

観光

あまり観光の時間は多く取れなかったのですが、前日入りした日には少し松江の空気を味わうことができました。

f:id:marikokobayashi:20181116000111j:plainf:id:marikokobayashi:20181116000015j:plainf:id:marikokobayashi:20181116000032j:plainf:id:marikokobayashi:20181116000052j:plain

初日夕食には美味しいご飯をいただきました。お邪魔したのは「おでん庄助」さん。地元の人にも知られたおでん屋さんです。我々が向かったときはもうすでにメニューの売り切れが多くて「メニューは選べないけど盛り合わせなら出せますよ」とのことでした。

f:id:marikokobayashi:20181116132531j:plainf:id:marikokobayashi:20181116132617j:plain

いただいた盛り合わせはどれも美味しかったです。東京だったらもっと高いであろう夕ご飯が地方価格でしかも美味しい…ワインの種類も豊富に用意されており弊社のワイン大好き社員が感激のあまりワイン講義を初め出すほどでした。

次回以降のRWCなどで松江市観光を検討されている方へ向けたアドバイスを一つ書いておきます。松江市周辺で外食できるお店は全体的に東京より閉まるのが早かったりメニューがなくなるのが早いので、東京の時間感覚で行くとご飯難民になる可能性があります。

この日私達は8時ぐらいからご飯場所を探しましたが、他のお店で「もうご飯がない」と断わられていました。庄助さんで美味しいおでんにありつけたのは運が良かったです。

2日目のアフターパーティには出ずに帰りましたが、初の島根・松江だったメンバーが多かったので、もっとゆっくりすればよかったかもしれないですね。しかしそれはまたいつか機会があるときにリベンジしたいですね。

まとめ

Ruby関連のイベントにスポンサーブースを出すのは初めてで慣れない箇所もありましたが、メドピアを知っていただく機会が増え、多くの方との交流ができたことが嬉しかったです。

個人的な話となりますが、私は地方出身の人間なので、地方のIT産業の活性化には興味があります。

松江の学生は、学生のうちからこのような世界レベルのRubyプログラマーのセッションを自分の地元で聞ける機会に恵まれていて素晴らしいなと思いましたし、島根県・松江市にはこういった取り組みをぜひこれからも続けてほしいと思いました。

弊社としても、今後もこのようなスポンサーの機会がまたあれば良いなと思っております。

f:id:marikokobayashi:20181116002540j:plain

以上、RubyWorld Conference2018のレポートをお届けいたしました!


(☝︎ ՞ਊ ՞)☝︎是非読者になってください


メドピアでは一緒に働く仲間を募集しています。 ご応募をお待ちしております!

■募集ポジションはこちら

https://medpeer.co.jp/recruit/entry/

■開発環境はこちら

https://medpeer.co.jp/recruit/workplace/development.html


新人エンジニアが開発合宿に参加してみて@湯河原

$
0
0

こんにちは!メドピア10月入社の新米エンジニアの宮原です。 今回のブログでは、メドピアで恒例となっている開発合宿に参加してきましたので、こちらの様子をお届けしたいと思います。

メドピアでは、これまでにも年2〜3回ぐらいのペースで開発合宿を開催しています。湯河原で開発合宿を行うのは、昨年3月以来になります。

tech.medpeer.co.jptech.medpeer.co.jp

合宿テーマ

開発合宿のテーマは毎回異なり、技術研鑽や技術的負債の解消、他にも新規サービス立ち上げ等を行ったこともあります。過去の開発合宿では新規サービス立ち上げをテーマに実施され、弊社のサービスとして事業化されているものもあります。

合宿に行く前に各自、取り組む内容について宣言してから開発します。今回はこんな感じです。

  • 業務改善ツールの開発
  • メドピア既存サービスへの新機能開発
  • 新技術を使ったサービス開発

取り組み方は自由でチームを組んでも良し。個人でモクモクと取り組むも良し。特に制約は無いので、自身が取り組みたかった開発がしやすいです。 後日、オフィスにて合宿に参加できなかったエンジニアやデザイナーさんたちへ、成果報告LTをすることになりました。

開発合宿向きの旅館「湯河原 おんやど 恵」さん

今回の合宿は湯河原の「おんやど 恵(めぐみ)」さんにお邪魔しました。メドピアでは過去の合宿でも利用させていただいております。こちらの旅館は、SE出身の社長さんが経営されている旅館で、エンジニアに嬉しい「開発合宿プラン」などが用意されています。お隣にコンビニができたこともあり、お酒やお菓子などの補給線が確保されより開発向きになりました。

f:id:nyagato_00_miya:20181212153651p:plain

おんやど恵み 開発合宿プラン

当日は、湯河原駅に直接集合!湯河原駅近くの「トルティーノ」さんでランチをとってから一同、おんやど恵へ向かいます。

f:id:nyagato_00_miya:20181212153942j:plain
総勢15名の参加となりました!

開発の様子

到着後すぐに会議室へ向かいます。各々合宿で取り組むテーマを発表し、いざ開発スタートです。

会議室には、電源タップ、USB充電器、おやつ、ホワイトボード、腰サポートグッズ等、設備も充実。開発スタートから、もくもく作業をする人もいれば、リフレッシュも兼ねて一足先に温泉に浸かりに行く人も。 ※ディスプレイのレンタルプランもあるそうです。

f:id:nyagato_00_miya:20181212154443j:plain
今回のテーマをそれぞれ発表し、いざ開発開始です
f:id:nyagato_00_miya:20181212154251j:plain

基本的に、食事・お風呂・就寝以外の時間は開発タイムです。初日から夜遅くまで開発に没頭するメンバーもいました。

f:id:nyagato_00_miya:20181212154705j:plain
斯く言う私も、モクモクしていました

湯河原ご飯レポート

※美味しそうなご飯の写真が続きます。空腹時の閲覧はご注意ください

f:id:nyagato_00_miya:20181212155007p:plainf:id:nyagato_00_miya:20181212155013p:plainf:id:nyagato_00_miya:20181212155017p:plain

f:id:nyagato_00_miya:20181212155021p:plain
十二庵さんの豆腐プリンは絶品でした

最終日にランチに行った「うおたつ」さんには足湯もありました。 湯河原では、"足湯駆動開発"が大変捗ります。

最終日

2泊3日の楽しい合宿を終え、成果発表を行います。
技術研鑽に務める人もいれば、がっつりと作り込みデモまでする人まで各々の成果が発表されました。今回の合宿では、最も優れた発表には商品が進呈される!とのことで、みんな気合の入った発表が多かった印象です。

f:id:nyagato_00_miya:20181212154900p:plain
合宿の成果が発表されました

画像認識に取り組んでみました

私は、“食品判別AI”を作ってみました。メドピアにはグループ会社からDiet Plusというアプリがリリースされています。ユーザーから送られてきた食べ物の写真を、管理栄養士さんが確認して健康サポートをしてくれます。
そこで、食品の判別は自動化できそうだなと考えていたこともあり、合宿でやってみることにしました。

学習データの加工や、ニューラルネットワークの構築に時間を要しましたがなんとか形にすることができました。 今回は、GCPにVMのインスタンスを立てて、この中でSystemd + Nginx + uWSGI + Flaskのアプリケーションを動かしています。

f:id:nyagato_00_miya:20181212173511p:plain
お寿司が正しく判別された様子。

TensorFlowのサンプルの中に、再学習するサンプルが用意されています。簡単に画像認識を試す場合は、こちらを活用してみてください。
便利なことにTensorFlowでは、dockerイメージが公開されています。こちらを活用することで、めんどうな環境構築を一瞬で終わらせることができます。
(公式から公開されていると安心感がありますね) https://www.tensorflow.org/install/docker

例えば、猫の分類など行ってみましょう。ロシアンブルー、ペルシャ、メインクーン、ベンガルなどの画像を10枚程度集め、それぞれディレクトリに入れます。 こちらのディレクトリを指定して再学習を実行します。

$ cd cats
$ python3 retrain.py --image_dir ~/cats

学習結果を確認するには、label_image.pyを使います。以下のファイルをGithubからダウンロードし、retrain.pyと同じディレクトリに置きます。 https://github.com/tensorflow/tensorflow/raw/master/tensorflow/examples/label_image/label_image.py

以下の様に、再学習で生成された学習データを活用して判定させます。

$ python3 label_image.py \
--graph=~/tmp/output_graph.pb \
--labels=~/tmp/output_labels.txt \
--input_layer=Placeholder \
--output_layer=final_result \
--image=/cats/test/test_cat.jpg

するとどうでしょう、入力した猫の画像から、猫の種類を判別してくれます。学習データが10数枚であれば、ローカル環境で十分学習させることが可能です。
例で述べさせていただいた内容は、公式にチュートリアルとして掲載されています。ぜひ、トライしてみてください。
https://www.tensorflow.org/hub/tutorials/image_retraining

まとめ

開発合宿では、普段の業務でなかなか取り組めないタスクや負債を潰したり、新しい技術に集中して取り組める絶好の機会だと思います。 また、2泊3日という短い時間で成果を出す必要があるので、自身がこの短期間にどこまで出来るかを確認できる良い機会でもあると思います。 以上!2泊3日のメドピア開発合宿@湯河原 おんやど 恵のレポートでした。

追伸

合宿後の発表会で、僭越ながら私が、kenzoさんと同着で表彰されました。 まさかの同着1位ということもあり、2人で商品同等額を山分けさせていただきました。
頂いた賞金は、HHKB 無刻印を買う軍資金にさせていただこうと思っています。


(☝︎ ՞ਊ ՞)☝︎是非読者になってください


メドピアでは一緒に働く仲間を募集しています。 ご応募をお待ちしております!

■募集ポジションはこちら

https://medpeer.co.jp/recruit/entry/

■開発環境はこちら

https://medpeer.co.jp/recruit/workplace/development.html

エンジニアのスキル底上げを目的として『テックサポート制度』を始めました!

$
0
0

こんにちは!メドピアエンジニアの難波です。

今回はメドピアで先月(2018年12月)から始まったテックサポート制度について紹介させて頂こうと思います。

『テックサポート制度』とは?

『テックサポート制度』とはエンジニアの開発力を底上げしてメドピアの事業開発を加速させることが目的の、1人あたり年間12万円まで開発効率・スキルの向上に関わるサポートを行う制度です。

最近色々な企業で同様の「AWSやGCPを積極的に触ってもらうために年間(月間)○○円まで個人で自由に使える制度を作りました!」という声を聞くようになりましたが、弊社ではAWSやGCPのようないわゆるIaaS以外にもElastic CloudやHerokuなどもOKです。他にもRubyMineやDashのような開発効率促進系ツール、珍しいものだとGo RailsやUdemyのようなオンライン学習サービスについてもサポート対象です。

サポート対象は基本的に社内ドキュメントに記載されているリストにあるものということになっておりますが、現場のエンジニアからCTOやエンジニアマネージャーに要望を挙げて、これは良さそうだねと判断されたものはすぐにリストに追加されるようになっています(まだ始まったばかりの制度なのでどんどんアップデートしています)。

ちなみに一部を抜粋すると現状このようなものが載っています。

実際に使ってみた感想

私の場合は早速以下の2つのツールにテックサポート制度を使いました。

  • Tower
    • 評判の良いGUIのgitクライアント。普段はCUIでgitコマンドを実行するのですが、デザイナの人に勧めることもあるので使い勝手を試してみたかったため
  • Microsoft Azure
    • AWSやGCPはよく使うのですが、Azureはあまり触っていなかったのでこの機会に使ってみることにしました

テックサポート制度が始まってからの印象ですが、普段良さそうなツールを見つけても有料だと「必要になったら改めて……」となりがちなのが「とりあえず試してみるか、サポートされるし。」に変わるのは心理的な障壁をなくす効果という点でとても良いなと感じています。

また自分自身での活用についてだけでなく、同僚(特にまだ経験の浅い人)に対して有料ツールを勧めやすいという効果もありました。経験の浅い人にこそ有料でも便利なツールを積極的に使ってもらうことで成長のきっかけとしていって欲しいのですが、今までは「オススメなんだけどちょっと高いんだよなあ」というように躊躇することがありました。それが「とりあえずテックサポートで試してみてよ」と言えるようになったのは良いですね。

メドピアのエンジニア支援制度

今回のテックサポート制度導入によってメドピアで行っているエンジニア支援の制度やイベントは以下のようになりました。

  • 書籍支援制度
  • イベント(カンファレンス等)参加支援制度
  • 開発合宿
  • ランチLT
  • 輪読会
  • 週次KPT
  • プルリク振り返り会
  • テックサポート制度 <- NEW!!

今後もメドピアではエンジニアがよりパフォーマンスを発揮できる環境づくりに注力していきますので、興味が有る方のご連絡をお待ちしております!


(☝︎ ՞ਊ ՞)☝︎是非読者になってください


メドピアでは一緒に働く仲間を募集しています。 ご応募をお待ちしております!

■募集ポジションはこちら

https://medpeer.co.jp/recruit/entry/

■開発環境はこちら

https://medpeer.co.jp/recruit/workplace/development.html

Railsフロントエンドボイラープレート「medpacker」の紹介

$
0
0

メドピアマッスル部のフロントエンドエンジニア村上(@pipopotamasu)です。

最近筋トレに時間を割かれ、家でコードが書く時間が減ってしまったのが悩みの種です。

今日は最近作ったメドピア用Railsフロントエンドのボイラープレート、「medpacker」を紹介しようと思います。

github.com

medpacker作成の背景

Railsには公式で大きく分けて2つのフロントエンドの環境があります、アセットパイプラインとwebpackerです。 これらはメドピアの既存プロダクトでも使ってきたのですが、プロダクトの規模が大きくなったり、時間が経つにつれて色々な辛みが見えるようになってきました。

アセットパイプライン

  • 最終的にJSやCSSのファイルを1つのファイルに出力するため、プロジェクトが大きくなってくるとファイルサイズが大きくなってしまう(キャッシュが効いてないと読み込みが遅すぎる、開発時のビルドに時間がかかる)
  • ES6+を使いたい場合はwebpack等のBundlerを自前で設定する必要がある(またはbundlerをwrapしているgemを使う必要がある)

webpacker

  • アセットパイプラインの欠点を解消(複数ファイル出力、webpackが設定済み)したのはよいが、webpackの設定がrailsにラップされすぎていてわかりづらい
  • 初期設定だとプロダクションビルドにsource mapがついていたりなど?な設定がある
  • webpack本体のバージョンアップについていけてない(webpackがv4なのに対して、webpackerはwebpack v3依存)

これらの辛みのため新規でRailsプロジェクトを作成する時、今まで通りアセットパイプラインやwebpackerで構築してしまっていいのかという疑問が生じてきました。 またフロントエンド開発をメインで行うのがサーバーサイドエンジニアや外部のフロントエンドエンジニアさんということもあり、プロジェクト間で採用技術や質を標準化するために、またフロントエンドの環境構築の時間短縮のためRailsフロントエンド用のボイラープレートを用意しそれを横展開するのがよいのではないか?

ということで、これらの辛みを克服するためにメドピア用Railsフロントエンド用のボイラープレート「medpacker」を作成しました。

medpackerのコンセプト

medpackerのコンセプトは大きく2つです。

素早く、簡単に導入できる

webpackerのような導入の簡単さを目指し、Railsのアプリケーションテンプレートを使用して設定できるようにしています。これによりrails newをしてから2~3分でフロントエンド環境を構築することができます。

だいたい開発や本番環境で必要なものが初期から設定されている

アプリケーションテンプレートで導入した直後から、webpack・babel・lint・postcss・minify等がすでにセットアップされているため、すぐに開発に着手できるようになっています。

Get started! 実際にmedpackerを導入してみる

それでは実際にmedpackerを導入してみましょう。

STEP1: medpackerをローカルにcloneする

$ git clone https://github.com/medpeer-inc/medpacker.git

STEP2: rails newした直後のrailsプロジェクトを用意する

$ rails new test-project

STEP3: アプリケーションテンプレートを用いて、先ほど作成したプロジェクトにmedpackerを適用する

$ cd test-project
$ bin/rails app:template LOCATION=path/to/medpacker/template.rb

この際いくつかCLIに質問されるので、用途に応じて答えてください。

remove 'app/assets'? y
remove 'test/'? y
need example page? y
Overwrite /Users/yamatomurakami/work/test-project/config/routes.rb? (enter "h" for help) [Ynaqdhm] a

STEP:4 railsを起動する

$ bin/rails s

サーバーを起動し、以下の画面が出たら導入完了です

( ※need example page?にyesで答えないとこのページは表示されません)。

f:id:ec0156hx39:20190122002939p:plain
medpacker_home

medpackerの中身について

作成に至った背景、コンセプトなどはすでに説明した通りです。ここではmedpackerの中身についてみていきます。

webpack

medpackerの核ともいうべきbundlerです。 もちろんフロントエンドのアセットをビルドするために入れてます。 Rails側でビルドしたアセットをHTMLに読み込むためのヘルパーを用意してあり、それを使うことで読み込むことができます。 productionモードでビルドするとCSS, JSがminifyされるようになっています。

webpack-dev-server

webpackでの開発をサポートするツールです。ざっくり以下のような機能があります。

  • JSを変更した時差分のビルドをしてくれる(webpackのwatchと同じ)

  • リロードせずに更新したファイルがブラウザに適用される(Hot module replacement, HMR)

  • 上記のHMRができない場合は自動的にブラウザをリロードし、更新分のアセットを取得する

CSS(SCSS), PostCSS

導入時点で、SCSSとPostCSSの設定がされています。

(※メドピアでは全てのRailsプロジェクトでSCSSを使用しているので、決め打ちで入れてあります)

またPostCSSで初期導入されているプラグインは以下2つです。

  • autoprefixer・・・自動的にベンダープレフィックスをつけてくれるライブラリ
  • postcss-flexbugs-fixes・・・ブラウザが潜在的に持っているflexboxのバグを踏まないように、自動整形してくれるライブラリ

babel

JSを色々なブラウザで読み込めるように(例えば最新の記法が古いブラウザでも読み込めるように)変形/代替してくれるライブラリになります。 導入時点で設定済みなので、IE11とか気にせずにJSを書いても問題ありません。

基本的にメドピアの推奨環境に合わせてありますが、この辺をいじれば対応ブラウザを変えることができます。

lint系

SCSSのlint(stylelint)とJSのlint(eslint)の2つが設定済みです。導入側のプロジェクトのCIに組み込んでおいてください。

Vue.js

medpackerに入れるか迷ったのですが、導入時にVue.jsが初期設定してあります。 個人的にJSライブラリを入れるのは本当に必要になった時に初めて入れればいいと思っているのですが、プロジェクト間での採用技術を統一するために入れてあります(以前とあるプロダクトで外部のエンジニアさんにお手伝いしていただいてた時、社内のほとんどのプロジェクトでVue.jsを使っているにも関わらず、しれっとReactが入っていたことがあったので...)。

工夫したところ

ライブラリアップデート

月1回のペースで、自動的にgemとnpmのアップデートPRがCIで作られるようになっています。 作成後は私がPR内容の確認・マージをしています(この辺は手動です)。 「medpackerを適用しようとしたがバージョンが古い...」というメンテされずに放置されるという事態は避けるようにしています。

アプリケーションテンプレートの使用

より楽にプロジェクトにmedpackerを適用するためにRailsのアプリケーションテンプレートを使用しました。 最初はこの便利機能の存在を知らずに、こちらの方法をメインの適用方法にと考えていました。今考えるととてつもなく使いづらいですね。。。

終わりに

現在メドピアでは3つのプロダクトでmedpackerを使用しています。 使っているうちに出てくる改善点や追加機能などは日々更新していく予定です。

もしこのブログを見た人で「うちではこんなの使ってるよー」とか知見がある人はこっそり教えていただけたらありがたいです。medpackerに取り入れるかもしれません。

またこのmedpackerを作る上でpixivさん、スタディストさんのブログを参考にさせていただきました。本当にありがとうございました。

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

フロントエンド原理主義者が目論んだ脱webpacker – スタディスト開発ブログ – Medium


(☝︎ ՞ਊ ՞)☝︎是非読者になってください


メドピアでは一緒に働く仲間を募集しています。 ご応募をお待ちしております!

■募集ポジションはこちら

https://medpeer.co.jp/recruit/entry/

■開発環境はこちら

https://medpeer.co.jp/recruit/workplace/development.html

MedPeerをrails 5.2へアップデートしてました!🎊

$
0
0

こんにちは、エンジニアの森田です🌴

rails 5.2.0が発表されたのが2018年4月10日🚃

大分遅くなってしまいましたが、2018年10月18日にメドピアが運営する医師専用コミュニティサイト「MedPeer」を、rails 5.1.6からrails 5.2.0にアップグレードしていました🎊

f:id:madogiwa0124:20190105151411p:plain

今回は、アップグレードを行った手順や躓いた部分を今更感もありますが紹介いたします🙌

アップグレードまでの道のり

まずはバージョンアップまでに行ったことの概要を下記にまとめました。

  1. リリースノートとアップグレードガイドを読んで影響を確認
  2. Gemfileのrailsのversionを5.2.0に変更してbundle update
  3. rails app:updateで設定周りを更新
  4. テストを実行し、失敗したor警告が発生した箇所を修正
  5. 社内の検証環境にデプロイして、動作確認
  6. 本番デプロイ
  7. 振り返り会

それぞれについて、実際にやったことを紹介いたします🙇

リリースノートとアップグレードガイドを読んで影響を確認

まずはRails5.2の変更点を知るためにRailsGuideのアップグレードガイドとリリースノートを読みました👀 (私は英語があまり得意ではないので、日本語版もあって非常に助かりました・・・!)

アップグレードガイド

リリースノート

Gemfileのrailsのversionを5.2.0に変更してbundle update

月に1回bundle updateを行っていて、比較的依存Gemのバージョンが上がっている、またマイナーバージョンのアップデートということで影響も少なそうだったので、bundle updateで依存Gemを含めて、アップグレードを行いました。

※月1回のbundle updateの施策については、こちらから👀

speakerdeck.com

rails app:updateで設定周りを更新

次にrails app:updateをとりあえず全てyesで実施して、危なそうな部分を戻す対応を行いました。影響が大きそうだった差分だけ下記に記載しておきます👀

  • bin/railsbin/rakeでspringをロードしないような変更が入っていたので反映しませんでした。
  • active_storagetest_unit関連の設定ファイル追加及び既存ファイルへの更新は、使用していないので反映しませんでした。
  • config/application.rbconfig.load_defaults5.2に変更しました。
  • bootsnapは開発環境のみで動くようにし、本番での使用は一旦様子見しました。

設定ファイル関係はわからないことも多かったのですが、技術顧問の前島さんにレビューしてもらえるので安心・・・🙏

f:id:madogiwa0124:20190105151558p:plain

テストを実行し、失敗したor警告が発生した箇所を修正

とりあえず全体でテストを走らせて落ちた箇所と警告が発生していた部分を修正しました👷
主な修正は下記のようなところでした。

Arelのexistsを使っていた箇所を修正

existsを使っている箇所でDEPRECATION WARNING: Delegating exists to arel is deprecated and will be removed in Rails 6.0.の警告が発生していたため、下記のようにEXISTSではなくNOT INを使って行うように修正しました。

# before
where(Expert.where('users.id = experts.user_id').exists.not)
# after
where.not(id: Expert.select(:user_id)) }

Dangerous query method対応

rails 5.2から下記のようにsqlをハードコーディングするとDangerous query methodの警告が発生するようになりました。

Post.order('RAND()')

MedPeerでは固定の文字列を渡している箇所でのみ発生していたので、Arel.sqlで対応しました。

Post.order(Arel.sql('RAND()'))

テストの無い管理画面に立ち向かう

今回railsのバージョンを上げると同時に管理画面で使っているadministrateというgemのバージョンも大きくあがりました。メドピアでは管理画面ではクリティカルな部分以外でテストコードを書いていません😥
全てを手作業で確認するのは大変なので、今回はRails.application.routesを使ってpathを生成し、各画面遷移時にステータスコード200が返却されることを検証することで、手動テスト前の最低限の品質保証、バージョンアップによるエラーの検知が出来るようにしました🙇

require'rails_helper'moduleAdminRoutingTestHelperclass<< self# Admin配下のpath名と、それに合致するcontroller名とaction名を取得defadmin_routes(actions)
      routes = routes(actions)
      routes.select { |route| route[:name].include?('admin') }
    endprivatedefroutes(actions)
      routes = Rails.application.routes.routes
      routes = routes.map { |route| prepare_route(route) if route.name }.compact
      routes.select { |route| actions.include?(route[:action]) }
    enddefprepare_route(route)
      { name: route.name, action: route.requirements[:action], controller: route.defaults[:controller] }
    endendendRSpec.describe '画面に正常に遷移できるか確認'doIGNORE_PATH = [
    # 自動テスト対象外のpathを記載 
  ].freeze

  # 前提データが必要な場合は自動テストを行うのが難しいので、一覧と新規に限定
  admin_routes = AdminRoutingTestHelper.admin_routes(['index', 'new'])
  # 対象外のpathを除外
  target_routes = admin_routes.select { |route| IGNORE_PATH.exclude?(route[:name]) }
  url_helper = Rails.application.routes.url_helpers

  # 対象のpathの件数分、遷移しステータスコード200が返ってくることを検証
  target_routes.each do |route|
    describe route[:name] do
      before do
        visit url_helper.url_for(host: Capybara.app_host, controller: route[:controller], action: route[:action])
      end
      it "#{route[:controller]} # #{route[:action]}"do
        expect(page.status_code).to eq 200endendend

社内の検証環境にデプロイして、動作確認

テストのある部分はテストを全て通し、ない部分については先述の仕組みで確認したあと、ディレクターさんにも協力して頂き、業務上クリティカルな箇所について手で検証しました。

今回はマイナーバージョンアップということで、普段使っている本番相当の検証環境にバージョンアップを反映して検証を行いました。

本番デプロイ

通常業務に影響がなさそうという検証が出来たので、2018年10月18日にバージョン対応をmasterブランチにマージし、本番環境にデプロイしました🎊

VersionUp振り返り会

最後にバージョンアップの際に行ったことを整理して、KPTで振り返りを行いました。以下が出てきた内容の抜粋です🙇

Keep

  • 管理画面のテストを最低限ではあるが、自動的に行うことが出来た。

Problem

  • Gemfileで過去に色々な経緯からバージョンが固定されているGemがあり、月1のbundle updateで1年半程更新していないものもあった。railsのバージョンアップによる依存関係の解消時に大きくバージョン上げる必要があったため、検証負荷が高くなってしまった。
  • バージョンアップ用の環境を用意しておらず通常開発と同じ環境でバージョンアップの検証も行っていたため、通常開発業務の検証の際にバージョンアップを切り戻すといった作業が必要でメンテナンス負荷が高く、現状の検証環境のrailsのバージョンを把握しづらかった。

Try

  • Gemfileを読み解く会を開催し、経緯を理解した上で外せるものはバージョン固定を外す。
  • GemfileのVersion固定は、bundle updateのタイミングで見直す等の仕組み化を行う。
  • VersionUpのときは専用の環境を用意する。

Gemfileに記載されているgemがどのように使われているのか、なぜバージョンが固定化されているのかなどを読み解く会をやるといった今後のアクションに繋げることが出てきて、バージョンアップをするだけでなく、その過程で感じた問題等も話せたので振り返りいいのでは?という気持ちになりました🙌

おわりに

大分乗り遅れた感はありますが、今回はMedPeerをrails 5.2にバージョンアップした話でした。 現在6.0に関しても色々と情報が公開されてますが、次回の6.0に関しては乗り遅れずにバージョンアップ出来るように今回の反省を生かしていければと思います!

GitHub メンションを Slack DM する機能を Serverless Framework で作った話

$
0
0

メドピアマッスル部見習い kenzo0107です。

今回は GitHub のコミュニケーションを円滑にすべく導入した GitHub 通知の Slack DM 機能になります。

導入経緯

GitHub.com でイシューコメントやプルリクエスト等でコメントをした、してもらった、時に気づかず放置されてしまうことが度々ありました。

Slack 上で
「あれ、どうなりました?」と聞くと、
「コメントくれてたんですね、すいません、気づきませんでした」と。

この防止策の一環として導入したのが、 GitHub メンションを Slack DM に通知する機能になります。

Slack には既に GitHub アプリあるけど?

Slack には既に GitHub アプリがありますが、個人宛てに DM 通知するには個々が認証する等、一手間あります。

その為、認証等せずとも、GitHub.com 通知を個人宛のメンションとして Slack DM する仕組みが必要と考えました。

新入社員さんに一手間かけてもらうことなく、スムーズに GitHub + Slack のコミュニケーションを体験してもらえるようにしました。

システム構成

f:id:kenzo0107:20190218155842p:plain

  1. mogezo 君が GitHub イシュー or プルリク コメントで hogeko さんへメンション
  2. GitHub Webhook を AWS API Gateway で受け、
  3. Lambda を実行し、
  4. hogeko さんへ Slack DM 通知する

図の点線で囲んだ AWS 内の処理を Serverless Framework で構築しました。

導入手順

以下導入手順の大まかな流れです。

  1. GitHub Webhook 設定
  2. Slack Incoming Webhook URL 発行
  3. serverless framework で API Gateway + Lambda + CloudWatch Logs 構築

GitHub Webhook 設定

Organization の Webhook を作成します。*1

設定項目内容
Playload URL 仮設定で良いです (ex. https://hoge )。
後ほど設定します。
Content type application/json
Secret 認証の際に必要なパスワード情報です。
適当なランダム値を設定してください。

対象イベントは以下としました。

  • Commit comments
  • Issue comments
  • Pull requests
  • Pull request reviews
  • Pull request review comments

f:id:kenzo0107:20190215182516p:plain

Slack Incoming Webhook 作成

以下リンクから Slack Incoming Webhook URL 作成します。

https://slack.com/services/new/incoming-webhook

Serverless framework で API Gateway + Lambda 構築

事前準備です。

serverless framework プロジェクトを clone します。

git clone https://github.com/medpeer-inc/githubcom2slack
cd githubcom2slack

Nodejs は本プロジェクトでは AWS Lambda 最新の 8.10.0 としています。*2

node -v

v8.10.0

Serverless Framework インストールします。

npm install -g serverless

関連ライブラリをインストールします。

npm install
bundle install -j4 --path vendor/bundle

AWS Access Key ID, AWS Secret Access Keyを環境変数設定します。

現状弊社では direnv を利用し設定しています。*3

// direnv インストール
brew install direnv

// 環境変数設定
echo -n 'export AWS_ACCESS_KEY_ID=HOGEFUGA' >> .envrc
echo -n 'export AWS_SECRET_ACCESS_KEY=BUDOUBAR' >> .envrc
direnv allow .

AWS KMS Key 作成

aws kms create-key

{
    "KeyMetadata": {
        "AWSAccountId": "xxxxxxxxxxx",
        "KeyId": "yyyyyyyyyyyyyyyyyyyyyyyy",
        "Arn": "arn:aws:kms:ap-northeast-1:xxxxxxxxxxx:key/yyyyyyyyyyyyyyyyyyyyyyyy",
        "CreationDate": 1549615619.872,
        "Enabled": true,
        "Description": "",
        "KeyUsage": "ENCRYPT_DECRYPT",
        "KeyState": "Enabled",
        "Origin": "AWS_KMS",
        "KeyManager": "CUSTOMER"
    }
}

キーのエイリアス作成

aws kms create-alias \
    --alias-name alias/githubcom2slack \
    --target-key-id yyyyyyyyyyyyyyyyyyyyyyyy

秘密情報の暗号化

今回、秘密情報として扱うのは、以下2 点です。

  • GitHub Webhook Secret
  • Slack Incoming Webhook URL

AWS Lambda コンソール上で暗号化ボタンをポチッと押すのでなく、暗号化した文字列を環境変数として設定します。

GitHub Webhook Secret 暗号化

aws kms encrypt \
  --key-id arn:aws:kms:ap-northeast-1:xxxxxxxxxxx:key/yyyyyyyyyyyyyyyyyyyyyyyy \
  --plaintext "<GITHUB_WEBHOOK_SECRET>" \
  --query 'CiphertextBlob' \
  --output text

Slack Incoming Webhook URL

aws kms encrypt \
  --key-id arn:aws:kms:ap-northeast-1:xxxxxxxxxxx:key/yyyyyyyyyyyyyyyyyyyyyyyy \
  --plaintext "<SLACK_INCONMING_WEBHOOK_URL>" \
  --query 'CiphertextBlob' \
  --output text

<SLACK_INCONMING_WEBHOOK_URL>https://の後の ドメインからの文字列を指定してください。*4

上記 2 つ生成された値を secrets.yml に設定します。

  • secrets.yml は以下の様になります。
GITHUB_WEBHOOK_SECRET_ENCRYPTED: *****************************
SLACK_INCONMING_WEBHOOK_URL_ENCRYPTED: *********************************
AWS_KMS_KEY_ARN: arn:aws:kms:ap-northeast-1:xxxxxxxxxxx:key/yyyyyyyyyyyyyyyyyyyyyyyy

秘密情報の暗号化

yaml_vault で secrets.yml ファイルを暗号化した secrets.yml.enc をリポジトリで管理します。*5

env \
  AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID} \
  AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY} \
bundle exec yaml_vault encrypt \
  --cryptor=aws-kms \
  --aws-region=ap-northeast-1 \
  --aws-kms-key-id=alias/githubcom2slack \
  secrets.yml -o secrets.yml.enc

KMS key を利用した暗号化・復号権限を特定 IAM Group に絞ることで、そのグループに属するユーザのみ暗号化・復号が可能になります。

GitHub.com ユーザと Slack ユーザを紐付け

以下 2 つを紐づける為に git2slackNames という hash 値を設定します。

  • GitHub.com ユーザ名
  • Slack ユーザ名
// github username : slack username
const git2slackNames = {
  'kenzo0107': 'kenzo.tanaka',
}

上記の場合、新たにユーザ追加する度にコード修正が必要となります。

もし GitHub.com ユーザ名 と Slack ユーザ名 に命名規則がある場合、以下メソッドで命名規則に沿った変換を実装すると追加の手間がありません。

弊社では命名規則があるので、変換処理をするようにしています。*6

function extractUsernameFromMessage (message) {
  let usernames = []
  let usernameCandidates = message.match(/@[a-zA-Z0-9-]+/g)
  if (!usernameCandidates) {
    return usernames
  }

  for (let i in usernameCandidates) {
    var u = usernameCandidates[i].replace('@', '')
    if (denySlackDmUsers.indexOf(u) > -1) {
      continue
    }
    if (git2slackNames[u]) {
      usernames.push(`@${git2slackNames[u]}`)
    }

    // github username と slack username に命名規則があれば、
    // ここで変換 git --> slack username へ変更するも良し
  }
  return usernames
}

例)命名規則がある場合

  • GitHub.com のアカウント名: medpeer-<firstname>-<lastname>
  • Slack のアカウント名: <firstname>.<lastname>

以下のような変換が可能です。

for (let i in usernameCandidates) {
  ...
  ...
  if (u.match(/^medpeer-/)) {
    usernames.push(`@${u.replace(/medpeer-/, '').replace(/-/, '.')}`)
  }

いざデプロイ

sls deploy -v

GitHub Webhook URL 設定

GitHub Webhook URL が仮設定状態だったので、生成されたエンドポイントを設定します。

sls info

Service Information
service: githubwebhook2slack
stage: production
region: ap-northeast-1
api keys:
  None
endpoints:
  POST - https://xxxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/production/webhook
functions:
  githubWebhookListener: githubwebhook2slack-production-githubWebhookListener

上記の例では、 https://xxxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/production/webhookがエンドポイントになります。

こちらを仮設定だった Github Webhook URLに設定します。

では、早速試してみましょう!

GitHub.com のプルリクエストでメンションします。

f:id:kenzo0107:20190227143042p:plain

するとすぐに Slack DM 通知がきます。

f:id:kenzo0107:20190227123309p:plain

通知がこないな、という時に

Lambda のログが CloudWatch Logs に流れてますので、ログを確認してください。

そもそもログが出ていないのであれば、 Webhook の設定忘れや URL に誤りがある等チェックしてみてください。

まとめ

弊社では、非エンジニアも GitHub でコミュニケーションしています。

今回の実装によって、コミュニケーションが円滑になったことに加えて、 GitHub 上で明示的にメンションすることが増えた様に思います。

また、 GitHub から API Gateway + Lambda の認証が確立できたことで、 GitHub イベントをトリガーとした AWS リソースの変更に応用する様にもなりました。

こちらについては今後執筆できればと思います。

以上です。 参考になれば幸いです。


(☝︎ ՞ਊ ՞)☝︎是非読者になってください


メドピアでは一緒に働く仲間を募集しています。 ご応募をお待ちしております!

■募集ポジションはこちら

https://medpeer.co.jp/recruit/entry/

■開発環境はこちら

https://medpeer.co.jp/recruit/workplace/development.html

*1:リポジトリを絞りたい場合は、個々のリポジトリの Webhook に設定してください。

*2:nodenvでセットアップしています。

*3:特段これでなくてはいけないということではないので、適宜設定してください。

*4:https:// を含めるとエラーになるかと思います。

*5:serverless-secrets-plugin という暗号化する npm 管理可能な package もあるのですが、暗号化時にパスワードを設定する必要があります。そのパスワードの管理をAWS パラメータストアにすると、結局、KMS が必要となり、秘密情報を取得する手間がさらに増えてしまう為、導入経験がある yaml_vault にしました。その他にもあろうかと思いますが、もし良いのあるよ!という場合は是非教えてください!

*6:命名規則がない場合はこの辺りを DB に持たせても良いかもしれません。

Viewing all 210 articles
Browse latest View live