Retty新卒エンジニアの入社半年間の振り返り〜Part.3〜

Retty新卒エンジニアの堤です。 入社して半年ほど経過して、ようやく仕事も身についてきました。 私はアドテクチーム1に所属しているので、チームの紹介と入社してからの仕事内容についてお話します。

自己紹介

好きな言葉は「ご飯おかわり」です。
しかしオフィスが麻布十番にあるため、量を食べようと思うと、ただでさえ高い食費が大変なことになってしまうのが最近の悩みです。

Rettyに入るきっかけですが、オフィスに話を聞きにいった時期にちょうどアドテクチームの立ち上げの話を聞きました。プロジェクト立ち上げ初期にJoinしたいこと、競技プログラミングをやっていた関係でアドテクに興味があったこと、さらには食べることも大好きだったことからRettyに入ることを決め、アドテクチームに加わりました。

f:id:rettydev:20181204180754j:plain ▲入社式での自己紹介の様子

チームの紹介

広告とは

アドテクチームは、Retty内のインターネット広告を運用する仕事をしています。

インターネット広告と聞くと、あまりいいイメージを持たない人もいるかもしれません。私自身、もともと広告って邪魔だな、と思う派の人間でした。ただ、広告の裏側に触れていくにつれて、広告の面白さを感じるようになってきました。

私が思う広告の面白さ、重要性は大きく3つあります。

  1. 0.1秒間で行われる広告オークションの仕組み
  2. メディアの収益源であるという点
  3. ユーザーと企業とのマッチングという考え方

本記事では広告の説明はメインではないので、1について以下で少しだけお話します。

広告オークションという仕組み

広告も出し方によって大きく2つに分けられて、

  1. 広告主とメディアが直接やりとりし、期間と金額を先に決めて広告を出す
  2. 間に広告事業者が挟まり、ユーザーのメディア訪問の際にリアルタイムに広告を出す

このうち、アドテクチームは2の広告を担当しています。本記事では、特別な記述がなければ、広告はこちらを指すこととします。

メディアで広告が出されると、広告のリクエストが広告事業者に飛びます。事業者側では、

  • メディア
  • 広告枠の過去実績(Click率など)
  • ユーザー属性(Cookieなどから識別)

を判断材料として、広告枠に出稿する広告を募集します。時間内に最も高く入札した広告が、広告枠に出稿する権利を購入します。この広告オークションが事業者ごとに秒間数百、数千万も捌かれています。もともと私自身競技プログラミングにハマっていた時期もあったため、この裏側の仕組みにはわくわくします。

ちなみにこの仕組みはReal Time Bidding(RTB)と呼ばれ、もともとは金融系で用いられていた仕組みだったらしいです。

詳しい仕組みについては「広告 仕組み」などで検索してみてください。

チームの仕事

アドテクチームでは、上記RTBによる広告の運用をしています。主には、

  1. 新規広告事業者と連携し、収益の底上げ
  2. 広告オークションに関わるパラメータの調整
  3. その他収益に繋がりそうなシステムの導入

1, 2については定常的に行っているものになりますが、大きく収益につながることが多いのは3になります。特に、最近ヘッダービディングという仕組みができて取り入れたことにより、10%〜20%の広告収益向上に繋がりました。

収益以外に、広告の質についても責任を負っているので、Rettyのメディアに相応しくない広告(暴力的なものなど)や、強制リダイレクト広告のようなユーザビリティを侵害するような広告をいかに防ぐかなども日々検討しています。

Retty入社後の仕事内容

Rettyに入社してから担当した仕事内容として、細かい仕事を上げると結構ありますが、エンジニアリングの比重が大きそうな以下の3つについてお話します。

  • 広告周りの知識を身につける
  • ヘッダービディングの導入
  • フロアプライスの調整

広告周りの知識を身につける

私は入社前、1ヶ月ほどRettyでインターンをしていました。当時の最初の仕事は広告周りの知識を身につけることでした。広告周りの主な知識として、

  1. 広告の歴史
  2. Rettyで使われている広告運用の方法
  3. 他社の取り組み

の3つが挙げられます。 広告の歴史が何の役に立つのかと最初は思っていました。しかし、広告の歴史は比較的浅いため現在の仕組みにつながるものが多く、最新の仕組みの理解や今後登場する仕組みの予測に役立つ、ということを業務に携わっていて学びました。

ネット広告の歴史はこちらに詳しく書かれています。

https://dmlab.jp/web/history.html

2について、RettyではAdManagerというGoogleの広告配信システムを利用しています。AdManagerは現在多くのメディアで用いられており、機能も豊富となっています。 ただ、機能が豊富すぎるので、最初はとっつくのにかなり苦労しました。 インターン中の半分はAdManagerの使い方を学ぶことに費やしていました。 GoogleがAdManager学習用のサイトを用意している2ので、それを利用して学習しました。

ヘッダービディングの導入

インターン中盤〜入社後にかけて、ヘッダービディングという仕組みの導入・運用を行いました。国内でも早い取り組みだったため模索しながらでしたが、収益改善にもつながったためかなり面白い仕事でした。

ヘッダービディングとは?

ヘッダービディング3の詳細は長くなるので補足ページで確認していただくとして、超ざっくり4というと、

今まで:
広告事業者に広告を要求し、広告が返ってきたらそれを出す。指定価格以上の入札が返ってこなかったら2番目の広告事業者にリクエストを行う。以下繰り返し。
広告が返ってこなかった場合のレイテンシ増大や、2番目の広告事業者の方が入札価格が高かった場合の機会損失が発生。
f:id:rettydev:20181130153808p:plain

ヘッダービディング導入後:
複数の広告事業者を並列で接続し、最も入札価格が高いものを出す。
f:id:rettydev:20181130153819p:plain

といった感じです。

上の説明だけだと、今までなんで並列で繋がなかったの?という疑問が湧きますが、SSPからメディアに入札価格をリアルタイムに送信する仕組みがなかったことが理由です。

苦労した点

