TECH PLAY

BASE株式会社

BASE株式会社 の技術ブログ

590

この記事は BASE Advent Calendar 2019 の11日目の記事です。 devblog.thebase.in はじめまして!こんにちは! BASEのCommerve Dev Group所属の白数です! サーバサイドをメインで開発しています。 私は現在、新卒2年目で18卒と呼ばれる代となります。 BASEには今年の9月に入社しました。今月で4ヶ月目です。 入社してからは、Eコマースプラットフォーム「BASE」の社内向け管理画面の新規機能を開発しています。 そんな若手エンジニアの私がどのようにしてBASEでの働き方をキャッチアップしているのかについてご紹介しようと思います! 特に私と同世代や学生の方にBASEで若手がどのようにして働いているのかを少しでも伝わると幸いです。 BASEで働く上で意識していること・実践していること 1. タスクと進捗の共有 BASE では入社時に、日々の業務でのちょっとしたお困りごとについて質問したり、オフィス生活に関する質問対応をしてもらえるメンターが付く期間が1ヶ月間用意されています。 この時に社内では timesチャンネル と呼ばれる新入社員とメンターがメインでやり取りを行うチャンネルを作るのですが、メンターの他にグループのメンバー、他グループのメンバーが参加しています。 メンター期間はこのチャンネルで日報や困っている事を書くのですが、私の場合、メンター期間後もこのチャンネルを利用し続けています。 やっていることは出勤時には、 今日やること 退勤時には 今日やったこと いわゆるタスクと進捗を共有することです。 自分のタスクの内容をグループのメンバーに共有することで、 もし作業の方針や順序が間違っていたりしたらその場で指摘をもらうことができています。 同時に自分が作業するTODOリストとしても機能しており、タスクが明確化していて「次は何をやるんだっけ...?」といった状況にならず効率が良いです。 毎週、上長と 1on1 の面談をする時間が設定されているのですが、 毎日タスクを共有していることで上長は自分が今どんな事をしているのかを把握してくれていて、1on1が設定されている理由の本質である 思っていること 困っていること などの話に100%の時間を使うことができています。 また、作業内容のログとして後日確認する事もできるので週末に振り返りを行う際、より詳細な観点で良かった事、悪かった事について考えることができていると感じます。 2. 技術的な問題で詰まったときは、とりあえず共有 私は大学生の時に長期インターンで働いていた頃はRuby、そして新卒で入社した会社ではJavaを書いていました。 現在のBASEでは PHP で開発しています。 PHPは大学時代の授業で触ったことがある程度で実務で利用するのは初めてです。 書いている言語が頻繁に変わっていることもあり、 入社2ヶ月ぐらいはBASEのサーバサイドにおける仕様面やCakePHPの細かい使い方が理解できていないこと、 PHPUnitを書いた経験がない事などが理由で頻繁に原因不明のエラーに詰まっていました。 30分ほど悩んでも一向に進まない時は、自分のtimesチャンネルに投げるようにしていました。 BASEにはPHPに詳しいエンジニアがたくさんいます。 解決策や助け舟を出してもらい、開発をしていく上で必要となる最低限の知識を爆速でキャッチアップして実装に入ることができました。 3. タスクで困ったときや、方針を決めるときは直接話す タスクを進めていて進め方などに困ることがあります。 その時は当日、遅くて翌日中には上長へ直接相談するように心がけています。 Commerce Dev Groupでは、毎日12:00からスタンディング形式で タスク内容や困っている事を共有する場として昼会を実施しています。 いろんな人の見解を聞きたいときはこの時に共有して相談するようにしています。 Slack等で話すことも可能ですが、 直接相談した方が早いのと、より細かいところまで認識を擦り合わせることができます。 4. 糖分を摂取 オフィス内にBASEの3つの行動指針のうちの1つである「Be Hopeful」が描かれた飴が置いてあります。 プログラムを書いていて糖分が欲しいな〜という時はこの飴を摂取しています。 デザインが可愛いくて美味しいです。 おわりに BASEの若手エンジニアの私が働く上で意識していることを、あくまで一例ではありますが紹介してみました。 一番は大切なのは、 常に自分の現状を共有することで多方面からアドバイスを貰いやすくなっている印象です。 BASEは若手が成長できる場があると思います。 実際私もまだ入社4ヶ月ですが、エンジニアとして大きくレベルアップしている実感を持っており、 これからもプロダクトと共に成長してBASEを盛り上げていきます。 明日はBASE BANKの河越さんとProduct Managementの坂東さんです!お楽しみにー!
この記事はBASE Advent Calendar 2019の11日目の記事です。 devblog.thebase.in こんにちは!BASEのProduct Management Groupの船坂です。 2019年9月にBASEに入社し、ようやく4ヶ月目に入ったところです。プロジェクト単位でアサインされ、ディレクションを中心に業務を行っています。 今回は、BASEに入社してからEコマースプラットフォーム「BASE」というサービスをいろいろな角度から理解するためにしてきたことをご紹介します。 カスタマーサポート研修 最初は、自主的なものではなく研修ですが、、、 BASEには入社直後にカスタマーサポート業務を体験する研修があります。 この研修には、 まずはショップオーナーとしてネットショップ作成サービス「BASE」を実際に利用してみる。 次に、よくお問い合わせいただく質問内容に対し、模擬的に回答を作成する。(その後CSのメンバーに回答を添削してもらいます。) という2つのステップがあります。 これによって、「BASE」というサービスの利用シーンを深く理解すると同時に、現時点で初めてサービスを使う方がどの部分でつまづく可能性があるのかを短期間で把握することが出来ます。 また、1. の過程で気になったことを社内ドキュメントにまとめて記載するというステップが含まれています。利用に慣れてない視点だからこそ気づくことができる新鮮なフィードバックを社内にすることができて、とても良い仕組みだと感じています。 実地見学 僕自身、IT業界に携わっていると基本的なことは画面の中で出来てしまうのでおろそかにしてしまいがちなのですが、何かを理解するときはその場所に行き、自分の目、耳、触覚など、五感で経験することが一番大事だと思っています。現地に訪問し、オーナーさんやそのショップ、買ってくださるお客様などの様子を実際に感じることで、自分たちが作るサービスの画面の向こうにいる人たちの存在を確かめます。サービス理解という意味だけでなく、普段のモチベーションに直結します。結果、アウトプットの質も段違いに高くなると思っているので、とてもおすすめです。 BASEの場合、オフラインに展開されているものとして、渋谷マルイにある常設店舗「 SHIBUYA BASE 」を始めとする実店舗や、先日行われたオーナーさん向けイベント「 BASE OWNERS DAY 2019 」などのオフラインイベントがあります。入社直後から、これらの場所には実際に足を運んでいました。 経営陣や執行役員の考えを知る 経営陣や執行役員陣がこれまでどういう考えで事業やサービスづくりと組織づくりに携わっているかをなるべく深く理解するために、いろいろな情報をチェックしました。社内に向けて発信されているメッセージや外部で話しているインタビュー記事、普段の何気ない言動やSlackへの投稿などです。どういった人たちがどういうモチベーションでプロダクトを作っているのかを理解することは、どういったサービスなのかを理解することに直結すると思っています。 ちなみに、自分の役割はそこにスムーズに乗っかりつつ、僕にしか出来ないことを全力でやって更にプロダクト成長を加速させることなのかなと思っています。 社内プロジェクト調査 BASEはプロジェクト単位でチームが組まれ、サービスの開発が進んでいきます。当然、社内全体ではどういうプロジェクトを走らせるかが入念に検討されているのですが、一個人としては、自分のプロジェクトに入ってしまうと、意外と実際の業務の中では全体が見えにくくなってしまいます。 そこで、入社したタイミングで、社内ではどういうプロジェクトが走っていて、それはだれが、なぜ今やっているのかをヒアリングしながら考えていました。この作業で、大きな流れの中で今サービスがどういうフェーズなのか、理解できた気がします。 知り合いへのヒアリング ありがたいことにEコマースプラットフォーム「BASE」はたくさんの方にご利用いただいており、自分の知り合いでもネットショップを運営してくださっている方や、ショッピングアプリ「BASE」で商品を購入したことがある方が思った以上にいらっしゃいます。 生の声を聞ける方々は僕にとってとても貴重な存在です。BASEに入社してからは、「BASE」を利用したことがある知り合いと何度か飲みに行き、なぜ使っているのか、どんなところが好きか、どこが使いにくいのか、いろいろな話を聞かせてもらいました。 こういうことを繰り返していくと、なんとなく「サービスを形成しているコミュニティ」全体の雰囲気が見えてきます。 これがわかってくると、どのような施策が効果的なのかを感覚的につかめてくるのではないかと思っています。 データ分析 とはいえもちろん感覚だけでは話は進まず、詳細かつ確実な現状把握にいちばん効くのはデータだと思っています。Google Analyticsから自社で独自に蓄えているデータまで、どんどん調べてみています。BASEでは僕の所属するProduct Management Group含め、必要な従業員はRedash等で業務に必要なデータの分析を自由に行うことができます。全体像は上記のような方法で調べて、細部はデータで確証を得ながら理解を進めている形です。 まとめ 今回この内容にしたのは、経験上、理解が進めば進むほどそのサービスは"じぶんごと"になっていき、それがサービスを作るのに一番大事なことだと考えているからです。 極論、どんなスキルよりも当事者意識こそが、サービスに本当に必要な価値提案や、小さくても確信をついた修正案を産み出すのだと僕は信じています。 最後まで読んでいただきありがとうございました! 明日は、BASE BANKの河越さんとProduct Managementの坂東さんです!楽しみ!
この記事は BASE Advent Calendar 2019 の10日目の記事です。 devblog.thebase.in お久しぶりです。 BASEビール部部長(兼Data Strategyチーム)の氏原です。 1年ちょっと前に Yahoo!の近傍探索ツールNGTを使って類似商品APIをつくる という記事を書きました。あれからだいぶ経ちましたがその間に類似商品APIはコツコツと改善を続けています。例えばファッション系以外で精度が良くないという話を前の記事にも書きましたが、画像以外に商品のタイトルや説明文の特徴量も使うようにするなどしてそれも改善されています。 そうした取り組みのなかで近傍探索に使っていた NGT を別のものに切り替えましたので、それを紹介したいと思います。 類似商品APIについて 類似商品APIはBASEのアプリで各商品の詳細ページを開いたときに表示される「関連する商品」という部分などで利用されています。 その時開いた商品と似た商品を提示することで良い商品探しに役立つようにとAPIを開発しています。 NGTについて NGT はYahoo!Japanさんが開発している近傍探索用のツールです。 NGTは任意の密ベクトルに対して事前に登録した(同次元の)ベクトルから最も距離が近いベクトルの上位数件(k件)を高速に近似k最近傍探索(k-Nearest Neighbor Search)するためのソフトウエアです。 高次元ベクトルデータ検索技術「NGT」の性能と使い方の紹介 以前はこれで画像の特徴量vectorを保存して、商品ページが開かれた際に似た画像をもつ商品を関連商品として提示していました。 NGTはとても性能がよく、1024次元の特徴量vector350万個を登録しても数十msecでレスポンスを返してくれてオンデマンドで関連商品を出すのにとても重宝してました。 問題点 ただ運用を続けていると色々と問題もでてきました。特に辛いのはindexの構築にかかる時間とindexのサイズです。 NGTは登録したvectorを全てそのまま持っています。そのため1024次元の特徴量vector350万個を登録した時点でindexのサイズは20G程になります。これがメモリに全部載るので、AWSでサーバを立てると結構なサイズのメモリを持ったインスタンスが必要になります。 また、NGTは必要なvectorを全てinsertした後にbuild_indexでindexingをする必要があります。登録したvectorをdeleteするメソッドがなく、差分更新が(おそらく)できないため、daily batchで毎度indexを作り直していました。(※私の理解不足の可能性もあります。間違ってたらごめんなさい。) faiss 上記の問題を解決するために、現在では別のツールで近傍探索を行っています。 faiss です。 faissはNGTと同じくvectorの近傍探索のためのツールでFacebookが開発しています。 NGTと比べてなにが嬉しいかと言いますと、最終的に出来上がるindexのサイズです。 faissは高速化やindexの圧縮のための仕組みが用意されていて、いろいろ試した結果indexのサイズがNGTでやっていた時の1/200くらいになりました。衝撃です。 もちろん圧縮するがゆえにある程度の精度低下は覚悟しなくてはいけないんですが、目立つほどでもありませんでした。 使い方はこんな感じです。 index作成 import faiss import numpy as np import random dim = ... # 特徴量の次元数 nlist = ... # 特徴量が何個の空間に分割されるか m = ... # 近傍探索するときに対象の空間以外に隣の空間を何個まで利用するか nbits = ... # 特徴量をここで指定したbit数で表現するように圧縮する quantizer = faiss.IndexFlatL2(dim) index = faiss.IndexIVFPQ(quantizer, dim, nlist, m, nbits) vecs = [(item_id, vec), ...] # 商品IDと特徴量のセットのリスト # 圧縮のために特徴量の分布を学習する必要がある # 実際の運用ではここは事前に学習しておいて学習済みのindexをloadする assert not index.is_trained train_data = [v for _, v in vecs if random.random() < 0.01 ] index.train(train_data) assert index.is_trained # indexへの追加はまとめてではなくこんな感じで細切れでもよいので # vecsが大きすぎて全部をメモリに乗せられない場合は # generatorで必要な分だけ読み込みながらやったりしても大丈夫 batch_size = 10000 for i in range ( 0 , len (vecs), batch_size): input_vecs = [] input_ids = [] for item_id, vec in vecs[i:i+batch_size]: input_vecs.append(vec) input_ids.append(item_id) input_vecs = np.array(input_vecs, dtype=np.float32) input_ids = np.array(input_ids, dtype=np.int64) index.add_with_ids(input_vecs, input_ids) # 保存 faiss.write_index(index, "features.index" ) indexの利用 import faiss index = faiss.read_index( "features.index" ) vec = ... # 近傍探索のターゲット vecs = np.array([vec], dtype=np.float32) D, I = index.search(vecs, 1000 ) # 近傍1000個とってくる D = D[ 0 ] I = I[ 0 ] for item_id, distance in zip (I, D): # add_with_idsでindex作ってるとここでIDが返ってくる ... wiki が充実してるので使うにあたってあまり困ることはありませんでした。 差分更新 faissで作成したindexは、(サポートしているindexなら)登録されたvectorの部分削除が可能です。 import faiss index = faiss.read_index( "features.index" ) target_id = ... # 消したいitem_id index.remove_ids(np.array([target_id]) つまりdaily batchで差分更新ができるため、index構築時間を激減させることができます。これならdailyと言わずもっと更新頻度を上げられます。 ちなみにindexのmergeとかもできるそうなので、vectorが大量にある場合はN個に分けて、N個のjobでindexを作成して後でmergeするみたいな使い方もできるようです。 まとめ faissはFacebookが使ってるだけあって1兆個のvectorを登録して動かすなんてスケールまで想定しているようです。もちろんそのためには 色々工夫 が必要なようですが。 faissを導入したことでindexの構築やAPIの運用がかなり楽になり、インフラやアルゴリズムの改善に力をいれることができるようになりました。リコメンドのシステムを作る際などで近傍探索用のツールが必要であればfaissを検討してみてはいかがでしょうか。 明日はプロダクトマネジメントグループの船坂さんとエンジニアの白数さんです。お楽しみに。
この記事は BASE Advent Calendar 2019 の10日目の記事です。 devblog.thebase.in こんにちは、はじめまして。 SREの相原です。 BASEには2019年9月に入社し、今月で4ヶ月目に突入しました。 SREでは各々の改善業務のほか、日々の問い合わせや依頼業務、トラブル対応など、突発的に発生するタスクがあります(数はとても少ない)。 これはどのような業種、職種においても言えることかと思いますが、 突発的かつ迅速に対処しなければならないタスクは、 担当が特定の人(その問題に知見がある人や、問題解決力が高い人)に偏ってしまう傾向にあります。 幸いBASEでは、各メンバーが主体的にボールを拾っていく文化があったため、大きな問題にはなっていませんでしたが、 私が入社した時点でSREメンバーが6人と増えてきたこともあり、試しに当番botを導入することになりました。 当番botについて 当番は問い合わせなどに対して窓口としての機能を持つ(必ずしも1人で解決する必要はない) 当番にはメインとサブを設定し、メイン当番が休みの時や、手が回らないときはサブが対応する 当番は週ごとに切り替わる 当番の通知はslackでおこなう こんな感じで当番をお知らせしています。 良かったこと チームとして: 対応するタスクの偏りが少なくなった もともと自主的に拾う文化があったとはいえ、それぞれの得意領域や興味はバラバラなのでどうしても対応するタスクの種類は多少偏ります。 当番botを始めることによって、この偏りが少しなくなったと感じています。 個人として: 心理的なブレーキがなくなる これは入社したばかりの私だからこそ感じる事かもしれませんが、 問い合わせや突発的に発生するトラブルは初見のものが多く、中には解決までの糸口が見当もつかないものもあります。 そういった問題に手を挙げるのは、人によっては億劫になってしまうかと思います。 しかし、当番という役割があることによって手を挙げざるを得ない状況になるので、 「手を挙げたはいいが、解決できなかったらどうしよう」という心理的なブレーキがなくなります。 このブレーキがなくなったおかげで今では臆することなく、何か問題があればとりあえず手を挙げることができるようになったと感じています。 他メンバー、他チームとのコミュニケーション機会が増える 対応する問題の中には、1人で解決できないものももちろんあるので、他のメンバーに質問することになります。 入社初期の段階で、質問を通してメンバーとのコミュニケーションを図ることができました。 他にも、他チームの方と関わる機会が増えたりと、当番botを導入することで、結果自分のためになることが多くありました。 botの 改善 同じ組み合わせの担当だとマンネリ化してくるので、ランダムに組み合わせを変えていく仕組みを入れたいなと考えています。 また、今のbotはただ当番を報告するだけのものなので、 メッセージに遊び心を入れるなどして、見た人のモチベーションが少しでも上がるような工夫ができればと思っています。 最後に 私は主体的にボールを拾っていくBASEの文化が好きなので、当番botを導入することには少し懐疑的でした。 しかし前述した通り、結果として自分にとってためになったことが多くあったなと感じています。 BASEにはMove Fastという行動指針があります。 これは「速く動くこと。多くの挑戦から多くを学ぶために、まずはやってみよう。」という意味が込められていますが、 今回の当番botを通して、あれこれ悩む前に先に行動することで得られることは多々あるという学びになりました。 明日はプロダクトマネジメントグループの船坂さんとエンジニアの白数さんです。お楽しみに!
この記事はBASE Advent Calendar 2019の9日目の記事です。 devblog.thebase.in はじめまして。BASE株式会社のtatsuと申します。 最近、業務にて guzzle を使う機会がありました。結論から述べますと guzzle のみで実現することは出来ず Amazon sqs を併用するという形で落ち着いたのですが、いくつか知見を得ることも出来たのでその事について書きたいと思います。 主に guzzle/Pool と guzzle/RetryMiddleware の話になります。 最初の壁:ResponseがどのRequestの結果なのか分からん まず並列処理を実装しました。実際のものとは違いますが流れは一緒。 $urls = [ 'https://example_base.in/1', 'https://example_base.in/2', ]; $guzzle_client = new Client(); $requests = function ($urls) use ($guzzle_client) { foreach ($urls as $url) { yield function () use ($guzzle_client, $url) { return $guzzle_client- > postAsync($url); }; } }; (new Pool($guzzle_client, $requests($urls), [ 'concurrency' = > 10, 'fulfilled' = > function ($response, $index) { // dbに保存とか }, 'rejected' = > function ($reason, $index) { // dbに保存,ログ出力とか } ]))- > promise()- > wait(); レスポンスを受け取って保存という段階で 「どのリクエストの結果を受け取っているか分からん。」となりました。 送信は request の生成順に処理されますが、結果はもちろん順不同で返ってきます。 そりゃ並列処理ですからね… この問題の解決策は簡単で、request 生成部分を少し変えるだけ。 $urls = [ 'index1' = > 'https://example_base.in/1', 'index2' = > 'https://example_base.in/2', ]; $requests = function ($urls) use ($guzzle_client) { foreach ($urls as $index = > $url) { yield $index = > function () use ($guzzle_client, $url) { return $guzzle_client- > postAsync($url); }; } }; これで pool の成否処理の第2引数が'index1','index2'といった値になります。何もしないとrequest の生成順に整数が振られますが、上記の様に任意の値を渡すことも出来ます。 ちなみに失敗時の第1引数には基本的に exception が入ってきます。 下記を参考に処理を分けてあげると良いかもしれません。 http://docs.guzzlephp.org/en/latest/quickstart.html#exceptions 二つ目の壁:Retryの度に結果を保持したい 一応の並列処理は出来た。ということで retry についても考えてみます。 guzzle では RetryMiddleware というリトライの方法を標準で用意してくれています。 // clientに設定 $handler_stack = HandlerStack::create(new CurlHandler()); $handler_stack- > push(Middleware::retry(retryDecider(), retryDelay())); $guzzle_client = new Client(['handler' = > $handler_stack]); function retryDecider() { return function ( $retries, Request $request, Response $response = null, RequestException $ exception = null ) { // 最大5回 if ($retries > = 5) { return false; } // 4xx or 5xx はリトライ if ($response && $response- > getStatusCode() > = 400) { return true; } return false; }; } function retryDelay() { return function ($retries) { return (int) pow(2, $retries - 1) * 1000; }; } 上記の例はかなりシンプルですが、リトライの判断部分と待機時間を実装して Middleware::retry() に渡してあげれば良しなにリトライしてくれます。 注意点としては、delay は単位がミリ秒です。timeout は単位が秒なのに http://docs.guzzlephp.org/en/stable/request-options.html#delay ここでまた問題が生じました。ある理由から最終的な成否だけでなく、retry 時の結果も逐一保持したいとなりました。さて困った。 pool の成否へは request がすべき動作が終わってから到達します。つまり retry 時には実行されません。そうなると retry の中で保持を行う必要があります。 retryDecider() で行いたいところですが、どのリクエストの retry なのか判別する必要があります。上記の様に request が入ってきているので、request-header 等に識別子を設定して判別する事も出来ます。ただ、こちら側の都合を request に持たせるのはどうなのかという考えが浮かび別の方法を取ることにしました。 RetryMiddleware を自作してみました。 …こう言うと凄そうに聞こえるかもしれませんが、実際は標準のものをちょっと変えただけです。 全文は長いので変更箇所の辺りだけです。 private function onFulfilled(RequestInterface $req, array $options) { return function ($value) use ($req, $options) { if (!call_user_func( $this- > decider, //$options['retries'], $options $req, $value, null )) { return $value; } return $this- > doRetry($req, $options, $value); }; } 上記と同じ事を onRejected() にも deciderに $options['retries'] (リトライ回数)では無く $options ごと渡す様にしました。 これで request-options に識別子を持たせることでリクエストを判別する事ができる様になりました。request-ooptions は key を既存のものと被らない様にすれば影響はないですし、リクエスト先にも渡ることはありません。 $options['retries'] 自体は $options に同keyが無ければ __invoke() で追加されます。つまり RetryMiddleware の外から操作する事も出来るということでもあります。 少し長くなってしまいましたが、これで pool × retry の実装が出来た! …と思っていました。この時は 三つ目の壁:PoolとRetryMiddlewareって併用出来ないの&リトライ中にプロセス死んじゃう 開発の世界で三段オチを味わうことになろうとは…。 それぞれをさっくり解説しますと PoolとRetryMiddlewareって併用出来ないの リトライするテストを書いていたら何かおかしいということで検索したところ、「asyncRequest() と RetryMiddleware を併用すると同期してしまいます」といった情報を見つけました。いやいやいや、そんなはずは…と思いつつ試してみました。 方法は簡単でリクエスト毎にリトライの待機時間を変えてみます。 request1 は[2つ目の壁]にある様に回数に応じてべき秒で増えていき、request2 は2秒固定でリトライを繰り返してみます。並列処理が非同期で行われているのであれば、4回目で順番が入れ替わりrequest2が連続するはずです。結果は…? 綺麗にrequest1とrequest2が交互に並び続けました。ワーイ さらに追い打ちをかける様にもう一つ問題が発覚します。 リトライ中にプロセス死んじゃう これは一般的ではない環境での事象ですので詳細は省きますが、あるタイミングで実行中のプロセスが強制的に終了させられてしまうという事がわかりました。終了までに多少の猶予があるのですがリトライの待機時間によっては、待機中に強制終了されてしまい終了に備える事が出来ないのです。プロセスの管理側からの操作なので内部で対策をするにも限界がりそうでした。ちなみに強制終了後すぐに再起動されます。 上記二点の問題が浮上し悩んでいた所、同僚からこんな言葉が(実際にはgitのコメント)…。 「リトライをAmazon sqsに任せちゃうのは?」 …なるほど? 結果的にこれが自分がとった解決方法となりました。 sqs に関しての説明は長くなってしまうので「便利なメッセージキューイングサービス」とだけ言わせて頂きます。その sqs の機能の一つで遅延機能があります。 https://docs.aws.amazon.com/ja_jp/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-delay-queues.html これと実装してきた guzzle を組み合わせる事で上手くいきそうです。 リトライの判断と待機時間の算出はそのまま使い、失敗してリトライが必要な時に待機時間を持たせて sqs に投げる様にします。 待機自体は sqs でするので強制終了の猶予時間以上の処理は無いので、終了に備える事が出来ます。 イレギュラー中のイレギュラーとはいえ起こり得る事に対処出来て良かった。 まとめ この方法を採った大きな要因として、そもそも作っていたものが sqs からキューを受け取ってリクエストを送るというものだったという事があります。なので sqs の導入コストはほぼ0でした。 そもそも使っていたなら気づけよとも思いますが、こういう時って離れて視野を広くするのがなかなか難しかったりします。 そんな時に第三者の視点から意見を貰えるのは本当に有り難いですよね。同僚の言葉が無ければ時間を掛けて guzzle で頑張っていたと思います。それも不正解では無いと思いますが、今回は sqs との連携を選んで良かったと感じています。また guzzle で色々やったからこそ、それほど苦労せずに連携させられたという事もあるはずです。 長くなってしまいましたが、業務上の課題は積極的にオープンにすると良さそうというお話でした(guzzleどこいった)。 明日は id:beerbierbear と id:yk4o4 の記事です。お楽しみに!
この記事はBASE Advent Calendar 2019 9日目の記事です。 devblog.thebase.in こんにちは、BASE株式会社 ランニング部部長の元木です。 フルマラソンのサブスリー達成を目指して日々トレーニングに励む傍ら、Owners Marketingというチームでサーバーサイドエンジニアもやっております。 前書き 弊社が提供するネットショップ作成サービス「BASE」(以下「WEB」)とショッピングアプリ「BASE」(以下「アプリ」)では、 Amazon CloudSearch (以下「CloudSearch」)を利用して商品の検索機能を提供しております。 当記事では、 CloudSearch のインデックスを更新する処理を、どのようにバッチからワーカーに置き換えたのかをご紹介させていただきます。 この記事は、「 BASE Advent Calendar 2019 」の9日目の記事です。 バッチの何が問題だったのか 商品データは、ショップオーナーさんや購入者のアクションによって刻々と変化していきます。 BASEのシステムは、その商品データの変化を捉えて CloudSearch のインデックスを更新していくこととなります。 CloudSearch のインデックスを更新するのは比較的時間がかかる処理であることと、 CloudSearch には 「更新の頻度が10秒に1回を超えると、スロットリングが発生する場合がある」 という制限があるため、BASE ではバッチプログラムを用いて、非同期でこの処理を実行しておりました。 しかし、バッチ処理には インデクシングが必要な商品数は日々増えていくが、処理をスケールできない DBへの負荷が高い という問題がありました。 試験的に、アプリ側には Amazon SQS (以下「SQS」)をポーリングして CloudSearch のインデックスを更新するワーカーが導入されておりましたが、一部のアクション(商品の登録・更新・削除)しかカバーしておりませんでした。 以下が、切り替え前のシステム構成です。 弊社CTOの  @dmnlk と話した結果、これを以下のような構成に変更することが決まりました。 SQS の前段に Amazon SNS (以下「SNS」)を配置する。 WEB用の SQS を新たに追加する- アプリ側、WEB側ともに、バッチをワーカーに完全に置き換える 以下が、目標とするシステム構成です。 ちなみにこの時の私の Amazon SNS に対する理解度は、こんな感じでした。 次からは、どのような段取りでシステム構成を切り替えていったのかをご説明します。 バッチからワーカーへ 手順1 : Amazon SNS の配置   まず、SQS の前段に SNS を配置し、商品データが更新された際の通知先を SQS から SNS に切り替えました。 アプリケーションの変更点としては、インデックスの更新が必要になるアクションが発生した際の通知先を SQS から SNS に切り替えるだけです。 手順2 : WEB用のワーカーとSQSの配置 次に、WEB用のワーカーと SQS を配置し、ポーリングを開始しました。 まだ、一部のアクションしか SNS に通知されない状況ですので、引き続きワーカーとバッチを並行稼動させています。 WEB用のワーカーを実装する際には、バッチとワーカーが同じ動作をすることを保証するために バッチのテストコードを書く(それまでは無かった) バッチのテストコードとワーカーのテストコードとでデータプロバイダーを共通化し、入力と出力が一致することを保証する といった工夫も行ないました。 SNS と WEB用の SQS を連携させる際も、いきなり全てのイベントを通知させるのではなく、 SNS のメッセージ・フィルタ機能 を利用して通知するイベントを徐々に増やしていくようにしました。 手順3 : Amazon SNS に通知するアクションを増やしていく 次に、SNSに通知するアクションを徐々に増やしていき、最終的に CloudSearch のインデックスを更新する必要のある全てのアクションが SNS に通知されるようにしました。 ここでも、一気に全アクションを通知するよう変更するのではなく、段階的に通知するアクションを増やしていくようにしました。 実は、手順2, 3 を実施していく過程で何度かWEB用のワーカーに不具合が見つかり、改修を行なっております。 しかし、バッチとの並行稼動を続けていたことと、ワーカーの仕事量を少しずつ増やしていくようにしていたため、大きな問題に発展することはありませんでした。 手順4 : バッチの退役 全てのアクションが SNS に通知されるようになったところでバッチを退役させ、すべての作業が完了しました。 まとめ サービスが成長していくにつれて、以前は問題なかったシステムから問題が生じるようになることは往々にしてありえます。 一方で、システム構成の変更は比較的リスクが高い上に(何か新しい機能を提供するわけではないため)地味な作業であるため、ついつい、問題が大きくなるまで放置してしまいがちです。 私としては、作業の段取りを工夫することでリスクを最小限に抑えつつ、サービスの成長に応えられるシステムを構築し続けていきたいと考えております。
この記事はBASE dvent Calendar 2019 8日目の記事です。 devblog.thebase.in こんにちは、はじめまして。 2019年8月に入社したデザイナーの石井です。 入社してからEコマースプラットフォーム「BASE」のWebやアプリ、グラフィックなど、様々なデザインを制作してきました。 各プロダクトのデザイン制作時、UIを作成する際にチームのコミュニケーション、共通理解、制約条件をそろえてデザインしていくことが大変重要です。 それらをより活発に、より洗練されたものにするために最近ではデザインシステムの構築に励んでいます。 今回はショッピングアプリ「BASE」においてのデザイン言語システムの初期の段階(β Version)についてお話をさせていただければと思います。 デザイン言語システムの必要性 僕は過去数年間ほどWebとモバイルのアプリケーションの構築と設計を続けてきました。エンジニアやディレクターなど様々なメンバーと協力してプロダクトを作る経験をしてきた中で、より効率的にプロダクトを成長させるための設計システムを構築する方法を学んできました。 デザイン言語システムに求められる要素はたくさんあります。タイポグラフィ、レイアウトとグリッド、色、アイコン、コンポーネント、コーディング規約から、音声とトーン、スタイルガイド、ドキュメントなど多岐にわたります。 そして各デザイナーの考えをデザインシステム上で他のメンバーに適切に共有し、一貫性を保てるように設計する必要があります。そして、チーム全体でデザインシステムを改善していくことで、より良いプロダクト作りを支えていく基盤となります。 デザイン言語システムがあることで、個別のUIを考える時間を短縮し、どのようなユーザー体験を作るかを考えることに集中できます。また、デザイナー間の共通認識ができることで、複数のデザイナーが同じプロダクトの制作に関わる際にも一貫したユーザー体験の提供することにつながります。 ショッピングアプリ「BASE」のデザイン体制 僕が入社するまで、ショッピングアプリ「BASE」の担当デザイナーは1人でした。その時点ではデザイン言語システムはありませんでした。 入社後、僕もアプリのデザインを担当することになり、また今後新たに入社してくるデザイナーがアプリを担当する可能性も鑑み、デザイン言語システムの構築を始めました。 どうやって構築していったか デザイン言語システム構築を開始する前に、基本的なスタイルガイドを作成しました。ここで、タイポグラフィ、色、アイコン、情報アーキテクチャを大まかに定義しました。 こちらは、タイポグラフィーの定義、カラーの使用パターンと洗い出し、スペーシングの確立後のスタイルガイドです。 さらにiOSおよびAndroid共通のアイコンのガイドラインを確立したものがこちらです。 標準化されたコンポーネントの定義 従来、多くのスタイルガイドにおいて、コンポーネントをアトミックコンポーネントとして定義し、それを使用してより複雑な分子を構築されているのを見てきました。 理論的には、これは首尾一貫した柔軟なシステムを作成するためにうまく機能します。 しかし実際には、これらの再利用可能な原子がさまざまな方法で使用され、あらゆる種類の分子を作成できることがよくあります。これは一貫性のあるデザイン言語システムの維持を妨げ、一貫したユーザー体験の創出を阻害してしまう可能性があります。 これらのコンポーネントを作成する際、ライブラリと呼ばれるマスターファイルにそれらを集約しました。またマスターファイルの更新は僕自身のみで行えるようにしています。 ライブラリを構築している間に、個々のコンポーネントを類似のアイテムを含むアートボードに整理し始めました。 これらのアートボードは、一般的なカテゴリ別にナビゲーション、マーキー、コンテンツ、画像等に分類しました。 これらのコンポーネントをiOSおよびAndroid用に1セット作成しました。タブレット用のコンポーネントはモバイルのコンポーネントとほぼ同じなので、技術的なレベルでは、コードは2つの異なるスタイルがあれば済みます。 デザイナーは、共通のコンポーネントを使用して画面を設計することができ、iOSやAndroidだけでなくさまざまな画面サイズに簡単に適応させることができるようになります。 このコンポーネントスタイルガイドはまだ議論が始まったばかりでお見せできるものはありませんが、今までチーム内で共通のコンポーネントの共通理解や作業方法の一貫性がなかったのでそこをシステム化に現在取り組んでいます。 おわりに アプリのデザイン言語システムの発展はまだまだこれからです。引き続きMove Fastなプロダクト作りの基盤の整備に努めていきます。 明日はエンジニアの齋藤さんと元木さんです!
この記事はBASE Advent Calendar 2019の8日目の記事です。 devblog.thebase.in エンジニアの右京です! みなさん! Storybook は使っていますか?BASE では UIコンポーネントの社内展開 はもちろん、日々の業務の中でもサンプルの実装を共有したりするために Storybook が使われています。BASEではこれを「特定のリポジトリにコードをコミットすると、自動的に社内向けサーバーへデプロイされる仕組み (ようするに社内 GitHub Pages ですね)」を利用して社内共有しているのですが、毎度のセットアップが大変なので Gtihub Actions を使ってお手軽に設定できるようにしてみたよ、という内容です。 github.co.jp TL;DR 社内用向けドキュメントサーバーへのデプロイを他のリポジトリから使いやすいように Action 化して配信するようにしました プライベートリポジトリにある Action は直接参照することができないので Personal access token と npx を使います CircleCI から GitHub Actions に変更することで様々なイベントにフックすることができ、柔軟性のあるデプロイが可能になりました デプロイ(コミット)の Action 化 これまでこのサーバーを利用したい時は、利用側の CircleCI で専用のリポジトリを clone して commit を作って push して...といった処理を用意する必要がありました。利用するプロジェクトが増えていく中、この利用法のスケールのしにくさに問題を感じていたため、GitHub Actions でスパっといけないか?と思ったのが今回のスタート地点です。 このエントリでは今後はこのリポジトリのことを「static-pages-repo」と呼ぶことにします。 まず、前提として static-pages-repo は master に push されると public ディレクトリ以下が自動で社内向けのサーバーへデプロイされるようになっています。 └── public ├── webservice1/master/... ├── webservice1/hogefuga/... ├── webservice2/master/... ├── ... └── index.html // デプロイされているURLのリストが入っています 今回作る Action では、上記の構成に沿って以下のフローを実行します この配置に合わせて利用側から成果物をコピー コピーした成果物をコミット アクセスしやすいように public/index.html を更新 リモートへ push これを行うためには3つの引数が必要そうです。 public の配置を決定するためのプロジェクト名(prefix) public の配置を決定するためのブランチ名 成果物(コピー元)のパスの指定 つまり Action としてはこんな感じに利用できるのがよさそうです。 - uses : static-pages-repo with : project : webservice1 branch : feature/hogefuga source : dist 早速実装に...と行きたいところですが、気をつけるべき点が2つあります。 プライベートリポジトリのアクションは直接実行することはできない Workflow 内で他のリポジトリを扱う場合は secrets.GITHUB_TOKEN では権限不足 それぞれ見ていきます。 プライベートリポジトリのアクションは直接実行することはできない パブリックなリポジトリの場合は リポジトリ名やパスを指定することで 直接他のリポジトリから Action を参照して、利用することできます。ですが、プライベートリポジトリの場合はこの方法は使えないため、回避方法を考える必要があります。 static-pages-repo を clone...? と思いましたが、それだとあまり現状と変わらない...ので多少見栄えがよくなりそうだと npx で Action をインストールできるようにしてみることにしました。 一度 npx 経由で Action をコピーしてしまうことで、 自身のリポジトリ内の Action として実行します。 - run : npx https://github.com/baseinc/static-pages-repo install - uses : ./.github/actions/static-pages-repo Workflow 内で他のリポジトリを扱う場合は secrets.GITHUB_TOKEN では権限不足 GitHub Actions を使ったことがある人は知っているかもしれないのですが、元々 Workflow で使える変数として secrets.GITHUB_TOKEN が用意されています。通常であれば、このトークンを使えばプライベートリポジトリでも GitHub API を呼び出したり、リポジトリに Git での書き込みができるのですが、残念ながらこのトークンではリポジトリを跨いだ操作を行うことはできません。 これは代わりに Personal access token (以下PAT) を使用することで解決します。 actions/checkout にもこれに関する記述 があるので参考にしてみてください。 実際に Action を作る では、以上の2つの注意点に気をつけながら実際に Action を作っていきます。 Action の開発方法には Docker コンテナ版 と JavaScript 版 があり、今回は Git での操作がメインになりそうだなという理由でシンプルに実装できそうなDockerコンテナ版を選択しました。 公式ドキュメントにそってまず Dockerfile を作ります。alpine でもよいですが、 public/index.html を Node.js で作成しているため、 node:12 を選択しています。 ファイルの置き方ですが、これらを直接 Actions が実行するわけではないので(コピー元)、安直に action というディレクトリを作って配置することにしました。 FROM node:12 COPY entrypoint.sh /entrypoint.sh ENTRYPOINT ["/entrypoint.sh"] 次に action.yml を作ります。action.yml は引数や出力、実行方法を設定するためのファイルです。 name : 'static-pages-repo' description : 'deploy static pages' inputs : # ここに追加したキーが引数となります pat : description : 'Actionで必要になるPAT' required : true project : description : 'ディレクトリ名のprefix' required : true branch : description : 'ディレクトリ名' required : false default : 'master' source : description : '配信したいコンテンツへのパス' required : true outputs : url : description : 'デプロイ先となるURL' runs : using : 'docker' image : 'Dockerfile' args : - ${{ inputs.pat }} - ${{ inputs.project }} - ${{ inputs.branch }} - ${{ inputs.source }} 最後に entrypoint.sh を作って実際に動かすスクリプトを書いていきます。PAT を受け取って、static-pages-repoを操作していきます。 #!/bin/sh -l # 作業用のリポジトリをクローンします、PAT($1)を付加していることに気をつけてください git clone https:// $1 @github.com/baseinc/static-pages-repo.git .github/tmp/repo # project/branchなディレクトリを作って成果物をsourceからコピーしてきます mkdir -p .github/tmp/repo/public/ $2 / $3 cp -rT $4 .github/tmp/repo/public/ $2 / $3 cd .github/tmp/repo # 配置されたファイルへのリンクを含む public/index.html を生成します npm ci npm run build-index # 最終的に差分が生まれていれば git commit して push します git add . git config user.name actions git config user.email actions@binc.jp if git commit --dry-run > /dev/null ; then git commit -m " Update $2 / $3 " git push origin HEAD fi git push https:// $1 @github.com/baseinc/static-pages-repo.git master # このようにして値を出力しておくと、次の step でこれを元に検証したりすることができます echo " ::set-output name=url::https://static-pages-repo.com/ $2 / $3 / " Action を配信する npx スクリプトを作る npx にあまり馴染みのない方もいるかもしれませんが、簡単にいえば「インストールせずに一度だけ使えるパッケージ」という感じでしょうか。 package.json を作って以下のようなスクリプトで Action をローカルにコピーしています。 #!/usr/bin/env node const path = require( 'path' ) const fs = require( 'fs-extra' ) const src = path.resolve(__dirname, '../action' ) const out = path.resolve(process.cwd(), '.github/actions' ) ;(async() => { await fs.ensureDir(out) await fs.copy(src, `$ { out } / static -pages-repot`) } )() これを上のものと合わせると、最終的には他のリポジトリからこのような step で static-pages-repo デプロイすることができるようになります。 - run : npx https://${{secrets.PAT}}@github.com/baseinc/static-pages-repo install - uses : ./.github/actions/static-pages-repo with : pat : ${{ secrets.PAT }} project : webservice source : dist 便利そうに見えませんか?実際に PAT を設定するのは、利用側であることに気をつけてください。 これで static-pages-repo 側は完成です! ここまでのディレクトリ構造はこのようになっています。 ├── action │ ├── Dockerfile │ ├── action.yml │ └── entrypoint.sh ├── bin │ └── install.js // npx で実行されるスクリプト ├── webroot │ ├── webservice1/master/... │ ├── webservice1/hogefuga/... │ ├── webservice2/master/... │ ├── ... │ └── index.html ├── README.md ├── package-lock.json └── package.json 実際に Action を使ってデプロイする これで準備が整ったので、あとは各リポジトリに導入するだけです! 今回は、実際に BASE で運用しているパターンから3つをご紹介します。 master に push されたらデプロイ Pull Request が作られたらデプロイ Pull Request に特定のコメントがついたらデプロイ master に push されたらデプロイ 一番簡単でわかりやすい、馴染みのある感じだと思います。 ほとんどのリポジトリは、master にマージされたときに更新を実行するようにしています。 name : deploy master on : push : branches : - master jobs : build : runs-on : ubuntu-latest steps : - uses : actions/checkout@v1 - uses : actions/setup-node@v1 with : node-version : '12.x' - uses : actions/cache@v1 with : path : node_modules key : ${{ runner.os }}-node-${{ hashFiles('**/yarn.lock') }} restore-keys : | ${{ runner.os }}-node- - run : yarn install --frozen-lockfile - run : yarn storybook -o dist - run : npx https://${{secrets.PAT}}@github.com/baseinc/static-pages-repo install - uses : ./.github/actions/static-pages-repo with : pat : ${{ secrets.PAT }} project : webservice source : dist Pull Request が作られたらデプロイ UIコンポーネントのような基本的に Storyboard がセットになるものはこれを設定しています。 Pull Request が Open されると同時にデプロイされるため、レビューの際にはスムーズに Storybook を確認することができます。 name : open pull-request on : pull_request : types : [ opened, reopened ] jobs : build : runs-on : ubuntu-latest steps : - uses : actions/checkout@v1 - uses : actions/setup-node@v1 with : node-version : '12.x' - run : yarn install --frozen-lockfile - run : yarn storybook -o dist - run : npx https://${{secrets.PAT}}@github.com/baseinc/static-pages-repo install - id : deploy uses : ./.github/actions/static-pages-repo with : pat : ${{ secrets.PAT }} project : webservice branch : ${{ github.event.pull_request.head.ref }} # 実行時の event を受け取って使うことができるため、ここから branch 名を取得して決定しています source : dist - if : "steps.deploy.outputs.url" # 前の step が成功したときに発行される url があったら、PRのコメントにURLを書き込みます env : URL : ${{ steps.deploy.outputs.url }} API_ENDPOINT : ${{ github.event.pull_request.comments_url }} GITHUB_TOKEN : ${{ secrets.GITHUB_TOKEN }} # ここは同じリポジトリ内なので GITHUB_TOKEN でアクセスできます run : | curl -X POST -H "Authorization: token ${GITHUB_TOKEN}" -i ${API_ENDPOINT} -d "`printf '{ \" body \" : \" deploy to %s \" }' ${URL}`" Pull Request に特定のコメントがついたらデプロイ 必ずしも Storybook が必要ではないアプリケーションのリポジトリでは、Pull Request にコマンドコメントを書くことで任意のタイミングでデプロイが行えるようにしています。 これには issue_comment というイベントを使うのですが、これは特定ブランチには紐づかないイベントで、デフォルトブランチを起点に実行されるため少し複雑です。 name : on comment pull request on : issue_comment : types : [ created ] jobs : build : runs-on : ubuntu-latest steps : - id : check # コメントに deploy-storybook の文字列が含まれていて、pull_requrest なら実行します if : "contains(github.event.comment.body, 'deploy-storybook') && github.event.issue.pull_request" env : API_ENDPOINT : ${{ github.event.issue.pull_request.url }} GITHUB_TOKEN : ${{ secrets.GITHUB_TOKEN }} run : | # pull_request のデータを API 経由で取得して、ブランチ名を特定します branch=`curl -X GET -H "Authorization: token ${GITHUB_TOKEN}" ${API_ENDPOINT} | jq -r '.head.ref' ` echo ::set-output name=branch::$branch # このようにすることで id(=check) に紐づく outputs を step からも書き出すことができます # これ以降は branch が特定できていたら続行します - if : "steps.check.outputs.branch" uses : actions/checkout@v1 with : ref : ${{ steps.check.outputs.branch }} - if : "steps.check.outputs.branch" uses : actions/setup-node@v1 with : node-version : '12.x' - if : "steps.check.outputs.branch" run : | yarn install --frozen-lockfile yarn storybook -o dist npx https://${{secrets.PAT}}@github.com/baseinc/static-pages-repo install - id : deploy if : "steps.check.outputs.branch" uses : ./.github/actions/static-pages-repo with : pat : ${{ secrets.PAT }} project : webservice branch : ${{ steps.check.outputs.branch }} # 前の step で特定した branch 名を使用します source : dist - if : "steps.deploy.outputs.url" env : URL : ${{ steps.deploy.outputs.url }} API_ENDPOINT : ${{ github.event.issue.comments_url }} GITHUB_TOKEN : ${{ secrets.GITHUB_TOKEN }} run : | curl -X POST -H "Authorization: token ${GITHUB_TOKEN}" -i ${API_ENDPOINT} -d "`printf '{ \" body \" : \" deploy to %s \" }' ${URL}`" 一つ気をつけるべき点として、 job 自体にも if を設定でき もっと簡潔にかけそうなのですが、if が通らなかった場合に job が 0個となり Workflow 自体が失敗したことになってしまいます。 また、実際にはこれらと「 Pull Request が close された際にディレクトリを削除する Action 」をあわせて運用しています。 めでたしめでたし Storybook を Move Fast にデプロイできるようになったことで、 Speak Openlyな開発 にまた一歩近くことができたと思います。 お気づきの方もいると思いますが、単なる静的ファイルホスティングなので Storybook 以外にももちろん使うことができます! 明日は id:kmotoki と id:tatsuta2 です!
はじめに この記事はBASE Advent Calendar 2019の7日目の記事です。 devblog.thebase.in こんにちは、BASE投資部部長の菊地です!2019年は多くのBASE社員のマネーリテラシーを高めることができて、なかなか満足のいく一年となりました! さて、2019年業務の方がどうだったか振り返ってみると1月にエンジニアリングマネージャー(以下EM)に就任し、7月からは約20名のエンジニアが所属するService Devというセクションのマネージャーに就任しました。カレンダーを振り返ってみると今年は1on1を400回、採用面接は70回ほど行ったりとマネージャー業にどっぷりと浸かった一年となりました。 巷ではピープルマネジメントに費やす時間が増え、コードを書く時間が減ることが多いEMに対してネガティブな印象を持つエンジニアが多いようです。 しかし私はこの一年間EMとしての役割を果たすことでやりがいを感じることがたくさんありました。みなさんにEMって楽しそうだなと思って頂けたら嬉しいと思い、そのいくつかを紹介させて頂こうと筆を取りました。 BASEにおけるEMの役割 昨年、私の上司である@fshinが「 エンジニアとしてワクワクし続けるためのエンジニアリングマネージャという役割分担 」という記事を書いてEMの役割について言語化しています。 この記事にあるようにBASEのEMに求められる役割を一言で言うと 「事業、プロダクトに貢献しながら、チームのエンジニアの活躍にコミットすることで、メンバーの評価を上げること」 ということになります。 会社によってはEMが技術をリードする責任も求められる場合もあるようですが、BASEではエンジニアの上位職としてEMの他に技術でリードするテックリード(以下TL)という役職が存在するのでEMはピープルマネジメントだったりに専念することができます。(逆に言うとTLはピープルマネジメントを求められないので、技術に専念することができます) 私がやりがいを感じるとき さてここからは私がEMをやっていてやりがいを感じる瞬間を、それにまつわる取り組みなどを交えながら紹介させて頂きます。 チームメンバーを輝かせられたとき EMの仕事をマネジメントというとなんだか堅苦しい印象ですが、より強いエンジニア組織を作る、メンバーの成長を支援して市場価値を高めるという側面ではプロデューサーと呼んだ方が個人的にはしっくりきます。もっと輝けるのではと思った人をプロデュースして、それまで以上の輝きを放つようになったときに私はEMとしてのやりがいを感じます。 例えば黙々とコードを書いていてあまり目立たっていないと感じていたチームメンバーが、私が背中を押すことでグングンと成長し今では社内の誰からも信頼され頼られる存在に成長した人がいます。これぞEM冥利に尽きる瞬間です。私は彼の社内でのプレゼンスを上げるために次のような働きかけをしました。 アイコン重要 BASEではSlack, G Suite(GmailやGoogle カレンダー等), Asana, Kibelaなどなど多くのツールを使用しています。社員数が100人を超え社内の認知を得ることが難しいフェーズになっているので、アイコンにこだわりを持つことは社内で認知されるために非常に重要だと考えていました。 しかし当時彼はアイコンを何も設定していませんでした。そこで私は「アイコンは必ず設定しよう」、「各ツールでアイコンを統一しよう」、「アイコンはキャッチーなものにしよう」とアドバイスをしました。その結果今では社内のほとんどの人が彼のアイコンを見れば彼のことを想起できるようになり認知度が飛躍的に高まりました。 他部署の人との関わり重要 日常的にカスタマーサポートチーム等の他部署からお問い合わせ調査依頼がやってきます。こういう依頼があった際はなるべく拾いにいくように伝えました。理由はお問い合わせの調査をすることでBASEのサービスやシステムの理解が深まりますし、他部署のメンバーからの信頼を得ることができるからです。 最初の頃は積極的には拾いにいっていなかったので、私が拾って彼に振っていたのですが、いつしか抵抗がなくなったのか今では自ら進んで拾いに行ってくれるようになりました。自分からタスクを取りにいくような働き方になった結果、多くの人から頼られる存在になりました。 理想としているチームに近づいているのが目に見えたとき 突然ですが、 wevox というサービスをご存知でしょうか。wevoxとは「定期的なアンケートを実施することで、社員の仕事や会社とのエンゲージメントを数値化しよう」というサービスです。 BASEの場合は3ヶ月に一度全社員からアンケートをとって、「上司との関係性は10点中9点で世の平均よりも良好です」といったようにたくさんの項目が個人レベルで数値化されマネージャーに共有されます。(匿名と実名での回答が設定でき、BASEでは現在実名で回答する形式をとっています) 私は「明るく、楽しく、健康的」なチームを目指しているので、wevoxでそういった項目の数値が高かったり前回よりも改善されていると自分の日々の取り組みの成果が現れ、目指しているチームに近づいているのだなと喜びを感じます。 メンバーと頻度多くコミュニケーションを取ることでお互い何でも言い合える関係を築くことがEMにとって何よりも大事だと考えているので、そのためにいくつかの取り組みを行なっています。 1on1 もはや定番になっているかもしれませんが、1on1がEMにとって最も重要な業務であると考え、全ての業務より優先して全メンバーと毎週30分行なっています。主に1on1のバイブルとなっている『 ヤフーの1on1 』に書かれている下記のようなことを実践しています。 この本には1on1の目的は「メンバーの才能と情熱を解き放つこと」と書かれていますが、まずはシンプルに雑談でもいいからとにかく毎週30分行うということに専念しました。 定番の質問 自分が部下として1on1をしていた時を思い返しても「何かお困りごとはありますか?」といきなり聞かれてもなかなか思いつかないものでした。ですので「以下の質問を毎回聞くので何か考えてきてください」とチームメンバーに伝えるようにしています。これらの質問も『ヤフーの1on1』を参考にしたものです。 今日は何の話をしましょうか? 何かお困りごとはありますか? 私(EM)にできることはありますか? 毎回聞くからねと宣言することによって、1on1の時にメンバーが議題を用意してくれるということが増え、1on1の充実度が上がったように思います。 予習 1on1を行うにあたって1人につき15分ほど予習を行なっています。チームメンバーには毎週末に週報を書いてもらっているので週報を確認して一週間にどのようなことをしていたのか把握したり、タスクの進捗は順調そうか確認したり、これまでの1on1の議事録を見返したりすることで話したい内容をある程度箇条書きして臨むようにしています。そういった準備をすることでチームメンバーが今困っていることなどに気づきやすくなるといった効果があると感じています。 議事録 毎週10人近くのメンバーと1on1を行なっていると、誰が何を言っていたのか分からなくなることがあります。また自分が誰に何を言っていたのかも分からなくなることがあります。そのようでは信頼を積み重ねることは難しいと感じ、毎回議事録をとって、終わったら議事録の内容をSlackでDMするようにしています。信頼を積み重ねるにはとても大事だと認識しているのですがけっこうサボりがちなので反省しています、2020年はサボらないで頑張りたい。 ニチレイ(日例) 毎日12時からスタンディング形式でチームメンバーが集まって今やっていることや困っていることを5分ほどの時間を使って共有しあう時間をとっています。議論したり雑談をしたりとチーム内のコミュニケーションを毎日作れるのでニチレイの重要度はとても高いと考えています。 Unipos Unipos というのは社内のメンバー間で感謝を送り合うサービスです。 感謝することも、感謝されることもやりがいを持って業務に取り組むためにはとても重要であると考えているのでチームのメンバーには感謝のハードルを極力低く設定して、たくさん感謝を送り合うように伝えています。 チームメンバーを採用できたとき BASEではスクラム採用に取り組んでいて、エンジニアの採用にはEMがフルコミット(スカウト、カジュアル面談、書類選考、面接、オファー面談、採用会食など盛り沢山)しています。BASEのスクラム採用について興味のある方は弊社採用マネージャーの 米田が登場している記事 をご参照ください。 さて、採用活動がEMにとって最重要タスクであることは言うまでもありませんが、一方でなかなか成果に結びつきにくいものです。100人カジュアル面談を実施して2,3人入社して頂ければ御の字といったところではないでしょうか。 BASEから内定をお出ししている方は他社でも複数内定が出ていることがほとんどです。複数内定が出ている中からBASEを選んで頂けるように毎度毎度全社総動員でアトラクトをしています。そうやって最終的にBASEを選んで頂けた際にはみんなで喜びます。この瞬間がEMとしてやりがいを感じる瞬間です。 採用活動に携わるようになって良かったと思う点をいくつか紹介します。 視座が上がった 自分が面接官を担当するということはBASEのサービスであったり、ビジネスモデル、社内環境のことなどを魅力的に語れる必要があります。 また候補者からは以下のような質問をはじめ、様々な質問をされます。これらに自信を持って答えられなければ候補者に不安を与えてしまうことになり、BASEを選んでもらうことは難しくなります。 BASEはどんな会社ですか なぜBASEに転職したのですか 今抱えてる組織的な課題はなんですか 評価の方法を教えてください などなど 採用活動に携わるようになってから、プレイヤーの時と比べて一段高い視座を持てるようになったと感じています。 技術力が高まった 先日 「エンジニアリングマネジャーになってから技術力が伸びるパターン」 という記事が話題になりましたが共感を持って拝見しました。 候補者と面談をするにあたって職務経歴書に書いてある知らない用語を調べたりすることで、決して深くはありませんがプレイヤーの時には得られなかった広い知識が身についたと感じています。 相場が分かった スカウト等の採用活動をWantedly, LAPRAS, 転職ドラフト, Green, Findyなどたくさんのサービスを利用して毎日のように転職顕在層/潜在層のエンジニアをチェックしています。この経験を通じてスキルだったり会社だったり年収の相関が大体分かるようになり、今後の自分のキャリア設計に参考になると感じています。 最後に この一年間EMとして様々な経験をさせて頂きました。つらいなーと思うこともたくさんあった気がしますが、一年を振り返ってみるとつらかったことは思い出せずやりがいを感じたことばかりが思い出され不思議なものです。まだまだEMとして期待に応えられず不甲斐ないことが多くありますが、優秀なチームメンバーに支えられ何とか会社の成長にコミットしてこれたと思っています。2019年はチームメンバーに感謝の毎日でした。 2020年は「BASE」をより良いサービスに成長させられるように、エンジニア組織をまだまだ成長させていきます!2020年が今からとても楽しみです! 明日は、テックリードの右京さんとデザイナーの石井さんです!お楽しみに!
この記事は、「 BASEアドベントカレンダー2019 」7日目の記事です。 devblog.thebase.in BASE株式会社で採用をやっているaipon( @AiYoneda )です。 今回は、Slack Workflow Builderを使ったワークフロー自動化についてご紹介します。 プログラミング知識がなくとも、簡単に作成することができるので、まだ試したことのないという方はぜひお試しあれ! BASEでのSlack Workflow Builderの活用方法 人事関連のワークフローについて、労務回りはSmartHR、採用はHERPを使っていますが、利用サービスで吸収できない細かなワークフローについては主にSlack Workflow BuilderもしくはGoogleフォームを利用しています。 Slack Workflow BuilderとGoogleフォームは、下記のような使い分けをしています。 回答を一覧で見る必要がない・見返す頻度が少ない(Slackで回答が流れても困らない)、回答内容をもとにシート上で計算等を行う必要がない→Slack Workflow Builder 回答を一覧で見たり、見返す頻度が多い(Slackで回答が流れると困る)、回答内容をもとにシート上で計算を行う必要がある→Googleフォーム 回答をスプレッドシートで一覧で見たいもの、計算式を組んだりVLOOKUPなどあれこれしたいものに関してはGoogleフォームで回答してもらい、回答があれば特定のチャンネルで通知を飛ばしています。(Slackへの通知の飛ばし方は こちらの記事 をご参考にされてください。) 人事関連では、主に下記でSlack Workflow Builderを利用しています。 採用会食費用の補助申請 社内懇親会費用の補助申請 オフィスアンケート(社内向けサービスについての意見収集) 相談窓口(健康相談、働き方相談など) 人事制度やリファラル採用など、全社に関わる施策を実施する際は運用方法が全社に浸透するかがポイントになりますが、Slack Workflow Builderはフォームへの回答、回答の閲覧はSlack上でシームレスに行うことができ、全社の利用の障壁が低くなるというメリットがあります。 Workflowの作り方 ここからは具体的な利用方法についてのご紹介です。 今回は、採用会食費用補助の申請ワークフローを作る例とします。 採用会食補助のワークフローは下記です。 利用者(申請者)が補助申請 採用チームが内容を確認 問題なければ採用チームが稟議申請 稟議決裁がおりれば、採用チームが稟議を利用者(申請者)に共有 利用者(申請者)が会食実施 利用者(申請者)が経費精算 1から2のワークフローをこれまでGoogleフォームで行っていたのですが、URLが見つからなくなったり、Slackから遷移するのが少し面倒ではないかと懸念していたので、Slack Workflow Builderがリリースされたのを機に移行してみました。 1から2のワークフローをSlack Workflow Builderで実現する際のワークフローを細かく分解すると下記になります。 利用者(申請者)が補助申請 利用者(申請者)のDMに回答内容が送信 上記と同タイミングで、採用チームのチャンネルに回答内容を含むカスタマイズされたメッセージが届く 採用チームの担当者が内容を確認 それでは実際に作っていきます。 ワークフロービルダーを起動 メニューの一番上のワークスペース名をクリックすると、「ワークフロービルダー」があります。 これをクリック。 これまで作成したワークフローが表示されます。 ワークフローを新規作成 先ほどの画像の、右上の「作成ボタン」をクリック。 ワークフローに名前を付けます。 今回の例では、「採用会食費用補助申請フォーム」と入力します。 ワークフローの開始を設定できます。 今回は社内の誰かが申請しようと思ったときにワークフローを開始したいので、「アクションメニュー」を選択。 ワークフローを開始するチャンネルを選択します。プライベートチャンネルにも設定できます。 今回は #general とします。 「短い名前を追加」は、ワークフローを開始する際にチャンネルで表示される名称です。 利用する人が分かりやすい名称に設定するのがよいでしょう。 アクションメニューを設定するとこのように表示されます。 ワークフローの概要を設定したら具体的なステップを設定していきます。 最下部の「ステップを追加」からステップの追加を行うことができます。 ステップには「メッセージの送信」と「フォームを作成する」の2種類を設定することができます。 今回の場合、採用会食を実施する日時や参加するメンバー等の内容を申請者に入力してほしいので、フォームの作成を行います。 質問と回答の形式を設定してみました。 回答のタイプは入力形式のものやリスト形式、Slackのユーザー名やチャンネル名を選択する形式もあります。 上記はすべて「短い回答」でフォームを作成しています。 また回答者に回答内容を送っておきたいので、「提出された回答をチャンネルまたはDMで他のメンバーに送信する」にチェックを入れ、送信先を該当のワークフローをクリックしたユーザーに設定します。 ここまでで下記が完了しました。 利用者(申請者)が補助申請 利用者(申請者)のDMに回答内容が送信 あとは下記のステップです! 上記と同タイミングで、採用チームのチャンネルに回答内容を含むカスタマイズされたメッセージが届く 採用チームの担当者が内容を確認 特定のDMやチャンネルにメッセージを送信するステップを作成します。 冒頭と同じように、ワークフローの概要から「ステップを追加」をクリックします。 今度は「メッセージを送信」をクリックします。 回答の送信先チャンネルの指定と、メッセージを作ります。 メッセージには変数を入れられるので、それぞれの項目の回答内容や、フォームを提出したメンバーをメッセージに加えることができます。 Previewを見ながらメッセージを作成できるのもいいですね! メッセージができたら保存します。 ワークフローの作成はここまでです。 完成したら、ワークフローの概要から「公開する」をクリックしましょう! 稲妻アイコンをクリックしてフォームが意図した通りにできていれば完成です! 採用チームのチャンネルにはこのように通知が飛んできます。 おわりに 今回は誰でもできるワークフロー自動化の例をご紹介しました。 バックオフィスのメンバーが新しい制度や取り組みをする際に業務フローに手間がかかることが懸念の一つとしてあがることがあるかと思います。 こういった便利なツールを使うことで、制度起案者・運用者の心理的障壁がなくなるといいなーと思っています。 明日は、デザイナーの石井さんとテックリードの右京さんです!お楽しみに!
この記事はBASE Advent Calendar 2019の6日目の記事です。 devblog.thebase.in こんにちは、Product Management Group所属の 森本です。BASEには、2019年8月に入社し、Eコマースプラットフォーム「BASE」の企画やディレクションの業務に従事しています。 入社してからは、PayPal決済の導入のディレクションと、次期のプロジェクト企画などに携わっていたり、日が浅いうちからプロジェクトを任せてもらえる環境で、個人的には想像通りでとてもありがたいなと感じています。 BASEのProduct Managemantチームは、戦略、製品、事業数値まで責任をもちます。いろんなバックグラウンドをもっている方が在籍しているので、CRMに強い、計測に強い、など各々の強みを活かしてプロジェクトを牽引していることも多々あります。 私はエンジニアあがりのディレクターではなく、新卒で広告系のプロダクションでWEBディレクターを経験し、次に主にアパレル系のECサイト構築や外部連携・運用の要件定義や進行管理を担う PM / Dを経験してきました。 なので、あまりテクニカルなお話はできないのですが、BASE入社4ヶ月のディレクターがプロジェクトを進めていて良いな(今後も続けていきたい)と思ったものごとをご紹介したいと思います。 SQLをすこしかけるようになり、ほしい情報を得ることができた BASEではエンジニアやマーケッターだけでなく、全員にRedashを解放していて、みんな平等にデータを調べられる機会が与えられています。恥ずかしながら私は今までSQLの知識がなかったのですが、すこし勉強してRedashでほしいデータを見れるようになりました。まだまだほんの少しですが、以前から習得したかったことだったのでうれしい。 ちなみに、SQLの学習は Progate で、にんじゃわんこのデータベースをなんどもSELECTし、問題を実践で解いて勉強しました。失敗と成功をくりかえしていると、いつの間にか自信もつくので、環境を構築しなくてもすぐに学習ができるProgateに感謝です。(PR記事ではないです) 何より、新たなプロダクトに携わると、どんなテーブルで構成しているのか?どれくらいこの機能は利用されているのか?などをざっくりと把握したいときに、そこでエンジニアに聞いたり、過去の設計書などから情報を得る、といったことをせず、自らの手でシュッと調べることができるのも、とても効率的で前向きな姿勢でよいなと思います。(もちろん複雑な条件でお手上げの時は、詳しい人に聞くと教えてくれる環境でありがたいです...) UI/UXのレビューミーティングによる思考共有 各プロジェクトのデザインについて、毎週2回 UI/UX MTGという会議が開催され、そこでは各プロジェクトで進めているデザインのレビューが行われます。自分の携わっているプロジェクトはもちろんのこと、他のプロジェクトで進めているデザインなどの経緯やデザイン意図を聞けるので、考え方が常にチーム内で共有され、アップデートし続けることができるのはとてもよいなと思います。 法人向けのプロダクトでは、比較的優先度が下げられがちな管理画面のUI/UXですが、「BASE」は「誰でも簡単に使える」というコンセプトがサービスの根幹になるので、このミーティングはとても大切な時間であると思うのです。 個人的には、みんなのプロダクト愛を感じることができたり、まだまだ詳細を把握できていない機能を知ることができたりするので、そういった面でもこのミーティングが好きです。 このプロジェクト・機能は何のために?をしたためる シンプルでわかりやすく、ユーザーにどんな価値を提供できるのか、をプロジェクトごとや小さな出来事でも書く習慣があること。 日常的に、Slackなどでも「これはオーナーさんのどういう課題を解決できるのか」ということをよく目にすることがあり、BASEのメンバーは常にオーナーさんのことを考えている集団だなぁと感じることが多々ありますが、とくにプロジェクトの企画をたてるときにわかりやすく明文化することで、チームメンバーへ共有するときや、プロジェクトを進める中で判断に悩んだときに読み返すことができるブランケット(安心毛布)のような存在になります。 また、他のプロジェクトの記事(Kibelaを利用しています)を見て、めっちゃいいなと感じることは自分のプロジェクトにも反映することができるので、今後もしっかりと書いて、ブレずにいいプロダクトに反映していきたいです。 まとめ 上にあげたことすべてに言えるのですが、単発的に行うのではなく、日々コツコツと積み上げていくことができているからこそ価値があるのだと思います。 ただ、努力し続けていても気持ちがネガティブだと、どんどん悪い方へいってしまいがちなので、常にポジティブに取り組むことが大事です。行動指針のひとつに、「Be Hopeful」があるように、BASEのメンバーはポジティブな思考のひとが多いように思います。私もBe Hopefulにagreeです。 すこしルー大柴風になってしまいましたが、来年もコツコツと楽しくやっていきたいと思います! 明日のアドベントカレンダーは、Service Devマネージャーの菊地さんと人事の米田さんです!
この記事は BASE Advent Calendar 2019 の6日目の記事です devblog.thebase.in どうもこんにちは、Frontend Groupの青木です BASEではVue.js+TypeScriptを採用したフロントエンド開発を行っています 今回のブログでは、私が業務で直面したちょっとした課題を、Vue.jsのカスタムディレクティブを実装して解決し、npmのパッケージとして公開した話をします 何を作ったか vue-remove-whitespace - npm spanやaなどのinline要素内での改行による意図しないスペースを削除するVue.jsのカスタムディレクティブを作りました 挙動としては < p v-remove-whitespace> お問い合わせは < a href = "#" > こちらから </ a > お願いします </ p > というvueのtemplateが、レンダリングされる際には < p > お問い合わせは < a href = "#" > こちらから </ a > お願いします </ p > となります ディレクティブの指定がある場合と無い場合では、 お問い合わせはこちらからお願いします お問い合わせは こちらから お願いします のような見かけ上の違いが生じます この程度の文字数であれば、最初から改行を含めなければいいのでは?と思うところですが、各要素のattribute属性やイベント修飾子、propsなど入力するものが増えたりすると、改行したくなったりすることもあり、解決できるものを作ろうと思いました npmのpackageとして公開するまでにしたこと 公式のカスタムディレクティブのドキュメントを読む https://jp.vuejs.org/v2/guide/custom-directive.html 使っているライブラリのソースコードを読む https://github.com/logaretm/vee-validate/blob/v2/src/directive.js 現在公開されているnpmのカスタムディレクティブを探し、ソースコードを読む https://www.npmjs.com/search?q=keywords:vue https://www.npmjs.com/search?q=keywords:vue-directive 調べている過程で、 vue-focus というライブラリの開発構成がシンプルそうでいいなと思い、構成をforkして、機能実装を済ませ、初回のリリースを終えました テストの実装と、TypeScriptの開発環境で使いやすくする 公開して満足してしまい、追加で開発しようと思っていたことができていなかったのですが、今回のアドベントカレンダーを機会に、テストの実装とTypeScriptの開発環境で使いやすくしてみました テストを実装するまでにしたこと 公式の単体テストのドキュメントを読む https://jp.vuejs.org/v2/guide/unit-testing.html デモリポジトリをクローンし、テストを体験する https://vue-test-utils.vuejs.org/ja/ 実際にテストを書いてみる https://github.com/aokiken/vue-remove-whitespace/tree/master/ tests import { removeWhitespace } from '../index' export default { directives: { removeWhitespace } , template: ` <div v-remove-whitespace> <span>1</span> <span>2</span> <span>3</span> <span>4</span> <span>5</span> </div> `, } import { mount } from '@vue/test-utils' import InlineElementWithLineBreak from './InlineElementWithLineBreak' describe( 'InlineElementWithLineBreak' , () => { const wrapper = mount(InlineElementWithLineBreak) it( 'renders the correct markup' , () => { expect(wrapper.html()).toBe( '<div><span>1</span><span>2</span><span>3</span><span>4</span><span>5</span></div>' ) } ) } ) 簡単なテストですが、期待通りに動いていることが確認できました🎉 TypeScriptの開発環境で使いやすくするためにしたこと index.d.tsを書く https://github.com/aokiken/vue-remove-whitespace/blob/master/index.d.ts import { DirectiveOptions } from 'vue' export const version: string export const removeWhitespace: DirectiveOptions export const mixin: { directives: { [ key: string ] : DirectiveOptions , } , } package.jsonにtypesを追記する https://github.com/aokiken/vue-remove-whitespace/blob/master/package.json#L61 これらを追記したおかげで、TypeScriptのプロジェクトでも、型情報が参照できるようになりました🎉 最後に 今回のnpm公開は、実装の過程でコンパクトな機能が出来たので、これなら公開できそうと思ったことと、 まずはやってみよう という思いから挑戦してみました おまけ1: いくつか対応策はあった 閉じタグの>を改行すればスペースは入らない < div > < span > 1 </ span >< span > 2 </ span >< span > 3 </ span >< span > 4 </ span >< span > 5 </ span > </ div > 開始タグの<tag以下を改行すればスペースは入らない < div > < span > 1 </ span >< span > 2 </ span >< span > 3 </ span >< span > 4 </ span >< span > 5 </ span > </ div > コメントアウトを挟めばスペースは入らない < div > < span > 1 </ span > <!-- --> < span > 2 </ span > <!-- --> < span > 3 </ span > <!-- --> < span > 4 </ span > <!-- --> < span > 5 </ span > </ div > おまけ2: cssで工夫するとスペースや改行が入り込む https://codepen.io/aokiken/pen/vYBLMyv 気にならなければ良い気もしている おまけ3: 英語では困らない そもそも単語区切りでスペースが入るので、困らないのか、と実装してて気がつきました 逆に、日本語、中国語、タイ語などの言語では有効そう 明日はService Devマネージャーの菊地さんと人事の米田さんです!
この記事は BASE Advent Calendar 2019 の5日目の記事です。 devblog.thebase.in こんにちは、Data Strategyマネージャーの鈴木です。社内には鈴木が沢山いますので、普段は下の名前(りょう)で呼ばれています。 Data Strategyチームでは、BASEに存在するデータを活用する事によって、人手ではカバーできないようなきめ細かな価値や気付きを、データ分析や機械学習を中心とした取り組みを通じてプロダクトやサービスに提供しております。 中でも以下の課題については、Data Strategyチーム(以下DSチーム)の重要課題として取り組んでおります。 オーナーズ(ショップオーナー)の方々の成長をどのように支えていくか 購入者の方々が安心して購入できるプラットフォームをどう作るか オーナーズと購入者の接点を商品やショップを通じていかに増やすか 今回はその中から、いくつか具体的にユーザーの皆様の利便性向上のため活用している部分についてざっくりと紹介したいと思います。 (技術的側面につきましては、DSメンバーがこれからアドベントカレンダーで紹介していきますのでお楽しみに!) 1. オーナーズの方々の成長をどのように支えていくか オーナーズの成長を支えるには、様々なポイントがありますが、その中でも一番最初に行うべき事は、ショップをいかに数値で客観的に見る事が出来るようにする事と考えております。 そこで、DSチームではオーナーズの方々の様々なデータを提供するための仕組みづくりを進めてきました。 その一つが、今年の夏から進めてきているオーナーズの方々の管理画面にあるデータページ向けデータとなります。 データページの紹介はこちら: note.com 「BASE U」の記事はこちら: baseu.jp こちらはAWSのAurora/DynamoDBの他、ETLやLambda/Fargate/SQS/SNSなどを活用したプラットフォームとなっており、現在80万ショップの日次・週次・月次のデータを個別集計およびリクエストベースで表示する仕組みとなっております。 また、当日分データを表示するためのデータのリアルタイム取得に向けたプラットフォーム作りも並行して行っており、先日公開されました。 note.com 一方、データだけあってもその活用法がないと活かす事が難しいため、来年はそのデータ活用方法も含めて提案していければと思っております。 2. 購入者の方々が安心して購入できるプラットフォーム 「BASE」では販売が制限されている商品があります。 help.thebase.in 現在、各商品に対しての自動検知を行い、もし検知した場合に社内チームに連絡する仕組みがあります。 この通知には、上記の登録が制限された商品以外にも、話題になっている商品などの検知も行っており、ビジネスチームのサポートも同時に行っております。 3. ショップオーナーズと購入者の接点を商品やショップを通じていかに増やすか 「BASE」においてショップオーナーと購入者の間には、さまざまな接点が存在しますが、大きく分けて販売の場とブランディングの場に分かれます。これらは独立型ECや従来のSNSなど独立している場合もありますが、ポップアップやモール型ECなど場として混在しているケースもあります。 その中でもデータによって効率的に接点を増やす事が行える場所としてはモール型ECがあり、DSチームでは発足直後からショッピングアプリ「BASE」の機能改善を行ってきました。 ここではいくつかデータを活用した箇所を紹介したいと思います。 今日のおすすめ商品 アプリユーザーの興味の対象を推定し、 「BASE」でよく売れている商品の商品や、興味を引いている商品と、マッチングを行なっています。 買い物や参照傾向も一定の影響を及ぼしていますので、興味のある商品群を定期的に探していると、探さなくても興味がありそうな商品がよくでてくるようになります。 なお、類似商品も別軸で見ているので、あまりにも同じような商品が存在するときは、ショップの運営状態から優先的に紹介するような仕組みになっています。 おすすめショップ 「BASE」の人気ショップの中から、一定の基準を満たしたショップを紹介しています。 アプリを使い続けていくと、より好みのショップがおすすめされていくようになります。 ショップ側も、継続的に運営を続けていくと、購入者により近い存在になるような仕組みを取っています。 特集 パーソナライズされた情報と過去のトレンドを元に、購入者が興味が持ちそうなテーマを毎日作成しています。 タイトルは過去数年の間に手動で作成していたデータを元に自動生成しています。 こちらは、時代によって旬の言い回しが変わってくるため、定期的にモデルの更新を行っています。今の時期ですと、カレンダー・クリスマス・ギフト等と言った商品が紹介されています。 技術的な内容については、DSチームメンバーの齋藤( @pigooosuke )の記事に詳しく記載しております。 devblog.thebase.in まとめ いくつか取り上げてきましたが、BASEにおいては、データ活用を非常に重要視した取り組みをおこなっており、それを実現するためにメンバーも続々と加入しております。 今後とも、データ活用をさまざまな手法で行い、皆様の利便性に貢献していければと思います。 明日は、Frontendチームの青木さんとProduct Managementグループの森本さんです!お楽しみに!
この記事はBASE Advent Calendar 2019の5日目の記事です。 devblog.thebase.in こんにちは。BASE株式会社でOwners Growthチームのマネージャーをしている遠藤です。 Owners Growthチームのミッションは、「BASE」に登録しているショップの成長を支援したり、運営のサポートを行うことで「BASE」でのショップの開設・運営の体験を向上させ、ショップさんに継続的にご利用いただくことです。 今回はアドベントカレンダーということで、このミッションを遂行するために普段どんなことを行なっているのか、特にOwners Growthチームで行なっている戦略や戦術を決めるための仮説検証プロセスを簡単にご紹介したいと思います。 そもそもOwners Growthってどんなことを考えながら仕事をしているの?とか、どうやって仮説を検証しながら戦略を作るの?っていう疑問に答えられるような記事にしたいなと思いました。 ただし実際の社内データは直接お見せことができないので、身近に存在する問題を探した結果、投資というテーマを題材にしてみました。 目標から考えるKPI設計と仮説検証プロセス I君は最近、株に興味をもち投資を始めようと思い立ちましたが、どの会社の株を買えばいいのかわからず悩んでいます。 ここでのI君の目標は何をどのくらい買うのか、つまり 投資方針を決めること になります。 そこでI君はこの目標を達成するための要素として 株価が上がりやすいこと リスクが抑えられること の2つがあることに気づき、まずはこの株価の上がりやすさとリスクを定量化する必要があると考えました。 KPI設計 上がりやすさは過去の株価の変化率とし、リスクを株価の標準偏差と定義します。 つまり、過去の株価がプラスに変化してきた銘柄は今後も上がりやすく、上下の変動が大きかった銘柄はリスクが大きいと考えたのです。 そしてこの変化率(Return)が大きくてリスクが低くなるような以下のような指標を作ってKPIとし、そのKPIが最大になるようにします。 IT企業に勤めているI君は最近よく耳にするA社とB社に絞って、どちらに投資したらより大きなKPIが得られるのか調べようと思いました。 仮説1の作成 ここで、 どちらかKPIの高い銘柄に投資することが投資効率が高い という仮説を立てます。 この仮説を検証するために、それぞれの銘柄のKPI指標を計算して比較してみようと思います。 検証のための下準備 まず以下のように2社の株価の取得してcsvファイルに保存するようなスクリプトを実行しました。 getPrice.py #python from datetime import datetime from concurrent import futures import pandas as pd from pandas import DataFrame import pandas_datareader.data as web def download_stock(stock): try: print(stock) stock_df = web.DataReader(stock,"yahoo", start_time, now_time) stock_df['Name'] = stock output_name = stock + '_data.csv' stock_df.to_csv(output_name) except: bad_names.append(stock) print('bad: %s' % (stock)) if __name__ == '__main__': ticker_stocks = ["ticker_a","ticker_b"]#ticker array workers = len(ticker_stocks) with futures.ThreadPoolExecutor(workers) as executor: res=executor.map(download_stock, ticker_stocks) 仮説1の検証 上のスクリプトを実行して得られたcsvファイルを使って、それぞれの銘柄を評価していきます。 まずKPIを算出してみます。 getKpi.R #リターンを計算する a = read.csv("/Users/tsuyoshiendo/Documents/flask/stock/a_data.csv") b = read.csv("/Users/tsuyoshiendo/Documents/flask/stock/b_data.csv") colnames(a)[7] = "adjClose" colnames(b)[7] = "adjClose" a$ret = as.numeric(a$adjClose) / as.numeric(c(NA, a$adjClose[1:nrow(a)-1]))-1 b$ret = as.numeric(b$adjClose) / as.numeric(c(NA, b$adjClose[1:nrow(b)-1]))-1 #余分なデータを削除 a=a[!is.na(a$ret),c("Name", "Date", "ret")] b=b[!is.na(b$ret),c("Name", "Date", "ret")] #リターン ex_a = mean(a$ret) ex_b = mean(b$ret) #リスク sd_a = sd(a$ret) sd_b = sd(b$ret) #KPI kpi_a = ex_a/sd_ap#a社のKPI kpi_b = ex_b/sd_am#b社のKPI paste("a's KPI = ", kpi_a, ", b's KPI = ", kpi_b, sep="") 実行結果 a's KPI = 0.0549560106863018, b's KPI = 0.081528997872345 aのほうがKPIの値が高く、リスクに対して相対的にリターンが大きいといえます。 検証結果の深掘り しかし、ここで得られた結果に基づいて B社だけに投資しようと考えるのは拙速 です。 KPIを設計する際に作ったリスク指標は標準偏差、すなわち株価変動の大きさを表しているのですが、2社の株価が全く同じように連動しているとは考えづらいため、それぞれの組み合わせ方によってはリスクを抑えられる可能性があります。 そのため、2社の組み合わせパターンに応じたKPIを算出してみたのが次です。 twoAssets.R #covariance cov_a_b <- cov(a$ret, b$ret) weight_patterns <- seq(from = 0, to = 1, length.out = 1000) two_assets <- data.table(wx = weight_patterns) two_assets[, wy := 1 - wx] two_assets[, ':=' (ex_p = wx * ex_a + wy * ex_b,sd_p = sqrt(wx^2 * sd_a^2 + wy^2 * sd_b^2 + 2 * wx * wy * cov_a_b))] two_assets <- two_assets[wx >= 0 & wy >= 0] two_assets[, ir := ex_p / sd_p] ggplot() + geom_point(data = two_assets, aes(x = sd_p, y = ex_p, color = ir)) + geom_point(data = data.table(sd = c(sd_a, sd_b), mean = c(ex_a, ex_b)), aes(x = sd, y = mean), color = "red", size = 3, shape = 18) + theme_bw() + ggtitle("efficient frontier") + xlab("Standard Deviation") + ylab("Expected Returns") + scale_y_continuous(label = percent) + scale_x_continuous(label = percent) + scale_color_gradientn(colors = c("blue", "red"), name = "ir ratio", labels = percent) + theme_gray (base_family = "HiraKakuPro-W3") + expand_limits(x = 0, y = 0) two_assets[ir == max(two_assets$ir)] この結果、以下の曲線のような図が得られました。 この曲線はA社とB社の組入比率1,000通りのパターンに対して、縦軸にリターン、横軸にリスクをとってプロットしたものです。 そして曲線の両端がA社とB社の2社にそれぞれ単独に投資した場合になります。 軸の取り方から左上に行くほどKPI指標の大きな(=最適な)組み合わせであると言えますが、これを見ると両端の点ではなく、曲線上の中間地点の方がKPIの値が大きいことがわかります。 実際に最も高いKPIとなった組み合わせはA社に24%、B社に74%投資したパターンでそのKPI指標は0.0832と、それぞれ単独で投資をした場合の0.055と0.082よりも大きいです。 これら検証の結果、仮説1は誤っていた(棄却された)、すなわちどちらかだけに投資するのではなく、リターンとリスクに応じて投資比率を変えるべきであることがわかります。 ここで次の疑問が湧きます。 複数の会社に投資をすることで KPI指標をさらに高めることができるのではないか、です。 最初の仮説検証プロセスの結果、KPIをさらに高めるための次の仮説を検証していきます。 仮説2の作成 「組み合わせ方によって最も高いKPIを予測することができる」。 仮説2の検証 I君はこの仮説2を検証するため、C社とD社にも投資してみようと思いました。 まずC社を合わせた3社のパターンで先ほどの図を描いてみます。 threeAssets.R weight_patterns <- seq(from = 0, to = 1, length.out = 1000) #expected_return ex_a = mean(a$ret) ex_b = mean(b$ret) ex_c = mean(c$ret) ex_d = mean(d$ret) sd_a = sd(a$ret) sd_b = sd(b$ret) sd_c = sd(c$ret) sd_d = sd(d$ret) cov_b_a <- cov(a$ret, b$ret) cov_a_c <- cov(b$ret, c$ret) cov_c_b <- cov(c$ret, a$ret) three_assets <- data.table(wx = rep(weight_patterns, each = length(weight_patterns)),wy = rep(weight_patterns, length(weight_patterns))) three_assets[, wz := 1 - wx - wy] three_assets[, ':=' (ex_p = wx * ex_a + wy * ex_b + wz * ex_c, sd_p = sqrt(wx^2 * sd_a^2 + wy^2 * sd_b^2 + wz^2 * sd_c^2 + 2 * wx * wy * cov_b_a + 2 * wx * wz * cov_a_c + 2 * wy * wz * cov_c_b))] #four_assets[, ':=' (ex_p = ww*ex_a + wx*ex_b + wy*ex_c + wz*ex_d, sd_p = sqrt(ww^2 * sd_a^2 + wx^2 * sd_b^2 + wy^2 * sd_c^2 + wz^2*sd_d^2 +2 * ww * wx * cov_a_b + 2 * ww * wy * cov_a_c + 2 * ww * wz * cov_a_gg + 2 * wx * wy * cov_b_c + 2 * wx * wz * cov_b_gg + 2 * wy * wz * cov_c_gg))]#4資産以上の場合 three_assets <- three_assets[wx >= 0 & wy >= 0 & wz >= 0] three_assets[, ir := ex_p / sd_p] p = ggplot() + geom_point(data = three_assets, aes(x = sd_p, y = ex_p, color = ir)) + geom_point(data = data.table(sd = c(sd_a, sd_b, sd_c), mean = c(ex_a, ex_b, ex_c)), aes(x = sd, y = mean), color = "red", size = 3, shape = 18) + theme_bw() + ggtitle("frontier") + xlab("Standard Deviation") + ylab("Expected Returns") + scale_y_continuous(label = percent) + scale_x_continuous(label = percent) + scale_color_gradientn(colors = c("blue", "red"), name = "kpi", labels = percent) + theme_gray (base_family = "HiraKakuPro-W3") three_assets[ir == max(three_assets$ir)] 結果は3社の最適な組み合わせのKPI=0.0845となり、2社だけの場合よりさらに高くなりました。 同様にD社も加味して4社で試すと、KPI=0.0850となってさらに高くなります。 その場合、当初曲線だったグラフは以下のように、取りうる選択肢が広がった分だけ多くなって線から面となります。 この作業の繰り返しによって以下の結論が得られます。 3社の場合より4社のほうが伸び幅が逓減するため、 銘柄数が多くなるほどKPIは高くなるもののその値は一定値に収斂する という可能性を推測することができます。 したがって、仮説2は正しいと言えます。 仮説検証で得られた戦略の実行プラン 仮説1と仮説2の検証によって得られた知見から、投資対象の銘柄を増やすことによってKPI指標の最大化を図ることができ、このときの株式の保有割合は上で実施したような方法によって知ることができます。 しかしながら、実際の現場ではこうして得られた知見をそのまま使うことはしません。 最適解として得られた保有割合が何を示しているのか考え、データの裏にある真実を探る必要があるからです。 ここで得られる真実とは、株価(リターンとリスク)によって得られる最適な保有割合は一意に決まり、合理的な投資家であれば皆その比率を保有すると推測されます。 したがって、KPIの設計が正しく、市場参加者の大部分がこのKPIに対して合理的に投資するという前提に立てば、各投資家が今回得られた保有比率に応じて売買しているため、その結果として価格形成された現在の株価(正確には時価総額)はこの比率が反映されているはずです。 言い換えれば、最適な保有比率そのものが時価総額の比率によって決定されていると考えることができます。 結果的に、当初立てた目的を達成するためには、より多くの銘柄を時価総額の比率で重み付けされた指数(S&P500やTOPIXのETFなど)に投資する必要があるといえそうです。 さいごに ここまで、目標に対してKPIを設計してアクションを決定するまでのプロセスをご紹介しましたが、実際の業務ではこのように単純な前提を置くことはできないので、仮説と検証を繰り返して得られた結果の背後にある事実を正確に捉えながら戦略の精度を高めていく必要があります。 今回はアドベントカレンダーということで若干テクニカルな話をあえて盛り込んでみましたが、実際の業務ではそこまでスクリプトを書いて分析する時間は多くありません。 むしろ手元にあるデータや事象から論理的に戦略を決め、メンバーとコミュニケーションを取りながら円滑にプロジェクトのディレクションをすることも重要です。 もしこうした仕事に興味を持っていただけたら、採用窓口からのご応募をお願いいたします。 ぜひお待ちしております。
CTOの川口( @dmnlk )です。 2019/11/30に池袋で行われたDevelopers Boost 2019に主催の翔泳社様より招待頂き、基調講演として登壇させて頂きました。 Developers Boost は「U30エンジニアの登竜門」と題されており、U30エンジニアのためのカンファレンスです。 テーマが「これが私の戦い方」というものだったので、自分がどのように自分の市場価値を決め成長し生存戦略を立ててきたかを「プロダクトファーストに価値を創造するエンジニアとしての生き方」というタイトルで発表させて頂きました。 資料は下記になります。 エンジニアの市場価値をは人それぞれあると思いますし、技術をどう伸ばしていくか何のためにやっていくかも人それぞれだと思います。 自分にとってのキャリア形成の価値基準に「プロダクト」を置いたのが私の戦い方になっています。 これを見聞きして頂いた皆様のエンジニアのとしてのキャリア形成の方向性の一つの材料になってもらえれば幸いです。 他の方の発表をいくつか聞かせて頂きましたが、大変参考になるものが多くU30といえども色々な戦い方の多様性があるなと感じました。 また、懇親会でも多くの方とお話させて頂き刺激を受けさせて頂きました。 改めて、このような機会を頂いた翔泳社様にはお礼を申し上げさせていただきます。 来年はまだギリギリU30の枠なので参加したいと思います。
この記事は BASE Advent Calendar 2019 の4日目の記事です。 devblog.thebase.in こんにちは、BASE BANK株式会社 Dev Divisionに所属している東口( @hgsgtk )です。 普段、ブログやカンファレンスでアウトプットする内容としては、PHP・Go ・Pythonなどサーバーサイド言語を用いた話や、テストやコード設計の話が多いのですが、せっかくの年に一回のアドベントカレンダーなので、普段はあまりしないクラウドインフラのレイヤーの話をしたいと思います。 私は、「 YELL BANK(エールバンク) 」というサービスの開発・運用をしています。そのサービスの運用にあたって、Relational databaseとして、 Amazon Aurora を利用しています。  Amazon Auroraを利用するにあたって、アプリケーションから参照系リクエストを受け付けるエンドポイントを定義するのにCustom endpointを使用しています。 このCustom endpointにおいて、フェールオーバーを想定した場合に必要な、設定のちょっとした注意点を紹介してみます。 ケーススタディ 次のように、1台の書き込みロールのインスタンスと、2台の読み込みロールのインスタンスの計3台のインスタンスがぶら下がるクラスターを想定してみます。 AWS RDSのマネジメントコンソール画面 このクラスターにおいて、アプリケーションからは test-database-1-analysis という読み込みロールのインスタンスは参照せず、分析用として使うという構成だとします。 Amazon Auroraのエンドポイント 前提知識として、Amazon Auroraには4つのエンドポイントが存在します。 docs.aws.amazon.com まず、現在のプライマリDBインスタンスに接続する クラスターエンドポイント 、DBクラスターの使用可能なAuroraレプリカのいずれかに接続する 読み取りエンドポイント 、Auroraクラスター内の特定のDBインスタンスに接続する インスタンスエンドポイント 、そして今回の記事のタイトルでもある カスタムエンドポイント です。 カスタムエンドポイントは、選択したDBインスタンスのセットを表し、選択したグループ内で負荷分散を行い接続します。 今回のケースでは、読み取りエンドポイントの一部をアプリケーションから使いたいことが要求です。その要求をカスタムエンドポイントであれば叶えられそうです。 カスタムエンドポイントタイプ 実際にカスタムエンドポイントを設定します。ただし、フェールオーバーが起きてインスタンスのロールが入れ替わった際の考慮は忘れてはいけません。そういった場合に知っておくべきは カスタムエンドポイントタイプ です。 このカスタムエンドポイントタイプによって、当該エンドポイントに関連づけることができるDBインスタンスが決まります。具体的には、 READER ・ WRITER ・ ANY の三種類があります。 READER: 読み取り専用のAuroraレプリカであるDBインスタンスのみが、これの一部になれる WRITER: マルチマスタークラスターにのみ適用される、複数の読み書きDBインスタンスを含めることができる ANY: 読み取り専用のAuroraレプリカと、読み取り/書き込みのプライマリインスタンスの両方がこれの一部になれる カスタムエンドポイントタイプは、マネジメントコンソール上で見る限りは、表示されていないので、気がつきにくいですが、マネジメントコンソールで作成した場合には、ANYになります。 カスタムエンドポイントタイプの ANY または READER を AWS マネジメントコンソール で選択することはできません。AWS マネジメントコンソール で作成するすべてのカスタムエンドポイントは ANY タイプになります。 https://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/AuroraUserGuide/Aurora.Overview.Endpoints.html#Aurora.Endpoints.Custom.Properties マネジメントコンソールから作ってみると 例えば、マネジメントコンソールで作成したカスタムエンドポイントに、設定時点で読み込みロールのインスタンスを選択してみます。 マネジメントコンソール画面からカスタムエンドポイントを作成する この状態でマネジメントコンソールから フェールオーバー させてみます。すると、カスタムエンドポイントに設定したインスタンスは次のように変化します。 フェールオーバー後のエンドポイントメンバー ロールが 書き込み になってしまいました。これでは、レプリカのみを参照したいエンドポイントにしたい意図を満たすことができません。 どうすればいいか 先ほどお伝えした通り、現時点(2019年12月4日)で、マネジメントコンソールから、カスタムエンドポイントタイプを設定する方法はなく、デフォルトでANYとなります。しかし、AWS CLIからはカスタムエンドポイントタイプを指定できるので、そちらで作っていきましょう。 AWS CLIでカスタムエンドポイントを作成する場合、 create-db-cluster-endpoint を使用します。 $ aws rds create-db-cluster-endpoint --db-cluster-identifier test-database-1 \ --db-cluster-endpoint-identifier reader-2 \ --endpoint-type READER \ --static-members test-database-1-instance-1-ap-northeast-1c test-database-1-instance-1 オプション endpoint-type にて READER を指定することで、カスタムエンドポイントタイプがREADERになります。 また、この時の設定方法として、設定したい読み込み専用DBインスタンスと、現在のプライマリDBインスタンスの両方を設定しておきます。するとマネージメントコンソールからは次のように見えます。 AWS CLIから作成したカスタムエンドポイント 現在、読み込みロールとなっている test-database-1-instance-1-ap-northeast-1c のみがエンドポイントメンバーとなっており、プライマリDBインスタンスは読み込み専用ではないのでメンバーにはなっていません。 こうしておくと、フェールオーバーが起きて、書き込み・読み込みが入れ替わった場合においても、読み込み専用のインスタンスに対して解決するようになります。 フェールオーバー後のエンドポイントメンバー ちなみに、YELL BANKの運用のためのインフラ構成管理は、基本的に Terraform で行なっていますそのため、Terraformのtfファイルにて次のように設定しました。 resource "aws_rds_cluster_endpoint" "test-database-read" { cluster_identifier = aws_rds_cluster.test-database.id cluster_endpoint_identifier = "test-database-read" custom_endpoint_type = "READER" static_members = ["test-database-1", "test-database-2"] } おわりに 以上、Amazon Auroraのちょっとした小ネタの紹介でした。この他にも、サービスの新規開発時や運用していく中で、クラウドインフラのレイヤーの経験もたまっていっているものがあるので、2020年も小出しにでもみなさまにお届けできればと思います。 明日は、Data Strategyチームの鈴木さんとOwners Growthチームの遠藤さんです!お楽しみに〜!
この記事はBASE Advent Calendar 2019 4日目の記事です。 devblog.thebase.in こんにちは、島田です。 一昨年はエンジニアとして業務をおこない、昨年はエンジニアリングマネージャー(以下EM)を担い、今年はまたエンジニアとして一年を過ごしました。 エンジニアに戻って改めて感じたことは、BASEのEMはエンジニアリング~という言い回しをしていますが、エンジニアとは全く役割が違うものだと思いました。 EMをやっているときは、いかに事業を成長させるか、成長させるためにどういった組織・チームを作るか、そのためにはどういった人や文化が必要か、その人が最大限活躍するためにはどういった制度や場が必要かを考えていました。 エンジニアに戻ってからはその制度や文化の中で過ごしていたのですが自分がEMになる前(一昨年頃)のエンジニア組織より開発や成長できる環境が整ったなと感じました。 特にエンジニアの人数が増え、さらに入社された方たちもいわゆる強い人と言われるエンジニアばかりなのでSlackで流れるやり取りや、GitHubのコードを見ているだけでも相当勉強になります。 さて、エンジニアに戻ったのでEMのころに担っていた採用や組織づくりの役割はなくなりましたが、1on1は継続して行っています。ただしする側ではなく受けるとしてです。 今回は自分が1on1をする側の時に思っていたこと、受ける側になって思ったことまとめたいと思います。 1on1を「する側」としてやっていたこと 人事、社内ルールのについて共有 成長過程にあるベンチャー企業というのは入社される方も多いですし、新しく選定される制度、変更される制度も多かったのでそれの共有を行っていました。 最近では人事・労務チームが全体に共有してくれているので最近のBASEの状況なら無くてもいいかなという感じです。 1ヶ月の振り返り 僕は月1で1on1を行っていました。 メンバーのGitやSlackでのやり取り、社内ドキュメントなどを常にウォッチし、各自の成果をまとめておき1on1で成果を確認し、評価項目についてお互いの認識のすり合わせを行っていました。 エンジニアは成果物での評価も大事ですが僕は適切なレビューや顧客問い合わせなどの運用フォロー、知見を生かした教育なども評価対象として伝えていました。 やりたいことを聞く・今後期待することを伝える 組織として期待することを伝えつつ、本人がやりたいことを聞いていました。 どんなに重要な事でも本人のやる気の方向性とミスマッチがあると生産性が下がってしまうのでなるべく期待との乖離が無いうように気をつけていました。 質疑応答 会社のこと以外にも自身の体調のことや家庭などのプライベートなことや、会社に対してこうしてほしい、こうしたいなど日々思っていることの共有をしていました。 以上が僕は主に行っていた1on1の内容です。 1on1を「受ける側」になって思ったこと 「受ける側」が1on1に対して心理的負担を感じる場合がある プロジェクトの進捗が思わしくない時は、受ける側としては気が重かったです。 さらにその状況で評価シーズンになると今後のことで心理的な負担は大きかったのでこういった状況を相談できる場になると良いなと思いました。 年齢が近かったり、同じライフイベントを経験したEMは比較的話しやすい 現在のEMは年齢が近く同じようなライフイベントを経験しているので、「子供のお迎えで数日間早退します」や「子供の看護で休みます」など業務外の要件も言いやすいです。 話しやすさと気持ちのバランスは比例するところがあり心理的な安定にも繋がると思うので相性は大事だなーと思いました。 場所ややり方を変えることで1on1への心理的負担を軽減させる 1on1の時間に対してネガティブな印象ができてしまうとその時間が来るのが負担になってしまうのかなと思いました。 そういった状況では何も意見が出ず、悪い流れが続いてしまい結果的には生産性が下がってしまうことになります。 そうった場合は時間や場所を変えみる、やり方を変えてみるなど環境を変化させるのも必要かなと思いました。 最後に 1on1をする側から受ける側になって一番重要だと思ったのは、お互いに「する側」「される側」という意識は持ったほうが良いということです。 BASEは年齢や役職関係なくフラットに話せる環境ですし、行動指針に「Speak Openly」を掲げていることもあり、セクションやチーム内で上下の関係を意識することがあまりないので「1on1は上司から部下にするもの」というよりは対等な関係性を基本としてお互いに気になっていることや期待していることを伝えあったほうが認識の齟齬やコミュニケーションストレスが少なくなり長期的に見たときに生産性は上がるのではと思いました。 明日はData Strategyマネージャーの僚さんとOwners Growthマネージャーの遠藤さんです!
こんにちは!BASEのProduct Divisionでエンジニアをやっております川島( @nazonohito51 )と申します。 この記事はBASE Advent Calendar 2019の3日目の記事です。 devblog.thebase.in 前の日は id:ngsw の EC2における単位時間あたりの名前解決制限の対応 と id:chiiichell の エンジニア2年生がリーダブルコードを読んで今年自分が書いたコードを振り返る でした。 今回は普段は見ることのない大型技術カンファレンス運営の裏側について書かせていただこうと思います。 こちらは文字通り裏側でクロージングの準備をしている実行委員長の背中です。 PHPカンファレンスとは PHPカンファレンスとは2000年から続いている、PHPカンファレンス実行委員会によって開催しているカンファレンスです。最近は参加人数も1500人を超え、日本の他のプログラミング言語のカンファレンスと比較しても非常に人数が多く、おそらく日本でも最大級のソフトウェアエンジニアのカンファレンスなんじゃないかと思います。(ただし他のカンファレンスは複数日開催だったり、資本が大きかったりと、1日のみ参加費無料のPHPカンファレンスと単純に参加人数でイベント規模を比較することは難しいと考えています) 第20回目の開催となる今年は beyond .* というテーマを掲げて、PHPのさらなる飛躍を目指したカンファレンスでした。公式サイトのロゴは飛躍するための飛行機の滑走路をイメージしたデザインとなっています。 PHPカンファレンス実行委員会とは PHPカンファレンス実行委員会とは謎の秘密結社です。夜な夜な暗いところに集まり、フード深くかぶりながら緑色の液体を飲んで祈祷を捧げる怪しい集団です。 というのは冗談で、正体は 日本PHPユーザ会 のメンバーです。特に参加資格があるというわけではないのですが、基本的に1回は当日スタッフを経験された方がもっと深くイベントに関わりたいと思ったときになっていただいています。いわゆるコアスタッフと呼ばれる実行委員会のメンバーの他、当日と前日のみ手伝ってくださる当日スタッフや、会場のネットワーク周りを用意してくださるNOCメンバーなどがいらっしゃり、私も全貌を把握しているわけではありませんが、スタッフアサイン表を見る限りは今年は112名もの人数でPHPカンファレンスは運営されていました。 実行委員会は裏で何をやっているのか? 前日までの準備 今年の実行委員会が使っていたタイムスケジュールは以下のようになります。 これだけを見るとスポンサー・スピーカー・プログラム関連のスケジュールが目立ちますが、実際のところそれ以外の作業がたくさんあります。 Webサイト 広報 デザイン(Tシャツ、パーカー、ネームカードetc) ゲストスピーカー招致 スタンプラリー 音響・映像配信 書籍販売、サイン会企画 懇親会 会場設計 etc... それぞれに担当を初回のキックオフMTGで決め、毎月1回のMTGを積み重ねながら準備を進めます。また今年はelePHPantや懇親会参加チケットの販売のためにネットショップ販売などもありました。(弊社のサービスを利用していただきました) タイムスケジュール上は今年の6月辺りから開始していますが、ある意味本当の開始点は去年の10~11月辺りだったりします。というのも、イベントというのはまず先に場所の確保から始まります。企画だけ進行しても実際に開催できる場所がなければ何も実現できないためです。そしてイベント会場の確保というのは大変で、大体の場合1年以上前から動き始めないと予約できません。実はPHPカンファレンス2019の会場予約作業をしたのは私なのですが、参加者・スタッフの負荷を考えてイベント翌日は休日である日を確保したかったところ、そういった日はすぐ取られてしまっていたので、泣く泣く日曜日で確保した経緯があります。 前日と当日 前日にあたる11/30(土)はいよいよそれまで準備されてきたものを実現します。会場に全ての必要な物品が集まるので、それらを元にひたすら肉体労働、肉体労働、肉体労働です。 本当にたくさんの椅子を並べるのももちろんですし、他にもスポンサーブースの設営もします。 特に大変なのが実は電源で、PiOの電源は床下に収まっているのですがこのカバーがめちゃくちゃ重たいです。2,30キログラムあると聞いています。またとても持ちづらく、指を挟んだりして怪我をしやすいです。ブースへの配線次第では何十個もこのカバー開け締めしなくてはならないので、指を挟まなくてもカバー持ち上げ作業の積み重ねで腰がやられます。 そして前日作業の最たるものがこの業界では蟹工船と表現されるトートバッグのチラシ詰め作業です。丸く配置したテーブルにスポンサー様のチラシをずらりと並べ、ひたすら1枚ずつ取っていきます。本当に何時間もこの作業を続けるので、何度もぐるぐる回りながらやがて全員がバターになっていきます。 こうすることによって当日、ようやくイベントが開催することが出来ます。 そしてイベントが終われば当然のように後片付けがあります。PiOに会場をきれいな状態をお返ししなければならないので掃除を含めてまた同じような作業をしていきます。 実行委員会での私の役割 私は会場設計という役割で携わさせていただいていました。主な作業内容は文字通り会場の設計をすることで、各担当からの依頼に合わせて備品の配置を行います。また会場である 大田区産業プラザPiO とやり取りすることも必要になります。 備品を配置するだけといえば簡単そうに聞こえるのですが、実際のところMTGを重ねるごとに各担当からの依頼や状況が変わり続け、そのたびに何度も書き直しました。特に大きかったのは途中のスポンサー枠の増加です。元々の計画でも過去最高の数のスポンサーブースを用意しなければならなかったのですが、途中から更に増えるとなって少し頭を抱えたことがあります。上記の図は最終決定の設計なのですが、ここまでに一体何回の書き直しをしてきたのか分からないくらい書き直しました。 私はこの役割を2年間携わってきたのですが、会場とはイベントにおけるあらゆる利害関係が最終的に収束する場所なのだという気づきがありました。それ故にあらゆる利害関係者の要求の変化に対応し続けることがこの役割のキモなんだと考えています。 会場設計とシステム設計は似ている もう一つの気付きとして会場設計とシステム設計って似ている、というものがありました。 会場設計はあらゆる要求に対応し続ける必要があります。しかしそこには会場の物理的な制約や、あるいは要求と要求との間で発生するコンフリクトなどから全ての要求に応えきるのは実は難しかったりします。例えばスポンサーブースを増やしたくても会場の物理的な大きさ以上に配置することは出来ません。また各ブースに電源を供給しようにも会場の電源が置かれている場所は限られているため、ブースが配置できる位置はある程度限定されます。さらにはセッション会場と音が出やすいブースを近づけることは出来ないため、サイン会を行う書籍販売所やスポンサーブースをセッション会場に近い位置に置くことは出来ません。 このように会場設計は要求と要求とのバランシングや、物理的制約とのバランシングなどを行っていきます。大量の変数があるなかで、それぞれの目的をきちんと捉え、イベント進行上ある程度満足できる質に落とし込んでいく作業であるとも言えます。 これって常に技術的な制約や、現在の実装上の制限の中から、その時々のステークホルダーからの要求に応え続けるシステム設計そのものだなと感じました。なので、というわけではないですが、システム設計に興味のある方は一度会場設計を経験してみることをおすすめします。 イベント運営の醍醐味 最後に何故自分がイベント運営をやっているのか、その醍醐味は何なのかというお話をさせていただくと、今考えてみても正直良く分かりませんでした。少なくともイベントが上手くいったとき、イベントで喜んでくれる方がいらっしゃるのはとてもやりがいを感じますが、何かしら実利的な見返りがあるかと言われるとそんなに無いなと思っています。将来の転職活動のときになにかしら効果があるかと言われれば、それもあまりないだろうなと思います。(やってないよりは遥かに良い印象なのですが、純粋に転職効果を狙うなら他にもっと良い方法があると思います) しかしイベント運営をやっていると本業における立ち回りに還元されるもの少なからずあると思っています。会社規模にもよると思いますが、会社が大きくなると自分のやっている作業が巨大なものの一部となっていきます。そのため自分で事業を直接的にハンドリングしている感覚が薄くなったりするのですが、イベント運営はそんなことはありません。ほとんど直接的にハンドリングする必要があります。 感覚的な話になってしまい申し訳ないのですが、イベント運営をやることによって普段の業務において血の通った決断を行えるようになってきた感触があります。私もご多分にもれず、言われるままに作業をしていたり、目的感があいまいなままやってきたりしている部分は多々あったのですが、どこか目的を見据えた思考や判断ができるようになってきた気がします。どういった因果関係でそうなったのか今でもはっきりと言語化出来ないのですが、先述の通りイベント運営をすることで少なからず本業に還元されるものはあると感じています。 明日はBASE BANKの id:khigashigashi さんとProduct Dev Divisionの id:smdksk です!お楽しみに!
こんにちは!2019年ももう少しで終わりですね。さて、この度は、12/1(日)に東京・蒲田で開催された PHP Conference Japan 2019 にて、3名のメンバーが登壇しました。また、プラチナスポンサーとして協賛いたしました。今回は、スピーカーとして参加しためもりー( @m3m0r7 )、金城( @o0h_ )、東口( @hgsgtk )の3名から参加レポートをお届けします! PHP Conference Japanとは PHP Conference Japan 2019 とは、12/1(日)に開催された、プログラミング言語PHPのカンファレンスです。今年は、約1400人のPHPエンジニアが集った国内最大級のカンファレンスとなりました。 phpcon.php.gr.jp BASEは、プラチナスポンサーとして本カンファレンスに協賛いたしました。 https://phpcon.php.gr.jp/2019/ 会場は、昨年の開催に続き、大田区産業プラザ PiOでした。 BASEのブース 企業ブースでは、弊社メンバーが持っているありったけの ElePHPant でブースを彩ってみました。本当にたくさんの参加者の方とお話させていただけ、一同大変楽しい時間となりました。 また、アメリカから来日されていた Adam Culp さんにも、ブースに足を運んでいただき、写真を撮っていただく貴重な機会がありました。 Adam Culp さんは、マイアミで開催されている SunshinePHP というPHPのカンファレンスのオーガナイザーをされていたり、PHPコミュニティを世界的にリードされてる一人です。 Adam Culpさんとぱしゃり 当日の書店コーナーでは、著者によるサイン会があり、個人的に書籍「みんなのPHP」に寄稿していた、めもりー( @m3m0r7 )・東口( @hgsgtk )がサイン会に参加していました。 書籍「みんなのPHP」サイン会終了!しかしまだまだ買ってくれ!!!頼む!!!! 写真は著者の集合写真です。 pic.twitter.com/2gkJWXOxWj — uzulla (@uzulla) December 1, 2019 発表内容・資料 めもりー( @m3m0r7 )、金城( @o0h_ )、東口( @hgsgtk )それぞれから、発表内容をお伝えします。 めもりー @m3m0r7 Track2 (小展示ホール)にてセッションさせていただきました。 このセッションでは PHP で並列処理、非同期処理をどう実装すればよいのか?といったお話を主軸に、そもそも並行処理や並列処理、非同期処理とは何なのか?という構成になるようにしていました。ホール自体が大きく、多くの方に起こしいただいて感謝しております。 東口さんに、「え?elePHPant 持っていかないんですか?」と言われ、大事だよな、ということで、販売が始まってすぐに購入した elePHPant を会場に持っていきました。 資料自体は、実は私一人で作り上げたものではなく、私の今までの経験を含め、 Go 言語を使用している BASE BANK の東口さんや budougumi0617 さんにも見ていただいたり発表練習に付き合っていただき、そして校正に校正を重ねてきていて、お越しいただいた皆様に楽しく理解してもらえるように尽力しておりました! そして、懇親会 LT では、「PHP を JVM 言語にする」という内容でデモを交えてお話させていただきました。 実は吉祥寺 pm でしかお話したことのない内容で、今まで 「PHP で JVM を実装する」という話を各イベントで主軸にお話していましたが、それとはまた別で PHP そのものを JVM 言語にしてしまうといった内容になっています。 なお、今年のセッションは PHP カンファレンス 2019 で最後です。トータルの登壇回数は 15 回で、発表時間は 270 分です。 詳しくは個人の note にポエムを添えてまとめていますので、ご興味があればご覧ください。 今年の登壇実績|めもりー @m3m0r7 金城 @o0h_ Track5(会議室AB)にて発表をさせていただきました。 まず最初に、そして最も印象に残っているのは、満員になった会議室の光景です。 登壇の機会を頂いた身としては、聴衆の多さに緊張もしつつ、「こんなに多くの人と同時に自分の好きな話を共有できるんだなぁ」と嬉しく感じました。 今年のPHP Conference Japanでは、他の多くのセッションでも満員やそれに近い状態が生まれていた印象があります 。凄いパワーを持ったイベントだと思いました。こんなに熱量の濃い空間を創り上げてくれたスタッフの皆さんに、心より感謝を申し上げます。ありがとうございました! さて、今回の内容は、自分自身が「そういう話があれば興味を持つかもな、聴いてみたい!」とプロポーザルを提出したものです。そのため、「知見を共有する」「知ってもらいたいな」という想いも大事にしつつ、コミュニティに対して「問いを投げかける」ような気持ちで臨みました。 25分という枠の中で語りきれたのは、ごく僅かな部分に過ぎません。が、もしこのセッションをきっかけに git clone git@github.com:composer/composer.git を試みた方が出てくれたなら最高です!! 東口 @hgsgtk プラチナスポンサーとして協賛したので、BASEのスポンサーセッションを担当いたしました。また、懇親会LTでも発表させていただきましたので、それら両方の資料を公開いたします。 知見のない技術スタックをプロダクション導入するエンジニアの導入戦略 BASEのスポンサーセッションとして発表いたしました。私は、BASE株式会社の子会社である BASE BANK株式会社 にて、 YELL BANK(エールバンク) というサービスを開発する際に、技術選定を行いました。その際に感じた、" 技術選定という意思決定における不安 に対してどう立ち向かっていけばよいか"について資料として形にしてみました。 当日は、非常に多くの方に足を運んでいただく形になりました。「技術選定の意思決定を行ったことがある方がどれくらいいるか?」と会場に問いかけた際に8割くらいが「ある」という方だったため、同じような課題感や境遇に立ったことがある方が多いようでした。 発表後は、Twitter上にて共感の声や、「参考になった」というポジティブな反応がありひとまず一安心しました。 #phpcon #Track4 運用しないとわからないことがある わかる — アッキー (@akki_megane) December 1, 2019 #phpcon #Track4 話の中で「一般化」を何度も聞いた 素晴らしい発表、と思った 目指すところの高さを感じた — 央 (@sogaoh) December 1, 2019 分かりやすかったし勇気づけられた #phpcon #track4 — えすと👀 (@shostako_Vc) December 1, 2019 また、発表後にわざわざブースにおこしいただいて質問しに来てくださる方も複数いらっしゃり、"開発現場におけるテスト"や"技術評価の前段階にどういった準備をするか"など、改めて「自分がどうしたか?」と更に振り返る機会になり、大変有意義な時間になりました。 当日、会場にて聞きに来てくださった方々にはこの場を借りて改めて御礼申し上げます。 自作してみようxUnitフレームワーク こちらは懇親会LTにて話した内容です。今年のPHP Conference Japanでは、PHPエンジニアであれば一度は必ずお世話になったことがあるだろう PHPUnit の開発者である Sebastian Bergmann さんがスピーカーとしてドイツから来日していました。 日頃のPHPUnitへの感謝と愛を伝えようと思い、xUnitフレームワークを自作してみることで、改めてPHPUnitの凄さを知ろうという発表をしてみました。 これは余談ですが、Sebastian Bergmannさんとは前日のスピーカーディナーの際に直接話させていただきました。「PHPUnitを何故つくったのか?」という背景や、今後どういう構想があるのかといった話を聞かせていただきました。英語での会話はまだまだ難がある状態の私でも優しくゆっくり話していただき本当に素晴らしい方でした。もっと英語での会話力をつけてがっつり議論できるようになりたいと強く思った時間になりました。 おわりに PHP Conference Japan 2019 の実行委員長である板谷郷司さんをはじめ、実行委員会の皆様、また弊社エンジニアのセッションをご静聴してくださった皆様に心より御礼申し上げます。PHPコミュニティに対して協賛や登壇という形で微力ながら貢献できたこと光栄に思います。 来年の開催は10月11日 の開催とのことで、心より楽しみにしております。誠にありがとうございました!
この記事は、「BASE Advent Calendar 2019」の3日目の記事です。 devblog.thebase.in 前の日は id:ngsw の EC2における単位時間あたりの名前解決制限の対応 と id:chiiichell の エンジニア2年生がリーダブルコードを読んで今年自分が書いたコードを振り返る でした。 devblog.thebase.in Product Dev Divisionの加賀谷です。内部統制の実現手段の1つである「ITへの対応」について整備改善を日々進めています。社内の内部統制事務局と監査法人に評価とアドバイスをいただきながら、IT統制のうちの全般統制への対応を実施しています。 AnyPay社さんの GitHubを組織運営に活用する事例 にあるとおり、活動の中で行う文書管理、承認、証跡管理にGitHubがとても有効だと感じることが多くありました。この記事では先の記事に学び行ってきた中で、弊社のIT統制におけるGitHubの活用事例をご紹介したいと思います。 規程類の管理 GitHubにリポジトリを作り、規程類をMarkdownで統制項目毎に.mdファイルを作っています。アドバイスいただいたことをGitHub Issueに起こして議論を進め、規程の更新はプルリクエストを経てmasterへマージしています。GitHub Flowで運用していて、masterが現在適用されている統制の整備状況としています。 規程類は一度作り終えたら終了というものではなく、会社やサービスの維持と発展のために改善を加えていくものなので、どういった経緯でこの方法になったのか、どんなIssueからどんな議論がされたのか、追跡や検索ができることが大切です。そういった面からもGitHubはとても有効です。 PDFとホスティング また、GitHubとは直接関係ありませんが、規程類をMarkdownで書いておいて良かったことを紹介します。GitHubでMarkdownが表示できるとはいえ社外の監査法人とやりとりする時があるので、リポジトリに参加してもらうことは現実的に難しかったりします。そこで、スクリプトでMarkdownをPDFに変換しています。 markdown-pdf ではCSSでフォントや紙のサイズを指定することができるので、文書のデータと体裁を分けて考えることができます。concatもできるので、1つのMarkdownファイル毎に1PDFではなく、規程類のまとまり毎に1つのPDFにconcatしています。 pdf.js #!/usr/bin/env node const fs = require( 'fs' ), path = require( 'path' ), markdownpdf = require( 'markdown-pdf' ), config = require( 'config' ); const walk = (dir, callback) => { fs.readdirSync(dir).forEach((item) => { const filePath = path.join(dir, item); const stat = fs.statSync(filePath); if (stat.isDirectory()) { walk(filePath, callback); } else if (stat.isFile()) { callback(filePath); } } ); } ; config.get( 'pdf.docs' ).map(doc => { let strings = [] ; walk(doc.dir, filePath => { if ( /.*\.md$/ .test(filePath)) { strings.push(fs.readFileSync(filePath, { encoding: 'utf-8' } ).replace( /---[\s\S]*?---/ , '---' )); } } ); markdownpdf( { paperFormat: "A4" , cssPath: "pdf.css" , remarkable: { linkify: true , } } ) .concat .from.strings(strings) .to(doc.dest, function () { console.log( "created" , doc.dest); } ); } ); pdf.css body { font-size : 0.625em ; font-family : "ヒラギノ明朝 ProN W3" , "Hiragino Mincho ProN" , "MS P明朝" , "MS PMincho" , serif ; } abbr [ title ] : after , a [ href ] : after { content : "" ; } GitHubに参加していない社内メンバーに公開するには Docusaurus が便利です。Markdown+Docusaurusの活用については BASEの開発者様向け機能のドキュメントサイトをリニューアルしたときの記事 もご覧下さい。 特権IDの管理と承認 本番データベースなどの重要なシステムにアクセスする特別な権限を持つメンバーは、業務に応じてアクセス管理権限一覧に基づき設定しています。また特権の付与や権限を変更する際には責任者の承認を必要とするワークフローにしています。今誰がアクセス権限を持っているかの一覧、どういう理由で申請したのか、誰が承認したのか。ワークフローの構築と証跡管理、モニタリングにGitHubを使っています。 アクセス管理権限一覧をYAMLで書いておき、追加及び削除の申請はYAMLファイルへプルリクエストをして、必要な承認者のレビュー(PullRequestのapproved)を受ける運用にしています。承認者は CODEOWNERS に定義することでプルリクエスト作成時に自動で固定できます。 .github/CODEOWNERS ファイル自体のコードオーナーは、モニタリングするメンバーにしています。 まだ実施していませんが、gitリポジトリ内のYAMLファイルにすることでプログラムとの親和性が上がり、TerraformやAnsibleなどインフラ構成管理の中にも組み込むこともできると思います。 CODEOWNERS # CODEOWNERSの例です。 # デフォルト * @violetyk # 本番DBへアクセスするユーザの承認 /permissions/production_db.yml @dmnlk 障害管理台帳 前提として障害は起きないように策を講じるのですが、万が一事故や障害が起きたときには解決に至るまでを記録・管理する手段を講じておくことが必要です。 障害の記録や管理にはGitHub Issueを、Issueの自動作成にGitHub Actionを使っています。弊社では障害発生時のワークフローとして、Slackで #incident_yyyymmdd のようなチャンネルを作り現況報告をするところから始まります(Slackがダウンしていたら代替のチャットで)。 GitHub Actionで定期的に障害のチャンネルが作られているかどうかを監視し、あった場合にはGitHubに障害管理Issueを作ります。障害管理Issueは自動的に incident タグをつけて責任者にアサインされます。責任者が障害報告を書いて障害対応が終わったらIssueをクローズしています。 GitHubで管理し起票を自動化することで、報告漏れをなくし、未解決の障害や根本対応を要するものの定期的な棚卸しなどが容易になりました。 GitHub Action 障害のチャンネルを監視してIssueを自動で作るGitHub Actionは次のように書いています。 .github/workflows/create_issues.yml name : Create Github issues from Slack incident channels on : schedule : - cron : '0 1 * * *' jobs : build : runs-on : ubuntu-latest steps : - uses : actions/checkout@v1 - name : Set up Python 3.7 uses : actions/setup-python@v1 with : python-version : '3.7' architecture : 'x64' - name : Install dependencies run : | pip install --upgrade pip pipenv pipenv sync - name : Create issues env : SLACK_TOKEN : ${{ secrets.SLACK_TOKEN }} run : | YYYYMMDD=`date +"%Y%m%d" --date "1 day ago" ` pipenv run create_issues --repo=baseinc/info-system --term=${YYYYMMDD} --label=incident --assignee=xxxx JasonEtco/create-an-issue GitHub Actionの JasonEtco/create-an-issue はIssueをテンプレートから作るActionです。このActionを定期的に行う必要があるSaaSのアカウントの棚卸しタスクに使っています。GitHub Issueとして作成し、各SaaS管理者にアサイン、棚卸し内容を書いて終わったらクローズするという運用で証跡管理しています。 .github/workflows/create_issues_quarterly.yml name : Create issues quarterly on : schedule : # 1Q(1/1), 2Q(4/1), 3Q(7/1), 4Q(10/1) (UTC) - cron : '0 1 1 1,4,7,10 *' jobs : build : runs-on : ubuntu-latest steps : - name : Checkout uses : actions/checkout@v1 - name : Review Slack uses : JasonEtco/create-an-issue@v1.2.0 with : args : workflow_templates/review_accounts/slack.md 内部統制の環境整備と開発者体験の両立を目指して 弊社の事例をご紹介させていただきました。まだまだ手探りでやっているところが多いので、活用事例をお持ちの方いらっしゃったらぜひお話を聞かせていただきたいです。 コーポレートエンジニアのミッションは、ショップオーナーに安心してサービスを使い続けていただくために、業務の有効性及び効率性・財務報告の信頼性・事業活動に関わる法令などの遵守並びに資産の保全といった内部統制の環境整備と開発者体験の両立を考え続け、改善し続けていくことです。必要以上のルールや運用が難しい統制を固めることでサービスの成長スピードを落とさず、逆に加速できるよう、柔軟なバランス感を持って改善を進めていきたいと思います。 明日は id:khigashigashi と id:smdksk です。お楽しみに! 「BASE Advent Calendar」の記事一覧はこちらです。 devblog.thebase.in