サービス成長との付き合い方

はじめに

サーバサイドとAndroidネイティブアプリの開発を兼務している坂根です。

webアプリ開発を行うエンジニアと聞くと、 要求された機能を実装する つまりディスプレイと睨めっこしながら黒い画面に文字をタイプし続けるコーディングが中心のお仕事、と考えられている方もいらっしゃると思います。 しかし、私は なぜ開発をするのか 開発をする目的は何か 背景の理解を大事にして働いています。

皆さんの身近にいるエンジニア、想像するエンジニアは、どのような働き方をされているでしょうか? 最近、背景の理解を大事にしていないエンジニアが案外多いのではないかと感じています。 今日は背景の理解を大事にするとはどういうことか、私の仕事を通して紹介させていただきます。

要件の分解・分析

弊社では主にwebディレクターが開発案件のビジネス要件を考えています。 ユーザーが抱える潜在的な課題の解決、リーチ・認知の拡大、リピート率の向上など、webアプリエンジニアには考えが及びづらい箇所を基点に日々議論されています。

しかし、考えが及びづらいが故にwebエンジニアは全容の掌握を怠ってしまう場合があります。

全容の掌握

全体の掌握を怠ってしまうと何が起こるのでしょうか? webディレクターが事前に検討しているため、降りてくる要件を実現するだけで 近い将来の目標 に到達することは難しくないでしょう。 しかし 数年後の目標 に自然な形で到達するためには、課題から数年後の目標までの道筋を設定した上で、中間に近い将来の目標を置く必要があります。 つまり全体の掌握を怠ってしまうと、発射角を誤り望んだ未来や自然な未来に辿り着かないケースに発展し、無駄に難易度の高い仕様変更を重ねていくことになると考えています。

数年後の目標

以上のことから、課題やターゲットを明確にして、より良い解決方法を求めることを大事にしています。

解決策の模索

解決したい課題と数年後の目標を明確にできたからと言って、直ちに最善の解決策を出せるとは限りません。 議論に挙がる解決策がユーザーに驚きを与えるものだとしたら、どうでしょう? 「使い方が分からない」「意味や意図が理解できない、不安」など、ユーザーにとってもサービスにとっても多くの混乱を招きそうです。 問い合わせや不満に繋がり、やがてはサービスからの離脱も・・・と考えられ、この状態では誰も幸せにならないことも容易に想像できます。

では、驚きを軽減するためどのような方法があるでしょうか? 驚き = 予期しない事象を体験したときに起こる瞬間的な感情 ということで、予期できる事象を考えます。 予期できる事象とは、同一サービス内で類似の体験がある、他サービスで体験しており既に親しみがある、などが考えられます。 類似の体験があることで学習コストは低くなり、ストレスも溜まりづらいなど副作用が期待できます。

コストの掛かり過ぎる解決策の場合はスモールスタートさせることもありますが、可能な限りユーザー体験を考えることも大事にしています。

妥当性の担保

ここまで考えたので、もうこれ以上考えたくない!という方もいらっしゃるかもしれませんがもう少しです。最後までお付き合いください。

これまでは下記の流れで考えてきました。 数年後の目標に向かって道筋が通ることを確認できたはずです。 しかし、この結論について妥当性が担保されていません。

トップダウン

そこで、逆向きに考えてみます。なぜその結論に至ったのか、根拠を確認します。 納得できる理由付けになっていたでしょうか?納得できる理由付けではない場合、ここで妥協してはいけません。 将来的に不幸になる人が出てくる結果に繋がるため、解決策の模索からやり直します。

ボトムアップ

おわりに

さて、ここまでお読みいただいたみなさま、いかがでしたか?

時間の無駄では?webディレクターの仕事では?案件のスピード感落ちない? などなど、いろいろな感想があると思いますが、以上がコーディング前に考えごとをする私の仕事の一部です。

目的を達成するために・・・私はこれからも背景や全容の掌握を大事にします。 また、その他大勢のwebアプリエンジニアにも大事にして欲しいことだと考えています。

このような働き方に共感・理解いただけるwebアプリエンジニアの方と働けるようになること、日々楽しみにしています。 ご興味ありましたら是非ご応募ください。


弊社では一緒にプロダクトを改善していただける仲間を探しています!

こちらでお気軽にお声がけください!

ネイティブアプリエンジニアの採用って難しいですよね。。。

ジモティーのウェブチームについてお話ししたいです

サーバーサイド開発からiOSアプリ開発チームに異動して感じたこと

こんにちは!ジモティーでiOSアプリ開発を行っている小林です。

私は元々サーバーサイドエンジニアでしたが、2ヶ月ほど前にiOSチームにジョインしました。今回はiOSアプリ開発を始めたばかりの私が感じたRailsによるサーバーサイド開発との違いについて感じたことや、アプリの開発を始めて良かったと感じたことをご紹介したいと思います。

iOSチームにジョインした経緯

入社して約2年半Railsを使用してサーバーサイドの開発を行ってきました。Railsで一通りの開発が経験できたのと、Railsとは違った開発手法を体験したいという思いがありiOSチームへの異動を希望していたところ、その願いが叶えられiOSチームに異動することになりました。 今は約1ヶ月間の研修が終わり実際のアプリ開発を着手し始め、日々新しい学びがありワクワクしながら仕事をしています。

次からは、iOSアプリ開発を始めて感じたサーバーサイド技術との違いについてご紹介します。

iOSアプリ開発を始めて感じたサーバーサイド技術との違い

動的型付け言語 (Ruby) と静的型付け言語 (Swift) の違い

コンパイルの有無の違い

Rubyはソースコードを変えるとすぐに結果を確認できますが、Swiftはコンパイルが必要なためコーディング⇄結果確認のサイクルに時間がかかるようになりました。その代わりにコンパイル時にバグを見つけることができたり、実装時にIDE (Xcode) が随時エラーを指摘してくれたりするため、実装の手戻りは減ったように感じます。

ダックタイピングできないためインターフェースの数が多くなる

SwiftはRubyと違いダックタイピングの機能がないため、同等の機能を実現するためにインターフェース(Swiftのプロトコル)を多用することになります。そのため、記述が冗長になったり、ファイル数が増加したりしますがエラーがコンパイル時点で分かったり、可読性が高いという利点があると思います。

nilの取り扱い