ヘッダービディングを入れた当初は、国内での取り組みが少ないため知見が転がっていなかったのが辛いところでした。私が導入を担当したのが海外(Amazon)のヘッダービディング事業者だったため、英語のマニュアルと格闘しつつ、社内のシステムに合うように組み込むことに苦労しました。

特に、別事業者のヘッダービディングと並列で接続したのですが、ヘッダービディングは今までの広告配信の流れに割り込むため、少々組み込み方が複雑になります。他の事業者との並列接続方法などはマニュアルになかったため、

  • 既存の広告配信時系列の把握
  • 他のヘッダービディング組み込みの挙動把握
  • 地道なデバッグ

を繰り返してなんとか組み込むことができました。 これまで非同期通信処理の実装経験がほとんどなかったのですが、複数事業者並列のヘッダービディングの実装では複数の非同期処理が絡んだ処理が入るため、混乱しながら図を描いていたのを覚えています。

今回導入したAmazonのヘッダービディングは一旦導入するとずっと収益を底上げし続けてくれるので、長期的に大きな収益を作ってくれることが期待できます。
ヘッダービディングの組み込みでは、広告関連処理の時間軸を把握する必要があったため、社内の広告リクエストがどのように行われているかの理解が大きく高まりました。

フロアプライスの調整

フロアプライスとは

広告オークションで用いられるパラメータの一つとして、フロアプライスというものがあります。そもそも広告オークションの入札方法は、大きく2つに分類されます。

  1. 1st Price Auction
  2. 2nd Price Auction

1については、一般的なオークションと同様で、最高の入札をした人がその額を支払って購入するもの。2は、最高の入札をした人が2番目の入札額(+1円)を支払って購入するものとなります。 少し例を出すと、

入札額
A: 100円
B: 60円
C: 180円
のとき、

1st Price Auction: Cが180円で購入
2nd Price Auction: Cが101円で購入

となります。ぱっと見だと1の方法が購入される側からすると嬉しく思いますが、2の方が高い入札が行われやすくなります5。 2のとき、フロアプライスというものをメディア側が設定できて、フロアプライス以下の金額では落札されないようになります。 先程の例を用いて、フロアプライスを引いた場合を説明すると、

フロアプライスがX円のときのCの購入額
100円のとき: 101円
150円のとき: 151円
200円のとき: 購入されない

このため、適切なフロアプライスを設定することで広告収益の底上げにつなげることができます。

フロアプライス調整の取り組み

フロアプライスは、もともと調整者の経験と勘によって調整されてきたものでした。しかし、調整にかかる時間的コストや正確性を考えると機械的に行えたほうが適切です。そこで、検討中も含めて以下の3つの方法を取り入れています。

  1. A/Bテストによる調整結果の見える化
  2. 1の結果をもとにした、フロアプライス自動最適化(検討中)
  3. 他社自動最適化ソリューションの導入

現在は1と3を同時に走らせています。A/Bテスト基盤は、Rettyで用いられている既存の配信システムの機能6を用いることで簡単に導入でき、しかもフロアプライス調整以外の施策にも利用できるため効果の高いものとなりました。 このA/Bテスト基盤を用いて現在はまだ手動調整中ですが、既存のフロアプライスと比較して、広告収益の2〜3%の上昇につなげることができました。

現在は、バンディットアルゴリズムを利用して2を実現できないか模索中です。

このように、アドテクチームは直接収益につなげる仕事ができるので、自分の施策・実装が収益にどの程度貢献したかをモチベーションにしながら日々取り組んでいます。

今後について

アドテクチームでは、不適切広告の監視やルーチンワークの自動化など、まだまだやることが山積みとなっています。今後は自分からチーム貢献できる仕事を見つけていけるよう、広告やビジネスの知識もより一層吸収していこうと思います。


  1. 正確には"AdTechnology & Alliance Group"ですが、私の担当がアドテク領域のため、本記事ではアドテクチームと表記します。

  2. https://publisheruniversity.withgoogle.com/splash/ja/index.html

  3. いま話題のヘッダービディングとは何か https://magazine.fluct.jp/2016/03/15/2336

  4. 厳密には異なり、メディアによってはヘッダービディング導入前にも広告事業者の並列接続は行っている場合もある。ただ、入札価格がリアルタイムにわからないため機会損失は発生しうる。

  5. セカンドプライス・オークション https://www.ifinance.ne.jp/glossary/business/bus142.html

  6. AdManagerのKey-Value機能 https://support.google.com/admanager/answer/188092?hl=ja

Webサービスを支えるユーザログ基盤開発@Retty

はじめに

Retty Inc. Advent Calendar 2018 2日目の記事です。

toCで展開している我々のようなサービスでは、 A/Bテスト等を通じて機能開発に対する分析を行っています。

それらを支えるユーザイベントのロギングは サービスの持続的な開発を支える重要な要素の一つです。

この記事を書くにあたり、前調査で調べてみたところ システムログやアプリケーションログの文脈でのログ設計は言及されるものが多いものの サービスにおけるユーザログ設計に関して語られることは意外と少ないもようです。

Web における集客効果を測定する代表的なツールとしては Google AnalyticsGoogle Tag Manager といったサービスが使われており スマートフォンアプリの文脈では、Firebase Loggingなどの強力なサービスが 既にあることから 独自に設計するケースが少ないからかもしれません。

しかしながら、持続的な開発していく上で 設計や開発方針で色々考えることがあるなと思い この記事を書き連ねてみることにしました。

自己紹介が遅れました。 Rettyでソフトウェアエンジニアをやっています @takegueです。

免責事項

  • 本記事では、システムやアプリケーションのログについては言及しません。
  • ログを受けて転送するためのバックエンドシステム (ログの収集〜転送〜分析) も 同様に重要ではあるのですが、これについては今回触れません。
  • ロギングに関する具体的なフレームワークの話にスコープを絞ります。

なぜ大事なのか?

冒頭では、なぜ重要かまでについて触れませんでしたが 最初に、ユーザログに関する戦略および設計の重要性について触れておきたいと思います。

データの質が分析の質につながる

当たり前ですが、ログとして取得していないことは分かりません。 ユーザログの分解能が、分析でわかるユーザ行動の最大の分解能です。 そのため質の高い分析の為には、質の良いユーザログが必要です。

