クラシル開発ブログ

クラシル開発ブログ

チームのコミュニケーションをより円滑にし、スピードを上げていくための取り組み

こんにちは、dely の Android チームで施策をやりながらアプリ改善に取り組んでいる tummy です。 以下記事を書いてからもう 2 ヶ月が経過し、Android チームも 1 人増えて 4 人になりました。この 3 ヶ月でメンバーの数が倍になっています(すごい)

tech.dely.jp

この 3 ヶ月間主に阿吽の呼吸でまかなえていたことを掘り起こして文章化し、チームの共通認識を作るところに注力していました。今回はその取り組みについて紹介できればと思います。

リリースマネージャーのような役割になってみる

Android チームは 1 人 1 つ何かしらの施策を担当しており、同時並行で進んでいます。2〜3 人の時期は、bot のレオくんが水曜日になるとそろそろリリースしない?とお知らせしてくれるので、それをみたときにリリースするかどうか決めていました。

f:id:rnitame:20200225085543p:plain
レオくんがお知らせしてくれている様子

しかし 3 人中 2 人が大きめの施策を担当しているのもあり、なかなかリリースしようという雰囲気になることが少なかったです。そして、4 人目がジョインした今月、細かい施策が爆速で進むようになった結果、1 週間経った時点でマージされているプルリクが 10 を超えるようになりました。

さすがにこれはコンスタントにリリースしていかないとやばいと思い、リリースマネージャーのようなことをするようになりました。とはいってもそこまで形式張ったことはやっておらず、主に以下のようなことをしています。

  • リリースサイクルの策定(次に詳細を書きます)
  • リリース作業、及びその効率化

以下の記事で書かれているように、弊社の Android CI は bot が大変活躍してくれているので、より多くの過程を bot にまかせられるように実装 & 調整中です。

tech.dely.jp

リリースサイクルの策定

やりました。経緯は上記に書いたとおりです。プロダクトレビューという文化が弊社にはあるため、そのレビュー結果への対応期間も考慮しつつも、基本的には水曜日にコードフリーズ & デバッグ、何もなければ木曜日リリースというフローを踏んでいます。

f:id:rnitame:20200225091143p:plain

リリースノートの自動化など、このサイクル内でもまだ自動化できそうな箇所があるので、引き続き模索していきます。

設計について議論したりする場所が爆誕

f:id:rnitame:20200225091435p:plain
クラシル Android の最初のコミットログ

クラシル Android は 4 年もののアプリで、設計も複数パターンあったりしてとても複雑になっています。メンバーが 3 人になり、こういった箇所にもだんだん手を入れられるようになってきたことと、過去の事情によって実装されている箇所などを質問する場として Android エンジニア MTG が生まれました。

4 人になった現在は設計をこれからどうしていくか、コードスタイルについてどれを採用していくかなど、古くからあった懸念への対応方針について議論することが多くなり、改善方面にも力を入れられているのでとても活発になっています。(次回で 6 回目まで来ました 🙆)

f:id:rnitame:20200225092506p:plain

議事録や仕様書の共同編集化

プロダクトレビュー等を通してガンガン仕様が変わる弊社ですが、仕様書が過去のまま更新されず、最新状態がわからないという問題がありました。また、それに伴い仕様の認識のズレが起きてしまっていました。 そこで、Qiita に共同編集にしたほうがいいものとしなくてもいいものを画像つきで記事にしてみました。

f:id:rnitame:20200225105542p:plain

この記事がきっかけになったのか、積極的に共同編集にしてくれる方が増えてきててとても嬉しいです。 まだ問題は残っていますが、解決の一歩になったのではないかと思っています。

プロダクトレビューについての内容の明確化

プロダクトレビューは CXO の坪田さん発信で始まったものですが、なぜ?何をみてるの?という観点が明確ではありませんでした。そのせいか、せっかくのレビューの機会でぎくしゃくした雰囲気になることがありました。 そこで、自分が集められる情報を Qiita にまとめて坪田さんに共有、ヒアリングすることで足りない箇所をアップデートしました。坪田さんにとてもわかりやすい資料も作成いただいて、全体に共有されました。これからどんどんこの考えが浸透していくといいなと思いますし、自分も意識したいなと思います。

まとめ

ざーっと今までやってきた取り組みをご紹介しました。まだまだやれることはたくさんあるので、引き続きやっていきます。 また新しい取り組みを始めた際にはブログでお知らせできればと思います!

dely では様々なポジションのエンジニアを積極採用中です!興味がある方はぜひご連絡ください!

ユーザーお問い合わせの技術的サポートについて

はじめまして。開発部の sakura818uuu です。
CS(カスタマーサクセス)チームのサポートエンジニアを始めて2ヶ月が
経過しました。

今回は、ユーザーお問い合わせの技術的サポートについて
具体的にどんなことをやっているかをご紹介しようかと思います。

はじめに

クラシルでは、ユーザーさんが困った時にお問いあわせをするページがあります。
お問い合わせの内容は課金やメルマガ、不具合の報告など様々です。

f:id:sakura818uuu:20200217093631j:plain


お問い合わせがあるとCSチームがユーザーさんとのやり取りを行います。
技術的サポートが必要な場合は、開発部のCS技術的サポート役にご相談
いただくフローになっています。

去年の12月から私が技術的なサポート役を担当しています。

f:id:sakura818uuu:20200217090238p:plain

どんなお問い合わせが多いのか?

課金やメルマガ、不具合のお問い合わせが多いです。

※技術的サポートをしたお問い合わせに限るため、お問い合わせ全体のデータではありません。

お問い合わせの対応件数などを可視化して社内に共有をしています。

f:id:sakura818uuu:20200218080815j:plain

月によってお問い合わせ内容の傾向は違います。
新機能追加やマーケティング施策によってお問い合わせ内容の傾向は変化するので、 日々のキャッチアップが非常に重要なことを身にしみて感じています。

どうやってサポートしているのか

サポート方法はケースバイケースですが、主に3種類の方法があります。

1.ツール

お問い合わせの内容に合わせて様々なツールを使います。
ツールでしか得られない情報や設定できない情報があるためです。

調査/設定した結果をもとにCSチームにご連絡します。

メルマガツール・課金管理ツールなど扱うツールの数はたくさんあります。
この2ヶ月でツールの使い方は全て社内にドキュメント化しました。