Swiftの利点として大きく感じたのはnilを許容するかしないかを型として明示的に表現できることです。Rubyではnilのチェックが漏れているため実行時にエラーになることが多々ありましたが、Swiftでは実装時やコンパイル時にエラーになり間違いに気づくことができます。その代わりとしてnilを許容するオプショナル型を扱うための構文を学習するコストが少しかかりました。

# Ruby
foo = nil
# ~中略~
foo.size # nilになる可能性があるが、ぼっち演算子(&.)をつけ忘れたため実行時エラー
// Swift
var foo: String  // String型
var bar: String? // nilを許容する Optional<String> 型

foo = nil // コンパイルエラー(fooはnilを許容しないため)
bar = nil // OK(barはnilを許容するため)

print(foo.count)  // => 0
print(bar.count)  // コンパイルエラー(barはnilを許容するが、値をアンラップしたり、オプショナル・チェインを使用したりしておらず、nilになることが考慮できていないため)
print(bar?.count) // => nil (Rubyのぼっち演算子に似たオプショナル・チェインという仕組みが用意されている)

Rubyで参照型なものがSwiftで値型になっている

Rubyでは参照型だったStringが、Swiftでは値型になっています。Swiftでは配列やハッシュまでもが値型なのには驚きました。しかし、値型になことになったことにより、Rubyで防御的にdupによるコピーを行っていたケースでコピーが不要になりました。また、Copy on Writeの仕組みによりデータが変更される時になってはじめてコピーされる仕組みのためパフォーマンスも確保されているようです。

# Ruby
foo = [1, 2, 3]
bar = foo
foo[0] = 5
p bar # => [5, 2, 3] (Rubyの配列は参照型のため、fooと同じ配列を参照しているbarも書き換わる)
// Swift
var foo = [1, 2, 3]
var bar = foo
foo[0] = 5
print(bar) // => [1, 2, 3] (Swiftの配列は値型のためbarは書き換わらない)

アプリケーション開発におけるフレームワークによる違い

弊社ではWebアプリケーション開発にRuby on Railsを使用しています。Railsの場合はRailsが敷いてくれたMVCをベースとしたCoC(設定より規約)のレールに乗り効率よく開発ができていました。 一方iOSのネイティブアプリケーション開発の場合は、標準でMVCをサポートしているものの自由度が高く、規約がない状態だと三者三様の書き方になってしまうこともあり、弊社では以前本ブログでもご紹介したClean Architectureを導入しています。 Clean ArchitectureがRailsより疎結合なことと、依存関係逆転のためのインターフェース(プロトコル)が多いことにより、1つのアクションを実現するために必要なファイル数が多くなっています。

設計で重視される部分の違い

前述の通りWebアプリケーションの開発ではRailsが敷いたレールに乗っていれば効率良く開発することができましたが、ビジネスロジックの設計の良し悪しが後々の改修の際の開発効率に影響することがありました。 逆にiOSアプリの開発では、ビジネスロジックは主にサーバー側にあるためビジネスロジックの設計が開発効率に及ぼす影響は少ないですが、アーキテクチャー設計の良し悪しが開発効率に及ぼす影響が大きいと感じました。

リリース方法の違い

サーバーサイド開発ではリリース、障害発生時の切り戻しは簡単に実施できましたが、iOSアプリのリリースは事前にAppleによる審査が必要なため簡単には実施できません。また、Appleのガイドラインを満たしていないと審査がパスせずにリリースができないといったことが起こりえます。 そのため、予めリリース内容にガイドライン違反になるものがないかのチェックや、サーバーサイド開発よりテスト工数を多く確保し、複数人での実機を用いたテストを集中的に実施する期間を設けて品質を担保しています。

iOSアプリ開発を始めて良かった点

これまで行ってきたサーバーサイド開発に加えてiOSアプリの開発も始めたことにより、特に以下の点が良かったと感じました。

  • モダンな静的型付け言語であるSwiftを学んだことにより、プログラミング言語に対する知識が相対化され理解が深まった。
  • サーバーとアプリ両方のソースコードを把握することにより、サービス全体への理解や、問題が発生した場合の原因特定までのスピードが上がった。
  • サーバーとアプリのインターフェースであるAPIの設計について、開発着手後に仕様が変更された場合のアプリ開発への影響の大きさを理解することにより、その重要性が改めて認識できた。

おわりに

最後までお読みいただきありがとうございます。

ジモティーを更に成長させる為に一緒に取り組んでいただけるエンジニアを募集しています。 ジモティーのエンジニア組織は比較的少人数で一人ひとりの貢献がプロダクトの成長に大きく寄与するため、やりがいが大きいと感じています。また、上に記載した通り、チームの異動についても柔軟に対応してもらえるので色々な技術を習得することが可能です。 もし少しでも気になればお気軽にご応募ください。


弊社では一緒にプロダクトを改善していただける仲間を探しています!

こちらでお気軽にお声がけください!

ネイティブアプリエンジニアの採用って難しいですよね。。。

ジモティーのウェブチームについてお話ししたいです

ジモティーにおけるGoogle Apps Scriptの活用とclaspを使ってコード管理する方法

こんにちは、ジモティーでエンジニアをしている中村です。

以前の橋本の記事が好評だったので、今回もデータ活用周り、特に弊社でのGoogle Apps Script(以下GAS)の活用について書いていきたいと思います。

ジモティーにおけるGASの活用例

弊社ではGASを色々な用途で利用しています。ここではその一例をご紹介します。

KPIメールの送信

KPIメールとは、橋本の記事でもご紹介した通りサービスのコンディションを測るための重要指標を集計し、1日に2回全社員向けに送信しているメールのことです。 GASを使ってBigQueryやFirebase、SpreadSheetなどから各KPIを集計してそれをスプレッドシートとメールに出力しています。

リリース履歴の保存

監査のためにリリース履歴を保存する仕組みにも利用しています。 弊社ではreleaseというブランチを切っており、このブランチにマージされたものが本番にリリースされます。 リリース時はメインブランチからrelaseブランチ向けのプルリクを作ります。 このプルリクがマージされたときに、GitHubのWebhookでGASへリクエストを飛ばして、それを受け取ってリリース履歴保存用のスプレッドシートに書き出しています。

CircleCIのモニタリング

弊社ではCIツールとしてCircleCIを利用しているのですが、度々「CI遅くない?」「遅いよね」「うん遅い」みたいな会話が発生していました。 なので、本当に実行時間が伸びているのかを検証するためにGASでCircleCIのAPIを使って各ジョブの実行時間を取得し、スプレッドシートに書き出すことで平均実行時間をモニタリングできる仕組みを構築しました。