場当たりなログ取得を行っていると、例えばの以下の様な場合に問題が発生します。 「この新機能やたらと最近人気高いけど、周辺行動ってどうなってるの?」 「この前のoooする人のユーザは、このページでどういう行動をしているの?」

これらの分析に「あー、ログがないので分かりませんね」となると ログの仕込みから入ることになるため、所謂PDCAスピードの劣化の原因に繋がります。

「最初は大ざっぱに作って後から細かく分析する」といったcoarse-to-fineな戦略は 分析のためのログ設計として、スピード感を大事にする場合にはとても相性が悪い、と肌感として感じています。

詳細なログから周辺化して、行動の粒度を粗くする手法を考える方が良くうまくいきます。

ユーザログはビジネスロジックなどに密結合する

ユーザログの集計値がサービス改善のための目標値になることは多いはずです。 そもそもの目的はそれらの状況把握だからです。

ユーザ行動の“重要なイベント” は “分析においても重要"であることから、 どのようなユーザログを送るかは、即ちビジネスロジックと呼ばれるようなサービス自身の ドメインに密に紐付きます。

つまり開発としても非常にセンシティブなものになり、開発の際に憂慮するべきことが一つ増えます。

さらにログの埋め込みは、凝集性が低くなりやすく それこそprintデバッグのようにあちこちに埋められることになります。 そして事故で大事なログを他の人が誤って消さないように、コメントで

*ここはとても大事なところなので消さないこと!**”

のような文字列が埋め込まれるようになります

フロントエンド開発の激化

この数年でフロントエンド開発の様相が一変してしまいました。

テンプレートエンジンを用いてバックエンド側でHTMLを生成し、 ちょっとした動きをつけるために jQueryを使ってjavascriptを書いて時代から Vue.jsやReactといったフレームワークを利用し、javascriptMVCを記述する時代に変わりました。 ServiceWorkerといったブラウザ技術やSPA, AMPなどのWeb技術の周辺技術の発展も目覚ましいものがあります

とりわけサービスのフロント部分はtoC企業において、開発によって変更がなされる最も激しい部分です

ユーザログ基盤の設計は、これらの激しい変化に耐えうるものでなければなりません。

「サービス開発を続けていくうちに気づけばログが取れていない」の様な自体は必ず避けなければなりません。

Web開発におけるユーザログ設計の留意点

じゃあいかに設計するの?という時の観点として、僕の場合は以下の様な観点で考えてみました。

変更のしやすさ / 頑健性のバランスの問題

printデバッグほどの手軽さで追加/削除できるようにすれば良いかというと ログ量の設計の問題や大事なログを誤って消さないようにする必要もあるため、 変更のしやすさだけを追求すれば良いというわけではありません。

かといって、実装を隠蔽すれば良いかというとそういうことでもなく ログの内容はドメインの内容にそのまま直接紐付くことが多いため 隠蔽に苦労します。 ガチガチに設計として堅くしたとして、それがボトルネックになって 開発全体の速度が落ちることになることだけは、必ず避けなければなりません。

テスタビリティも課題の一つといえます。 何よりあらゆる多重発火や欠損を防ぎたいため、結合テストがある方が安心して開発できますが 結合テスト単体テスト比べると高コストです

何をユーザログとして取得すべきか?

分析の質はログの質次第、ということを言ったように 何を取得すべきかは非常に大事な要素です。

何が必要か分からんから、あらゆる情報をログに詰め込む!みたいなことができるかというと それをしてしまうとログの転送量の問題やパフォーマンスの問題に直面することになるでしょう 外部サービスを利用しているならば上限設定もあるはずです。

ユーザの行動ログを送る処理が重いせいで UXが悪くなってしまったでは本末転倒です ユーザの抱えるデバイスについても、格安SIM等のような帯域の厳しいデバイスも多く存在します。

プライバシーの問題もあります。 データの主権者がユーザであり、ユーザはそのデータの取り扱いに対して権利を持つことが GDPRによって定められ、EU圏内では個人データに関する厳しい取り決めがなされたことは まだ記憶に新しいでしょう。

コンポーネントからドメインを分離する

例えばレコメンドなどの機能開発を行ったとして、枠ごとにログを仕込むことを考えましょう

Vue.jsの場合、例えば以下の簡単なコンポーネントが考えられます

<!--
 RecommendedItem.vue
-->
<template>
  <article>
    <h2>{{ title }}</h2>
    <!-- some contens... -->
  </article>
</template>

<script>
export default {
  props: ['title'],
};
</script>
<!--
  RecommendedList.vue
-->
<template>
  <ul>
    <recommended-item
      v-for="item in items"
      v-bind:title="item.title"
      />
  </ul>
</template>

<script>

import RecommendedItem from './RecommendedItem';

