Chatwork Creator's Note

ビジネスチャット「Chatwork」のエンジニアとデザイナーのブログです。

ビジネスチャット「Chatwork」のエンジニアとデザイナーのブログです。

読者になる

Chatwork のプロダクトフェーズと技術選定の流れ

こんにちは!
Chatwork 株式会社のプロダクトマネージャー (PM)、宮下 (@ryugoo_) です。
2013 年にモバイルアプリエンジニアとして入社し、 2014 年に Android 専任になり、 2019 年からは PM に転向してそろそろ 2 年になろうとしています。

さて、今回はエンジニアから PM になった私から見た、 Chatwork の技術選定の流れの変化について話してみようと思います。

f:id:cw-miyashita:20201129204458p:plain
技術選定の歴史

2013 年 - PMF を目指すために

2013 年当時の Chatwork は PMF (Product Market Fit) を目指すフェーズでした。PMF とはユーザーの課題を認識し、想定する人たちに適切な形で製品を届けられている状態を目指すことです。

このフェーズでは PMF を目指すために、課題の発見と検証を素早く繰り返す必要があります。小さな組織がこれをおこなうためにはエンジニアリングパワーを最大化するための技術選定が必要です。

Chatwork の生い立ちは様々なところで語られていますが、技術選定についてはそこまで語られていないのではないでしょうか?

kigyolog.com

PMF を目指すにあたって、プロダクトは可能な限りの検証をおこなう必要があります。そのためにはプロダクトにおけるユーザーが目にする部分、触れられる部分のレイヤーに集中することが大切です。

その結果、マネージドサービスである Amazon Web Services (以下 AWS) の上で、LAMP 構成でアプリを構築 (リアルタイム通信は Google App Engine の Channel API) するというシンプルな構成となりました。

今でこそ AWS の上には多くのサービスがありますが 2013 年当時はまだ数も多くなく、 Chatwork では主に

  • Amazon ELB (ロードバランサー)
  • Amazon EC2 (HTTP, PHP アプリケーションサーバー)
  • Amazon RDB (データベース/MySQL)
  • Amazon S3 (ストレージ)
  • Amazon CloudFront (CDN)

これらを使用していました。非常にシンプルですね。できるだけ多くをマネージドサービスに任せることで、クライアント、サーバー双方のアプリに手を加え続けるためです。

そして、クライアント、サーバー双方のアプリでは、

  • クライアント
    • フロントエンド : JavaScript, jQuery
    • モバイル : Titanium Mobile
  • サーバー
    • PHP 5
    • Python (Google App Engine)

このような構成となっていました。こちらもシンプルですね。特にモバイルアプリはクロスプラットフォームの開発ツールを使い、 iOS / Android アプリのコードをできるだけ共通化していました。

Chatwork ではこうした構成を取ることで、結果として PMF はうまくいきそうだという手応えを感じる一方で、技術的な課題を早くも感じ始めることになりました。

例えばサーバー側ではモノリシックなアプリとなっているがゆえに、 1 箇所でトラブルが起きるとサービスが全断する。ユーザー数が爆発的に増えて、プッシュ通知が分単位で遅延するといったユーザー体験が損なわれる問題が起きはじめました。

2014 〜 2016 年 - 技術的課題を解決するために

2014 〜 2016 年はキャズム理論でいうアーリーアダプターが急激に増えたフェーズでした。それと同時に同じフィールドで戦うプレイヤーが現れては消えていった年でもあります。

2013 年までの技術的課題を解決するために、「新しい言語選定をする」動きが立ち上がりました。もともと Chatwork は、それまでに作られていた PHP 製の社内システムにアドオンする形で構築されていたため、サービスのためのアプリとして最適化されていませんでした。

そこで Scala が採用されることになります。静的型付け言語としての型の明確さや、処理速度やレスポンスタイムといったパフォーマンス面の課題を解決するために、 PHP から別の動的型付け言語に移行する理由が薄かったことがあります (と、いうようなことを選定の場で私が話した覚えがあります) 。

Scala を採用しようと決めたは良いものの「なにを、どうやって、いつまでに」が決まっていませんでした。モノリシックをやめたい、パフォーマンスを上げたいという定性的な課題はあるものの、それらの課題の優先度を決めたり、どれぐらいの性能を目指したいのかとか、どれぐらいのコスト (お金や時間などなど) をかけられるのか?という定量的な課題分析ができていない状態です。そうした中でリニューアルをしようとなったのですから、「全てを作り直そう」となる (なってしまう) のは必然的でした。

結果として、 2015 年にサーバーサイドリニューアルは失敗してしまいます。プロジェクトマネジメントの観点での振り返りもありますが、プロダクトマネジメントの観点で見れば「ユーザーに提供したい価値」の欠如が原因の 1 つだったと思います。

その後、2016 年にはプロジェクトマネジメントの観点で振り返りを行い、まず解決するべき領域 (メッセージ処理) にスコープを絞り、 PoC (Proof of Concept) を行い、現実的な Scala 導入を実現することになりました。