GASの導入理由

弊社ではデータ分析基盤にBigQueryやFirebase、SpreadSheetなどのGoogle系のサービスを利用しています。KPIメール配信システムの開発の際にそれらのサービスと連携がしやすかったためGASを導入しました。

GASのデメリット

ただ、GASを使い始め以下のような課題もありました。

  • Webエディタでの開発がしづらい
  • コードレビューがしづらい
  • 問題があったときに元の状態に戻しづらい

これらを解決するために、弊社では clasp というツールを導入しました。

clasp の導入

claspとは?

clasp は Google謹製の、GASをローカルで開発出来るようにするためのツールです。 pull コマンドでリモートのGASのコードをローカルに落としたり、push コマンドで簡単にアップロードが出来ます。 また、弊社ではいまのところ利用していませんが、TypeScript や ES6 も使えるみたいです。

弊社では、clasp でローカルに落としたコードを GitHub にプッシュすることでGASのバージョン管理をしています。

claspの使い方

最後にclaspの使い方を簡単にご紹介したいと思います。

1. claspのインストール

clasp は npm を使ってインストールします。

# claspをインストール
> npm i @google/clasp -g

2. clasp で Google アカウントにログイン

以下のコマンドでお使いのGoogleアカウントにログインします。

> clasp login

ブラウザが起動するので、シート作成時のアカウントを選択してclaspからのアクセスを許可してください。

3. Googleスプレッドシートの新しいシートを作成する

弊社では、GASとSpreadSheetを紐つけて管理しているので、SpreadSheetのスクリプトエディタからGASを起動していきます。

4. スプレッドシートのツールメニューから「スクリプトエディタ」を起動

スクリプトエディタを起動

GASのWebエディタが開きます。

5. GASのプロジェクト名を設定

適当な名前をつけてください。

GASのプロジェクト名を設定

6. 試しにスクリプトを実行してみる

↓のように関数を実装して、▶ を押して実行してみてください。

スクリプトを実行

表示メニューの「ログ」、もしくは Cmd + Enter で実行結果を確認できます。

実行結果

7. プロジェクトのプロパティのスクリプトIDをコピー

GASのスクリプトエディタの「ファイル」メニューの「プロジェクトのプロパティ」の、スクリプトIDをコピーしてください。

スクリプトIDをコピー

8. ローカルに適当な名前のディレクトリを作成

> mkdir clasp_sample
> cd clasp_sample

9. clasp clone でローカルにGASのコードをClone

> clasp clone 【手順7でコピーしたID】
Warning: files in subfolder are not accounted for unless you set a '.claspignore' file.
Cloned 2 files.
└─ appsscript.json
└─ コード.js
Not ignored files:
└─ appsscript.json
└─ コード.js

Ignored files:
└─ .clasp.json
> ls
appsscript.json コード.js

10. ローカルでファイルを編集してPush

function myFunction() {
  Logger.log("ぼくジモティー");
}

編集してプッシュします。