export default {
  components: {
    'recommended-item': RecommendedItem,
  },
  data() {
    return {
      items: [
        { title: 'あなたが読むべきたった一つの本' },
        { title: 'おら、東京さ、行くぞ' },
        { title: ‘突破力 〜成功するたった一つの方法〜' },
      ],
    };
  },
};
</script>

これに対するログを考えたとき、以下のような情報は取得するでしょう

  • ページに関する pageviewログ
  • List 自体の in-viewログ
  • ListItem に対する in-viewログ
  • ListItem に対する click ログ

これだけのログがあれば

  • レコメンド自体のinview率 = List Inview数 / PV 数
  • レコメンド自体のクリック率 = ListItem click数 / List in-view 数
  • レコメンドで表示されるアイテムに関するクリック率 = ListItem click数 / ListItem in-view 数

のような分析程度が行えます。

問題となるのが ListItemに関するログです。 より詳細な分析を行おうと考えた場合、以下の様なコンテキストが欲しくなります

  • List自体の表示のロジック (e.g. 価格順 / オススメ度順 / 人気順 ...)
  • ListItemが何番目に表示されているか?
  • ListItemに表示しているコンテンツの情報 (e.g. 商品ID / 文言等 …)

これらの情報を ListItem自体のclickやinviewの周辺情報として仕込むことになりますが 一方でコンポーネントの設計として考えると、これらの情報は ログのためだけに必要な情報であって、コンテンツ表示のために必要な情報ではありません。 そのためListItemにこれらの情報を与えるのは知りすぎ、ということになります。

コンポーネント指向で設計を考えると、下位のコンポーネントほど使いまわしがしやすいようにシンプルに保ちたいですが ユーザログ的には 下位のコンポーネントほどユーザ行動に関係するため 詳細なログを仕込みたくなります。

これはアプリケーションの好ましい設計と相反し、コンポーネント自体が情報を持ちすぎる原因にもなります。

ログは容易にドメインロジックと密結合しやすいという話を先ほどしましたが コンポーネントが情報を知りすぎて、果てはビジネスロジックもガチガチに埋め込まれている… といった情報は避けたいところです。

どのようになったか?

ここから具体的な開発な話になります。

Rettyで採用しているフレームワークはVue.jsですが Vue.jsに用意されているプラグイン機構を使いうことで 全てのVueコンポーネントに対してmixinとして共通処理を記述することができます。

今回作ったログ機構は、これを使うことで本体側のコンポーネント設計と切り離しました。

また フレームワークとの密結合 / 外部サービスとの 密結合を避けるため ログ機構自体を 以下の3つのパートに分けています。データ基盤とかでよく見るETL処理の様な構成にしています。

Extract部
Transform部
Load部
コンポーネント(DOM)ツリーのイベントの管理と
コンテキスト情報の埋め込みを担う
ログとして送信するための
情報の加工を行う。ドメイン特有のロジック等も入ってくる

ユーザログの送信とその受け手となるサービスとの調整を担う
  • フレームワークと結合部分
  • イベントの定義
  • イベントのハンドラ管理
  • ユーザ情報の定義
  • フィルタリング
  • データの変形
  • ドメイン特有のロジック

Extract部は、最も開発が激しく変化の激しいくフレームワークとも結合する部分です フレームワークのライフサイクルに合わせた開発を行います。 下位コンポーネントから上位コンポーネントへ伝播することで、 上位コンポーネントは下位コンポーネントに対してコンテキスト情報の付加を行えるようにしています。 これにより下位コンポーネントが知りすぎることはなくなります。

Transform部では、Extract部から送られてきた情報の加工およびフィルタリング等を行います。 ドメイン特有のロジック等もここに入れ込むことで、集約化を図ります また変化の激しいExtractから切り離すことで、ドメイン特有のロジックを保護する目的もあります

Load部では送信のために必要な外部サービスとの連携の役割を担います。 効果的にデータを送信するためのバッファリングだったり、失敗した場合のフォールバック処理等も行います。

フレームワークとの密結合するExtrac部に集約されるため、Transform部やLoad部は疎に保たれ テスタビリティを高く確保した実装ができます。

typescriptを入れ込むこむとでより幸せになれます。 上記の様な構成では、interafceやデータ型の恩恵を大きく受けることができます。 これらのやりとりの際に、型があるとログの形式に関するバリデーションコストが低くなります。 Vue.js は TSサポートしていますが、少々扱いにくい点があったりしますので Vue部分のTS化は様子を鑑みてかなと考えています。

おわりに

本記事では、ユーザログの設計と開発についてまとめました。 今回は現段階のログ基盤についてお話しいたしました。いかがでしたでしょうか?

ベンチャー企業としてのアジリティ、理想のデータ基盤を実現するため 上記のような開発をしてたりします。

ご興味をもたれた方、盛り上げてやるか!と思って頂いた方 Rettyでは一緒に働く仲間を絶賛募集中です。

corp.retty.me

Retty新卒エンジニアの入社半年間の振り返り〜Part.2〜

こんにちは、激辛な食べ物が大好きな2018年新卒エンジニアの諏訪です!Rettyではサーバーサイド兼フロントエンドエンジニアをしています。インターンを含めるとほぼ2年間Rettyにいますが、入社半年の節目として振り返ります!

入社前

大学に通いつつ独学でプログラミングを初め、自作PCでサーバーを作るなどプログラミングからネットワークやインフラ周りまで幅広く勉強していました。ずっと独学で勉強していたので自分の技術力に不安を感じ、Web系のインターンに7社参加し、実際の現場でプロダクトに触らせてもらうことで、実装からリリースまでのフローやプランナーとのコミュニケーションなどを学びました。 Rettyには大学3年の頃から1年半ほどインターンに参加し、インフラからフロントまでフルスタックに働くことができそうだと思いRettyにJoinしました。

f:id:rettydev:20181115173323j:plain △大学3年の頃から1年半ほどインターンに参加(2017年11月撮影、左が諏訪、右は先輩エンジニアの竹野)

なぜフロントもサーバーサイドもやっているのか

RettyにJoinするまではフロントはほとんど触ったことはありませんでした。サーバーサイドを極めていくには、フロントからどういうリクエストが来てどれだけのトラフィックが必要なのか、どういうネットワーク構成でどの部分でデータがキャッシュされているのかなど、知らないといけないことはたくさんあります。最初から一つの分野に絞ってしまうと視野が狭くなってしまうので、新卒の段階ではまずフルスタックに働けるようになろうと思いました。最初はフロントからやりたいなと考えていたタイミングで、ちょうどフロントの案件が発生したこともあり、フロントエンドの世界へと足を踏み入れました。

最初はVue.jsやVuexの概念を理解するのに苦労しましたが、先輩にペアプロペアプログラミング)やコードレビューをしていただき、実務で触れるレベルまでスキルを磨くことができました。

今ではフロントの開発が楽しくなってしまい、7 : 3ぐらいの比率でフロントメインで業務に携わっています(笑)

関わったプロジェクト

入社後は主に以下の二つのチームを兼務しています。

WEBチーム

