TECH PLAY

株式会社エニグモ

株式会社エニグモ の技術ブログ

241

少しさかのぼりますが、11/1に「ここでしか聞けない AI 、 機械学習 サービスの活用例」をテーマに開催された 第4回 Google Cloud INSIDE Digital に 弊社エンジニアの木村が登壇しました。 弊社で運営しておりますショッピングサイトバイマへ出品された商品画像に 規約違反 となるような不適切なものが含まれる場合があるため、その検出に Cloud Vision API を活用した事例についてお話ししました。 発表資料を公開します。 ショッピングサイトにおける商品画像への Could Vision API の活用 from 慎太郎 木村 Vision API の他、 エニグモ では DWHとしての BigQuery をはじめ、それに伴ったデータ収集・分析・活用のインフラとして GCP や AWS を活用しています。それらを活用してデータの収集、整備、 機械学習 モデルの運用、サービスへの組み込みなど大規模データを扱ってみたいエンジニアを募集しています。 ↓↓↓ 株式会社 エニグモ 正社員の求人一覧 hrmos.co
アバター
dry-validation (1.3) で Form Object を実装する こんにちは、エンジニアの齊藤です。 この記事は Enigmo Advent Calendar 2019 の12日目の記事です。 本日は、バリデーションロジックの開発で Form Object の設計を支える dry-validation について書きたいと思います。 Form Object について ユーザー向けの ウェブアプリケーション の実装で必ずといって発生するのが、インプット値のバリデーション処理です。 ウェブフォームだけでなく API のリク エス トパラメーターや CSV ファイルのコンテンツ等そのケースは様々です。 Enigmo で開発している ウェブアプリケーション の多くは Ruby on Rails で実装されています。 Rails アプリケーションでは、 Model にバリデーションを実装する のが一般的ですが、モデルの アトリビュート を逸脱したこのようなケースではそれぞれ専用のクラスを実装する必要があります。 Form Object 自体は有名な Rails の Fat モデルを改善する 7 Patterns to Refactor Fat ActiveRecord Models という記事のなかで紹介された設計テクニックの一つです。 Before dry-validation 今回紹介する dry-validation 以外の実装では先程の 7 Patterns... にも登場する virtus と ActiveModel::Validations を使って実装しています。 virtus で アトリビュート を定義して、通常のモデル同様にバリデーションルールを実装します。 実行方法も通常の ActiveModel の インスタンス と差異がないので、バリデーション専用のモデルを実装しているのと同じです。 app/forms/product_form.rb class ProductForm include Virtus .model include ActiveModel :: Model attribute :product_name , String attribute :reference_number , Integer attribute :images , Array [ Hash ] validates :product_name , presence : true validates :reference_number , presence : true end 実行結果 [ 11 ] pry(main)> form = ProductForm .new( product_name : '' , reference_number : ' 1000 ' ); form.valid?; form.errors => #<ActiveModel::Errors:0x000055839f9bacd0 @base = #<ProductForm:0x000055839f9bb090 @errors = #<ActiveModel::Errors:0x000055839f9bacd0 ...>, @images =[], @product_name = "" , @reference_number = 1000 , @validation_context = nil >, @details ={ :product_name =>[{ :error => :blank }]}, @messages ={ :product_name =>[ " を入力してください。 " ]}> dry-validation 1.0 released virtus 自体は少し前に 開発が止まっていた ので、 作者 が新たに開発している dry-validation が気になっていたのですが、全く違う設計のライブラリのため導入までにはいたりませんでした。 バージョン 1.0.0 (執筆時 1.3.1) が リリース されたのをきっかけにこちらに乗り換えてみることにしました。 dry-validation の良いところは スキーマ を定義する DSL の記述がわかりやすく、 ドメイン ロジックの バリデーションルール と明確に処理を分離できる点だと思います。 ここからは dry-validation を使った Form Object の実装方法について説明していきます。 Configurations dry-validation 自体の共通の 設定 はベースクラスを用意して定義します。 エラーメッセージの出力時の ロケール の設定、共通ルールの マクロ やカスタムデータタイプをここで定義します。 例えば、ここに定義している StrippedString は、ありがちな前後のスペースを取り除くのと、空白であった場合に nil に強制する文字列のハンドリングのために利用するデータタイプです。 class ApplicationContract < Dry :: Validation :: Contract config.messages.default_locale = :ja config.messages.backend = :i18n config.messages.load_paths = [ Rails .root.join( ' config/locales/ja.yml ' ), Rails .root.join( ' config/locales/en.yml ' ) ] module Types include Dry ::Types() StrippedString = Types :: String .constructor { | str | str.strip.presence } end end Schemas Schemas は dry-validation の重要な機能でデータを プリプロ セスしてバリデーションするための DSL です。 例として以下のようなパラメーターを Schemas を定義してバリデーションします。 params は HTTP パラメーター向けの スキーマ 定義のメソッドです。 スキーマ 定義の DSL はほぼ記述そのままなので理解しやすいのではないでしょうか? バリデーションの実行結果からクレンジングされた入力値とエラーがそれぞれ .values 、 .errors で取得できます。 { " product_name ": " Rustic Paper Gloves ", " reference_number ": " 59142 ", " images ": [ { " url ": " http://ruelarmstrong.com/howard ", " caption ": "" } , { " url ": " boyer.name/rhett_wunsch ", " caption ": "" } ] , " description ": " " } app/contracts/product_contract.rb class ProductContract < ApplicationContract params do required( :product_name ).filled( Types :: StrippedString , max_size? : 200 ) required( :reference_number ).filled( :integer ) optional( :images ).value( :array , min_size? : 1 ).array( :hash ) do required( :url ).filled( :string , format? : URI :: DEFAULT_PARSER .make_regexp) optional( :caption ).maybe( Types :: StrippedString , max_size? : 500 ) end optional( :description ).maybe( Types :: StrippedString , max_size? : 1000 ) end end 実行結果 [ 7 ] pry(main)> result = ProductContract .new.call( product_name : " Rustic Paper Gloves " , images : [{ url : ' http://ruelarmstrong.com/howard ' , caption : '' }, { url : ' boyer.name/rhett_wunsch ' , caption : '' }], reference_number : ' 59142 ' , description : ' ' ) => #<Dry::Validation::Result{:product_name=>"Rustic Paper Gloves", :reference_number=>59142, :images=>[{:url=>"http://ruelarmstrong.com/howard"}, {:url=>"boyer.name/rhett_wunsch"}], :description=>nil} errors={:images=>{1=>{:url=>["は不 正な値です。"]}}}> [ 8 ] pry(main)> ap result.values.to_h { :product_name => " Rustic Paper Gloves " , :reference_number => 59142 , :images => [ [ 0 ] { :url => " http://ruelarmstrong.com/howard " , :caption => nil }, [ 1 ] { :url => " boyer.name/rhett_wunsch " , :caption => nil } ], :description => nil } => nil [ 9 ] pry(main)> ap result.errors { :images => { 1 => { :url => [ [ 0 ] " は不正な値です。 " ] } } } => nil Rules スキーマ 定義をクリアしたデータをさらに Rules を使って ドメイン ロジックのバリデーションを実行できます。ここで扱うデータは スキーマ 定義をクリアしているのでロジック自体の実装に集中することが可能です。 ここでは例として単純な reference_number が存在するかをデータベースに問い合わせる ドメイン ロジックと、各 images の URL の妥当性を判定するルールを追加しました。 class ProductContract < ApplicationContract ... rule( :reference_number ) do next if Product .exists?( reference_number : value) key.failure( :invalid ) end rule( :images ).each do next if ImageChecker .call(value[ :url ]) key(key_name << :url ).failure( :invalid ) end end 実行結果 [ 88 ] pry(main)> result = ProductContract .new.call( product_name : " Rustic Paper Gloves " , images : [{ url : ' http://ruelarmstrong.com/howard ' , caption : '' }, { url : ' http://boyer.name/rhett_wunsch ' , caption : '' }], reference_number : ' 59143 ' , description : ' ' ); ap result.errors Product Exists ? ( 1 .2ms) SELECT 1 AS one FROM " products " WHERE " products " . " reference_number " = $1 LIMIT $2 [[ " reference_number " , " 59143 " ], [ " LIMIT " , 1 ]] { :reference_number => [ [ 0 ] " は不正な値です。 " ], :images => { 0 => { :url => [ [ 0 ] " は不正な値です。 " ] }, 1 => { :url => [ [ 0 ] " は不正な値です。 " ] } } } => nil Macros 例えば、登録フォームとログインフォーム、ログイン API で ID であるメールアドレスのフォーマットを判定するロジックを共 通化 したい場合 Macros が利用できます。 マクロのブロックにはパラメーターを使うことも可能です。 app/contracts/application_contract.rb class ApplicationContract < Dry :: Validation :: Contract ... register_macro( :email_format ) do unless URI :: MailTo :: EMAIL_REGEXP .match?(value) key.failure( :invalid_email_format ) end end end app/contracts/login_contract.rb class LoginContract < ApplicationContract params do required( :email ).filled( Types :: StrippedString ) required( :password ).filled( Types :: StrippedString ) end rule( :email ).validate( :email_format ) end app/contracts/registration_contract.rb class RegistrationContract < ApplicationContract params do required( :email ).filled( Types :: StrippedString ) required( :first_name ).filled( Types :: StrippedString ) required( :last_name ).filled( Types :: StrippedString ) end rule( :email ).validate( :email_format ) end 実行結果 [ 15 ] pry(main)> result = RegistrationContract .new.call( email : ' foo ' ); ap result.errors { :first_name => [ [ 0 ] " を入力してください。 " ], :last_name => [ [ 0 ] " を入力してください。 " ], :email => [ [ 0 ] " は不正なメールアドレスです。 " ] } => nil [ 16 ] pry(main)> result = LoginContract .new.call( email : ' bar ' , passsword : nil ); ap result.errors { :password => [ [ 0 ] " を入力してください。 " ], :email => [ [ 0 ] " は不正なメールアドレスです。 " ] } => nil Rspec ユニットテスト では、シンプルにバリデーションメッセージの期待値をテストしています。 spec/contracts/product_contract_spec.rb describe ProductContract do subject { result.errors.to_h } let( :contract ) { described_class.new } let( :result ) { contract.call(params) } describe ' params ' do describe ' product_name ' do subject { super ()[ :product_name ] } describe ' .filled? ' do context ' filled with valid name ' do let( :params ) { { product_name : Faker :: Commerce .product_name } } it { is_expected.to eq( nil ) } end context ' not filled ' do context ' without key ' do let( :params ) { {} } it { is_expected.to eq([ ' を入力してください。 ' ]) } end context ' nil ' do let( :params ) { { product_name : nil } } it { is_expected.to eq([ ' を入力してください。 ' ]) } end end end end end end 実行結果 ProductContract params product_name .filled? filled with valid name is expected to eq nil not filled without key is expected to eq [ " を入力してください。 " ] nil is expected to eq [ " を入力してください。 " ] Finished in 0.57222 seconds (files took 9.7 seconds to load ) 3 examples, 0 failures Conclusion 最近の開発で以前 virtus ベースで実装したかなり大規模な入力値のバリデーションを dry-validation を使って実装したのですが、 コントラ クト自体のテストも実装しやすく柔軟に構成できるため、今後も利用していきたいと思います。 ここでは紹介しきれない機能がまだまだありますし dry-rb 自体興味深いプロジェクトなので、ぜひ ドキュメンテーション や ソース を参照していただければと思います。 参考 dry-rb dry-validation dry-validation 1.0.0 released 株式会社 エニグモ 正社員の求人一覧 hrmos.co
アバター
こんにちは、データマーケター?の嘉松です。 データ活用推進室というチーム(1人なう)で、MAツールの導入から運用といった CRM の推進と、データ活用の推進や業務を効率化するためのツール作成など、現場に近い立ち位置で業務を行っています。 背景 社内のG Suiteが、Businessにアップグレードされたことで、App Makerを無料で利用することができるようになった。 目的 無料で利用できるApp Makerを使わないのはもったいない。 もったいないおばけが出る前に、 App Maker、どんな感じで使えるのか? そのためには、どんなスキルセットが必要か?  を、そこはかとなく捉えること。 Google App Maker とは App Makerは Google が提供するとっても簡単にWebアプリケーションを開発することができるプラットフォーム。 ドラッグ&ドロップなどの操作で、Web上にフォームやテーブルなどのインターフェース、データベースを作ることで、Webアプリケーションとして公開することができる。 「非エンジニア職のビジネスマンにとってITスキルを磨く超ステキなきっかけの一つになるだろう」との意見も。 Excel ・ Access & VBA の代替手段になり得る。 App Makerのベースは Google Apps Script(GAS) App Makerの特徴 HTML& CSS の知識が不要 ドラッグ&ドロップでページとUIを簡単に作れる。 Google サービスとの連携 App MakerはベースはGASなので、様々な Google サービスとガッツリ連携可能。 スプレッドシート ドキュメント ドライブ Gmail カレンダー 翻訳 データベースの作成が簡単 本格的なデータベースをクリックベースで Google ドライブまたは GCP 内に作成できる。 無料で強固なサーバーに設置できる G Suite Business以上のユーザーであれば追加料金なしで利用可能。 サーバーの準備は全く不要。 App Makerの入り口 App Maker HOME developers.google.com Google Developers appmaker.google.com アプリ、作ってみた 作ったアプリ ランニング計算機 1キロのペース(○分○秒)を入力すると、フルマ ラソン の完走タイムを計算してくれるアプリ。 ランニング計算機 作った感想 テキストボックス、ボタン、ラベルなどの作成は、パーツ(Widgets)から作りたいパーツを選んで配置するだけ(ドラックアンドドロップ)なので、スーパー簡単。 パーツの一覧 処理、このアプリの場合はボタンを押した時の計算処理は、下のようなウィンドウ内のエディタにGASで記載する。このエディタの使いっぷりが良くない。ウィンドウ、小さいし。  UIの見栄え(デザイン)は、基本的には最小限の変更しかできない。ガッツリ見栄えを良くしたいなら、 CSS を使えばできる。 まとめ G Suite Businessで無料で使えるApp Maker、どんなものかと触ってみた。 UIの作成は、パーツの一覧からドラック&ドロップするだけなので超簡単。 実行したい処理はGASで記述が必要なので、普通にプログラミングが必要。 UIの見栄えのカスタマイズは最小限。その範囲だと結構ショボい。 CSS でリッチにできるけど、そうすると本末転倒か? App Makerは、G SuiteのBasicからBusinessにアップグレードすることで利用できる数少ないサービス。 Google さんもきっと力を入れて、より一層使いやすくなってくると思う。 ということで、今後の進化に期待!! 株式会社 エニグモ 正社員の求人一覧 hrmos.co
アバター
こんにちは、デザイナーの本田 です。 この記事は Enigmo Advent Calendar 2019 の2日目の記事です。 今日は エニグモ が運営しているファッションメディア STYLE HAUS の色管理方法について紹介していきます。 放置されていた_variables.scss Sassファイルの中で重要な_variables.scssファイル。このファイルにデザインを管理する様々な変数を定義すれば、変数の値を編集するだけで全体的なデザインの更新ができるようになります。 ただ、最初に設計した後、放置されやすいのも事実です。 STYLE HAUS の開発は ECサイト BUYMA の開発の空いた時間にちょこっと進めるスタイルなので、デザインの全体を見返すタイミングがなかなか作れず、3年ほど設計を見返せずにいました。 variable.scssを3年放置した図 また、変数も最初のうちは問題なかったのですが、繰り返し修正や変更があり、名前がわかりにくいものに。 メインカラーがピンクのサービスなのもあり、ピンクだけで9種類も増えていました。 $pink : #d04589 ; $pink-dark : #c82676 ; $pink-dark2 : #c3196d ; $light-pink : #f5edf1 ; $light-pink2 : #f2e2ea ; $light-pink3 : #f3dee8 ; $light-pink4 : #f8f3f6 ; $white-pink : #FCF7FA ; $pink-neue : #d77eaa ; ぱっと見では $light-pink4 と $white-pink の判別が・・・ とりあえず、色だけでも変数化すればサイト全体のデザイン見直しに役立つと思ったので、取り組みました。 やったこと 1. サイト上で使われている色の洗い出し _variables.scssを使わない代わりに、カラーコードはベタ書きで運用していたため、実際に使われている色の洗い出しをしました。 全部で20色以上・・・あったのでこのあたりでストップ。 2. カラーパレットの作成 被ってしまう色がメインのピンクと無彩色だったので、2色のみ100〜900まで作成しました。 // Gray scheme $color__white : #ffffff ; $color__black : #000000 ; $color__gray--100 : #f6f6f6 ; $color__gray--200 : #efefef ; $color__gray--300 : #dddddd ; $color__gray--400 : #bebebe ; $color__gray--500 : #969696 ; $color__gray--600 : #888888 ; $color__gray--700 : #555555 ; $color__gray--800 : #3a3a3a ; $color__gray--900 : #212121 ; // Pink scheme $color__pink--main : #dc4991 ; $color__pink--100 : #fcf7fa ; $color__pink--200 : #faf6f8 ; $color__pink--300 : #f5edf1 ; $color__pink--400 : #f3dee8 ; $color__pink--500 : #feb4d5 ; $color__pink--600 : #d77eaa ; $color__pink--700 : $color__pink--main ; $color__pink--800 : #c3196d ; grayは綺麗なカラーパレットに。ピンクは無理やり作ったので若干バランスが... 3. 命名規則 の統一 $red だけだと、どの赤なのかわからなくなるので、他の色も揃えました。 色自体はそのままですが、 ソースコード を見たときに「カテゴリ指定の色が使われているんだな」ってことが伝わるようになると思います。 // Before $beauty : $pink ; $fashion : $red ; $lifestyle : #c2a20b ; $celeb : $pink ; $children : #d4669c ; $men : #5a72c0 ; $baby_kids : #f07468 ; // After $category__beauty : $color__pink--main ; $category__fashion : #e82152 ; $category__lifestyle : #c2a20b ; $category__children : #d4669c ; $category__men : #5a72c0 ; $category__baby_kids : #f07468 ; $category__horoscope : #8765c5 ; また、メインのカラーはいろんな場所で使いまわしているので、最初に使った変数を他でも利用すると関連性がぱっと見でわかるようになります。 $color__pink--main : #dc4991 ; $color__pink--700 : $color__pink--main ; $category__beauty : $color__pink--main ; 4. 残す変数名を決める 2,で作ったカラーパレットは、全体のデザインを知っている場合は使いやすいのですが、 アサイ ンされたばかりの人やエンジニア からし たらどの色を使えばいいのかわかりづらいので、 わかりやすい変数名はそのまま残す方向にしました。 // Others $pink : $color__pink--main ; $pink-dark : $color__pink--800 ; $light-pink : $color__pink--300 ; $white-pink : $color__pink--200 ; $red : $category__fashion ; $green : #89d045 ; $blue : #4589d0 ; $white : $color--white ; $back-black : $color__gray--800 ; $light-gray : rgba ( 239 , 239 , 239 , 0.3 ); $default : $color__gray--800 ; $text : $color__gray--800 ; $hr : $color__gray--300 ; $dark : $color__gray--800 ; $super-light : $color__gray--200 ; これをファイルの下に残すことで、「テキストってどの黒だっけ?800?900?」「ボーダーの色って何色だっけ?」と悩むことが少なくなるかなと思います。 5. ちょっとずつ リファクタリング あとはプロジェクト毎に少しずつ リファクタリング ・・・! ここのモチベーションを保つのは、結構むずかしいかなと思っていたのですが、 ソースレビューの際に、カラーコードをベタ書きしている箇所はレビュアーから指摘してもらえたので、毎回モチベーション高く取り組むことができました。 結果 _variables.scssに指定した色変数は38個あったのですが、全体を見直した結果49個になりました。 同じ色を重複して書いているので、実際の色自体は28と変化なしでした。 感想 プロジェクトに アサイ ンされて、結構最初に取り組んだのですが サービス全体のデザイン理解にとても役立ちました。 最初にもうこれ以上色は増やさないぞ!!という気持ちで作ったので、UIを考えるときに、色について悩むことがなくなりました。 1回も使ってない色ももちろんあります。( $color__pink--400 とか) おそらく今後も使わないですが、この変数名を残すことによって $color__pink--300-2 とかめちゃくちゃな 命名規則 が発生しなくなると思うので残しています。 ソースコード からデザインデータまで徹底して整備すると、色だけじゃなくフォントや余白も統一したくなって、デザインデータもかなり整理することができました。 シンボル化されたデザインパーツ(Sketch) 参考URL Sassの色管理方法について考えてみた | 株式会社インディバル 変更に強いSassの色管理プラクティス - Qiita 株式会社 エニグモ 正社員の求人一覧 hrmos.co
アバター
こんにちは、 サーバサイドエンジニアの 伊藤 です。 新卒Rubyエンジニアがオススメする実務で役にたった技術書5選 この記事を書いた時から、ちょうど1年が経ちました。 本当に、時が経つのは早いですねー。。。 そんなこんなで、今年もこの季節がやって来ました。12月と言えば、そうAdvent Calendar の時期ですね!!! ということで、Enigmo Advent Calendar 2019 を公開します!!! Enigmo Advent Calendar 2019 記念すべき1日目は、 私、伊藤が 「 OSS 初心者が初めてのコントリビューションを通して学んだ3つのこと」 をお届けします。 そもそものきっかけ やったこと 学んだこと ソースコードへの抵抗がなくなった。 OSS への PR の出し方、所作を学ぶことができた。 OSS を身近に感じることができた。 そもそものきっかけ 弊社では、EC サイトの BUYMA だけではなく STYLE HAUS というファッションメディアの開発も行なっています。 STYLE HAUS の開発時、記事パーツのア イコン画 像を Yaml で管理するため、 zilkey/active_hash というgem を導入しました。 基本的には ActiveHash *1 を用いることで、スムーズに開発を行うことができました。 ただ、 ActiveHash で生成したデータを、sort しようとした時 ActiveHash::Base.order が存在しないことに気がついてしまいました。 確かに、 ActiveHash を利用して生成したデータは Array なので、 Enumerable#sort_by を用いることで、sort は可能です。 ↓こんな感じ。 w/ Enumerable #sort_by Image .all.sort_by(& :title ) ただ、 Rails の ActiveRecord にどっぷりハマった身としては、どうしても ActiveHash::Base.order を使いたい。。。 ↓こんな感じ。 w/ ActiveHash :: Base .order Image .order( :title ) そこで、隣の席の先輩エンジニアに相談してみました。 伊藤: なんで、 ActiveHash::Base.order ってないんですかねー? ActiveHash のREADME にも ActiveRecord -like model って書いてあるのに。。。 隣の席の先輩エンジニア: そんなの知らん。無ければ自分でメソッド生やしたらいいやん。 伊藤: ヒィィィィィ(゚ロ゚;ノ)ノ 当時の私にはメソッドが無ければ自分で実装するとう言う発想はなく、 まさに青天の霹靂でした。 と言うわけで、 やったこと 本家 ActiveRecord の order メソッドの実装を読み込む。 TDD を意識して、 RSpec を実装する。 (2) で実装したテストをパスさせるように、 ActiveHash::Base.order を実装する。 PR を作成する。 祈る。そして、待つ。 何だかんだ最初は OSS と聞いてビビっていましたが、 基本的にやった事はこれだけです。 パッチを投げてから ActiveHash の戻り値を chainable にするため ActiveHash::Relation が導入されたので、 実際は(4)と(5) の間でレビューを受けて order をcahinable に対応する処理を追加実装しました。 今回実装の内容は割愛しますが、ご興味のある方はこちらをご覧下さい。 github.com 学んだこと ソースコード への抵抗がなくなった。 恥ずかしながらこれが私にとって初めての OSS コントリビューションでした。 *2 こちらで ActiveRecord の ソースコード をがっつり読み込むまでは、どちらかと言うとネットに落ちているブログ記事や公式ドキュメントを読むだけで終わっていました。 それで理解できなければ、先輩エンジニアのところに泣き込んでいました。。。 *3 しかし、今回 ソースコード を読み込んだ事で、 ソースコード に対しての畏怖の念は無くなりました。 現在では公式ドキュメントはもちろん、それでも分からなければ ソースコード を読み本当の仕様を確認できるようになりました。 OSS への PR の出し方、所作を学ぶことができた。 PR のコメントの文面は OSS ごとに特有のものがあると思います。 すでに、当該の OSS でマージ・クローズされている PR のフォーマットを参考にしました。 内容に関しては、 MECE を意識しました。 *4 MECE とは、 M utually(お互いに) E xclusive(排他的に) C ollectively(集合的に) E xhaustive(余すところのない) 要は、「内容に漏れがなく、重複がないように。」と言うことです。 レビューしてくれる方が実装内容および、 PR コメントを見ただけで全てが理解できるように意識しました。 OSS を身近に感じることができた。 以前は OSS コントリビューターさんやメンテナーさんは、雲の上の存在。 いや、むしろその存在すら意識していなかったかも知れません。。。 今まではどこかで OSS はあって当然のように思っていましたが、微力ながらのコントリビューションを通してその考えを新たにすることができました。 私たちが OSS の恩恵を受けられるのは、多くのコントリビューターさん、メンテナーさんのお陰なんだなぁと。 本当に感謝です。 今後も機会があれば微力ながら OSS に貢献していきたいと思います。 本日はこの辺で、 最後まで読んでいただきありがとうございます! 株式会社 エニグモ 正社員の求人一覧 hrmos.co *1 : ActiveHash は、 Ruby のHash を ActiveRecord -like のモデルとして使用するためのbase class です。 *2 : レビューを受けてマージされるまでの間に他の OSS に投げたパッチがマージされてしまいましたが。。。 *3 : 良くも悪くも弊社のエンジニアは良い方ばかりなので、基本的に質問すれば親切に教えてくれます。 *4 : これに関しては一回の PR で学んだと言うよりは、いくつかの PR を経て学んだと言う感じですが。。。
アバター
エニグモ 、 BUYMA の中のひとを知ってもらおうと、エンジニアへのインタビューをしてみました。 第5回は、2018年2月入社の検索エンジニア、 伊藤明大(通称:めーだい) さんです。 インタビュアー 小澤:2011年4月入社。部長。 伊藤翔 :2018年5月入社。新卒2年目。 職種は? 伊藤翔 (以下、翔): 明大さんのポジションは、検索エンジニアですか? Webエンジニアですか? 伊藤明大(以下、明大): 検索エンジニアですかね、わかんないけど(笑) Webエンジニアってのもよくわからないけど、検索をメインでやっているエンジニアですね。 前職は? 翔: 前職ではどんなことをやられていたんですか? 明大: 前職はSolrやElasticSearchを使った検索だけをやってました。 そのまえはFastっていう製品を使っていましたが、それから OSS に置き換えようという話になってSolrやElasticSearchを利用するようになりました。 翔: 大学卒業してからはずっとエンジニアをやられているんですか? 明大: 前職は2社目で、その前は新卒で SIer に入社してそこに10年ぐらいいました。 翔: その時も検索を担当されていたんですか? 明大: 全然やってないです。どちらかというとインフラよりで、サーバーをラックに載せたり、ケーブル配線もしてました(笑) 本当に なんでも屋 みたいな感じでした。 小澤: 開発もしてたんですか? 明大: 開発は開発部隊が別にいたのでほとんどやってないです。お客さんと開発部隊の間を取り持つ窓口みたいな立場ですね。 小澤: いわゆる「SE」ですね。 明大: そうです、まさに「SE」でしたね。 開発部隊が作ったものをお客さんの環境にインストールするわけですよ。それようのツール作ったりとか、手順書を作ったりとか。 いまからは想像できないような作業内容ですね。 お客さんの環境で作業するときは、1人がオペレーター・作業するひとで、1人が手順書読み上げと、、、 小澤、翔: 読み上げ!! 翔: 読み上げっていうのはただ手順を読むんですか? 明大: そう(笑) 基本的にはコピペ作業なんだけど、コピペしたことが手順書とあっているよね、っていう確認をして、その人が確認して「OK」っていったらオペレーターが「Enter」していいっていう手順ですね。 1年に2回しかリリース作業がないのですが、そこまでやる?っていう作業をしていました。 翔: それを10年間やってたんですか? 明大: そうなんだけど、6年目ぐらいかな、あまり進歩ないなと思って別のところを希望したんですよ。そしたらもっとひどい環境でした。 リリース間際のプロジェクトに開発の スペシャ リストがくるっていう触れ込みで配属されてしまい、お客さんが期待していろいろ質問してくるんですが全くわからない。 これは地獄だと(笑)。ということで、もとの環境がよく見えて(笑)、戻って3年ぐらい過ごしました。 まあでもやっぱり違うなと思ってWeb業界に転職しました。 翔: 転職したときは検索に携わりたいと考えていたんですか? 明大: いや全然(笑) 一番は自社サービスを持っている会社を希望していました。 それは1社目で感じたしがらみ、たとえばサーバーを導入するのもいいと思ったものじゃなくて会社が売りたいものを売る必要があるんですよ。 自社サービスだと自分でスペックも価格も考えていいものを選べるだろうなと考えました。 それと、1社目もWeb関連がやりたいと希望してできる部署に配属してもらいました。ですが、仕事がなさすぎて(笑) 配属されて5,6ヶ月仕事がなくて自習していました。そしたら別のところから仕事あるよって話がきたのでそこに行きました。 翔: 1回目の転職ではどういうことをするか決まって入社したんですか? 明大: もともとは検索というよりはデータ、ビックデータが流行り始めた時期でやりたいなと思っていました。 それで Hadoop とかを運用している部署で、そこに検索のチームもありました。それで入ったんですが、検索を担当している人が少なかったので自分が担当することになりました。 自分が希望して検索ってことではないです。それでもやったことがなかったのでやってみたかったです。 エニグモ への転職について 翔: 2社目から エニグモ へ転職されたときの考えはなにかありますか? 明大: 2社目では検索しかできなくて飽きていました。それと、複数のサービスを持っている会社でしたが、各サービスからくる検索案件をさばくだけで終わっていたので、自分からこうしたいってことができていませんでした。 次はサービスに入り込んでサービス提案もできるようなところ、それと服が好きなのでファッションのサービスがいいなと思っていました。 翔: たしかに、明大さんおしゃれですよね。 明大: え、(笑) ありがとう。 翔: 小澤さんと2大ファッショニスタ的な存在と思っていました(笑) 明大: え、そうなの(笑) 小澤: 私も「服好きですよね」って聞くだけ聞いたことがありますね。 「調子乗って俺より上にでるんじゃねーぞ」って意味でね(笑) 入社後の印象 翔: 入社後の会社の印象はどうでしたか? 明大: すごく自由だなと思いましたね。いい意味でね。あとはみんな若いなと思いましたね。 前の会社もエンジニアが依頼してくる部署なので、エンジニア以外と働くことがほとんどなくて、エンジニア以外が近くにいるっていうのが新鮮でしたね。 なんかいいなって(笑) 翔: 入社してからの一番の思い出は何ですか? 明大: 開発合宿かなぁ。 開発合宿が初めてでしたし。 翔: え、そうなんですか。Web業界ってどこも合宿やっているイメージでしたけど。。。 明大: 前の会社はなかったですね。やっているところが発信しているだけですからね。 普段の業務で関わらないエンジニアと関われたし、なによりリフレッシュになりました(笑) 翔: 他の部署の人を含めて合宿やりたいなって明大さんが言っていたと聞いたんですが本当ですか? 明大: そうですね。ブレストするだけとかの合宿、たとえば経営層で合宿やりましたとかたまにありますけど、そういうのをエンジニア以外も含めてやっても面白いかなと思って言いました。 翔: なるほど、検討します。 明大: え!翔くんが仕切るの(笑) 翔: 決裁権はないですけど、提案はできるかなと(笑) ちなみに業務で思い出に残っているものはありますか? 明大: 業務だと、直近でやっていた検索システムの更改ですかね。 アーキテクチャ ーからやり直して、6ヶ月ぐらいやって最近リリースできました。 エニグモ に入って一番でかくて、やりきったなと。 翔: やりきったって、大丈夫ですか?(笑) 小澤: 燃え尽きたんじゃなく?(笑) 明大: そういうわけじゃないです(笑) 当初やりたいと思っていたことを妥協なくやりきりました、という意味です(笑) 入社後に変わったこと 翔: エニグモ に入ってから変わったことはありますか? 明大: もともとはスケジュール管理をしっかりしてたんですけど、そんなにやらなくなりました。 良いとも悪いとも思ってないですが、振り返ると変わったなぁと思います。 翔: スケジュール管理しなくなったと聞くと悪いことに思いますがどうですか? 明大: なぜかというと、締切りの決まっている作業をほとんどやらなくなったからですね。 先程の検索システム更改は出品リストのリプレイスと一緒にやったので期限は決まっていましたが、それ以外はいつまでってほとんどないですね。 現在取り組んでいる業務 翔: 直近は何をされているのでしょうか? 明大: 検索システムリプレイスが一旦落ち着いたので、在庫のある商品の検索をできるようにするっていう施策をやっています。 翔: 普段の業務はどういうチーム構成ですか? 明大: ほとんど1人ですかね。 もうひとりいたこともありましたが、別のチームに移ってしまいましたし、上司の木村さんに手伝ってもらうこともありますが、基本は1人ですね。 1人でやるのはいろいろ良くないので誰かいるといいなと思っています。 翔: 一緒にやる人は検索の経験がないとだめですか。経験がなくても大丈夫ですか? 明大: 検索やってましたって人はだいぶレアだと思っているので経験は期待していないですね。 検索やりたいですって人がいればいいですけど、もともとインフラやってたけどもう クラウド の時代だからアプリよりに行きたいなって人、そういう人がSolrのような ミドルウェア から入るのが、入り口としてはよいかなと思っています。 逆に開発ばかりやっていたけどインフラへって人でも入り口としてはいいと思いますね。 間口は広いですよ(笑) 開発について 小澤: もしかして3社あって一番プログラミングしてますか? 明大: いま一番書いています! 前職でも API 担当者と検索インフラって別れていたので、ツールは作っていましたけど、ゼロから検索 API を作ったのは今回初めてですね。 翔: そうなんですか?! 明大: 既存の API の修正をしたりはありましたが、 フレームワーク から選んで開発もしてって初めてですね。 いまめちゃめちゃ成長してます(笑) 小澤: シロウトか!(笑) 下から入ってきていま上に登っていますって感じですね。 明大: 最近は自分のことを晩成型なんだなって思っています(笑) 翔: 大器晩成ですね。僕も負けないように頑張ります。 失敗 翔: 入社後に失敗したことがあれば教えて下さい。 明大: 細かい失敗はあると思うんですが、失敗を失敗と思っていない、反省点として捉えていますね。 直近でいうと、出品リストのリプレイスで、もともとデータベースでやっていたものをSolrに乗り換えますってなったんですよ。 そうすると、Solrのインデックスを作るまでのタイムラグが絶対にあるんですが、それを伝えきれてなかったなと反省しています。 小澤: なれている人からすると当たり前のことですからね。 明大: そうですね。検索を長くやっていたせいで見落としてしまいましたね。 エニグモ のいいところは 翔: では エニグモ のいいところを教えて下さい。 明大: これまでの経験を踏まえると、エンジニア以外との距離感が近いのが一番いいなと思います。 以前の職場ではエンジニア以外が普段何をしているのか全くわからなかったんです。 それがわかるようになってそこから課題感を見つけられたり、エンジニアとしてどうしたらよいかと、そういうことができるのがいい環境だなと思います。 翔: 僕は新卒として入社しているのでこれが当たり前だと感じているのですが、そうではないんですね。 明大: 恵まれてるんだよ(笑) でも、どっちがいいかはわからないですけどね。もしかしたらエンジニアの世界だけでやりたい、ソッチのほうがいい人っていうのもいるでしょうし。 結局は本人にフィットするかどうかってことなので、いいかどうかはその人次第ですね。 翔: 逆に改善点はありますか? 明大: 入って最初に思ったのは、ドキュメントが少ないですし、メンテナンスされてないのが辛かったですね(笑) でもそのバランスって難しい、すべて残す必要はないと思いますし、効率よく必要最低限を残したいので、整備したいなと思っていますね。 それと1人で作業することが多いので、作業上の相談、グチ(笑)を話せる人があまりいないですね。 木村さんは協力はしてくれますが一緒のタスクはやっていないですからね。 翔: 一人ぼっちなんですね(笑) 明大: それは言い方がよくないけど、作業レベルのことって細かいことじゃないですか、それを話す相手がいないのがちょっとつらいですかね。 まあそれは エニグモ 全体の話じゃなくて自分の置かれている立ち位置の話ですけどね。 翔: 話し相手が欲しいと(笑) 明大: それはおかしいだろ!そこまで一人ぼっちじゃないよ(笑) 今後について 翔: マネジメントとかは興味ありますか? 明大: 1社目が一番マネジメントしていました。それは管理職に上がるかどうかっていう キャリアパス しかないので、それをするしかないという環境でしたね。 それが嫌でやめたので、マネジメントと技術と半々ぐらいがいいかなと思っていますね。 一緒に働きたいひと 翔: どんな人と一緒に働きたいですか? 明大: 向上心のある人、立ち止まらず常に走り続けている人ですかね。自分なりの考えを持っている人がいいですね。 逆にいうと、言われたことしかできない人はいやですね。 意見のぶつかり合いがあったとして、それが言える人、そういう環境がいいと思う人がいいですね。 翔: いろいろ新しいこととか挑戦していく人ですかね。 明大: 新しいこともそうですし、いま抱えている問題を自分で見つけて、それをどうするか考えられる人ですね。 あとは刺激しあえる関係性がいいですね。技術的なことや考え方で刺激しあえて、一緒に楽しく仕事ができる人がいいですね。 翔: 経験は関係ないですか? 明大: 経験はおまけぐらいで、やればできるでしょと思っていますね。 ヤンキー感 翔: 体育会系な感じですか?(笑) 明大: そうじゃなくて、そういう考え方の人なら経験がなくてもやればできるでしょって、おまけぐらいに思ってます。 翔: 情熱的な感じですね。もともと体育会系ですか? 明大: 体育会系の定義がわからないけど(笑) 翔: ずっとスポーツをやってたとか、 明大: スポーツはずっとやってたけどね。 翔: いやなんか、話しかけるときに「は?!」「あ?!」みたいな反応するじゃないですか(笑) 小澤: そうですね、こわいですね(笑) 明大: そう? 本当に?(笑) 翔: 最初話したときヤンキーなのかなと思いましたよ(笑) 小澤: 完全にヤンキー感出てますよね(笑) 明大: いやそれ、この会社はいって初めて言われるようになったんですよ(笑) 「あ?!」とは言わないし、声が大きいだけだよ。 翔: はじめのころは話しかけるのにビクビクしていたんですけど、最近はこれが明大さんなんだなって慣れました。 でも他のメンバーも怖いって言ってますよ(笑) 明大: だから結局1人で作業しているから、あいつ何やっているかわかんねーし、話しかけると怖いしって印象になってるんですよ。 小澤: でも明大さんは明確に上と下で言葉を分けますよね。それがたぶんヤンキーっぽいと思いますけどね(笑) 明大: ハハハッ(笑) でも僕は年上の人に敬語で話されるのがいやなんですよ。気を使ってしまうので。 なので翔くんにはタメ口で話しますし、上の人には敬語で話しますね。 小澤: そんな上じゃないけどね(笑) 明大: でも僕が小澤さんにタメ口だったら変じゃないですか? 小澤: そうかなぁ(笑) 翔: 基準は年齢だけですか? 明大: 年齢と社歴かなぁ。木村さんには敬語だし、、、キャラもあるかな。たとえば山本くんにはタメ口だけど、木村さんと年齢はそんなに変わらないですしね。 応募を考えている人へのメッセージ 翔: 応募を考えている人になにかメッセージはありますか? 明大: まずは気軽に話を聞きにきていただければいいなと思っています。 いきなり書類応募、面接って重たいじゃないですか。話聞きたいなっていうのでいいと思いますね。カジュアルな感じでいいと思います。 新人にアド バイス 翔: 毎回聞いているんですが、新人、主に私になにかアド バイス いただけますか? 明大: 新人にかぁ。最初は浅く広くやってみて、楽しいと思うことがあったらそれを深めてみるのがいいかなと思います。 いまの所属はあるけど、ある程度したら別のことをやってみて、たとえばインフラでも、検索でもいいですし、別のことやらせてくださいってやってみるといいと思いますよ。 やれとは言わないですけどね(笑) 翔: ミドルウェア はいいかもしれないですね。 明大: 検索やる? 小澤: でも怖いからね。伊藤くんビビってんじゃん(笑) 翔: 子鹿のように震えてます(笑) 趣味 翔: 趣味の話をしたいんですが、明大さんよくランニングされていますよね。 明大: ランニングは、趣味っていうよりは健康のためかな。走ってて楽しくないですしね。 健康って、身体的な面と精神的な部分と。その2つでやっています。 翔: ?! 精神的なって、やっぱり体育会系な発想ですね(笑) 明大: そんなことないよ。運動して汗かくが一番のストレス発散だと感じていますね。 翔: あー、ストレス発散ですか。鍛錬なのかなと思いました(笑) 明大: 修行じゃないよ。 翔: フットサルもやりますよね。 明大: フットサルも趣味だったけど最近全然やらないですね。 趣味は、、 スノーボード ですね。昔からずっとやっていて、子供が生まれてから2,3年やれてなかったんですけど去年からまた行き始めて スノーボード 熱が熱くなってますね。 翔: 出身が雪国なんでしたっけ? 明大: 生まれは静岡で幼稚園までいて、小学校からはずっと神奈川です。 スノーボード が盛んなところに住んでいたわけではないです。 翔: Android っぽい時計をつけていますが、ガジェットも好きですか? 明大: これはランニング用に買ったアマズフィットっていうブランドで、日本では売ってなくて買ったんですが失敗したと思ってますけどね。 でもランニング用にはあまり使えなくて ファームウェア アップデートを待ってますね(笑) ランニング以外は良くて睡眠の質や健康状態がわかったり、ライフト ラッキング に良いツールかなと思ってます。 小澤: Apple Watch 買えばいいんじゃないかと思いますけどね(笑) 翔: はい、ではこんなところですかね。ありがとうございました! 明大: ありがとうございました! 株式会社 エニグモ 正社員の求人一覧 https://hrmos.co/pages/enigmo/jobs?jobType=FULL
アバター
エニグモ 、 BUYMA の中のひとを知ってもらおうと、エンジニアへのインタビューをしてみました。 第4回は、2017年3月入社のインフラエンジニア 夏目さん です。 インタビュアー 小澤:2011年4月入社。部長。 伊藤:2018年5月入社。新卒2年目。 前職について 伊藤: まずは前職について伺いたいのですが、前職では何をされていたのでしょうか? 夏目: 前職は SIer をやっていまして、新卒研修後に配属先を決めるときに開発とインフラどっちが良い?と聞かれて、インフラを選びました。 伊藤: なぜ開発じゃなくてインフラを選択したんですか? 夏目: みんな開発を選ぶんですけど、インフラってコン トロール している感があって良いな、と。力を持ちたかったんです(笑) それで官公庁とか金融系のインフラ構築・設計をやっていました。 小澤: ということはインターネットじゃなくて イントラネット ですか? 夏目: たぶんそうですね。 正直言うと自分が構築したシステム上でどんなアプリが動いているか知らなかったんですよね。 知らなくてもなんとかなりますし、そもそもあまり興味がありませんでした。 こういうアプリが動くからこういうサーバを作ってくれ、という流れじゃなくて、こういう要件のサーバ一式、計何百台作ってくれみたいな依頼で作っていました。 「そもそもこれって誰が使ってるのかな?」って思っても、それが見えることはありませんでした。 転職について 伊藤: そこからなぜ エニグモ へ転職されたのですか? 夏目: 自分の関わっているシステムを、誰がどういうふうに使っているかということを知るすべがあまり無くて。 なんのために作っているのかわからないのに、ただただスケジュールに追われてシステムを作ってるのが嫌だなと思って、自社サービスをやっている会社に転職を考えて エニグモ を選びました。 エニグモ は面接がサクサク進んだ印象があって、面接の中でもこれといって引っかかる点がありませんでした。 他社だと、実際のところ何をやっているんだろうと疑問に思うことがあったんですが、 エニグモ にはそういう疑問はありませんでした。 入社後 伊藤: 入社後の エニグモ の印象はどうでしたか? 夏目: 前の仕事ではバッファの全く無いスケジュールが引かれて、それに追い立てられてしまって目が死んでる人がたくさんいたんですが、Web系はそういうことが無さそうだな、と想像していて。 概ね思っていたのと同じで、自分でやりたいことをやっていく、っていう感じでしたね。 伊藤: じゃあだいたい想像通りだったんですね。 夏目: そうですね。ただ、Web系の企業って良い意味でやり合うというか、ここの作りって変じゃないですか?みたいな指摘をバシバシする感じがあると思っていたんですが、 そんなに皆そういった感じのことを言わないですね。みんな優しいなぁ、平和でいいなと思いました。 小澤: なんだこのクソコードは、みたいな(笑) 夏目: 冗談交じりでもクソコードっていう人いないですよね。 変な期待を抱いていました(笑) 伊藤: エンジニアに対してはそうですかね。エンジニア以外はどうですか? 夏目: 自分が今まで持っていた「自分の勤め先にはこういう人がいる」っていうイメージと全然ベクトルの違う人がいっぱいいますね。 同じフロアにエンジニアじゃない人がいっぱいいるっていう状況をまったく想像できなかったので。 なので最初の頃は「なんか皆すげーシュッとしてるな…」って思いました。 企画のみなさんは数字を追うために頑張っていて、そういう仕事をしている人をそもそも見たことがなかったので斬新でした。 エンジニアだけの環境だとそういうのはあまり見えないので、いろいろ見えるのはいいですね。 一番の思い出は? 伊藤: 入社してからの一番の思い出を教えて下さい。 夏目: まったく業務外なのですが、2年前ぐらいにみんなでスカイダイビングをしたことです! 普段は全然アクティブなことはやらないんで、こういうタイミングじゃないと一生やらないなと思ったんで思い切ってやってみました。 せっかくだからって余計にお金払って普通のコースより高いところまで行ったんですが、落ち始めた瞬間に気圧で耳が痛くなって。早く降ろしてくれーって。 ある意味その経緯がすごい面白かったんですけど、もう二度とやらないですね。。。 伊藤: もうやらないんですね(笑) 夏目: これまで旅行ってゆっくりするのがメインで、身体を動かして楽しむっていうのがあまりなかったんですけど。 アクティブにみんなで体験して、っていうのが自分の中では新しかったのでいい思い出です。 伊藤: ちなみに業務で一番思い出に残っていることはなんですか? 夏目: 業務では、大きなことはなくて少しずつをたくさんやっているので、来るものをひたすら打ち返していたら2年が経ったって感じですね。 変わったこと 伊藤: 入社後に変わったと思うことはありますか? 夏目: さっきの話のように、イベントごとのときはアクティブになろうと頑張るというか、意識するようになったことですね。 根っこはあまり変わっていないかもしれないですが、比較的人と絡むようになったかなと思います。 あとは、前職だとお客さんがいて、その間に入っている大元のシステム会社がいて、それを自分の会社が受けて、の下に三次請け、四次請けってかたちで、コミュニケーションを取らなきゃいけない人がムチャクチャいっぱいいたんですよ。ほとんどの場合に間に入っていることが多くて、板挟みでとにかく辛い。色々な方向に対してイライラしていたんですが、今はそういうのが全く無いです。 小澤: まったくない?ホントですか? 夏目: 自分にしかイライラしないです! なのでストレスが減りましたね。 現在の業務内容 伊藤: 現在の業務内容を教えて下さい。 夏目: これまではずっと細かいことをたくさんやっていたのですが、夏ぐらいからショッパーの方のためのシステムを開発するプロジェクトが動いていて、そのインフラを担当しています。 BUYMA の本体ではなくて、 AWS にマイクロサービスで、 Kubernetes で作りましょうというプロジェクトをやっています。 k8s を使っているプロジェクトは今まで GCP を使ってアプリの人がやっていたので、インフラチームはあまり関わっていなかったんですが、今回は AWS で、EKSで作りましょうとなったので初めて k8s をしっかり触っています。 伊藤: じゃあ k8s は今回が初めてですか? 夏目: そうですね。テキストをなぞったことはありますが、業務では初めてですね。 AWS 化やインフラの構成管理だったり、既に規模が大きいタスクがいろいろとあるので、 k8s にまで手を出したら他がおざなりになるなと思って積極的には関わっていませんでした。 ですが実際にやってみるとそんなに敬遠するほどでもなかったです。実務でやってみないとわからないですね。 最大の失敗は 伊藤: 入社後の最大の失敗を教えて下さい。 夏目: 自分が推し進めたいこととして、GitLabや Redmine のバージョンアップを定期的にやっているんですが、 Redmine をオンプレから AWS に移行してみたら3時間ぐらい使えなくなってしまいました。 あくまで社内ツールで、 BUYMA のサービスに影響することではないのですが、社内の皆さんにはすごく迷惑をかけました。 私の基準では「この時間でメンテナンスします」と宣言した時間内で終わらないというのはすごくダサいと思っていて、自分にイライラしました。 エニグモ のいいところは 伊藤: エニグモ のいいところを教えて下さい。 夏目: 先程言ったようなバージョンアップなどの作業をやろうとしたときに、今普通に動いてるんなら無理にやらなくていいじゃん、という人がいなくて、抵抗なく新しいものを受け入れてくれますね。 サーバの構成管理にChefを使っていたのですが、自分が入社して使ってみたときに辛いなと思ってってAnsibleを提案してみたら「やってみれば」と言われて、スッと受け入れてくれました。 伊藤: Ansibleを導入したのは夏目さんなんですか? 夏目: そうですね。 まだ中途半端でChefもあってややこしいんですけどね。 小澤: 色んなものが2つずつありますよね。 アプリだと Rails と PHP もそうですし、ResqueとSidekiqもそうですし。いい加減整理整頓しないとね(笑) 夏目: 導入は容易なんですよね、やりきる力が弱い(笑) 改善ポイント 伊藤: いま少し話に出たんですけど、改善ポイントはありますか? 夏目: 初めの方に言った、皆あまり バチバチ やり合わないというか。遠慮しているのとは違うと思いますが、今動いているものでもこれって変じゃないですか?ってことを言う人がもっといてもいいかなと思いますね。 ぜんぜんやっていないわけではないですが、もっといたらいいなと思います。 一緒に働きたいひとは 伊藤: どんな人と一緒に働きたいですか? 夏目: インフラチームで考えると、多方面から様々な依頼がありまして。 運用保守、障害対応、新しいアプリ用のサーバー構築、などジャンルを問わず依頼がきます。もちろんインフラチームとしてもやりたいことがあります。 そういったときに、優先順位をよしなにつけて柔軟に対応できる人がいるとありがたいなと思います。 まあそれはやっていくうちにできるようになるので、すぐにできなくてもいいんですけどね。 あとは空気を読まないで言いたいことが言える人ももっといたらいいですね。 伊藤: では応募を考えている人へメッセージはありますか? 夏目: 自分もWebのインフラは未経験で入社してやれているので、経験ないとか、知識がないとかで尻込みしないで、雰囲気にマッチすれば馴染みやすいしやっていけると思います。 自分が今持っているスキルだけ考えて、自ら門を閉ざさないでほしいですね。 小澤: そもそも今のインフラチームでWebのインフラをやっていた人っていないですもんね。 夏目: そうですね。 唐突にインタビューアーにダメ出し 小澤: 伊藤さんがただ聞いてるだけだなぁと思って隣で聞いていたんですが、どうですか? 夏目: そうですね。合いの手が弱いですね(笑) 小澤: インタビュアー交代かなぁ(笑) 夏目: 事前のアンケートを読むなって言われたんですが読むしかないですよ(笑) 小澤: もう少し、なんだろう、深く聞くというか。 夏目: それってこうなんですか?具体的にはどうですか?とかね。 他の会社のインタビュー記事とか、雑誌のインタビューとか読まないですか? 伊藤: インタビュアーってあまり喋らないほうがいいのかなと思いまして。。。 趣味は自作キーボード 伊藤: では、気を取り直しまして(笑) 趣味はなんですか? 小澤: ここからはインタビュアーっぽくできるかな(笑) 仕事の話だとインフラはまだ良くわからないから深く聞けないよね。 夏目: 自作キーボードですね。 伊藤: キーボードはいつぐらいから作っているんですか? 夏目: 日本国内のブームになっているのがこの1〜2年ぐらいで、自分もそれで盛り上がっているからはじめました。なのでこの1年半ぐらいです。 伊藤: 自作キーボードの醍醐味ってどういうところですか? 自分の使いやすいキーボードを作りたいんですか、作るのが楽しいんですか? 夏目: はじめは自分の体、手の大きさ、指の長さにあったキーボードを作るのが目的でした。 以前ErgoDoxというのが既製品で流行っていいなと思ったけど高かったので買えなくて。 最近ブームになってきて、手頃な価格のキットが出るようになったので購入して分割キーボードを作りました。 今はキーボードを作りたいから作っています。というかパーツを買いたいから買っています。 伊藤: パーツを買いたいだけですか?(笑) 夏目: キーボードを作りたいからパーツを買っているんじゃなくて、パーツがほしいから買うんです!(笑) 伊藤: パーツって結構な値段しますよね? 夏目: でも趣味の世界ってカメラとかオーディオとか、上には上にありますから、そこまではかからないですよ。 小澤: パーツって何を買うんですか?軸を買うんですか? 夏目: そうです、そうです。軸っていうかスイッチを買うんです。 スイッチを買って軸にかぶさっている上と下のパーツがほしいんです。軸はいらない! 小澤: それはなんていうんですか? 夏目: ハウジングです。 この会社のスイッチはだめだけどハウジングはいいから。みたいなパーツ取りをしたいんです。 伊藤: ハウジングだけでは売っていないんですか? 夏目: 売ってないです。スイッチごと買ってハウジングだけほしいんです。 それで他社のスイッチの軸とバネにハウジングはこのパーツでって。自分の作りたいスイッチを作るんです。 伊藤: それは自分にあった良いものなんですか? 夏目: 良いです、良いと思い込んでいるんです(笑) グリスを塗って滑りを良くしたりとか、音がカシャカシャ言わないとか。 伊藤: それっていま会社で使っているキーボードですか? 夏目: そうですよ。 伊藤: 自作キーボードを使っていて困ることはないんですか? 夏目: タイピングがめっちゃ遅くなりました。いまだに打ち間違いが多い(笑) 小澤: やめちまえ(笑) それ仕事遅くなってるんじゃない? 夏目: なってないです!! 満足感が、やる気が出るんで、結果的にはプラスになっていると思います。 伊藤: タイピングに慣れてきたらどんどんプラスになっていくと、、 夏目: でもそれより先にまたキーマップを変えてしまうんで、 伊藤: なんと! キーボードは一緒でもキーマップを変えるんですか? 夏目: そうですよ。例えば今は人差し指の位置に「-」があるけど、ここじゃないんじゃないかって、どんどん変えています。 打ち損じが多いと、ここじゃないってまた変えて、そしてまたわからなくなるっていう遊びです。 伊藤: じゃあ完成はしないんですね。 夏目: キーボードもキーマップも完成しないです! 小澤: 英字の部分はさすがに変えない? 夏目: そうですね。変えるのは記号ですね。 Dvorak とかオリジナル配列に変えたりする人はいますが、普通のキーボードと併用できなくなっちゃうんで自分はそこは変えないですね。 伊藤: マウスにこだわりはないんですか、 トラックボール にするとか? 夏目: トラックボール に興味はありますが、使ったことはないですね。 範囲選択してスクリーンキャプチャをすることも多いので、そういう微細な動きができるのかなぁ?と思っています。 伊藤: 暇なときコロコロしてると気持ちいいですよ(笑) 夏目: 暇なんですね、羨ましいです(笑) 伊藤: 暇じゃないですよ。怖い怖い。 夏目: マウス、、すぐ壊れるんですよね。 自分が使っているメーカーがチャタりやすい。 小澤: ちゃたりやすい?? 夏目: チャタリング って押しても反応しないとか。まあスイッチ変えれば良さそうなんですけど。 チャタるって一般用語じゃないんですか? 伊藤: 初めて聞きました。 小澤: 有線じゃないんですか? 夏目: あーなるほど、無線だからかな、、、、 伊藤: 他に趣味はありますか? 夏目: キーボードに似てますが、PCの自作ですね。高校生ぐらいから作ってます。 こちらもパーツを買って、余ったからそれを使うように別のPCを作って、って感じでやってます。 何か目的があって作るんじゃなくて、作りたいから作るんです。 伊藤: 高校生からPC作るって、大学も情報系だったんですか? 夏目: いや全然、大学は広報メディアっていう文系の学科です。 高校は工業高校で情報系の学科で。そこでパソコンを弄っていて飽きました。 こんなことやってたらSEになってしまう、それはだめだと思って、雑誌の編集がやりたかったので、マスコミの勉強ができるところを探して行きました。 勉強したんですけど、マスコミは食えないなと思って、結局SEになっちゃいましたね。 伊藤: 話変わりますが、運動とかしますか? 夏目: 思い出したようにたまにランニングをしています。 秋に健康診断があって数値がやばいなぁ、ってなって、冬に走って3ヶ月ぐらいで飽きて、夏は暑いから走らないで、また健康診断で思い出すっていうルーチンです。 最後に 伊藤: 若手のメンバーに何かありますか? 夏目: BUYMA のシステムって改善点がいっぱいあると思うんで色々触ってみて失敗して自分の経験にしていけばいいと思いますよ。 たくさん失敗したらいいんですよ。 伊藤: ありがとうございます。 インタビューのやり方は勉強しておきます! 小澤: はい、それでは以上で。 ありがとうございました! 伊藤・夏目: ありがとうございました!
アバター
初めまして、19年新卒webエンジニアの平井蒼大です。 弊社では、昼休憩時間を使って、最近勉強したこと、 興味があること、最近行った勉強会やカンファレンスの内容などをLT形式で自由に発表するHacker’s Delightという場が設けられています。 私も先日、「Draperの ソースコード リーディング」というお題で発表しましたので、その内容を掲載したいと思います。 動機 今回、Draperの ソースコード リーディングに至った理由は以下の二つです。 Draperの仕組みを知りたい。 Ruby , Ruby on Rails についての知識を増やす。 Draper とは DraperはPresenter層を提供するgemです。 draperを使うことで以下の利点があります。 モデルの肥大化を防ぐ グローバル空間ににヘルパーメソッドが追加されることを防ぐ。 理解する部分 今回、 ソースコード リーディングを通して理解するDraperの機能は以下の二つにしました。 また、今回、ArticleクラスをラップしたArticleDecoratorクラスを生成してDraperを使った状況を下に、説明したいと思います。 Decoratorクラスの インスタンス 生成方法 下のコードのように、ラップされたクラス(Articleクラス)の インスタンス に.decorateすることで、Decoratorクラスの インスタンス が生成されます。この動きを ソースコード を読んで理解しました。 Article .first.decorate ラップされたクラスのメソッドの呼び出し Decoratorクラス内で delegate _allを呼び出すと、ラップされたクラスのメソッドを使うことができます。下の例では、 delegate _allすることで、Articleクラスで定義されているpublished?メソッドを呼び出しています。 この動きも ソースコード を読んで理解しました。 class ArticleDecorator < Draper :: Decorator delegate_all def publication_status if published? " Published at #{ published_at }" end end end Decoratorクラスの インスタンス 生成方法 Decoratorクラスの インスタンス が生成されるまでの流れを以下の二つのセクションに分けて、説明したいと思います。 Railtieによって、Draper::Decoratableモジュールが ActiveRecord ::Baseにincludeされる流れ decorateメソッドの中身 つまり .decorateが呼ばれる前のDraperが読み込まれた際の処理と、.decorateが呼ばれた後の流れを分けて説明します。 Railtieによって、Draper::Decoratableモジュールが ActiveRecord ::Baseにincludeされる流れ まずは、 railtie.rb を読んで、どのような初期化ステップが設定されているかを見ます。 今回関係する ソースコード は以下になります。 module Draper class Railtie < Rails :: Railtie # 他の初期化ステップが追加されている initializer ' draper.setup_orm ' do [ :active_record , :mongoid ].each do | orm | ActiveSupport .on_load orm do Draper .setup_orm self end end end Draper::Railtieクラスは、 Rails :Railtieクラスのサブクラスなので、 Rails のブートプロセス時に呼ばれます。 initializer の働きは以下です。 ブロックの内容をdraper.setup_ormという名前の初期化ステップとして設定 初期化ステップ自体はMyapp.application.initialize!が呼ばれた時に呼び出される 次に、initializerのブロック内の説明をします。 ActiveSupport . on_ loadは activesupport/lib/active_support/lazy_load_hooks.rb で定義されています。 def on_load (name, options = {}, &block) @loaded [name].each do | base | execute_hook(name, base, options, block) end @load_hooks [name] << [block, options] end def run_load_hooks (name, base = Object ) @loaded [name] << base @load_hooks [name].each do | hook , options | execute_hook(name, base, options, hook) end end private def with_execution_control (name, block, once) unless @run_once [name].include?(block) @run_once [name] << block if once yield end end def execute_hook (name, base, options, block) with_execution_control(name, block, options[ :run_once ]) do if options[ :yield ] block.call(base) else if base.is_a?( Module ) base.class_eval(&block) else base.instance_eval(&block) end end end end 今回の場合だと on_ loadは、 @loaded(:active_record)に対応するものがあれば、そのブロックを即時実行し、なければ@load_hooksに :active_recordというキーでDraper.setup_orm selfを格納します。 この@load_hooksに格納されたDraper.setup_orm selfはlazy_load_hooks.rb内で定義されているrun_load_hooksが呼ばれた時に実行されます。 run_load_hooksは ActiveRecord::Base 内で、以下のように呼ばれています。 ActiveSupport .run_load_hooks( :active_record , Base ) 今回の場合、run_load_hooksメソッド内のbaseは ActiveRecord ::Baseです。@load_hooks[:active_record]にはload(:active_record) { Draper.setup_orm self } によって Draper.setup_orm selfが格納されています。 つまり、 ActiveRecord ::Baseで、run_load_hooks(:active_record, Base)が呼ばれると、lazy_load_hooks.rb 内のexecute_hook内で ActiveRecord ::Base.instance_eval{ Draper.setup_orm self } が実行されます。 Draper.setup_ormは Draperモジュール で定義されています。 def self . setup_orm (base) base.class_eval do include Draper :: Decoratable end end instance_eval内のselfはレシーバーのオブジェクトなのでDraper.setup_orm self のselfは ActiveRecord ::Baseです。 つまり、Draper.setup_orm( ActiveRecord :Base)が呼ばれ、 ActiveRecord ::BaseがDraper::Decoratableをincludeします。 decorateメソッドの中身 先程説明した流れで、Draper::Decoratableが ActiveRecord ::Baseにincludeされます。 Article .first.decorate のdecorateメソッドはDraper::Decoratable内で定義されているdecorateメソッドに対応します。 Draper::Decoratable は以下のようになっています。 module Draper module Decoratable extend ActiveSupport :: Concern def decorate (options = {}) decorator_class.decorate( self , options) end def decorator_class self .class.decorator_class end module ClassMethods def decorator_class (called_on = self ) prefix = respond_to?( :model_name ) ? model_name : name decorator_name = "#{ prefix } Decorator " decorator_name_constant = decorator_name.safe_constantize return decorator_name_constant unless decorator_name_constant.nil? if superclass.respond_to?( :decorator_class ) superclass.decorator_class(called_on) else raise Draper :: UninferrableDecoratorError .new(called_on) end end decorateメソッド内では直下のdecorator_classメソッドを呼びます。decorator_class内 のselfはArticleの インスタンス (Article.first)です。 extend ActiveSupport ::ConcernがあるのでClassMethods配下のメソッドはそのモジュールがincludeされたクラスのクラスメソッドになります。ですので、上のdecoratorメソッド直下のdecoratar_class内では、Decoratable::ClassMethods内のdecorator_classを呼び出しています。 このdecorator_classで、ラップされたクラス(Articleクラス)のDecoratorクラス(ArticleDecorator)を取得しています。 このdecorator_class内では、Articleの インスタンス がmodel_nameを持つか調べ、あったらprefixとします。そのprefixとDecoratorを文字列連結した変数とsafe_constantizeでDecoratorクラスを取得しています。 つまり、Decoratable直下のdecorateメソッド内のdecorator_classはArticleDecoratorになり、メソッド内ではArticleDecorator.decorate(self, options)を実行しています。 この時の、decorateは ArticleDecoratorクラスが継承している Draper::Decoratorクラスのinitializeに対応しています。 module Draper class Decorator def initialize (object, options = {}) options.assert_valid_keys( :context ) @object = object @context = options.fetch( :context , {}) handle_multiple_decoration(options) if object.instance_of?( self .class) end class << self alias_method :decorate , :new end このような流れで、ArticleDecoratorクラスの インスタンス が生成されます。 ラップされたクラスのメソッドの呼び出し Decoratorクラス内で delegate _allを呼び出すと、ラップされたクラスのメソッドを使うことができる仕組みを ソースコード を読んで理解したいと思います。 delegate _allメソッドは Draper::Decoratorクラス で定義されています。 module Draper class Decorator def self . delegate_all include Draper :: AutomaticDelegation end AutomaticDelegationモジュールをincludeしています。 AutomaticDelegation ではmethod_missingが定義されています。 module Draper module AutomaticDelegation extend ActiveSupport :: Concern def method_missing (method, *args, &block) return super unless delegatable?(method) object.send(method, *args, &block) end def delegatable? (method) return if private_methods.include?(method) object.respond_to?(method) end # 他のメソッド method_missingの中で使用されているobjectは、ArticleDecoratorクラスが継承しているDraper::Decoratorクラスのinitializeの中で定義されています。 def initialize (object, options = {}) options.assert_valid_keys( :context ) @object = object @context = options.fetch( :context , {}) handle_multiple_decoration(options) if object.instance_of?( self .class) end このinitializeはDraper::Decoratableモジュール( ActiveRecord ::Baseにincludeされた)のdecorateメソッド内で、.decorate(self, options)という形で呼ばれています。 module Draper module Decoratable extend ActiveSupport :: Concern def decorate (options = {}) decorator_class.decorate( self , options) end この第一引数がobjectに当たるので、今回の場合のmethod_missing内のobjectはActicleクラスの インスタンス になります。 method_missingの ソースコード に戻ります。 module Draper module AutomaticDelegation extend ActiveSupport :: Concern def method_missing (method, *args, &block) return super unless delegatable?(method) object.send(method, *args, &block) end def delegatable? (method) return if private_methods.include?(method) object.respond_to?(method) end # 他のメソッド method_missingは呼び出さそうとしたメソッドが定義されていない時に、呼び出されます。引数のmethodには呼び出そうとしたメソッド名がシンボルで入ります。 delegatable?では、プライベートメソッドを呼び出そうとしているか、また、Articleクラスの インスタンス が引数methodに入れたメソッドを呼び出せるかを調べています。 よって、m ethod_missingの1行目では、呼び出そうとしているメソッドがプライベートメソッドまたは、Articleクラスの インスタンス メソッドでない場合、祖先クラス(objectクラス) のmethod_missingが呼ばれ、NoMethodErrorを発生させます。 そうではない場合、object(Articleクラスの インスタンス )にメソッドを渡して、メソッドを実行しています。このような仕組みで delegate _allをするとラップされたクラスのメソッドを使うことができます。 所感 最初にも、記載させていただきましたが、今回 ソースコード リーディングに至った理由としては以下の二つが挙げられます。 Draperの仕組みを知りたい Ruby , Ruby on Rails についての知識を増やしたい 最後にこれらの理由に沿って、今回の ソースコード リーディングの感想を書きたいと思います。 Draperの仕組みを知りたい Draper全体の機能と比べるとかなり一部ではありますが、仕組みを ソースコード ベースで理解することができました。 ソースコード リーディング前の仕組みを理解できていないモヤモヤ感が晴れた瞬間が ソースコード リーディングの楽しみの一つなのかなと感じました。 Ruby , Ruby on Rails についての知識を増やしたい railtieで、 Rails の初期化処理を拡張したり、method_missingの使い方、その他のメソッドなど今回の ソースコード リーディングで知識を増やすことができたと思います。ただ、railtieによって初期化ステップが設定される流れ、その初期化ステップが実際に実行される部分などは、さらに調べて理解度をあげたいと感じました。 また、 発表という形をとったため、Draperの仕組みについて言葉で伝えられる程理解する必要がありました。準備は大変でしたが、理解度をあげることができたのはとても良かったと感じています。 最後まで読んでいただきありがとうございます。
アバター
エニグモ 、 BUYMA の中のひとを知ってもらおうと、エンジニアへのインタビューをしてみました。 第3回は、2018年9月入社、データサイエンティストの 庄子 さんです。 インタビュアー 小澤:2011年4月入社。部長。 伊藤:2018年5月入社。新卒2年目。 これまでの経歴について 伊藤: エニグモ 入社まではどんなお仕事をされていたのですか? 庄子: 前職はデータ分析の受託 ベンチャー で、その前は精密機器メーカーに10年近くいました。 伊藤: 大学ではなにをされていたのですか? 庄子: 大学は 理工学部 で、材料や物質工学の研究をしていました。研究室では物性物理です。金属の酸化物の粉をまぜて焼いて解析するような内容です。 それから大学院には進まずに就職しました。 研究所に配属されて、部品の異常検知 アルゴリズム の開発などをして、その後、カメラの事業部に異動して、 オートフォーカス の アルゴリズム 開発をしました。 オートフォーカス がいかに早く、ピンぼけせずに、というような開発です。 伊藤: そこからなぜデータ分析の道に進まれたのですか? 庄子: アルゴリズム を開発して、仕様書にして組み込みソフトの部門に渡すんですね。検証のための開発はしますが、自分のコードは製品には載らないんで、自分の書いたものが製品にのるようなことがしたいなと思ったんです。検証だけでなく、最後まで自分で責任を持ちたいなと思いました。 それで転職活動していて、なんとなく 機械学習 とか ビッグデータ とかに関わることがしたいなと思い、データ分析の受託 ベンチャー に応募しました。流行っていたってのもありますが、実装して問題解決にも関われそうだなと思い転職を決意しました。 エニグモ へ 伊藤: エニグモ に入社した決め手を教えて下さい。 庄子: 採用が早かったのが一番の決め手です。異例の早さだと聞いて、縁かなと思いました。 もちろん BUYMA のデータを扱えるのは魅力的ですし。 小澤: たしかにあんなに早いことはないですね。 庄子: それと、知り合いに エニグモ に関わったことのある人がいて、人間関係が良さそうな会社という評判を聞いていたので安心して決めることができました。 入社後の印象は 伊藤: 入社後の エニグモ の印象はいかがですか。 庄子: 聞いていたとおり皆さん人当たりがよくて、いい人が多いですね。お互いを尊重している感じがします。 他人を批判するような言い方をする人がいないですね。 なので仕事をすすめるうえで、解決すべき問題があって、それを真っ当な議論で進めることができています。 エンジニアでいうと、おしゃれへの感度は人それぞれですね(笑) 伊藤: ファッションへのプレッシャーはありましたか? 庄子: 多少ありましたが、面接で大丈夫ですよと言っていただけていたので、プレッシャーはそんなになかったです。 入社してからの一番の思い出は 伊藤: 入社しての一番の思い出はなんですか? 庄子: ランチにたくさん誘ってもらいました! 伊藤: たしかなんか一時期バズってましたよね。 小澤: 「はじめてデータサイエンティストという人が来た!」って。 庄子: 最初に出したブランドのセグメンテーションがすごくキャッチーで、皆さんに刺さったんだと思います。 データサイエンティストは数が少なく、募集から採用まで時間がかかったと聞いていて、期待されているんだなと思いました。 伊藤: プレッシャーは感じませんでしたか? 庄子: 入ったときはやれることをやるだけと思って、そんなに感じていませんでした。 施策を回す、実現することに意識が注力してしまって、、価値を出せているのかと、もっと手っ取り早くデータ分析できるところもあるはずなのに目が行き届いていないなとか、ビジネス的な インパク トを出せていないなと思って焦ったこともありました。 入社後変わったことはありますか 伊藤: なにか変わったことはありますか? 庄子: 施策を課題からタスクに落として遂行するのが大事だとおもっていたのですが、組織づくりや採用にかかわれるようになって、人を増やして会社としてのデータ分析のプレゼンスを挙げたいという思いが強くなりました。 小澤: 一人目のデータサイエンティストということで困ったことや良かったことはありましたか? 庄子: データの可視化や、 アルゴリズム の手法に関して記事にしたり報告したりしたときに、反応が薄いというか、どれだけ伝わっているのかと不安に思った時期もありました。 こういうやり方に皆さん慣れていないんだろうなという感じで仕事をしていました。 歩み寄りや、認識を合わせることが必要なので、もし僕の伝え方が不十分だったとしたら、気軽に分かりづらいと指摘してくれても良いのにと思うこともありました。 でも分析を繰り返していくうちに、フィードバックを受けるようにもなり、だんだんキャッチボールをできるようになってきたと思っています。 データサイエンティストとして提案しやすい状況になってきていると思います。 現在の業務について 伊藤: 現在の業務を教えていただけますか。 庄子: 今やっているのはセール施策のA/Bテストとその効果検証と、セール対象のターゲットユーザーを判別する アルゴリズム 開発もしています。具体的には離脱しそうな人の抽出する アルゴリズム 開発ですね。 その他、 マーケティング 施策の効果検証もしています。 小澤: 伊藤くん、わかってる? 伊藤: いや。。普段関わることがあまりないというか、(Webエンジニアなので)そもそもやっていることが全然違いますし、、、 庄子: そうなんですよね。思ったよりエンジニアと関わらないんですよ。エンジニアとは普段、 木村さん (庄子の上司のWebエンジニア)としか話さないですね。 伊藤: 最近だと、平井くん(2019年新卒Webエンジニア)と実装やってましたよね? 庄子: そうですね。でも具体的な実装については木村さんに指揮をとってもらって、僕の作った API を平井くんが使っていましたが、平井くんのやっていることは直接見ていなかったです。 平井くんの実装で API の使い方を間違えていたところもあったので、もっと直接コミュニケーションを取ったほうが良かったと思っています。たとえばどういう目的でこの実装が必要で、といったところをちゃんと事前に共有していなかったなとも思っています。これからその施策の結果をちゃんと共有したいと思っており、準備をしています。 失敗 伊藤: エニグモ での最大の失敗を教えて下さい。 庄子: 気づけば分析で数値的な インパク トを出せていなかったなと思っているのが最大の失敗なんですが、わかりやすい失敗でいうと、 GCP の インスタンス をポチッと消してしまいました。 伊藤: それは結構重要な インスタンス だったんですか? 庄子: まあそんなに重要じゃないというと言い過ぎですが、同僚の使っていた分析環境なので、 BUYMA のサービスに影響はしないですが、分析には影響がでますよね。作り直すことはできるので消してもいいんですが、消してしまったと気づいたときは焦りました。 エニグモ のいいところ 伊藤: エニグモ のいいところを教えて下さい。 庄子: これも先程申し上げたとおり、人がいいなと思っていて、、、 小澤: 「先程申し上げたとおり、、」ってなんか硬いなぁ(笑) 面接みたい(笑) 伊藤: そうですね、もっとラフな感じで大丈夫ですよ。 庄子: (笑) エニグモ のいいところは、話していてストレスがないですね。オレがオレがって人がいないですし。 ファッション業界ってちょっと "うぇい" な人が多いとおもってたんですが、 伊藤: そうですよね、僕もファッション業界ってイケイケな人が多いんだろうなと思っていました。そういう人がいないと。 庄子: エンジニアもエンジニア以外もコミュニケーションが取りやすいと思っています。 あとは余計なことを言われないですね。細かいプロセスに関してのマイクロマネージメントはしないですね、やりたいことをやらせてくれます。 伊藤: 小澤さんが自由ということですね。 庄子: それから、サービスとして売り手も買い手もどちらも有利にしたいというサービスですよね、 エニグモ じゃなくて BUYMA のいいところですが。そういうユーザー志向の強いサービスに関わりたい人はいい人が多いんじゃないでしょうか。 伊藤: まとめると、 エニグモ の人はいい人ばかりということですね。 庄子: あとは、意外とオフィスの立地がいいと思ってます。人が多すぎないですね。 たとえば、渋谷・六本木・新宿でランチにでると人とぶつかりそうになるんですよ。それが嫌だったんです。ここはランチの選択肢がメチャクチャ多いわけではないですが、十分なぐらいあって、人にもぶつからないのでいいですね。 伊藤: お昼は外に行くことがおおいですか。青山でおしゃれランチ。 庄子: あまりおしゃれな店には行かないですよ。 ココイチ とかよし牛とかですね(笑) 改善点 伊藤: エニグモ の悪いところ、改善点はありますか? 庄子: エンジニアの雑談がへたですね(笑) オレがオレがって自己主張する人がいないのの裏返しなのか、、、エンジニアはもともとおとなしい人が多いとはおもいますが、もうちょっと雑談があってもいいのかなとおもいますね。 雑に「こういうタスクを 機械学習 でできない?」とか、そっから仕事になったりするかなぁとか、誰がどのぐらいデータ分析や 機械学習 に理解・興味があるのかなぁとか、雑談から仕事をやる上でのヒントを得られると思っています。まあ、自分から雑談しに行ってもいいんですけど、、、 伊藤: あんまり雑談するタイミングもないんですかね。自分の席で仕事して、終わったら帰る、ってしてたら雑談するタイミングがないですよね。 たとえばリゾートスペースでみんなでランチしますってやったら接点ができて雑談するかなとも思うんです。 小澤: ランダムランチじゃないですか? (以前に伊藤が開催していたランチイベント、いまは開催されていない) 伊藤: 復活ですかね(笑) たとえば庄子さんがやっている Slack の times チャンネルに気軽に聞くのもありですか? 庄子: そうですね、ぜんぜんありです。 伊藤: ただ飲み会のときはみんな騒がしいと。結構飲み会は参加されてますよね。 庄子: そうですね、お酒自体が好きですし、話すきっかけとしていい機会だと思っています。 伊藤: 飲み会に限らず、イベントにも積極的に参加されている印象があります。たとえば昨日の ボードゲーム 部にも一緒に参加しましたし。 庄子: そうですね。 僕の信念というか方針があって、 Webサービス に関わりたいという動機ともかかわるんですが、いろいろなポジションの人の話を聞いて、 BUYMA 全体のサービスができるまでの仕組みに一貫して関わりたいという気持ちが強いので、横のつながりができる場には極力出たいなと思っています。 エニグモ はまだ100人ぐらいの組織なのでそれができると思っています。 どんな人と働きたいですか 伊藤: どんな人と一緒に働きたいですか? 庄子: 自分が興味を持っていることを積極的に発信しながら、どうやって BUYMA に使えるかなと考えられる人と仕事したいですね。 自分とは違った視点を持った人だと刺激にもなりますし、いろんな角度から BUYMA を見られるようになるかなと思います。 伊藤: データサイエンティストと聞くとハードルが高い、経験がないとできないと思ってしまうのですがどうですか? 庄子: ハードルが高いというのはやっていることが難しいそうってことですか? 伊藤: はい、そうですね。 庄子: もちろん 機械学習 の理論を詳細に知ろうとすると、数学ができないとだめだとか、知っていたほうが アルゴリズム のチューニングのやり方を思いついたりして優位になるということはあるのですが、難しいことをやろうと思わなければ、自分の得意な分野でデータ分析に関わっていくという職種だとおもっていて、たとえばビジネスに強いバックグラウンドがあればそれを生かしてデータ分析をしていくというキャリアもあると思います。また、 機械学習 を使うとなると、 Python やR等を使わないといけないのでプログラミングの知識も必要です。そういった意味で必要なスキルの幅が広いので難しそうとおもわれるのですが、ビジネス寄りの人や、エンジニアのバックグラウンドの人、理論や数学が好きというひとが、それぞれ得意なところを持ち寄って歩み寄っていけばいいと思っています。 なのでデータへの関心があれば誰でもなれると思っています。 伊藤: 最初からすべてのスキルを持っていないとなれないというわけではない、ということですね。 庄子: そうです、すべて持っていなくても大丈夫です。実際のデータに触れて、何らかの意思決定に関わっていく経験が重要ですし、その過程で必要なスキルは身につけていけば良いと思います。 応募を考えている人へ 伊藤: 応募を考えている人になにかメッセージはありますか? 庄子: ちょうど一年ぐらいたつのですが、やればやるほど掘り下げたいところが出てきます。 ユーザーの行動ログや、画像データもありますし、データ分析をやりやすくする基盤や ダッシュ ボードを整えたり、やりたいことはたくさんあります。 課題がいっぱいあるので、解きたい課題から挑戦してくれる人がいいですね。 ご趣味は 伊藤: 唐突ですが、ご趣味は? 庄子: 趣味は、ない(笑) 伊藤: 映画鑑賞とか? Netflix とか? 庄子: 映画、漫画、動画を見たりしますよ。先日「天気の子」をみました。 旅行も好きですよ。 伊藤: 海外旅行とかはいかがですか? 庄子: 一昨年に新婚旅行で ベトナム に行きました。 今度のリフレッシュ休暇で海外に行こうと思ったら、パスポートの期限が切れていて諦めました。 ヨーロッパも5カ国ぐらい行きました。中でも、 クロアチア のプリト ヴィッツ ェ国立公園に行けたことは一生の思い出です。 伊藤: ヨーロッパは社会人になってからですか? 庄子: そうです。学生の頃はあまり旅行に興味はありませんでした。 20代のころはテニスをしていました。1社目の会社にテニス部があってそこでずっとやっていました。 大学時代はスキーサークルに入っていました。 基礎スキー という分野がありまして、採点する人がいて採点されるんですよ。それをやっていました。 競技スキーはオリンピックの種目になっていますが、 基礎スキー はそういうのじゃないですね。 伊藤: (検索しながら)正確性・合理性によって競われる採点競技なんですね。 最近は 基礎スキー をやっているんですか? 庄子: 基礎スキー はやらないですよ(笑)いまは年1回スキーに行くぐらいですね。 伊藤: 問題点は採点基準が明確ではないと。 庄子: そう! スキー場でスクールやっている人が採点するんですけど、その人の独自ルールになっていると思います。 同じ級でも受験する場所で違うと思います。 これを統計的に モデリング するのが僕の夢です。。。嘘です(笑) 伊藤: 最近は運動しますか? 庄子: 最近はしないです。ちょっと暗くなるんですが、ヘルニアで腰が痛くて。。。 あとは趣味、、、読書。技術書読むのも好きです。 Kaggleもやっているんですが、そんなに真剣にはやれていないです。 伊藤: Kaggleは予想が当たりましたね。 庄子: どういうことですか? 伊藤: これまでのインタビューは仕事のことばかりでプライベートなことを聞いていないから聞いてみようと小澤さんと話していて、庄子さんまじめだからKaggleですよって言ってたんですよ(笑) 庄子: まじめって(笑) 好きなことを仕事にしたいと思っているのでやりますよ。 小澤: いいですね、趣味と仕事が一緒にできるっていいですよね。 庄子: そんなに週末はまじめにやってないですけどね(笑) 小澤: はい、それではこの辺で。 庄子: ありがとうございました。 伊藤: ありがとうございました!
アバター
エニグモ 、 BUYMA の中のひとを知ってもらおうと、エンジニアへのインタビューをしてみました。 第2回は、2016年3月入社の 山本さん です。 インタビュアー 小澤:2011年4月入社。部長。 伊藤:2018年5月入社。新卒2年目。 前職について 伊藤: 前職は何をされていたのですか? 山本: パチンコの開発をしていました。パチンコって真ん中に液晶あるじゃないですか。あれを制御する組み込みソフトの開発です。 デザイナーさんがAfterEffectsで動画をたくさん作るんですよ。それを組み合わせて表示します。 あとは音を鳴らしたりランプを光らせたり 役物 を動かしたり。 小澤: 最近パチンコやってないからわからない、、 言語や開発環境は? 山本: C言語 ですね。 フレームワーク はないです。 転職について 伊藤: なぜそこからWebの会社にきたんですか? 山本: 開発をしているので、開発について調べるじゃないですか。Webで調べるんで、Web開発の情報が多いですよね。 そうしているうちに興味を持って、勉強会ってのがあるんだとかわかってきて、2014 年の Github Kaigi というのに行ってみました。 そこでWeb開発たのしそうだなぁと思いました。 伊藤: Github を使ってたんですか? 山本: 前職ではGitはおろか Subversion も使ってなかったんですけど。Rebuild.fmの公開収録があったり面白そうだなとおもって行きました。 それでWeb開発をやってみようと思って、 Rails とReactをやってみました。 小澤: Rails はわかるんですが、なんでReactやったんですか。 山本: JavaScript もやらないとなと考えて調べると、ちょうどそのころは jQuery からReactへ、というような記事が多くて、やるなら流行っているものをやろうと思いました。 小澤: パチンコのUIをやっていて、もともとUIが好きでReactやったわけではないんですか? 山本: そうでもないですね(笑) エニグモ に決めた理由 伊藤: エニグモ の決めたのはなぜですか? 山本: 自分でやっていた Rails とReactが使えるところに行こうとおもって応募しました。 そしたら書類審査がはやくて、面接も早くて、内定が出たので決めました。 小澤: それだけですか? 山本: そうですね。 とにかく書類も面接の結果もどんどん返ってくるんですよ。こんな感じで仕事ができるのかっ、というスピード感が決め手でしたね。 入社後の印象は 伊藤: 入社してみての印象はどうでしたか? 山本: 自由だなぁと思いました。あれやれこれやれ言われないし。 もちろんタスクは振られるのですが、あとよろしくってかんじで。 小澤さんが自由なんですかね。 小澤: 私が自由ってどういうことですか (笑) 山本: 細かいこと言わないですよね。 小澤: そうですね。 山本: あとは、社員の人がキラキラしていてビビりました。 エンジニアだけじゃないし、前職に比べたら女性もいっぱいいますし。ファッションの会社ですし。 みんなおしゃれな店でランチしてそうで、コンビニ弁当なんか食べてたらちょっと恥ずかしい、みたいな。ちょっとした恐怖感はありました。エンジニアは関係ないですけど。 小澤: そういえば、ファッションの会社ってことでエンジニアに敬遠されることもあると思っているのですが、入社前に、おしゃれじゃないとだめかなとか不安はなかったですか? 山本: それは考えませんでしたね(笑) 一番の思い出 伊藤: 入社して一番の思い出はなんですか? 山本: 入社して一ヶ月ぐらいでお問い合わせのリニューアルに参加しました。 右も左もわからない状態で、レビューでもいろいろ指摘されたりもしましたが、すごく成長できました。 伊藤: レビューは厳しかったですか? 山本: 厳しいですけど、すごく丁寧で、親切に教えてくれたって感じですね。上から指摘されたりとか、怖いという感じではないです。 開発手法についても、 スクラム でやろうってなってて初めてでしたし、データベースも個人で使った見たていどで仕事ではやったことなかったですし、 Apache Solr( 検索エンジン )なんて知らなかったし。 いろいろはじめてでわからないことがわからない状態でした。ただ、一緒のチームの 齊藤さん と業務委託の方が二人共すごい技術力が高くて、二人の実装や設計スキルを見ながら勉強してました。 失敗 伊藤: 失敗したことはありますか? 山本: 定時後に大きめのリリースをして BUYMA が止まってしまいました。 小澤: そんな事あったっけ。どのぐらい止まりました? 山本: 完全に止まったわけではないですが、2時間ぐらい不安定でした。まあ見えにくかったので、止まってしまっていましたね。 それまでJSや CSS をブラウザでキャッシュするようにクエリパラメータをつけていたのですが、 SVN のリビジョンがついていたんですよ。それの仕組みがいやで変えたかったんです。 結構影響が大きいので、みんながリリースしない時間にやろうということになり、定時後にしてしまいました。 そしたら、原因の詳細は忘れましたが、ネットワークが不安定になってしまいました。 伊藤: 教訓はありますか? 山本: 定時後にリリースしないってことですね!(笑) 小澤 そうっすね(笑) 助けてくれる人も少ないですからね。調整して定時内にやりましょう。 変化 小澤: 入社してから変わったことはありますか? 山本: 変わったことですか? 小澤: 例えば、おしゃれになってますよね(笑) 山本: そうかも知れないですね(笑) みんなキラキラしてるし、おしゃれな人がたくさんいるんで、参考になる人がたくさんいるからですかね。入社前よりはだいぶ気を使うようになりました(笑) 変わったことかぁ。。。給料が上がりました!! 伊藤: そんなにですか(笑) 小澤: はじめが低かったからじゃないですか(笑) 山本: それもありますが、順調に上げてもらっていると思ってます。 給料を上げてくれるいい会社です(笑) 伊藤: 給料が上がって、おしゃれにもお金をかけられるようになったと(笑) どんな人と一緒に働きたいですか 伊藤: どんな人と一緒に働きたいですか? 山本: レガシーな部分もいっぱいあるんで、自分で勝手に改善したり、新しい技術を取り入れてくれる人ですね。 伊藤: 勝手に作って、リリースしていいんですか? 小澤: リリースはだめだよ。上場企業には統制というのがあってだな(笑) 山本: リリースはだめだけど直前までは勝手にやって大丈夫ですよ。自由なんで。 リリースするには、ちゃんと承認をもらってリリースしましょう。 会社のいいところ 小澤: 開発・仕事じゃないところで エニグモ のいいと思うところはありますか? 例えば、コーヒー屋さんとか。 山本: コーヒー飲まないんですけど、ビールがあるのはいいですね。 世界のビールがおいてあって、18:30以降はのんでいいんですよ。 あとはリゾートスペースが好きですね。とくにコーヒー屋さんの前のリゾートスペースが好きです。 ここだけモニターがなくてミーティングで使えないんですけど、集中したいときにここに来ます。 たれこみ 小澤: そういえば先日、伊藤さんから「山本さんはずっと Twitter を見てる」とタレコミがありましたが仕事してますか? 伊藤: Twitter 見てて、仕事は外注してる疑惑ですね(笑)あんなにできるはずがない(笑) 山本: もちろんずっと Twitter を見てるわけではないですよ! たまたま休憩するタイミングが同じなんじゃないですかね(笑) 伊藤さんは仕事しないで山本を見てるってことでしょ(笑) 弊社には Twitter を見る自由はありますよ。 今後 伊藤: 今後やりたいことはありますか? 小澤: ずっとフロントエンドチーム作りたいって言ってますよね。できてなくてすみません。 山本: フロントエンドの改善したいですね。一人だと辛いんでチームでやりたいです。 小澤: 伊藤くんが小さく手を上げてますけど。 山本: サーバーサイドがもう少し強くないとまだ一緒にやるのはつらいですね(笑) 伊藤: なぜか毎回、私のダメ出しみたいになりますね。 マネージメントは興味ないですか? 山本: ありますよ。 これから先ずっと技術を突き詰めていくタイプかっていうとそうでもないと思っていて、タスクを細かくして人に振っていくのも自分にむいているかなと最近感じていますね。 小澤: えっ、そうなの。いいこと聞いた。 山本: 技術もまだやっていきますが、マネージメントもありかなと思っています。 若手に向けたメッセージ 伊藤: ぼくら若手に向けたメッセージはありますか? 山本: Webの世界はすごく広大なので迷うことも多いと思うのですが、色々触って自分の好きな分野を見つけて、そこを伸ばしていってほしいです。 今後は特定の分野に特化した人が活躍するんじゃないかなぁとなんとなく思っています。 小澤: それではこの辺で、ありがとうございました。 山本・伊藤: ありがとうございました!
アバター
エニグモ 、 BUYMA の中のひとを知ってもらおうと、エンジニアへのインタビューをしてみました。 第1回は、2011年3月入社の齊藤さんです。 インタビュアー 小澤:2011年4月入社。部長。 伊藤:2018年5月入社。新卒2年目。 エニグモ に入社した理由・動機 伊藤: エニグモ に入社したのはなぜですか? 齊藤: 自社サービスがいいなと思って会社を探していました。 盛り上がっていたのでゲーム会社も面接に行ったのですが、すごい 機械的 で、 MySQL 運用したことありますか、とか、そういう質問ばかりで。 エニグモ は(前)部長とエンジニアの人と面接して、自分のできることに興味を持ってもらって、最終含めて3回面接しました。 EC に興味があったのと、どうしようかと思っているところで Stylist というサービスを公開しました、というメールがきたので、面白そうだなと思い入社を決めました。 残念ながら Stylist は今はクローズしていますけど。 小澤: エニグモ の前はイギリスですよね。 齊藤: もうやめて来ていました。 前の会社は受託で、ス クラッチ &ビルドみたいなことを何回もやるんで毎回工夫できて面白かったんですが、自社でやっているサービスも見ていたのでソッチのほうが面白そうだなと思っていました。 現在担当している業務は 伊藤: 最近はどんな業務をされていますか? 齊藤: 4年前ぐらいにユニット化されてからは、自分たちでこれやったら良さそう、変えたほうが良さそう、面白そうだなって思うア イデア を提案してやっています。 自分で発案したものもありますし、浮いていて面白そうだなってのを拾ったりしてやってます。 その前はトッ プレベ ル会議みたいなのがあって、大きなテーマのものは誰がやるかみたいなのが決まっていました。 小澤: この3年ぐらい、主にパーソナルショッパー向けの機能の開発をしていますよね。 齊藤: そうですね、それが僕の担当しているロールっぽくて、 小澤・伊藤: ぽくて (笑) 伊藤: ぽくてって認識なんですね。 齊藤: お問い合わせリニューアルぐらいからそうですね。 お問い合わせ機能が、データベースの設計から見た目までひどかったんですよ。でも、それまでは機能しているものをあまり見直さなかったんですよね。 でもあれは、なんで変えていいってなったんですかね? 小澤: 3年ぐらい言い続けたからじゃないですかね。 お問い合わせでなにかしたいという要望はあったんですが、お問い合わせは触っちゃだめだってエンジニアはみんな思ってたんですよね。 齊藤: 改修内容が大きすぎるから触れなかったんですかね。データベースから見た目まで全部やりたくなっちゃうから。。 結局そのときは全部やり直しましたね。 初めてメイン以外のデータベースの導入を決めて、データベースを設計して、既存データの移行を考えて。 UI はそのとき React が盛り上がっていたので、やったことはなかったけどやるでしょってなって、Redux の設計も勉強して導入しました。 伊藤: どのぐらいかかったんですか? 齊藤: 最初は3ヶ月ぐらいかと思ってましたが、半年ぐらいかかりました。 小澤: 当初計画の3ヶ月ってのは、もう長いからそれ以上考えられないっていう、漠然とした3ヶ月です。 齊藤: それまで3ヶ月もかかるのはなかったですからね。大体長くて1ヶ月みたいなものが多かったですね。 性能検証もあとから追加したり、見積もりが随時変わっていったっていうのも理由としてありますね。 それからこのとき 山本さん が入社して1ヶ月ぐらいでプロジェクトに参加して、1ヶ月ぐらいですでに何ヶ月も一緒にやっているような存在感で、インクリメンタルな実装を相互にレビュー・マージしていく作業フローもすぐにスムースにこなしていて驚きました。 そのプロジェクトのあとは、商品画像の差し替え、、、画像枚数からルールに従って URL を生成する処理になっていたので、画像が並べ替えられなかったんですよね。ショッパーや社内からも画像を変更したり並び替えたいという要望はずっとあったんですが、それも手を入れるのが大変だからってやってなかったんですよ。 ちょっとした改修では済まないものをそこからやるようになりました(笑)。 これも半年ぐらいかかりました。 管理画面( BUYMA 本体とは別の Java の Web アプリ)をいじらずに管理画面を改修するのもこの頃からやってます(笑)。 小澤: それもお問い合せ改修からですかね。 齊藤: そうですね、管理画面の変更( Java )がめんどくさかったんで。 画像の差し替えは半年メインラインと並走して開発したのでリリースでたくさんしくじりましたね。 今みたいに Git じゃなくて SVN だったという要因もあったり、商品画像はサイトのいろいろなところに出ているので色んな人が修正するんですよね。それでリリースマージが難しくなってしまって。。 お問い合わせ改修も半年かかったんですが、色んな所に表示される機能ではないので影響が小さかったんです。 余裕持ってやるつもりだったんですが、マージに労力を取られすぎて、マージ終わったらもうメンテじゃんていう感じで出してしまいました。 そのときはメンテ明けは普段は朝に帰るんですが、24時間ぐらい会社にいました。 小澤: その後にフィーチャーフラグとか、どんどん出していくように実装をしましたよね。 齊藤: そうですね。 その後は CSV での一括出品機能でした。機能としてはあったんですが、100品しか更新できない、下書きにしかならない、変更できない項目があるとか、使いにくいところがいっぱいあったんですよ。 1万件を上限にしたので、一つのバグでショッパーさんの全商品が壊れてしまうようなリスクがありました。 これはテストを入念にしたので時間がかかりましたね。 大変でしたけど、商品数の多いショッパーさんにすごく使われているので良かったです。 伊藤: 直近は何をされていますか? 齊藤: 直近はとある機能をやっているんですが、これまで BUYMA は モノリス なアプリなので改修自体が必要以上にめんどくさいなと感じていて、良い機会なのでマイクロサービスな設計で作ろうと思っています。 Kubernetes とか使ってインフラのメンバーにも協力頂いて進めたいです。 エニグモ に入社してからの一番の思い出 伊藤: 入社してからの一番の思い出ってなんですか?社員旅行でハワイに行ったりしてますよね? 齊藤: 一番か、難しいですね。。。。ハワイも思い出ですけど、 僕はメンテナンスが好きで、 小澤・伊藤: え、そうなんですか! 齊藤: メンテナンスでリリースするのって、サービスをコン トロール している実感が強いじゃないですか。 深夜にサービスを止めて、 ソースコード をリリースして、明け方徐々に使われだしていくその現場にいる感じが好きですね。 最近はリリースでサービスを止めるような機会は減ったのであまりないですけど。 伊藤: 一番の思い出はメンテナンス。 齊藤: 長期間開発してリリースしたメンテナンスは印象にとても残ってます。 頻繁にできる仕事ではないですけど、その分区切りにもなるし、達成感になりますね。 どういう人と働きたいですか 伊藤: これから、どういう人に来てほしいですか。どういう人と働きたいですか。 齊藤: アイディアがある人が良いですね。自分だったらこう変えるとか、これは良くないとか、自分の意見が言える人がいいですね。 あとは、人の変更見て、塩梅を汲み取って柔軟に実装できる人がいいですね。 例えば「そんなに作り込まなくてもいいです」っていう感覚が分かる人。 伊藤: 一気にハードル上がりますね。 齊藤: 思っている塩梅で、書けてないというか、「 工数 削るために ユニットテスト 書きませんでした」って来られると、そうじゃないよなと思いますし。 小澤: そうですね。 齊藤: チームで上手に動ける人がいいですね。不必要に足引っ張るようなレビューコメントで全体のリズムが悪くなることもあるので。 作業分担してても自分事になっている人がいいですね。 僕は年長者ですけど、経験者とばかり働きたいわけじゃないんですよ。 人のコードや動きを見て、良いと思うとか、これはこうじゃないですかってはっきり言える人がいいですね。 伊藤: 初心者とまではいかないですけど、あまり経験がなくてもいいと。 齊藤: そうですね。センスが良ければ関係ないと思います。 これまでにした一番大きなミス 伊藤: これまでにした一番大きなミスってなんですか? 齊藤: それは、画像差し替えプロジェクトのメンテナンス開けでバグがいっぱい出たときですね。 小澤: げんなりしたっていう。 齊藤: こんなに考慮漏れができるのかって思いましたね。 まあ、障害がでるとチーム感が出るというか、協力者が出てきてものすごいスピード感で解消していく過程はそれで楽しいですけどね。 小澤: すぐ直せっていうバグ修正はアドレナリンがでて楽しいですよね。 今後 小澤: 20年ぐらい プログラマー やっていて飽きないですか? 齊藤: 飽きないですね。 小澤: 次から次へあたらしい技術やアイディアが出てきてすごいなと思うんですよ。 齊藤: そうっすね。楽しい仕事です。 伊藤: ライバルはいるんですか? 齊藤: ライバル意識することはないですけど、、、、。 影響を受けるメンバーはいます。 小澤さんは調べるのはやいなと。バグがあったら「そこじゃない?」って速効性があってミニマムな修正を思いつくのが早いですし。 小澤: おぉぉ(笑) 齊藤: 浅香さんは作業の精度がずっと安定していてレスポンスも早いですよね。インフラ面から全体を見られていますし。 メンテナンスのバグ対応でもたくさん助けていただきました。 大川さんは無駄に作らないでツールがあるじゃんていって、新しいツールを使いこなすのも早いですし、考え方がスマートですね。真似できない。 木村さんも勉強熱心で、時代にそって変えて行くべきことを実行してますよね。最近マネージメントよりですけど。 皆さんとても良い刺激になります。 伊藤: みんな最近マネージメントよりな印象ですけど。 マネージメントには興味ないですか? 齊藤: 僕はミーティングがいっぱい入ってくると違うなって思ってしまいます。 リーダーシップはとりたいとは思いますけど、自分も動きたいですね。指示を出すようなロールではなくてメンバーでいたいです。 もっと上手な人が設計や案を出せばそれを実装したいってなると思います。 マネージメントするよりも、足を引っ張るようになるまでは開発を続けていきたいですね。 伊藤: オープンソース に貢献するのは興味ないんですか? 齊藤: そうですね、フォーカスしてやってみたことはないですね。 Rails とか開発しているのは楽しそうですけど、、ユーザーフェイシングな開発している方が面白いと思います。 あとは BUYMA ってレガシーなところがいっぱいあるから、新しいものに変えたいですね。 小澤: アイディアはいっぱい出てきますよね。 齊藤: 悔しいですけど「あれ、C社はできているのにうちはできていないなって」思うところがいろいろあって、そういう負債が消えていってほしいと思ってます(笑)。 若手になにかありますか 伊藤: 若手になにか思うことはありますか? 齊藤: 遠慮しないほうがいいと思います。なにか思うことがあったら提案しておいて、すぐにはできないことも多いですが提案すればだいたい自分がやることになるんで。 あとは障害とかあったら参加してみるとか。オンタイムに障害を克服することが開発者としてサービスに近づく一歩のようなところもあると思います。 うちの人って調べたことのログ張ったりするじゃないですか。それってここまでやったってのもあるし、他の人にもここ見ればいいって伝えたことになってて。 伊藤: 次同じことがあったらそれを見ればいいと。 齊藤: 思い出したらそれを真似してみれば良いんですよ。 昔はそうでした。人も少なかったんで誰もそんな親切には教えてくれなくて。見てればわかるっしょって感じでした。 小澤: 怖い会社ですね。 齊藤: はいったときからそうでした。 (※今も昔も聞けばやさしく答えてくれる優しい人ばかりです) 小澤: 齊藤さんのはじめのころのエピソードで面白かったのは、明日やろうと思ってたものが起きたらできていたって言う話。 伊藤: なんすかそれは。 齊藤: はじめのプロジェクトが二人でこんなサービスを作ってみてっていうのだったんですよ。 その人はやりましたとか言わないでしれっとプッシュしてありました。 伊藤: それは齊藤さんに割り当てられたタスクだったんですか? 齊藤: 今みたいに計画してやっていく感じじゃなくて、ざっくりしてたんですよ。 もうひとりの人は誰に頼らなくても一人でできちゃう人なんで、ついでにやっておきましたという感じです。 それで、やんなかったら全部作られちゃうなと思いました。 伊藤: そんなこともあったんですね。 齊藤: そうですね。それで折れてやめたいとは思いませんでしたが、頑張んなきゃなと思いました。 ちなみに 齊藤: 僕が一番目に指名されたのは(年長者なので)気を使ってですか? 伊藤: そうじゃないです。 小澤: そうじゃないですね(笑)。 誰に聞いてみたいって聞いたら、真っ先に齊藤さんが出てきました。
アバター
こんにちは。気が付けば入社から一年が経ち、 新卒の肩書きを失った @sean0628_i です。 4月4日(木)、4月5日(金)の日程で開発合宿を行ってきました。 場所は前回と同じく、 おんやど恵さん にお邪魔しました。 前回の開発合宿 が2017年だったようなので、2年ぶりの開催ですね。 テーマ 今回は『チーム力のアップ』をテーマに設定しました。 前回は個人個人好きなことを行うスタイルだったようですが、 今回はテーマが『チーム力のアップ』でしたので、基本的にはチーム開発で取り組むことにしました。 参加者の希望を聞き入れつつ、実行委員が1-3人のチームに分けました。 そして、チームごと取り組む内容を決定し、合宿に臨みました。 合宿一ヶ月ほど前にチームを発表し、準備期間も設けました。 スケジュール感: 3月初 - チーム発表 3月中 - 内容決定・下準備 4月4日、5日 - 合宿開催 合宿でやったこと 内容に関しては、業務に関する範囲であれば基本的になんでもOK です。 最終的にそれぞれ以下の内容に取り組んでいました。 内容: 1班:STYLE HAUS フロントの性能改善(画像リサイズ) 2班: BUYMA フロントの性能改善 3班:商品画像分類 4班:STYLE HAUS タグのサジェスト 5班:Jenkins のデプロイジョブのGitLab-CI 化 6班:GitLab Runner Autoscale on AWS 当日の様子 東京駅出発 皆さん遅れてくるかなぁと思い15分前集合にしましたが、まさかの全員5分前行動。 出発の20分前には全員集まりました、、、さすがです!!! 開発開始 湯河原駅 周辺で昼食をとって、開発開始です。 テーブルはチームごとです。 キーボード作成が趣味のメンバーは、自作キーボード持参で合宿に臨んでいました。 BUYMA パーカーを着て開発するメンバーも! 夕食 夜は宴会!!! テンションの上がったメンバーがコスプレをし、晩酌をしていると、、、 密告され、部長殿におこられてしまいました。。。 宴会の後は反省して、黙々と開発に励んでおりました。。。 記念撮影 宿の前でパシャり。 2日目はお昼まで開発を行い、 湯河原駅 周辺で昼食を食べ帰路につきました。 まとめ 当日は大きく成果を出せたチーム、あまり成果を出せなかったチームとありました。 ただ、事前準備の期間をおよそ1ヶ月用意したこともあり、どのチームも最低限の成果は出せていたように思います。 また、通常の業務を離れて普段あまり関わることのないメンバーと開発を行うことができて、 参加者それぞれ新たな気づきがあったことと思います。 『チーム力のアップ』という点で見ても、縦・横両方の意味で向上することができました。 私個人としても一日中シニアエンジニアの方に手取り足取り教えていただき、 Serverless、Docker などの知識を深めることができました。 またとない貴重な時間を過ごさせていただきました。 今後も積極的に社内勉強会、開発合宿を開催していきたいと思います。 最後まで読んでいただきありがとうございます。
アバター
はじめまして。 BUYMA の iOS アプリチームでデザインを担当しています。 この記事を読んでくださりありがとうございます。 2019年2月に、お問い合わせ機能をネイティブ化しました。 より使いやすくするために、Webviewからネイティブ化し、あわせてリデザインすることになりました。 Webとアプリを作業する上で、デザイナーとして苦労した部分を、新しいデザインと一緒に書いていきたいと思います。   まず、 BUYMA の iOS アプリってどんな感じ?   BUYMA はショッピングサービスなので必要なページがたくさん。 現状、アプリの半分くらいが、Webviewです。 ネイティブ化されたページとWebviewのページが混在しているため、ページによってデザインテイストが異なっていることを課題のひとつとして感じています。 ユーザーがアプリを起動してから目的を達成するまで、気持ちよく使えるよう統一されたデザインにしていきたいと考えています。   実際にデザインするときに考えていること2つ   1・デザインのトレンドを押さえた ユーザーインターフェース 古っぽいデザインは、扱っているコンテンツさえ古く感じさせてしまうのでもったいないです。特に BUYMA では、日本未入荷のレアアイテムを揃えていたり、最新のトレンドを発信しているサービスのため、サービス ブランディング の観点からも、とても大事になってくるポイントだと考えています。 そのためまずはとにかく他社リサーチに時間をかけています。国内外の人気アプリのデザインをチェックしてよく使われているUIを参考にしたり、海外のデザインギャラリーを漁ってアイディアの引き出しを増やしています。 よく参考にしている海外デザインサイト 2・ユーザーが目的を達成するためのデザイン トレンドだけ追いかけて「とりあえず流行りのデザインを取り入れたけど、実は使いにくかった」となっては本末転倒です。そのため、ユーザーが目的を達成しやすいデザインかどうか、という視点が重要になってきます。 そのページではユーザーに何を達成してほしいのかを明確にすること、そしてそれを達成するためには、ユーザーにどういうアクションをしてほしくて、きちんとアクションしやすいデザインになっているか、ということを考えながらデザインを行っています。   お問い合わせ画面の目的   BUYMA はCtoCサービスです。注文がはいるとパーソナルショッパーが買い付けに行き、商品を送ってくれるサービスです。 商品によって、配送日数が異なっていたり、買い付けに行ったけど在庫が無かったというケースが発生する場合があります。 もしユーザーが BUYMA を、在庫を持つ一般的な ECサイト だと思って使っていた場合、「想定よりも配送日数が長かった」「注文したのにキャンセルされた」といった体験はユーザーのペインポイントになりかねません。 このペインポイントは注文前に商品について事前問い合わせをすることで解消できます。 お問い合わせ画面では、このようなペインポイントを解消することを目的とし、2点を達成することが必要だと考えました。 BUYMA はCtoCサービスだと伝えられていること パーソナルショッパーへ在庫や商品詳細についてお問い合わせがしやすいデザインであること   現状のお問い合わせ画面の課題   それでは本当にCtoCサービスということが伝えられているのか、お問い合わせがしやすいデザインになっているのかということを念頭におき、現在のデザインから改善できるポイントがないかを考えていきます。 ▼現状のデザイン ▼現状の課題 CtoCサービスということが伝えられているのか → 1 パーソナルショッパーへの問い合わせフォームだということが伝わりづらいため、CtoC感がなくハードルが高い印象を与えかねない。 お問い合わせがしやすいデザインになっているのか → 2 商品ページからお問い合わせボタンをもっとタップしてもらいたいので、現状よりもボタンに気づきやすくしたい。 → 3 お問い合わせ一覧ページは、なんのアクションをしてほしいページなのか分かりづらい。 以上の課題に対してリデザインしていきます。   課題に対するアプローチ   ▼改修後のデザイン ▼課題に対する変更点 1 ・CtoC感がなくハードルが高い印象を与える。 メッセンジャー アプリに寄せたUIで問い合わせのハードルを下げ、気軽さを出しました。チャット形式のUIは、サイトへの問い合わせではなく、パーソナルショッパーとの人対人のコミュニケーションであることを意識づけられたと思います。 パーソナルショッパーのアイコンをナビゲーションバーの右位置に表示し、常に目に入るようにして、パーソナルショッパーの存在感を出しました。アイコンをタップすると、パーソナルショッパーページや出品アイテム一覧を見ることができます。 他人のお問い合わせ内容を見やすくしました。改修前はお問い合わせ内容がトグルに包まれて一文しか出ていなかったものを、全文出してひとめでわかるようにしました。他人のお問い合わせ内容が目に入ることで、 BUYMA はCtoCサービスであり、在庫確認などの問い合わせが必要であることを伝えられたのではないかと考えています。   2 ・商品ページのお問い合わせボタンをもっとタップさせたい。 商品ページのお問い合わせボタンの領域を広め、件数を表に出すことで、他の人の活発なお問い合わせのやりとりに気づきやすくなるようにしました。結果、問い合わせボタンのタップ数を130%増やすことができました。   3 ・お問い合わせ一覧ページは、なんのアクションをしてほしいページなのか分かりづらい。 新規問い合わせボタンをアイコン化して視覚的に認知しやすいようにしました。また、スクロールしてもボタンを画面右下のポジションでフィックスさせることで、ユーザーがどのタイミングでも問い合わせフォームへ進めるようにしました。 新規問い合わせボタンと同レベルで目立っていた「指名リク エス ト(※)」のボタンの優先順位を下げて奥へ場所を変更しました。指名リク エス トボタンの優先順位を下げたことで、クリック数は減りましたがリク エス ト数に影響は無かったため、ページで誘導したいアクションをよりシンプルにできました。 ※「指名リク エス ト」とは、自分が探している商品を世界中にいるパーソナルショッパーにリク エス トすることで自分の代わりに欲しい商品を探してもらうことができるサービスのこと。   Webデザインとアプリデザインの違い   Webページのデザインの場合は Photoshop でデザインし、コーディングまで担当しますが、 アプリデザインに関してはSketchで作成したデザインをZeplinにエクスポートして、エンジニアに実装してもらっています。 ちなみに、Webデザインの頭でアプリデザインをしてもエンジニアからの手戻りが発生することが多いです。 わたしはWebとアプリの違いを理解しないまま、アプリのデザインをはじめてしまったので修正対応に苦労しました。 たとえば… ・このボタンを押したときのボタンの挙動は?アニメーション入れる? ・ここはページの遷移はモーダルなのか?プッシュなのか? ・可変要素で無いパターンの場合、高さを詰めるか? ・アラートやアクションビューの要不要 ・タブバー隠すか出すか ・なんか実装してみたらここの動きがヘン Webページのデザインでは考慮しないようなポイントのため、アプリのデザインをするときはアプリの仕様をおさえておくことが必要です。 わたしは、モーダルとプッシュの役割の違いから勉強しなおしました。 わたしが今回特にお世話になった記事はこちらです。 ユーザインターフェイスのデザインのヒント - Apple Developer iOS ヒューマンインターフェースの原則 初心者がアプリのUXデザインで押さえておくべき9つの原則 [iOS版]アプリのUIデザイナーとして働き始める時に覚えておくと便利な用語 わたしと同じように、これから iOS のアプリデザインに挑戦するという人がいたら、ひとまず Apple のヒューマンインターフェース ガイドライン は必読です。 アプリの仕組みを理解することで、エンジニアとの共通言語が増え、コミュニケーションもより円滑になります。   BUYMA の iOS アプリチーム   とこんな感じで、 iOS エンジニアに助けてもらいながら、お問い合わせ画面を新しいデザインにすることができました。 Webデザインをするとき、HTMLや CSS のコーディングのことまで考えながらデザインしますが、 同じようにアプリデザインでもアプリエンジニアの実装のことも考慮してデザインできるデザイナーでありたいなと思います。 デザイナーだけでデザインをするというよりは、チームみんなで良いプロダクトになるよう日々ディスカッションしています。 BUYMA の iOS アプリチームのメンバー構成は、デザイナー・エンジニア・MDでアプリの改修やブラッシュアップをおこなっています。 わたしたちと一緒に iOS アプリをつくっていくメンバーを募集しています。 現在ディレクターが不在のため、特にディレクター職の応募をお待ちしています! www.enigmo.co.jp
アバター
はじめに この記事は Enigmo Advent Calendar 2018 の25日目です。 BUYMA でプロダクトマネージャー・ディレクターのようなことをしています。 機械学習 に関する案件を初めて進めてみようと思い、 プロダクトマネージャー・ディレクター目線 で 、やってきたことや学んだことをまとめます。 知識がなくてもプロジェクトや案件は進めれるとは思いますが、ある程度理解があることで、プロジェクトの幅も広がりますし、エンジニアとのコミュニケーションも円滑になりますし、 何より自分も楽しいです 。 また、もし 機械学習 に関して知見がない会社でプロジェクトを進めていく場合の参考になればと思います。 この記事の対象者 非エンジニア の プロダクトマネージャー・ディレクター  向けの記事です。 プロジェクトや案件で 機械学習 を利用しようと検討しているがどうしたらよいかわからない 自分で 機械学習 学びたい!せっかくなら、プロジェクトや案件にしたい!(自分はこのタイプです) という方向け。 対象ではない方 技術的な話とかかわらない方 既に 機械学習 を利用してプロジェクトを推進している方 機械学習 に関わるプログラミングを実施している方 エンジニアの方 全体の流れ 1.基本知識のインプット 2.実際にコーディングもやってみる 3.プロジェクト・案件にするまで 基本知識のインプット ゼロからスタートする場合は、何から初めてよいのか?と悩むことも多いと思います。 一番最初にやってみた まず参考にしたのが下記の記事 【保存版・初心者向け】独学でAIエンジニアになりたい人向けのオススメの勉強方法 網羅されていて、わかりやすいが、プロダクトマネージャー・ディレクターとしては、ここまではいらない。(実際にやってみて途中でいろいろ挫折。時間もかかる) その中で、やってきたことをプロダクトマネージャー・ディレクター向けに書きます。 下記に出てくる記事や本を読めば、最低限は大丈夫ではないかなと思います。 STEP1:まず 機械学習 を知る 例に漏れずここから。流し読みでもかなり面白い。 人工知能は人間を超えるか (角川EPUB選書) 読み終わると AIってなんかすごそうってなる なんかすごくAIがわかった気持ちになれる エンジニアが言っていることが何となくわかるようになる STEP2 : Python を知る 実際にコーディングもやることを考えると、ディレクターでも Python は必須です。 全く触れたことがなかったので、本当に簡単なところから。 ドットインストール 動画を通勤中や移動中に見る。理解しようとせず最初は、流し見でよいと思う。 3回ほど聞くと、なんとなくわかってくる。 終わると 動画なら、なんでも良いと思う Python がわかった気になれる Python を書いてみたくなる エンジニアの言っていることがわかるようになる Python3入門ノート 非常にわかりやすかった。 エンジニアではないので、日々コーディングしないと言語は習得が難しいので、なんとなく理解する程度で、読み飛ばす。 その中でも、読んでおくとよさそうのは、 Part3の「Numpyの配列」 ただ、読んでもほぼ忘れる。 あと、読み進める中で気になるところは、写経して実際にコードを書いみると良い。 一番重要なのは、 あとから困ったときに調べるために使う こと 。 読み終わると Python がかけて嬉しくなる エンジニアの仲間に入れてた気がする STEP3(+α):Courseraのmachine-learning 無料 でここまで使える学習ツールはすごい! 機械学習 のロジック部分で、実際にはCourseraを全くやっていなくてもコーディングはできる。知っておくとパラメーターチューニングの意味を理解できる面白い。 通勤中などの移動中に聞ける。 英語できなくても、日本語も用意されているのでほぼ問題ない。 こういう記事 からも力をもらいながら、完了。 Courseraのmachine-learning 学習を終えると なんか 機械学習 の理論がわかった気になれる ただ、本当にすぐに忘れる アンドリュー先生(講師の先生)が優しすぎて好きになる 実際にコーディングもやってみる PandasとMachineLearning KaggleのLearn がめちゃくちゃ良い。しかも 無料 。 英語だけれど、 Google翻訳 を使えばほぼ問題なく進められる。 機械学習 を実際に行う際のベースとなる部分なので、これはやっておくことをおすすめする。ただ、使わないと忘れるので注意。 本を読みながら深掘りしてみる pythonで始める機械学習  を読んで、気になるところは写経してみる。 実際のプロジェクトをする際は、 機械学習 のパラーメーターチューニングなどはディレクターには求められていないと思うので、1章から3章くらいまでやれば十分だと思う。 読み終わると 機械学習 のコードが書けて、嬉しくなる なんか実際のデータでやってみたくなる (補足)コーディングに利用するツール 自分で環境構築できる方はぜひそちらで。 環境構築ができない/しない方は、 Google の Colaboratory でやるのがおすすめ。 これも無料で簡単なので、僕もこれを使いました。めっちゃ楽です。 プロジェクト・案件にするまで はじめに プロマネ として、 機械学習 プロジェクトを始めるならこれは必読書です。 仕事ではじめる機械学習 本書では、 機械学習 やデータ分析の道具をどのようにビジネスに生かしていけば良いのか、また不確実性が高いと言われている 機械学習 プロジェクトの進め方について 整理しています。 本書はもともと、 機械学習 の初学者向けに書いた文章からはじまりました。入門者のために書きはじ めたのですが、実際には理論を軽めにしたソフトウェアエンジニア向けの実践的なカタログのような形 になっています。 アルゴリズム の話などは他の書籍でも数多く取り上げられているので、本書ではプロジェクトのはじ め方や、システム構成、学習のためのリソースの収集方法など、読者が「実際どうするの?」と気にな るであろう点を中心にしています とある通り、 プロジェクト始めたいけど、でどうするの? ということが書いてありますので、実施にプロジェクトを進める方にはすごく学びが大きいかと思います。 実際に、「その作業必要?」のようなリソース判断をすることや、全体像を理解することで手戻りを少なくする一助にもなるかと思います。 この要約 がわかりやすかったです。 読み終わると 機械学習 のプロジェクトは思っているよりも多くのことがあるのだと知る 実際にプロジェクトをやってみたくなる 実際にやったこと 本書の中で重要だと思ったのは、 機械学習 を使うことを目的化しないということでした。 機械学習 の本なのに、 別に 機械学習 を使う必要は必ずしもない  と書いてあり驚きました。 実際に案件化していくステップとしては、 1.ユーザーの課題を明確にする 2. 機械学習 を使わずに解決する方法を考える 3.どうしても必要な場合は 機械学習 を活用する です。 機械学習 の有無にかかわらず、 ユーザーの課題を明確にする ことからスタートして、 機械学習 を解決策の一つ として持っておく感覚かと思います。 STEP1:ユーザー課題を明らかにする プロジェクトを進めるにあたり、課題を明確化します。 実際に僕らが進めた場合は、経営陣やビジネスサイドの方と、 1.経営陣が認識している事業課題 2.現状現場が認識しているサービス課題 3.サービス以外にも、現場メンバーの作業に関する課題 など、様々なレイヤーでサービス課題/業務課題に関するディスカッションする機会を取りました。 課題を俯瞰して考えることで、課題理解も広がり、実際モチベーションも上がりました。 このステップを終えると やっぱり〇〇はすごい解決したいよね〜(サービスの大きな課題)を再認識できます 普段そんな作業をしてるのか。。。という現場のリアルを知れるます 他の部署の人と仲良くなります 実際にやる前提で話をするので、すごく楽しいです。 STEP2:課題の解決策を考えて、進める 初めて案件を進めるとなれば、どのように案件を進めるかが難しいかと思います。 はじめての 機械学習 関連のプロジェクトということもあり意識したのは、いかに ライトに着地させて一定の効果を出す かでした。 そのための課題設定と解決策を決めます。 本当は、これが理想。 ただ、簡単に見つからないですよね。。。 実際に進めていくなかで、最初の案件として一番しっくりきたのはこういう施策でした。 機械学習 を取り入れる理由が明確にあり 、且つ 工数 も比較的小さいもの。 ここを見つけてプロジェクト化していくことで、進みやすくなるかと思います。 実は、ここで言う開発 工数 とは、 機械学習 の基盤を作成する 工数 は除いて考えています。 機械学習 の運用基盤を作成するのは 工数 がもちろん高いので、 できるだけ使い回せるロジックにすること 一度作成すれば、それほどアプリケーション開発の 工数 が変わらないこと が重要なので、複数案件化できる状態にしておくことで基盤を作りやすくなるかと思います。 STEP3:巻き込む人を間違えない 前出の まとめ記事 にもありましたが、案件を進める上で上記のメンバーは巻き込む必要があります。 そこで間違えると話が進まないので、初めてプロジェクトを進める場合は気にしてみてください。 まとめ/終わりに ここまでお読みいただきありがとうございました。 はじめての記事でしたが、少しでも参考にしていただければ嬉しい限りです。 基本的なインプットから、コーディング、プロジェクト化など一通りやってみて非常に勉強なりました。 実際にコーディングもプロジェクトもやってみることが一番だと思いました。 案件を考えるときに「 機械学習 」という選択肢を持てるのは、 プロマネ としては強みにな今後なりうるのだろう思います。 ただ、選択肢の一つということを意識することは重要 機械学習 を使う!と言いながら、やはりいちばん大切なのは、「ユーザーの課題は何か?」という問いに尽きると改めて思いました。 実際に、知見がない中で初めてプロジェクトを進める場合は、関わるメンバーや案件は慎重に進めるとよいと思います。 時代の後押しはあるので、比較的会社も挑戦を応援してくれると思います。
アバター
概要 エニグモ サーバーサイドエンジニアの @gugu です。 この記事は Enigmo Advent Calendar 2018の24日目 です。 関数型言語 って結局何なの?と思ったので調べてみました。私が疑問に思ったことをベースに調べた内容を記載していこうと思います。 ※ 関数型言語 として主に Haskell をメインに調べているので、 関数型言語 すべての話ではない記述があるかもしれません。 対象者 オブジェクト指向 は知っているけど、 関数型言語 は知らないって方。 私と同じような 関数型言語 への疑問を持っている方。 参考書 すごいH本 (この略し方、、、センス良い!) 参考サイト https://qiita.com/stkdev/items/5c021d4e5d54d56b927c https://qiita.com/arowM/items/0305d4f439752f285438 https://qiita.com/hiruberuto/items/26a813ab2b188ca39019 https://qiita.com/wcsakurai/items/5c83a0dc8c8175861902 ありがとうございます! 関数型言語 の疑問 「関数型」っていうけど手続き型でも オブジェクト指向 でも関数を書くじゃん? 私がはじめに思った疑問です。みなさんもそう思いませんか? 下記のようです。 関数型言語 の関数というのは副作用のない純粋な関数のこと。(詳しくは後述) すべてが関数でできている。 Haskell だとif文も関数なのでelseの省略は不可。※言語によってそれぞれ例外あり。(そもそも何か値を返すのが「関数」、返さないのが「プロシージャ」と呼ぶ。 C言語 からプロシージャも含めて「関数」と呼ぶようになったとか。) オブジェクト指向 ではクラス内に関数を書く。(お作法的に。なぜこんな話をするのかは後述) 関数型言語 の中でもマルチ パラダイム 言語って両方使えて最強じゃね? 結論から言うとそうでもないらしいです。 その前に 関数型言語 の種類について説明。。。 純粋 関数型言語  → その名の通り関数型のみ。例: Haskell とか マルチ パラダイム 言語 → 関数型と オブジェクト指向 の両方の仕組みを備えている。例: Scala とか オブジェクト指向 が備わっているということは副作用が発生する可能性が増えるということ、つまり 関数型言語 の本来の目的とズレてしまっていることになります。(もちろん適材適所で オブジェクト指向 と関数型で使い分けられるメリットはあるかと。) 別に 関数型言語 でなくても オブジェクト指向 で副作用なく作れば良いのでは? まずは先に副作用がないとはどういうことか 雑な説明かもしれませんが、変数が途中で変わらず参照透明であることです。 束縛 関数型言語 で変数に「代入」することを「束縛」と呼びます。代入と違う点は一度値を入れると変更できません。 x = 1 x = 2 -- error 参照透過性 引数が同じなら返り値も必ず同じになる関数のことを「参照透過性」と言います。 で、 オブジェクト指向 でも参照透明に書けば良いのでは? その通りで、 オブジェクト指向 でも副作用を避けて書くことは大事です。ですが、そもそも言語としてのアプローチが異なります。 関数型言語 :副作用を排除する。というより普通に書けば排除される。 オブジェクト指向 : カプセル化 して副作用を内側へ隠蔽する。 関数型言語 は副作用が起きづらい?だから? 下記メリットがあります。 テストや保守を容易にする バグがおこりずらい 再利用可能な部品を作りやすい (注目)遅延評価 遅延評価 副作用がないので実現可能に。 使用するときに一度だけ計算されキャッシュされます。無駄な処理を省くことができます。 関数型言語 ってどうやって書くの? これが代表例なのかわかりませんが、私が一番なるほどと思ったのはループ文です。 関数型言語 は関数がメインなのでループをさせる際はfor文ではなく 再帰 を使用します。( Haskell だとそもそもforもwhileも無いみたいです。) Wikipedia(関数型言語) で、 オブジェクト指向 、 関数型言語 どちらで書くべき? すみません、わからないです。。。(汗) 個人的な意見だと、やはり堅牢でテストコードの書きやすい 関数型言語 がベターなような気がしますが、 オブジェクト指向 のほうが「オブジェクト」を自然と意識して作れるので設計しやすいような気もします。ただ書き慣れているだけかもしれないのでやはり 関数型言語 なのでしょうか。 関数型言語 の勉強に Scala は向いていないかも。。 関数型言語 の勉強で Scala を勉強していたのですが、結局は オブジェクト指向 脳を使って オブジェクト指向 で書いてしまうので、なにが 関数型言語 の特徴なのか理解しづらいです。 関数型言語 を学びたければ純粋型を学ぶのが良いかと思います。 まとめ 実際私が 関数型言語 への疑問を中心に書いたので、偏った知識かもしれません。しかし同じような疑問を持っている方に少しでも役にたてばと思って書いてみました。 調べた感じだと 関数型言語 は良いことづくしなような気もしますが、数学的でやはりとっつきにくいイメージも大きいかと思いました。結局は「 関数型言語 を知りたければ 関数型言語 で書くべし!」なんでしょうね。。 関数型言語 と仲良くなれるようにがんばります!
アバター
この記事は Enigmo Advent Calendar 2018の23日目 です こんにちは。 iOS チームでエンジニアをやっています。 Codable使ってますか? iOS チームでは、 Alamofire + Codable で ネットワークまわりの実装を行なっています。 最初はいいのかわからなかったのですが、今ではなくてはならないものになっています。 すごく便利すぎて、Codable無しじゃ開発できない!そんな生活を送っています。 Codableについて軽く説明からの、実際使ってみて、Codableの得意なところと苦手なところを書いていこうと思います。 TL;DR Codable良いから使ってみて! Codableになれると、 Enum をたくさん使うようになる Codableとは Swift4からFoundationに追加されたtypealiasです。 ( Codableは、 プロトコル ではありません ) Encodable と Decodable の二つ プロトコル に準拠します。 https://developer.apple.com/documentation/swift/codable Codable、何に使うの? JSON を扱う際の、 エンコード / デコード を Codableを使い、簡易に表現します どうやって使えばいいの? 例えば itunes . apple .com から取得できる JSON から今リリースしているアプリのVersion知りたい!という時に、使えます。 JSON : { " resultCount ": 1 , " results ": [ { .... " sellerUrl ": " https://www.buyma.com ", " trackName ": " BUYMA(バイマ) - 海外ファッション通販アプリ ", " currentVersionReleaseDate ": " 2018-12-12T06:02:14Z ", " version ": " 3.3.0 ", " minimumOsVersion ": " 10.0 ", ... } ] } Codable: struct AppInfoResponse : Codable { let results : [Results] struct Results : Codable { let version : String } } すごい簡単ですね。 Codableの良さ Foundation 純正 純正なので、Swiftのバージョンが上がった際に、オンタイムでアップデートされている サードパーティ のライブラリの場合、そのライブラリのアップデート対応が終わるまで Xcode /Swiftのバージョンを上げれない JSONDecoderのカスタムもいける API のレスポンス内のデータで、Date / DateTimeクラス が 2018-11-4 23:59 や 2018-06-24T23:59:59+09:00 のように揃っていないケースがあります JSONDecoder の dateDecodingStrategy を .custom にすることで、様々なケースのFormatに対応することができます decoder.dateDecodingStrategy = .custom { let container = try $0 .singleValueContainer() let string = try container.decode(String. self ) let formatter = DateFormatter() /// Date format: ISO_8601 formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZZZZZ" if let date = formatter.date(from : string ) { return date } formatter.dateFormat = "yyyy-MM-dd HH:mm" if let date = formatter.date(from : string ) { return date } return Date() } Codableの苦手なところ nil で返さず、空文字で返すと失敗する URLがあるけど、URLになっていない struct UserResponse : Codable { let name : String let imageUrl : URL ? enum CodingKeys : String , CodingKey { case name case imageUrl = "image_url" } } 成功: { " name ": " P ", " image_url ": " https://www.buyma.com/image " } </ pre > 失敗: < pre > { " name ": " P ", " image_url ": "" // 空文字 } 対応策 image_url を 空文字ではなく、 nil にしてもらう init(from decoder: Decoder) を実装する e.g.: init (from decoder : Decoder ) throws { let container = try decoder.container(keyedBy : CodingKeys.self ) name = try container.decode(String. self , forKey : .name) imageUrl = try? container.decode(URL. self , forKey : .imageUrl) } Arrayの中に、様々な Classが混ざっているケース "topics" の各Objectの "type" を見ないといけない: { " topics ": [ { " type ": " sale ", " title ": " 夏のセール開催中! ", " products ": [ ] } , { " type ": " news ", " title ": " 夏のセール開催中! ", " image_url ": " https://www.buyma.com/image ", " link ": " https://www.buyma.com/ " } , { " type ": " topic ", " title ": " 韓国ブランド集めました! ", " search_url ": " https://www.buyma.com/search " } ] } 対応策 type 見るCodableを使って、一度どの type になるのかを判定してから再度デコードする チームでCodableをどうやって使っているか 開発フロー API を cURL で叩いて json を取得 Codable準拠したstruct Responseを作成する cURL で取得した json ファイルを使用してstruct Responseにデコードされるかテストを実装する サンプルコード ネットワーククライアントのデコード処理: func decode (_ type : T.Type , from data : Data ) -& gt; T? { do { return try decoder.decode(type, from : data ) } catch { print( "---- API Parse Error ---" ) print(String(bytes : data , encoding : .utf8) ?? "" ) print( "Error Description: \(error) " ) return nil } } Codableができているかどうかのテスト: class ResponseTests : TestCase { func testDecodeResponse () { guard let path = Bundle( for : type (of : self )).path(forResource : "ResponseSample/sample" , ofType : "json" ), let fileHandle = FileHandle(forReadingAtPath : path ) else { fatalError() } guard let response = decoder.decode(SampleResponse. self , from : fileHandle.readDataToEndOfFile ()) else { fatalError() } XCTAssertNotNil(response) } } まとめ Codableにすることで、 CodingKey に準拠した CodingKeys を書かないといけない手間はありますが、それを書いてもメリットが大きいです 苦手はありますが、 API 設計に起因する部分が多いと思うので、チームで API を相談する際に、何が苦手なのかを伝えるといいと思います SwiftyJson, ObjectMapperと使っていましたが、ほぼCodableに移せました
アバター
この記事は Enigmo Advent Calendar 2018の22日目 です。 はじめに https://atarimae.biz/archives/7374 交番と犯罪件数が正の相関があるからといって、交番を減らして犯罪件数は減らないですよね。 さて、データ分析を行う上では、相関関係と因果関係を切り分けることが重要になることがあります。 例えば、KPIとある数値xが相関しているとします。 x → KPI という因果関係であれば、xの操作でKPI向上の施策を検討することができます。 逆に、KPI → x という因果関係であれば、xを操作してもKPIは変化しません。 y → x なのか、x → y なのか、xとyの相関関係の有無だけでは、因果関係は分かりません。 この記事では、 機械学習 ブロフェッショナルシリーズ、 統計的因果探索 を参考にしています。 なお、、z → x , z → y という未観測の共通原因(交絡因子とも呼ばれる)zが存在する場合についても、書籍では扱われていますが、今回の記事では未観測の共通原因については扱わないことにさせていただきます。 LiNGAM , の2変数の関係bを以下のように構造方程式とう呼ばれるモデルであると仮定します。 , の2変数で現れ、 , は外生変数がノイズ項です。 なお、LiNGAMでは非 ガウス分布 として扱います。 因果関係の推定の結果、 -> という因果関係がある場合は、 となり下記のようになります。 ここで、LiNGAMは以下の方法があります (1)独立成分によるによるアプローチ (2)回帰分析と独立性によるアプローチ 独立成分によるによる推定 既に Python で実装している方がいらっしゃるので、このアプローチに関してはそちらを参考にさせていただきます。 https://github.com/ragAgar/LiNGAM モジュールとともに、lingam.pyをimportします。 import pandas as pd import numpy as np from lingam import LiNGAM scikit-learn付属のボストン不動産デー タセット を使います。 価格をyにするのが自然ですが、敢えて 価格:x 部屋数:y とします。 boston = load_boston() df = pd.DataFrame(boston.data, columns=boston.feature_names) x = boston.target y = df.RM.values 推定を実行します。 data = pd.DataFrame(np.asarray([x, y]).T, columns=[ 'target' , 'rooms' ]) lg = LiNGAM() lg.fit(data) 推定結果 rooms ---| 9.102 |---> target 部屋数が上がると、価格が上がる。という正しそうな因果関係を推定することができました。 回帰分析と独立性によるアプローチ 本を Python で実装しました。ここでは、因果の向きのみの推定を行います。 なお、 エントロピー は近似であることが本では述べられており、近似式の導出については、参考文献を読む必要があります。 def calc_r (x, y): return ((x - np.mean(x * y) - np.mean(x)*np.mean(y)) / np.var(y) * y, (y - np.mean(x * y) - np.mean(x)*np.mean(y)) / np.var(x) * x) def normalize (x): return (x - np.mean(x)) / np.std(x) def entropy (u): Hv = ( 1 + np.log( 2 * np.pi)) / 2 k1 = 79.047 k2 = 7.4129 gamma = 0.37457 return Hv - k1*(np.mean(np.log(np.cosh(u))) - gamma)** 2 - k2 * (np.mean(u * np.exp(- 1 * u** 2 / 2 )))** 2 def lingam_reg (x, y, columns): xr, yr = calc_r(x, y) m = entropy(normalize(x)) + entropy(normalize(xr) / np.std(xr)) \ - entropy(normalize(y)) - entropy(normalize(yr) / np.std(yr)) if m >= 0 : print ( '{0} ---> {1}' .format(columns[ 0 ], columns[ 1 ])) else : print ( '{0} ---> {1}' .format(columns[ 1 ], columns[ 0 ])) 推定を実行します。 data = pd.DataFrame(np.asarray([x, y]).T, columns=[ 'target' , 'rooms' ]) lg = LiNGAM() lg.fit(data) 推定結果 rooms ---> target こちらも正しそうな推定結果を偉ました。 部屋数以外の変数でもやってみる INDUS: 小売業以外の商業が占める面積の割合 y = df.INDUS X = pd.DataFrame(np.asarray([x, y]).T, columns=[ 'target' , 'INDUS' ]) lg = LiNGAM() lg.fit(X) lingam_reg(x, y, columns=[ 'target' , 'INDUS' ]) 推定結果 INDUS ---|- 0.648 |---> target INDUS ---> target 小売業以外の商業、つまりオフィスの面積の割合が増えると、不動産価値は上がる。正しい気がする。 税率 y = df.TAX X = pd.DataFrame(np.asarray([x, y]).T, columns=[ 'target' , 'TAX' ]) lg = LiNGAM() lg.fit(X) lingam_reg(x, y, columns=[ 'target' , 'TAX' ]) 推定結果 target ---|- 8.586 |---> TAX target ---> TAX 不動産価値が高いと、税率が上がる。今までは逆の因果関係だが、この場合は正しそう。 最後に ごく簡単な例ですが、統計的因果探索であるLiNGAMを体験することができました。 前述の構造方程式の通り、変数間の関係が線形である等、仮定があることは注意しなければいけませんが、あくまで因果関係の向きを推定する上では、その仮定の上でも議論が進められると思います。 また、こちらの取り組みについても興味深いです。 https://qiita.com/MorinibuTakeshi/items/402cb905e70655724d35 未観測の共通要因については今回割愛してしまいましたが、LiNGAMは因果推論という枠組みの一つの取り組みであり、傾向スコアやグレンジャー因果性といった取り組みについても今後学習を進めていきたいと思います。
アバター
エニグモ の  @takurokamiyoshi です。 この記事は Enigmo Advent Calendar 2018 の21日目の記事です。 私は主にフロントエンド周りの実装や ディレクション 業務を行っています。 弊社ではファッション ECサイト である BUYMA を運営しており、 利用者とのコミュニケーションツールとしてメールやアプリ、そしてLINEも重要なツールだと考えています。 BUYMA LINEアカウントについて https://www.buyma.com/contents/line/ これからLINEアカウントの友だち数も増やして、LINEアカウント内でいろんな施策を実施したいと思っています。 LINE Messaging API とは? LINE社が提供するLINEカウントを通じて、ユーザとの双方向コミュニケーションを実現する API です。 メール施策で実施しているシナリオ配信のような配信に利用にしたり、ボットとして活用しているLINEアカウントも多いようです。 ※とりあえずLINE Messaging API 試してみたい人は無料で試せるLINE Developer Trialプランがあります。LINEアカウントを作成いただいて curl でLINE Messaging API をコールしてもらえればと思います。 Flex Messageとは? LINE Messaging API ではテキスト、画像、動画、カ ルーセル といった様々なメッセージタイプでメッセージを配信することができます。 Flex Messageは複数の要素を組み合わせてレイアウトを自由にカスタマイズできるメッセージです。 html、 css に近い感覚でメッセージレイアウトを考えることができます。 Simulator Flex Message Simulator LINE社が提供しているものになります。 Simulatorなしでゼロから作成するのはかなり困難で Flex Message Simulatorではサンプルがいくつか準備されているものでこれをベースに考えると良いと思います。 Flex Messageはレイアウトの自由度が高いのでいろいろ検討が必要だと思い今回の記事を作成しようと思いました。 レイアウト案 いくつかの案を記載していきます。 ※ curl の{XXX} の部分は各環境で置き換えてください。 ※ curl は長くなったので一部改行、空白なしにしています。 縦積みパターン とりあえず何かに使えそうだと思います。 curl -v -X POST https://api.line.me/v2/bot/message/push \ -H ' Content-Type:application/json ' \ -H ' Authorization: Bearer {channel access token} ' \ -d ' { "to": "{userId}", "messages":[ { "type": "flex", "altText": "this is a flex message", "contents": {"type":"bubble","body":{"type":"box","layout":"vertical","spacing":"none","margin":"none","contents":[{"type":"image","url":"{url}","aspectMode":"cover","aspectRatio":"5:2","size":"5xl","action":{"type":"uri","uri":"{uri}"}},{"type":"image","url":"{url}","aspectMode":"cover","aspectRatio":"5:2","margin":"sm","size":"5xl","action":{"type":"uri","uri":"{uri}"}},{"type":"image","url":"{url}","aspectMode":"cover","aspectRatio":"5:2","margin":"sm","size":"5xl","action":{"type":"uri","uri":"{uri}"}},{"type":"image","url":"{url}","aspectMode":"cover","aspectRatio":"5:2","margin":"sm","size":"5xl","action":{"type":"uri","uri":"{uri}"}},{"type":"image","url":"{url}","aspectMode":"cover","aspectRatio":"5:2","margin":"sm","size":"5xl","action":{"type":"uri","uri":"{uri}"}}]}} } ] } ' 商品一覧パターン スマホ サイトの商品一覧のような形のレイアウトです。 もっと長くしてもよいかなとも思いました。 curl -v -X POST https://api.line.me/v2/bot/message/push \ -H ' Content-Type:application/json ' \ -H ' Authorization: Bearer {channel access token} ' \ -d ' { "to": "{userId}", "messages":[ { "type": "flex", "altText": "this is a flex message", "contents": {"type":"bubble","hero":{"type":"image","url":"{url}","size":"full","aspectRatio":"10:3","aspectMode":"cover","action":{"type":"uri","uri":"{uri}"}},"body":{"type":"box","layout":"horizontal","spacing":"md","contents":[{"type":"box","layout":"vertical","flex":1,"contents":[{"type":"image","url":"{url}","aspectMode":"cover","aspectRatio":"1:1","size":"xxl","action":{"type":"uri","uri":"{uri}"}},{"type":"image","url":"{url}","aspectMode":"cover","aspectRatio":"1:1","margin":"md","size":"xxl","action":{"type":"uri","uri":"{uri}"}},{"type":"image","url":"{url}","aspectMode":"cover","aspectRatio":"1:1","margin":"md","size":"xxl","action":{"type":"uri","uri":"{uri}"}}]},{"type":"box","layout":"vertical","flex":1,"contents":[{"type":"image","url":"{url}","aspectMode":"cover","aspectRatio":"1:1","size":"xxl","gravity":"bottom","action":{"type":"uri","uri":"{uri}"}},{"type":"image","url":"{url}","aspectMode":"cover","aspectRatio":"1:1","margin":"md","size":"xxl","action":{"type":"uri","uri":"{uri}"}},{"type":"image","url":"{url}","aspectMode":"cover","aspectRatio":"1:1","margin":"md","size":"xxl","action":{"type":"uri","uri":"{uri}"}}]}]},"footer":{"type":"box","layout":"horizontal","contents":[{"type":"button","action":{"type":"uri","label":"More","uri":"{uri}"}}]}} } ] } ' 細いメッセージパターン 意外と需要がありそうです。 リッチメニューを出しているとメッセージが見える範囲が狭くなりますし、画像作成は必要ありません。 curl -v -X POST https://api.line.me/v2/bot/message/push \ -H ' Content-Type:application/json ' \ -H ' Authorization: Bearer {channel access token} ' \ -d ' { "to": "{userId}", "messages":[ { "type": "flex", "altText": "this is a flex message", "contents": {"type":"bubble","body":{"type":"box","layout":"horizontal","contents":[{"type":"button","style":"primary","action":{"type":"uri","label":"リンク1","uri":"{uri}"}},{"type":"button","style":"secondary","action":{"type":"uri","label":"リンク2","uri":"{uri}"}}]}} } ] } ' まとめ・感想 自由度が高いので細かいこだわりを反映できるメッセージですがやはり複雑なものを考えるのは難しいです。 Flex Messageをゼロから作成するのは結構大変なので、他の人が作成したものを共有していけるようにしていければ良いなと思いました。 ざっと触ってみてちょっとだけ慣れてきました。慣れてくれば、Simulatorなしでも作成できるようになるのだろうか。。 また以前は マルチキャスト /v2/bot/message/multicast で Flex Messageを配信できなかったですが使用できるようになったので大量配信にも使えそうです。 今回調べたことを実際の施策の中で活かしていきたいと思います。 参考 LINE Developers ドキュメント
アバター
この記事は Enigmo Advent Calendar 2018 の 20日 目の記事です。 概要 GCP (GKE)を利用してログからユーザ属性を 機械学習 により予測し、 その結果を API で返却するシステムを構築しました。 運用していく上でCI/CDツールは何にしようかなぁというところで、 今後社内でGitlabを使っていくという流れがあったのと GKEとの相性も良さそうということでGitlab CIを利用することにしました。 1 今回はGitlabと GCP (GKE)環境を連携する方法と、 Gitlab CIのキモとなる.gitlab-ci.ymlの内容についてまとめたいと思います。 システム構成 簡単にデプロイ対象のシステム構成を紹介します。 赤い枠で示したものが今回の作業対象になります。 環境条件 今回は下記の環境を使用しています。 Gitlabについては毎月バージョンが更新されるので、旧バージョンを利用されている方などは若干UI等が変わっているかもしれません。 Gitlab GitLab Community Edition 11.5.3 GKE クラスタ バージョン 1.10.9-gke.5 CI/CDのフロー CI/CDは下記のフローで行います。 テスト環境 developブランチへのcommit&push 以降は自動 dockerイメージのbuild dockerイメージをGCSへpush GKE(CronJon)へのデプロイ GKE(Deployment)へのデプロイ 本番環境 本番はすべて手動 developブランチからmasterブランチへのMR作成 マージ GKE(CronJon)へのデプロイ GKE(Deployment)へのデプロイ 設定手順 Gitlab CIを利用するにあたり必要な設定を行っていきます。 サービスアカウント作成 GCP (GKE)とGitlab CIとの連携のために GCP コンソールからサービスアカウントを作成します。 ここでは仮に「Gitlab CI」という名前のサービスアカウントを作成します。 役割(role)に下記の2つを付与してください。 Kubernetes Engine 開発者 ストレージ管理者 キーのタイプで JSON を選択し、キーを作成し、ローカルにキーファイルをdownloadします。 変数の設定 GKEへの認証情報をGitlabに登録します。 対象のプロジェクト( リポジトリ )のSettings -> CI/CD -> 変数にて、 キーに「SERVICE_ACCOUNT_KEY」を入力し、 値に先ほどdownloadしたキーファイルの中身をそのままコピペします。 下記の例では「SERVICE_ACCOUNT_KEY_PROD」というキーもありますが、 これは本番とテストでプロジェクトを分けているためです 1つのプロジェクト配下の場合は1つのキーで問題ないと思います。 Specific Runnersの登録 下記コマンドにてSpecific Runnerを登録します。 今回はdocker in docker方式で行います。 shell executorを利用する場合は こちら を参考にしてください。 $ sudo gitlab-runner register -n \ > --url [ URL ] \ > --registration-token [ TOKEN ] \ > --executor docker \ > --description " docker-runner " \ > --tag-list " gke " \ > --docker-image " docker:stable " \ > --docker-privileged ※指定するURLとTOKENについてはGitlabのSettings -> CI/CD -> Runnerの項目を参照してください。 今回はオンプレ版のGitlab環境を使用していますが、 gitlab.com でShared Runnerを利用する場合には こちらの手順は不要です。 .gitlab-ci.ymlの作成 今回はCronjobへのデプロイ定義を例に説明します。 Deploymentへのデプロイ定義も基本的に同じです。 共通設定 テスト/本番環境など、デプロイ対象の環境が複数ある場合にはどうしても定義が冗長的になります。 それを解消するために、下記では YAML のアンカー/ エイリアス とGitlabのextends(v11.3で追加)という機能を利用して、共通的な定義はまとめるようにしています。 .auth_gke : &auth_gke | gcloud auth activate-service-account --key-file=key.json gcloud config set project ${PRJ_ID} gcloud config set container/cluster ${CLUSTER_NAME} gcloud config set compute/zone ${CLUSTER_ZONE} gcloud container clusters get-credentials ${CLUSTER_NAME} --zone ${CLUSTER_ZONE} .setting_dev : environment : develop variables : CI_DEBUG_TRACE : 'true' PRJ_ID : 'XXXXX-dev' CLUSTER_NAME : 'dev-XXXX' CLUSTER_ZONE : 'asia-northeast1-a' GCS_KEY : 'XXXX.json' ENV_CONFIG : '/PATH/conf/settings_dev.json' .setting_prod : environment : production variables : PRJ_ID : 'XXXXX-prod' CLUSTER_NAME : 'XXXXX' CLUSTER_ZONE : 'asia-northeast1-a' GCS_KEY : 'XXXX.json' ENV_CONFIG : '/PATH/conf/settings_prod.json' services : - docker:dind stages : - build - deploy ジョブ定義① ここではdockerイメージのbuildと、GCRへのコンテナイメージのpushを行っています。 extendsで先程定義した内容を継承しています。 only:changesで該当のファイルに変更があった場合のみジョブが実行されるようになります docker build_base : extends : .setting_dev image : 'docker:stable' stage : build tags : - gke script : - docker info # Build the image - docker build --cache-from ${IMAGE_TAG} -t ${IMAGE_TAG}:${IMAGE_VER} . # Log in to Google Container Registry - echo "$SERVICE_ACCOUNT_KEY" > key.json # - docker login -u _json_key --password-stdin https://asia.gcr.io < key.json - docker login -u _json_key -p "$SERVICE_ACCOUNT_KEY" https://asia.gcr.io # Push the image - docker push ${IMAGE_TAG}:${IMAGE_VER} only : refs : - develop changes : - Dockerfile - bin/* - conf/* - requirements.txt ジョブ定義② ここではテスト/本番環境に対する2つのCronJobリソースへのデプロイを行います。 本番環境へはwhen: manualを定義して手動でデプロイするようにしています。 deploy_model_builder_dev : extends : .setting_dev image : 'claranet/gcloud-kubectl-docker:1.2.2' stage : deploy tags : - gke script : # Authenticate with GKE - echo "$SERVICE_ACCOUNT_KEY" > key.json - *auth_gke # set up CronJob - cat kubernetes/XXXX-job.yaml | envsubst | kubectl apply -f - only : refs : - develop changes : - .gitlab-ci.yml - kubernetes/XXXX-job.yaml deploy_bulk_judgement_dev : extends : .setting_dev image : 'claranet/gcloud-kubectl-docker:1.2.2' stage : deploy tags : - gke script : - echo "$SERVICE_ACCOUNT_KEY" > key.json - *auth_gke # set up CronJob - cat kubernetes/XXXX-job.yaml | envsubst | kubectl apply -f - only : refs : - develop changes : - .gitlab-ci.yml - kubernetes/XXXX-job.yaml deploy_model_builder_prod : extends : .setting_prod image : 'claranet/gcloud-kubectl-docker:1.2.2' stage : deploy tags : - gke script : # Authenticate with GKE - echo "$SERVICE_ACCOUNT_KEY_PROD" > key.json - *auth_gke # set up CronJob - cat kubernetes/XXXX-job.yaml | envsubst | kubectl apply -f - only : - master when : manual deploy_bulk_judgement_prod : extends : .setting_prod image : 'claranet/gcloud-kubectl-docker:1.2.2' stage : deploy tags : - gke script : # Authenticate with GKE - echo "$SERVICE_ACCOUNT_KEY_PROD" > key.json - *auth_gke # set up CronJob - cat kubernetes/XXXX-job.yaml | envsubst | kubectl apply -f - only : - master when : manual ロールバック せっかくなので ロールバック の手順についても記載しておきます。 Gitlabの左メニューから運用 -> 環境とたどっていくと、下記のような画面が表示されます。 上記で記載した.gitlab-ci.ymlにenvironmentという定義があると思いますが、 そこで定義した文字列が環境のところに表示されているのがわかると思います。 たとえばここでproductionをクリックします。 すると下記の画面に遷移します。 この一覧はproductionに対するコミットの一覧です。 たとえば直前のコミットに ロールバック したい場合は赤枠のところをクリックすればOKです。 もちろん、この画面から任意のコミットを選択して再デプロイすることも可能です。 environmentの定義があることで環境毎のコミット履歴が一覧で見れるかつ、 そこからのデプロイ、 ロールバック も簡単に行えます。 複数環境がある場合にはぜひ設定することをおすすめします。 まとめ 今回、Gitlab CIを初めて利用してみましたが、特に癖もなく使いやすかったです。 Auto DevOpsや、先日発表されたGitLab Serverlessなど、まだまだアツい機能があるので、時間を見つけて検証してみようと思います。 当初はGitlabのAuto Devopsの利用を考えていましたが、検討時点では利用しているGitlabのバージョンが古かったため、ひとまずGitlab CIを利用することにしました。  ↩
アバター
この記事は Enigmo Advent Calendar 2018 の19日目の記事です。 はじめに ネタ何にしようかなぁと思って、 カジュアルな感じでかつ単発で終わるようなもの 検索、 自然言語処理 関連で何か 年末的な何か ということを踏まえて、 Qiitaチームに日々挙げている自分の作業日報を可視化して2018年の振り返りをしてみることにしました。 私が エニグモ に入社したのが今年の2月なので、正確には1年ではないですが、 細かいことは気にせずいきたいと思います。 作業フロー 作業フローとしては、 Qiita API v2を利用して自分の作業日報の本文と作成日時を取得 取得した本文を 形態素解析 器にかけてキーワードのみを抽出 抽出したキーワードをword cloudを利用して可視化 可視化したものを見て思い出に浸る という感じで進めようと思います。 Qiita API で日報の本文を取得 アクセス トーク ンの発行 Qiitaチームからデータを取得するためアクセス トーク ンを発行します 今回は ユーザの管理画面 から発行しました。 API の詳細については 公式ページ に色々と記載があります アクセス トーク ンの利用 取得したアクセス トーク ンはAuthorizationリク エス トヘッダに指定して利用します。 今回は python で実装しましたが、 curl の場合は下記のような感じで取得できます。 $ curl https://qiita.com/api/v2/authenticated_user/items \ -H " Authorization: Bearer [アクセストークン] " 下記のメソッドを定義して日報データを取得します API を利用して20件ずつデータを取得 取得したデータはDataframeに突っ込み、不要なデータは取り除く 取得した作成日時をDatetimeIndexに設定して、月単位でデータが扱えるようにしておく def get_report (): BASE_URL = 'https://XXXX.qiita.com/api/v2/items?' PER_PAGE = 20 # tagで絞り込み QUERY = 'tag%3A%E6%97%A5%E5%A0%B1%2F%E4%BC%8A%E8%97%A4%E6%98%8E%E5%A4%A7' curr_page = 1 with open ( 'qiita_access_token.txt' ) as f: qiita_access_token = f.read().strip() header = { 'Authorization' : 'Bearer {}' .format(qiita_access_token)} # レスポンスヘッダを見てページング処理 df_list = [] total_cnt = 0 while ( True ): target_url = '{0}page={1}&per_page={2}&query={3}' .format(BASE_URL, curr_page, PER_PAGE, QUERY) req = urllib.request.Request(target_url, headers=header) res = urllib.request.urlopen(req) if curr_page == 1 : # ヘッダから全投稿数を取得 total_cnt = int (res.info()[ 'Total-Count' ]) # 最終ページの計算 if total_cnt % PER_PAGE == 0 : last_page = total_cnt // PER_PAGE else : last_page = total_cnt // PER_PAGE + 1 res_body = res.read() res_dict = json.loads(res_body.decode( 'utf-8' )) # dataframeに変換 df_list.append(pd.io.json.json_normalize(res_dict)) # 最終ページチェック if curr_page == last_page: break curr_page+= 1 # df_listを結合 report_df = pd.concat(df_list, ignore_index= True ) # 必要なデータのみ抽出 report_df = report_df[[ 'body' , 'created_at' ]] # created_atの文字列をdate型に変換 report_df[ 'created_at' ] = pd.to_datetime(report_df[ 'created_at' ]) # DatetimeIndexに変換 report_df.set_index( 'created_at' , inplace= True ) return report_df 形態素解析 器でtokenize&前処理 janome (辞書はneologd)を利用し、前処理として下記を実施します。 unicode 正規化 改行コード削除 大文字→小文字に統一 名詞のみ抽出(数字は対象外) def analyze (): # charfilter ## unicode正規化 ## 改行コード削除 charfilters = [UnicodeNormalizeCharFilter(), RegexReplaceCharFilter( u' \r\n ' , u'' )] # tokenizer tokenizer = Tokenizer(mmap= True ) # tokenfilter ## 大文字→小文字 ## 名詞のみ抽出(数は除去) tokenfilters = [POSKeepFilter( '名詞' ), POSStopFilter([ '名詞,数' ]), LowerCaseFilter()] # analyzer analyzer = Analyzer(char_filters=charfilters, tokenizer=tokenizer, token_filters=tokenfilters) return analyzer def get_word_list (report_list, analyzer): word_list = [] for report in report_list: word_list.append( " " .join( analyzed_line.base_form for analyzed_line in analyzer.analyze(report))) return word_list 可視化処理 word cloudを利用して可視化するためのメソッドを定義します def draw_wc (vecs_dic, fig_title): font_path = '/System/Library/Fonts/ヒラギノ角ゴシック W3.ttc' wordcloud = WordCloud(background_color= 'white' , font_path = font_path, min_font_size= 15 , max_font_size= 200 , width= 1000 , height= 1000 ) wordcloud.generate_from_frequencies(vecs_dic) plt.figure(figsize=[ 20 , 20 ]) plt.imshow(wordcloud,interpolation= 'bilinear' ) plt.axis( "off" ) plt.title(fig_title,fontsize= 25 ) plt.show() 指定した月の日報を可視化する では実際にやっていきます。 まず、私が入社した月の日報を見てみましょう。 1年間分の日報データを取得 df_reports = get_report() 可視化したい月を指定 target_month = '2018-02' 月の日報をまとめて 形態素解析 でtokenize my_analyzer = analyze() words = get_word_list(df_reports[target_month].body.tolist(), my_analyzer) TF-IDFで各単語を重み付け vectorizer = TfidfVectorizer(use_idf= True , token_pattern= u'(?u) \\ b \\ w+ \\ b' ) vecs = vectorizer.fit_transform(words) words_vectornumber = {} for k,v in sorted (vectorizer.vocabulary_.items(), key= lambda x:x[ 1 ]): words_vectornumber[v] = k vecs_array = vecs.toarray() all_reports = [] vecs_dic = {} for vec in vecs_array: words_report = [] vector_report = [] for i in vec.nonzero()[ 0 ]: vecs_dic[words_vectornumber[i]] = vec[i] 可視化 draw_wc(vecs_dic, target_month) 「入社」というキーワードでフレッシュ感がほのかに漂っています。 その他でみると「性能」、「分析」、「検証」、「測定」とかが大きくでてますね。 そういえば、この月は検索システムの性能改善をメインでやっていた気がします。 続いて6月の日報を見てみます。 「韓国」と大きく出てますが、これな何でしょうかね。全く覚えがありません(笑) 「chef」、「レシピ」、「zero」とかはこのあたりでchef zeroで何かの検証をしていたんでしょう。きっと。。 「パーソナライズド」、「fasttext」があるので、この時期あたりからパーソナライズサーチの検証をしてたんだと思います。 最後に11月 他人の日報なんて誰も興味ないと思うので、これで最後にします。 せっかくなのでクリスマスっぽくしてみました。 先月の内容なので、特に振り返ることもありませんが、 Gitlab CI周りの調査/検証や API の開発をメインで実施していた時期でした。 おわりに 今回初めてword cloudを使ってみましたが、 視覚的にデータを見るのって新たな気付きがあったりして楽しいですね。 来年はフォントとか画像をもうちょっとオサレな感じにして日報Tシャツ作ってヘビロテしようと思います。
アバター