speakerdeck.com

この時点で Scala アプリケーションで導入されたシステムは例えば以下のものがあります。

  • Kubernetes
  • Apache HBase
  • Apache Kafka

Write と Read を分離する CQRS + ES モデルが取られ、指数関数的にメッセージ量が増加していた課題に対応することになります。メッセージ量が増えることで、様々な処理も遅延し、ユーザー体験上の劣化が生まれていたからです。

プロダクトマネジメントからいえば、提供するべきユーザー価値を見つけ出し、そこから技術を使うのが本筋です。ただ、当時の Chatwork にはプロダクトマネジメント文化がなく、技術先行になっていました。

しかし、ユーザーへの悪影響を解消するために、まずは技術的課題のどこを解決しなければならないのかを検討し、 PoC によって仮説検証をおこない、いつまでに提供するかを考えて Scala 導入を実現できたのは大きな一歩だったと感じています。

一方でクライアントサイドはというと、 2014 年からモバイルアプリのネイティブ化が始まりました。 Android アプリは 2014 年末に、 iOS アプリは 2016 年の春にリリースされます。

これだけの差がついた理由は、 Android アプリはサーバーサイドリニューアルを待たずに現行の API を使うことを前提としていて、 iOS アプリはサーバーサイドリニューアル後の API を使うことを前提としていたためです。

リニューアル後の API を使う前提というのが楽観的であるというのはそのとおりだと思いますが、その他にも当時の iOS / Android アプリの状況というのもこうした選択をした理由となっていました。

それぞれのアプリは Titanium Mobile で作られていましたが、その性質上、 iOS アプリは比較的実現したい UI や機能を実現しやすい一方で、Android 版では多くの妥協が必要でした。2014 年の頭に Android アプリのリニューアルをおこなったのですが、その際に実現したかった UI や機能の多くを実現できなかったのです。

リニューアルしてなお iOS アプリとの乖離がある状況であったことや、今後、グローバルで見たときには Android の方がシェアが大きいことから、 Android アプリのネイティブ化を急ぐことになりました。

ちなみに、それぞれのアプリでは、 Objective-C と Java を使うことになります。 2014 年の WWDC で Swift が発表されましたが、言語としての成熟度や初のネイティブ化を進める上での不確定要素を減らすため、当時は使用を差し控えることになりました。

同様に Android 側も Kotlin の使用を 2014 年から検討していましたが、非公式サポートであったことや言語仕様が不安定であったことから、情報のキャッチアップは続けながらも使用はおこなわないという決断をしています。

  • iOS
    • 言語
      • Objective-C
    • 特徴的なライブラリー
      • Parcoa (パーサーコンビネーター)
      • ReactiveCocoa (リアクティブプログラミング)
  • Android
    • 言語
      • Java
    • 特徴的なライブラリー
      • RxJava (リアクティブプログラミング)
      • Retrolambda (Java 8 バックポート)

また、フロントエンドに関してもリニューアルプロジェクトは進んでいましたが、 2016 年に頓挫してしまいます。このときは Angular と RxJS を使った構成で進んでいました。プロジェクトは技術的な要因よりも、プロジェクトマネジメントの観点での理由で停止することになります。

2014 〜 2016 年は PMF を迎えたプロダクトが抱える技術的課題を解決するために試行錯誤する年となりました。その結果、プロジェクトマネジメントや、その前提となるプロダクトマネジメントの課題が浮き彫りになったように感じます。

2017 〜 2020 年 - ユーザー影響を最小化するために

2017 〜 2019 年は同じフィールドで戦うプレイヤーがある程度安定し、各プレイヤーはそれぞれの戦略で面を広げつつ、キャズムを超えてアーリーマジョリティに進むための戦略を考えるフェーズでした。

そして、 2020 年。コロナ禍によってリモートワークが大きく取り沙汰されると、一気にキャズムを超え、アーリーマジョリティ層に突入してしまったと考えています。

アーリーマジョリティ層に突入するとユーザー数が急激に増加します。ユーザーが数が増えれば増えるほど、たとえ起きてしまった問題が小さかったとしても見逃せないほど多くのユーザーに影響を与えることになります。そのため、より適切な技術選定が重要になってくるのです。

Chatwork では 2016 年末に Scala を用いたメッセージングシステム "Falcon" をリリースし、 2017 年になると "プロダクトマネジメント室 (PM 室) " ができます。この PM 室ができてから、現在までの間に技術選定までの流れが変わったように思います。

まず、ユーザー課題ベースで「何が課題なのか (What) 」と「なぜ課題なのか (Why) 」を提示し、それにたいして「どのように解決したいのか (How) 」をまとめた PRD (Product Requirements Document) が作られるようになりました。

この PRD を書くのは PM です。新規機能系プロジェクトに関してはまず PRD を書き、そこからプロジェクトとしてパッケージングされ、達成するべき事柄に最適な技術を選ぶという流れができあがりました。