f:id:sakura818uuu:20200217113251p:plain:w270
Qiita:Teamに数十個の記事があります

ドキュメント化したことで確認する時間が減り対応時間が削減されました。

2.データ

お問い合わせを調査する時に、ツールを使う以外に
SQLでデータを出して調査することも多いです。

データを出して調査した結果をもとにCSチームにご連絡します。

主にredashでクエリを書いています。
CSの調査でよく使うクエリはお気に入り登録して、すぐ使えるようにしています。

f:id:sakura818uuu:20200217140423p:plain

参考:redashのクエリお気に入り登録について

tech.dely.jp

3.開発部内で相談

アプリの不具合やサーバーサイドが絡んできそうなお問い合わせには、
私だけで解決できないので開発部内のメンバーと相談します。

開発部とCSチームのハブになって動きます。
ハブの役割は以下のようなことをしています。

f:id:sakura818uuu:20200217132139j:plain

<開発部へ伝えるとき>
・エンジニア側が調査しやすい情報を事前に揃える
・いつから何件発生しているかなど対応の温度感を伝える

<CSチームへ伝えるとき>
・技術的な用語を使わずに原因を説明
・調査に時間がかかりそうな場合の調整や対応検討

開発部のメンバーとはなるべく密に連絡を取り合うようにしています。
いつも迅速に対応していただきすごく助かっています。

さいごに

CS(カスタマーサクセス)チームのサポートエンジニアとして
ユーザーお問い合わせにどのように対応しているか
をご紹介しました。

多種多様なお問い合わせが来るため調査が難しいこともありますが、
CSチームや開発部に助けてもらっています。

「迅速に、丁寧に」ユーザーさんのお問い合わせに答えられるよう
これからもサポートしていけたらと思います。


delyの開発チームについて詳しく知りたい方はこちらをどうぞ

Zendeskからslackへの通知方法

はじめまして。sakura818uuuです。
Zendeskのチケットをslackに通知する方法をご紹介します。

はじめに

現在、私はCS(カスタマーサクセス)チームの技術的なサポートを行っています。

note.com

delyのCSチームでは、Zendeskを去年から使い始めました。
Zendeskのチケットが届いたり更新されたりすると、slackに通知が飛ぶようにしておりとても便利です。

このブログでは、Zendeskのチケットをslackに通知する方法をご紹介します。

手順方法

Incoming Webhookの設定、Zendesk拡張機能の設定、Zendeskトリガの設定を順に行っていきます。

Incoming Webhookの設定

  1. このページでIncoming Webhookと検索・選択します

f:id:sakura818uuu:20200207172532p:plain


2. 「Slackに追加」ボタンを押します

f:id:sakura818uuu:20200207154922p:plain


3. Zendeskの通知を飛ばしたいslackのチャンネルを検索・指定します f:id:sakura818uuu:20200207155032p:plain


4. チャンネルの指定が完了したら「Incoming Webhook インテグレーションの追加」を押します f:id:sakura818uuu:20200207161759p:plain


5.インテグレーションの設定 にある Webhook URL をコピーします。 このコピーしたURLは次のZendesk拡張機能の設定で使用します。

f:id:sakura818uuu:20200207162030p:plain

Zendesk拡張機能の設定

  1. Zendeskにログインします
  2. ホーム画面から歯車マークを押し設定ページに飛びます

f:id:sakura818uuu:20200207163145p:plain:w50


3. 設定の拡張機能を選択します

f:id:sakura818uuu:20200207163621p:plain:w200


4. ターゲットを追加を選択します

f:id:sakura818uuu:20200207163707p:plain


5. HTTPターゲットを選択します f:id:sakura818uuu:20200207164215p:plain


6. HTTPターゲットの所定の欄を埋めていきます。

↓デフォルト f:id:sakura818uuu:20200207164405p:plain

↓埋めたもの f:id:sakura818uuu:20200207164458p:plain

・タイトルはご自由に
・URLはIncoming Webhookの設定の5の手順で得たURLを貼り付け
・方法はGETからPOSTに変更
・コンテンツタイプはJSON (コンテンツタイプはデフォルトでは表示されていませんが、方法をPOSTにすると表示されます)
・基本認証は今回はチェックなし


7. 送信ボタンの横にある選択肢をターゲットのテストからターゲットの作成に変更します f:id:sakura818uuu:20200207165255p:plain


8. 送信ボタンを押します f:id:sakura818uuu:20200207165337p:plain


9. ターゲットが作成されたら完了です

f:id:sakura818uuu:20200207165506p:plain

Zendeskトリガの設定

  1. Zendeskにログインします


2. ホーム画面から歯車マークを押し設定ページに飛びます

f:id:sakura818uuu:20200207163145p:plain:w50


3. ビジネスルールのトリガを選択します

f:id:sakura818uuu:20200207165748p:plain


4. トリガを追加 を選択します

f:id:sakura818uuu:20200207165832p:plain


5. トリガ名と説明を書きます

f:id:sakura818uuu:20200207165942p:plain


6. 条件を設定します。ここの条件は使いやすいよう適宜変更してください。

今回の条件:
「チケットが作成され、ステータスが解決済みではなく、チケットにパブリックコメントがあるとき」

f:id:sakura818uuu:20200207170114p:plain


7. アクションを設定します。

まず、「ターゲットに通知」を選択します。
次に、「ターゲットに通知」の横枠に Zendeskの拡張機能の設定の9 で設定したターゲット名前が出てくるので選択します。
ここでは横枠に「テストです。」を選択しました。

f:id:sakura818uuu:20200207170651p:plain

コピペしやすい用

{
   "attachments":[
      {
         "fallback":"新しいお問い合わせがありました",
         "pretext":"新しいお問い合わせがありました",
         "color":"#D00000",
         "fields":[
            {
               "title":"件名: {{ticket.title}}",
               "value":"URL: {{ticket.url}}"
            }
         ]
      }
   ]
} 


JSONボディももちろんカスタマイズ可能です

参考: Slack API attachmentsチートシート - Qiita


8. 作成ボタンを押します

f:id:sakura818uuu:20200207171145p:plain


9.トリガが作成されていれば完了です

f:id:sakura818uuu:20200207171647p:plain

完成したらどうなるか

Incoming Webhookの設定、Zendesk拡張機能の設定、Zendeskトリガの設定が完了すると Zendeskからslackに通知が届くようになります🎉