WEBチームではRettyの検索ページ( ex. https://retty.me/area/PRE13/ )や、店舗情報詳細ページで施策や機能追加の業務を行なっています。

配属直後はフロント初心者だったため、施策や小さめの機能追加の実装をしていましたが、5月頭ぐらいには店舗情報詳細ページに予約カレンダー機能を追加するといった大きめの実装を任せてもらいました。10月には、お店検索需要が高まる忘年会シーズンに向けた施策の実装も行いました。VueでA/Bテスト基盤を作るなど技術的にも色々できた半年でした。

PCに実装した予約カレンダーは、日付選択時にアニメーションを与えることで、切り替わっていることに気づいてもらえる仕組みを入れました。また、カレンダーを導入するにあたり、これまではDBにデータを取得しに行くことでカレンダーを生成していたのですが、負荷の問題からDBを直接見にいくことはせず、間にElasticsearchを挟むなど様々な工夫をしています。

スマホカレンダースマホカレンダー
WEB版Rettyの予約カレンダー

javascriptCSSをプロダクトレベルでほとんど触ったことがない状態からスタートしましたが、配属直後から実務を任せてもらえたことで、webpack等のビルド環境やVue.js等のフレームワークを実務レベルで使い、他エンジニアのコードレビューができる状態まで短期間で成長することができました。 また、プランナーとのコミュニケーションや他チームとの連携、仕様をもらってからの工数見積もり等、個人でプログラミングをしている時にはあまり身につかないスキルや経験を学ぶことができました。

編集チーム

編集チームでは主にRettyグルメニュース( https://retty.news/ )の入稿画面の改修や開発生産性向上の業務に携わっています。入稿画面にSEO向けの機能の追加やtextlintを利用した記事の自動校正システムの導入をしました。

開発の生産性を向上させるものとして、これまで手動で行っていたDBマイグレーションの自動化やステージング用のDBの自動更新、デプロイ自動化など、オペレーション・ミスが発生しそうな部分の自動化を行いました。 この辺りではRDSやCircleCI、Elastic Beanstalkの経験を積むことができました。

これからについて

個人での開発はたくさんしてきましたが、チームで開発をするのは初めてだったので自分のスキルの足りなさを感じる一方、いろいろな方にサポートいただいたおかげで、メンターからは「セマンティックなHTMLからBEMとscss、モダンjsの書き方、Vue.jsにおけるコンポーネント設計、バンドラー環境開発など、フロントエンドの最初のハードルである膨大な量の技術をあっという間にものにしていく姿には1年目とは思えない安定感があった。」との評価をいただくほど、成長を実感できた半年でした。

現在はフロントメインで働いていますが、大規模なトラフィックに耐えられるサーバーサイドの設計実装やパフォーマンスのチューニングをするのが好きなので、いずれはサーバーサイドをメインにしたいと思っています。ですが、フロントのスキルもまだまだなので、当面はフロントメインで去年の自分の10倍は成長する気持ちで頑張りたいと思います!

Retty新卒エンジニアの入社半年間の振り返り〜Part.1〜

はじめまして。2018年度の新卒でエンジニアとして入社しました山田です。 4月の入社から半年という節目を迎えるに当たり、これまでRettyでチャレンジしてきたことについてまとめていきたいと思います。

自己紹介

高専出身です。 在学中は電子制御工学科に属しており、ロボットを作るための様々な技術(機械工学、プログラミング、電気工学etc…)を包括的に学んでいました。 ロボコンに出場して、地区大会で優勝したこともありました!

その傍ら、プログラミングを通した物作りにハマり、個人でAndroidアプリを作って販売してみたり、ベンチャー企業ウェブアプリケーション開発に参加したりとソフトウェア開発に携わってきました。

そのような経験を積む中で、自分が興味関心を持っていること(プログラミング以外に食べることも好きです!)を仕事にしたいと考え、卒業後、Rettyへジョインしました。

主な業務について

4月下旬からアプリチームに参加し、APIサーバー開発からiOS/Androidアプリ開発までを手がけています。 Rettyのアプリチームでは、フロントエンドであるiOS/Androidアプリの開発は勿論、それらがバックエンドで使用しているAPIサーバーの開発も担っています。

スクラム開発を導入しており、今までチームでの開発経験に乏しい僕にとっては、とても良い刺激になりました! 特に、タスクにポイントをつけておくことで、チームとしてどのくらいのタスクをこなすことができるのか数値化し、今後の改善に活かしていくやり方はとても合理的だなと感じました。 参加してすぐの時は見積もりの勘所がわからず、大変だったな〜という思い出があります。
広範な技術を扱うチームなので、とてもたくさんのことにチャレンジしています!

f:id:rettydev:20181025111648p:plain
Rettyアプリチームのメンバー(手前右が山田)

APIサーバー開発

アプリチームにジョインして最初に担当したタスクがAPIの機能追加でした。 Rettyで使用しているAPIサーバーはKotlinで開発されています。
Kotlinは個人的なAndroidアプリ開発では触っていたのですが、Android以外でKotlinを使用することがなかったため、とても新鮮でした!
また、今までSQLにそこまで多く触れたことが無く、SQLを活用してDBからデータを取得してくるということに対してとても不便そうなイメージを持っていました。しかし、そういった部分にも挑戦することができ、例えばViewのような、自らの知らなかったより便利な機能を知ることができ、そんなイメージを払拭することができたなど、多くの学びを得ることができました。

iOSアプリ開発

次にiOSアプリの開発にも参加しました。 Rettyに入社するまでiOSアプリ開発の経験は一切なく、Objective-Cは勿論Swiftも、iOS固有の知識も何も知らない状態だったので、思い通りにUIを作ることがなかなかできず、最初はとても苦労しました。
しかし、アプリチームの先輩エンジニアから手取り足取り教えていただき、参加してから一ヶ月が経過した時にはきちんと開発に参加できるようになりました。 実際に、開発に関わったものとしては、非ログイン機能や新しい投稿画面の実装、各種不具合修正などを行いました。

Androidアプリ開発

7月ごろからAndroidアプリのリニューアルに向けた開発も行なっており、現在はこの業務がメインです。

僕が開発に参加するまでのAndroidアプリは、Activity内にUI周りの処理やUIと関係ないデータの処理などが入り混じっている、いわゆる「FatActivity」な設計となっていました。これによってコードの見通しがとても悪く、リファクタリングも簡単に行えないような状態でした。
これをなんとかしたいと思い、リニューアルをするという良いタイミングだったので、「今後のためにもMVPアーキテクチャを適用し、テストの導入を進めていきたい」とチームに提案したところ、快く受け入れてもらえ、設計の改善も自ら主導することになりました。

最近では、投稿ユーザーさんのプロフィール詳細画面のリニューアルを行いました。以前よりもサクサク動くようになり、メンバーからも「使いやすくなった」というフィードバックをもらえました!

f:id:rettydev:20181015142121p:plain
Android版Rettyアプリのユーザー詳細画面

おわりに

Rettyにジョインしてからおよそ半年、サーバーサイドからAndroid/iOSアプリのフロントエンドまで、知っていたことも全く知らなかったことも、区別なく様々なことに挑戦してきました。これにより、領域を気にせず、知らないことにも臆さず、やりたいと思ったことにどんどん突っ込んでいける力が身についたと感じます。

今後も、この自由に挑戦できる環境を活かして、ゆくゆくはプロダクト開発を担えるフルスタックエンジニアになることを目指して、技術の区別なくどんどん知識を吸収し経験を積んでいきたいと思います!

新卒メンバー全員でシャッフルランチをつくり、PDCAサイクルを回してみました(後編)

2018年4月に入社した新卒エンジニアの堤です。前回に引き続き、入社後に新卒メンバー全員で初めて企画・開発した「シャッフルランチ」の取り組みについてご紹介します。

f:id:rettydev:20180925185708j:plain ▲2018年の新卒入社は総合職3名、エンジニア4名の計7名。職種を問わず新卒メンバー全員が参加する研修が2週間ほど行われました

1. シャッフルランチトライアル後のシステム改善

まずは、トライアルでのシャッフルランチ導入後の改善について。

主に、

  • 構成の刷新
  • シャッフルロジックの改良

についてお話します。

(1) 構成の刷新

前半でもお話しましたが、シャッフルランチの実施初期はGASを利用していました。ただ、運用していく上で大きく2点課題がみつかり、構成を刷新することになりました。 具体的には下記の2点です。

  1. GASがチーム開発に向かない
  2. Slackボタンが使えない

1について、GASが標準ではバージョン管理に対応していません。基本的にはWeb上のIDEからGASのソースコードを直接編集する形になります。この場合、

  • 複数人で開発するときに編集箇所が競合し、編集が適用されないなどの面倒が生じる可能性がある
  • バージョン管理がされないので、以前の状態に戻すことが難しい

という状況になります。 トライアル開発時は完全に分担しており、開発量も多くなかったので問題になりませんでしたが、運用にあたって加えたい機能が次々と出てきたため、このまま開発を続けることが難しくなりました。 ちなみに、GASとローカルを同期する機能は以下にまとめられているので、個人開発であればバージョン管理の問題は解決されそうです。

https://techblog.recruitjobs.net/development/maneged_google-apps-script_by_github

2つ目の課題は、Slackボタンが使えない点でした。 背景として、アンケートや出欠確認でSlackのボタンを使いたいよね、という話が上がっていました。ただ、Slackのボタンには3秒以内にレスポンスを返さないとTimeoutする仕様があり、GASだと3回に1回程度はTimeoutしてしまいました。 これに関してはGASを使っている以上はどうしようもないので、トライアル時にはボタンの利用を諦めました。 その他にも、GASのJavaScriptが古い(ECMA Script 5)だったり、動作時間に制限(1度に6分まで)があるなど、今後の運用を考えると移行したほうが良さそうだという話になりました。

以上の理由から、最初のトライアルを終えて2度目のトライアルまでに、主に新卒エンジニアの神(こう)と諏訪が以下の状態にお引越ししてくれました。

  • バックエンド
    • Botkit (Node.js)
  • 開発環境
    • GitLab CI
    • Rancher

BotkitはNode.jsによるbot作成フレームワークの一つで、Slack以外にも様々なChatBotを作成することができます(https://botkit.ai/)。 また、GitLabを立て、CIツールとしてGitLab CIを用いることで、Docker Image作成の自動化を図っています。GitLab CIについてはこちら(https://qiita.com/bremen/items/f47f383b9931a840a25c) に詳しく書かれています。 さらに、Rancherを利用することで、GitLab CIによって作成されたDocker ImageのデプロイをGUIから簡単に行えるようにしています(https://www.slideshare.net/recruitcojp/rancherrancher)。 これによって、

  • チーム開発のスピード向上
  • (レスポンス速度改善により)Slackのボタンが利用可能に
  • botフレームワークの利用により、bot機能の開発が楽に

が実現されました。

(2) シャッフルロジックの改良

トライアルを実施してみると、「よく話す人と同じグループになったためシャッフル感がない」「同じ部署の人と一緒になった」などという意見が散見されました。 今までは完全にランダムシャッフルを行ってグループを作っていましたが、「似ている社員が違うグループになるようにシャッフルしたほうがいいのではないか?」という話が上がりました。 そこで、社員の下記の属性を利用し、似た社員が同じグループになりにくいようなシャッフルを行うこととしました。

  • 社員属性
    • チーム
    • 職種
    • 性別
    • 勤続年数

要件はこちらです。

  1. 属性の似た社員が同じグループになりにくい
  2. メンバーは毎回異なるようにしたい

「属性の似た社員が同じグループになりにくい」ということで、確率を利用したロジックを用いることにします。今回は「焼きなまし法(Simulated Annealing)」1という方法を用いることにしました。 焼きなまし法組み合わせ最適化問題に対するアルゴリズムの一種で、巡回セールスマン問題(TSP)などを解決するときに利用されます。 今回は

  • 条件
    • 同一グループに属する社員同士の類似度をコストとする
  • 制約
    • 全体のコストを低下

と設定し、ロジックを作成しました。 ロジックの大まかな流れがこちらです。

  • 全体のコストが小さくなるように、以下のようにSwapをR(=2000)回程度繰り返す
    1. 別々のグループからランダムに2人選ぶ
    2. 2人を入れ替えたときのコスト変化を計算する
    3. コスト減少ならSwap、そうでなくても確率PでSwap

焼きなまし法では、こちらの「確率P」が徐々に下がっていくことで、最初はコストが増加するSwapも許容しつつ、だんだんコストが減少するSwapのみ許容に変化していき、最適な解に近づけていきます。 今回は「メンバーは毎回異なるようにしたい」という要件があったため、確率Pの値は割と高い値を保ったままにしています。そうでないと最適解に近い値を求めてしまい、毎回メンバーが固定されてしまうからです。 今回はこちらのロジックを採用することにより、属性の似た社員が同じグループに固まることを防ぎつつ、メンバーが毎回異なるようなシャッフルを実現できました。

補足
今回のコストは以下のように設定しています。
グループgのコストは以下のように定義する
- groupCost(g) := (baseCost(g) / groupSize(g) ** 2) ** 2
ただし、
- baseCost(g) := グループgに属するメンバー同士の類似度合計
- メンバー(a, b)の類似度 := sameJob(a, b) + sameTeam(a, b) * 2 + sameSex(a, b) + distHist(a, b)
- sameXXX(a, b) := XXXが同じなら1、そうでないなら0
- distHist(a, b) := 勤続年数が近いほど大きくなる値
このとき、全体のコストは各グループコストの合計である。

その他、出欠の自動化であったり、取得に時間のかかるSpreadSheetのCache作成など、課題が上がるごとに機能を追加していきました。 現在もシャッフルランチの運用をより良く、より楽にするために改善を重ねています。

2. 本格運用を開始

2018年4月に新卒でRettyに入社し、Webプランナーを担当している山田陸です。

新卒メンバーみんなで試行錯誤を重ね、トライアル期間に見つけた課題も解消し、7月より全社でシャッフルランチの本格運用を開始しました。 新たな課題が出る一方で、社内ではポジティブな変化もありました。

(1) 課題:参加率の低下

トライアル期間中は、出席者に直接声をかけることも行なっていたため多くの社員が参加してくれましたが、自動化するために出席確認をbotで送る仕組みに変えたところ、大幅に参加率が低下してしまいました。

原因は、シャッフルランチの目的やメリットが社内に浸透しきれていないことや、既に予定が入っていて参加できない人を含めて選定している点などが考えられます。 完全自動化からは少し離れてしまいますが、社内でのアナウンスなど、シャッフルランチが定着するための取り組みも行なっていこうと考えています。

(2) ポジティブな変化:社員同士の声のかけ合いやすさ向上

一度ランチをしたことで、その後も社内で声をかけやすくなったという声がありました。ランチを通じて、仕事だけでなくお互いのプライベートなども知れば、互いの距離はグッと縮まります。シャッフルランチをきっかけに、仕事でのコミュニケーションにもつながることを垣間見ることができたことは、非常に嬉しいです。

3. 今後のシャッフルランチについて

Rettyはこれからも事業の拡大に伴い、社員の数も増えていくことが予想されます。社員同士の交流は、会社全体が「チームRetty」として成果を生み出していく上で欠かせません。 社員同士の交流を活性化するためにも、シャッフルランチの改良を重ね、参加率を向上させていきたいと思います。

私たち新卒メンバーが企画した「シャッフルランチ」によって社内のコミュニケーションが活性化され、サービスやビジネスの成長に貢献できたら嬉しいです。

新卒メンバー全員でシャッフルランチをつくり、PDCAサイクルを回してみました(前編)

こんにちは、2018年4月に新卒でRettyに入社し、現在はプロダクト部門でWebプランナーを担当している山田陸です。

2018年の新卒入社は、総合職が私を含めて盛山、武藤、私の3名、エンジニア職が神、諏訪、堤、山田、の4名、計7名います。今回は、新卒で入社した私たちが入社後に初めて企画・開発した「シャッフルランチ」について、メンバーを代表して私とエンジニアの堤からご紹介します。

f:id:rettydev:20180925184148j:plain ▲入社式の記念撮影(2018年4月、メッセージフラッグを囲む7名が新卒メンバー)

1.なぜシャッフルランチなのか?

(1) 研修中に得た気づき

今年の新卒入社研修は、職種を問わず新卒メンバー全員が参加する研修が2週間ほど行われました。同期間、私たちは毎日、話してみたい先輩社員に声をかけ、ランチに出かけていました。

それぞれのチームの仕事内容や雰囲気などについて、ざっくばらんに話を聞くことがランチの主な目的でしたが、先輩たちからは「以前よりも社員同士の交流が減っている気がする」といった声がありました。

社員数は100名を超え、部署の数も増え、顔を合わせる機会の少ないメンバーが増えていると感じていたようです。

成長と共に組織が拡大するベンチャー企業ではよくある話だと思い、当初はこの課題に対して私たち自身が何かするという発想はありませんでした。しかし、研修では「チームで動いてこそ、大きな成果を生み出すことができる」という話を繰り返し聞き、Rettyではいかにチームワークを重視しているかについて知ったことで、改めてこの課題に向き合うことを考えました。

(2) 課題解決方法の検討とシャッフルランチの決定

  入社して1週間が経過した4/9(月)の研修後に新卒メンバー全員で集まり、他社の事例なども参考にアイデアを出し合いました。そうして検討が始まったのが、他社でも導入事例が増えている「シャッフルランチ」です。グルメサービスを運営するRettyでは、「User Happy」なサービスを提供するために、まずは社員自身が「食」を楽しむことを大切にしています。部門や職種の垣根を超えた交流を活性化する方法としても、「食」に関わることの方が、Rettyらしいと思いました。また、「シャッフルランチ」であれば、2週間の研修期間の中でも企画・開発し、研修後もPDCAサイクルを回すことができそうだと判断しました。

早速、研修期間のランチで感じた課題や、それを解消したい旨を研修責任者に伝え、新卒メンバーのプロジェクトとして取り組むことが決定し、4/13(金)には全社員が集まる総会でも発表しました。

開発に向けて、まずはトライアルとしての運用フローを検討しました。私たちが考えたフローは、下記です。

  • 社内のコミュニケーションツールSlackに、シャッフルしたメンバーのチャンネルを自動で作成
  • 新卒メンバーが音頭を取りながらお店選び、予約などを実施
  • シャッフルメンバーでランチ
  • ランチ後にメンバーにアンケートを実施

まずは上記のフローでトライアルを実施し、シャッフルランチが上手くいきそうかどうかを検証することにしました。

2.シャッフルランチシステムの開発

新卒エンジニアの堤です。

ここからは、神、諏訪、山田、堤の4名による新卒エンジニアチームで行った、シャッフルランチ導入までの取り組みを紹介します。

(1) 技術選定と役割分担

シャッフルランチを新卒プロジェクトとして決めたのが4/9(月)ですが、それを社内で発表しデモを動かすのを4/13(金)の総会の日に設定しました。 そのため作業期間は4日、しかも日中は研修のため、空き時間を使っての短い時間で作り上げる必要がありました。 そこで新卒エンジニア4人で相談し、サービスの仕様や構成、役割分担を決定し、各自進めていきました。

  • 仕様
    • 社員のランチ出欠をSpreadSheet上に保存しておく
    • 決まった日時に、ランチ参加社員をシャッフルしてグループを複数作成
    • Slackにてグループチャットを作成し、ランチを促す
    • Googleカレンダーにランチの予定を登録
  • 構成
  • 役割分担
    • DB連携機能
    • 環境構築・スケジューリング
    • Slack連携機能・Googleカレンダー連携機能
    • ランダムシャッフル

全社発表の日までにデモを実行できるまで作成し、5月の半ばに最初のバージョンを完成させてトライしました。

(2) 実際の動作

実際に作動させた場合、Slack上でグループを作成した後、このようにメッセージを投げてシャッフルランチを促します。 f:id:rettydev:20180925103139p:plain

(3) 構成について

今回は機能がシンプルなため、GAS + SpreadSheet(DB) + Slack(UI) とすることで、サーバを用意せず準備・運用の手間を省く構成としました。 GASとは、

Google Apps Script は、Googleが提供するサーバーサイド・スクリプト環境です1

GASの詳細は省きますが、簡単なWebアプリケーションや定期実行スクリプトJavaScriptで作成し、動作させることができます。 また、GASを利用するときにデータベースが必要な場合は、SpreadSheetが一緒に用いられます。GASからのアクセスが簡単で、かつSQLの知識がなくともデータベースを編集できるという利点があります。今回参加者リストを管理したのですが、新卒総合職もリストを編集・閲覧する必要があり、この利点が活きました。 また、UIとしてSlackを利用しています。理由として、

  • 社内で日常的にコミュニケーションツールとして利用されている
  • APIが充実している

ことが挙げられます。 Slack連携やこれら全体の連結は、新卒エンジニアの諏訪が行ってくれました。

(4) SpreadSheet連携

GASでSpreadSheetを利用するに当たって、表を配列として扱う必要があります。2 読み込み・書き込み時に配列への操作が必要となるため、例えば次のようなSheetがあった場合に、

id team user
0 営業 Shogo
1 App Yutaka
2 検索 Shiho
3 経理 Taro

3行目のuser列の値を取り出す操作は以下のようになります。

// シートを取得
var sheet = SpreadsheetApp.getActive().getSheetByName('sheet1');
// 3行目
var rowIndex = 3;
var colStartIndex = 2;
var rowNum = 1;
var colNum = 1;
var data1 = sheet.getRange(rowIndex, colStartIndex, rowNum, colNum).getValues();
// または
var sheetData = sheet.getValues();
var data2 = sheetData[rowIndex][colStartIndex];

ただ、これだと1行目のタイトル行の扱いが面倒だったり、数値での列指定のため列の追加に弱かったりします。そこで、新卒エンジニアの山田が以下のライブラリを作ってくれました。

https://5hyn3.github.io/posts/use-spreadsheets-from-gas-without-thinking-about-difficulties/

詳細はリンク先で書かれていますが、SpreadSheetの各行をオブジェクトとして扱えるようにし、上記の問題点を解決してくれました。また、updateやsave等のメソッド作成により、DBを扱うようにSpreadSheetを扱えるようになりました。

(5) サービスの運用と課題

最初のトライアル実施までに、念入りに新卒メンバーでテストしていたこともあり、無事社員をシャッフルしてランチに行くことができました。 ただ、いくつか足りないこと、やりたいことが数回のトライアル実施を通じて挙がってきました。次回の記事では、挙がった課題とその解決策についてお話します。

PyCon ThailandでGeoDjangoについて話してきました

こんにちは、Retty Global Team サーバーサイドエンジニアの omega (@equal_001) です。

PyCon Thailand参加に参加し、GeoDjangoについて話してきました。

PyCon Thailand

th.pycon.org

6/16, 6/17の二日間で開催されました。 今回が第一回目の開催で、参加者は200人程だったそうです。

現地の学生さんやTHAI PROGRAMMER というタイのプログラマコミュニティの方々も参加されていました。

話した内容

CFP: Powerful geographic web framework GeoDjango

docs.google.com

GeoDjangoというDjango地理空間情報を扱うための公式モジュールについて話しました。 Retty Global でもレストランの位置情報を扱う際に使用しています。

今回はBeginner向けの発表にしたのであまり深い話はしませんでしたが、当日の参加者でGeoDjangoを知る人が少なかったので発表後に良いフィードバックをいくつかいただけました。

感想

タイでもブロックチェーン, 機械学習系の発表が多めでした。 タイのプログラマ事情は日本にいるとなかなか耳にしないのですが、自国向けサービスがたくさんあり近年はスタートアップも増えてきているとのことです。

来年も開催するかもしれないとのことだったので、次回はDjangoを使った開発の知見などを発表したいと思います。

一緒にRetty 海外向けサービスの開発をしたい人を募集しています!

Retty Global Teamでは海外向けサービスの開発に興味のあるエンジニアを募集しています!

iOSエンジニア(Swift) | Retty株式会社