TECH PLAY

スマートキャンプ株式会社

スマートキャンプ株式会社 の技術ブログ

226

こんにちは!スマートキャンプの21卒の内定者としてエンジニアインターンをしている関口です! 私はBOXIL開発チームに所属しており、現在BOXILのインフラ基盤をAWS EC2からAWS ECS/Fargateへ移行するプロジェクトを行っています。 この記事ではそのプロジェクトを通して得た技術的な知見を共有していきます。 移行前、移行後のインフラ基盤の紹介 ECSとFargateの概要 基盤構築の際に詰まったこととその解決策 Fargate運用することの課題 Fargateのコンテナへログインするための概要 具体的なコンテナログインのやり方 amazon-ssm-agent、AWS CLIのインストール アクティベーションの作成、コンテナのマネージドインスタンスへの登録、SSMエージェントの起動 おわりに 移行前、移行後のインフラ基盤の紹介 現在のBOXILのインフラ基盤はEC2で運用が行われています。 EC2インスタンスはOpsWorksで管理しているものの、自動でのスケールには対応しておらず、アクセスが急増した際などは手動での対応が必要です。 また、スケールが柔軟にできないためにサーバーのコストが最適化できず、サーバーの管理にもエンジニアの工数がかかっています。 今回の移行の一環としてECSでコンテナを運用することにしたため、スケールの柔軟性をあげることができます。 加えて、Fargateを利用することでEC2インスタンスの管理を手動で行う必要がなくなります。 そのためエンジニアがアプリケーション開発により集中できるようになります。 ECSとFargateの概要 ECSとはフルマネージド型のコンテナオーケストレーションサービスで,コンテナをスケーラブルに運用、管理できるサービスです。 ECSにはタスクとサービスという概念があります。 タスクとはECS上で実行するコンテナを定義したもので、1つのタスクの中にコンテナを複数束ねることもできます。 サービスとはそれぞれのタスクをいくつ起動するかを定義したものになります。 サービスを実行するためのインフラストラクチャの選択肢として、EC2とFargateを選ぶことができます。 FargateとはEC2インスタンスを管理することなくコンテナを実行できるサービスです。 ECSとFargateについての詳しい説明はAWSの公式ドキュメントをご覧ください。 docs.aws.amazon.com docs.aws.amazon.com 今回ECSに移行することによってオートスケールができるようになります。 これによって、アプリケーションのアクセスがあまりない平日の夜間や休日に起動するサーバーの台数を自動で減らすことができ、インフラコストを削減することができます。 基盤構築の際に詰まったこととその解決策 Fargate運用することの課題 ECSの移行作業を進めていく中で詰まったことの1つに、Fargateで動かされているコンテナにログインができないという問題があります。 2021年2月現在、公式でFargateで動かしているコンテナにログインする機能はありません。 コンテナにログインができないため、既存のEC2運用でサーバーへログインして行っているリリースバッチの実行や障害調査ができなくなる懸念があります。 そのためFargateで実行されているコンテナにログインする方法を模索しました。 Fargateのコンテナへログインするための概要 前述した通り、現時点で公式の機能としてFargateコンテナにログインする方法はありません。 その代替案としてSystems Managerの機能である Session Manager を使って実現することにしました。 本来、Session ManagerはAWSのアカウント内にあるインスタンスに対してアクセスすることができるサービスですが、 エージェントをインストールし、AWSのアカウント外で管理しているものに対して、アクティベーションを設定することでアクセスすることができるようになります。 ただし、AWSのアカウント外で管理しているものに対してエージェントを経由してログインすることは料金が発生します。 この仕組を使うことでFargateで動いているコンテナにもログインすることが可能になりますが、エージェントが起動している時間分、料金がかかってしまいます。 Session Managerを使いコンテナにログインする流れは以下のとおりです。 Systems Managerのエージェント(以下SSMエージェント)をFargateで動かすコンテナにインストール Systems Managerのアクティベーションを使ってコンテナをマネージドインスタンスに登録 コンテナ内でSSMエージェントを起動 マネージドインスタンスのコンソール画面やSession Managerのコンソール画面からセッションを開始 アクティベーションを使ったコンテナのマネージドインスタンスへの登録、SSMエージェントの起動はコンテナを立ち上げた際に実行されるエントリーポイントのシェルの中で行います。 アクティベーションはSystems Managerのコンソール画面で設定するかAWS CLIを通して作成することができます。 今回アクティベーションの作成は、以下の理由からコンテナをマネージドインスタンスに登録するタイミングで作成します。 アクティベーションには期限があるため 必要なときだけログインできるようにしたいため 管理者側でアクティベーションを取得更新したくないため アクティベーションはAWS CLIを通して利用するため、DockerfileでAWS CLIのインストールも行います。 またSSMエージェントのインストールもDockerfileで行います。 保険的に既存の運用と同じようにコンテナにログインできる仕組みはつくったものの、 コンテナの実行環境にログインすることはあまり好ましくないため、本番環境の運用では基本的にコンテナにはログインできない構成にしました。 そのため、コンテナのログイン可否については SSM-ACTIVATE という環境変数で管理することにしました。 具体的なコンテナログインのやり方 amazon-ssm-agent、AWS CLIのインストール まず、Fargateで実行されるコンテナのDockerfileにamazon-ssm-agentをインストールします。 agentのインストールの仕方はOSごとに異なっているため、OSごとのインストールの仕方は下記のドキュメントを参照してください。 docs.aws.amazon.com 今回Fargateで動かすコンテナはRubyのDockerイメージを利用しているため、そのイメージとあわせてサンプルコードはDebianにします。 #ssm-agentのインストール RUN wget https://s3.amazonaws.com/ec2-downloads-windows/SSMAgent/latest/debian_amd64/amazon-ssm-agent.deb && \ dpkg -i amazon-ssm-agent.deb && \ rm -f amazon-ssm-agent.deb && \ cp /etc/amazon/ssm/seelog.xml.template /etc/amazon/ssm/seelog.xml #AWS CLIのインストール RUN curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip" && \ unzip awscliv2.zip && \ ./aws/install && \ rm -rf ./aws && \ rm -f ./awscliv2.zip アクティベーションの作成、コンテナのマネージドインスタンスへの登録、SSMエージェントの起動 アクティベーションの作成、コンテナのマネージドインスタンスへの登録、SSMエージェントの起動はコンテナを立ち上げた際に実行されるエントリーポイントのシェルで行います。 シェルファイルは以下のようになります。 if [ "$SSM_ACTIVATE" = "true" ]; then  # アクティベーションの作成 ACTIVATE_PARAMETERS=$(aws ssm create-activation \ --default-instance-name "${SSM_INSTANCE_NAME}" \ --description "${SSM_INSTANCE_NAME}" \ --iam-role "service-role/AmazonEC2RunCommandRoleForManagedInstances" \ --region "ap-northeast-1") export ACTIVATE_CODE=$(echo $ACTIVATE_PARAMETERS | jq -r .ActivationCode) export ACTIVATE_ID=$(echo $ACTIVATE_PARAMETERS | jq -r .ActivationId) # コンテナのマネージドインスタンスへの登録 amazon-ssm-agent -register -code "${ACTIVATE_CODE}" -id "${ACTIVATE_ID}" -region "ap-northeast-1" -y # ssm-userからrootユーザーにスイッチするための権限付与 echo "ssm-user ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/ssm-agent-users # SSMエージェントの登録 nohup amazon-ssm-agent > /dev/null & fi アクティベーションを作成するコマンドのオプションでインスタンスの名前やIAMのRole、Regionの指定をします。 インスタンスの名前は SSM_INSTANCE_NAME という環境変数で管理しており、この環境変数の定義はECSのタスク定義でおこなっています。 デフォルトではSSMエージェントを起動した場合、コンテナにSSMユーザーとしてログインしますが、SSMユーザーは権限が限られているため、rootユーザーにスイッチできるように設定しています。 以上より、SSMエージェントが起動しました。マネージドインスタンスのコンソール画面かSession Managerのコンソール画面からセッションを開始することができ、Fargateで動かされているコンテナへのログインができるようになります。 おわりに 本番環境での運用は基本的にコンテナにログインはせず、 障害対応などの場合に限りコンテナにログインする方法をとっていく方針です。 既存のEC2運用でおこなっていることをコンテナで運用した際にも実現しようとしたところ、実現したいことをまとめている記事が少なく、調査に困ったため、今回の記事を書きました。 昨年の9月から行っていた移行プロジェクトも終わりを迎えつつあります。新しい基盤の負荷テストがおわり、いよいよ新しいインフラ基盤でBOXILが動こうとしています。 コンテナでBOXILが運用されていくことが楽しみで仕方ありません!
アバター
こんにちは、スマートキャンプのエンジニアの瀧川です。 私はこのブログではBigQuery大好き芸人としてよく記事を書いてますが、実は普段の業務では30%くらいの時間をエンジニア採用に割いております。 今日は弊社で実践している技術試験及び技術面接について、 どのような目的を持ってどのように設計したか、またそこにかける思い を書かせていただこうと思います。 弊社に興味を持っていただき選考を受けようとしている方はもちろんですが、自社で技術の見極めに課題がある採用担当者の方など にも参考になれば嬉しいです!       ※ 今回は中途採用に限って書いていきます。 ※ 弊社の採用フロー全体については別途エントリーがあるので、興味があれば見てみてください。 tech.smartcamp.co.jp 背景 現在の選考フロー 技術面接で知りたいこと いままでの課題 課題解決のために事前技術試験の導入 導入した技術試験の構成 知識を問うクイズ コーディングテスト 筆記テスト 試験結果を踏まえて技術面接 導入効果 まとめ 背景 現在の選考フロー こちらのエントリー にも書いてある通りですが、ざっくりと以下のようなフローを取ることが多いです。 (候補者の方によって期待値調整の面談を挟んだりといった調整が入ることがあります) カジュアル面談 技術面接 カルチャーマッチ面接 最終面接 「1. カジュアル面談」は候補者の方に弊社の事業・文化・技術などへの理解を深めて頂く事を主な目的としており、選考要素はありません。 「2. 技術面接」については後述しますが、候補者の方の技術力をはじめ、技術的な志向性やロジカルシンキングなどを見させていいただきます。 「3. カルチャーマッチ面接」は候補者の方が弊社のカルチャーにマッチするかどうかを判断する機会となっています。主に弊社の行動指針であるSOCS(Smart thinking, Ownership, Collaboration, Speed)を基準として判断させていただきます。 「4. 最終面接」は弊社のCEOと経営視点での期待値や事業の展望などについてお話しいただき、その中で人柄などを判断する機会となっています。 技術面接で知りたいこと 前述したフローを見てわかると思いますが、色んな視点で候補者の方のマッチ度を計っていくことになります。 ※ 候補者の方に負担がかかってしまう事や選考の途中で他社に決まってしまう事を減らすためにも、技術面はなるべく一回の面接で判断したいと考えています。 では候補者の方の技術面についてなにが知りたいのでしょうか? 前提として 採用のゴールは中長期的に活躍してもらえそうか 、というのが一番重要だと考えています。 その上で技術面を分解すると下のようになるかと思います。 今いるメンバーとの志向性のマッチ度 例) ビジネス志向があるか、技術がどれくらい好きか 現時点での活躍想定 例) どのタスクにアサインできるか 技術的にやりたい方向性と自社で実現できる(できそうな)ことのマッチ度 例) 難しいことをやりたいのか、新しいことがやりたいのか 中長期的に想定される技術課題への対応力 例) 過去に課題を乗り越えたか、未知の課題に対してどうアプローチするか 要約すると「切磋琢磨できるメンバーがいて、現状のスキルが活かせて、中長期的に成長できそう」という状況が自社と候補者両方にとって良い状態で、かつ技術的にマッチ度の高い状態だと考えています。 いままでの課題 最初は候補者の方の経歴から気になったことを聞くだけだったのですが、上記の視点で聞き漏れてしまうことがあったり、相手とのコミュニケーションの中で意図した質問をすること難しく属人化してしまうといった課題がありました。 これらを改善するために質問のテンプレートを用意し、そのテンプレートに沿って網羅的に質問することで知りたいことを把握しようとしました。 しかしその方法では網羅度は高まるものの一つ一つの話題を深堀りしきれず、志向性や課題への対応力などはあまり確認できていませんでした。 その後はテンプレートを改善したり、噂に聞くホワイトボード試験を導入してみましたが、どうしても その面接の時間内で「網羅的かつ志向性まで深ぼった質問を属人化せず実現」することは難しいという結論 になりました。 そこで次は 事前に技術試験を受けていただくことで上記の課題を解消しよう としました。 ※ ホワイトボード試験はその場で課題となるアプリケーションの仕様を説明して、設計や実装手順などを説明していただく形で実施しましたが、相手の得意な話に寄ってしまい聞きたいことが聞けなかったりと、今回の目的にはあわなかったのですぐに辞めてしまいました 課題解決のために事前技術試験の導入 改めて事前の 技術試験を導入することのメリットや期待値 は以下だと考えています。 候補者の方の志向性についても多く知ることができる 事前に推測した候補者の方の特性をもとに質問を準備できる どういった回答が来るのかも予測しやすいため、その回答に対してさらに質問も考えておける 面接の流れが型化されるので属人化や抜け漏れが解消される 技術試験というと点数をもとに合否を判断するイメージがありますが、弊社では上記のようなことを期待して導入してるため正答率などはほぼ見ていません。 余談ですが技術試験を導入するにあたって、試験問題の作成や候補者の方に提出して頂くためのフローと仕組みを作る必要がありました。 それらに少なからずコストがかかることが予想されましたが、 track を利用することで技術試験の導入や運用・管理のコストを大幅に下げることが出来ました。 trackは候補者の方がWeb上で技術試験が受けられるサービスとなっています。 出題側としては受験者の管理が容易でコーディング試験を解く過程まで見ることができたり、用意されている問題が豊富だったりといったメリットを感じています。 導入した技術試験の構成 何度か実践をしながら改善を行い、現在は以下のような設計で落ち着きました。 (改めて書いてみるとこれだけ長い時間を弊社のためにかけていただけているのは本当にありがたいことで、その分私たちもできるだけ候補者の方の懸念や期待に応えられるように気を引き締めないといけないと感じます) 知識を問うクイズ(最大15分) コーディングテスト(最大90分) 筆記テスト(どちらかを選択)(最大30分) インフラ系 アプリケーション系 次にそれぞれどういった意図で出題しているか、具体的な設問を考える上でのコツなどを説明します。 知識を問うクイズ こちらはWebアプリケーション開発に携わる上での知識を問う幅広いクイズを出題しています。 意図としては目的でも書いた通り、 候補者の方のスキルや経験を網羅的に把握するため に用意しています。 そのためこのクイズの問題を考える上で一番大切にしているのは、 面接時に話を広げやすいか、広げたときに効率的に情報を引き出せそうか という点になります。 例えばデータベースの設計に関する問題を出したら、面接時の質問では設計の経験の有無、パフォーマンスに対する知見、利用したことのあるデータベースの種類といった形で聞けると技術の幅がわかると考えています。 あとはあえて知っている人のほうが少ないような問題を選ぶこともあります。 例えばAWSのニッチなサービスの用途についての質問などですね。 その意図としては、回答の選択肢を、 サービスを知らなくても消去法で回答できるようにすることで、未知の課題に対してロジカルに対応できるか を見ることができると考えています。 回答が間違っていたとしても、面接時に回答の思考プロセスを確認して共感できればいいなと思っています。 コーディングテスト こちらはよくあるアルゴリズムを組むコーディングテストを採用しています。 とはいっても競プロで出題されるようなゴリゴリの問題ではなく、そこまで 難しくはないけど普通に書いていると複雑になってしまう ような問題を選ぶようにしています。 例えば色んな形式の入力値を変換して出力したり、ビジネスロジックとしてあり得るような問題がいいかなと考えています。 その意図としては、もちろん効率のよいコードが書けるというのは大事なエンジニアのスキルだと思いますが、チームでコードを書く上ではそれだけでは不足していると考えています。 例えば関数をどういった粒度で切り出しているのか、関数を切り出した意図は何なのか、関数の引数と返り値をどのように設計しているか、言語のどういった機能を使っているかなど特徴が出ると思います。 そこから、 候補者の方がどんな知識を持っているのか(オブジェクト指向や関数型)、どんな経験をしてきたのか(エラーハンドリングや設計思想)を推測できる と考えています。 たとえコードが正答でなかったとしても、上記の知識や経験が弊社で活かせるものであればマッチしているという判断になります。 もちろん試験は試験、チーム開発はチーム開発という思いで回答いただく方もいるので、そこのすり合わせは面接でするという形になります。 筆記テスト こちらはインフラ系かアプリケーション系の2つの問題を用意しており、どちらかを選択してご回答頂いています。 2つから選択いただいている理由としては時間が長くなりすぎるということもありますが、弊社ではインフラ専任といった役割の分け方をしておらず、募集する際も区別していないため、どちらに自信があるのかを見るためという側面もあります。 筆記テストを採用している意図としては、まず フォーマットを指定せず自由記述としているのである程度の性格や特性が把握できる 点があります。 例えば優先度などを明確にしているか、疑問点などが書いてあるか、複数パターンを考慮しているかなど人によって特徴が出るので、そこからその人が何を重要視して考えるかを推定できると考えています。 (今までの経歴によっても特徴は変わって、受託開発だとドキュメントも納品物の一つで...といったこともあるので、経歴を踏まえて見るのがよいと思います) その特徴が自分を含め今いるメンバーの志向性と近いか遠いかを判断して、面接時に確認するという流れになります。 また内容に具体性が欠けていた場合は、候補者の方にとって経験や知識がないシチュエーションだと考えられるため、未知の課題に対してロジカルに考えられているかに重点を置いて見るようにしています。 次に各問題について。 インフラ系について、特定のシチュエーション下での設計やインシデントへの対応などを回答いただくような問題にしています。 設問を考える上で意識していることは、実際に自社のサービスで起きたこと、もしくは起きうることを題材とするようにしています。 そうすることで 候補者の方がどれくらい自社のインフラ構成に馴染みがあるか、即活躍できるかなどが判断できる と考えています。 また、実際に起きたことであれば 自社メンバーがどのように対応したかといった情報もあるので、そことの差分も見る ことができます。 アプリケーション系についてもほぼ同様で、特定のシチュエーション下でのアーキテクチャ設計や実装方法を問う問題にし、実際のサービスに則した題材としています。 試験結果を踏まえて技術面接 以上のことを踏まえて、技術面接という形で直接話を聞き、事前に推測したことを元に判断していくことになります。 面接は60分~90分程度で実施し、前半40分程度技術試験からの質問、後半20~50分程度経歴についての質問といった配分 にしています。 導入効果 導入して半年ほど経ち、そこそこの人数の方に受けていただいたので(圧倒的感謝)、その中でどういった効果があったか、どう感じているかを書いていきます。 まずは当初課題に感じていた属人化や抜け漏れについてはかなり解消することができ、 面接を開発メンバーに任せたりとスケール できるようになりました。 また候補者の方についても今までは「技術力はおそらく高い」といった粗い認識しかできていなかったところが、 「技術の幅」「知識の深さ」「志向性」といった観点で分解して認識できるようになり、マッチ度の判断がしやすくなりました。 加えて候補者の方のマッチ度の高い点、マッチ度の低い点が明確になり、 候補者の方へのアトラクト(訴求)がしやすくなったり、次の面接への引き継ぎもしやすくなる効果 もありました。 別の視点で、開始する前は技術試験は受験者が増えれば、試験内容が流出することもあるだろうし、同じような問題を解いた方がいてもおかしくないということで、定期的に問題を差し替える必要があるかなというイメージがありました。 しかし、今回弊社で導入した目的を鑑みると、あくまで面接に主眼を置いているため差し替える必要性も薄いと考えています。 と、ここまで良かった点を書いたのですが、導入してから上に書いたようなメリットを感じられるようになるまではハードルがありました。 一つは、問題を考え、それを一定数の方に受験いただき、面接をして、その結果をもとに問題を改善するといったサイクルを回すコストが高いと感じています。 母数がそこまで多くなく、実際の候補者の方で上を実施していく必要があるので、あまりよくない問題を出してしまうと面接も進めにくくなりますし、候補者の方も回答しにくく悪い印象をもたれる懸念もありました。 そのため最初から完成度の高い問題を出す必要があり、そこのコストは高かったです。 ただ一度しっかりとした問題を考えることができれば、 その後の運用は安定するので、とてもメリットの多い施策 だったと感じています。 まとめ 今回弊社で導入した技術試験について、どういった思いで実施しているのか、どういったことを考えて設計しているのかを書かせていただきました。 もし読んでくださった方の中で、弊社に興味を持っている方がいれば、この記事を参考にして私達が何を重要視しているかなどを知っていただけたなら嬉しく思います。 また採用に携わって似たような課題をお持ちの方がいらっしゃいましたら、ぜひ少しでも参考になれば幸いです! We are hiring! smartcamp.co.jp
アバター
はじめに スマートキャンプでPMをしている郷田です。 唐突ですが、みなさんは障害対応の経験はありますか? どのようなシステムを運用していても、発生したエラーをキャッチし対応を行うことはよくあることかと思います。 弊社では新卒1年目のエンジニアが担当プロダクトにおけるエラー対応を運用ルール化する旗振りを行っていたのですが、ルールを決定するための進め方に課題を感じた部分がありました。 そこで、自分が「仮説検証」の考え方を取り入れてアドバイスする機会があったので、この記事では実際の運用ルール決定プロセスを題材にそのアドバイス内容などを紹介したいと思います。 ※ 前述の新卒1年目のエンジニアには今回記事化するにあたって了承をとっています。ありがとうございます! はじめに 作られた運用ルール プロセスの課題 仮説検証型アジャイル開発について 実際にアドバイスした内容 本人からのコメント 最後に 宣伝 作られた運用ルール 弊社プロダクトでは、システムのエラー発生時に、その内容が1つのslackチャンネルにリアルタイムで通知されます。 過去のエンジニアが少なかった時代には、発生したシステムエラーの対応をそのエラーの内容にあわせて得意な人が自主的に行っていました。 ですが、現在エンジニアが9人(4チーム)で1つのプロダクト開発を行うようになったところ、以下の問題が起きるようになりました。 システムエラーへの反応や対処が有志で行われるため、誰も対応せず見逃してしまうエラーがあった。 複数のサブチームに分かれて開発しているため、他チームの担当内容は重要度が判断できなかった(ので巻き取ることができなかった)。 そこで、この問題を解決するために当初作られたルールは、いくつかアプローチ案があったうえで、採用案を決めたようでした。 以下がそのアプローチ案と採用案です。 ## アプローチ案 1. 反応をする担当を1人決める - メリット:分担が明確 - デメリット:担当の負担が膨大 2. 反応をする担当を複数人決める - メリット:担当の負担がそこまで大きくない - デメリット:誰も反応しない可能性がある 3. 反応をする担当を日替わりで1人決める - メリット:担当の負担が小さい、分担が明確 - デメリット:担当が今日自分である事に気付かない可能性がある ## エラー対応ルール - エラーの対応者は日替わりで1人とする(正社員のみ、祝日含む) - Slack botで担当者を告知 & 担当者にメンションが飛ぶ プロセスの課題 このルールは起案した新卒1年目のエンジニア1人によって作られ、他のチームメンバーにも確認して同意が取れたと判断したため、ルールの決定がされていました。 しかし、決定後にチームメンバー複数人から以下のようなコメントがありました。 - botでの担当告知はランダム or 何か決まりがある? - 担当になった人が休みだった場合はどういう運用になるの? - 休みの人がいる場合botだと実現できない気がするけど大丈夫? - 差込で至急修正しなきゃいけないような内容とかが飛んできた場合って回答担当のタスクに差し込まれる感じですか? - 日替わり担当制は良いと思うけど、どこまでが担当範囲になる? - 一次切り分けが担当者の責務?内容によってみんなに調査お願いするみたいな感じ? - メイン担当が見れない場合に誰も見なかったら初動遅くなるかも? コメントから見て取れるように、運用ルールの課題感や背景が伝わっていなかったり、担当者は何をすればいいのかがわからなかったりする問題が起きていました。 そこで、複数人の関わる「運用ルール」=「プロダクト」と見立て、仮説検証型アジャイル開発に当てはめた業務の進め方をアドバイスしました。 仮説検証型アジャイル開発について 「仮説検証型アジャイル開発」とは、不確実性が高いなかで「正しいものを正しく」作っていくために、ギルドワークス株式会社 代表取締役の市谷さんが提唱しているモデルになります。 今回メンバーへの説明として参考にさせていただいた記事は以下になります codezine.jp このモデルを用いた理由は、実開発の前に仮説検証のサイクルが存在していることが図解にてわかりやすいと考えたためです。 実際にアドバイスした内容を以下に共有していきます。 実際にアドバイスした内容 こんにちは!郷田です。 直近の課題を感じている各slackチャンネルでの運用についてルール決めと意見だしが色々動いてて良いですね! 一方でJさん(※新卒1年目エンジニア)の考えてくれた取り決めに対して、運用するチームの皆さんからも「そもそも論」を含む質問や意見が出ているように見えました。 チームで運用するための取り決めが、もしかしたらJさんによる「ぼくの考えた最強の運用ルール」で決定されてるのではないでしょうか・・・? 上記の状態になっていると仮置きして、プロダクトマネジメントの観点からコメントをしてみようと思います。 プロダクトマネジメントを抽象的に捉えると、「チームの運用ルール」というプロダクトを作ると言い換えられます。 まず、プロダクトマネジメントでは、実開発の前には必ず「仮説検証ループ」というのが存在してます。 以下の図でいう「仮説探索(正しいものを探す)」にそれが該当します。 error対応チャンネルの現状の課題として書かれている以下は、このなかの「仮説立案」に当たると思います。 現状の課題投稿に対する反応および対処が有志によるものなので見逃してしまうエラーがある・投稿数が膨大なので有志の負担が大きい また、これらの仮説に対するアプローチの候補は「MVP特定」に該当すると思います。 課題に対するアプローチの候補 「投稿に対する反応および対処が有志によるもの」返事をする担当を1人決める返事をする そして、今後取るアプローチは「アジャイル開発の作るサイクル(右側のサイクル)」の話に該当すると思います。 対応ルール - エラーの対応者は日替わりで1人とする (正社員のみ、祝日含む) - Slack botで担当者を告知 & 担当者にメンションが飛ぶ 今回「仮説立案」〜「MVP特定」〜「アジャイル開発の作るサイクル」まで、すべてJさん1人だけで完成されてしまっており、「仮説探索(正しいものを探す)」サイクルがチームで1度も回らずに「つくる」段階まで来ていると思います。 つまり、「正しいものを探さずに、つくる物を決めて作っている状況」です。 よって決定したルールに対して多くの疑問がでてしまったのでは無いか?と推測することができます。 「Jさん1人だけの運用ルール」であれば影響範囲は自分だけなので1人で決定しても良いのですが、今回のプロダクトは「チームの運用ルール」です。そのため、仮説検証をチームで回すことが不確実性(ルールを作るリリースをした後に、ルールが変わる結果になったこと)を無くす最短ルートだと思います。 これが実際のプロダクト開発では、開発した後に要求が違うことがわかり手戻りが大きく発生するので「大きくコストをかけたが良いものを作れなかった。」という非常に残念なことになったりします。 ... とはいいつつ、今回作っているのはルールなので手戻りコストは低いです。 そこで、私の場合どのように進めるかを図に合わせた形で紹介してみたいと思います。 まず、以下画像のように仮説検証を段階に分けることができます。 今回はシステムを開発するわけではないので、「目的選択の段階」「実態選択の段階」「手段選択の段階」まで最初からやっちゃいます。 (普通のプロダクト開発でも、コストの低い「文言修正」などは一気にやっちゃうことはあります。) ## 現状の課題 1. システムエラーへの反応や対処が有志で行われるため、誰も対応せず見逃してしまうエラーがあった 2. 複数のサブチームに分かれて開発しているため、他チームの担当内容は重要度が判断できなかった(ので巻き取ることができなかった)。 ## 課題に対するアプローチの候補 - 返事をする担当を1人決める - 返事をする担当を複数人決める - 返事をする担当を日替わりで1人決める ## 今後取るアプローチで郷田が良いと思っている案 - エラーの対応者は日替わりで1人とする (正社員のみ、祝日含む) - Slack botで担当者を告知 & 担当者にメンションが飛ぶ ここで重要なことは、「今後取るアプローチ」は、自分の案であるとしていることです。 今後取るアプローチで郷田が良いと思っている案 このタイミングで作ったルールを使うことになるエンジニアの皆さんに一度意見をもらうことで、使う人が考える問題など洗い出すことができます。 例) 日替わり担当制は良いと思うけど、どこまでが担当範囲になる? 初動を誰がキャッチするかは明確になるけど、その後の対応フローが曖昧な現状だと以下のような懸念点ありそう 一次切り分けが担当者の責務?内容によってみんなに調査お願いするみたいな感じ? これにより、皆さんの疑問を元に、もう少し本質的なディスカッションをすることができます。 考えた例) 郷田: そもそもエラー対応の初動レスポンスが遅くなってしまうことで、開発チームの誰が拾ってるかもわからないのが問題だと思っているのですが、このあたり皆さん問題に感じてたりしますか? 他の方: たしかにそれはありそう。だとすると解決策としては当番制にするって話よりも、見た人は誰でもいいからまずは投稿を見たってことがわかるように「👀 」のリアクションだけつけるのはどう?その後対応できる人はスレッドにコメントを残してく感じ! そうすればまずは投稿する側も、対応する側も見てるか見てないかだけでもわかったりすると思う。 あとは、errorの無視していいものは「🤗」リアクションをつけるとかでも良いかも。 また、実際の対応は一次切り分けもあるので、超緊急だと思う人がいない限りは翌日の朝会で取り上げるというルールにするのはどうですか? 郷田: 意見ありがとうございます!確かに、その解決策良いですね!他の皆さんも同じ意見であれば、Goodリアクションもらませんか? 👍 + 5 最後にこれらを元に「目的選択」「実態選択」「手段選択」を完了したとして、「順序選択」に移ることができます。 郷田: この先のTODOは以下で考えてます! - [ ] この記事を皆さんに認識してもらえているか、朝会で議題にだす - [ ] 実際にリアクションするのを1週間試してみる - [ ] 運用に問題があればレトロスペクティブ(KPT)で問題を感じた人がProblemとして書く こうやって上記のように「正しいものを探す」フェーズを1サイクル回すだけでも、本質的な課題とそれに対する解決策が、チーム全員で話しながら進めることができるのではないでしょうか? 本人からのコメント 丁寧なご助言ありがとうございます…😭 確かにサイクル回さずに決めきってしまったなと反省しました🙇 いただいた内容を元に、どうするべきだったか振り返ってみたいと思います! 改めてありがとうございました! 最後に 新卒1年目のエンジニアが問題解決をしたいため自分から起案して動いた内容なので、素晴らしい活動だと感じています。 しかしうまく成果につながらないことは非常にもったいないので、今回コメントをさせていただきました。 今回は運用ルールを1つのプロダクトととして捉えて、仮説検証を回す考え方をご紹介しました。 運用ルールに限らず普段の業務の多くにも転用できると思いますので、みなさんもぜひ使ってみてください。 宣伝 私は今回話したような仮説検証を日々担当するSaaSプロダクトで行っています。 そのノウハウの一部を自社開催のイベントにて共有させていただきますので、ご興味のある方はご参加ください! 【2月25日開催】B2B SaaSエンジニアMeetup - SharingIssues Online #2 仮説検証 smartcamp.connpass.com
アバター
こんにちは!!! スマートキャンプでエンジニアをしている吉永です! 去年の8月に入社し、BOXILのフロントエンド開発に主に関わっています。 自己紹介記事&前回の記事はこちら 弊社の主力サービスであるBOXILは、リリースしてから既に何年も経っているということもあり全てが最新という訳ではなく、インフラからフロントまで様々な技術的負債を抱えています。 フロントエンドでは古いライブラリを使ってしまっているケースや、UIライブラリに依存してしまっているというケースが挙げられます。 他には、CoffeeScriptの中でnew Vue...としてVueを動かしている部分もあり、可読性や保守性に大きな弊害をもたらしてしまっている状況です。 昨年Vue 3がリリースされましたが、各種ライブラリの対応はまだ追いついていない部分も多く、「Vueのバージョンを2系から3にあげたいけど、主要なライブラリがいまだ対応していないからVue 3にあげることができない」といったケースが多くあるかと思います。 立ち上がったばかりのサービスとは違い、どうしても古いコードを全てをVue 3に対応した形式に作り直すというのは莫大なコストと手間がかかってしまうのではないかと考えました。 そこで今回、Vue 2.xの部分を全て書き直すのではなく、Vue 2.xと3を共存させる形でプロジェクトを管理できないかと考え、その第一歩としてVue 3をVue 2.xと一緒のページでレンダリングしてみようと思い、試してみました。 注: 本記事は失敗したという内容とそこで得た学びが書かれています。成功した話ではないのでもし解決方法を知っている方がいればSNSやはてブコメントにて教えていただけると幸いです。 すでに存在した事例 やったこと 結果発表 Vue2のCDNや、vue.min.jsと同じような感じでVue3を持ってくる方法(失敗) npm install vue@next的なのを使って上手くVue2とVue3をどちらも入れる方法 まとめ 追記(2021/2/8) 何が違ったのか すでに存在した事例 最初に、既に同じような事例が存在しないかどうかを調べてみました。 その結果 プロジェクトをVue 3に対応できる状態にした後、バージョンを上げる 先にバージョンを上げてしまい、大量に出たエラーを修正する Vue 2.xからVue 3にあげた系の記事では、主にこの2つに分類される例しか見つかりませんでした(自分調べ) やったこと 前述した通り、走り出してからかなり経っているプロジェクトに上記の実例と同じ方法を取るのはかなり危険な上に膨大な工数を必要とします。 よって1, 2の方法とはまた違う、共存できそうな方法を2つ挙げてみました。 Vue 2.xのCDNや、vue.min.jsと同じような感じでVue 3を持ってくる方法 npm install vue@next的なのを使って上手くVue 2.xとVue 3をどちらも入れる方法 今回はその検証用に、以下のVue 2.xのプロジェクトを用意しました。 3,4どちらの方法でも、共通でmain.jsに記述して最終的にVue2とVue3のコンポーネントが同じページに描画できれば成功とします。 そして、Vue 3のレンダリング用タグとして、IDをappV3としたタグをindex.htmlに設置しました。 結果発表 Vue2のCDNや、vue.min.jsと同じような感じでVue3を持ってくる方法(失敗) CDNで提供されている Vue 3 をローカルに落としてきて、new Vue3...的な方法で行けるのではないかと思い試してみました。 import Vue from 'vue' import App from './App.vue' import { Vue3 } from './vue3' import AppVue3 from './AppVue3.vue' Vue.config.productionTip = false new Vue( { render: h => h(App), } ).$mount( '#app' ) Vue3.createApp(AppVue3).mount( '#appV3' ) こんな感じで、Vue 3のjsファイルをimportしてきて、createApp(Vue 2.xだとnew Vue)します。 この状態で実行してみると以下のようなエラーが出ました。 まず、Vueファイルが正常に呼び出されているのか、処理ができていないのはレンダリングだけなのかなどを調べるべく、main.jsのコードを以下のように変更しました。 import Vue from 'vue' import App from './App.vue' import { Vue3 } from './vue3' Vue.config.productionTip = false new Vue( { render: h => h(App), } ).$mount( '#app' ) const testAppVue3 = { render() { return "HelloVue3" } , created() { console.log( "HelloVue3" ) } } Vue3.createApp(testAppVue3).mount( '#appV3' ) importしたVueファイルをマウントする形ではなく、オブジェクトを作りその中でcreatedと、正当な使い方ではないですが確認のためrenderに文字列を渡し、走らせてみました。 すると... HelloVue3が正常に出力されていることがわかります。 画面にも正常に表示されているため、Vue 3としては正常に呼び出せていそうということがわかりました。 しかし、同じようにrenderでcomponentをreturnしたり、templateで頑張って描画しようとすると上記の画像と同じエラーが出てしまうため、templateを変換するときにVue 2.xの処理とバッティングしてしまっているのではないかと考えました。 npm install vue@next的なのを使って上手くVue2とVue3をどちらも入れる方法 Vue3のチュートリアル にもあるように、npm install vue@nextを使うと現在の最新安定版のVue 3をインストールすることができます。 ですが、このままインストールしてしまうと、既存のVue 2.xの記述も3に上書きされてしまい動かないので、package.jsonをこのように修正します。 無理やりvue3と名前をつけてやることでインストールしたい考えでしたが、 No valid versions available for vue3 というエラーが出てインストールに失敗してしまいます。 調べてみると " dependencies ": { " core-js ": " ^3.8.1 ", " vue ": " ^2.6.11 ", " vue3 ": " npm:vue@^3.0.0 " } , という形でpackage.jsonに記述すると「名前を分けてバージョンごとにインストール出来るらしい」という情報を見つけ試した結果、正常にインストールすることができました。 それではmain.jsを編集していきます。 まずは先ほどの実装でできていた、objectを渡す方法から試してみましょう。 import Vue from 'vue' import App from './App.vue' import { createApp } from 'vue3' ; Vue.config.productionTip = false new Vue( { render: h => h(App), } ).$mount( '#app' ) const testAppVue3 = { render() { return 'HelloVue3' } , created() { console.log( 'HelloVue3' ) } } createApp(testAppVue3).mount( '#appV3' ) 結果は やはり、 VueファイルをimportしないのであればVue 3は動く。 ということがわかります。 ですが、やりたいことはVue 2.xと共存させて動かしたいということなので、先ほどと同じようにVueファイルをimportしてcreateAppすると... ダメみたい。 まとめ 結果として、3,4どちらの方法でもcreatedやdataなど画面のレンダリングに関係ない部分の処理であれば動かすことができました。 が、レンダリングが関わってくるとどうやっても上記のエラーが出てしまうため、時間の関係もあり一旦ここで調査終了となりました。無念... Vue 2.xだけど一部だけcompositionAPIを使ってみたいみたいな要望だったり、ここだけVue 3で処理したいみたいなのであれば実装できる...かもしれないという結果に落ち着くのかなあと感じました。 更にcompositionAPIを使いたいだけであれば、既に それ用のライブラリ が存在するため、わざわざ今回の方法で実装する必要はないかな...といった感じです。 よって、共存できないと仮定すると 「全てVue 3にあげた上で工数をかけてエラーを少しづつ消していく」 、というのが今のところの最適解なのではないかなと思います。 やり残したこととしては、なぜselfHook.bind is not a functionのエラーが出てしまうのか、他に共存した上で少しずつVue 3に上げていく方法を模索するなどがあると思っており、次回のテックブログなどで続編が出ることを期待していただければという形で、本記事を締め括らせていただきたいと思います。 追記(2021/2/8) 本記事を見て、Vue2.xとVue3の共存はできないのかという検証をしていただいた方がいました。 - その方の記事はこちらから その記事中でなんと、上記で掲げていたエラーを解決してくださっており、私の環境でも再現できるか試してみたいと思います! 何が違ったのか 基本的なコードに大きな違いはないですが、成功している方ではwebpack.config.jsにてVueのエイリアス設定をしている部分がありました。 なので、vue.config.jsを作成しその中に以下のコードを追記します。 module.exports = { configureWebpack: { resolve: { alias: { 'vue$' : 'vue/dist/vue.common.dev.js' , 'vue3$' : 'vue3/dist/vue.esm-browser.js' , } } } } ついでにmain.jsのVue3実行部分も少し変更して... const testAppVue3 = { template: `<p>Hello {{ message }}</p>` , data() { return { message: 'Vue3!' } } , created() { console.log( 'HelloVue3' ) } } Vue3.createApp(testAppVue3).mount( '#appV3' ) そして、そのまま実行してみると 動いてる!!!!! ではこれを、.vueファイルをimportする形で試してみると import Vue from 'vue' import App from './App.vue' import * as Vue3 from 'vue3' ; import Vue3Component from "./AppVue3.vue" Vue.config.productionTip = false new Vue( { render: h => h(App), } ).$mount( '#app' ) Vue3.createApp(Vue3Component).mount( '#appV3' ) なるほど、これは失敗してしまう。 import時にVue3ファイルとして読み込んで欲しいところをVue2.xとして読み込んじゃっているのかなという感じもしますが、とにかく一歩前進しました。次回記事までにこの辺を解決できたらなと思います! 最後になりますが、記事を読んでくださっただけではなく、そこから追加検証して頂き、さらにそれを記事として載せていただいたことに深く感謝しております。本当にありがとうございました!
アバター
こんにちは!スマートキャンプでエンジニアインターンをしている中田です。 昨年の11月からインターンを始め、 BOXIL の開発に携わっています。 BOXIL開発チームでは、毎月一度「薪入れ」と称した開発改善の日を設けています。 薪入れは、普段の業務で後回しになってしまっている箇所のリファクタリングなど、技術的な改善をしていくことを目的に実施しています。 昨年度末には、その薪入れの拡張版として、いつもより長めの12/28~12/30の3日間をかけて年末開発改善を実施しました。 年末開発改善で取り組んだ内容は以下のとおりです。 スクラム管理ツールの内製化 Cloudflareの調査 CoffeeScript->TypeScriptへの置き換え など 本記事では上記の年末開発改善での取り組みの中から、僕を含め入社1年未満のメンバー3人で取り組んだ、「スクラム管理ツールの内製化」での実施内容について紹介します。 スクラム管理ツールの内製化 目的 弊社の開発チームでは、開発手法としてスクラム開発を採用しています。 ※ スクラムの定義や用語については スクラムガイド をご参照ください。 これまで弊社では、スクラムの各スプリントの稼働予定やポイント消化率などをスプレッドシートへ手動でマッピングして管理していました。(下図) スプリント情報管理シート(各チーム) 各チームのスプリント情報を統合したマスタシート ※ 図のデータにはダミーを入れてます このようなスプレッドシートを使用した管理方法には、以下のような問題点を感じていました。 過去データの整理が煩雑な点 分析用として過去のスプリントのポイント消化数をグラフ化しているものの、細やかな分析がしづらい点 Asanaでタスクと消化ポイントの管理をしているが、 スプレッドシートへポイントを別途入力しなければならない点 上記の問題点を改善するべく、新たにツールを内製化することにしました。 仕様 先述の問題点を踏まえ、内製化するツールの仕様を検討しました。 # --- ユーザー管理・設定機能 --- ## ユーザー管理 - 登録 - ログイン - アドミン権限 - ユーザーのグルーピング - スプリント期間の変更 ## 設定 - ユーザー設定 - 名前 - 表示設定 # --- メインの表示・入力機能 --- ## スプリント (入力) - 予想稼働時間(手動入力 or GoogleCalendarから取得?) - 実稼働時間(手動入力) - 予定消化ポイント(手動入力 or Asanaから取得) - 実消化ポイント(手動入力 or Asanaから取得) - 稼働阻害要因(手動入力) # --- 分析機能 -- - 各スプリントでのサマリ(実消化ポイント, 実稼働時間) ↑ を全体・チーム・個人でフィルタして表示 - グラフ化(実消化ポイントの変遷, 実稼働時間の変遷) - 予測(稼働時間から予測する消化ポイント) スプレッドシートでの管理・運用方法をベースに、入力の煩雑さを減らし、分析機能をリッチにしたツールを目指します。 ※ 実装にあたるのがメンバー3人なので、3日間で出来ても設定・入力機能までだろうという目論見でした。分析機能などは後々補填していく想定です 実装 DB構造 まず、決定した仕様をもとにDB構造を検討しました。 DB構造 各テーブルはそれぞれ、以下の役割を担います。 - Companies:企業の情報を持つ - Projects:プロジェクトの情報を持つ - Teams:チームの情報を持つ - Users:ユーザーの情報を持つ - TeamUsers:Teams - Usersの中間テーブル(ユーザーの複数チームを跨いでの所属を考慮) - Sprints:プロジェクト単位のスプリント情報を持つ - TeamSprintHistories:チーム単位のスプリント情報を持つ - UserSprintHistories:ユーザー単位のスプリント情報を持つ アプリケーションの実装 次いで、アプリケーションの実装に入りました。 アプリケーションの技術構成は以下の通りです。SPAで開発しました。 フロント:Vue.js(HTTPライブラリ: Axios, UIライブラリ: Element UI, Router: Vue Router) 選定理由:弊社の既存サービスで利用していることもあり、素早く実装できそうだったため。 APIサーバー:Golang(Framework: Gin, ORM: Gorm) 選定理由:会社でも今後積極的に採用していこうとしている言語であり、個人的にも最近学習していた言語だったため。 また、以下の役割分担で開発を行いました。 フロントエンド:2名(設定画面の人とスプリント管理画面の人) バックエンド:1名 結果 1日目はほとんど環境構築で終わり、2日目から機能の実装に入りました。 最終的な進捗は以下の通りです。 フロントエンド ログイン画面 プロジェクト選択画面 プロジェクトの設定画面 スプリント管理画面 バックエンド モデリング JWTを用いた認証機能の実装 プロジェクト設定関連のAPIの作成 フロントエンドは 認証/プロジェクト設定/スプリント管理 周りの開発を終え、バックエンドは 認証機能/プロジェクト設定 周りのAPIの実装ができました。スプリント管理周りのAPIの作成・繋げ込みには至らず、少々物足らずな進捗となりました。 今後 まずは、今回の年末開発改善内で実装が終わらなかったスプリント周りのAPIの実装を進めていきたいです。ここまで終えると、一旦スプレッドシートでの管理と同様のことが可能になる想定です。 その後、実際にアプリケーションを運用してスプリントのデータを貯めていきつつ、機能的にリッチにしたい部分(分析周り、Asana/Google Calenderを連携して自動入力)の開発や、スプレッドシートで持ってる既存データの移行などを進めていきたいと考えています。 おわりに 今回は年末開発改善で取り組んだ「スクラム管理ツール内製化の試み」について紹介しました! 普段業務で使用していないGolangでの実装など挑戦的なこともしており、個人的にも良い経験となりました。 以前の管理方法での問題点の解決に加えて、色々と痒いところに手が届いたツールとして機能させたいので、今後もしっかり開発したいと思います! 参考 https://www.scrumguides.org/docs/scrumguide/v1/Scrum-Guide-JA.pdf
アバター
こんにちは、スマートキャンプの徳田です。 みなさんの会社ではテックブログやってますか? findy-enterprise.connpass.com 昨年12月16日に、「 エンジニア採用の最前線 メルペイとスマートキャンプが語る!採用につなげるテックブログ運営の秘訣 」というオンラインイベントが開催されました。 メルペイ社と弊社スマートキャンプのブログ運営担当が登壇し、それぞれブログをどういう考えで運営し、どう執筆者と連携を取っているかをディスカッション形式で話していました。 本記事はイベントレポートとして、トピックと話していた内容をまとめたものとなります。 登壇ゲストについて 株式会社メルペイ 安藤喜子さん スマートキャンプ株式会社 中川 眞誠さん 「テックブログの立ち上げの背景、社内での位置付けは?」 メルペイ 安藤さん: Q: 「どれぐらい採用目的を意識していますか?」 スマートキャンプ 中川さん: 「ブログの運営体制、役割分担、記事発信までのフロー」 メルペイ 安藤さん: Q: 「なんでWordPress?」 Q: 「スマキャンはエンジニア全員がブログを書いているけど、メルペイはどれぐらいの人が書いているの?」 スマートキャンプ 中川さん: Q:「担当制だと書かない人とか出てこない?」 Q: 「書いてもらうための取り組みは何かある?」 「採用への貢献はあったか」 スマートキャンプ 中川さん: メルペイ 安藤さん: 松岡さん(モデレータ。Findy) 「執筆者のモチベーションの維持をする方法」 メルペイ 安藤さん: スマートキャンプ 中川さん: 二人への質問 Q: 法務に関するレビューはしている? Q: 業務時間は使って良い? Q: 執筆者は1記事にどれぐらいの時間をかけていますか? 「これからブログを初める人たちに向けて」 メルペイ 安藤さん: スマートキャンプ 中川さん: まとめ 関連する記事 登壇ゲストについて 株式会社メルペイ 安藤喜子さん メルペイ Engineering Office Team 所属。2018年にメルカリに入社し、一年前ぐらいからメルペイに。 メルペイ社外発信の強化や社内組織活動などが役割。 社内の『社外発信は正だ』という雰囲気、文化がとても良いと思っている。 engineering.mercari.com スマートキャンプ株式会社 中川 眞誠さん スマートキャンプ株式会社 ソフトウェアエンジニア。2019年入社。前職はSIer。 一年前(2019年12月辺り)からTechBlog担当に。 tech.smartcamp.co.jp ここからは、イベント中にお話されていた内容のメモとなります。 「テックブログの立ち上げの背景、社内での位置付けは?」 メルペイ 安藤さん: 立ち上げ背景 当時のメルカリCTO 柄沢さんが2015年くらいに立ち上げ。勢いでスタート。 柄沢さん「技術力がある、強い人が多い組織はテックブログをしている」 これまでGREE、株式会社クロコスでのブログ立ち上げも経験がある方 今も続けている理由 アウトプットは正義だから。 技術力つよつよのアウトプットも良いが、アウトプットすること自体良いこと そういうカルチャーを作ることが大事で、立ち上げた柄沢さんが退職した後もその文化が残っている アウトプットすると自分たちの学びになるし、世の中のためにもなる Q: 「どれぐらい採用目的を意識していますか?」 どれぐらい意味あるんだろう?と思って直近半年入社した人にブログ見てるか聞いたら全員見ていた すごい読んでいた、ためになりました というポジティブな意見や、 マシンラーニングといった内容は最近までちゃんとアウトプットしていなかったので、「タグが無いんだ...」という意見も聞いた アトラクトの意味はあるのかなと思っている (※ アトラクト...自社に興味を持ってもらい、内定承諾をしてもらうための活動) スマートキャンプ 中川さん: 立ち上げ背景 安藤さんと同じく、立ち上げを自分がやったわけではなく、立ち上げた前任者から引き継いで担当している。以下は前任者から聞いた内容。 エンジニアブログやっている会社は技術に理解がありそう、エンジニアが中で活躍していそうなイメージがあった そもそも個人で技術情報を発信できているエンジニアが社内には少なく、それを増やしていける土台にすると個人のためにもなりそう 1〜2年前は採用活動でかなり苦労していて、採用が進んだ後に「他社のほうが成長できそうだから」という理由で自体されることが多かった toBのビジネスなのでアプローチが難しく、どういったことをしているのかを伝えられるようにブログを始めた 採用目的 メルペイ社と同じくアトラクトの狙いが大きい。認知を増やすという狙いもあるが、どちらかと言うとアトラクトのほうが重要。 「ブログの運営体制、役割分担、記事発信までのフロー」 メルペイ 安藤さん: 役割分担 ブログそのものの開発担当 WordPressを使っていて、現在は書き手からのWordPress改善リクエストに答えている ブログの推進担当 (安藤さん) 執筆頻度、技術領域のアウトプットの偏りなどを見て、少ない領域を書いてもらえるように社内で調整したりする アドベントカレンダー、夏の特集記事といった企画のリード 執筆する上でのガイドラインを作ったりもする レビュワー 記事の質を高めるために、書き手のチームメンバや社内の有志が担ってくれている。 書き手 ブログを書いてくれる人。 締切の期限といったものは設けていない。クォータごとにアウトプットしたい領域がある場合は、それまでに書いてほしいといったことはある。 公開の3日前までにレビュワーにレビュー依頼を出してもらい、修正してもらう Q: 「なんでWordPress?」 今年の7月ぐらいまでははてなブログを使っていたが、テックブログ以外のカルチャーといったものも含めた大きなWebサイトを作ることにしたため engineering.mercari.com Q: 「スマキャンはエンジニア全員がブログを書いているけど、メルペイはどれぐらいの人が書いているの?」 2〜3割ぐらい...? 2020年8月にやったブログ連投企画では、そこで初めて書いた人が12人ぐらいいた。書いたことがない人は結構いる スマートキャンプ 中川さん: 執筆を担当する人 社内にエンジニア・マネージャが合わせて12人ぐらい居て、それぞれ3ヶ月に1回ぐらいは書く 持ち回り制でやっていて、定期的に担当が回ってくる 運営は二人 はてなブログで運営している理由はよくわからない。エンジニアブログははてなブログで運営されているイメージがあるから? Q:「担当制だと書かない人とか出てこない?」 今のところ逃げる人は幸い居ない みんなの協力によって成り立っている Q: 「書いてもらうための取り組みは何かある?」 ハードルを下げるための取り組みとして、運営メンバの週次MTGに執筆予定者を呼んで、テーマの相談をしたり、アウトラインをその場で決めたりしている。 最近始めた良かった取り組み。とっかかりとして書きやすくなる 「採用への貢献はあったか」 スマートキャンプ 中川さん: 定量的な成果は出していないが、新卒・中途ともに認知の向上は実感している 中途採用ではブログを見て会社を知ってくれた人がいた 2年前にブログを作って、ペースを維持しつつ途切れずに更新できている組織だということを評価してくれることもある 始めた理由の一つでもある、技術的に成長できなさそうで辞退されるようなケースは明らかに減っているため、効果はありそう メルペイ 安藤さん: 他のオウンドメディア「メルカン」ではJDページへの流入数は見ているが、テックブログはJDへの流入数は見ていない。 mercan.mercari.com Job Descriptionにそのポジションに関係のあるバズった記事を載せておくと、応募者が事前に読んで理解を深めてきてくれる。そういう使い方をしている HRに「このJob Descriptionにつける良いエントリありませんか?」って聞かれる 社内の採用担当への協力という意味の効果はある カジュアル面談に見せたりもする スカウトで一緒に「今度こんなイベントを開催します」と送る際に、そこで登壇する人のエントリを一緒に共有したり 松岡さん(モデレータ。Findy) やり初めてやめちゃう会社が多い。 アトラクトに徹する目的のほうが続きやすくて、ブログからの応募数を基準にしちゃうと続かない。 SEOコンテンツマーケティングとは違うものなので注意 「執筆者のモチベーションの維持をする方法」 メルペイ 安藤さん: 重要だが本当に大変。 上から、下からの両方を実施している。 CTOやVPoEが "なぜ技術広報をやるのか" というドキュメントを書いたり、All Hands(週次定例)で定期的に発信したり、ブログ書いたり、すでに社外発信している人に社外発信についてのLTをしてもらったり... 安藤さんは数字と定性を書いた人にフォードバックしたり、書いてもらった人に書いたモチベーションを聞いて改善したりする 書き手のモチベーションは人によって様々なので、色々な方向の施策を考える必要がある チームメンバから書かないの?って言われたり、「この仕事の話ブログにすると良さそうだけど誰か書かない?」みたいな話が社内であって良い傾向 良い記事を書く必要は無い、というのを伝える 「マサカリが怖い」という話 (※ マサカリ: 内容に対する鋭い、厳しい指摘のこと) 社内の技術に強い人々がレビューしてくれるから大丈夫だと伝えている それでも怖い、不安だという人は怖くなくなるまでそっとする 評価対象に入れることを明言している スマートキャンプ 中川さん: 日頃モチベーションを高めるための施策はしていない。できていない。 これまでマンパワー・気合で何とか書き続けていたため、それを解消するために持ち回りにしてモチベーションに左右されないようにした 二人への質問 Q: 法務に関するレビューはしている? 中川さん「特にチェックしていない」 安藤さん「PRレビューの仕組み自体はあるが、テックブログにおいては必須ではない。エンジニアによるレビューがPRレビューの代わり。ただ、連投企画のときはスポットで知財チームによるレビューを入れている」 Q: 業務時間は使って良い? 安藤さん「社外発信はすべて業務時間。イベントなども該当。本業に影響が無いようにしてもらって、忙しければ代打を用意したりすることもある」 中川さん「良い。業務に元々組み込んでいる」 Q: 執筆者は1記事にどれぐらいの時間をかけていますか? 安藤さん「人によりますが1記事あたり1日ぐらい」 中川さん「丸一日ぐらい。レビューの手戻り次第によって上下はある。一日で平均5000文字ぐらい書いてもらっている」 「これからブログを初める人たちに向けて」 メルペイ 安藤さん: 自社なりのやる意味を決める必要がある。トップが会社としてブログに理解があり、やることが良いことだという状況になっていることが重要。 ある程度会社自体に余裕が無いとできない施策だと思う 会社の上部とちゃんと位置付けを握っておいて、伝えていくことが大事 やってみて、半年ぐらい更新が無かったら閉じるといったルールを決めて始めるのも良いと思います。 社員が書く必要があるので難易度は他のメディアよりも高いが、社会的な意義は大きいし意味もあるものです。 スマートキャンプ 中川さん: なぜやるのかを明確にするのが一番大事。やり始めると大変なのでちゃんと理由がないと続かない 継続しないと放置されているブログができて見られ放題になるというリスクがある。マイナスプロモーションにならないように注意。 見切り発車は良くない レポートは以上です。 まとめ いかがでしたか? 私はメルペイさんとスマートキャンプの運営方針の違いが興味深く感じ、会社の規模・フェーズの違いによるものかな...?と、楽しみながら聞くことができました。 社内でテックブログを運営している人、これからしようと考えている人の参考になると幸いです。 関連する記事 tech.smartcamp.co.jp tech.smartcamp.co.jp tech.smartcamp.co.jp
アバター
スマートキャンプ、エンジニアの入山です。 昨年末から弊社BOXILでは、EC2からECS/Fargateへのインフラ移行作業を実施しています。 EC2運用からコンテナベースのECS運用への移行は、インフラの思想として異なる部分も多く、一筋縄ではいかないということを日々痛感しています。特に運用面に関する仕組みやノウハウは大きく異なっているため、今までと同等の運用を異なる方法で実現する必要があり、頭を悩ませることが多いです。 例えば、今まではEC2にSSHしてコマンドを投入していたが、ECS/Fargate上で同じことをどうやってやるのか…など。SSHしなくても良い運用にすることも必要ですが、今まで当たり前に出来ていたことが万が一の時にどうやっても出来なくなるのはやはり辛い問題ではないでしょうか。 また、とりあえず移行はできたけど、今までよりも運用に時間や手間が掛かるようになった…といったことも避けたいです。移行作業にあたっては、これらのマイナスのギャップを減らし、プラスのギャップが多くなるように気を付けて取り組んでいます。 今回は、移行作業の中でデプロイ時のフローに掛かる時間や手間を削減することを目的に、AWS CodeDeployのBlue/Greenデプロイの承認フローをSlackでChatOpsできるようにした話を紹介したいと思います! 背景 実装概要 構成 実装 Slack App作成 Webhook有効化 メッセージ通知処理(SNS + Lambda) CodeDeployのイベントトリガー設定 CodeDeploy操作処理(API Gateway + Lambda) Interactive Components有効化 動作確認 終わりに 背景 従来のEC2環境のデプロイフローはJenkinsで構成されており、作業者は以下の手順で操作を行いデプロイを行っていました。(作業者の操作が必要な箇所を抜粋) 1. Jenkinsへログイン - ID/Passwordによる認証 2. (Jenkins) Pre環境のデプロイ開始 3. (Jenkins) Prod環境のデプロイ開始 4. (Jenkins) デプロイの完了承認(旧バージョンの終了) これに対して、ECS環境のデプロイフローはCircleCIとCodeDeploy(Blue/Greenデプロイ)で構成しており、作業者は以下の手順で操作を行う必要があります。 1. CircleCIでデプロイ用のTagを作成 2. AWSへログイン - ID/Passwordによる認証 - 2要素認証 - スイッチロール 3. (AWS) 新バージョンの本番昇格承認 4. (AWS) デプロイの完了承認(旧バージョンの終了) 操作が必要なステップ数はどちらも4ステップですが、AWSへのログインは2要素認証とスイッチロールが必要となります。そのため、従来よりもデプロイの際に手間がかかるという課題が出てきました。 この課題に対して、AWS上の承認フローをSlackで操作できたら当たり前に便利…ということで、SlackでのChatOpsを実装することにしました。 ChatOps実装後の手順は、以下となります。 1. CircleCIでデプロイ用のTagを作成 2. (Slack) 新バージョンの本番昇格承認 3. (Slack) デプロイの完了承認(旧バージョンの終了) 実装概要 CodeDeployのBlue/Greenデプロイにおける以下ステップに関する通知・操作をSlackで実施できるようにします。 (デプロイの開始処理は、他サービスをトリガーとする想定のため範囲外) 【ステップ3】テストトラフィックルーティングのセットアップ完了後の待機 [操作] トラフィックの再ルーティング(Continue) [操作] デプロイを停止(Stop) 【ステップ5】: 本稼働トラフィックを置き換えタスクセットに再ルーティング完了後の待機 [操作] 旧タスクセットの終了(承認) [操作] デプロイを停止(Stop) 構成 AWSの各サービス(SNS + Lambda, API Gateway + Lambda)とSlack App(Webhook, Intaractive Component)を連携させた、以下の構成となります。 実装 Slack App作成 Slackでは、メッセージ受信用のIncoming Webhookとコールバック用のInteractive Componentsを利用します。 Slack APIの App管理ページ から、新しいSlack Appを作成します。 Slack Appが作成されると Basic Information ページが表示されます。このページから、Incoming WebhookやInteractive Componentsの設定ができます。 Webhook有効化 先程作成したSlack AppでIncoming Webhookを有効化します。 Add features and functionality から Incoming Webhook を選択し、 Activate Incoming Webhooks をオンにします。機能が有効化されると Add New Webhook to Workspace ボタンが表示されます。 Add New Webhook to Workspace から、メッセージの投稿先チャンネルを選択し、Webhookを作成します。 Webhookが作成されると、Webhook URLが表示されます。後述のメッセージ通知用のLambda関数にこのURLを設定します。 メッセージ通知処理(SNS + Lambda) CodeDeployのイベントをSlackに通知するためのLambda関数を作成します。 シンプルなイベントの通知処理に加えて、操作が必要なフローに対応したイベントの場合は、コールバック用のインタラクティブメッセージボタンが表示されます。 前項で登場したSlackのWebhook URLを、環境変数 SLACK_WEBHOOK_URL に設定します。 Python サンプルコード(メッセージ通知) import os import json import urllib.request SLACK_WEBHOOK_URL = os.environ[ 'SLACK_WEBHOOK_URL' ] def lambda_handler (event, context): # print("Received event: " + json.dumps(event)) message = event[ 'Records' ][ 0 ][ 'Sns' ][ 'Message' ] message_dict = json.loads(message) deployment_id = message_dict[ 'deploymentId' ] if "status" in message_dict: deployment_status = message_dict[ 'status' ] elif "instanceStatus" in message_dict: deployment_status = message_dict[ 'instanceStatus' ] slack_color = "good" slack_title = f "Deployment ID: {deployment_id} ({deployment_status})" slack_value = None slack_actions = None # Statusに合わせて通知内容を設定 # テストトラフィックへのルーティング完了(READY) if deployment_status == "READY" : slack_value = "新バージョンのデプロイが完了しました。 \n リリース作業を継続する場合は、Continueを押してください。" action_type = "approval_ready" # 置き換えタスクセットへの再ルーティング(Succeeded) elif deployment_status == "Succeeded" : slack_value = "新バージョンへのトラフィック切り替えが完了しました。 \n リリース作業を完了する場合は、Continueを押してください。 \n (旧バージョンのコンテナは停止されます)" action_type = "approval_termination" # デプロイ停止 elif deployment_status == "ABORTED" : slack_color = "danger" slack_value = "デプロイが中止されました" else : slack_color = "warning" if deployment_status == "READY" or deployment_status == "Succeeded" : slack_actions = [ { "name" : "deployment" , "text" : "Continue" , "style" : "danger" , "type" : "button" , "value" : json.dumps({ "actionType" : action_type, "deploymentId" : deployment_id}), "confirm" : { "title" : "リリース継続確認" , "text" : "リリース作業を継続します" , "ok_text" : "Yes" , "dismiss_text" : "No" } }, { "name" : "deployment" , "text" : "Stop" , "type" : "button" , "value" : json.dumps({ "actionType" : "stop_deployment" , "deploymentId" : deployment_id}), "confirm" : { "title" : "リリース中止確認" , "text" : "リリース作業を中止します" , "ok_text" : "Yes" , "dismiss_text" : "No" } } ] data = { "username" : "CodeDeploy" , "attachments" : [ { "color" : slack_color, "fallback" : "You are unable to promote a build" , "callback_id" : "wopr_game" , "attachment_type" : "default" , "fields" : [ { "title" : slack_title, "value" : slack_value } ], "actions" : slack_actions } ] } headers = { 'content-type' : 'application/json' } req = urllib.request.Request(SLACK_WEBHOOK_URL, json.dumps(data).encode( 'utf-8' ), headers) with urllib.request.urlopen(req) as res: body = res.read() 次に、SNSトピックを作成し、上記Lambda関数を呼び出すサブスクリプションを作成します。 SNSトピック タイプ: スタンダード サブスクリプション プロトコル: AWS Lambda エンドポイント: 上記Lambda関数 CodeDeployのイベントトリガー設定 CodeDeployでのBlue/Greenデプロイ時の各イベントをSNSに発行する設定を行います。 デプロイに関する設定は、以下の通り設定されている前提として省略します。 デプロイ設定 デプロイタイプ: Blue/Green Load balancer: テストリスナーあり トラフィックの再ルーティング: トラフィックを再ルーティングするタイミングを指定します(例:1時間) CodeDeployのコンソールから該当アプリケーションを選択後、 デプロイグループの編集 ページの 詳細 – オプション 項目から以下のトリガーを作成します。イベントは、デプロイ中に通知・操作したいタイミングに関連するイベントに絞っています。 トリガー イベント: デプロイの停止 、 デプロイの準備が完了しました 、 インスタンスの成功 SNS: 前項のSNSトピック ここまでの設定完了後に、CodeDeployでBlue/Greenデプロイを実行すると、各イベントをトリガーに以下のような通知やボタンがSlackに表示されるようになります! CodeDeploy操作処理(API Gateway + Lambda) SlackのIntaractive Componentsからのコールバック結果に応じて、CodeDeployを操作するLambda関数を作成します。 CodeDeployの操作は、AWS SDKを利用したAPIコールによって行います。そのため、Lambdaの実行ロールにCodeDeployへのアクセス権限が必要となります。 CodeDeploy — Boto3 Docs 1.16.49 documentation また、Slackからのリクエスト検証用のVerification Tokenを、環境変数 SLACK_VERIFICATION_TOKEN に設定します。Verification Tokenは、 Basic Information ページの App Credentials 項目に表示されています。 Python サンプルコード(CodeDeploy操作) import json import os import boto3 from base64 import b64decode from urllib.parse import parse_qs client = boto3.client( 'codedeploy' ) SLACK_VERIFICATION_TOKEN = os.environ[ 'SLACK_VERIFICATION_TOKEN' ] #Triggered by API Gateway def lambda_handler (event, context): # print("Received event: " + json.dumps(event)) if event[ 'isBase64Encoded' ]: body = parse_qs(b64decode(event[ 'body' ]).decode()) else : body = parse_qs(event[ 'body' ]) payload = json.loads(body[ 'payload' ][ 0 ]) action_value = json.loads(payload[ 'actions' ][ 0 ][ 'value' ]) # Validate Slack token if SLACK_VERIFICATION_TOKEN == payload[ 'token' ]: if action_value[ 'actionType' ] == "approval_ready" : approval_ready(action_value[ 'deploymentId' ]) slack_text = "リリース作業の継続を受け付けました。トラフィックを切り替えます。" elif action_value[ 'actionType' ] == "approval_termination" : approval_termination(action_value[ 'deploymentId' ]) slack_text = "リリース作業の継続を受け付けました。旧バージョンのコンテナを停止します。" elif action_value[ 'actionType' ] == "stop_deployment" : stop_deployment(action_value[ 'deploymentId' ]) slack_text = "リリース作業の中止を受け付けました。" else : return { "isBase64Encoded" : "false" , "statusCode" : 403 , "body" : "An error occurred." } return { "isBase64Encoded" : "false" , "statusCode" : 200 , "body" : slack_text } else : return { "isBase64Encoded" : "false" , "statusCode" : 403 , "body" : "This request does not include a vailid verification token." } def approval_ready (deployment_id): response = client.continue_deployment( deploymentId=deployment_id, deploymentWaitType= 'READY_WAIT' , ) print (response) def approval_termination (deployment_id): response = client.continue_deployment( deploymentId=deployment_id, deploymentWaitType= 'TERMINATION_WAIT' , ) print (response) def stop_deployment (deployment_id): response = client.stop_deployment( deploymentId=deployment_id, autoRollbackEnabled= False , ) print (response) 次に、コールバックを受け取るためのAPI Gatewayを作成します。 今回は、HTTP APIで以下のように設定して作成します。 API APIタイプ: HTTP API 統合: Lambda Lambda関数: 上記Lambda関数 ルート メソッド: POST リソースパス: (default) 統合ターゲット: (default) ステージ ステージ名: $default (default) 自動デプロイ: Enabled (default) HTTP APIを指定することで、ステージのデプロイなどを気にすることなく、シンプルにAPI Gatewayを利用出来ます。(作成された時点でデプロイまで完了され、利用可能な状態になります。) Interactive Components有効化 最後にSlack App側でInteractive Componentsを有効化します。 この設定により、Slack上のインタラクティブメッセージボタンの操作が、API Gatewayにリクエストされるようになります。 Add features and functionality から Interactive Components を選択し、 Interactivity をオンにします。機能が有効化されると Request URL 項目が表示されます。 Request URL 項目に、API GatewayのエンドポイントURLを入力します。 動作確認 CodeDeployでBlue/Greenデプロイを実行して、ステップ毎にSlackへ以下の通知が表示され、Slack側のボタン操作でCodeDeployのステップが進めば実装は完了です! 【ステップ3】テストトラフィックルーティングのセットアップ完了後の待機 【ステップ5】: 本稼働トラフィックを置き換えタスクセットに再ルーティング完了後の待機 終わりに 今回は、AWS CodeDeployのBlue/Greenデプロイの承認フローをSlackでChatOpsできるようにした話を紹介しました! ChatOpsを上手く活用することで簡単に運用の効率化ができるだけでなく、運用の効率化は長期的に効果を発揮するため、検討する価値は大いにあるのではないでしょうか。 また、今回と同様の仕組み(AWS SDKからのAPIコール)を使うことで、CodeDeployだけでなく様々なAWSサービスを操作できるので、そちらも是非試してみてください!
アバター
BOXIL 開発に携わっている、新卒エンジニアの高砂と申します。 本記事では、12月21日に弊社で開催された「テクニカルライティング研修」について紹介します! テクニカルライティング: 情報を正確かつ効果的に伝達する文書作成技法のこと。狭義では専門分野における技術者向けの文書を対象としており、この記事内では狭義を指す。 今回の研修の主な目的は、この弊社エンジニアブログ(以下「弊ブログ」という)の記事クオリティ向上です。 そのため、普段このブログを執筆しているエンジニアを中心とした12名が参加しました! 研修の講師は、サイボウズ株式会社のプロダクトライター(製品のヘルプ等の執筆者)である殿岡理恵( @txdriet )さんが引き受けてくださりました。 当日の流れ 研修は全体で約4時間でした。内容は以下の通りです。 自己紹介 主語と述語の話 文章の長さと並べかた 構成とタイトル 読みかたを考えさせないために 読者の前提知識への配慮 ライターが繰り返しやっていること 間違えやすい漢字と常用漢字表 人間の認知能力も知っておきましょう ワークショップ 番外編:UXライティングの考えかたまとめ 研修はZoom上で行われ、各自リモートで参加しました。 座学パート 前半は、わかりやすい文章を書くための技術を座学形式で学びました。 よくある文章をもとに、改善前の書き方と改善後の書き方を比較して説明して頂き、とても理解しやすい内容でした。 【例:主語と述語の対応】 改善前: このサービスの特長 は、過去に入力したデータを簡単な操作で 取得できます 。 改善後: このサービスの特長 は、過去に入力したデータを簡単な操作で 取得できることです 。 ワークショップパート 後半は、100~200文字程度の文章を参加者それぞれが実際に書き換えてみるワークショップを行いました。 お題に選ばれた文章は弊ブログ記事内から抜粋されており、執筆者を中心にZoomのチャットが盛り上がっていました! 【例:「 セールスチームにKPTを導入してみた 」より】 【改善前】 弊社スマートキャンプのセールスチームに、開発チームで行っているKPTによる振り返りを導入してみました。 この記事では実際にKPTを導入するまでの背景と流れ、また実施してみたセールスチームからの感想を紹介いたします。 きっかけ ある日に1日かけて、ボードメンバーとセールスチームのマネージャー数名と共に、BALES(自社サービス)の現在のアウトカムの設定・ターゲットの詳細化・仮説の設定などを行うMTGでファシリテーターをさせていただく機会がありました。 ディスカッションの準備から関わらせてもらったため、 参加されてた方からは「フレームワークを使いふわっとしたことを整理するのが得意な人」と認識していただき、別の議題について相談していただいたのがKPT導入のきっかけとなりました。 【改善後】 弊社スマートキャンプのセールスチームで、KPTによる振り返りを導入してみました。 「KPT」とはKeep/Problem/Tryの略で、振り返りのメソッドの一つです。プロジェクトの中での取り組みや進め方に対して「Keep=よかったこと」「Problem=悪かったこと」「Try=次に試すこと」についてそれぞれ考えていきます。弊社では、開発チームですでに導入しているメソッドです。 この記事では、セールスチームにKPTを導入するまでの背景と流れ、また実際に導入してみての感想を紹介します。 きっかけ XX月ごろ、BALES(自社サービス)のミーティングで、私と他の数名でファシリテーターをする機会がありました。このミーティングでは、現在のアウトカムと仮説の設定、ターゲットの詳細化などを行い、私はディスカッションの準備から関わることになりました。 これを通して、 私は参加者から「フレームワークを使い、ふわっとしたことを整理するのが得意な人」と認識されることになり、その後別の議題についても相談してもらったことがきっかけで、KPTを導入することになりました。 おわりに 研修の事後アンケートではこのような意見が寄せられており、参加者の満足度が非常に高かったことが分かりました! 「記事を分かりやすく書くコツを具体的に教えてくださり、執筆の参考にとてもなりました。」 「今後エンジニアブログを書く上で、意識したい内容が盛り沢山でした。人間の認知能力に基づいた読み手のストレス耐性の話はとても興味深かったです。」 「社内にプロダクト寄りのライティングノウハウをもつ人物がいないため、今回の研修はとても勉強になりました。研修の内容もひとつひとつがわかりやすく、頭に入ってきやすかったです。」 「テクニカルライティングで本日伺ったスキルや考え方などは、テックブログへの応用はもちろんですが、Slackやメールなどのリモートでのテキストコミュニケーションが増えた現状でのどこでも利用できる素養として学ぶことができ、大変感謝しております。」 弊社では今後もこういった取り組みを通して、チーム全体でのスキル向上に取り組んでいきます! smartcamp.co.jp
アバター
スマートキャンプエンジニアの中川です。 このブログが開設されてから、今日でちょうど 2 年が経ちます。 また、私は去年のこの時期、つまり開設されてから 1 年時点でこのブログを引き継ぎ、以来今まで 1 年間運営してきました。 そこで、この記事ではこの 1 年間でブログがどう変わってきたかを振り返ってみようと思います。 ブログやその記事自体もそうですが、社内でテックブログを存続・発展させるためにやってきた取り組みなどに関してもご紹介します。 また、開設から 1 年間の出来事は以下の記事にまとめられていますので、ご興味のある方はこちらもあわせてご覧ください。 エンジニアブログを開設して1年でなにが起きたかすべてオープンにする - SMARTCAMP Engineer Blog ブログの数値を振り返る はじめに、このブログの数値が 1 年でどのように成長したかを振り返ります。 こちらが去年の数値。 そしてこちらが今年の数値です。 アクセスが 3 倍弱、読者が 3 倍強(いつもありがとうございます 🙏)、総ブックマーク数が 2 倍強といった結果になりました。 もちろんこれらが直接的に会社のテックブログの成長を測れるものではないものの、少なくとも成長に寄与するものではあるので、素直に嬉しいです。 運営のバトンを受け取ってから、今後どういう記事を出していこうかを試行錯誤した時期もあったりしたなかで、倍以上に数値を伸ばせたことはありがたいことだと感じています。 いつも記事を書いてくれるメンバー、見てくださってるみなさま、ありがとうございます! ブログの記事を振り返る 今年は計 50 本の記事を公開しましたが、そのなかでも特に以下の記事がよく読まれました。 tech.smartcamp.co.jp tech.smartcamp.co.jp tech.smartcamp.co.jp tech.smartcamp.co.jp tech.smartcamp.co.jp 純粋な技術記事もあればポエム記事もあるのがこのブログの特徴で、それが顕著に現れている結果かなと思います。 特にはじめに挙げた記事は反響が大きく、Twitter 上で数多く言及されていたり、社外の Podcast で紹介されたりしていました。 また、そういったいわゆる「バズ」が発生した際は、いただいた反響を逐一 slack で共有するなどしてメンバー全員で盛り上がることができたのも良い思い出です。 ブログの運営を振り返る 次に、ここ 1 年のブログ運営についてです。 運営のバトンを渡されてから試行錯誤してきたなかで、特に良かった試みについてご紹介したいと思います。 次に紹介する 2 つの取り組みはどちらも今年始めたものですが、今となっては運営に欠かせない仕組みになっています。 執筆者の書き始めをサポートする 当ブログは弊社のエンジニアや PdM に持ち回りの当番制で記事を執筆してもらっています。 ブログを執筆する行為は業務にあたり、業務時間を使って執筆することも当然 OK なのですが、やはり「文章を書く」ということへの感じ方や得手不得手は各人によりけりです。 執筆が苦手なメンバーにかかる負荷を減らすことこそがスムーズに当番制が機能する鍵だと考え、なにかサポートできることはないだろうかと模索した末にたどり着いたのがこの取り組みです。 具体的には、執筆するにあたってかならず最初にブチ当たる「テーマを考える・記事のアウトラインを考える」ハードルを下げるために、通称「召喚定例」というものを始めました。 これは、私を含めた運営メンバー 2 名 + その時々の執筆予定者 の座組で上記のテーマ・アウトライン決めをする会議です。 毎週異なる執筆者を呼んで時間調整をすることになるので多少の事務的なコストはかかりますが、執筆者が何を書こうか一人で悩んでしまいなかなか書き始められないなどといったことが減りました。 また、執筆者が事前にテーマを考えていた場合には、運営メンバーが一読者としてどういうことが知りたいかを打ち返すなど、一種の壁打ちとしても活用してもらえています。 嬉しい誤算としては、このことによって「記事を執筆してもらったものの、こちらが想定していた内容と違ってどう指摘したものか頭を悩ませる」のような悲しい事態も防げるようになりました。 GitHub を利用した記事のレビュー体制をつくる こちらは記事のクオリティを上げるために始めた取り組みです。 この取り組みを始めるまで、執筆者にははてなブログで書いた記事を下書き保存してもらい、そのプレビュー URL を発行してもらうことで内容を見ながら、slack 上でレビューするようなフローでレビューしていました。 手軽で良い面もあるのですが、やはり slack 上だと複数個の指摘をするのが億劫になってしまったり、記事中のどこを指しているのかが曖昧になってしまったりといった課題がありました。 そこで、GitHub に記事をやり取りするためのリポジトリを作成し、そのリポジトリに対するプルリクエストの形で執筆した記事(markdown)を投げてもらう仕組みに変更しました。 この形であれば、レビューコメントが複数発生しても問題ありませんし、記事の特定の行に対して指摘をすることも容易です。 さらに、記事に対する指摘をする際に、たとえば誤字脱字程度の簡単な修正であれば、GitHub のサジェスト機能を使うことで、執筆者はサジェストを確認して良ければ取り込むだけでよくなり、指摘対応の手間も削減できました。 実際のサジェスト付きコメント 当初狙っていた記事のクオリティを上げる効果は明らかに出始めたのですが、最終的に記事を公開するためにはてなブログに記事をコピペしたり画像をアップロードするなど、人力の作業が発生してしまっているイケてない部分が現状の課題です。 来年はここらへんを GitHub Actions を使うなどして自動化していけるとより手間のかからない運用にしていけるかなと思っています。 まとめ 以上が、当テックブログ運営担当者としての今年の振り返りでした! こうして振り返ってみると、執筆する側の負担を減らして無理なく継続していける仕組みに注力した一年だったなと思います。 来年も改善を繰り返して、より読みごたえのある記事を公開する手助けをしていきたいです!
アバター
こんにちは! スマートキャンプで開発組織の責任者をしている米元です。 コロナ禍によって激動の1年となった2020年も残りわずかとなりましたが、皆様いかがお過ごしでしょうか。 読者の皆様が心身ともに健やかに年末年始を迎えられれば幸いです。 さて先日の記事にも書かせて頂きましたが、弊社では採用活動の一環で選考に入る前のエンジニアの方に向けて、会社や開発組織のご説明をするカジュアル面談を行っています。 tech.smartcamp.co.jp そのカジュアル面談での時間をより濃い内容にするために、当記事では候補者の方々から頂く質問の中でも特によく聞かれることについて書かせて頂こうと思います。 面談の前にこの記事を読んで頂いた方は弊社の理解度が高い状態で面談に臨めますし、そうでない方も面談時に聞けなかった部分を補完する情報になれば幸いです。 また、そもそもスマートキャンプってどんな会社?という方はこちらの会社説明資料をごらんください。 speakerdeck.com 目次 目次 よく聞かれる質問 どんな人を求めていますか 複数の開発チームがあるとの事ですが、どのようなチーム分けなのでしょうか タスクがエンジニアにアサインされるまでの流れを教えてください 入社をしたら最初にどのようなタスクをアサインされるのでしょうか リリースの頻度はどれくらいでしょうか Railsやミドルウェアのバージョンアップを積極的に行っているとのことですが、どのように進めているのでしょうか 技術的な課題はどのようなものがありますか BOXILについて BALES CLOUDについて 技術選定はどのように行っていますか 今後Rails以外の技術を使うことはありますか 今後、開発組織はいつ頃にどれくらいの規模になりますか リモートワーク中心の働き方は今後も続くのでしょうか まとめ よく聞かれる質問 それではさっそくよく聞かれる質問とその回答を紹介していきましょう。 どんな人を求めていますか 技術面に関してはその時々の求人内容によるので割愛します。 エンジニアに限らず、候補者に共通して求めるものとして、弊社の行動指針である 「SOCS」 の体現があります。 「SOCS」とは SmartThinking、Ownership、Collaboration、Speed の4つの行動指針の頭文字です。 SmartThinking 仮説思考・本質思考 先見性と創造性 Ownership リーダーシップ 主体性 Collaboration 社内・社外共創 Speed 実行スピード 生産性(質/時間) 社内では更に詳細な定義があり、評価や選考基準の1つとして利用しています。 複数の開発チームがあるとの事ですが、どのようなチーム分けなのでしょうか BOXILとBALES CLOUDというプロダクトがあり、それぞれにプロダクトマネージャー、デザイナー、エンジニアなど複数職種のメンバーが所属しています。 エンジニアはBOXILが業務委託とインターンの方を合わせて9名、BALES CLOUDは2名です。 BOXILの方はエンジニアが多いので更にエンジニア内でサブチームに分けています。 サブチームの数やメンバーの分け方は、 その時点の案件の内容と優先度 メンバーのスキルややりたい事 によって2〜3ヶ月ごとに組み合わせを見直しております。 例えば直近の例だと、インフラが得意な中堅メンバーとインフラを学びたい若手メンバーでサブチームを組んでインフラの改善作業をしています。 現在は3つのサブチームに分けた体制で安定した成果が出ており、当面はこの体制でメンバーを組み替えながら開発していきます。 BOXILのこれまでの変遷はこちらの記事にも書いています。 tech.smartcamp.co.jp タスクがエンジニアにアサインされるまでの流れを教えてください まず前提として弊社ではスクラム開発を行っており、1週間で1スプリントのサイクルを回しています。 少し前の記事になりますが、スクラムやリモート下での開発の様子は以下の記事をご覧ください。 tech.smartcamp.co.jp tech.smartcamp.co.jp 弊社でタスクの元になる案件の発生パターンはおおまかには以下の3つがあります。 事業戦略に基づいて関係部署から企画や施策が提案される Slackの開発リクエスト用チャンネルで調査依頼や不具合報告が送られる 開発チーム自身がシステム改善や機能改善の案を提案する ※重大な不具合対応など緊急性が高いものは除く それらのタスク案を Product Backlog に積んで、プロダクトマネージャーが中心となり優先順位付けをします。 仕様についての確認やすり合わせはリファインメントとして朝会の中で行い、着手出来る状態になったものが次のスプリント以降のタスク候補となります。 週次のプランニングの際に開発チームで話し合いを行い、タスク候補の中から翌週の Sprint Backlog に入れるものを選び、誰が担当するかを決めるという流れになっています。 入社をしたら最初にどのようなタスクをアサインされるのでしょうか まずはプロダクトの仕様や開発フローに慣れて頂くために、比較的小さめのタスクからお願いします。 1人で作業するというより複数人で1チームとして作業を進めることが多いので、 モブプログラミング やペアプログラミングなどでコミュニケーションをとりつつ進めて頂くことになると思います。 これはリーダーやマネージャー候補の方でも同じです。 弊社のオンボーディングについては以下の記事が参考になるかと思います。 少し前の記事のため現在は変わっている部分もありますが、 「入社した方に出来るだけ早く活躍していただくための支援をする」 という基本的な考え方は変わっていません。 tech.smartcamp.co.jp リリースの頻度はどれくらいでしょうか 時期によって異なりますが、だいたい 週に3〜4回はリリースをする ことが多いです。 1回のリリースに複数の変更が含まれており、週に5〜15件程度の変更が行われていることになります。 Railsやミドルウェアのバージョンアップを積極的に行っているとのことですが、どのように進めているのでしょうか 基本的には通常の開発と並行して進めています。 Railsのバージョンアップなど工数がかかるものについてはノウハウのある外部の方にスポットで協力をお願いし、短期間でリリースまで一気にやりきる場合もあります。 それらに工数をかけるべきかの判断は誰が行うのか?という質問もよく頂くのですが、弊社では 開発チーム自身にその裁量が与えられています。 その分、開発チームも中長期の事業に与えるインパクトに責任を持ち、いつどのように行うべきかを判断しています。 技術的な課題はどのようなものがありますか BOXILについて インフラはEC2でRailsのアプリケーションを直接動かしていますが、歴史的背景によりオートスケールが出来ない状態になっているなどの運用上の課題をいくつか抱えています。 これに関しては1,2ヶ月後を目処にECS Fargateに移行して複数の課題をまとめて解決する方向で対応を進めています。 また、フロントエンドに関しては数年前の構成と直近1,2年の比較的モダンな構成が混在しており、今後はこれらをモダンな構成に統一することを検討しています。 アプリケーションのアーキテクチャとしては、BOXILとBOXILのメディア機能が同じアプリケーション内に同居していて設計上も密結合しています。 こちらもやや負債となっているため今後改善していきたいと考えています。 BALES CLOUDについて 比較的新しいサービスであることや日々リファクタリングを進めていることもあり、大きな技術的負債はありません。 ただ少人数で開発をしていることもあり、UIの変更に伴うバグを回避するための影響範囲の調査やテスト工数が大きな負担になっていました。 直近ではこの問題を解決するためにmablというテスト自動化ツールの導入を進めています。 技術選定はどのように行っていますか 新しい技術やツールを導入する際は、基本的にはチーム内で裁量を持ってボトムアップで決めています。 特にルールなどはありませんが、大きな技術導入の場合は他のチームにも相談や情報共有を行いながら進めることが多いです。 組織の規模が大きくなると複数チームで同じ技術検証をするなどの無駄も発生しそうなので、今後はチームごとの裁量を維持しつつ情報共有の仕組みを作っていく必要があると考えています。 今後Rails以外の技術を使うことはありますか あります。 弊社では主力事業であるBOXILがリリースされた2015年頃からRailsを利用してきました。 RailsとRubyの優れたエコシステムを利用することで、当時のまだビジネスが不確実な状況でもスクラップアンドビルドを繰り返しながら素早く事業を立ち上げ、ここまで成長することが出来たと考えています。 フロントエンドに関してはVue.js + TypeScriptを利用する機会が増えてきましたが、バックエンドは依然としてRailsが中心となっています。 一方でこの数年でフロントエンドのフレームワークの進化と周辺のエコシステムが整備されたことで、バックエンドにRailsのようなフルスタックなWebアプリケーションフレームワークを選定する理由が以前より少なくなっています。 実際に他社の事例でもバックエンドはGoなどの静的型付け言語あるいはサーバーレスでAPIを書き、フロントはVue.js/React.jsとTypeScriptといった構成が選ばれる事がかなり増えております。 エンジニアの中でもこれらの技術に対する人気がかなり高まっており、採用活動への影響も無視出来ないものになってきました。 また、弊社のVISION/MISSIONを実現するために中長期でどの技術に投資していくかを考えた時に、 ユーザーに価値を提供し続けるために最適な技術を選択できる状態にすること 社内のエンジニアが技術的なチャレンジをすることができる環境を用意すること が重要だと考えています。 これらに対する施策の1つとして、弊社でもバックエンドはGo・フロントエンドはVue.js/Nuxt.jsやReact.js/Next.js + TypeScriptといった構成を積極的に採用していく予定です。 実際にサーバーレスでAPIを作成した事例は以下の記事で紹介しています。 tech.smartcamp.co.jp Railsに関しては今後も弊社の中で主力技術の1つとして利用し続けますし、事業の内容やフェーズ・開発メンバーによっては最適な選択肢の1つであり続けると考えています。 今後、開発組織はいつ頃にどれくらいの規模になりますか 現在は業務委託やインターンの方を合わせて15名程度の規模です。 職種や役割別の内訳としては以下のようになっています。 プロダクトマネージャー 2名 プランナー 1名 デザイナー 1名(開発専任の人数。1月に2名になる予定です) エンジニア 11名 今後については事業の状況やその他の外部要因などで変わりますが、今のところ2年後の時点で30人前後にする想定でいます。 リモートワーク中心の働き方は今後も続くのでしょうか 今は週1出社・週4リモートが基本となっていますが、今後のコロナの状況次第で週2〜3の範囲で出社する割合が増える可能性はあります。 弊社ではコロナ禍以前も全社的に週1のリモートデーがあり、オフラインとオンラインの両方の働き方の良い点を理解しています。 そのため、仮にコロナ禍が終わったとしてもリモートワークを完全に無くすことはなく、逆にフルリモートのみにすることも考えにくいです。 業務内容や事業フェーズ、チームやメンバーの自立度によってリモートの向き不向きは変わってくるため、個人的には週1以上の出社頻度をどうするかは部署やチーム単位で選び、必要に応じて全体での出社タイミングを設けるのが良いのではないかと考えています。 まとめ カジュアル面談でよく聞かれる質問とその回答についてまとめてみました。 面談をするしないに関わらず、少しでも弊社に興味を持って頂いた方の参考になれば幸いです。 ここまで読んで頂いてありがとうございました!
アバター
BOXILでプロダクトマネージャー(以下PM)をしている笹原です。 PMを名乗りだしてから1年が経過し、実際に自分はどういった働きができていたのかを振り返ってみたいと思いました。 そこで、今回は開発チームのエンジニアや企画メンバー、セールスやメディアなど関係する部署の方たちにアンケートに答えていただき、360度フィードバックを受けたのでその結果を共有していきたいと思います。 アンケートの質問項目 PMという役割に対する社内の認識 セールスとの連携 業務の移譲と分散 定量分析と情報共有 終わりに アンケートの質問項目 答えてもらった設問は10点満点評価1問、記述7問で合計8問です。 あなたが考えるPMの役割を教えて下さい あなたが考えるPMの役割に照らし合わせたときに今期のBOXILのPMは何点でしたか 点数の理由を教えて下さい PMが行いすぎていた、もしくは行う必要がなかったと思うことを教えて下さい PMが行わなかった、もしくは行なっていたが不足していたと感じることを教えて下さい PMが行なっていたことのうち、優れていたと思うことを教えて下さい 来期、PMに求めることを教えて下さい その他、なにかあれば自由にどうぞ 実際の回答を紹介しながら、これから振り返って行きたいと思います。 PMという役割に対する社内の認識 一番最初には『あなたが考えるPMの役割を教えて下さい』という、直接的な評価ではない質問を用意しました。 周りがどういった期待をしているのかを知ることで、現在とのギャップを見たいと思ったからです。 回答を見ていきましょう。 設問『あなたが考えるPMの役割を教えて下さい』の回答 この回答を見て、想定していたよりも回答がバラけたなという印象を受けました。 これは質問自体がざっくりしすぎて回答しづらかったのが一因としてあると思いますが、まだ社内でのPMに対する認識が薄いことも要因としてありそうだなと思いました。 これは次の質問の結果にも表れています。 次の質問は、前述の回答と比べて今期のBOXILのPMはどうか、について評価点方式で回答してもらうものです。Q1で回答してもらったところとどれだけギャップが有るのかを知りたく、10点満点で点数をつけてもらいました。 設問『あなたが考えるPMの役割に照らし合わせたときに今期のBOXILのPMは何点でしたか』の分布 点数の分布は結構高い点に固まっていました。これは回答者が、PMという役割の者と仕事をする経験が少なく、Q1での回答が私のしていたことに引きずられてしまったことが理由かなと思います。 PMの役割に対する認識が揃っていないことは、他部署との協力が不可欠なPMにはよくはない状況だと思います。 プロダクトによって求められるPMの役割は異なるとは思うので、ボクシルに求められるPMとはかくありき、というものをさらに探り、それを発信していくと周りからの協力も得やすくなるだろうと思いました。 セールスとの連携 BOXILというプロダクトは、ユーザー向けには「法人向けSaaS比較サイト」であり、ベンダーには「SaaSマーケティングプラットフォーム」としてリードを提供しています。 出典: スマートキャンプ会社説明資料 ただ、2020年11月期については注力ポイントをユーザー向けに絞ることとしており、ベンダー向けの施策はあまり行っていませんでした。 ベンダーの方と直接、接点を持っているのがセールスです。今回はセールスの方にもアンケートの協力をお願いしていました。 実際のコメントはこちらになります。 セールスの方からのフィードバック 書いてくださっているのですが、あまりフィードバックすることがなかったということがわかる内容になっています。 今期はユーザー向けに注力していたため、セールスとの連携が薄くなっているのは意図していたことではあったのですが、このフィードバックにもそれが表れています。 ただ、振り返ってみると、ユーザー向けの価値提供のための機能であってもベンダーに影響することは少なくなかったです。 ユーザーに対して提供している情報はベンダーに入力してもらっているものであり、ユーザーに対してどのように見られるのかを気にされるベンダーも少なくないからです。 そういった施策を行うタイミングではセールスの方への説明をさせてもらうこともあったのですが、優先度が低くなってしまうこともありました。そういった連携の仕方が『セールスとのコミュニケーションが少し唐突すぎた』というフィードバックにつながったのだと思います。 業務の移譲と分散 PMを名乗りだしてから一年と冒頭で書きましたが、それまではBOXILの開発リーダーをしていました。 もし、今までの経緯に興味がある方がいらっしゃれば、以下の記事にまとまっているので併せてお読みください。 ボクシル開発チームの変遷を振返ってみる - SMARTCAMP Engineer Blog BOXIL開発チームの「今」と「これから」 - SMARTCAMP Engineer Blog 半年間でエンジニアが3倍!!ボクシルチームの変遷をまたまとめてみた - SMARTCAMP Engineer Blog 開発リーダーをしていたところからPMになる上で意識したことのうちの一つが、エンジニアリング業務の移譲です。 データ構造からフロントの設計、インフラの環境などBOXILのエンジニアリングに関するところは、リーダーとして一番詳しいという自負がありました。 だからこそ、エンジニアリング領域に過剰に口出しをしてしまうことを危惧しており、そうならないように意識していました。 その確認の意味合いで『PMが行いすぎていた、もしくは行う必要がなかったと思うことを教えて下さい』という質問を入れました。 その回答は以下になります。 設問『PMが行いすぎていた、もしくは行う必要がなかったと思うことを教えて下さい』の回答 特になしというコメントが大半で、エンジニアリングに関する回答はありませんでした。 この結果は意識していたことが他者から見ても達成できていたことの表れだと思ったので、嬉しかったです。 ただ、逆に企画に関してはタスクの分散がうまくいっていなかったことが、フィードバックから分かる結果となりました。 『PMが行わなかった、もしくは行なっていたが不足していたと感じることを教えて下さい』という質問でも、 (企画チームのメンバーから)全部に対してする必要はないけど、時々説明をもう少ししてもらいたいなということはあったかな。 企画チームの組織化・マネジメントができれば、もっと上流に集中できたかもしれない。 というようなフィードバックもあり、PMとしての領域についてはもう少しタスクを分散することでうまくいくところがあったのではないかと思います。 定量分析と情報共有 『PMが行なっていたことのうち、優れていたと思うことを教えて下さい』という質問では、主に定量分析と、情報共有に関して書いてくれている人が多かったです。 設問『PMが行なっていたことのうち、優れていたと思うことを教えて下さい』の回答 定量分析については、BOXILの中でデータアナリストが設けられていなかったため、足りない部分を埋めるためにPMである私がやっていたので、そこを評価してもらったのだと思います。 また、もともとBOXILのエンジニアをしていたこともあり、テーブル構造やログ仕様についても詳しかったため、仮説を立てたり、施策を評価するために必要な分析を適切に行うことができました。 定量分析を評価してもらったのは、もう一方の情報共有がうまくいっていたこともその一因ではないかと思います。 BOXILでは開発をScrumで行なっており、情報共有の場としてSprint Reviewが活用されています。Sprint Reviewにはセールスやメディアと言った他部署の方にも積極的に参加してもらっており、仮説をたてるために行なった分析や、施策の評価も共有しています。 Sprint Reviewを盛り上げるためにチーム全体で試行錯誤してきた結果が回答として表れており嬉しいです。 Sprint Reviewの様子 ただ、定量分析がしづらい領域について、適切に定性的な評価をすることはあまりできていませんでした。 『PMが行わなかった、もしくは行なっていたが不足していたと感じることを教えて下さい』という質問でも以下のようなフィードバックがありました。 数字に対する目標を定めるのはとても早く、迅速に感じたが、数字以外の要素で判断する明確な基準があまり定まっていない気がした。 そのため、低い数字が出てから対策、その結果がでたらまた対策と言ったような施策の進め方になってしまい、後手後手で回ってしまった気がする。 終わりに この一年間、PMとしてやってきたことを360度フィードバックを用いながら振り返ってみました。 PMは多くのステークホルダーとの密接に関わりながら仕事を進めていくので、他者からのフィードバックを得ることは大事だと思います。 この機会に、いつも関わっている方たちから様々なフィードバックが得られたので良かったです。
アバター
こんにちは!スマートキャンプでインサイドセールスに特化した SaaSである BALESCLOUDを作っているエンジニアの井上です。 昨今の状況下で、最近リモートで働きはじめた人は多いのではないでしょうか 弊社でもこの状況でリモートで働いており、slackでのコミュニケーションがほとんどになりました。 今回この記事では、そんなslackなどで行うテキストのコミュニケーションの改善についての取り組みについて一部ご紹介します! 非同期コミュニケーションとは? なぜ改善する必要があったのか? 開発チームの抱える課題 目指した理想の状態 実際に行った施策 改善の取り組み方 実際の改善内容を紹介 進捗を共有する 相談をわかりやすくする 調査内容を記事化して共有する コードレビューをいつしてほしいかを伝える 同期のコミュニケーションはいつ使う? まとめ 非同期コミュニケーションとは? 非同期コミュニケーションは相手が即時に返答する必要がないコミュニケーションのことを指しており 冷蔵庫のメモのように、その場に相手がいなくてもコミュニケーションが完結するものです。 それに対して、同期コミュニケーションは対面での会話やZoom等での会議など、相手が同じ時間に居ることで成立するコミュニケーションのことです。 なぜ改善する必要があったのか? 同期のコミュニケーションはその特性上 その場に 相手が必要ですが、これは相手の時間を一定時間拘束することにもなります。 BALES CLOUDチームは小さいチームということもあり共通認識出来ている部分が多いので、一般的なサイズのチームよりも非同期のコミュニケーションで事足りるシーンが多く 不要な同期によるコミュニケーションにより開発時間が削れられることもありました。 小さいチームであるということは、1人あたりの開発時間が削られることに対するチームの開発量や速度への影響がとても大きいので この改善に取り組むことを決めました 開発チームの抱える課題 通常のコミュニケーションや共有が同期的に話すのがめいんだったため、コミュニケーションに時間が多く取られ 開発できる時間が削られてしまうという課題がありました。 目指した理想の状態 目指した状態としては、通常のコミュニケーションや共有をほぼ非同期に移していことで開発できる時間を増やすことです。 実際に行った施策 実際に非同期にするとコミュニケーションのパターンごとに伝える情報が足りないなどの問題が発生しました。 その問題に対してノウハウを蓄積していき、蓄積された内容が今回の記事になります。 改善の取り組み方 改善の取り組み方としては、毎週の振り返りの時間に、チームのノウハウやルールを蓄積していくような動きにしました。 これはチームとしての共通認識をもたせたいという意味と、チームで暗黙知になっている部分を明示化することにありました。 自分たちの当たり前を明示化することにより、どのような考えでコミュニケーションをとっているのかドキュメントにもなると考えたからです。 実際の改善内容を紹介 ここからは実際に取り入れた改善内容をご紹介します。 進捗を共有する 進捗系ではタスクの完了・未完了であったり、予定した進捗から変更された点の共有などがあります。 予定した進捗からの変更では、主に遅くなりそうな場合に共有します。 想定外に詰まったり、作業量が増えたりなどで進捗が遅くなる場合に共有することでチームでタスク進捗の共通認識をとります。 例) このタスク完了したので、次これやります! この開発でライブラリのアップデートをする必要があり、リリースは木曜くらいになりそうです。 相談をわかりやすくする 相談系では「悩んでるので相談したいです!」だとアクションとしてどのような相談になるのかが分かりにくいこともあるかと思います。 このような場合に非同期で完結したいのか、それとも同期でやりたいのかをはっきりさせる必要がありました。 私たちは非同期を基本としたうえで、同期でやりたいときにだけ相談の手段を明示しています。 例) 同期 この実装のロジックがどうすれば簡潔になるか悩んでるのでペアプロお願いできますか? この設計悩んでいるので15:00から30分くらいZoomで相談できますか? 非同期 調査内容を記事にまとめましたので、FBなどあったら記事にコメントお願いします! このUIをAとBパターンでどちらが理解しやすいか悩んでいるので、このスレッドで意見ほしいです! 調査内容を記事化して共有する 調査内容を共有するとき、Zoomを使った口頭での共有では説明がしづらく感じることが多くがありました。 これをドキュメントサービス(弊社ではkibela)にまとめることにより、内容が理解しやすくなり、もう一回聞き直すなど口頭で起きていたコミュニケーションのコストも減ったかと思います。 また、わからない場合はslackのスレッド・記事へのコメントで話すことで疑問に思った内容も残せます。 一方で、本当に難しい内容の場合もあるので、その際にはZoomでの会議を設定し口頭や画面共有でのコミュニケーションも取り入れます コードレビューをいつしてほしいかを伝える コードレビューを依頼される側は、依頼された瞬間にレビュータスクが積まれた状態になります。 しかし、障害対応や急ぎでリリースしたいもの以外のほとんどのコードレビューは、今すぐに取り掛からなくても良いものでした。 また、レビューの際に重点的にレビューしてほしい箇所があったり、実装の背景を説明しておいたほうがいい時もあります そのため、レビューを依頼する時に適用する下記のルールを追加しました。 - レビューの期限・タイミングを明記する - PRの内容を理解してもらうために口頭で説明する必要があるかどうかを明記する 例) こちらのレビューはnot 急ぎでお願いします! 急ぎでリリースしたいバグ修正のため、レビュー今お願いできますか? こちらのレビューの前に5分くらい実装の共有させてください、レビュー自体はnot急ぎです。 同期のコミュニケーションはいつ使う? 同期のコミュニケーションは難しい課題の議論やアイディア出しなどのブレストなどで使用します。 難しい課題やブレストの場合、テキストのみですと議論の開始から完了までやり取りが多くなり時間がかかります。 このような場合は同期でコミュニケーションを取ったほうがやり取りがその場で行えるので、効率が良かったです。 まとめ いかがでしたでしょうか? 今回はチームの非同期コミュニケーションの改善事例を紹介しました。 この中の1つでもリモートワークのコミュニケーションの参考になれば幸いです。
アバター
こんにちは!スマートキャンプで インサイドセールス管理システム『BALES CLOUD』 を開発・運用している中川です。 今回は、上記のプロダクトが有しているフィルター機能を、個人的な興味から Prisma でトレースして作ってみたところ、良いポイントがいくつもあったので紹介したいと思います! また、Prisma を試すにあたって、既存の DB からスキーマを生成して実行環境を用意したので、そのあたりについても説明した記事になります。 Prisma とは Node.js や TypeScript で使用出来る ORM DBMS は PostgreSQL、MySQL、SQLite に対応 データベースのスキーマをデータモデルに変換したうえで、データモデルに対して型推論が効く VSCode などエディタ上でクエリを書く際の DX が良い チュートリアルやドキュメントなど、非常に詳しくまとまっているため、詳細は以下の公式サイトを参照してください。 www.prisma.io 余談ですが、Prisma の現行バージョンは 2 でして、1 からはアーキテクチャごと刷新されています。 GraphQL とは完全に切り離されていたりするので、バージョン 1 の Prisma を触っていた方は再度学び直す必要がありそうです。 前提:どういったプロダクトか はじめに、前提としてどういったプロダクトのどういった機能について話すのか簡単に説明させてください。 プロダクトのアーキテクチャについて 以下の構成の SPA です。(インフラ部分は省略) フロント:Vue.js バックエンド:Ruby on Rails データベース:PostgreSQL 今回お話するのは Ruby on Rails 部分において Active Record を駆使して実現しているフィルター機能の部分になります。 プロダクトのデータ構造について このプロダクトは主に顧客情報を管理する機能を提供していて、その顧客ひとりひとりの情報を call_targets テーブルの 1 レコードとして永続化しています。 直接 call_targets テーブルのカラムとして持っている情報(電話番号、住所など)もあれば、別のテーブルで管理してリレーションさせている情報もあります。 たとえば、ある顧客との商談ひとつひとつを記録する opportunities テーブルなどです。 こういった call_targets テーブルとリレーションを持ったテーブルが 20 個以上 存在している状況です。 フィルター機能について こちらの GIF で操作している「絞り込み」の部分で、要は 絞り込み検索 になります。 前述した call_targets テーブルを軸として、条件に合致した顧客だけを絞り込んでいく、プロダクトの使い勝手を左右する機能です。 また、この条件というのも豊富に用意されており、 call_targets 自身が持つ電話番号などはもとより、商談の有無(前述した opportunities テーブル上にリレーションされているレコードがあるかどうか)や、ユーザーが自由に追加出来るカスタム項目に対しての入力値を絞り込めるようなものになっています。 フィルタ条件同士はフィルタの種類(会社名、電話番号、など)が違えば共存が可能で、条件間は AND 検索になります。 さらに、条件のそれぞれに対してそれを「含む」のか「含まない」のか、を指定出来るようにもなっています。 会社名でフィルタリングする 課題 実は、このフィルター機能はいくつか課題を抱えています。 書き直してみるモチベーションにもなったところでもあるので、箇条書きで簡単に説明します。 Active Record ではうまく実現できない部分がある 複雑な条件だと生の SQL が登場したりする 条件をパースして適切なフィルタオブジェクトに変換している 条件を Condition クラスとして表し、使う条件ごとにインスタンス化するなど可読性を上げる工夫をしているが、どうしても"機能に対して手間がかかっている感"が拭えない (上記のことなどから)コードが複雑で、見通しが悪い フィルタに関連するコードが散らばっている 以上がプロダクトとフィルタ機能の概要、そして抱えている課題になります。 前提としているデータモデルについて 今回定義しているモデルはそれぞれ以下のような役割を担っています。 call_targets: 架電対象の情報が保存される call_results: ひとつひとつの架電結果が保存される hearing_ranks: ヒアリングに成功した場合に記録され、架電対象のランクなどが保存される モデル同士の関係性は、まず call_targets モデルがあり、1:多でリレーションされる形で call_results モデル、さらに call_results モデルから 1:多で hearing_ranks モデルが存在するような形です。 Prisma のデータモデルで表すと以下のようなコードになります。 // 今回の記事で扱わないカラムは省略しています model call_targets { id Int @id @default(autoincrement()) company_name String @default("") company_address String @default("") call_results call_results[] } model call_results { id Int @id @default(autoincrement()) call_target_id Int? comment String? call_targets call_targets? @relation(fields: [call_target_id], references: [id]) hearing_ranks hearing_ranks[] } model hearing_ranks { id Int @id @default(autoincrement()) call_result_id Int? rank Int call_results call_results? @relation(fields: [call_result_id], references: [id]) } プロダクトに関しての前提条件の説明は以上になります。 ではさっそく、Prisma をこのプロダクトのデータベースに対してセットアップして試していきます! Prisma をセットアップする 公式サイトで紹介されている手順に沿って以下のように進めていきます。 数手順で完了する手軽さが素晴らしいですね。 今回の作業用ディレクトリの作成と Prisma のための準備 $ mkdir prisma-handson && cd prisma-handson $ npm init -y Prisma CLI のインストール $ npm install @prisma/cli --save-dev Prisma schema を作成 $ npx prisma init 新しく prisma ディレクトリが作られ、なかに .env と schema.prisma ファイルが作られていることが確認できれば OK です。 さらに、実行結果に表示されている Next Steps に従っていくつか設定を進めます。 .env に DB の接続情報を設定する .env を開き、以下のように接続情報を入力し保存します。 DATABASE_URL="postgresql://johndoe:randompassword@localhost:5432/mydb?schema=public" schema.prisma に 接続先の DBMS を設定する datasource db { provider = "postgresql" url = env("DATABASE_URL") } 上記の provider 部分を、 postgresql 、 mysql 、 sqlite の 3 つから接続先の DBMS と一致するように選びます。 データベースのスキーマから Prisma のデータモデルを生成する $ npx prisma introspect Environment variables loaded from /Users/user/prisma-handson/prisma/.env Prisma schema loaded from prisma/schema.prisma Introspecting based on datasource defined in prisma/schema.prisma … ✔ Introspected 63 models and wrote them into prisma/schema.prisma in 1.86s Run prisma generate to generate Prisma Client. ここで schema.prisma を見てみると、無事データモデルが作成されていることが確認出来るかと思います。 リレーションやNull許可、デフォルト値なども漏れなく作成されました また、このスキーマファイルは定義ジャンプも効くようになっているため、たとえばデータモデルのリレーションに登場した他のデータモデルがどうなっているか詳細を見たいといったときに便利です。 Prisma Client をセットアップする コード上から import して使用することになるクライアントを生成します。 下記のコードでパッケージのインストールとクライアントの生成が完結します。 $ npm install @prisma/client Environment variables loaded from /Users/user/prisma-handson/prisma/.env Prisma schema loaded from prisma/schema.prisma ✔ Generated Prisma Client (version: 2.11.0) to ./node_modules/@prisma/client in 1.70s You can now start using Prisma Client in your code: import { PrismaClient } from '@prisma/client' // or const { PrismaClient } = require('@prisma/client') const prisma = new PrismaClient() Explore the full API: http://pris.ly/d/client 以上で Prisma のセットアップは完了です! 動作確認する それでは、まずは動作確認も兼ねて簡単なクエリを実行してみます。 import { PrismaClient } from "@prisma/client" ; const prisma = new PrismaClient (); async function main () { const call_targets = await prisma.call_targets.findMany (); console.log ( call_targets ); } main () . catch (( e ) => { throw e ; } ) . finally (async () => { await prisma.$disconnect (); } ); main 関数のなかが実際にクエリを実行している部分です。 findMany メソッドは引数に取得条件を取るため、引数なしの場合は全件取得することになります。 アプリケーション上でいえば、このクエリは単純に一覧を表示するようなシーンで使用するクエリですね。 このファイルを $ ts-node scripts.ts で実行してみたところ、無事に結果が返ってきました。 フィルタ機能を模倣してみる ここからは、実際のフィルタ条件を紹介しながら、そのフィルタを実現する Prisma のコードを紹介していきます。 複数条件のフィルタ まずは「会社名( company_name )に テスト を含む」かつ、「住所( company_address )に 東京都 を含む」条件でフィルタしてみます。 // main以外は同様のため省略 async function main () { const call_targets = await prisma.call_targets.findMany ( { where: { AND: { company_name: { contains: "テスト" , } , company_address: { contains: "東京都" , } , } , } , } ); console.log ( call_targets ); } 上記コードで条件を満たした call_targets レコードを絞り込めます。 見ての通りになりますが、各条件をひとつのオブジェクトとして集約させられました。 where , AND など、構成する条件ごとにオブジェクトとしてまとめるような構造になり、見通しも良いです。 関連先を条件とするフィルタ 次は call_targets モデルの関連先である call_results モデルの特定のカラムに対して条件を設定するフィルタをかけてみます。 具体的に言うと、「紐付いているコール結果( call_results )のコメント( comment )に テスト を含むものが一つでも存在する」条件によるフィルタです。 これを実現するのは以下のコードのようになります。 async function main () { const call_targets = await prisma.call_targets.findMany ( { where: { AND: { call_results: { some: { comment: { contains: "テスト" , } , } , } , } , } , } ); console.log ( call_targets ); } 前出の条件でいう company_name といったカラムのように関連する call_results を指定でき、さらにその中で条件として some をオブジェクトのキーの形で設定します。 some は「ひとつでもマッチすること」を表し、他には every (すべてがマッチすること)や none (ひとつもマッチしないこと)といった条件が設定できます。 ネストされた関連先 最後に、ひとつ前の call_results モデルからさらに関連する hearing_ranks モデルの特定のカラムに対して条件を設定するフィルタをかけます。 つまり、 call_targets モデルから見ると二段階の関連をまたぐフィルタです。 今回は rank が 3 より大きい hearing_ranks レコードを関連に持つ call_results レコード、をさらに関連に持つ call_targets レコードを取得するようなフィルタリングを行います。 これは以下のようなコードで実現できます。 async function main () { const call_targets = await prisma.call_targets.findMany ( { where: { AND: { call_results: { some: { hearing_ranks: { some: { rank: { gt: 3 , } , } , } , } , } , } , } , } ); console.log ( call_targets ); } ネストされた関連先でも問題なくフィルタリングできました。 データモデルのネスト構造がそのままオブジェクトの深さに直結するので、ちょっとウッとなる見た目にはなってしまいますが、ネストが深くなろうと型推論がきっちり効くので、書く分には快適でした。 実アプリケーションで引数に地でこうしたオブジェクトを渡すことはないだろう(きっとビルダー的な関数でフィルタ条件のオブジェクトを組み立てるはず。。。)ことも鑑みて、こうして見た目上のオブジェクトの階層が深くなってしまうことはそこまでデメリットとは感じていません。 まとめ 駆け足になりましたが、Prisma でフィルター機能をいくつか書き換えてみました。 クエリを操作する豊富な API が用意されているため、他の複雑なフィルタ条件についても問題なく再現できそうな感覚を得られました。 プロダクトのフィルター機能を実際に Prisma で書き換える予定はありませんが、いつかやってみたいです! それでは! 参考 www.prisma.io www.prisma.io
アバター
スマートキャンプエンジニアの瀧川です。 みなさん、BigQueryは使っているでしょうか? BigQueryはデータのインポート、加工、エクスポートを便利にする機能が数多くリリースされており、ファンも多いことと思います。 かくいう私もBigQuery大好きマンとして社内で名を馳せていて、最近だと下の記事で笑顔になった1人です。 ユーザーを笑顔にする BigQuery の使いやすい SQL 新機能 | Google Cloud 公式ブログ 今回は2019年末にリリースされた BigQuery Scriptingに注目して、どんなものなのか、既存のワークフローの中でどう使えるのか を試してみようと思います! BigQuery Scripting 試しにFizzbuzz書いてみた 既存のワークフローの一部を置き換えてみる お題: 個人情報の秘匿化 実装方針 完成したScript(クエリ) まとめ BigQuery Scripting BigQuery Scriptingとは、2019年末にBeta版が発表された新機能となります。 今までのBigQueryでは単一のクエリを実行することしかできませんでしたが、これにより 複数のクエリの実行、変数・条件分岐・ループ が利用できるようになりました。 例えば「集計対象カラム一覧テーブルから取得したカラム名を動的に埋め込んだクエリ」みたいなメタクエリも簡単に書くことができます。 試しにFizzbuzz書いてみた 新しい言語を始めるときにみなさんはなにから書き始めるでしょうか? 私はなんだかんだでFizzbuzzを一回は書いている気がします。 (パターンマッチなどあるか、ifは値を返すかとか見てたりしますね) ということでBigQuery ScriptingでFizzbuzzを書いたのが以下になります。 declare i int64 default 1 ; declare result array<string> default []; create temp function fizzbuzz(num int64) as ( case when mod (num, 15 ) = 0 then ' FizzBuzz ' when mod (num, 3 ) = 0 then ' Fizz ' when mod (num, 5 ) = 0 then ' Buzz ' else cast (num as string) end ); while i <= 100 do set result = array_concat(result, [fizzbuzz(i)]); set i = i + 1 ; end while; select str from unnest(result) as str 変数宣言( declare )、UDF定義( create temp function )、ループ( while )、代入( set )を使って、他のプログラミング言語とほぼ変わらないロジックで書くことができました。 既存のワークフローの一部を置き換えてみる 今回、BigQuery Scriptingでなにができるかを見るために、実際に弊社で動いているワークフローの一部を BigQuery Scriptingのみ で実装してみることにします。 お題: 個人情報の秘匿化 弊社ではアプリケーションのデータ、ログをBigQueryに集約しています。 それをそのまま社内に公開してしまうと、個人情報が含まれるため問題となってしまいます。 そこで今は以下の画像の通り、DigDagとbqコマンドを使ってクエリを投げて秘匿化したテーブルを作成するようにしています。 (以前書いたこちらの記事も参考にしてください BigQueryだけでRedashから個人情報見えなくする方法解説 - SMARTCAMP Engineer Blog ) 生成された秘匿化クエリ /* 独自の設定ファイル masking: dataset: 'raw_dataset' tables: users: - column: first_name - column: email */ select * replace ( to_hex(sha256( cast (first_name as string))) as first_name , to_hex(sha256( cast (email as string))) as email ) from `raw_dataset.users` 今回はこれをBigQuery Scriptingで実装し直してみようと思います。 実装方針 秘匿化するテーブル、カラムをどこかで管理する必要がある(上述の設定ファイル相当)ので、今回はSpreadsheetで管理し、それをBigQueryにテーブルとして連携させておくことにします。 あとは秘匿化前のデータセットの INFORMATION_SCHEMA.TABLES から対象テーブルを抽出して、それに合致する秘匿情報を上記Spreadsheetのテーブルから取得して、動的にクエリを構築して実行という流れになるかと思います。 完成したScript(クエリ) 最終的なScriptが以下のようになりました。 多少冗長なところはあるにせよ、既存の処理とほぼ同じようなScriptができました。 これがBigQueryで直接動かせるのは楽ですし、BigQuery Schedulingで定期実行すれば管理コストがとても低くなるなと思います。 (1点詰まったのは、ドキュメントでは execute immediate でplaceholderが利用できるっぽかったのですが、syntaxエラーでできませんでした🤔) 実行時間も気になるところだと思いますが、今回サイズがまちまちな100テーブルを対象に実行してみて10分程度でした。 既存のワークフローだと都度APIリクエストをしているので、今回のScriptのほうが早いのではないかと思ってましたが、そこまで変わらない(または遅い)かなと思います(既存ワークフローは他の処理も混じってるので単純な比較はできてないですが)。 declare input_dataset string default ' raw_dataset ' ; declare output_dataset string default ' masked_dataset ' ; declare mask_master_table string default ' master.masking_columns ' ; # temp vars declare target_table_names array<string>; declare target_table_name string; declare i int64 default 0 ; declare replace_columns_statement string; # e.g. [ ' users ' , ' profiles ' , ' corporations ' , ...] execute immediate format( """ select array_agg(table_name) from %s.INFORMATION_SCHEMA.TABLES """ , input_dataset) into target_table_names; while i < array_length(target_table_names) do set target_table_name = target_table_names[safe_offset(i)]; # マスクするカラムからreplace()のパラメータを生成 # e.g. to_hex(sha256( cast (full_name))) as full_name, to_hex(sha256( cast (email))) as email execute immediate format( """ select string_agg('to_hex(sha256(cast(' || column_name || ' as string))) as ' || column_name, ',') from %s where table_name = '%s' """ , mask_master_table, target_table_name) into replace_columns_statement; execute immediate format( """ create or replace table %s as select * %s from %s """ , output_dataset || ' . ' || target_table_name , ifnull(format( ' replace(%s) ' , replace_columns_statement), '' ) , input_dataset || ' . ' || target_table_name ); set i = i + 1 ; end while まとめ BigQuery Scriptingいかがでしたでしょうか? データ基盤やインフラに知見がないエンジニアでもBigQueryで完結して、さくっとデータを加工するバッチを作成することができるので、有用だと感じています。 Spreadsheet連携やDataStudio連携など周辺機能も多く、とにかくBigQueryに入れればなんとでもなる感がとてもいいですよね! これからもBigQueryにはお世話になります。
アバター
こんにちは!!!スマートキャンプ、エンジニアの吉永です。 私は8月にスマートキャンプに中途入社し、今月で3ヶ月目となります。 前職では受託開発を主にした小さな企業に未経験で入社し、そこで一年間フロントエンド、バックエンド問わず開発したり、テックリードのような業務も行ったりしていました。 小さな会社なので部署というような区切りはほぼ無く、社長含め全てのメンバーがエンジニアといったようなエンジニア集団の環境で、日々開発タスクをこなしていました。 しかしある時期をきっかけに、外部の方と協力する機会が増え、エンジニアだけがいる環境から様々な人間が関わる環境へと変わっていき、とあるプロジェクトを進めている最中、私達エンジニアサイドと、企画サイド、デザイナーサイドでうまく噛み合わず、スケジュールが大幅に遅れてしまいました。 そして、このことをきっかけに私自身がエンジニア以外の人間に対して苦手意識を持ってしまい、なかなか克服することができずにいました。 この記事では、スマートキャンプが転職後の私に対してどのような意識の変化をもたらしたのか、どう克服したのかを書きたいと思います。 注 : 前職での失敗例などが出てきますが、今でも交流のある大好きな会社ですし、disりたい訳ではないです。噛み合わなかったことには理由があると考えており、今後に反省が活きれば良いと感じています。 失敗を振り返る まずは苦手意識を持つまでの流れを少し振り返ってみたいと思います。 この時のチーム構成 エンジニア(4人) 企画(3 ~ 4人) デザイナー(1人) といったようなメンバー構成でした。 この時の言い分 エンジニア 僕達は悪くない デザイン確定後もXDが無言で変更されている Atomic designを採用しろと言われたが、XDで上がっているボタンやモーダルのサイズが尽く違う 知らされていない仕様や当初からわかっていた厳しめスケジュールで追い詰められていた 企画(3 ~ 4人) 僕達は悪くない スケジュールはこれで行けると思っていた 仕様など考慮が足りない部分は申し訳ないが、既存の機能を踏襲していたりしてどうしても変更が難しかった デザイナー(1人) 僕は悪くない しっかりパーツ作って欲しかったのに1 ~ 3pxズレてるんだけど... 僕の中ではこの部品はこういう動作をする予定だったんだけど... という感じで、各役割ごとに言い分があり、それぞれの部署で他の部署に対する共有していない要望や考えを持ち合わせていました。 では、具体的に何が悪かったのかを自分なりに説明してみたいと思います。 噛み合わなかった原因 暗黙知の部分を形式知だと思い込んでいた 開発は開発、デザインはデザイン、企画は企画という意識が邪魔をして、お互いにお互いのことを指摘できなかった その割には誰が何を決めるという部分が不明確で、仕様とデザインで一体となったものにならなかった プロジェクトを進める上で、各個人には何かしらのここだけは譲れないポイントがあるのは当然のことです。 しかし、それは自分だけの世界で常識だと思い込んでいる部分もあり、相手にとってそれが常識とは限りません。 エンジニアとデザイナーの間で起きた問題 エンジニア なんか12.6みたいなよくわからん幅のパーツあるけど、13でええやろ!w なんかXDがデザイン決定後も大量に更新されてるけどどれが正解なんだろ...自分は最新版にしておこう。(結果メンバーごとに持っているXDのデザインバージョンが違う) ここ、実装上かなり難しいぞ...こんなところに時間とってる暇ないんだけどな... デザイナー(1人) 急いで作ったけど、ピクセルのズレとか無いよね...?このパーツは全部12で揃えたいな! ここのパーツの幅が思ってたよりも広いな...ちょっとだけだし修正しちゃおう! これ機能にはあまり関係ないけど、こちらの方が視認性が良いしこういうデザインにしておこう! といったような、各々の心の中で完結してしまっていた問題がかなりありました。 これは単にコミュニケーションが不足していたことで起きた問題で、適度に自分の思いを共有していれば防げた問題でした。 また、エンジニアという立場ながらデザイン的に疑問に思ったことなどが多々ありましたが、自分の管轄外だと思っていたことにより指摘をせず、結果余計な工数がかかってしまったり、「実装してみたけどやっぱり微妙だったね」といった結果を産んでしまうことになりました。 企画サイドとデザイナーサイドの間で起きた問題 仕様などを決めていた企画サイド、デザイナーサイドでも同じようにコミュニケーション不足による齟齬を生み、結果エンジニア、デザイナー、企画でそれぞれ違う仕様に対する理解をしてしまっていました。 全体で起きていた問題 一番の理由となったのは、各自が自分達はあまり悪くないという態度を取ってしまったことが原因だったように思います。 振り返ってみると各サイドごとに出来ていた部分、出来ていなかった部分は可視化されていますが、当時はそれを考えることすらせず、僕たちはしっかりやったけどあっちの対応は甘かったというように感じてしまっていました。 結果、エンジニア、デザイナー、企画の役割がうまく回らず、スケジュールが遅延した原因だと考えています。 しかしながら、こうした過去の失敗からなかなか抜け出すことができず、私はこうしたエンジニア以外との仕事の仕方について、やりにくさや苦手意識を持つようになってしまいました。 スマートキャンプに入社後、どうなったのか 前職とは違い、スマートキャンプには開発チームの他に、セールスやメディア、マーケティング、人事など数々の部署が存在しています。 しかし、これらの部署間でいがみ合うこともなく、部署間のコミュニケーションがかなり円滑に進んでいることに驚きました。 ここからは、私が入社後に感じた、他部署との連携をうまく取るために必要だった施策を紹介したいと思います。 1. やりたいタスクは、それをやりたい人間が説明する スマートキャンプのプロダクト部には毎朝、今週から来週あたりにかけてやりたい施策の説明をし、それにかかる期間(ポイント)をエンジニアが決める朝会と呼ばれる会が存在します。 朝会は基本、PdM、エンジニア全員、デザイナー一名、プランナーの方一名で行われます。 この時、細かい連携上発生したタスクや、計測結果から予測した改修タスクを説明するのはPdMが担い、デザインタスクや戦略部内で発生したタスクを説明するのは、朝会に参加している他部署の代表の人が説明します。 そして説明後に、実装するエンジニアと どのように実装するか 不明点に対する質問 かかりそうな期間と優先順位 などを話し合い、一つの案を実際のタスクへと落とします。 タスクを持ち寄った本人から説明を受けた上でわからないことがあれば都度質問できるため、認識の齟齬の原因となっていた暗黙知の部分を解消することができます。 そして、これにはもう一つ大きな意味があります。 例えばデザインタスクの説明を受けた上で、「やれなくはないかもだけど、かなり難しいよ」という部分があったり、「本来の設計上難しい」などエンジニアのみがこれまでは把握していた知識をデザイナーにも直接共有することができます。 そうすることにより、仕様上実装が難しい部分の早期発見や、「これはできないけど、こうなれば可能」と言った逆提案も起きるようになるため、より活発に議論することができるようになっていました。 2. 疑問点を見つけた時に、本人に直ぐ連絡できるオープンな環境がある スマートキャンプのslackには、 #boxil_dev_困 という、名前の通り実装上や業務上、困ったときに本人または誰かに質問するチャンネルが設けられています。 上の画像は、 #boxil_dev_困 に私が投稿した質問の一つです。 この時、私が聞きたかった内容は以下の通りでした。 パーツのCSS共通化を行いたかった デザインは同じだがサイズがほんのちょっと違うボタンが複数個あり、それを統一してもいいか。(ボタンサイズの差異は意図的なものか) ほんの些細なサイズの違いですが、もし意図してこのサイズになっていたとしたら勝手に変えてしまうと、本来デザイン時に目指していたものと、実際リリースされたことに違いが生じてしまいます。 また、意図していないものだとしたら、実装してしまうと後から「やっぱ違うな」と感じてしまい、手戻りが発生してしまうこともあります。 「デザイナーがこう指定してきたから」とか「こういうものを作ってくれと言われたから」という受け身の考え方ではなく、自分が思った当事者としての意見や、指摘を直ぐに共有することができるのが、このチャンネルの強みだと思っています。 どんな些細なことでも質問、指摘し、最終的な完成物の精度を上げていくことで、すれ違いを最低限に減らすことができます。 3. 他の部署が感じたプロダクトへの些細な違和感を、すぐに共有してもらえる環境がある 私は開発をする以上、バグはつきものだと考えています。(無い方が良いですが) また、どれだけ密接にコミュニケーションを取っていても、当時想定していなかった仕様や実装依頼が来ることもあります。 スマートキャンプのslackにある、 #boxil_product_request というチャンネルは、どこかのタイミングで起こってしまったバグの修正依頼や、追加の対応依頼などを、 気がついた人が誰でも依頼できる 場所になっています。 ここに投稿された依頼は朝会でPdMなどから共有された後ポイントをつけ、タスクに積まれていきます。 実装している私達には、ユーザーの気持ちを直接知ることのできる機会がかなり限られてしまっているため、実装した時点ではイケていると思った実装も、ユーザーにとっては使いにくかったり、コードレビュー段階では気が付けなかったバグが存在していたりします。 その制作物を、第三者視点で見た上で、実際に起きていた違和感に気が付き、いち早く共有してもらえる環境はエンジニアとして実装している私からしたら、ありがたい以外の言葉が見つかりません。 実際に、気がつかなければ大損害を招いていた可能性をある不具合などをいくつか報告していただいている上、「こういう機能がユーザーとしての要望が多いのですがどうでしょう」と言ったような提案を日々していただいており、個人的にはこのチャンネルが一番すれ違わないチーム開発をするための効果を発揮していると思います。 まとめと反省 スマートキャンプに限らずですが、何かプロジェクトが走るときにはエンジニアだけで完結することはまずありません。 チームとして行動するのであれば、先ずはお互いのことをよく知り、信頼関係を結ぶことが最優先でしたが、 前職にいた時点での私は恐らくその意識をエンジニア以外のメンバーに向けることができず、私だけの常識と、他人の常識の暗黙知となる部分を上手くすり合わせ出来ていなかったのだと思います。 その結果として、失敗体験のトラウマを自身に植え付けてしまいました。 スマートキャンプでは、そのプロジェクトに関わっている以上部外者という存在はおらず、全員が当事者となれる施策が多数あるため、入社したばかりの私でも様々な情報をslackや他の人から気兼ねなく得ることができました。 また、エンジニア主催で週に一回開発進捗を全部署に共有するSprint Reviewという会が開かれていたり、週次の朝礼や ピザパ のような全社員で情報を共有できる場があるため、全員が同じ情報を共有できる点においてかなり優れていると感じました。 最後となりましたが、退職した今も交流をしてくださっている前職の方々(この記事を見ていれば)や、この一件に関する相談に乗っていただいた方々、本当にありがとうございました。 この記事が境遇問わず、同じような悩みを抱えている方に届けば幸いです。 参考になった記事 note.com tech.smartcamp.co.jp 一緒に読んで欲しい記事 毎月開催!スマートキャンプのピザパーティを紹介します! #Collaboration|スマートキャンプ公式note『.▲.tent.』|note スマートキャンプの伝統文化「朝会」を紹介します! #Collaboration|スマートキャンプ公式note『.▲.tent.』|note
アバター
スマートキャンプの郷田です。 ​ 先日、本年度のpmconfに参加させていただきました。 pmconfとは、2016年から年に1度開催されるプロダクトマネジメントに携わる人たちが学び・切磋琢磨する場が提供されるカンファレンスです。 https://2020.pmconf.jp/ 私は昨年のpmconf2019から参加させていただいているのですが、昨年のカンファレンスで得た知見を実際にこの1年実践してきたものを紹介したいと思います。​ pmconf2019への参加まで 岡田 康豊さん:“失敗事例で学ぶ” 失敗しないプロダクトマネジメント - PMの必須スキルと、自走する組織のつくりかた - 小城 久美子さん:プロダクトの強い軸を作るプロダクトマネジメントフレームワーク 二木 祥平さん:LINEにおけるお金とユーザーのジレンマ 全セッションを通して その後の変化:社内でのPM認知 最後に ​ pmconf2019への参加まで ​ もともとの弊社はプロダクトマネージャー不在で、この役割を複数人で担っている状況でした。 また私も、プロダクトマネージャーという役割を全く知りませんでした。 ​ そんななか、自身が事業に関わっていく中でPMが必要かもしれないと気付き、社内で自称PMと名乗るまでについては以下記事にまとめて書いております。 ​ https://tech.smartcamp.co.jp/entry/2019/09/20/201824 ​ 自称するようになりPMに関連する書籍を読んだりはしていましたが、PMがどういった役割なのか自分ごととして噛み砕いて理解できていない状況が続いていました。 ​ そこで、PMとは何か?を学ぶためにpmconf2019に参加させていただきました。 ​ 実際に他社でPMとして働かれている先人の考えと行われてきた施策を知り、参加後は得た学びを実際に現場で試してみていました。 ​ 実際に参加したセッションごとで、学び・実践してきたことの一部を以下紹介いたします。 ​ 岡田 康豊さん:“失敗事例で学ぶ” 失敗しないプロダクトマネジメント - PMの必須スキルと、自走する組織のつくりかた - ​ PMは「やれることは何でもやる!」ことを求められる役割かと思いますし、私自身もそのスタンスで業務をしています。しかし、やれることを何でもやるためには一定のスキルが必要になるものの、どのスキルをつけていけばいいかは常に迷っておりました。 ​ 岡田さんはPMのテクニカルスキルをPM SkillChart HEXとして6つのスキル領域に分けて定義されております。 ​ こちらのセッションを元に、自身の経験ができていないスキルを理解することができました。 ​ 自身に当てはめた時、明らかに足りていなかったスキルであるDesign領域に関してはこのセッションをきっかけに実務として行い、最低限のデザインスキルはつけることができました。 ​ Figmaでのプロトタイプを自身で作成し、仮説検証に利用する 開発着手後のデザインに関するエンジニアからの壁打ち 受け入れ確認時にデザインに関する詳細なフィードバック ​ 実際にデザインを作ることで今までデザイナーが抱えている課題を身をもって経験でき、デザイナーの視点を身につけることでプロダクトの仮説検証、開発量フェーズでの効率化を推進することができたように思います。 ​ また、PM SkillChart HEXに関して別の利用用途ではありますが、PM以外の人に「今、私が何のスキルを伸ばそうとしているか?それはなぜか?」といった説明も簡潔に行えるようになり、今でも非常に重宝しているスキル定義となっております。 ​ 小城 久美子さん:プロダクトの強い軸を作るプロダクトマネジメントフレームワーク ​ 紹介されているインセプションデッキはすでに実施しており、また他のフレームワークも認識はしておりましたが、各フレームワークの役割や繋がりまでは体系立った理解をできておりませんでした。 ​ こちらのセッションでは、フレームワーク毎の関連とその使い方の事例が紹介されていましたため、今の事業に必要ではあるものの実施できていなかったフレームワークを明確にすることができました。 ​ セッションでの学びを元に、すでに仮説立てをしていた機能に関して、それまで実施できていなかった以下のようなことを行いました。 ​ 実ユーザーを元にロールごとでのペルソナの定義 ペルソナ毎でのバリュープロボジションキャンバス作成 Whyを元にCoreとのFitを確認 Whyを元にユーザーストーリーの作成 ​ これらにより、仮説の納得度が上がり、それまでと比べて解決策がとてもシャープになったように思います。 ​ 実際にこれらを行った仮説をユーザーに当てたときの反応が段違いに変わったため、今はこのサイクルのスピードと精度を高めていくことに注力しています。 ​ 二木 祥平さん:LINEにおけるお金とユーザーのジレンマ ​ ​ 当時のプロダクトの状況はPSFを探す段階でもあるため、混沌とした状況でプロダクト作りを推進していました。 ​ 人数も少なく、一人一人の思想がプロダクトに色濃く反映される状況のため、各所でハレーションが起きてしまっておりました。 ​ こちらのセッションでは、PMは「ジレンマ回収役」であり「PMの無能化により、妥協の産物が生まれる」と紹介されております。 ​ 当時はスプリントレビュー後にステークホルダーごとのmtgでそれぞれに向けた解説も必要であったり、実ユーザーに使われない機能をリリースしてしまっていたりと、自分が無能化していることに気付かされる言葉でした。 ​ こちらの言葉を元に、ジレンマを改善するために妥協するのではなく、複数ステークホルダーと仕事を行えるPMの特性を活かしステークホルダーの意思の背景を理解することによってプロダクトのビジョンやミッションとつなげてジレンマの解消をすることができるようになってきました。 ​ 結果としては、スプリントレビューでの事後MTGが不要になり全ステークホルダーを含むスプリントレビュー内で同一の前提のもとディスカッションを建設的に進められる状況まで変わりました。 ジレンマ解消が進むことにより関係者全員でユーザーが必要とする機能にフォーカスできるようにもなり、「これが欲しかった」と言われる機能開発を進めていけるようになりました。 ​ 全セッションを通して ​ 本記事では、参加させていただいた一部のセッションのみ紹介させていただきましたが、すべてのセッションからPMの現場に直結するノウハウを得ることができ、どのような業務を推進する場合においても自分の引き出しが一気に増えたことが一番の収穫となりました。 ​ ​ その後の変化:社内でのPM認知 ​ pmconfで学んだことを自身が取り組ませていただく中で、「プロダクトマネージメント」についても認知が広がってきたように思うため、この記事を書くにあたって社内の各役割の方に質問をしてみました。 ​ 質問 「プロダクトマネージャーの役割と仕事は何だと思いますか?」 回答 プロダクトの未来の姿から逆算をして、求められる要件の定義、開発・営業への共有、進捗管理 プロダクトの成果を最大化させるための計画立案をすること 企業成長(組織成長)に必要な、長期的ゴール設定をし、短期的な目標に落とし込み、タスクを振り分け、メンバーのアサインし、それぞれの目標達成のためのコスト・リソース・精神面での調整や支援を行うオーナーポジションの人 プロダクトマネージャーは社内外のステークホルダーを整理してPMFを推進していく際の最重要ポジションであり、ビジネスの鉄則となる”価値あるプロダクト”を”必要としている市場”に供給していくために必要不可欠な存在です。 プロダクトの価値を最大化するための方針や戦略を決め、開発とビジネスとユーザーを繋ぐ役割 プロダクトが成長しやすい環境を社内外に作っていく人 プロダクトにまつわる意思決定をまとめる要となる役割であり、マーケティングからメンバーの管理や期待値調整まで幅広くプロダクトの関心事を取り扱う仕事 最高のプロダクトを作り、ビジネスとしても成功させるために必要な全ての役割。その中で特にプロダクトの付加価値UPのために貢献できる仕事 ​ 実際に質問をさせていただくことで、社内でPMにどのような期待をされているかを知れたため、気が引き締まる思いです。 ​ 最後に ​ 本記事ではpmconf2019での学びと実践を紹介させていただきました。 pmconfはプロダクトマネージメントがより前に進むための定量・定性の両面で得れるものばかりで、運営者・登壇者・スポンサー企業・参加者の皆様にこのような場を作っていただき感謝しております。 先日開催されましたpmconf2020で新たに学んだことも多くあるため、これから試していきたいと思います。
アバター
スマートキャンプの20卒エンジニアの高砂です! 私は弊社の SaaS比較サイト「BOXIL」 の開発に携わっており、フロントエンドを中心に様々な機能を実装しています。 そんな中、Vue.js + TypeScriptで実装した機能群が複雑になってきた事から「より丁寧にテストを書いていきたい」という気運がチーム内で高まっていました。 そこで、元々Vue.jsが好きな私(下記参照)が試しにJestを触ってみる事にしました。 tech.smartcamp.co.jp 「せっかくならVue.jsはBOXILで使われている2系ではなく最新の3系で試してみたい」という個人的な興味もあり、「Vue 3 + TypeScript + Jest」という組み合わせで新規アプリ開発およびテスト作成に取り組んでみました。 本記事では、それらを実際に進めた際の試行錯誤を紹介していきます! Vue 3 + TypeScriptでプロジェクト新規作成 Jestの導入 失敗1. 公式ドキュメントの手順通りに導入 失敗2. 公式ドキュメントの手順 + 一部ライブラリをVue3系向けのバージョンで導入 失敗3. 公式ドキュメントの手順 + デモリポジトリのバージョンを参考にライブラリを導入 成功? デモリポジトリをcloneしてテスト環境構築 テストを書いてみる まとめ Vue 3 + TypeScriptでプロジェクト新規作成 まずは新規Vue 3プロジェクトを作っていきます。 今回は最新のVue CLIを採用する事で、素早くVue 3でのSPA構成を構築すると共に、容易なTypeScriptの導入も実現します。 $ yarn global add @vue/cli yarn global v1.22.10 (中略) success Installed "@vue/cli@4.5.8" with binaries: - vue ✨ Done in 31.51s. $ vue create hello-vue3 Vue CLI v4.5.8 ? Please pick a preset: Manually select features ? Check the features needed for your project: Choose Vue version, Babel, TS, Linter ? Choose a version of Vue.js that you want to start the project with 3.x (Preview) ? Use class-style component syntax? No ? Use Babel alongside TypeScript (required for modern mode, auto-detected polyfills, transpiling JSX)? Yes ? Pick a linter / formatter config: Prettier ? Pick additional lint features: Lint on save ? Where do you prefer placing config for Babel, ESLint, etc.? In dedicated config files ? Save this as a preset for future projects? No (中略) 🎉 Successfully created project hello-vue3. 👉 Get started with the following commands: $ cd hello-vue3 $ yarn serve 注意点として、TypeScriptを導入する為に下記2点を忘れず行う必要があります(下記画像も併せて参照のこと)。 Manually select features オプションを選択する TypeScript を選択する TypeScriptの導入設定 プロジェクト新規作成自体はこれで完了です! 初期ファイルにTypeScriptが使われている事が確認できました。 // src/App.vue < script lang = "ts" > import { defineComponent } from "vue" ; import HelloWorld from "./components/HelloWorld.vue" ; export default defineComponent ( { name: "App" , components: { HelloWorld } } ); < /script > 実際に起動してみても、TypeScriptが使われている事が分かるかと思います。 Vue 3 + TypeScriptの初期画面 Jestの導入 次にJestを導入するのですが、ここでいくつかハマりどころがありかなり苦戦したので丁寧に紹介していきます。 失敗1. 公式ドキュメントの手順通りに導入 最初は下記公式ドキュメントを参考に導入してみました。 vue-test-utils.vuejs.org まずは下記を実行してJestおよびVue Test Utils(単体テストライブラリ)をインストールし、 package.json に追記を行います。 $ yarn add --dev jest @vue/test-utils // package.json { " scripts ": { " test:unit ": " jest " } } 続いて下記を実行して *.vue ファイルを処理するためのプリプロセッサをインストールし、再度 package.json に追記を行います。 $ yarn add --dev vue-jest // package.json { " jest ": { " moduleFileExtensions ": [ " js ", " ts ", " json ", " vue " ] , " transform ": { " .*\\.(vue)$ ": " vue-jest " } , " testURL ": " http://localhost/ " } } 次に下記を実行し、 package.json および tsconfig.json に追記をしてトランスパイルや型推論ができるように変更します。 $ yarn add --dev ts-jest @types/jest // package.json { " jest ": { " transform ": { " ^.+\\.tsx?$ ": " ts-jest " } } } // tsconfig.json { " compilerOptions ": { " types ": [ " jest " ] } } そして最後に package.json に下記を追記して 、.ts のテストファイルを実行するように変更を行います。 // package.json { " jest ": { " testRegex ": " (/__tests__/.*|( \\ .|/)(test|spec)) \\ .(jsx?|tsx?)$ " } } これで上手くいく…と思ったのですが、後述のテストを実行してみると下記のエラーが出ました。 Cannot find module 'vue-template-compiler' from 'node_modules/@vue/test-utils/dist/vue-test-utils.js' そこで「無いのか…とりあえず追加しておこう」と yarn add --dev vue-template-compiler を実行したところ、今度は下記のエラーが出ました。 Vue packages version mismatch: - vue@3.0.2 (/Users/joe/study/hello-vue3/node_modules/vue/index.js) - vue-template-compiler@2.6.12 (/Users/joe/study/hello-vue3/node_modules/vue-template-compiler/package.json) この辺りで「もしかしてVue3系とここまでにインストールしてきたパッケージのバージョンの互換性が無いのでは…?」と思いました。 そこでその辺りについて調べてみると、下記のリポジトリを見つけました。 github.com ここで「これはVue3系だと公式ドキュメントの通りではダメそうだ」と思い、次のやり方で再導入してみました。 失敗2. 公式ドキュメントの手順 + 一部ライブラリをVue3系向けのバージョンで導入 基本的には失敗1の手順と同じですが、下記ライブラリについては @next の方をインストールしています。 $ yarn add --dev @vue/test-utils@next vue-jest@next この状態なら実行できるかも、と期待したのですが今度は下記のエラーが出ました…。 TypeError: tr.configsFor is not a function この tr.configsFor というのがかなり内部的なモノのようで、類似の事例もほぼ見つからない為、一旦考え方を切り替える事にしました。 失敗3. 公式ドキュメントの手順 + デモリポジトリのバージョンを参考にライブラリを導入 そこで今度は「今回の組み合わせはかなり限定的なバージョンの組み合わせでしか実現できない or そもそも実現不可能なのでは」と捉え、既に動いている実例を探しました。 すると下記リポジトリを見つけたので、これの package.json および yarn.lock を参考にしたバージョンでインストールをしてみました。 github.com これと同じバージョンなら行ける!と思いきや、またもやエラーが…。 そもそもこのリポジトリはconfigファイルも細かく分けており、一つずつ設定を揃えるのも難しいという判断になりました。 成功? デモリポジトリをcloneしてテスト環境構築 これらの失敗から「現状はVue.js、TypeScript、Jestそれぞれに精通していないと実現は難しい」と思い、先ほど紹介したデモリポジトリをそのまま今回のテスト環境として使わせて頂くことにしました。 本当は自力で環境構築をしたかったのですが、今回のゴールである「『Vue 3 + TypeScript + Jest』という組み合わせでテストを書く」ことを優先し、このような判断としました。 テストを書いてみる それでは早速、この環境でテストを書いてみます! 今回はテスト用に下記のような画面を作成したので、これに対してテストを記載していこうと思います。 テスト用に編集した初期画面 // src/App.vue < template > < div > < h2 > Vue 3 + TypeScript + Jest < /h2 > < Hello @say = "say" / > Message: {{ msg }} < /div > < /template > < script lang = "ts" > import { defineComponent } from 'vue' import Hello from './Hello.vue' export default defineComponent ( { name: 'App' , components: { Hello } , data () { return { msg: '' } } , methods: { say () { this .msg = 'わーい' } } } ) < /script > // src/HelloWorld.vue < template > < div class= "hello" > < h3 > Contain Test < /h3 > < ul > < li > {{ string }} < /li > < li > {{ integer }} < /li > < li > {{ float }} < /li > < /ul > < h3 > Click Test < /h3 > < button @click = "say" > say < /button > < /div > < /template > < script lang = "ts" > import { defineComponent } from 'vue' export default defineComponent ( { name: "HelloWorld" , props: { msg: String } , data () { return { string : "smartcamp" , integer: 100 , float : 0.1 } ; } , methods: { say () { this .$emit ( 'say' ) } } } ); < /script > テストを書く場合、今回だと下記のように記載します。 // src/App.spec.ts import { mount , shallowMount } from '@vue/test-utils' import App from './App.vue' import Hello from './Hello.vue' test ( 'uses mounts' , async () => { const wrapper = mount ( App ) expect ( wrapper.html ()) .toContain ( 'Vue 3 + TypeScript + Jest' ) expect ( wrapper.html ()) .toContain ( 'smartcamp' ) expect ( wrapper.html ()) .toContain ( 100 ) expect ( wrapper.html ()) .toContain ( 0.1 ) expect ( wrapper.html ()) .toContain ( 'Message: ' ) await wrapper.find ( 'button' ) .trigger ( 'click' ) expect ( wrapper.html ()) .toContain ( 'Message: わーい' ) } ) test ( 'uses shallowMount' , async () => { const wrapper = shallowMount ( App ) expect ( wrapper.html ()) .toContain ( 'Vue 3 + TypeScript + Jest' ) expect ( wrapper.html ()) .not.toContain ( 'smartcamp' ) expect ( wrapper.html ()) .not.toContain ( 100 ) expect ( wrapper.html ()) .not.toContain ( 0.1 ) expect ( wrapper.html ()) .toContain ( 'Message: ' ) // @ts-ignore await wrapper.findComponent ( Hello ) .vm.$emit ( 'say' ) expect ( wrapper.html ()) .toContain ( 'Message: わーい' ) } ) そして下記を実行する事で、テスト実行とその結果が表示されます! $ yarn test yarn run v1.22.10 PASS src/App.spec.ts ✓ uses mounts (31ms) ✓ uses shallowMount (4ms) Test Suites: 1 passed, 1 total Tests: 2 passed, 2 total Snapshots: 0 total Time: 2.568s, estimated 3s Ran all test suites. ✨ Done in 3.59s. まとめ 初めてこの組み合わせでの実装を試してみましたが、導入自体はほとんどが自動化されているものの、バージョンの調整はかなり難易度が高かったです。 github.com 今回用いたコードは上記リポジトリにまとめてありますので、ぜひ参考にしてみてください。 お読みいただき、ありがとうございました! [参考] TypeScript と一緒に使う | Vue Test Utils Vue.js + Jest + TypeScriptのプロジェクトを作る - Qiita GitHub - vuejs/vue-test-utils-next: The next iteration of Vue Test Utils, targeting Vue 3 GitHub - lmiller1990/vtu-next-demo: Demo repo with vue-test-utils-next, TS and vue-jest
アバター
 こんにちは、 https://boxil.jp を作っている徳田( haze_it_ac )です。 先月に今風?な構成のAPIを業務で作ったので、その紹介をしようと思います。 作るもの・要件 雑な図 外部のAPIを叩くためのアプリケーションです。 BOXILのAPIサーバから今回作るAPIを叩き、そこから外のAPIを叩いて情報を取得したり、処理をしたりするものです。 現時点ではBOXILのみで使われていますが、それ以外からも使用されることを予定・想定しているため、BOXILとは別の基盤で作成しどこからでも実行できるように構築する必要があります。 なお、今回のサンプルリポジトリは以下になります。ソースコードと合わせて読んでみてください。 github.com 全体構成 概要 雑な構成図 AWS CDK を中心に据えた、 AWS Lambda で実行されるアプリケーションです。 実行環境 Webアプリケーションサーバとしての構成は必要なかったため、サーバレスのAWS Lambdaを使用しています。 他のアプリケーションからリクエストを受け取るためにAPI Gatewayを使って受け取ります。 言語はJavaScriptです。(AWS CDKによりTypeScriptをそのままDeployすればよしなにやってくれるので、実際に書くのはTypeScriptのみです) CI/CD 実行環境としてGitHub Actionsを、CDにはAWS CDKを使っています。 CIはESLintを流しています。テストはちゃんと書いていません🙃 ローカル開発 変更したコードをその場で即座に動作確認をしたいので、ローカルで実行環境を用意します。 AWS SAM CLIを使うことで、ローカルにLambda相当の環境を作ることができて便利です。 npm run build で吐き出されたJavaScriptが実行されます。 構成要素の紹介 AWS CDKについて aws.amazon.com AWS Cloud Development Kit 、通称CDKは、AWSが用意している開発ツール、フレームワークです。 「コードを書いて cdk deploy を実行するとよしなにデプロイしてくれるマン」で、裏はCloud Formationで構成されています。 今回はAWS Lambda, API Gatewayをデプロイするために使いましたが、ECS、DynamoDB、その他諸々のデプロイにも使えます。便利。 GitHub Actions github.co.jp 去年の5月に一般公開されたGitHubの中に埋め込まれたCI/CD実行環境です。 1リポジトリにつき20並列まで動くので速いのが特徴。便利。 AWS SAM CLI github.com CLIでLambda Functionsを実行できるやつ。今回はローカルで開発する際にこれを使います。 template.yaml を作成し、 local start-api -t template.yaml で実行できます。 動かす際には npm run build を忘れないようにしましょう。 実装説明 ディレクトリ構成 cdk init app --language=typescript によって大まかな構成が出来上がります。 BranchとDeployフロー 特定のbranchへのpushをフックに、GitHub ActionsのJobを走らせています。 .github/workflows/development.yml [ development.yml ] name : development on : push : branches : - develop ... - name : CDK Deploy if : contains(github.event_name, 'push' ) run : cdk deploy --require-approval never env : ENV : development AWS_DEFAULT_REGION : 'ap-northeast-1' AWS_ACCESS_KEY_ID : ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY : ${{ secrets.AWS_SECRET_ACCESS_KEY }} 今回のリポジトリの場合、 develop へのpush (merge)でstaging環境へ、 master へのpush (merge) でproduction環境へリリースする流れとなります。 デプロイ時に使用するシークレット情報は、GitHubのSecret設定で環境変数を設定できます。 設定画面 困ったことと解決方法 Lambdaのnode_modules配置場所の問題 AWS CDKやAWS SAMでは、デプロイする先のディレクトリ(今回の場合は /src )を指定して、そのソースコードをLambdaに送るという動きをします。 そのため、該当のディレクトリに /node_modules を配置する必要がありますが、 cdk deploy 等の処理を実行する場所は cdk.json 等のあるリポジトリのルートディレクトリである必要があり、そこにも同一の node_modules が必要となります。 一般的には Lambda Layers を使用するところな気がしますが、CDKでうまく使用する方法が軽く調べても出てこなかった (調査力が足りないだけかもしれない) ため、シンボリックリンクを使い解決しました。 https://github.com/haze-it/github-action-and-aws-cdk-sample/blob/develop/package.json /package.json -> /src/package.json , /package-lock.json -> /src/package-lock.json , /node_modules -> /src/node_modules のシンボリックリンクを作成することで、実態は /src にありつつ、各コマンドは正常に実行できるようにしています。 もっと良い解決方法があれば教えてほしいです... 🥺 コールドスタート問題 Lambdaはホットスタンバイとなっている状態のリソースがあればそこから実行されますが、暫く実行されないとスタンバイ状態が切れ、Lambda環境が起動されてから実行される、所謂コールドスタートとなります。 CDKでデプロイしたLambda Functionsは起動にかなり時間が掛かるらしく、API Gatewayのtimeout設定の最大値である30秒を超えることが多発し、問題となりました。 docs.aws.amazon.com その対処としてAWSから Provisioned Concurrency という機能が提供されており、指定した個数ぶんのリソースが常に起動されている状態を実現することができます。 CDKではLambdaのリソース状態等を指定するCDK Stackの中で記述します。 // コールドスタート対応 // https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-lambda.Version.html#provisionedconcurrentexecutions sampleLambda.currentVersion.addAlias ( 'VERSION_NAME' , { provisionedConcurrentExecutions: 2 } ); github-action-and-aws-cdk-sample/lib/index.ts at master · haze-it/github-action-and-aws-cdk-sample · GitHub これで解決!...すると思いきや、それでも結構な割合でtimeoutが発生していたので、結局CloudWatch Eventsを使って、一分に一回空の呼び出しを行って、常にリソースを起動させる対応も並行して実施しています。 CloudWatch -> イベント -> ルール から設定ができます これでいまのところは問題なく運用できています。 複数アプリケーションから実行されるようになったら根本的な解決を考えないといけなくなりそうです。困った。 権限について CDK Deployを実行するユーザにはかなり強めの権限が必要になります。 必要な権限を調べつつ行いましたが、最終的にはAWS Lambda, IAM, API Gateway, Cloud Formationへのフルアクセスを設定することになりました。 Lambdaの環境変数を暗号化したりする必要がある場合には、更にKMSのアクセスが、他のリソースも使う場合はそれらのアクセスも必要になるかと思います。 当たり前の話ではありますが、暗号化キーやシークレットを外に公開しないよう注意しましょう。 ログについて ログはCloudWatchに保管されるようにしています。 特に何も設定していない状態だと、「呼び出したアプリケーション側のリクエストがどのログなのかがわからない」という問題が発生します。所謂分散トレースの話ですね。 その対処として、 サンプルアプリケーション上では行っていませんが、実際のコードでは、Request Parameterに request_id というパラメータを必須で入力するように設定し、ログを検索できるようにしています。 パラメータ指定とログ出力のイメージ 検索にはCloudWatch Log Insightsを使用しています。 filter @message like /sample_app_111/ のように指定すると検索できます。 docs.aws.amazon.com 参考 Developers.ioの複数の記事を参考にして作っています。いつもお世話になっています!ありがとうございます dev.classmethod.jp dev.classmethod.jp ESLintの設定は大半をバトルプログラマー柴田さんの記事をほぼそのまま使用しています 🙏 bps-tomoya.hateblo.jp 解説は以上です。 たまに詰まり所はありますが、便利で楽しいです。 是非皆さんも試してみてください!