f:id:sakura818uuu:20200207172006p:plain:w300

「SRE NEXT 2020」にdelyが協賛&登壇しました!

f:id:gomesuit:20200204233039p:plain

こんにちは。delyのSREの井上です。

delyは先日開催されたSRE NEXT 2020にGOLDスポンサーとして協賛をさせていただきました!当日はセッション枠を頂き、「delyにおける安定性とアジリティ両立に向けたアプローチ」をテーマに発表もさせていただきました。

セッションでは、

  • 前半:SRE本に則った理論の話
    • SREはプロダクト開発の速度を安全に高めるために存在しているということ
    • プロダクト開発の速度を安全に高めるためには単純さを追求することが重要であること
  • 後半:前半の理論に則ったdelyでの実践の話

をしました。スライドは公開済みですが、それだけだと伝わりにくい内容も含めてブログにも投稿させていただきます。

f:id:gomesuit:20200205143342j:plain

f:id:gomesuit:20200205143359j:plain 当日は多くの方が参加されていました!

SRE NEXTに参加してみて

SRE Loungeの勉強会は#5以降の会は全て参加させて頂いているのですが、いつも有意義な情報が得られて登壇者や運営の方には感謝しかないなと思っていました。なので、SRE NEXTでスポンサーという形でコミュニティに貢献できたことは良かったなと感じています。登壇の機会を頂けたことも感謝しかなく、今後も引き続きよろしくお願いしますという感じです。

個人的にすごく印象に残ったのはSREの今後というトピックでした。時代の変化でアジャイル開発やスクラム開発が当たり前の存在になったように、おそらくSLOやエラーバジェットも今後何かのフレームワークに落とし込まれて開発文化に取り入れられていくのだろうと思いました。そうなるとSREの存在意義はほとんどなくなるので、どういった形に役割が変化していくのかなというところを考えるのも面白そうだなと思いました。

目次

SREの存在意義

SREは「信頼性を高めるため」ではなく、「プロダクト開発の速度を安全に高める」ために存在しているということを出来る限り分かりやすく伝えるべく、SRE本に記載されている内容を元に順序立てて説明をしていきました。

SREが成し遂げようとしていることは結局SREだけでは達成できず、必ずプロダクト開発チームやマネジメント層との連携が必要になります。他組織と連携するためにもSREが何をしようとしているのかを誰もが理解できるように、可能な限り噛み砕いてスライドに落とし込みました。

旧来のサービス運用と組織分離

AWSやGCPが存在しない時代における「システム管理者」の業務は、既存ソフトウェアの活用だったので、サービスの運用業務はソフトウェアの開発業務とは別のスキルセットが必要とされました。

サービスが成長してくると、必然的に運用業務に人手が必要になるので人員調達の観点からほとんどの企業で開発と運用の組織が分離されました。

この組織分離には人員調達以外の点で大きなデメリットが存在します。

組織分離によって発生するコストとSRE

組織分離によるデメリットは直接的なコストと間接的なコストに分けられます。

  • 直接的なコスト
    • 手作業の運用業務による人件費の増加
  • 間接的なコスト
    • 目標の違いが引き起こす対立構造による開発スピードと安定性の低下
      • 開発組織の「新しい機能を早くリリースする」という目標
      • 運用組織の「サービスに問題が起きないようにしたい」という目標

これらのコストに対するGoogleのとったアプローチがSREになります。

コストに対するSREのプラクティス

f:id:gomesuit:20200203014326p:plain

SRE本に記載のあるいくつかのプラクティスをコスト別に分類してみました。

直接的なコストに対するアプローチは主に、「運用作業をどのように自動化するのか」と「自動化に使える時間をどうやって維持するか」の大きく2種類に分かれます。

間接的なコストに対するアプローチは主に「組織間の対立構造をどのように解消するか(発生させないか)」に焦点を当てたものになり、SLOやエラーバジェットはこちらに分類されると考えています。

SRE NEXTでもどちらの内容をテーマにするのか発表ごとに分かれていて、どちらに課題感を大きく感じているのかを考察しつつ聞くのもまた面白かったです。delyの発表では間接的なコストの方に対するアプローチについてお話しました。

対立構造が引き起こすプロダクト開発速度の低下

f:id:gomesuit:20200203020535p:plain

SRE本の中で「間接的なコスト」として語られている内容を図に落とし込みました。開発と運用の目標の違いが最終的にプロダクト開発速度を低下させる構造になっています。

重要なポイントとしては、開発/運用の組織体制が開発/SREという組織体制になったからといって、ただそれだけでこの対立構造が解消するわけではないという点です。

開発速度/安定性のバランスのコントロール

f:id:gomesuit:20200204020248p:plain

プロダクト開発速度を最大化させるには、開発速度/安定性のバランスをコントロールする必要があります。

ある項目において開発速度と安定性どちらを取るのか決めなければいけないタイミングが多々あると思いますが、客観的なデータがない限り交渉力のある人であったり立場のある人の一声で最後は決まってしまうと思います。

その場合、誰かの感覚がバランスを取っているということになると思うのですが、その「誰か」はどこかの組織に属しているはずなので結局、対立構造は解消しないということになってしまいます。

エラーバジェット

「誰かの感覚」に終止符を打つSREのプラクティスがエラーバジェットです。

f:id:gomesuit:20200204021555p:plain

予算がなくなった時点でリリースできなくなるという超強力なポリシー・・・!

f:id:gomesuit:20200204021611p:plain

機能のリリース速度を最大化するためにエラーバジェットの導入をSREの目標としましょうという内容がSRE本には記載されています。

f:id:gomesuit:20200203020735p:plain

エラーバジェットによって目標を統一することで、開発速度と安定性をコントロールしプロダクト開発速度の最大化を図ることが可能になります。

SREの存在意義の再確認

f:id:gomesuit:20200204031113p:plain

エラーバジェットの仕組みからも読み取れる通り、SREはSLOやエラーバジェット等のプラクティスを駆使して組織の対立構造を解消し、プロダクト開発の速度を安全に高めるために存在しているということが言えると思います。

エラーバジェットとは別のアプローチ

とはいってもエラーバジェット導入の難易度って時間がかかりますよね。なので、SRE本に載ってるプロダクト開発の速度を安全に高めるための別のアプローチも同時に紹介しました。

エラーバジェット導入の難易度