> clasp push
\ Pushing files...Push failed. Errors:
{ Error: User has not enabled the Apps Script API. Enable it by visiting https://script.google.com/home/usersettings then retry. If you enabled this API recently, wait a few minutes for the action to propagate to our systems and retry.

エラーがでます。

11. エラーメッセージにあるURLにアクセスして Google App Script API をONにする


12. もう一回 Push

> clasp push
└─ appsscript.json
└─ コード.js
Pushed 2 files.

今度は成功しました。 もう一度スクリプトエディタを確認すると。

プッシュ後のスクリプトエディタ

無事ローカルの修正がリモートに反映されています。

13. GitHubなどにPushしてバージョン管理

弊社ではGitHubを使ってGASのバージョン管理をしているので、最後に忘れず変更をPushします。

終わりに

最後まで読んでいただきありがとうございました。 本記事の内容が少しでもお役に立てば幸いです。

また、弊社ではジモティーを更に成長させる為に一緒に取り組んでいただけるエンジニアを募集しています。 もし少しでも気になればお気軽にご応募ください。


弊社では一緒にプロダクトを改善していただける仲間を探しています!

こちらでお気軽にお声がけください!

ネイティブアプリエンジニアの採用って難しいですよね。。。

ジモティーのウェブチームについてお話ししたいです

弊社iOSアプリにアーキテクチャを導入してみた

はじめに

サーバサイドチームに所属している丁(てい)です。

2016年の入社当初から今年の初めまで約3年半iOSチームに所属しながらネイティブアプリ開発をしていたので、今日はその中で弊社のiOSアプリにアーキテクチャを適用した過程などを紹介できればと思います。

当時のことを思い出しながらさまざまな紆余曲折などをしみじみと回顧しつつご紹介していく文章になるとは思いますが、お付き合いいただけますと幸いです。

ちなみにこちらの記事弊社Androidアプリにアーキテクチャを適用した内容が端的かつ整理された状態で紹介されているので、「お前のだらだら書く文章に付き合ってられねえわ」と言う方はこちらの記事をお勧めいたします。

当初の状況

私が入社した2016年5月時点での弊社iOSアプリは大体以下のような状況でした。

  • ほぼ全てのソースコードがObjective-Cで書かれている。
  • 初期開発時にWebViewで提供していた画面をネイティブに置き換えている最中。
  • ネイティブに置き換えた物はvoidメソッドが無数に存在しながら副作用しまくってるFatViewController状態。
  • 自動テスト?何それ美味しいの??

このような状況で当時流行りだったMVVM(+RxSwift)をObjective-Cでネイティブに置き換えたトップ画面に対してSwiftでリプレースしつつMVVM(+RxSwift)を試験的に採用してみようと言うことになりました。

ここで今振り返ると悪手だったのは当時のリプレース実装担当者も含めて全てのチームメンバーがアーキテクチャ(MVVM)に対しての知見があまりないまま採用して導入しまったことにより、コードメンテナンスが非常に困難な複雑で巨大なViewControllerが誕生してしまったことです。

「目玉焼きが焼ける」

上記のような状況でしたがチームメンバーの血と汗が滲むような量的努力により、なんとかサービスを提供するアプリとしてのギリギリの体裁は保っていました。(と思いたい)

そんな折、弊社iOSアプリのAppStoreレビューにこのような文章が書かれるようになります。

・アプリ使ってるとiPhoneがめっちゃ熱くなるんだけど・・・
・使ってる最中にすぐ落ちるからイライラする。
・iPhone熱くなりすぎ。目玉焼き焼けるんじゃね?w

実際に我々メンバーもリリース前テストなどでアプリを使ってるとiPhoneが発熱し使えば使うほど重くなり、最終的にはクラッシュすると言う事象を確認してましたが、ソースコードが整理されていないためどこに原因があるかを探ることすら困難な状況でした。

結構な発熱になるので「携帯カイロ機能搭載と言う名目で売り出せばいいんじゃね?」って言うあまり笑えない冗談を言いながらも、この課題に真っ向から向き合うことが出来ずに悶々とする日々を送っていました。

Androidへのアーキテクチャ導入

弊社はiOSと共にAndroidアプリをリリースしているのですが、先に触れたようにAndroidアプリにもいくつかの課題があり、それを解決するために外部から技術顧問の方を招聘して知識を借りながら当時チームリーダーだったW氏を中心にソースコードの整理とアーキテクチャの導入が先行して進んでました。

iOSアプリはというと引き続き前述の状況が続き、アプリの発熱、クラッシュの頻発などに加えソースコードの複雑性によりリリースのたびにバグが発生して対応に追われるという辛い状況が続いてました。

そこで弊社の偉い人とW氏とK氏と私で課題解決に関する議論を行った末、Androidに導入した知見を元にiOSにもアーキテクチャを導入してまずソースコードの見通しをよくしつつその過程で発熱、クラッシュの原因を炙り出して潰していくという方針が定まりました。

採用アーキテクチャの選定

iOSにアーキテクチャを導入するという方針が定まったことでようやくこの課題に対して一歩前に進める準備は出来ましたが、当時のiOSチームメンバーは正直「アーキテクチャ??何それ美味いの??」状態でした。

多少表現は大袈裟ですが、当たらずとも遠からずの状況でしたのでまずAndroidチーム所属だったW氏をiOSチームにコンバートして先陣を切ってもらいつつ、横で私がその知見を盗んで蓄積して他のチームメンバーに展開していくという作戦を立てることにしました。

まず最初に行ったのは「アーキテクチャ??何それ美味いの??」状態からの脱却のため、代表的なアーキテクチャをメンバーそれぞれが少し学習してからその内容を持ち寄って発表して、その中から弊社のアプリにマッチするものを選ぼうというものでした。

結論から言うとこの作業は無駄でした。 そもそもアーキテクチャに対する造詣が浅いので、多少MVVMやVIPERの記事を読んだところでその本質を理解できるわけがありません。

N氏と私でMVVMやVIPERに関する浅い知識を持ち寄って発表会を行いましたが全員いまいちピンと来なかったため、すでにAndroidアプリへの導入経験があるW氏の中にある程度知見のたまっているMVP(GUIアーキテクチャ)+CleanArchitecture(システムアーキテクチャ)を横展開で採用するということになりました。

※当初W氏が我々に説明するために書いた概要図がこちら。

MVP+CleanArchitecture構想図

終わりに

この記事を書いている2020年10月時点の弊社iOSアプリはほぼほぼSwiftでリプレースされており、まだまだ完璧な形とは言い難く課題はたくさんあるものの、ある程度ルール化されたアーキテクチャに則ってチームメンバー間で統一された思想のもとで開発が進められているせいで、残念なことに極端な発熱による目玉焼き機能は現在失われております。

今回はアーキテクチャの導入に至った経緯などをだらだらと書いただけの記事になってしまいましたが、次回以降はできるだけハマったポイントなどや失敗とそれに対する解決策などを語ることで、あわよくば同じような悩みを抱えている人の一助になれれば良いなという思いを頭の片隅に置きながらだらだらと書いていこうと思います。

現在の状況に至るまでの過程をこれから何回かに分けてゆるく書いて行くつもりです。 次回は導入計画立案や導入当初にあった出来事などを書いてみようと思います。


弊社では一緒にプロダクトを改善していただける仲間を探しています!

こちらでお気軽にお声がけください!

ネイティブアプリエンジニアの採用って難しいですよね。。。

ジモティーのウェブチームについてお話ししたいです

Androidプロダクトのアーキテクチャの過去未来

はじめに

 ジモティーでAndroidアプリを開発している杉田です。

 この記事では、ジモティーのAndroidアプリのアーキテクチャの変遷とそれを通してチームが成長していった話を書きます。そして最後に現状とこれからの方向性に触れます。

アーキテクチャとは

 プログラムの設計思想のことです。プログラムを大きな責務の単位ごとにコンポーネントで区切ります。そうすることでどこに何を書いてあるかがわかるので、チームに参加したばかりの開発者でも開発がしやすくなります。

アーキテクチャ導入前の課題

 私が入社した2017年当時は、Androidチームには共通認識としてのアーキテクチャがありませんでした。それどころかほとんど全てのコード(ビジネスロジック、通信処理、DB周りの処理)はActivityに書かれていました。そのため以下のような課題がありました。

  1. ビジネスロジックが共通化されず、色々なActivityで重複して記述されている
    • ビジネスロジックに変更が入ると、同じロジックが入っている全ての画面に変更を加える必要がある
    • 重複したロジックの変更が漏れて、バグが発生する
  2. 複数の役割を持った超巨大なメソッドが存在し、その変更によって思わぬバグが発生する
    • 例えば、巨大メソッドの最初に出てくる変数の値を変更したときに、その変数をメソッドの下の方でも参照していてバグが発生する
  3. Activityに色々な処理が書かれているため同時に同じActivityの改修作業をするとコンフリクトやデグレが発生する

 上記の様な状態なので、チームとしてもバグが頻発する状態ですし、3年前に新人として入社した私はバグをしょっちゅう出していました。  

 リリースの度にリグレッションテストにかなりの工数を取られ、リリース当日はビクビクしながらリリース結果を見守っていました。リリースの度に心理的な疲労が溜まっていく状態でした。

 この状態にチーム全体の危機意識が高まり、どこに何を書けば良いのか明確なルールを作ろうという動きが始まりました。

アーキテクチャ導入に向けて格闘する日々

 まず最初に始めたのは、Activityに集中している処理を分離する方法を模索するところからでした。

 処理の分離の単位として下記の候補が上がりました。 - GUIロジック - ビジネスロジック - 通信処理やDB周りの処理 

 この処理の単位で分離する方法として、GUIアーキテクチャとシステムアーキテクチャを導入したら良いのではないかという案が出て、話し合われました。

・GUIアーキテクチャ・・・主にUIにあるロジックとシステム関連のロジックを分離するためのアーキテクチャ
・システムアーキテクチャ・・・通信処理やDB周りのロジックを分離するためのアーキテクチャ

 そこでGUIアーキテクチャとシステムアーキテクチャの組み合わせで注目されたのは、以下の組み合わせです。

  1. MVP + Clean Architecture
  2. MVVM + Clean Architecture
  MVP MVVM
メリット ・Viewと表示のためのロジックの分離がシンプルにかける ・よりモダンなアーキテクチャである。
・データバインディングと組み合わせることで、Viewに値をセットするコードを省略できるので、より少ないコード量で実装できる
デメリット ・Viewに値をセットするためのコードが必要なため、記述量が多くなる ・実装が複雑になる。検討当時、Activityのライフサイクルと非同期処理のライフサイクルが合わない時にケアするコードが大量に必要

 当時チームで議論した結果、MVVMの方がActivityのライフサイクルと非同期処理のライフサイクルが合わない時にケアするコードが大量に必要で、ライフサイクルをうまく合わせるための知見がなかった私たちには難しそうだということでMVPを採用することになりました。

 また、システムアーキテクチャとしてClean Architectureが流行していたのと、当時アーキテクチャ導入のために参画してくれた技術顧問の方がMVP + Clean Architectureの導入経験があり、MVPの方がチームに導入しやすいということでClean Architectureを採用することになりました。

▼導入後のアーキテクチャはこんな感じになりました。

 アーキテクチャの導入にあたって、全員に知見が行き渡る様に、全員が順番に工数をとって導入を行っていきました。ただし、全員が実行するにしてもメンバー間でプロダクト仕様の理解に差があるため、下記の様な分担の方針を建てました。

仕様の理解が深い人 新人などの仕様理解が浅い人
・ビジネス上重要な主要導線の画面
・ビジネスロジックが複雑な画面
・開発規模が小さく、ビジネスロジックが少ない画面

 最初はチームリーダーが一つの画面にアーキテクチャを導入し、その知見をwikiにまとめてチームに共有しました。その後各メンバーがアーキテクチャ導入する際には、クラス図を書いて設計レビューを行ってから実装を行いました。その結果、実装前からアーキテクチャに対してある程度の理解を持つことができたので、各自のアーキテクチャ導入がスムーズに進みました。

アーキテクチャ導入の成果

 ここからはアーキテクチャを導入した結果を説明します。

アプリの品質が向上

 まず、メンバー間の技術レベルを問わず、ある程度の品質を担保できるようになりました。新人だった私もアーキテクチャの概念をキャッチアップすることで、何が良いコードで何が悪いコードなのか、それがなぜなのかを理解する助けになりました。また思想が同じなので、私が書いたコードと先輩が書いたコードの品質に大きな乖離はなくなり、レビューでも先輩に対して指摘しやすくなりました。  

 次に、デグレによる障害が劇的に減少しました。これはプログラムをコンポーネント化することで、同じ画面を開発した時にコンフリクトを起こすことがほとんどなくなったためでした。  

iOSにも同じアーキテクチャが適用できた

 さらに、もう一つ想定外の成果がありました。それはプラットフォームに依存しないアーキテクチャを採用したので、iOSにも同じMVP + Clean Architectureを導入できたことでした。その結果、iOSチームとAndroidチーム間でメンバーのコンバートを行う時のスイッチングコストが激減しました。  

 その結果、iOSとAndroid両方を開発できる人が増えたので、プロジェクト実行時の人材配置が柔軟になりました。また、iOSチームとAndroidチーム間の意見交換が活発になり、アプリチーム全体でプロダクトをよくしていこうという思考になりました。

アーキテクチャのこれから

次の課題

 Androidは最終的に、主要導線の画面だけにアーキテクチャが導入されました。まだアーキテクチャが導入されていない画面も残っています。

 アーキテクチャが導入された画面にも問題がないわけではありません。緊急案件などがあり、設計せずに継ぎ足したコードがあるせいでアーキテクチャが崩れてしまっている部分もあります。設計されている画面でも、MVPを採用したことが原因で、ボイラープレートコード(お約束で重複して書かなければならないコード)が量産されていて、そのせいでコードベースが多少膨らんで生産性が良いとは言えない面もあります。  

 また、アーキテクチャ導入時に試行錯誤をしたコードも残っています。MVP + Clean Architectureの後に一時MVVM + Clean Architectureを目指した時期があったため、MVPの代わりにMVVMを導入した画面も存在しています。

 さらに、例えば、MVP導入時に非同期通信ライブラリにRxJavaを採用しましたが、サードパーティライブラリは今後メンテナンスされ続けていく保証がありません。そのため、なるべくAndroidプラットフォーム標準のライブラリを活用したいところです。

 つまり、プロダクト全体としてみるとアーキテクチャがバラバラになっていて、MVPが適用されている画面に関してはボイラープレートが多いため生産性が上がりきっておらず、中には歴史的経緯でアーキテクチャ化した後にコードの設計が崩れてしまっている部分があるのです。

これからの方針

 これからの方針ですが、Androidチームは崩れてしまったコードベースを改善するためにMVVM + Clean Architectureに切り替えていく検討を始めています。

 2017年にアーキテクチャを導入した時に比べると、Androidプラットフォームは大きく進化しました。当時は、MVVMへのAndroidプラットフォームのサポートがなく、技術的に導入が難しかったのですが、現在はAAC(Android Architecture Components)を導入することで比較的簡単にMVVMを導入可能になっています。

 MVPからMVVMにアーキテクチャを切り替え、全画面にMVVMを適用することによって、下記の課題が解決することが見込まれます。

  • アーキテクチャの混在によって、コードの見通しが悪くなる
  • MVPを採用していることによる大量のボイラープレート
  • Androidプラットフォーム標準のライブラリを活用できていない

アーキテクチャの混在によって、コードの見通しが悪くなる

 これは説明しなくてもご理解いただけると思いますが、MVVMにアーキテクチャが統一されるのでコードの見通しは良くなります。

MVPを採用していることによる大量のボイラープレート

 MVPを採用したため、Viewへの値の設定や通信処理とActivityのライフサイクルの調整の処理が大量にボイラープレートとして書かれています。  

 AACを導入することで上記の様なボイラープレートをかなり削減できることが見込まれます。

Androidプラットフォーム標準のライブラリを活用できていない

 非同期処理にRxJavaを使っていましたが、AACとKotlinのコルーチンを組み合わせることで代替できるなど、2017年当時にサードパーティーライブラリに頼らざるを得なかった機能についても標準ライブラリでかなり補える様になっています。

その他

 MVVMの導入時に、先に述べた継ぎ足されたコードの設計も見直し、より開発がしやすいコードに生まれ変わらせていく予定です。

最後に:みなさん待ってます!

 上記の様な方針で進めて、開発生産性をあげ、サービスの成長にガンガン貢献できるチームになっていきます!

 Androidチームでは、この今後の改善に一緒に取り組んでくれる方を募集しています!

 一緒に頑張りましょう!!


弊社では一緒にプロダクトを改善していただける仲間を探しています!

こちらでお気軽にお声がけください!

ネイティブアプリエンジニアの採用って難しいですよね。。。

ジモティーのウェブチームについてお話ししたいです

ジモティーのデータ活用フローのご紹介

ジモティーiOSチーム所属のエンジニアの橋本です。

普段はiOSアプリの開発に従事していますが、 Webやネイティブアプリ(iOS/Android)の各種計測データの収集や社内への展開などの業務にも従事しています。

今回は、自分が担当しているデーター活用周りでの取り組みのご紹介をしたいと思います。

組織のコンディション判断と意思決定に利用されるデータ

Webアプリやネイティブアプリ(iOS/Android)が生み出す様々なデータは

収集・蓄積・加工  ▶  分析・活用

という過程を経るわけですが、その利用目的は大きくは2つです。

  • ジモティーというサービスの現状がどうなのか、 サービスのコンディションの善し悪しを判断するための利用
  • ユーザーの利用状況の傾向を把握し、次の打ち手を決める判断材料としての利用

この利用目的を達成するためにデータをどのように利用者に届けるかが重要となります。

エンジニアだけが利用するのであれば,BigQueryなどSQLが実行できる基盤・インターフェース(以下,SQL I/F)を用意しておけば事足ります。

しかし,ビジネスチーム(ビジネス企画,マーケティングなどを担当)やカスタマーサポートチーム(ユーザからの問い合わせ対応、ジモティーの運用ルール策定を担当)にSQL I/Fだけを提供すると、簡単なデータ取得・分析であればよいのですが,ある程度複雑なものになってくるとエンジニアがサポートする必要があります。

SQL作成で手間がかかっていると,ビジネスの意思決定のスピードが遅くなりますので,ある程度複雑なSQLが必要でKPIなど重要指標に関わる数値など,サービスのコンディションを確認するために必須な要素については自動的に取得してデータや分析結果を自動で必要な社員に送付する仕組みも整えています。

次の節からジモティーのデータ分析基盤の構成要素とデータがどのようにエンドユーザー(データ利用者)まで流れていくか図解しながら説明したいと思います。

データ基盤の大きな構造と利用フロー

データ基盤とSQL I/Fについては弊社のSREチームが2016年の段階で整備を進めており,その上でSQLの社内教育もあわせて進め,ビジネスチームメンバーの大半がSQLを書けるまでになりました。

Redash + Redshift + embulkでデータ分析基盤を構築した話

しかし,ある程度基盤整備とSQLの教育はできたものの,時代の変遷と共に例えば,ネイティブアプリのデータ収集・分析に使用していたGoogle Analyticsが廃止になりFirebaseに移行したり、 SQLの実行スピードやコスト面のメリットでRedshiftからBigQueryに移行するなどサービス規模の拡大と技術の進化にあわせてかなり構成が変更されています。

現在は,データ分析基盤はBigQuery中心の構成になっており、各種データは一度BigQueryに集約してからSQL I/F,Google Spreadsheetやメールを含めフロントエンドに(分析)データが供給されています。

下記にデータフローの観点での見取り図を示し,以降は図中に示した番号毎に説明していきます。

図1. 分析用データのフロー観点での見取り図 

分析用データのフロー観点での見取り図
分析用データのフロー観点での見取り図

[1]BigQuery

以前は、データ分析基盤の主軸としてAmazon Redshiftを利用していたのはこの記事の通りで、下記のメリットをかなり享受できました。

  • AWSで構築している既存サービスとの親和性
  • クエリに依存せず固定額(SQLに不慣れなメンバーも実行する前提なので)
  • データ量がまだそれほど多くないので安価に始められる

しかし、分析の幅を広げるため、分析対象をRailsのアクセスログまで拡張したい要望があり、大幅に分析するデータ量が増加しました。それに合わせて、改めてSQLの実行スピードといったパフォーマンス面とコストメリットの部分で検討をして、2017年にBigQueryの利用を開始しました。

当初はRedshiftとBigQueryを併用していたのですが、SQLの利用面で両方で持っているデータで JOIN(テーブルの結合)ができないデメリットがあり、2018年にBigQueryにすべてのデータが集約されました。

また、結果的にではありますがネイティブアプリのデータ収集・分析に利用するFirebaseやGoogle Spreadsheet、 Google Apps Scriptとの親和性が高かった(API連携が非常にやりやすかった)点もBigQueryに移行して良かった点です。

[2]データ転送はembulkとfluentd

データ転送にはOSS の データ転送ソフトウェアの emublk と fluentd を使用しています。 個々のRuby on Railsが動作するサーバー から出力されるアクセスログは fluentd によってAmazon S3に転送されて集約されます。

mongodbやAWS Aurora、 Amazon S3上のアクセスログ から BigQuery の転送には 現状は基本的に日次でデータを転送しています(アクセスログについては1時間毎に転送しています)。

embulkとfluentdのメリットは、 プラグイン機構によって様々なデータベース/データストレージ/ファイルフォーマットに対応でき、結果、連携先が増えても比較的新しく覚えることが少なく、対応にスピード感が出る点だと思います。

[3]FirebaseとBigQueryを連携

Firebase の Bigquery Exportを使用して、iOS / Android のネイティブアプリのイベントデータを蓄積しているFirebaseとBigQueryを連携させています。 BigQueryの転送先のスキーマ定義もカチッと決まっているので、慣れるとSQLも素早く書けるようになります。 このFirebaseのデータとRailsのアクセスログを組み合わせて分析することも多いです。

[4]Redash

弊社のSQL I/Fの要となるツールで、SQLの実行はもちろんのこと、下記のようなことも可能です。

  • ビジュアライズ : クエリの結果から円グラフ、折れ線グラフ、散布図などを描画
  • スケジューリング : 保存したクエリにスケジュールを設定し定時実行できる

Redashについては、当初、不具合も散見されましたが、弊社SREチームの吉田が、Redash コミュニティーに不具合の修正パッチをちょくちょく送っていました。そうしたら、いつの間にか吉田がRedashのコミッターになっていて、吉田と昼休みにご飯を一緒に食べているときに聞いてびっくりした覚えがあります。

弊社のRedashに保存されているSQLの量は年々着実に増えており、Redashと共に弊社のデータ分析文化も成長していっている感じがします。

[5]Google Apps Script /[6] Google Spreadsheet /[7] Gmail

前述の通り、 ビジネスチームに対するSQL I/Fの提供だけでは、

  • 複雑なSQLが必要な場合にエンジニアがサポートが必要な点
  • ある程度複雑なSQLが必要で定期的に確認する必要があるKPIなど重要指標に関わる数値が存在し、SQL I/F だけではその確認が困難な点

といったことに対応できないので、重要なデータや分析結果を自動でGoogleSpreadsheetに反映したり、メールで必要な社員に送付する仕組みも整えています。

[5]Google Apps Script

Google Apps Scriptを利用して、BigQuery・Google Spreadsheet・GmailとAPI連携してBigQueryからデータを取り出し、必要なデータをGoogle Spreadsheetとメールに出力しています。

Google Apps Script についてはGoogle Spreadsheetと紐付けて管理しており、claspというツールを利用してローカルの開発環境のスクリプトとGoogle Spreadsheet上のスクリプトを同期させます。これにより、ローカル開発環境のスクリプトをGithubで管理できるようにしています。

[6] Google Spreadsheet

ビジネスチームのメンバーはSQL自体はRedash上で開発して、定期的に確認しなければならない分析データ(例えば、サービス上の新しい施策の状況確認等)はRedash上ではなく、Google Apps Scriptを定期実行して日次で自動的にBigQueryからデータを取得してGoogle Spreadsheetに出力して確認します。

ビジネスチームがGoogle Apps Scriptを書くわけではなく、Google Spreadsheet上に用意されたSQLタブにSQLと反映するタブ名、取得開始日次、取得終了日時を記載すると自動的に定期実行して、結果を反映するフレームワーク(Google Spreadsheet+Google Apps Script のテンプレート)をエンジニア側が開発して、それをビジネスチームに使ってもらっています。

これらの仕組みのおかげで、ビジネスチームが自分たちの要求に応じてスピーディーにデータの分析と定期的な確認ができるようになりました。 以前のBlogに記載されていた下記のような状況は遠い過去のものとなりました!

これまでは、

「ディレクターがデータ抽出を依頼→エンジニアが指定されたデータをCSVに出力して渡す」

という形になっていました。

データの抽出の依頼が週に5、6件発生し、1件毎にエンジニアの工数が数時間取られるという状況です

[7] Gmail

弊社では、KPIメールというジモティーのサービスのコンディションを診断するためにWebアプリとネイティブアプリ(Android/iOS)の厳選された指標データを当日から30日前までの分をリスト化して毎日2回、社員に送信するようにしています。

このKPIメールにより、直近のサービスの状況を全社員が毎日、日次の変化を含めて確認することができます。何か指標に異常があった時に原因を含めて同じ数字でチーム内で議論できますので、社内での数値に対する関心を高める効果と共にコミュニケーションツールとしても非常に役立っています。

[8]Google Analytics

Webアプリに関するデータ分析に関しては、Google Analyticsを長年利用し続けています。

今後の展望

以前と比較してかなり、データ分析基盤は整ってきましたが、先進的な企業と比較するとまだまだやることがたくさんあります。

SQLを使った分析を更に容易にする

全社的にSQLの活用がかなり進んできていますが、複雑なSQLを組むときはエンジニアのサポートが必要な状況です。これは、アクセスログやFirebaseのイベントデータなど複雑なテーブルに対してそのままSQLを組むとどうしてもSQLが複雑になるのが原因です。

そのため、一度複雑なデータに関しては分析しやすい中間テーブルに分解して、自動的に中間テーブルにデータを流入させるデータフローを構築する手段が考えられます。

中間テーブルの設計がかなり難易度が高そうですが、実現できればSQLを書くのがかなり容易になると期待されます。

ストリーム(即時)データの分析

現時点のデータ分析基盤では一番鮮度が高いデータでも1時間前のデータです。 現在のジモティーのサービスではそこまで即時性の高いデータは必要ないのですが、エンジニア側が障害や異常があった時に即時性の高いデータが必要になります。 各種、開発者向けのモニタリングツールでも原因の推定はやれなくもないのですが、アクセスログなどのデータがすぐに参照できた方が障害・異常の原因特定はかなり容易になります。

かつてその目的で kibana を導入したことがあったのですが、サービス規模が巨大になりログ転送含めて運用がかなり大変になったため、使用をやめています。 こちらも kibana の代わりにうまく大量データを非同期で投入できるようなストリームデータ分析基盤の導入が待たれます。

現状、案として出ているのが flutnd + BigQuery streaming insert なのでこちらを試すところから始めたいですね。

データの可視化、ビジネスダッシュボード

現在データを可視化するのにGoogle SpreadsheetやRedashのグラフツールを利用していますが、 ストリームデータを扱えるようになったらKPIの状況などをリアルタイムに可視化できる基盤の導入なども行えるとよいと思っています。 まずは手始めにGoogle Data Studioの活用などが考えられるでしょう。

終わりに

というわけで、ジモティーは絶賛エンジニア募集中です。 データ基盤の整備をやってみたいという思われた方、課題は本当にたくさんあります!ぜひ一緒に取り組んでいきましょう。 よろしくお願いします!


弊社では一緒にプロダクトを改善していただける仲間を探しています!

こちらでお気軽にお声がけください!

ネイティブアプリエンジニアの採用って難しいですよね。。。

ジモティーのウェブチームについてお話ししたいです

ISUCON 10 予選参加も1139点で見事敗退 また来年

こんにちは、ジモティーエンジニアの吉田です。

普段はジモティーのバックエンド (Rails) とインフラを担当しています。2020/9/12 (土) に行われた ISUCON 10 の予選に参加したので、その内容をご紹介します。

ISUCON とは

ISUCON とは、お題となるWebサービスを決められたレギュレーションの中でチューニングして、そのスコアを競い合う大会です。

いい感じにスピードアップコンテスト、略して ISUCON (Iikanjini Speed Up Contest) のゆるい名前とは裏腹に、今年で10回目、参加チーム数は490組(1チーム最大3名)と国内でも比較的規模の大きいイベントです。ウェブ・インフラエンジニアであれば参加した事がなくても、一度は耳にした事のある方が多いと思います。

予選を勝ち抜いた上位30チームが本戦に出場し、優勝賞金はなんと100万円です。賞金額も大きいですが、業界でも有名なエンジニアがたくさん参加していて、その中で本選に出場できる事はスキルの高いエンジニアとしての証明にもなります。

チームメンバーとモチベーション

チームのメンバーは、まず、暑い季節もスカッと爽やかなギャグを飛ばす H さん、以前インフラとバックエンド、今は iOS を担当しています。次に、ビジネスチームとのブリッジングや仕様策定にとても強く、以前は主にバックエンド、最近は Android も担当している S さん、それに自分の3名です。

ジモティーではこのように複数の担当領域を兼務しているメンバーが多いのが特徴の1つです。

他の2人は初参加ですが、自分は過去に ISUCON 8 に参加した事があります。その時は再起動試験で失敗したのか、スコア0の残念な結果に終わってしまいました。

毎年この時期にタイムライン上で ISUCON が話題になると過去の記憶が蘇り、「あの時こうしておけば」「今の自分だったらどこまでいけるのか」といった思いに馳せる大会経験者は多いのではないでしょうか。ISUCON 9 はタイミングが悪く不参加に終わりましたが、技術研鑽よりもこういった思いが強く、参加を決めてすぐにメンバーを募集しました。

当日の朝から終了まで

前回参加時の失敗を踏まえて、事前に打ち合わせをして、担当や実施内容のおおまかな確認を行いました。プログラミング言語は複数から選択することができ、ジモティーでもバックエンドのメインで使用している Ruby を選択しました。

12:20に開始し、まずは準備していたプライベートリポジトリにアプリのソースコードや設定ファイルを登録して、初回のベンチマーカが無事成功する事を確認しました(スコア: 410)。

その後、インフラ周りのタスクとして alp のインストールや Nginx のログフォーマットを変更したり、Mackerel エージェントをインストールなど実施しました。alp で確認したところ、検索のエンドポイントが圧倒的に遅いのでここをなんとかしたいよね、と当たりをつけます。

ベンチマーカの挙動を確認した後、インデックスの追加でクエリの実行速度を改善できないか検討しますが、すぐに手を付けるのが難しそうだなと話しました。自分は Nginx の設定を変更してみたのですが、ベンチマーカとポータルが不安定で、変更の影響が不明なため、アプリ側をじっくり確認する方針に切り替えました。

16:00頃get '/api/estate/low_priced'get '/api/chair/low_priced' の結果を常に DB から検索していましたが、キャッシュ化できそうだったので Memcached をインストールし dalli 経由でキャッシュしました(スコア: 543)。また、get '/api/recommended_estate/:id' の SQL クエリを改善し、以後参照のない変数で mapmap! に置き換えました(スコア: 558)。

その後、DB をインメモリ化しようとしてしばらく色々とやってみたのですが、ベンチマーカがことごとく失敗し作戦は断念しました。細かい改善をしながら、スコアが徐々にしか上がらず苦しい時間が続きます。

17:17頃、突如メインのサーバに SSH で接続できなくなり、Mackerel からも接続不能のアラートが飛んできたため、運営にマシンの再起動をお願いして事なきを得ました。この間ベンチマーカを実行できない事から、2台目と3台目にも同じ環境を構築し、打ち手を並列して試せるようにしました。事前の作戦でローカルには実行環境を構築しない方針にしていたので、これはもっと早い時間に実施しておくと良かったと思います。

18:21頃、ユニットファイルに指定されていた unicorn の実行環境を production に指定したところ、若干スコアがあがりました(スコア: 625)。

18:40頃、MySQL のクエリキャッシュと InnoDB の設定を変更して、さらにスコアが伸びました(スコア: 928)。

18:50頃、どうしても DB の負荷が高いので、それまでアプリケーションと同じサーバで起動させていた DB を別サーバにする方針に舵を切ります。ところが設定ミスに気づかず、結局最後まで実現する事が出来ませんでした。estate と chair テーブルもそれぞれ別サーバに分けられそうだよね、と話していましたが、実現にはいたらず、これが一番の失敗だったと思います。

その後も細かい改善を実施しながら、19:00頃に、Nginx で一部の User-Agent のアクセスを一律 503 で返し、ベンチマーカを何度か実行するとスコアがかなり上がりました(スコア: 1066)。

終盤に chair と estate の検索をキャッシュ化しようと粘っていましたが、こちらはベンチマークが失敗し、残念ながら実現には至りませんでした。

ラスト20分はあわてないように予定通り変更をフリーズし、再起動後の動作確認を行って終了を迎えました。

振り返り

メンバーそれぞれができることを模索しながら、スコアアップに向けて手を動かす時間は何事にも代えがたい体験でした。また、仮説を立てて検証し、失敗を繰り返しながら正解した時にはそれまでの苦労がすべて吹き飛んでしまうほどの高揚感は、まさにエンジニアリングの醍醐味でもあります。

しかし、冷静にふりかえってみるといくつものミスや改善すべき点があったのも事実です。クエリの最適化にほとんど手を入れられなかった点、ボトルネックが DB の処理にある事に気づきながら別のサーバに切り出せなかったのは大きな失敗でした。

最後に

後日発表されたスコア(参考値)を確認すると予選突破ラインが 2100 付近だったようで、我々の 1139 ではまだまだ及ばない事が客観的に分かりました。来年こそは予選突破できるようにレベルアップをしたいです。また、ISUCON は普段の業務にもつながるとても素晴らしいイベントなので、社内でも複数チームで参加して感想戦をしたいです。

ジモティーではエンジニアを募集中です!一緒に ISUCON の話で盛り上がりましょう!


弊社では一緒にプロダクトを改善していただける仲間を探しています!

こちらでお気軽にお声がけください!

ネイティブアプリエンジニアの採用って難しいですよね。。。

ジモティーのウェブチームについてお話ししたいです