TECH PLAY

株式会社エニグモ

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

247

こんにちは。サーバサイドエンジニアの伊藤です。 Enigmo Advent Calendar 2019 、21日目の記事です。 先週末の12月14日(土)、 平成Ruby会議01 に登壇し、「Play with Ruby 」という題で発表してきました。 タイトルからはわかりにくいのですが、 parse.y をライブコーディングで操作し、雑な感じに右代入を実装するという話です。 TL;DR 内容 発表に至るまで 平成Ruby会議01 当日 トークセッション 発表 懇親会 最後に TL;DR とっても楽しかったです。 内容 RubyKaigi2019 で聴いた 「Play with local vars」by Tatsuhiro Ujihisa/@ujm の トーク に影響を受けて、右代入を実装した話です。 詳しくはこちらのスライドを御覧ください。 speakerdeck.com 発表に至るまで 実は今回が初めての人前での発表でした。 2019 年中に人前で発表することが1つの目標だったので、とりあえず CfP *1 を出してみました。 CfP を出した時からずーっと気が休まる暇がなかったように思います。CfP が採択されるまでは心のどこかで却下されることを祈っていましたし、採択されてからは発表当日インフルエンザにでもなってくれないかと思っていたほどです。。。 結果的には有難いことに採択され、幸運なことにインフルエンザにもならず、2019 年中に人前で発表するという目標を達成することができました。 ちなみに、CfP を提出した時点では parse.y をまともに読んだこともありませんでした。右代入の具体的な実装などは皆目見当がついておりませんでした。完全な勢いです。。。 CfP Advent Calendar 2019 というものを発見しましたので、 CfP の内容はこちらに公開してあります。 sean0628.hatenablog.com こちらを見ていただけるとわかると思うのですが、右代入を実装することはこの時点で決めていました。 ただ、右代入の実装方法が思いつかない最悪のケースが頭をよぎり、チキリにチキリきった結果、発表のタイトルを「Play with right-assignment」ではなく「Play with Ruby 」としてしまいました。 わかりにくいタイトルになってしまいすみませんでした。。。 平成 Ruby 会議01 当日 トーク セッション いろいろな角度からの発表があり、とても刺激的でした。正直、自分の発表のことで頭がいっぱいで、全体の半分くらいしか発表を見ることはできませんでした。。。 聴講だけで、もう一度参加したいくらいです。 個人的に1番印象に残っているのは、金子さんのキーノート 「What is expected?」 です。 内容はパーサーの話でした。ちょうど1ヶ月前に聞きたかったです。かなり深いところまで解説されていて、理解できなかったところもありました。時間を見つけて復習したいと思います。 発表 さて、自分の発表ですがみなさまのおかげで全体的にはスムーズに進めることができたかなと思っております。 今回初めてライブコーディングにも挑戦しました。 スライドのメモ確認のために敢えて ミラーリング してなかったのですが、スクリーンの角度が急すぎてエディターが何も見えないというハプニングが発生しました。 さらには、ライブコーディング中の1番緊張していたタイミングで、 Siri が起動するという奇跡的なトラブルにも見舞われました。 (今年1番 、いや人生の中で1番 Siri に怒りを覚えました。) そんなトラブルもありましたが、発表を聴きに来てくださった方々が暖かく見守ってくださったお陰でなんとか最後まで辿り着くことができました。 ありがとうございました。 懇親会 懇親会では Ruby や Rails のコミッタさんやコントリビュータさんから、 OSS との関わり方や RubyKaigi の裏話など貴重なお話を伺うことができました。 また、キーノートスピーカーの金子さんからは、右代入の実装に関して具体的なアド バイス をいただきました。 今回いただいたアド バイス をもとに、また Play したいと思います。 最後に 運営・スポンサー・参加者の皆さん、ありがとうございました。 みなさまのおかげで素敵な1日を過ごすことができました。 株式会社 エニグモ 正社員の求人一覧 hrmos.co *1 : Call for Proposal
こんにちは! 冬が苦手なディレクターの神吉です。 この記事は Enigmo Advent Calendar 2019 の 20日 目の記事です。 LINEの開発者情報をチェックしていて、ちょっと前にLIFF v2がリリースされていました。 https://developers.line.biz/ja/docs/liff/release-notes/#spy-releasedate_20191016 LIFFとは LIFFとはLINE Front-end Frameworkのことで、LINEが提供するウェブアプリのプラットフォームです。 JavaScript を書いて開発する感じです。 LIFFはけっこう前から提供されていましたが LIFF v1 → LIFF v2になって主に下記ができるようになりました。 外部ブラウザでLIFFアプリが動作する。 v1はLINE内ブラウザでのみ動作していました。 ユーザーのプロフィール情報とメールアドレスを取得できる。 LINEログインでできる仕組みに近い仕組みなのかなと思います。 QRコード を読み取れる。 新機能ですね! LIFFアプリの動作環境を細かく取得できる。 外部ブラウザで動作するようになって細かい情報を取得する必要がでてきたのかと思います。 LIFF v1もちゃんと触っていなかったのですがこの機会にv2を触ってみたいと思います。 とりあえずLIFFで QRコード を読み取りをやってみます。 事前準備 LINE ログイン を利用できるLINEアカウント ※Messaging API チャネルではないほうが良いです。 https://developers.line.biz/ja/docs/liff/release-notes/#spy-releasedate_20191111 HTTPS URL対応サーバ こちらを事前に準備してください。 LIFFアプリの追加 LINE Developersコンソールよりの 「LIFFアプリを追加」より必要事項を入力してLIFFアプリの追加をしてください。 ※今回は QRコード を読み取りをするのでScan QR の設定はONにしてください。 またLIFFサーバー API でもLIFFアプリを追加することができます。 https://developers.line.biz/ja/reference/liff-v1/#add-liff-app 追加後LIFF URLが発行され、LIFFを開くことができるようになります。 LIFFアプリの開発 ここからは実際の開発手順です。 LIFF SDK を読み込み LIFF SDK https://static.line-scdn.net/liff/edge/2.1/sdk.js を読み込んでください。 <!DOCTYPE html> < html lang = "ja" > < head > < meta charset = "UTF-8" > < title > sample </ title > </ head > < body > < script src = "https://static.line-scdn.net/liff/edge/2.1/sdk.js" ></ script > </ body > </ html > LIFFアプリを初期化する sdk.js 読み込み後に追加してください。 liff .init( { liffId: "XXXXX" // liffIdを指定。line://app/XXXXXの部分 } ) .then(() => { // 処理はここから } ) . catch ((err) => { // エラー処理 console.log(err.code, err.message); } ); QRコード を読み取り処理 document .getElementById( 'qr_button' ).addEventListener( 'click' , function () { if (liff.isInClient()) { liff.scanCode().then(result => { document .getElementById( 'qr_text' ).textContent = result.value; } ). catch (err => { document .getElementById( 'qr_text' ).textContent = err.message; } ); } } ); liff.scanCode() はLINEの QRコード リーダーを起動し、読み取った文字列を取得するメソッドです。 #qr_button にクリックするとLINEの QRコード リーダーが立ち上がり、 QRコード をスキャンすると #qr_text に QRコード で読み取ったテキストが挿入されます。 注意点 iOS 版LINEバージョン9.19.0以降では、 liff.scanCode() の提供を一時停止しています。(2019/12/18時点) liff.scanCode() は、外部ブラウザでは利用できません。 どう活用する? 手軽に QRコード リーダーを使えるのは良いです。 QR の読み取りも速いです。 またLIFFはLINEのuserIdと紐づけてイベントログを取得でき、データ活用できます。 そういったところ活かしオンラインとオフラインをつなぐような施策使えればと思います。 (今のところ BUYMA での具体的な活用案が浮かばずすみません。。) 感想 かなり簡単に試すことができました! LIFFを使用する上で実行環境による挙動の違いはしっかり把握しないといけないなと感じました。 今後もLINEの開発者情報はこまめにチェックして、新機能は早めに試してみたいと思います。 参考 LINE Front-end Framework LIFF v2 APIリファレンス 株式会社 エニグモ 正社員の求人一覧 hrmos.co
インフラチームの山口です。 ゼロ年代 後半ゆるふわ情報系学生でしたが紆余曲折の末にインフラエンジニア1年目となりました。 今回は編集距離を使用して SQL のクエリを クラスタリング してみたので記事にまとめてみます。 奇しくも、 伊藤直也 さんのブログで編集距離の記事が公開されたのが2009年だったのですが、時の流れの速さを感じてしまいます。 1.背景 DBのCPU負荷のスパイク時に、DBのクエリのログを取得・人手で集計して、CPU負荷が高いクエリを改善するという運用を実施することがあります。 ログ(クエリ)の量が少ない場合は良いのですが、大きくなるにつれ、人手での集計に伴い以下のような問題が発生しています。 人手での集計には時間を要する 作業者が変わると結果が一意に決定できない場合があり、集計作業の再現性がない スクリプト に起こして作業をしようとしても、 単純な文字列一致の方法で集計を試みると、 WHERE の条件にしている id が異なっているだけといったクエリをうまく拾えないことが予想されます。 今回は、2つの文字列に対して定義される編集距離(Levenshtein Distance)を使用して、 スクリプト での結果の集計を試みます。 (「TF-IDFやNグラムを使用してやるのもちょっと……」という個人的な気持ちがあったので) 2.関連する事例 「編集距離を使用してクエリ同士の距離を測ればなんかうまくいきそうな気がする」というワンアイディアで調べもせず スクリプト の作成をしてしまったのですが既存の参考文献を調べてみます。 sql query clustering filetype:pdf などで検索してみるといくつか事例が見つかります。 詳しい書誌情報は割愛でタイトルとざっと読んでどんな事やっているかだけ記載します。 クエリの クラスタリング というタスクの論文は書かれているようです。 論文書かれているということは、おそらく他のエンジニアも同じ問題にあたった人もいるはず。 (手でクエリのログ分類して大変な思いしてるのは私だけではなさそう……。) "Similarity Metrics for SQL Query Clustering", 2018. 先行研究で提案された特徴量がまとめられている。 著者らの提案手法については、理解する時間がなかったが、クエリの クラスタリング のタスク自体の概要を知るには良さそう。 "Text Mining Applied to SQL Queries: A Case Study for the SDSS SkyServer", 2015. ↑ の文献で引用されていたもの。 クエリのTF-IDFを特徴量にしてSOMで クラスタリング 、可視化をしている。 3.方法 編集距離とは( Wikipedia 参照) 編集距離については既存で参考になる記事が複数存在するため、本記事では特に説明せず、 Wikipedia からガッツリ引用します。 端的には2つの文字列の間の距離の尺度です。 レーベンシュタイン距離(レーベンシュタインきょり、英: Levenshtein distance)は、二つの文字列がどの程度異なっているかを示す距離の一種である。 編集距離(へんしゅうきょり、英: edit distance)とも呼ばれる。 具体的には、1文字の挿入・削除・置換によって、一方の文字列をもう一方の文字列に変形するのに必要な手順の最小回数として定義される編集距離(Levenshtein distance)は2つの文字列がどの程度異なっているかを示す距離の一種。 例 kitten を sitting に変更する場合は、最低でも3回の手順が必要とされるので2単語間の編集距離は3となる。 kitten sitten(kをsに置換) sittin(eをiに置換) sitting(gを挿入して終了) 今回の方法 以下の要件を満たす、なんちゃって クラスタリング を実行する スクリプト を作成しました。 クラスタリング 結果が一意に決まらない方法は避けたい 手元のマシンですぐに結果がみたい(時間がかかる方法は避けたい) 今回の方法を説明します。 クラスタ の代表と以下で記載していますが、実際はレコード中で一番はじめに出てきたものをその クラスタ の代表にしているだけで、特に重心などをとっているわけではないです。 また、今回は、クエリの先頭からN単語分を抜き出して使用します。 これは、「人間が比較する際もクエリの先頭から末尾まで見ておらず、先頭N文字で判断しているだろうという仮定」と、「試しにクエリ全体で計算してみると思いの外時間がかかった」ためです。 今回の方法の概要 - クラスタリング対象のクエリすべてに対して以下を実行する - クラスタリング対象の1つめのクエリの場合はそのクエリをクラスタ0の代表としクラスタ0に追加する - クエリと各クラスタの代表のクエリの先頭からN単語の部分文字列との編集距離を計算 - 編集距離が閾値以下かつ、最も近いクラスタに割り当てる - 閾値以下の距離のクラスタがない場合は新しいクラスタを作成する また、編集距離を取得する際に、一旦クエリをスペースで分割して、単語単位での編集距離を取得します。 日本語だと 形態素解析 が必要ですが、雑に半角スペースで単語に分割します。 >>> import editdistance >>> q1 = 'SELECT * FROM tbl_1 WHERE id = 1;' >>> q2 = 'SELECT * FROM tbl_1 WHERE id = 50;' >>> q3 = 'UPDATE tbl_1 SET id = 40 WHERE id = 1;' >>> editdistance.eval(q1.split( ' ' ),q2.split( ' ' )) 1 >>> editdistance.eval(q1.split( ' ' ),q3.split( ' ' )) 6 >>> import Levenshtein >>> Levenshtein.distance(q1,q2) 2 >>> Levenshtein.distance(q1,q3) 22 パラメータと使用したライブラリおよび スクリプト 使用したライブラリ editdistance (0.5.3) パラメータ(パラメータは エイヤで 経験的に決定した) 同じ クラスタ と判断するための 閾値 : 10 編集距離10以下で一番近い クラスタ に所属させる 編集距離10以下の クラスタ がない場合は新しい クラスタ とする 比較に使用する単語数: 先頭から100単語 SQL の先頭100単語を切り出し編集距離を計算する スクリプト #!/usr/bin/env python3 # coding: utf8 import csv import editdistance from collections import defaultdict # ファイルを読み込む def load_file (in_fname, skip_header= False ): queries = [] with open (in_fname, newline= "" , encoding= "utf8" , errors= 'ignore' ) as f: queries = list (csv.reader(f, delimiter= " \t " , quotechar= '"' )) if skip_header: del queries[ 0 ] return queries # 結果をファイルに書き込む def write_file (out_fname, result): with open (out_fname, 'w' , newline= "" , encoding= "utf8" ) as f: writer = csv.writer(f, delimiter= " \t " ) writer.writerows(result) # 半角スペースでクエリを分割して、先頭word_len単語のlistを作る def get_substr (query, word_len): return query.split( ' ' )[ 0 :word_len] # クエリの編集距離を計算する def calc_distance (query_1, query_2): return editdistance.eval(query_1, query_2) # 編集距離を使ってクエリをグループ化する def cluster (queries, query_pos= 1 , word_len= 100 , threshold= 10 ): clusters = [] result = [] for row in queries: current_query = row[query_pos] current_query_substr = get_substr(current_query, word_len) # 1レコード目の場合は新規のクラスタに入れる if len (clusters) == 0 : clusters.append(current_query_substr) row.insert( 0 , 0 ) result.append(row) continue # 2レコード目以降 current_cluster = "" # 初期値 min_dist = float ( 'inf' ) for cluster_id, cluster_query_substr in enumerate (clusters): current_dist = calc_distance(cluster_query_substr, current_query_substr) # 閾値以下 if current_dist <= threshold: # クラスタとの距離が近ければ所属するクラスタと編集距離を更新する if current_dist <= min_dist: min_dist = current_dist current_cluster = cluster_id # 閾値以下のクラスタがなければ新規のクラスタにする if current_cluster == "" : clusters.append(current_query_substr) current_cluster = clusters.index(current_query_substr) row.insert( 0 , current_cluster) result.append(row) return clusters, result # 各クラスタのメンバー数などのサマリを出力 def cluster_summary (clustering_result, cluster_pos= 0 ): cluster_members = defaultdict( int ) for row in clustering_result: cluster_id = row[cluster_pos] cluster_members[cluster_id] += 1 # サマリ print ( "### Clustering Summary ###" ) # レコード数 print ( "#of records {}" .format( len (clustering_result))) # クラスタ数 print ( "#of clusters {}" .format( len (cluster_members.keys()))) # 要素数上位10のクラスタ print ( "### top 10 clusters ###" ) print ( "rank \t cluster_id \t #of member" ) for indx, v in enumerate ( sorted (cluster_members.items(), key= lambda x: x[ 1 ], reverse= True )): print ( "{} \t {} \t {}" .format(indx+ 1 , v[ 0 ], v[ 1 ])) if indx >= 9 : break # 各クラスタのメンバー数 print ( "### #of member in clusters ###" ) for c_id in cluster_members.keys(): print ( "{} \t {}" .format(c_id, cluster_members[c_id])) if __name__ == '__main__' : # inputfile base_fname = 'bombay_queries.tsv' base_fname_without_ext = base_fname.split( '.tsv' )[ 0 ] in_fname = "data/{}.tsv" .format(base_fname_without_ext) # parameter word_len = 100 threshold = 10 # outputfile out_fname_result = "result/{}_result_wordlen{}_threshold{}.tsv" .format(base_fname_without_ext, word_len, threshold) queries = load_file(in_fname, skip_header= True ) clusters, clustering_result = cluster(queries, query_pos= 2 , word_len=word_len, threshold=threshold) write_file(out_fname_result, clustering_result) cluster_summary(clustering_result) 4.結果 デー タセット "Similarity Metrics for SQL Query Clustering", 2018. で使用されているのと同じ データセット の中の、 bombay_queries.csv を用いました。 デー タセット には アノテーション されたラベルが付与されています。 id label query 0_1_1 1 select course.course_id,course.title from course 0_1_2 1 select course_id from course 0_1_3 1 select distinct course_id,title from course 0_1_4 1 select course_id,title from course 0_2_1 2 select course_id,title from course where dept_name='comp. sci.' 0_2_2 2 select distinct course.course_id,course.title from course where course.dept_name='comp. sci.' 0_2_3 2 select course.course_id,course.title from course where dept_name='comp. sci.' 0_2_4 2 select course_id,title from course where course.dept_name = 'comp. sci.' 0_2_5 2 select course_id,title from course where course.dept_name='comp. sci.' クラスタリング の結果と所感 クラスタリング 結果を以下に示します。 結果の評価には、 こちら の説明を参照し、PurityとInverse Purityと F値 を使用します。 purity,inverse purity, F値 は0から1の間で1に近づくほど良い値です。 purityが0.75とやや大きいですが、これは クラスタ のメンバー数1の クラスタ が多数あるためです。 参照にした資料でも説明がありますが、1 クラスタ 1レコードに クラスタリング された場合purityは1になります。 今回の結果では、 218 クラスタ 中で クラスタ のメンバー数が1の クラスタ は156個ありました。 結果を定性的に確認してうまくできていそうな箇所を無理やり見つけます。 クラスタ 108の結果を見ると意図したとおりに クラスタリング できているように見えます。 108の結果をよく見ると、同じクエリですがスペースの差異があるもの( count(*)>1 と count(*) > 1 )を同じ クラスタ にまとめられています。ただ、完全文字列一致でやっても、事前に空白除去しておけば拾える箇所なので、編集距離を使った良さとは強く言えなそうです。 また、今回使用したデー タセット はとりあえず、クエリに アノテーション されているものを見つけたので、きちんとした理由もなく使っているのですが、そもそも私が クラスタリング したい目的とデー タセット 作成時の目的があっているのかなども確認する必要がありそうです。クエリの クラスタリング がうまくできると、嬉しいものの、社内で アノテーション するメンバーを複数人集めて、ラベル付きデータ作って評価するほどのものでも無いような気もするしなという気持ちもあります。 レコード数と クラスタ 数 #of records|629 #of clusters| 218 クラスタ のメンバー数降順10 クラスタ rank cluster_id #of member 1 0 127 2 1 33 3 14 31 4 12 22 5 134 16 6 3 14 7 145 14 8 16 13 9 48 10 10 2 8 purity,inverse purity, F値 purity inverse purity F measure 0.75 0.27 0.40 クラスタ 108の結果 cluster_id label query 108 9 select student.id,count(id) from (section join takes using (course_id)) natural join student group by id,name having count(id) = 2 108 9 select id,name from section natural join takes natural join student group by id,semester,year,time_slot_id,name having count(*)>1 108 9 select id,name from section natural join takes natural join student group by id,semester,year,time_slot_id,name having count(*) > 1 5. まとめ 編集距離を使用しクエリの クラスタリング を試みました。 クラスタリング 方法や、パラメータ、テストに使用したデー タセット など、全体的にエイヤな部分が多いですが、定性的に結果をみると類似したクエリをなんとなくまとめることができたようにも思います。実業務でクエリのログの クラスタリング に使えるかはかなり怪しいのと、Datadogなどの SaaS でもログの クラスタリング はできるようなので、ローカルであえて スクリプト 準備して クラスタリング する機会はないのかなとも感じます。 かなり散漫な記事となってしまったので無理やりまとめますが、新しい仕組みがどんどん出てくるなかで、昔からのやり方と新しいやり方適材適所でうまく扱えるようになっていきたいなと思います。 あと、もっと楽に精度良くやれる方法を教えてくれる人がいれば教えてほしいです(かなり切実)。 6. 参考 編集距離 (Levenshtein Distance) レーベンシュタイン距離 python-Levenshtein editdistance jkkummerfeld/text2sql-data クラシックな機械学習の入門 8. クラスタリング 株式会社 エニグモ 正社員の求人一覧 hrmos.co
サービスエンジニアリング本部の山本です。 この記事は Enigmo Advent Calendar 2019 の 18 日目の記事です。 普段はフロントエンド中心の開発をしていますが、たまに DX(Developer Experience) 的なことにも手を出しています。 今回はそんな DX のお話です。 やばい CI エニグモ が運営している BUYMA は Ruby on Rails アプリケーションとして動いており、自動テスト フレームワーク として RSpec を採用しています。 CIツールとしては Jenkins を採用していましたが、1 年以上の期間、常に Fail しているという エニグモ のようなイケてるウェブ企業としてはあるまじき状態が続いていました。 Jenkins は素晴らしいソフトウェアですが、当時動いていた Jenkins のバージョンは 1 系かつオンプレミスサーバーで動いていたこともあり色々と問題を抱えていました。 また、 エニグモ では Git ホスティング サービスとして Gitlab を採用していて、ちょうど社内では Gitlab CI を使っていこうという流れもあったため RSpec も Gitlab CI に移行することにしました。 Jenkins 時代の課題 ローカルと CI で実行結果が違う ローカルだと通ってたのに CI だと通らない 実行するだけで 2 時間くらいかかっていた 並列実行したいがサーバーのリソースの問題で難しい ブランチごとに実行出来ないため、マージしてみないと CI 上の結果がわからない ブランチごとに実行しようとするとサーバーのリソース不足になる ローカルですべてのスペックを実行するのは非現実的なため関係ありそうな箇所だけローカルでテストしたらマージしてしまう # jenkins という slack チャンネルがありましたが、失敗しているのが当たり前になってしまっていたため誰も気にしておらず、失敗通知が虚しく響き渡っていました。( 割れ窓理論 ) どうやって解決していったか ローカルと CI で実行結果を同じにしたい! まず、ローカルと CI での実行結果が違う問題について。 これは単純に Docker を使うことにしました。 BUYMA の開発環境はもともと Vagrant を使っていて Docker 化はされていませんでした。 今回はローカルの開発環境全部を Docker 化するのではなく、一旦テスト実行環境だけを Docker 化して CI 上でも同じイメージを利用する方針としました。 Gitlab CI にはジョブを実行する Gitlab Runner というツールを利用します。 Gitlab Runner の実行方式である Executor は Shell 、 Docker などの複数のなかから選択することができますが、今回は後述の実行時間短縮を実現するために並列で実行することを考えて Docker を選択しました。 Docker 上で Docker を動かす Docker in Docker という方式があり、それ専用のイメージも用意されているのでそちらを採用しました。 詳しくは こちら 。 各ブランチのMR ( Merge Request )ごとに実行したい! Jenkins 時代は開発用メインブランチである develop ブランチにマージされた時だけテストが走るようになっていたため、 MR をマージして初めてテスト結果がわかるという状態でした。 この場合マージした後に失敗したことに気づき修正する、という流れになりますが、テスト時間が長いこともあり失敗していることに気づかず放置されてしまいます。 マージする前にテストを実行する、という当たり前のことが出来ていない状態だったのです。 これを解決するための Gitlab CI の設定はかんたんで、 .gitlab-ci.yml にこのように記述するだけです。 only : - merge_requests 問題はリソースです。 Gitlab CI への移行当初、 Gitlab Runner サーバー はオンプレミスのサーバーだったため、複数の MR が同時に作成され RSpec のジョブがトリガーされた場合リソースが足りなくなってしまう懸念がありました。 そこで Gitlab Runner サーバー を AWS へ移行して Gitlab Runner の AutoScaling 機能を利用しました。 AutoScaling については こちら の記事が参考になります。 移行前の構成 移行後の構成 この構成の設計や移行作業は全て弊社インフラエンジニアである 夏目さん によるものです。ありがとうございました!! 実行速度を速くしたい! さて、ブランチごとに実行する環境は整いました。 しかし全てのテストが完了するまで2時間ほどかかってしまうという最大の問題が解決されていません。 MR を作成してマージするぞ!っと思ってからテストが終わるまで 2 時間は流石に待ってられません。 RSpec を速くする方法としては、テストコード内で無駄にレコードを作らないなど色々プ ラク ティスがあると思いますが、数百ファイルのテストファイルを全て見て修正するというのは現実的ではありませんでした。 そこで RSpec の並列化という方法で解決することを考えました。 元々 CI 上でのテストは他のテストファイルの影響を無くすため、通常の rspec コマンドではなくそれぞれのテストファイルを独立して実行する スクリプト を利用していました。 #!/bin/bash # 全てのスペックファイルを単独で実行するためのスクリプト。 fail_count = 0 spec_array = () while IFS = read -r -d $' \0 ' spec; do spec_array+ = ( " $spec " ) done < < ( find spec/ -name ' *_spec.rb ' -print0 ) spec_count = ${#spec_array[ @ ]} echo " Running $spec_count spec files " export SKIP_CODE_COVERAGE= 1 for (( i= 0 ; i<$spec_count; i++ )) ; do echo -e " \n ######################################################################### " echo -e " \n Running spec $(( i+ 1 )) of $spec_count " if ! bin/rspec " ${spec_array[$i]} "; then (( fail_count++ )) fi export SKIP_DATABASE_SEEDING= 1 done echo -e " \n $spec_count test files, failures in $fail_count files \n " [[ $fail_count -eq 0 ]] この スクリプト と Gitlab CI の parallel オプション を利用して並列化を実現しました。 まず、 .gitlab-ci.yml のジョブに parallel フィールドを追加します。 今回は 10 個のジョブを並列で実行することにしました。 parallel : 10 この parallel フィールドを追加すると、 ジョブの 環境変数 として CI_NODE_TOTAL と CI_NODE_INDEX という 環境変数 がセットされます。 CI_NODE_TOTAL には parallel で指定した値、 CI_NODE_INDEX にはそのジョブのインデックスがセットされるので、この 2 つの 環境変数 から実行するファイルを算出します。 並列化後の スクリプト はこちらです。 #!/bin/bash # 全てのスペックファイルを単独で実行するためのスクリプト。 fail_count = 0 spec_array = () while IFS = read -r -d $' \0 ' spec; do spec_array+ = ( " $spec " ) done < < ( find spec/ -name ' *_spec.rb ' -print0 ) spec_count = ${#spec_array[ @ ]} echo " CI_NODE_TOTAL is $CI_NODE_TOTAL " echo " CI_NODE_INDEX is $CI_NODE_INDEX " count = $(( spec_count / CI_NODE_TOTAL )) start_index = $(( count * ( CI_NODE_INDEX - 1 ))) if [[ $CI_NODE_INDEX -eq $CI_NODE_TOTAL ]] ; then count = $(( spec_count - start_index )) fi echo " Running $count spec files " echo " Start at $start_index " export SKIP_CODE_COVERAGE= 1 for (( i=$start_index; i<$start_index+$count; i++ )) ; do echo -e " \n ######################################################################### " echo -e " \n Running spec $(( i+ 1 - start_index )) (total: $(($i + 1 )) ) of $count (total: $spec_count ) " if ! bin/rspec " ${spec_array[$i]} "; then (( fail_count++ )) fi export SKIP_DATABASE_SEEDING= 1 done echo -e " \n $count test files, failures in $fail_count files \n " [[ $fail_count -eq 0 ]] テストを 10 分割することにより 2 時間かかっていたテストが 約 20 分 で完了するようになりました! 残念ながらジョブ実行用のスポット インスタンス の起動にある程度時間がかかるため、 10 分割で 10 倍速くなるというわけにはいきませんでした。 また、これ以上並列数を増やしても速くなることもありませんでした。 結果 ローカル環境と CI 環境での差異がなくなったため、ローカルで成功したのに CI では落ちることがなくなった MR ごとに実行できるようになったためテストが成功した状態でマージでき、メインブランチは常にクリアな状態が保てるようになった (たまに落ちてることもある) テストが 20 分で終わるようになった 所感 長らく不満に思っていた、 常に Fail している CI から脱却することができました。 人によるとは思いますが、個人的には CI が落ちて放置されているというような状態に精神が乱されるタイプなので心の平穏を取り戻すことができました。 上記の作業をする前に落ちまくって放置されているテストを全て直すという作業があったのですが正直そこが一番きつかったです。 エニグモ ではカオスな開発環境を一緒に正常化していきたいエンジニアを募集中です! hrmos.co
はじめに みなさん、こんにちは。 主に BUYMA の検索周り、時々 機械学習 なエンジニアの伊藤です。 今年もあっという間の1年でしたね。 振り返ってみると多くのことを学ばせてもらい、また成長させてもらった1年でした。 その中でも k8s がやはり自分の中では中心だった気がします。 なので今回は k8s ネタで今年を締めくくりたいと思います。 k9sとは? k9sとは k8s 上のリソースを監視、管理するための CLI ツールです。 k8s の CLI といえば kubectl が デファクト ですが、そのI/Fをよりリッチに使いやすくしたのがこのツールです。 公式のページは こちら 読み方は けぃないんず だと思います(たぶん) 個人的になぜ犬のロゴなんだろうというのがまず引っかかったので軽く調べたところ、 canine=犬 のヌメロニム表記であるk9と、 kubernetes の k8s が表記的に近いから犬なんだなと勝手に納得することにしました。 使ってみる では実際にk9sをインストールして使ってみましょう。 Linux 、 OSX 、 Windows 上で動作するようですが、今回は Mac 上でインストールして動かしてみたいと思います。 インストール brew install derailed/k9s/k9s 起動 k9s 表示される情報については大きく2つの領域に分かれており、上部に現在使用しているkubectlのcontext、 クラスタ 名や、コマンドヘルプ等が固定表示され、 枠内にpodの一覧など、指定した情報が表示されるようになっています。 デフォルトでは kubectl get pod で取得できる項目相当の一覧が見れますが、CPU、メモリ使用率なども表示されます。 基本操作について 基本的なところさえ抑えておけば、あとは直感的に操作が可能です。 コマンドモード : でコマンドモードになります。 続けて、 k8s のリソース名(alias名でも可)を指定することでその一覧が表示されます。 指定するリソース名を忘れた場合は Ctrl-a で確認できます。 例えば、deploymentの一覧を確認したい場合は :dp と入力して Enter でdeploymentの一覧が表示できます。 Esc でコマンドモードを抜けます。 フィルター / のあとにフィルターを指定することで絞り込みが可能です。 kubectlと同じうように -l app=XXXX で任意のラベルで絞りこみが可能ですし、 任意の文字列だけ入力してもその文字列がpod名やnode名に含まれれば絞り込みが可能です。 Esc でフィルターを解除します。 ソート Shift と特定のキーで項目毎のソートが可能です 各項目とキーの マッピング は ? で確認できます。 昇順/降順の切り替えは Shift-i で行います。 実践的な使いどころ 基本操作が分かったところで、よりk9sの便利さが伝わるように下記のような場面を想定した操作をしてみます。 あるdeploymentリソースに異常があり、該当のdeployment及びその配下のpodのdescribeを確認して、ログを確認する podへのport-forward 所感 kubectlに慣れている人や、aliasやpeco等でコマンド操作自体をゴリゴリにチューニングしている場合は、 k9sの使い始めは慣れない分、「そこまで便利か?」っていう感じを受けるかもしれません。 自分は最終的にどちらかに移行するというよりは、下記のような使い分けをすることで落ちついています。 単発の確認はkubectl 例えば今のpod一覧を知りたい時など、特定の情報だけ知りたい場合はわざわざk9sを起動する方が手間なので、 kubectl だけで済ませています。 解析作業や複数のコンテキストを跨る作業 こういう場合は圧倒的に k9s が便利です。 上でも記載していますが、 クラスタ 側に何か異常があった場合には、様々なリソースの情報を見ていくことになると思います。 そういった場合にはk9s上であれば、 わざわざコマンドを打つことなく少ないキー操作で確認が可能です。 また、複数のkubectlコンテキスト内のリソースを同時にみるのはkubectlではできませんが、 k9sでは別ターミナルで起動してコンテキストを切り替えることで、 ターミナルの切り替えだけで複数コンテキストの操作や参照が可能になります。 最後に k8s とその周辺エコシステムは進化が激しく、日々新しい機能やツールが出てきている印象です。 キャッチアップが大変ですが、今後も日々の運用やシステムの安定性に繋がるようなものは時間を見つけて試していきたいと思います。 株式会社 エニグモ 正社員の求人一覧 hrmos.co
こんにちは、サービスエンジニアリング本部の穴澤です。 今年の1月に入社して丁度1年になります。良いタイミングなので1年を振り返ってみたいと思います。 これから転職をしてみようかなと考えている人、新しい事に挑戦したいと思っている人に読んでもらえると幸いです。 1-3月 年始に入社。前日は眠れませんでした。6年働いた前職から転職し、人間関係はゼロから。使っている技術、サービスも違う。大丈夫かなー3ヶ月後生きてるかな?と思っていました。入社後は環境構築しつつ、その後運用業務を上司から少しずつ分けてもらい日々の業務全体をみるようになりました。エンジニアとしての経験を買ってもらって入社したものの、 プログラミング言語 での経験は主に PHP だったので エニグモ での Rails を使った開発に戸惑うところも未だにあります。このタイミングでチームに新しく入ってきた方も居たので受け入れ時に必要な情報やドキュメントを徐々にまとめて自分がサービスの説明や開発フローを説明できるものを準備しました。 この頃はまだ前職の人たちとの飲み会が多く、ホームシック的?な気持ちになったのを覚えています。ああ、転職したんだなあ、という気持ちでした。 EM見習いとして入社したので、入社したその月からメンバーの方と一人ひとり話をする時間を持ち始めました。ただし全然みんながなにやってるのかわからん。という感じでした。 4-6月 「社内での情報共有サービスを移行しよう」という動きがありチームを横断した形でエンジニア数名、関係者含めて小さいチームが発足しました。 私自身が刷新に前向きだったのでやってみようと思い手をあげることになりました。実際には部署をまたいで新ツールの使い方のレクチャや 既存のデータの移行スケジュールの共有、他の部署の人たちへの ヒアリ ングや、もっている課題などを集約しデータ移設などは 山本さん におんぶにだっこでした。 このチームのおかげで普段接点のない部署の方々とお話する機会があったかなと思います。名前もだいぶ覚えてきました。 夏場に大きな施策が控えており、メンテナンスが必要になったため自分が担当になりました。過去メンテのドキュメントを探しマニュアルや手順書の整備をはじめました。 メンテにはインフラエンジニアや他のサーバサイドエンジニアの方もいましたが「入社して半年でメンテ要員わたしで大丈夫かな?」と思いながらテスト環境で手順書の確認やわからないことを少しずつ潰していきました。 7-9月 サーバーメンテナンスは深夜に実行する事となり、夜に関係者が出社しメンテナンスを終えたのは朝でした。そのまま朦朧としたまま会社を出、 ファストフード店 でおいしい朝ビールをいただきました!色々と学びが多い月でした。メンテをしたことでちょっとシステムの全体構成や アーキテクチャ 全般が「わかった気」になったのを覚えています。 開発業務で機能をゴリゴリ書いている身ではないので案件の調整や小さい運用、リリースの調整やドキュメントの整備、問い合わせや MTG 、ちょっとした相談・・・。こういう事に忙殺されているとたまに「わたしエンジニアって言っていいんだっけ?」と悲観的になることもあるのです。なんだか何もしてないな、EMで入ってきてるけどわたし大丈夫かな。と不安になったこともありました。チームの成果を伸ばしたい、組織に貢献したい。なので技術力そのものだけじゃないところで活躍したい。自分の達成チケット数=成果ではないという事を上司に確認したこともありました。その際、「そういう前提であなたを迎えているのですから(大丈夫ですよ)」と言ってくれたのはいまでも覚えています。メンテも経験したことで、この辺りからはっきりと自分の役割を理解してきました。技術的な学習や実際の開発から完全に手をひいたわけではないけれどそこに自分の価値があるわけではない。というのが1年たった気持ちです。 10-12月 リリースに関わる改善をしたいと日頃思っていたので、夏過ぎからまた有志のチームで細々と情報の整理と改善の策を練るところからはじめています。ちょっと不安ですが自分にできることを探していこうと思います。 また春先から頑張ってきた採用活動が実を結び、 新しい人が入社してくれました! 人材育成も過去の経験をそこそこに活かしながら、今わたしの目の前にいる人の顔をみて、自分ができる「お手伝いをしていこう」と思った今日このごろです。 最後に 異なる環境に飛び込むことは色々考える事も多いし悩みも多いです。それでも今年楽しかったな、ここはやりきったな。という達成感はあります。 それと、自分がどんどん自社のサービス・支える人を好きになってると感じます。携われる領域やできることが些細なことでも一つふえると嬉しいものです。 環境や周りの人を強制的に変えることで視野が広まったり自分が少しずつ変わる変化を、来年も楽しんでいこうと思います。 株式会社 エニグモ 正社員の求人一覧 hrmos.co
少しさかのぼりますが、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:巻き込む人を間違えない 前出の まとめ記事 にもありましたが、案件を進める上で上記のメンバーは巻き込む必要があります。 そこで間違えると話が進まないので、初めてプロジェクトを進める場合は気にしてみてください。 まとめ/終わりに ここまでお読みいただきありがとうございました。 はじめての記事でしたが、少しでも参考にしていただければ嬉しい限りです。 基本的なインプットから、コーディング、プロジェクト化など一通りやってみて非常に勉強なりました。 実際にコーディングもプロジェクトもやってみることが一番だと思いました。 案件を考えるときに「 機械学習 」という選択肢を持てるのは、 プロマネ としては強みにな今後なりうるのだろう思います。 ただ、選択肢の一つということを意識することは重要 機械学習 を使う!と言いながら、やはりいちばん大切なのは、「ユーザーの課題は何か?」という問いに尽きると改めて思いました。 実際に、知見がない中で初めてプロジェクトを進める場合は、関わるメンバーや案件は慎重に進めるとよいと思います。 時代の後押しはあるので、比較的会社も挑戦を応援してくれると思います。