f:id:gomesuit:20200203034808p:plain

エラーバジェット導入の難易度はとても高いです。

「その期間のエラーバジェットを消費しきったら残りの期間リリースができなくなる」というルールは、運用しているサービスのビジネスモデルによってはかなり厳しいポリシーだと思います。

もちろん、SREの文化を浸透させ最終的にはエラーバジェットの導入を目指すべきだとは思いますが、導入しようと思って数日でできるような手軽なアプローチではないことは間違いないです。

SRE本においてはエラーバジェット以外にもプロダクト開発の速度を安全に高めるためにSREがすべきことについて触れており、発表ではその内容について紹介しました。

それは想定外の複雑さの削減単純さの追求です。

想定外の複雑さとSRE

f:id:gomesuit:20200204032636p:plain

SRE本においては開発速度と安定性がどちらも低下する要因として想定外の複雑さを挙げています。

具体例としては、ソフトウェアの領域においては密結合なソースコード、インフラの領域においては手作業で構築されたサーバなどが挙げられます。

想定外の複雑さはレイヤーに関係なく発生しますが、どこに存在していたとしても開発速度と安定性のどちらも低下させる要因になってしまいます。

f:id:gomesuit:20200204033114p:plain

SRE本には想定外の複雑さに対してSREがとるべき行動が記載されています。

  • 受け持っているシステムに想定外の複雑さが生じていたら差し戻す。
  • 関わっているシステムや、運用を受け持つことになるシステムから複雑さを取り除く努力を継続的に行う。

単純さの追求による開発速度と安定性の両方の向上

f:id:gomesuit:20200204033058p:plain

ソフトウェアやインフラなどの領域に関わらず、想定外の複雑さを継続的に削減していくことと、想定外の複雑さを新たに作り込まないことが、開発速度と安定性を両方高めていくためには必要です。

delyでの実践

後半はdelyにおける実践の話をしました。

正直なところ、他社の参考にできそうな奇抜な施策が出来ている状況ではないです。そもそも当たり前なことがまだちゃんと出来ていない状態なので、ひとつひとつの課題解消を愚直に進めつつ基盤を整えているフェーズです。

そういったフェーズの会社が他にも存在すると思ったので、やっていることは割と普通なのですが、dely自体のことも知ってもらえる良い機会だと思いいくつか紹介させて頂きました。

規模とフェーズ

f:id:gomesuit:20200203030300j:plain

サービスや組織のフェーズをまず知ってほしいと思ったので、サービスの規模とメンバーの増加傾向がわかるようにグラフを見て頂きました。

おかげさまでサービスは年々成長を続けていて、開発のメンバー数も増加傾向にあるのですが、注目してほしいのは2016年から2018年までの2年間、サーバサイドがほぼ1人での開発だったというところです。

この頃の状況は下記の2つの記事から読み取ることが可能です。

note.com

www.fastgrow.jp

2019年前半までとにかく機能リリース優先でやってきたこともあり、最近になってサービスの成長に伴って品質やセキュリティなどの点でいろいろな問題が発生し始めました。

課題

f:id:gomesuit:20200203025647p:plain

クラシルは開発開始から4年ほどが経っています。当然のように様々な課題を抱えています。

現状の課題が多いからといって、今までの選択が決して悪かったというわけではないですが、フェーズが変われば課題も変わるということは事実なので、話し合うべきなのは 「これからどうしていくか」というところだと思っています。

具体的なアプローチ

f:id:gomesuit:20200203025618p:plain

delyでは直近1年をかけて想定外の複雑さを減らし、今以上に増やさないための文化づくりをしてきました。

下記の3つの視点でアプローチを行いました。

  • 何が複雑なのかを認識し共有することにより可視化する
  • 複雑なものを減らす計画
  • 複雑なものを新たにつくらない仕組みづくり

想定外の複雑さを減らすアプローチ

改善MTG

f:id:gomesuit:20200203035120p:plain

f:id:gomesuit:20200205000650p:plain

複雑さの箇所を可視化するために行っているのが改善MTGです。想定外の複雑さを一番把握しているのは現場のメンバーですが、改善MTGを実施することでその感覚を言語化し、メンバー間で共有することを目的にしています。

想定外の複雑さも誰かの感覚の中にある限り、解消することは理論上不可能です。しかしMTGを行いメンバー間で議論を行うことで、感覚の先の根本的な課題が明確になっていき、どういった対応が考えられるのかや解消によって得られる効果などが言語化されます。PdM等のタスクの優先順位付けに決定権を持つ人が、機能開発タスクより改善タスクの優先度を上げるという意思決定をできるように可視化しておくことが想定外の複雑さを解消するためには必須であると考えています。

改善MTGの詳細については既に記事があるので興味があればこちらを読んでみてください。

tech.dely.jp

課題洗い出し会

f:id:gomesuit:20200205001012p:plain

f:id:gomesuit:20200205001741p:plain

改善MTGも運用開始直後においては課題の提案が特定のメンバーに偏るという課題がありました。その課題に対して行ったのがこの課題洗い出し会になります。

課題が潜んでいそうな技術分野をリストアップし、メンバー間で回答が見えないようにそれぞれの分野に対する課題感を回答してもらいました。その回答をマージして課題感がメンバー間でずれている箇所に対して議論をします。議論した内容をもとに課題を登録してもらうことで、今までなかった観点の課題が提案されるようになりました。

課題の提案もある程度慣れが必要な部分だとは思うので、不得意なメンバーでも提案しやすくなるような仕組みや、新しいメンバーが遠慮してしまわないような工夫が重要だと考えています。

リファクタリング計画

f:id:gomesuit:20200205004439p:plain

改善MTGはあくまで可視化までが目的になっているので、解消していく工程については別途、別の手段で進める必要があります。誰か1人が進めるのではなく分担して解消していく必要があるので、やみくもに進めるのではなく計画をたてるようにしています。

スプレッドシートに今抱えている課題を一覧化して、重要度と緊急度の観点から優先順位を議論して決めます。優先順位に伴って、誰がどの課題を解消するのか担当を埋めていきます。あとは担当者が愚直に解消していくという感じで進めています。

詳細については既に記事があるので興味があればこちらを読んでみてください。

tech.dely.jp

想定外の複雑さを増やさないアプローチ

設計レビュー

f:id:gomesuit:20200205005913p:plain