なお、PRD に書く How は技術的にどのようにするのか?ではなく、どのようなものをユーザーに提供することで課題を解決できるのかを示したもので、プロジェクトのための要件定義書とはまた異なります。

また、新しい機能を提供するためのプロジェクトとは別に、技術的課題を解決するための対応もあります。このようなものも、どのようなユーザー影響があるかを PM と確認しながら進める流れもできています。

さて、クライアントサイドの技術でいうと、2017 年には iOS アプリは Swift を、 Android アプリは Kotlin を、フロントエンドは React の導入をスタートしました。これらの導入も主に技術的課題の解決が主眼に置かれていましたが、導入に際してはユーザー影響が小さなところから始まりました。

例えば React の導入は Chatwork 上部のロゴ部分からスタートしています。

f:id:cw-miyashita:20201129194118p:plain
Chatwork Logo

ユーザー影響が小さな部分から新しい技術を取り入れることで、万が一の悪影響を最小限に抑えつつ、チーム内では新しい技術の理解を深めることができます。

2020 年現在、フロントエンドでは TypeScript, Redux を導入、 Android では Kotlin 移行を 100% 完了していて、 iOS ではコードベース 70% 以上を Swift に置き換えています。

逆にユーザー影響が大きな部分、 Chatwork でいうとチャットのタイムラインがそれにあたりますが、この部分にも新しい技術が導入されています。それが AST (Abstract Syntax Tree) をベースにした Chatwork 記法の解釈共通化と、描画共通化です。

先日の安宅の記事にも詳しい記載がありますが、 Chatwork のメッセージは独自のタグを使った装飾が可能で、 infotask といったタグを定義し、これをクライアントが解釈することでタスクの作成などの見た目を構築しています。

creators-note.chatwork.com

長年、各クライアントで独自の解釈がおこなわれていて、複雑なタグの入れ子をおこなった場合などにそれぞれ見た目が異なるという問題が発生していました。ユーザーからも度々指摘が来ていたところですが、これを解消するための PJ としてメッセージの AST 化と描画の共通化がおこなわれました。

より詳しい技術的な説明は別記事で今後おこなわれる予定ですが、ここでお伝えしたいのはこうした対応も「AST 化を進める上でユーザー影響がありそうな部分をどうするか?」という会話がおこなわれているということです。

会話をもとに決定された要件からこちらの技術が最適だから使用する、というユーザー課題を解決するための技術選定がおこなわれるようになりました。

これはサーバーサイドもインフラも同様です。ユーザー影響について会話されたうえで技術選定がおこなわれます。例えば今年の夏に冨田が書いた Elasticsearch 移行プロジェクトの記事があります。

creators-note.chatwork.com

こうしたプロジェクトも、PM 側から「ユーザー影響はありますか?」と問いかけをして、どのような影響があるのかを吸い上げています。途中、仕様的に変わりそうな部分 (移行前は Amazon CloudSearch) が出てきたときは、変更点を把握した上で Go サインを出しています。

もちろん、ありとあらゆる技術的課題の解決の動きに PM が口を出しているわけではありません。口を出すのはユーザー影響がある場合です。ユーザー影響がありそうだとエンジニアが感じたら PM に相談してもらって、一緒にどうするか考えるという動きを取っています。

これから - 攻めた技術選定を、ユーザーのために

SaaS プロダクトにおいて、技術とはユーザーの課題を解決するための具体化の方法です。ユーザー課題の発見と解決方法の探求は PM の専門領域です。一方で、解決方法の具体化は PM の専門領域ではありません。

「技術は手段なのだから、目的と混同してはいけない」という論説を見ることがあります。もともとエンジニアで、今 PM という立場にいる私はこの言葉を「技術は課題解決の具体化のための手段であるのだから、技術を使うことを目的としてはいけない」と読み取っています。

「流行っているから、イケてるから」このような感覚でなんとなくプロダクトの中に技術を投入するのではなく、課題解決のための手段として適切なものを選択しましょうねという意味です。 PM がおこないたい課題解決とは、ユーザー課題の解決なので、これをおこなうときに適切な技術を選択してほしいと願っています。

では、どのように適切な技術を選択するのか?そのためにはやはり、登場してくる多くの技術を学ぶ必要があります。先に書いた「流行っているから、イケてるから」こうした技術を学ぶことは悪いことではありません。むしろ良いことです。必要なのは学んだ技術をどのように活用するか、です。

アーリーマジョリティ層に突入したプロダクトは これから最大のユーザー数 を迎えていくことになります。 Chatwork においては爆発的なデータ量とトラフィックをさばき、それでもなお快適に使えるクライアントを提供し続けるという技術的な挑戦が控えています。

そのとき最高の解決手段となる技術を選ぶためには、常に面白そうな技術を学び活かしていく必要があります。これからの Chatwork はユーザー課題解決のために、より攻めた面白い技術が選定されていくことでしょう。

これを読んだ皆様とぜひ同じフィールドで仲間として戦っていきたいと思いますので、興味がある方は採用ページもご覧くださいね。それでは、ここで筆を置かせていただきます。

recruit.chatwork.com