アバター
スマートキャンプで業務委託でエンジニアをしている佐藤です。BOXILの開発を1年3ヶ月前から、沖縄からフルリモートでやっています。 皆さんは、毎日楽しくお仕事できていますか? エンジニアという職業は労働時間やストレスが多く、 IT業界は他の業界と比べて精神疾患にかかりやすい と言われています。 私はもともと自己否定ばかりしてしまう思考の癖があることに加えて、7年前に起業に失敗してメンタルを壊してしまったことをいまだに引きずっていて、日々悩みながら生活をしています。 スマートキャンプは、過労とは無縁で、メンバー間のサポートもよく、これ以上ないくらい私に合った職場です。それでも自分の心の問題で不安になったり、絶望感に襲われたりすることがあります。今回はそうなるたびに書き綴ったメモを、開発中にネガティブな気持ちにならないための技術としてまとめようと思います。 メンタルが強くないエンジニアはこんな気持ちになる ネガティブな気持ちになった原因を分類してみる a. スピード(タスクが遅れていること)に対しての不安 b. 品質(実装内容)に対しての不安 c. コミュニケーションに対しての不安 ネガティブな気持ちにならないための技 「a. スピード(タスクが遅れていること)に対しての不安」への技 進捗をつぶやきまくる 原因が見積もり失敗にある場合、プランニングを重視する 「b. 品質(実装内容)に対しての不安」への技 実装方針を決めておく 設計好きなメンバーに設計を相談する PRにコメントを書く 「c. コミュニケーションに対しての不安」への技 ランチに誘う まとめ メンタルが強くないエンジニアはこんな気持ちになる 皆さんは、開発中に以下のようなネガティブな気持ちになったことはありませんか? ひとつのタスクに時間をかけすぎている。この程度のことをなぜ解決できない?と自分を責める。 リファクタできるかやってみます!と言って2時間ほど作業をしたが、思ったよりもコードが複雑で何もできずに時間を無駄にした。 プロジェクトにライブラリを導入するための技術調査で、調査の終盤でプロジェクトでの要件を満たせないことがわかった。せっかく時間をもらったのに何も残せなかった。 ほかにも、開発中に 仕様の考慮漏れが見つかって血の気が引いた。自分はぜんぜん貢献できていないと思った。 朝起きたときから気分が憂うつ。昨日の夜にレビューを依頼したコードは批判されるに違いない。 レビューで指摘してもらったコードをうまく書き直すことができない。自分の技術力のなさを見せたくないので、冷や汗を書きながら作業をしている。 とか、開発中以外でも、 人に頼むのは遠慮するところがあるなぁ。実装方針の相談や、コードレビューの依頼とか。 自分の話をするのが苦手。誰も自分の話なんか興味ないのになぁと思う。 せっかく1 on 1をしてもらったのに、本当の自分の悩みを知られたら変に思われるんじゃないかと思って緊張してしまった。 など、私は頻繁にネガティブな気持ちになります。 ネガティブな気持ちになった原因を分類してみる 上記のリストを眺めてみると、ネガティブな気持ちは以下の3つに分類することができそうでした。 スピード(タスクが遅れていること)に対しての不安 品質(実装内容)に対しての不安 コミュニケーションに対しての不安 コードをすばやくきれいに書けて人柄も優れていれば、理想のエンジニアと言うことができると思います。それができないときにネガティブな気持ち、すなわち不安になるようです。 具体的には以下のようなものがあります。 a. スピード(タスクが遅れていること)に対しての不安 タスクに時間がかかり過ぎている 作業が遅いと思われているに違いない b. 品質(実装内容)に対しての不安 考慮漏れがあるかもしれない うまく実装できなくて情けない、恥ずかしい 不具合が出るのが心配 c. コミュニケーションに対しての不安 自分が担当の仕事でメンバーに迷惑をかけたくない 困っていてもメンバーに頼ることができない ありのままの自分を出せていない ネガティブな気持ちにならないための技 「a. スピード(タスクが遅れていること)に対しての不安」への技 まず、「スピードに対する不安」に対処する技を紹介します。 進捗をつぶやきまくる 簡単なタスクなのに時間がかかり過ぎていると思われないようにするためには、進捗をつぶやきまくる技が有効です。Slackで想定と違ったこと、辛いこと、時間がかかっていること、はまっていることなどを1日に何度もつぶやきます。助けてもらうことやアドバイスをもらうことが目的ではないので、文章はまとまっていなくてもいいです。「実は難しいタスクだった」ということがアピールできればOKです。 メンタルが強くない人は自分で自分の作業を「遅い!」と責める思考になることが多いです。本当の目的は他のメンバーに大変さアピールすることではなく、自分で自分に「そういうこともあるよ、仕方ないよ」と言い聞かせることだったりします。 Slackのチャンネルは、自分専用の分報(times_xxx)を使うのが一般的ですが、自己主張が苦手な人は自分のチャンネルを開設するのに抵抗があるかと思います。なので、チームのチャンネルがあると気が楽です。BOXIL開発チームでは、「#boxil_dev_心」というチャンネルがあります。 以下、Slackの実例です。 原因が見積もり失敗にある場合、プランニングを重視する タスクに時間がかかってしまった原因が、実際にコードを見てみると作業項目が見積もりよりも多かったということはよくあると思います。 その場合は、タスクに着手する前のプランニングをしっかりやるようにします。BOXIL開発ではスクラム開発の手法を取り入れているので、この記事でのプランニングとはスプリントプランニングを意味しますが、ウォーターフォールでは「内部設計」のフェーズに相当します。 プランニングで具体的な実装を考える文化がない場合は、チームリーダーに「事前に〇〇さんと実装方針を確認させてください」と言うようにするといいでしょう。メンバーの誰かとプランニングをしておくと、実装中に困ったことがあったら相談しやすいというメリットもあります。 さらに、プランニング後に追加作業が発覚した場合、プランニング時に作った細かなタスクの一覧に、「[追加] タスク名」のようにそれがわかるように項目を追加しておきます。自分の技術不足のために遅れたのではないことを示すことで、自分を守ることにつながります。 「b. 品質(実装内容)に対しての不安」への技 次に、「品質に対しての不安」に対処する技を紹介します。 実装方針を決めておく 品質に不安を抱えないために、ここでも事前のプランニングが重要になってきます。実装方法が見えていないときや複数考えられるときもちろん、そうでないときも、タスクに取り掛かる前にチームメンバーと実装方針を決めておくといいです。 そうすれば、もっとよいやり方があるんじゃないかという不安をずっと抱えたまま作業することや、うまく実装できなくて情けない思いをしたりすることがなくなります。 実装方針のプランニングは、複数人で実際のコードを見ながら、誰が書いても同じようなコードになるレベルまで話すのが望ましいです。 設計好きなメンバーに設計を相談する チームメンバーのなかで、設計の話が好きな人がいればチャンスです! たとえ忙しそうだったり、別のタスクの担当だったとしても、隙をみて話しかけてみるといいです。設計の話は、その人にとっては息抜きみたいなものです。 PRにコメントを書く 実装後に不安に思っていることはとりあえず全部PRに書いておくと、実装がイマイチだったとしても「ちゃんと考えている」ことをアピールをすることができます。PRの本文に「確認してほしいこと」という項目を作ったり、PRのFiles changedのコードに実装について考えたことをメモします。 メンタルが強くない人は自分に自信がないため、「バカだと思われなくない」と考える傾向があります。PRでは先手を打っておきましょう。 「c. コミュニケーションに対しての不安」への技 最後に、「c. コミュニケーションに対しての不安」に対処する技を紹介します。 ランチに誘う 困っていてもメンバーに頼ることができない、ありのままの自分を出してコミュニケーションが取れないと感じる場合は、仕事外でのコミュニケーションが不足しているからかもしれません。 普段からランチに誘ったり、仕事で助けてもらったお礼にお酒をごちそうしたりするなど 、仕事以外でのカジュアルなコミュニケーションを増やすといいです。 私のようにフルリモートのエンジニアだったら難しいところがあり、課題に感じているところなのですが、業務時間内で雑談時間を作るなどの工夫しています。 まとめ メンタルを強くするためには、今回のようなテクニック的なものだけでなく、マインドフルネス瞑想や心理学などでネガティブな感情に対処できるようになることが重要だとは考えています。それはそれでやりつつ、今回は仕事における工夫で不安をなくすテクニックをまとめてみました。 もともとメンタルが強くない、もしくは何らかの原因で一時的にメンタルが弱っているエンジニアの方が、少しでも仕事をしやすくなれば幸いです。
アバター
スマートキャンプ、エンジニアの入山です。 2020年7月にDockerとAWSのコラボレーションにより、単一コマンドでDocker ComposeのyamlファイルからAmazon ECS上に各コンテナをデプロイできる機能追加が発表され、非常に注目を集めました! From Docker Straight to AWS - Docker Blog AWS and Docker collaborate to simplify the developer experience | Containers ローカルでDockerを利用して開発を行っている方々は、ほぼ間違いなくDocker Composeを利用してアプリの動作に必要な各コンテナを一括管理しているかと思いますが、このECS Pluginを利用するとAmazon ECSへの各コンテナのデプロイとECSの動作に必要な各AWSリソースを一括して自動構築してくれます。 今回は、そんなDockerのECS PluginがDocker for MacのStable版でリリースされ、実際に試してみたので紹介したいと思います! DockerのECS Pluginとは 検証環境 事前準備 Amazon ECS用のcontext作成 ECSアカウント設定変更 DockerイメージをECRへPush ECSへデプロイ リソースの削除 まとめ DockerのECS Pluginとは DockerのECS Pluginは、DockerとAWSのコラボレーションにより実現したDockerの拡張機能で、docker-compose.ymlに記述された各コンテナリソースをAmazon ECSのFargate上に直接デプロイすることができる仕組みです。 ローカルで普段実行しているdocker-composeコマンドと同じく、upやdownなどのシンプルな単一コマンドでAWSへのデプロイが自動実行され、ECSの実行に必要な各NW設定や外部公開するためのLBなども一括して作成してくれることが特徴です! 今回は以下のDocker公式ドキュメントを参考に、動作確認を行っていきます。 docs.docker.com 検証環境 今回検証に利用した環境は、以下となります。 Docker for Mac Stable : 2.4.0.0 Dockerレジストリ Amazon ECR アプリケーション Docker公式サンプル 7月のリリース当初はDocker for MacのEdge版でしか利用できず、再インストールや既存のイメージが全て消えてしまうなど、リスクも多かったので手を出しにくかったですが、9/15リリースのバージョン2.3.0.5でStable版に統合されたので安心して試すことができます! 事前準備 Amazon ECS用のcontext作成 Dockerコマンドのデプロイ先にAWSを指定するために、専用のContextを作成します。 公式のサンプルに従って、myecscontextという名前でContextのcreateコマンドを実行します。 $ docker context create ecs myecscontext 対話形式で以下内容の入力が求められるので、順番に入力していきます。 AWS Profile AWS CLIなどを利用していて既存のProfileが存在する場合は、選択肢から選択が可能 新しくProfileを登録する場合はnew profileを選択し、profile名を入力 Region 東京リージョンを指定(ap-northeast-1) Credentials(既存Profileを選択した場合は省略可能) AWS Access Key ID AWS Secret Access Key Contextの作成が完了したら、作成されたContextの確認を行います。 以下のようにmyecscontextが作成されており、TYPEがecsとなっていれば成功です! $ docker context ls NAME TYPE DESCRIPTION ... default * moby Current DOCKER_HOST based configuration myecscontext ecs ap-northeast-1 ECSアカウント設定変更 DockerのECS Pluginを利用するために、ECSのアカウント設定でサービス名の新しいARN形式のオプトインを有効にする必要があります。有効になっていない場合は、デプロイ時にエラーが発生します。 以下のコマンドでECSの各設定の状況を確認できます。LongArnFormatに関する項目が対象の設定になります。 $ aws ecs list-account-settings --effective-settings { "settings": [ { "name": "containerInstanceLongArnFormat", "value": "disabled", "principalArn": "arn:aws:iam::123456789012:user/docker-ecs-user" }, { "name": "serviceLongArnFormat", "value": "disabled", "principalArn": "arn:aws:iam::123456789012:root" }, { "name": "taskLongArnFormat", "value": "disabled", "principalArn": "arn:aws:iam::123456789012:user/docker-ecs-user" } ] } LongArnFormatに関する設定を、以下のコマンドを実行して有効化します。 $ aws ecs put-account-setting-default --name containerInstanceLongArnFormat --value enabled $ aws ecs put-account-setting-default --name serviceLongArnFormat --value enabled $ aws ecs put-account-setting-default --name taskLongArnFormat --value enabled こちらの設定については、Edge版で少し内容が異なりますが、以下の記事を参考にさせていただきました! dev.classmethod.jp DockerイメージをECRへPush DockerのECS Pluginを利用してECSへデプロイする場合は、事前にDockerイメージをコンテナレジストリにPushしておく必要があります。今回は同じAWSアカウントのECRをレジストリとして利用します。 Docker公式のリポジトリをcloneし、サンプルアプリケーションのディレクトリに移動します。 $ git clone https://github.com/docker/ecs-plugin.git $ cd ecs-plugin/example/ 今回の要となるdocker-compose.ymlを編集します。 imageの部分をECRのリポジトリに変更 image: 012345678901.dkr.ecr.ap-northeast-1.amazonaws.com/example:latest ECRを利用する場合は、コンテナレジストリの認証が不要のため、以下の部分を削除 x-aws-pull_credentials: <<<your arn for your secret you can get with docker ecs secret list>>> イメージをPushするため、ECRの認証を行います。 $ aws ecr get-login-password --region ap-northeast-1 | docker login --username AWS --password-stdin 012345678901.dkr.ecr.ap-northeast-1.amazonaws.com Login Succeeded 認証が完了したら、DockerイメージをPushします。 $ docker-compose push Pushing frontend (012345678901.dkr.ecr.ap-northeast-1.amazonaws.com/example:latest)... The push refers to repository [012345678901.dkr.ecr.ap-northeast-1.amazonaws.com/example] f180fef4ee81: Layer already exists 2c3fd1bbef62: Layer already exists 4d7eb43f9ca3: Layer already exists 3c3553e7865b: Layer already exists 22ee430ae503: Layer already exists 60faa6c61cf1: Layer already exists cfd52085ef97: Layer already exists 408e53c5e3b4: Layer already exists 50644c29ef5b: Layer already exists latest: digest: sha256:0632336b6cd907468e9ea435b5e4039b5cf4450265e62860f62d8d8696fc0109 size: 2201 ECRにイメージがPushされていることを確認します。 $ aws ecr list-images --repository-name example { "imageIds": [ { "imageDigest": "sha256:0632336b6cd907468e9ea435b5e4039b5cf4450265e62860f62d8d8696fc0109", "imageTag": "latest" } ] } ここまでで準備は完了です! ECSへデプロイ 先程作成したECSデプロイ用のContext(myecscontext)を利用するよう、Dockerの設定を変更します。 $ docker context use myecscontext myecscontext Contextの変更が完了したら、ローカルでDocker Composeを起動するのと同じようにupコマンドを実行します。これだけで、AWSへのデプロイが始まります! $ docker compose up [+] Running 17/17 ⠿ example CREATE_COMPLETE 247.0s ⠿ ExampleDefaultNetwork CREATE_COMPLETE 6.0s ⠿ FrontendTaskExecutionRole CREATE_COMPLETE 22.0s ⠿ Cluster CREATE_COMPLETE 7.0s ⠿ BackendTaskExecutionRole CREATE_COMPLETE 22.0s ⠿ CloudMap CREATE_COMPLETE 48.0s ⠿ ExampleLoadBalancer CREATE_COMPLETE 153.0s ⠿ FrontendTCP5000TargetGroup CREATE_COMPLETE 1.0s ⠿ LogGroup CREATE_COMPLETE 1.0s ⠿ ExampleDefaultNetworkIngress CREATE_COMPLETE 1.0s ⠿ FrontendTaskDefinition CREATE_COMPLETE 3.0s ⠿ BackendTaskDefinition CREATE_COMPLETE 3.0s ⠿ BackendServiceDiscoveryEntry CREATE_COMPLETE 3.0s ⠿ FrontendServiceDiscoveryEntry CREATE_COMPLETE 2.0s ⠿ BackendService CREATE_COMPLETE 80.0s ⠿ FrontendTCP5000Listener CREATE_COMPLETE 1.0s ⠿ FrontendService CREATE_COMPLETE 82.0s 表示されたリソースのステータスが全てCREATE_COMPLETEとなれば、AWSへのデプロイは完了です! 今回のサンプルアプリケーションでは、ECSのタスクやタスク定義をはじめ、LBやNW関連を含む17リソースが250秒程で自動作成されたようです。 psコマンドを実行すると、各コンテナの稼働状況とLBのエンドポイントが表示されます。 $ docker compose ps ID NAME REPLICAS PORTS example-BackendService-hfEA5K76syiR backend 1/1 example-FrontendService-j829cZjBATkR frontend 1/1 ExampleLoadBalancer-0123456789012.elb.ap-northeast-1.amazonaws.com:5000->5000/tcp 表示されたLBのエンドポイントにアクセスしてみると、サンプルアプリケーションが動作していることが確認できました! リソースの削除 デプロイしたリソースについては、ローカルのDocker Composeと同じようにdownコマンドを実行するだけで、AWS上の各リソースが一括削除されます。 $ docker compose down まとめ 今回は、DockerのECS Pluginを利用したAmazon ECSへのデプロイを紹介しました。 以下の記事で紹介したAWS Coplilotなども含め、コンテナアプリの実行環境構築やデプロイが、誰でも簡単に行えるように日々整備されていっており、個人開発アプリの公開や新規プロダクトのプロトタイプ検証など、様々な用途で利用できそうです! DockerのECS Pluginは、GAに向けて本番アプリケーションの構築を移行できるレベルを目指しているようなので、今後のアップデートが楽しみです。是非皆さんも試してみてください! tech.smartcamp.co.jp
アバター