増やさないためのアプローチの一つとしては設計レビューを紹介しました。

想定外の複雑さを解消していっても、別のところで随時新しく生まれていたら削減している意味がないです。

サーバサイドエンジニアの数が増えてきたタイミングで、コードを書き始める前に設計完了時点でレビューをするように開発フローに組み込みましたが、SREもSLOの維持に責任を持っているので、アーキテクチャの観点など想定外の複雑さが発生していないかレビューできるようにSREも参加を必須にしています。

内容によってはかなり時間をかけて議論を行っていて、コードを書く前から全員で徹底的に議論を行うような文化が出来ています。

このあたりの内容はコアすぎてあまり書けないのですが、何かの振り返りのタイミングで記事を書いて共有できればと考えています。

意識

目的と手段の可視化

f:id:gomesuit:20200205005955p:plain

f:id:gomesuit:20200205013542p:plain

いろいろなアプローチを考えつつ複雑さを解消しているのですが、複数人で分担していくと、ついついリファクタリングすること自体に目がいってしまいがちで、本来の目的を見失うことが少なからず発生してしまいます。

リファクタリングすること自体が目的化してしまうと、つい必要のないところまで手を伸ばしてしまうと思うのですが、それをやってしまうと今やらなくてよいものまで実施してしまうことになるので、今までのアプローチの効果が薄れてしまいます。

なので目的と手段を可視化することで、今やっているタスクが何の目的の手段にあたるのかということを常に意識できるようにしています。

例えば、リファクタリングや開発ガイドラインの見直しなどはタスクに分解すると、不足しているテストの作成や開発フローの見直しなどになりますが、これらのタスク自体も手段であって目的のために行っているという状態になっているはずです。意識しなければいけないのは目的の方であり「開発速度と安定性の両方の向上」や更にその上の事業の成長になります。

さいごに

偉そうなことべらべら書いてる割にdelyのSREは実は全然始まってすらいません。少しづつ始めている内容は下記の記事にかいてあるので是非読んで頂きたいです。

tech.dely.jp

もちろんSREを絶賛募集しています!クラシルの開発スピードを落とさず、信頼性も担保するという難易度の高い課題に挑戦したい方はぜひ声をかけてください!

www.wantedly.com

delyの開発チームについて詳しく知りたい方はこちらもあわせてどうぞ

情報共有不足を打破するために試した7つのこと

はじめまして。

dely, Inc.でクラシルの開発に携わっている @sakura といいます。
本記事では、分析データを社内に情報共有するまで私がどんなことしたかを
赤裸々に紹介します。

この記事はデータ活用 Advent Calendar 2019の21日目の記事です。

はじめに

「こんなにも貴重なデータがあるのに全然活用できていない…てか誰も見ていない… 」 この状況を打破すべく一歩一歩やっていったことを共有します💪

泥臭くやったこと

スプレッドシートにしてハードルを下げる

f:id:sakura818uuu:20191217180009p:plain:w400
まずは、データをスプレッドシートにまとめました。

なぜかというと、そのデータがredashでしか閲覧できなかったからです。
エンジニア以外にとってはredashは心理的負荷が高く、スプレッドシートのほうが普段から使い慣れているので扱いやすいです。

弊社ではG Suiteを使用しているので、
スプレッドシートにすることでデータを見るハードルがぐっと下がりました。

データを整理して見やすく、負荷を少なく

f:id:sakura818uuu:20191217195835p:plain:w400

データの性質そのものではなくて、データを閲覧してほしい人を軸にして数千件のデータをカテゴリ分けしました。
上の画像は実際のスプレッドシートです。タブでカテゴリごとに分かれています。

何千件もあるデータを見るのは脳の負荷が高く疲れてしまいます。
従って、事前にカテゴリ分けをしてこの人はここだけ見ればOKというようにしました。

社内の複数人にメンションをつけて共有

f:id:sakura818uuu:20191217183124p:plain:w400

スプレッドシートが完成したら、データを見てほしい人にメンションをつけて共有しました。

特定のチャンネルで特定の人に「こういうデータがあって、ここの部分を利用できると思うんですがどうでしょうか」という風に、 チャンネル / メンションをつける人ごとにメッセージを変え、そのデータの効果が最大化するように努めました。
やってることはパーソナライズに近いかもしれないです。

オフラインで共有してアドバイスをもらう

f:id:sakura818uuu:20191217183352j:plain:w350

オンラインで伝えることは効率がよいですが、一番物理的距離が近く、必ず伝わるのはオフラインだと思い 2つのMTGで「貴重で役に立つデータがここにあるのでぜひ見てください」と共有しました。

その場で見てくださる方もたくさんいて、アドバイスを頂くこともできました。(このデータ役に立ちそう、といわれた時はうれしかったです)

Qiitaにも残して検索しやすく

f:id:sakura818uuu:20191217184230p:plain:w450

全社員が見れる社内の共有ドキュメントツールのQiitaを使い、軽くドキュメントを残しておきました。
slack内に残すと「あれ、あの情報どこいったっけ」となりがちなので、Qiitaにもまとめました。

ドキュメントに残すことで検索のヒットもしやすくなり、後から見返す時も探しやすくなります

bot作成してslackでかんたん確認

f:id:sakura818uuu:20191217185035p:plain:w450

定期的に確認したほうがいいデータだと思ったので、botを作成しました。
redashのデータを定期的にslackに通知することで、最新の情報をかんたんに確認することができます。

botの作成には色んなエンジニアさんが手伝ってくれました。ありがとうございます。

bot警察をしてデータを見る文化を作る

f:id:sakura818uuu:20191217191830j:plain

弊社のslackにはbotが何十個もおり、いくらかんたんに見れるといっても見過ごしてしまうことも多いです。
どうにかこのデータを見る文化を作ることはできないか、と考えました。

そこで、botが通知してくるデータを私自らが解釈・要約して投稿することもしていました。
botの内容に徐々にスタンプがつき、返信がつき、最近では私以外のメンバーが能動的に反応してくれることも多いです。 (すごい!)

まとめ

本記事では、分析データを社内に情報共有するまでにどんなことをしたかを赤裸々に紹介しました。

まとめとして、この経験から学んだ情報共有の五箇条を記しておきます。

f:id:sakura818uuu:20191218073226j:plain:w430

「情報共有難しい…💭」
と悩んでいる方の参考に少しでもなれば幸いです。





最後に告知です。delyではエンジニアを絶賛募集中です。
ぜひお気軽にご連絡ください。

https://www.wantedly.com/projects/329047

 

 

~OSSから学ぶ~ MVCフレームワークの保守性がモリモリ上がるクラス設計

こんにちは、delyコマース事業部エンジニアの小川です。

先月11月に入社し、エキサイティングな毎日を過ごしています。

この記事はdely Advent Calendar 2019 - Qiitaの24日目の記事です。

昨日はSREの松嶋さんが「AWS RunCommandを使ってEC2上に監視ダッシュボードをサクッと作る(Ansible+Terraform+Grafana編)」という記事を書いてくれましたので是非そちらも読んでみてください!

tech.dely.jp

コマース事業部では、現在「事業開発」と「ソフトウェア開発」がほぼ同時に進行しており、プロジェクトにおける確定要素と不確定要素が複雑に絡み合っています。 スピード重視でゴリゴリ実装していくのも興奮しますが、変化に耐えづらい実装をしてしまうと、その後の開発スピードに影響していまい、事業のスピードが落ちるなんて事にもなりかねません。

そこで、プロジェクトの保守性や拡張性をあげるには、どういった設計をしたら良いかを、OSSであるGitLabを一例として見ていきたいと思います。

今回は、model view controller 以外のどんなディレクトリ(=クラス)を導入しているか、それがどんな役割を担っているかを見ていきましょう。

それではいってみましょう。

GitLabが導入しているディレクトリ

GitLabはRailsで実装されています。app配下のディレクトリで、デフォルトで作成されないものを抽出すると以下のようになりました。

  • finders

さまざまな条件に基づいたコレクションを取得するクラス

  • graphql

Graphqlに関するクラス

  • policies

権限確認系に使われるクラス。独自のDSLで実装されている。

  • presenters

viewに関わるロジックやデータを持つオブジェクトをviewに提供するクラス

  • serializers

フロントで使われるJSONを構築するためのビジネスルールをカプセル化しているクラス

  • uploaders

CarrierWaveに依存しているUploader

  • services

ビジネスロジックが取りまとめられているクラス

  • validators

Activerecordが提供しているカスタムバリデーター

  • workers

SidekiqのWorkerクラス

今回はこの中から、個人的にあまり導入したことがない、finderspresentersあたりを見ていきます。

finders

finderクラスは「さまざまな条件に基づいたコレクションを取得する」責務を持つクラスになっています。

例えば、プロジェクトモデルの中でこのようなイシューを取得するメソッドを実装するより、

class Project
  def issues_for_user_filtered_by(user, filter)
    # たくさんのロジック...
  end
end

issues = project.issues_for_user_filtered_by(user, params)

下記のように実装すると、よりモデルを薄く保つことができるよ!っていうイメージですね。

issues = IssuesFinder.new(project, user, filter).execute

GitLabのFinderクラスは、基本的に#executeのみをパブリックメソッドとして持っているみたいです。

実装を見ていく

では実際にProjectsFinderを例に挙げて見ていきましょう。

まずはどこで#executeが実行されているか探してみます。 ありました、Admin::ProjectsControllerで以下のように実行されています。

ProjectsFinder.new(params: finder_params, current_user: current_user)
              .execute
              .includes(:route, :creator, :group, namespace: [:route, :owner])
              .preload(:project_feature)
              .page(finder_params[:page])

paramsにfinder_paramsを、current_userにはcurrent_userを指定しています。 find_paramsは、取得するプロジェクトの条件、つまりフィルタリングするパラメーターや、ソートの条件などのパラメーターを含めています。

では、ProjectsFinderの実装はどうなっているのでしょうか。

#initializeには、paramsとcurrent_userとproject_ids_relationをキーワード引数で渡します。

def initialize(params: {}, current_user: nil, project_ids_relation: nil)
    @params = params
    @current_user = current_user
    @project_ids_relation = project_ids_relation
end

#excuteの実装は以下のような形です。

def execute
    user = params.delete(:user)
    collection =
      if user
        PersonalProjectsFinder.new(user, finder_params).execute(current_user) # rubocop: disable CodeReuse/Finder
      else
        init_collection
      end

    collection = filter_projects(collection)
    sort(collection)
end

変数collectionに、フィルタリングのベースになる、プロジェクトのコレクションを代入しています。 その後、#filter_projectsでプロジェクトのフィルタリングをおこなった後、#sortにて結果のソートをおこなっていました。

フィルタリングとソートの条件は、#initializeの時に渡したparamsで指定しています。

メリットになりそうな事

パブリックメソッドである#executeが見通しがよく、コードリーディングしやすいと感じました。 デルメルの法則にも違反していなく、依存も少ない(浅い?)と言えそうです。

このコレクションの取得を、modelに#filterのようなメソッドで実装したらどうなるでしょうか? ProjectsFinderに実装されている、多くのprivateメソッドがmodelにも実装されることになります。しかもそのメソッド達は、#filterの結果を達成するために切り出されている(特に他のメソッドでは使われない)ロジックなので、そのメソッド達はfat modelになってしまう要因の一つだと思います。

取得のロジックが単純なうちは良いですが、上記まで複雑になってきたり、必要なパラメーターが増えてきたら、Finderクラスの実装を考えていいかもしれません。

ですが、プロジェクトの初期段階でも、取得系の実装が大きくなることが分かっている、かつ不確定要素がたくさんありそうならば、取得系のロジックをFinderに集約し、呼び出し側が変更に影響しないように実装するのもありだと思いました。

presenters

presenterは、viewに関わるロジックやデータを持つオブジェクトをviewに提供するクラスとなっています。 viewに直接書いてあるロジックや、modelにviewに関連するロジックやデータのメソッドは、presenterに実装します。

実装をみていく

使われ方を見てみます。今回はProjectPresenterを見ていきます。 presenterはviewで下記のように呼び出されていました。

-# @projectはProjectPresenterのオブジェクト
= render 'stat_anchor_list', anchors: @project.statistics_anchors(show_auto_devops_callout: show_auto_devops_callout)

パーシャルのreder時に、引数として ProjectPresenter#statistics_anchors の返り値を渡しています。 renderされているパーシャルは、

- anchors = local_assigns.fetch(:anchors, [])

- return unless anchors.any?
%ul.nav
  - anchors.each do |anchor|
    %li.nav-item
      = link_to_if anchor.link, anchor.label, anchor.link, class: anchor.is_link ? 'nav-link stat-link d-flex align-items-center' : "nav-link btn btn-#{anchor.class_modifier || 'missing'} d-flex align-items-center" do
        .stat-text.d-flex.align-items-center= anchor.label

のようになっています。

anchorsで渡されたデータを使って、リンクを生成していますね。 このパーシャルは、#link, #label, #is_link, #class_modifierのメソッドもつオブジェクトに依存しています。

では次に、ProjectPresenter#statistics_anchorsの実装見てみます。 下記はProjectPresenterの実装の一部です。

class ProjectPresenter < Gitlab::View::Presenter::Delegated  
  presents :project

  AnchorData = Struct.new(:is_link, :label, :link, :class_modifier, :icon)
  def statistics_anchors(show_auto_devops_callout:)
    [
      commits_anchor_data,
      branches_anchor_data,
      tags_anchor_data,
      files_anchor_data
    ].compact.select(&:is_link)
  end

  def commits_anchor_data
    AnchorData.new(true,
                   statistic_icon('commit') +
                   n_('%{strong_start}%{commit_count}%{strong_end} Commit', '%{strong_start}%{commit_count}%{strong_end} Commits', statistics.commit_count).html_safe % {
                     commit_count: number_with_delimiter(statistics.commit_count),
                     strong_start: '<strong class="project-stat-value">'.html_safe,
                     strong_end: '</strong>'.html_safe
                   },
                   empty_repo? ? nil : project_commits_path(project, repository.root_ref))
  end
end

ProjectPresenter#statistics_anchorsは#commits_anchor_dataや、#branches_anchor_dataを集めて返しています。

#commits_anchor_dataは、構造体であるAnchorDataを生成・返却しています。ProjectPresenterは、Gitlab::View::Presenter::Delegated(Rubyの標準のSimpleDelegatorを使用)を継承しているので、Projectのインスタンスメソッドである、#statisticsにもアクセスできます。

ちなみに、#branches_anchor_dataなど、#*_anchor_dataという命名のメソッドは全てAnchorDataを返却していました。

他にもオブジェクトの生成の仕方や、URL生成のヘルパーなど、しっかり作り込まれているのですが、長くなりそうなので割愛します。 オブジェクト生成の部分を端的に紹介すると、直接ProjectPresenter.newすることを禁止しており、下記のようなパターンで生成するようにしています。

# presentメソッドで生成するパターン
@project.present 

# Factoryクラスを使って生成するパターン
Gitlab::View::Presenter::Factory.new(@project).fabricate!

メリットになりそうな事

viewの表示のルールがpresenterに集約されていて読みやすいと思いました。 表示してはいけないものを表示してしまったなどの事故が起きにくくなりそうです。viewがコンフリクトした時の恐怖からバイバイできるのも個人的に好きです。

また、viewに比べかなりテストしやすくなっています。

導入するときには、presenterがどういった粒度で実装されるかをチームで決めておいた方が良いかもしれません。 GitLabはmodelに対してpresenterが実装されている風に見受けられました。(models/project.rbに対して、presenters/project_presenter.rbのイメージ)

プロジェクトによって、違う粒度での実装もありえると思います。例えば、viewで関連データが複雑かつ、多くのところで使われているパーシャルがあったら、それと1対1で対応させるようにすれば幸せになれそうですね。コントローラーにも同じロジックを書かなくて済むし、変更に対する影響範囲も明確です。もしコマース事業部で導入するとしたら、draperを使ったDecoratorとどんな住み分けするか(そもそも導入しない)とか、チーム内で議論してみたいです。

最後に

今回はこんな風にGitLabのクラス設計を見ていきました。 プロダクトの特性によって設計の仕方も変わってくると思うので、違うOSSも読んでみるのも楽しいと思います。

「保守性がモリモリ上がるクラス設計」と題しましたが、クラス設計に銀の弾丸はない気がするので、プロダクトの変化しやすい部分はどこか、設計することでどんな問題を解決したいかを、チーム内で議論を重ねて実装・検証することが良さそうです。

コマース事業部では事業も開発も挑戦することが多く、エンジニア・デザイナーを強く募集しています。 もし興味がある方がいましたら、お気軽にご連絡ください!

www.wantedly.com www.wantedly.com

AWS RunCommandを使ってEC2上に監視ダッシュボードをサクッと作る(Ansible+Terraform+Grafana編)

こちらは、dely advent calender 2019の23日目の記事です。
qiita.com
adventar.org
昨日は、サーバーサイドエンジニアのyamanoiさんが「画像管理をActiveStorageからCarrierWaveへ乗り換えた話」という記事を書きました。興味を持った方は、是非読んでみてください!
tech.dely.jp

こんにちは!
今年11月からdelyに入社しました開発部SREの松嶋です。
本記事では、Systems ManagerのRunCommand (Ansible-playbook)を使うことでより簡単に監視ダッシュボードを作ることができたので、その手順について紹介したいと思います。

はじめに

今年9月にGitHubまたはS3に保存しているAnsible-playbookを直接実行する機能がSystems Managerに導入されたのを覚えていますか。
この新機能によって、Ansibleを使うためにEC2にssh接続用の公開鍵の作成や管理をしなくてもplaybookを実行可能になりました。Ansibleのplaybookを作成するだけでサーバー設定ができるのは、設定や管理面で楽になりますよね。
ちょうど弊社でも運用やセキュリティの観点からSystems ManagerのRunCommandやAutomationを使用することで、sshしなくてもデプロイできる手順に順次置き換えている最中です。
aws.amazon.com

また今年の11月には、Grafana6.5がリリースされました。このアップデートによって、AWS Cloudwatchのメトリクスをより効率的に監視できる機能が新しく追加されました。例えば、ワイルドカードを使って動的なクエリを書くことが可能になったり、事前構築されたダッシュボードが用意されるようになったため素早くモニタリング開始することができるようになっています。
aws.amazon.com

そこで、今回はこれらの新機能を試すために、Systems Managerの公式ドキュメント「AWS-ApplyAnsiblePlaybooks」を使ってRunCommandでGrafanaの監視ダッシュボードを構築してみたいと思います。

今回使ったものは、以下の通りです。

  • Grafana 6.5.2
  • Nginx 1.16.1
  • Terraform 0.12.12
  • AWS-ApplyAnsiblePlaybooks (Systems Managerドキュメント)

Grafanaプロビジョニング用設定ファイルの用意

現在のGrafanaでは、データソースやダッシュボードをファイル管理することが可能となっています。そのため、事前に設定ファイルを用意しておけば、Grafanaの起動と同時にモニタリングを開始することができます。

まずは、データソース設定用ymlファイルを用意します。ここでは、データソースのタイプと認証方法の設定を記載します。セキュアな情報が必要となるのでAWSのパラメータストア等を使用して安全に管理してください。

  • cloudwatch-datasource.yml
apiVersion: 1

datasources:
  - name: cloudwatch
    type: cloudwatch
    jsonData:
      authType: keys
      defaultRegion: ap-northeast-1
    secureJsonData:
      accessKey: $AWS_ACCESSKEY
      secretKey: $AWS_SECRETKEY

続いて、ダッシュボード設定用ymlファイルを用意します。optionsのpathはダッシュボードのjsonファイル置き場を指定しています。

  • cloudwatch-dashboard.yml
# # config file version
apiVersion: 1

providers:
 - name: 'cloudwatch'
   orgId: 1
   folder: ''
   folderUid: ''
   type: file
   options:
     path: /var/lib/grafana/dashboards #dashboard jsonファイル置き場

監視ダッシュボード用jsonファイルは、公式サイトからダウンロードしました。今回は、以下3つのダッシュボードjsonファイル(EC2,EBS,Billding)を用意しました。

  • amazon-ebs_rev1.json
  • amazon-ec2_rev1.json
  • aws-billing_rev13.json

公式のダッシュボードは、他にもLambdaやCloudwatchLogs、RDSがあります。ここはお好みのものをどうぞ。
grafana.com

Ansible-playbookの作成

RunCommandで使用するplaybookは以下のような構成にしました。playbookの中身は、grafana及びnginxのインストール、設定をするタスクを記載しています。これらをzipファイルにまとめて後ほどs3にアップロードします。

.
├── README.md
├── main-ansible.yml
└── roles
    ├── grafana
    │   ├── files
    │   │   ├── amazon-ebs_rev1.json
    │   │   ├── amazon-ec2_rev1.json
    │   │   ├── aws-billing_rev13.json
    │   │   ├── cloudwatch-dashboard.yml
    │   │   └── cloudwatch-datasource.yml
    │   ├── handlers
    │   │   └── main.yml
    │   ├── tasks
    │   │   └── main.yml
    │   └── templates
    │       └── grafana.ini.j2
    └── nginx
        ├── files
        │   └── grafana.conf
        ├── handlers
        │   └── main.yml
        └── tasks
            └── main.yml

10 directories, 13 files

使用したplaybookのサンプルをgithubにあげていますので、参考までに。
github.com

terraformで環境構築

ここまで準備ができたら、terraformで必要なものを構築していきます。
今回Grafanaサーバー用に構築したのは、以下の通りです。VPCやサブネットは既存のものを使用しました。

  • EC2インスタンス
  • セキュリティグループ
  • IAMロール
  • IAMポリシー
  • S3バケット
  • S3オブジェクトのアップロード
  • Route53のAレコード

EC2インスタンスに付与するIAMロールに関しては、RunCommandの実行やGrafanaがCloudwatchのメトリクスを取得できるように以下のIAMポリシーをアタッチしておく必要があります。

  • Cloudwatchのメトリクス取得
  • 作成したS3バケットのアクセス権限
  • AmazonEC2RoleforSSM (Amazon管理ポリシー)

IAMロールとアタッチしたポリシーの例は、github上に置いているので参考にしてみてください。
github.com


Terraformの実行は、GithubActionを使うとGithub上でterrraform initからvalidateやshow, applyまで完結するのでおすすめです。GithubActionで実施したplanやapply履歴もgithubで確認することができます。
github.com

Ansible-playbookの実行

Systems Manager > Run CommandからAWS公式コマンドドキュメントの「AWS-ApplyAnsiblePlaybook」を選択します。
コマンドのパラメータは、以下のように設定しました。Source InfoはS3バケットに保管しているオブジェクトURLを記載してください。

Source Type: S3
Source Info: {"path":"object-url"} 
Install Dependencies: True
Playbook File: grafana-ansible/main-ansible.yml
Extra Variables: SSM=True
Check: False
Verbose: -v

後はRunCommand先のインスタンスを選択し、ログが必要な場合はCloudwatchLogsまたはS3に出力するように設定して実行すればOKです。
成功すれば以下のように表示されます。

f:id:akngo22:20191222210405j:plain
RunCommand実行結果

S3にアップロードしておくだけで、ssh経由せずにAnsible-playbookを実行することができるのはかなり便利だと思いました。ただ、デバックオプションを使ってログ出力していてもplaybookのどのタスクで失敗したのか表示されずデバッグしづらかったので、事前にplyabookが想定通りに動作することを確認の上で使う必要があると思います。この点は、RunCommandの実行結果で見れるようになると良いですね。

Grafana確認

Ansible-playbookの実行が成功したら、Grafanaにログインしダッシュボードを見てみましょう。ダッシュボード一覧にプロビジョニングしたダッシュボードが表示されていることが確認できると思います。
Grafana6.5では、ワイルドカードを使えるようになったため動的なクエリに対応できるようになり、AutoScalingでEC2インスタンスが増減しても自動でダッシュボードに反映されるようになっています。

f:id:akngo22:20191222205423j:plain
EC2インスタンスのダッシュボード

また、グラフをクリックすると「View in Cloudwatch console」というCloudwatchコンソール画面に遷移するためのディープリンクがコンテキストメニューに追加されていることが確認できます。このリンクをクリックすれば、Cloudwatchコンソール画面に飛び、対象メトリクスを表示させることも可能となっています。

f:id:akngo22:20191222205024j:plain
ディープリンクが表示される
f:id:akngo22:20191222205106j:plain
Cloudwatchコンソール画面に遷移できる


まだ、発展途上な感じではありますが今後よりAWS Cloudwatchと親和性が高くなる予感がするので、さらに使いやすくなるのではと思います。

最後に

delyではSREを大募集しています!興味ある人は気軽にまずはオフィスに遊びにきてください!
www.wantedly.com

delyの開発部について知りたい方はこちらをご覧ください!
speakerdeck.com