BASE株式会社のブログ - TECH PLAY

TECH PLAY

BASE株式会社

BASE株式会社 の技術ブログ

611

BASE BANKでエンジニアをしている @budougumi0617 です。 この記事はBASE Advent Calendar 2021 10日目の記事…ではなく、New Relic Advent Calendar 2021 10日目の記事です。 qiita.com TL;DR ソフトウェア開発チームのパフォーマンスを示す 4 つの指標がある https://www.devops-research.com/quickcheck.html デプロイの頻度 変更のリードタイム 変更障害率 サービス復元時間 New Relic Oneを使って「デプロイの頻度」を計測してみた Event APIとNRQLでデプロイ回数をNew Relic Oneのダッシュボードに図示できる https://docs.newrelic.com/docs/data-apis/ingest-apis/introduction-event-api ダッシュボード 自分たちチームのパフォーマンスはどれくらいなのか? 2009年に「10 deploys per day」というタイトルと共にDevOpsというキーワードが産まれて 1 から10年以上が経ちました。 10+ Deploys Per Day: Dev and Ops Cooperation at Flickr from John Allspaw www.slideshare.net 市場へサービスのリリースを繰り返し、より短時間で多くのフィードバックループを回すことがソフトウェア開発チームの生産性向上の鍵なようです。 この事実を具体的な指標にしたのが DevOps Research and Assessment(DORA)チーム の研究から導き出されたソフトウェア開発チームのパフォーマンスを示す 4 つの指標です( Google Cloudブログより引用 )。 デプロイの頻度 - 組織による正常な本番環境へのリリースの頻度 変更のリードタイム - commit から本番環境稼働までの所要時間 変更障害率 - デプロイが原因で本番環境で障害が発生する割合(%) サービス復元時間 - 組織が本番環境での障害から回復するのにかかる時間 LeanとDevOpsの科学[Accelerate] や クイックチェックツール では具体的なパフォーマンスレベルも示されています。 performance ( 表はさきほど同様 Google Cloudブログより引用 ) しかし、「 ではあなたのチームはどのレベルなの? 」と聞かれると「多分先週は毎日デプロイしてたはず…」なんて曖昧な回答しかできない状態でした。すでに他社ではこの質問に答えるためのアプローチ方法がいろいろ取られています。 生産性を可視化したい! / SUZURI's four keys https://speakerdeck.com/udzura/suzuris-four-keys-ce85acb4-bb98-4b6c-b6d8-0e62b5253872 普段「推測するな、計測せよ」「可観測性!!Observability!!O11y!!」なんて言っているのにこれではいけない…ということでチームの各指標をちゃんと計測しようと思いました。本記事ではまずは手始めにデプロイの頻度を計測した方法の紹介です。 デプロイ頻度を集計したい デプロイ頻度を計測するには様々なツールがあると思いますが、我々はNew Relicを使ってデプロイ頻度を集計することにしました。 New Relicを使ってデプロイ頻度を集計する理由は次のとおりです。 New Relicで集計している他のメトリクス情報と組み合わせて別の情報を引き出せる可能性がある 新たなツールを導入するよりも日々使うツールは少ないほうがよい 同様に確認が必要なダッシュボードは少ないほうがよい NRQLの便利さとグラフの作りやすさ サービスをデプロイしたという事実を集計する New Relicには Deployment Marker という他のパフォーマンス情報とアプリケーションのデプロイを関連付ける機能があります。 docs.newrelic.com qiita.com Deployment Marker を利用して Deployment イベントを記録すると、APMのグラフにデプロイタイミングを示す線がプロットされます。 New Relic One上でデプロイタイミングを記録しておくことでデプロイがメトリクスにどのような影響を及ぼしたのか解析できます。 Deploymentが一緒に描画されたメトリクス 最初はこの機能を使って記録した Deployment イベントをNRQLで集計すればよいと考えていました。 しかし、当時(そして2021年12月現在も、) Deployment はNRQLによって集計できるイベントはありませんでした。 デプロイ頻度を可視化するには集計してグラフ化する必要があります。そこで我々はデプロイのたびに独自のカスタムイベントを発行することにしました。 Event APIを利用した独自イベントの発行 New RelicのNRQLで集計できる type (NRQLの FROM に指定できるデータ)は Log や Transaction など多岐に渡ります。 そしてNRQLはユーザーが独自に定義した type のデータを集計することも可能です。独自定義のイベント( type )を作れるのがEvent APIです。 docs.newrelic.com Event APIを実行するために必要なもの Event APIを利用するために必要な情報は次の2点です。 License Key Account ID New Relic Oneを利用している場合、Event APIはLicense Keyを確認するだけで実行できます( 以前はInsert Keyが必要だったはずなのですが、2021年12月現在は非推奨 )。 docs.newrelic.com Licence Key自体は次のURLをクリックすると遷移できる「API Keys」の中から確認することができます。 同ページを見れば自分たち組織のアカウントIDも確認できるはずです。 one.newrelic.com Event APIの実行方法 Event APIの基本的な利用方法は先ほど記述したLicense Keyを利用して任意のJSONを curl コマンドで POST するだけです。 docs.newrelic.com 細かい仕様はありますが、任意のキー/バリューを含めることで記録する情報を自由に付与することができます。 今回は次のようなJSONフォーマットで独自イベントを発行することにします。 [ { " eventType ":" Deploy ", " deploy_user ":" ${DEPLOY_USER} ", " service ":" ${SEPLOY_SERVICE_NAME} " } ] たとえひとつのイベントだけでも配列で定義しないとうまくいきません。 時刻情報はAPI実行時の timestamp が自動的に含まれるので自前の定義は不要です。 「誰がデプロイしたのか?」も情報として含めていますが、これを集計すると謎の力学が発生してしまうのであくまで参考値として記録しておくだけにしておきます。 CircleCI上でEvent APIを実行する 上記のJSON定義をNew Relicのエンドポイントに送信すればイベントの記録が始まります。 我々のチームはCircleCIを使ってサービスをデプロイしているので、CircleCIのデプロイ用ワークフローの最後のステップにEvent APIを実行するようにします。 各サービスの .circleci/config.yml の中に次のjobを定義しました。 record-deployment : executor : name : aws-cli/default # curlコマンドが使えれば何でもよい steps : - setenv_prd # 環境変数のセットアップ - run : name : Send Deploy event to New Relic command : | curl -i \ -X POST 'https://insights-collector.newrelic.com/v1/accounts/${NEW_RELIC_ACOUNT_ID}/events' \ -H "X-Insert-Key:${NEW_RELIC_LICENSE_KEY}" \ -H "Content-Type: application/json" \ -d \ "[ { \" eventType \" : \" Deploy \" , \" deploy_user \" : \" ${CIRCLE_USERNAME} \" , \" service \" : \" ${CIRCLE_PROJECT_REPONAME} \" } \ ]" NEW_RELIC_ACOUNT_ID と NEW_RELIC_LICENSE_KEY という環境変数を設定する必要があります。 CIRCLE_USERNAME 、 CIRCLE_PROJECT_REPONAME はCircleCI実行時に自動的に設定される値です。 circleci.com あとはこのjobをデプロイフローの最後に設定しておきます。 デプロイフローの実行完了(デプロイ成功)のタイミングで Deploy というイベントが記録されます。 test-build-deploy : jobs : - test : filters : branches : only : main - build-push : requires : - test # .... - deploy-prd : requires : - approve-deploy-prd - record-deployment : requires : - deploy-prd Event APIを利用した独自イベントの集計 データが取得できるようになったならば後は集計してダッシュボードに掲載します。 New Relic Oneを開いたらQuery Builderに移動して Deploy イベントを集計していきます。 良いグラフができたら右下の「Add to Dashboard」ボタンを使ってダッシュボードにグラフを追加していきます。 参考までに我々が作成したグラフとNRQLのクエリを紹介します。 ダッシュボード 月間デイリーデプロイ数 SELECT count (*) FROM Deploy TIMESERIES 1 day FACET service SINCE 30 days ago 月間デイリーデプロイ数 まずは過去30日の日毎のデプロイ数です。サービス別にカウントしています。 NRQLは独自イベントに付与している独自属性を使って FACET を書くこともできます。 今週のデプロイ総数(先週比較) SELECT count (*) FROM Deploy SINCE this week COMPARE WITH 1 week ago ウィークリーデプロイ数 次に先週比較の数値です。 COMPARE WITH を使うと比較対象との比率まで簡単に示してくれます。 先週と比べて今週のデプロイ頻度は低いのか?気づかないうちにチームのパフォーマンスがブレてていないか?を確認したくて作りました。 週間デプロイ数 最後は直近4週間のデプロイ総数です。 SELECT count (*) FROM Deploy FACET weekOf(timestamp) UNTIL monday SINCE 5 week ago 週別デプロイ数 こちらもコンスタントにデプロイできているか?を確認するために作りました。 終わりに 本記事では「LeanとDevOpsの科学」などで示されているソフトウェア開発チームのパフォーマンスを示す4 つの指標のうち、デプロイ頻度をNew Relicを使って確認する方法を紹介しました。いくつか実際に利用している集計クエリも紹介しました。「こんな見方もできるのは?」「私たちのチームはこういう点をみている」とコメントある方は教えていただけると幸いです。 ソフトウェア開発チームのパフォーマンスの4つの指標ではデプロイ頻度の他に3つのキーが定義されています(再度 Google Cloudブログより引用 )。 デプロイの頻度 - 組織による正常な本番環境へのリリースの頻度 変更のリードタイム - commit から本番環境稼働までの所要時間 変更障害率 - デプロイが原因で本番環境で障害が発生する割合(%) サービス復元時間 - 組織が本番環境での障害から回復するのにかかる時間 デプロイ頻度以外については現状まだ計測できていません。 その他のキーについても可視化した際は本開発者ブログで共有させていただきます。 また、New Relic Oneでは最近サービスレベル目標(SLO)を設定、サービスレベル指標(SLI)、Error budgetを管理できるService Level Management機能もPublic betaになりました。 来年は4キーメトリクスの他に、SLIについても可視化していければなと思います。 docs.newrelic.com そして BASE Advent Calendar 2021 10日目の記事も書いたのでよかったら読んでみてください。 devblog.thebase.in 参考 LeanとDevOpsの科学[Accelerate] DevOpsの起源とOpsを巡る対立 エリート DevOps チームであることを Four Keys プロジェクトで確認する | Google Cloud Blog State of DevOps Report 2021を日本語で解説 https://puppet.com/resources/report/2021-state-of-devops-report https://twitter.com/t_wada/status/1420652243330605060 生産性を可視化したい! / SUZURI's four keys NewsPicksにCTOとして入社して1年でDX Criteriaを大幅改善した話 https://ubiteku.oinker.me/2015/07/01/devops%E3%81%AE%E8%B5%B7%E6%BA%90%E3%81%A8ops%E3%82%92%E5%B7%A1%E3%82%8B%E5%AF%BE%E7%AB%8B/ ↩
BASEアドベントカレンダー2021 10日目の記事です。 BASEアドベントカレンダー2021 10日目 BASE BANKでエンジニアをしている @budougumi0617 です。 マイグレーションファイルが含まれたPull Request(PR)が作られたとき、自動更新したER図をPRに追加するGitHub Actionsを作りました。 本記事では紹介するGitHub Actionsを利用すると次のようなメリットが得られます。 マイグレーションファイルをPRに出すだけでPRに更新されたER図が追加される 開発者は面倒なER図の更新作業から開放される レビューアはマイグレーションファイルを含んだPRをER図を見ながらレビューできるようになる プロジェクト関係者は常にメインブランチのマイグレーションファイルの状態と一致したER図を確認できる サンプルPR 自動生成したER図 TL;DR ER図はあれば便利だけれど運用しているとメンテが大変 k1LoW/tbls をGitHub Actionsで動かすとマイグレーションファイルのPull Request(PR)に自動更新したER図を追加できる RDBMSのスキーマをマイグレーションツールで管理しているのが前提条件 マイグレーションファイルの追加PRでマイグレーション後のER図を確認しながらレビューできる マイグレーションファイルを書くだけでER図をメンテする必要がなくなる サンプルリポジトリはこちらです。 github.com GitHub ActionsのYAMLを見たい方はこちらを参照してください。 https://github.com/budougumi0617/sample_tbls_actions/blob/v0.0.1/.github/workflows/tbls.yml k1LoW/tbls で自動生成しているER図はこのディレクトリにあります。 https://github.com/budougumi0617/sample_tbls_actions/tree/v0.0.1/schema/dbdoc マイグレーションファイルを含んだPRに対してGitHub ActionsでER図を更新しているPRのサンプルはこちらです。 https://github.com/budougumi0617/sample_tbls_actions/pull/2 ER図の必要性 Webアプリケーションを開発・運用しているならば、大抵の場合RDBMSを利用していると思います。 私のチームではRDBMSのテーブル定義を rubenv/sql-migrate によるマイグレーションによって管理しています。 github.com マイグレーションによるテーブル管理を行なっているとき問題になるのがER図の管理です。 マイグレーションファイルとER図を両方利用しているとどうやってER図を最新の状態と一致させるのかが運用上の課題になります。新規開発サービスだったり機能追加が活発なサービスの背後にあるRDBMSは毎週のようにマイグレーションが実施されます。このようなRDBMSの状態を手動でER図に反映し続けるのはかなりの労力が必要です。 また、マイグレーションファイルのレビューをするときはDDLとして正しいかだけではなく、DBの設計として妥当であるかもレビューする必要があります。そのためには他のカラム、他のテーブルを含めてマイグレーション後の姿を俯瞰的に確認する必要があります。 ER図を運用する時の課題 前述したとおりER図はあったほうが良いですがER図を作り維持し続けるには様々な課題があります。 本番環境のテーブルの状態とER図がかならずマッチしている保証がない マイグレーションファイルが真か?ER図が真か? ER図が古いだけなのか?それともマイグレーションが意図通り行われなかったのか? マイグレーションファイルを書くのが先か、ER図を書くのが先か 俯瞰的にレビューするならばER図がほしい オレは ADD COLUMN したいだけなんだ… マイグレーションをする側からすると少しDDLを書くだけで済むはずなのにマイグレーション結果に影響しないER図の修正まで気を使うのは大変… 単純なカラム追加だけならばよいですが、外部キー制約付きのテーブル追加のようなDDLを書く場合ER図の更新は非常に煩わしいものでした。 k1LoW/tblsという救世主 メンテフリーなER図を実現するための救世主が github.com/k1LoW/tbls です。 github.com qiita.com tbls のいいところそれだけで2記事くらい書けるので省略しますが、シンプルに設定した接続先のDBのスキーマ情報を(デフォルトでは)MarkdownとSVGファイルに出力してくれます。 自動生成したER図 CI-Friendly とREADMEにかかれている通りシングルバイナリで svg ファイルまで生成してくれる点もとてもありがたいです。これ系のツールでありがちな「まずは Graphviz をインストールします」ということもないのでCI上でも簡単に実行できます。 しかし、 tbls は「データベースに接続してER図を自動生成するツール」です。 つまりマイグレーションを当てた状態のデータベースを用意しないと tbls を使うことができません。 マイグレーションの妥当性を確認する際もER図を見たいのに、マイグレーションしないとER図が生成できません。 そこでマイグレーションファイルを含んだPRが作られるたびにActions上でRDBMSを起動するようにActionsを書きました。 マイグレーションを含んだPRでER図を自動更新するGitHub Actions 今回作成したGitHub Actionsの全容は以下のとおりです。 https://github.com/budougumi0617/sample_tbls_actions/blob/v0.0.1/.github/workflows/tbls.yml name : update er graph on : pull_request : paths : - schema/sample/** - schema/tbls.yml jobs : tbls : name : generate-and-push runs-on : ubuntu-latest services : mysql : image : mysql:5.7 options : --health-cmd "mysqladmin ping -h localhost" --health-interval 20s --health-timeout 10s --health-retries 10 ports : - 3306:3306 env : MYSQL_ALLOW_EMPTY_PASSWORD : yes MYSQL_DATABASE : sample MYSQL_USER : sample MYSQL_PASSWORD : sample steps : - name : Checkout uses : actions/checkout@v2 with : ref : ${{ github.event.pull_request.head.ref }} token : ${{ secrets.PERSONAL_ACCESS_TOKEN }} - name : Setup go uses : actions/setup-go@v2 with : go-version : '^1.17.1' - name : Execute migration run : | go get -u github.com/rubenv/sql-migrate/sql-migrate make up ENV=ci working-directory : ./schema - name : Execute tbls run : | curl -sL https://git.io/use-tbls > use-tbls.tmp && . ./use-tbls.tmp && rm ./use-tbls.tmp tbls doc -f working-directory : ./schema # tbls実行後、差分有りもしくは新規ファイルの数をカウントする - name : Count uncommit files id : check_diff run : | git status --porcelain | wc -l file_count=$(git status --porcelain | wc -l) echo "::set-output name=file_count::$file_count" working-directory : ./schema - name : Commit ER graph # 更新したER図をPRにコミットする if : ${{ steps.check_diff.outputs.file_count != '0' }} run : | git config user.name github-actions git config user.email github-actions@github.com git add . git commit -m "generate er graphs from actions" git push working-directory : ./schema # PRへ自動コミットしたらPRにコメントしておく - name : Report commit on pull request if : ${{ steps.check_diff.outputs.file_count != '0' }} uses : actions/github-script@v4 with : script : | github.issues.createComment({ issue_number : context.issue.number, owner : context.repo.owner, repo : context.repo.repo, body : 'Actions committed new ER files🤖' }) Actionsの主な流れは次のとおりです。 PRにマイグレーションファイルの変更が含まれていた場合実行する 事前にMySQLを起動しておく マイグレーションファイルを実行する tbls コマンドを実行してER図を生成する もし既存のER図と差分があったら、PRに対してその差分をコミットする PRにマイグレーションファイルを更新した旨をコメントする ひとつひとつ該当するYAMLの抜粋と一緒に解説していきます。 PRにマイグレーションファイルの変更が含まれていた場合実行する GitHub Actionsは特定のディレクトリに更新があったときのみ起動する設定が可能です。 私たちのリポジトリはアプリケーションコードと一緒にマイグレーションファイルがコミットされていますが、この設定によりアプリケーションコードを変更するだけのPRで意味もなくCIが実行されることを防ぎます。 on : pull_request : paths : - schema/sample/** - schema/tbls.yml 事前にMySQLを起動しておく GitHub Actionsはワークフロー中にサービスコンテナという名前でDBなどのDockerコンテナを起動しておく事ができます。 docs.github.com 今回は次のような宣言でMySQLを起動しておきます。 mysql : image : mysql:5.7 options : --health-cmd "mysqladmin ping -h localhost" --health-interval 20s --health-timeout 10s --health-retries 10 ports : - 3306:3306 env : MYSQL_ALLOW_EMPTY_PASSWORD : yes MYSQL_DATABASE : sample MYSQL_USER : sample MYSQL_PASSWORD : sample コードをクローンしてGitHubの設定をする コードをクローンしてGitの設定も行ないます。ここで設定したGitの設定は後半の更新したER図のコミット・pushにも利用されます。 - name : Checkout uses : actions/checkout@v2 with : ref : ${{ github.event.pull_request.head.ref }} token : ${{ secrets.PERSONAL_ACCESS_TOKEN }} マイグレーションファイルとアプリケーションコードを同じリポジトリに入れている場合、Actionsで利用するトークンはデフォルトのトークンではなく、払い出したトークンを利用したほうがよいです。 これはデフォルトのトークンでPRに対してコミットをpushすると、そのコミットに対しては(無限ループ事故の防止のため)Actionsが起動しないためです。 docs.github.com 本記事で紹介しているActionsは起動しなくても問題ありません。しかし、アプリケーションコードとマイグレーションファイルを一緒にPRで変更していた場合ActionsからのpushでActionsが起動しないことになります。 ただ起動しないだけならばいいのですが、 CIのチェックステータスも消えるため、他のActionsでfailしていた状態も消えます 。 PRのBranch Protection RuleでCIのステータスを利用しているならば必ずトークンを払い出して再度Actionsが実行されるようにしておきます。 マイグレーションファイルを実行する 先ほど示したMySQLに対してマイグレーションを実行しておきます。 これでER図を生成したいDBがGitHub Actions上に構築されます。 - name : Checkout uses : actions/checkout@v2 with : ref : ${{ github.event.pull_request.head.ref }} token : ${{ secrets.PERSONAL_ACCESS_TOKEN }} - name : Setup go uses : actions/setup-go@v2 with : go-version : '^1.17.1' - name : Execute migration run : | go get -u github.com/rubenv/sql-migrate/sql-migrate make up ENV=ci working-directory : ./schema make up ENV=ci コマンドは次のMakefileの設定と、 dbconfig.yml で成り立っています。 https://github.com/budougumi0617/sample_tbls_actions/blob/v0.0.1/schema/Makefile ENV := "local" up: ## Apply migration files sql-migrate up -env= $(ENV) -config= dbconfig.yml https://github.com/budougumi0617/sample_tbls_actions/blob/v0.0.1/schema/dbconfig.yml ci : dialect : mysql datasource : root@tcp(127.0.0.1:3306)/sample?parseTime= true dir : sample tbls コマンドを実行してER図を生成する tbls をインストールして実行します。CIフレンドリー過ぎてやることはこれだけです。あっけないですね… - name : Execute tbls run : | curl -sL https://git.io/use-tbls > use-tbls.tmp && . ./use-tbls.tmp && rm ./use-tbls.tmp tbls doc -f working-directory : ./schema tblsの設定自体も接続情報と sql-migrate が利用しているマイグレーション管理用のテーブルを除外するようにしているくらいです。 https://github.com/budougumi0617/sample_tbls_actions/blob/v0.0.1/schema/tbls.yml dsn : mysql://root@127.0.0.1:3306/sample?parseTime= true er : comment : true distance : 9 exclude : - gorp_migrations もし既存のER図と差分があったら、PRに対してその差分をコミットする 先ほど実行した tbls によってER図のファイルに何らかの変化があった場合、gitのステータスに差分が出ます。 一つでも差分を見つけたらコミットを作り、PRのブランチに対してpushします。 このようにしておくことで空コミットが作られたり、Actionsが無限にループ実行することありません。 - name : Count uncommit files id : check_diff run : | git status --porcelain | wc -l file_count=$(git status --porcelain | wc -l) echo "::set-output name=file_count::$file_count" working-directory : ./schema - name : Commit ER graph # 更新したER図をPRにコミットする if : ${{ steps.check_diff.outputs.file_count != '0' }} run : | git config user.name github-actions git config user.email github-actions@github.com git add . git commit -m "generate er graphs from actions" git push working-directory : ./schema PRにマイグレーションファイルを更新した旨をコメントする 最後にPRにER図を更新した旨を書くコメントを残して終わります。 - name : Report commit on pull request if : ${{ steps.check_diff.outputs.file_count != '0' }} uses : actions/github-script@v4 with : script : | github.issues.createComment({ issue_number : context.issue.number, owner : context.repo.owner, repo : context.repo.repo, body : 'Actions committed new ER files🤖' }) あとは PERSONAL_ACCESS_TOKEN と言う名前でGitHubパーソナルアクセストークンを保存しておけば次のPRよりER図が自動生成されるようになります。 自動生成したER図が追加されたサンプルPR 上記のGitHub Actionsを使って更新されたPRが次になります。 github.com このPRではマイグレーションファイルが一つ追加されています。 PRに対してGitHub Actionsが実行された結果、更新されたER図が追加されています。 サンプルPR 開発者は何もせずマイグレーション後のER図をPRに追加できました。 レビューアは更新されたER図を確認することで、表で示されたテーブル構成や図示されたリレーションを見ながらレビューすることができます。 https://github.com/budougumi0617/sample_tbls_actions/blob/migrate-card/schema/dbdoc/cards.md このPRが妥当ならばマイグレーションファイルと更新されたER図が main ブランチにマージされます。 終わりに 以上のGitHub Actionsにより、我々の開発では以下の開発者体験が実現できました。 GitHub上でER図をいつでもテーブル定義を確認できる ER図とマイグレーション結果が一致していることを保証する 開発者はER図を書く必要がない マイグレーション後のER図を確認しながらマイグレーションファイルのPRをレビューできる 明日は @pigooosuke さんです!
BASE Advent Calendar 2021 9日目の記事です。 フリーランスのフロントエンドエンジニア 坪内です。 BASE のお手伝いをさせていただくようになって 1ヶ月が経ち、色々見えるようになってきた中で最も気になっていた点の 1つが、 「 HMR されていない 」 でした。 BASE の Web フロントエンドは webpack でビルドされているのですが、 ローカルの開発環境が Docker 上で動いている事もあってか、残念ながら HMR はされていない状況でした。 「Docker 環境だから無理?」 「“ webpack HMR docker ” とかでググると色々出てくるし出来そうな?」 さっそく取り掛かってみました。 HMR とは? 一応、HMR の説明をしておきます。 https://webpack.js.org/guides/hot-module-replacement/ H ot M odule R eplacement の略です。 js モジュールの変更を、ブラウザをリロードすること無しに動的に差し替えて反映する webpack の機能の事を指します。 歴史は割と古く、webpack 1系の頃から試験的に導入されていました。 似たものとして、ファイルの変更を検知してブラウザを自動でリロードしてくれる liveReload があります。 仕組み HMR はおおよそ以下の流れで実現されています。 webpack-dev-server が HMR 用のコードを含めて js を返す WebSocket に接続 webpack がファイルの変更を検知したら差分ビルド 変更があったことを WebScoket で通知 変更差分情報を取得 変更のあったモジュールを取得して動的に置き換え 置き換えができない場合は liveReload される 構成 一般的な SPA の場合、起動した webpack-dev-server にブラウザからアクセスし、API などへのアクセスは必要に応じてリバースプロキシするという構成を取ります。 起動した webpack-dev-server ( http://localhost:3000 など)にブラウザでアクセス webpack でビルドされた html を返却 webpack でビルドされた js を返却 webpack で管理していないリソースは backend へリバースプロキシ 当初 BASE のサービスは CakePHP を使用しているので、上記と同様に前段に webpack-dev-server を配置して CakePHP へリバースプロキシする必要があると思っていました。 また、ローカル Docker 環境は架空のドメイン(ここでは仮に *.base.test )上で動くようになっているので、webpack-dev-server へもその架空ドメインとしてアクセス出来るようにしてあげる必要があると考え、Docker 環境内に webpack-dev-server を建てました。 これはこれで動くので間違いではないのですが、 Docker上で動かすので単純に重くなる Docker上で webpack-dev-server を使うかどうかを切り替えるのが面倒そう node-gyp によるネイティブパッケージに依存していると、Docker上で yarn install する必要がある 内部のライブラリへの yarn link が厳しそう などの問題があり、色々と調整や解決が大変そうでした。 助言 そんな中、ある助言をもらいました。 アセットだけホスト側の dev-server に繋げないんだろうか ❗❗❗ リバースプロキシしなければならないと思い込んでしまっていましたが、 CDN から js や css を取得して動かせるわけなので、webpack-dev-server を CDN のように扱うこともできるのではないか? 助言後 webpack-dev-server はホスト側( http://localhost:3081/ )で起動する CakePHP で js をロードしている箇所の URL を http://localhost:3081/ に向ける 以上。 js の向き先を変えるだけで良くなったので、docker-compose には影響を及ぼさず、とてもシンプルな仕組みになりました! webpack の設定 最終的な webpack の devServer 設定は以下です。 devServer: { allowedHosts: [ '.base.test' ] , client: { overlay: true , webSocketURL: 'ws://localhost:3081/ws' , } , headers: { 'Access-Control-Allow-Origin' : '*' , } , port: 3081, proxy: { '*' : 'http://localhost:8081/' , } , static : false , watchFiles: [ 'app/**/*' ] , } , 考慮した点 CORS を解決する Docker 環境 https://*.base.test から見ると、 http://localhost:3081 はクロスオリジンなアクセスになるので、 devServer.headers として CORS ヘッダーを返す必要があります。 また、webpack-dev-server へのアクセスを許可するドメインを devServer.allowedHosts に追加する必要がありました。 devServer: { allowedHosts: [ '.base.test' ] , headers: { 'Access-Control-Allow-Origin' : '*' , } , } , HMR の WebSocket 接続先や差分取得先などを webpack-dev-server に向ける html は https://*.base.test/ から返されており、そのままだと HMR 用の WebSocket などもそちらを向いてしまうので、 devServer.client.webSocketURL で ws://localhost:3081/ws へ向けます。 devServer: { client: { webSocketURL: 'ws://localhost:3081/ws' , } , } また、 webpack-manifest-plugin を使用している関係で、 output.publicPath を指定していたのですが、 output: { publicPath: '/' , } , これが差分の取得先に使われているので、webpack-dev-server で起動している場合は localhost:3081 へ向くようにする必要がありました。 output: { publicPath: env.WEBPACK_SERVE ? 'http://localhost:3081/' : '/' , } , webpack 管理外の js への対処 webpack でビルドされていない古い js が残っており、そのままだとそれらへのアクセスが 404 になってしまうので、それ用に devServer.proxy で CakePHP へリバースプロキシしています。 devServer: { proxy: { '*' : 'http://localhost:8081/' , } , } , また、それらを含め CakePHP 側に変更があった際に liveReload されるよう、 devServer.watchFiles を設定しています。 devServer: { watchFiles: [ 'app/**/*' ] , } , CakePHP で js の向き先を変える HtmlHelper を使用して、 <? = $ this -> Html -> script ( 'foo' ) ?> のように出力していれば、 App.jsBaseUrl を設定する事で向き先を変えることができます。 <?php Configure :: write ( 'App.jsBaseUrl' , 'http://localhost:3081/js/' ) ; これをローカルでのみ有効になる Config ファイルに記述して、必要に応じて切り替えるようにしています。 おわりに Docker 環境でもシンプルな形で webpack-dev-server で HMR する事ができました! この形で HMR できるとなると、 Proxyman や Charles などの proxy と組み合わせてみたりなど色々応用もできそうですね。 明日は @budougumi0617 さんです!
Chromatic とは Chromatic とは、Storybook のメンテナーが作成している Storybook 用のツールです。Storybook をビルドして公開したり、ストーリーごとのスクリーンショットを撮影し、差分を比較してくれる機能を備えています。 Chromatic を使うことにより、UI の予期せぬ変更を事前に検知することができます。本記事では Chromatic の導入、活用方法をご紹介します。 なお、BASE 社では社内の UI コンポーネントライブラリである BBQ で Chromatic を導入、活用しています。その経緯はアドベントカレンダー15日目に公開する記事でご紹介します。 Chromatic をプロジェクトに導入する サンプルプロジェクトを作成する 今回は Storybook 公式で用意されている サンプルプロジェクト を利用します。 プロジェクト作成にあたり、以下のコマンドを実行します。 $ npx degit chromaui/intro-storybook-vue-template taskbox $ cd taskbox $ yarn これで taskbox の作成が完了しました。 次に以下のコマンドを実行して Storybook を立ち上げ、どのようなコンポーネントがあるか確認します。 $ yarn storybook Storybook の画面 Button や Header、Page といったコンポーネントが存在することが確認できました。 サンプルプロジェクトに Chromatic を導入する 次に、Chromatic にログインします。「Add project」ボタンから新規プロジェクトを追加できます。 Chromatic の管理画面 GitHub レポジトリかプロジェクト作成のどちらかを選択します。今回は右側のプロジェクト作成を選択しました。 プロジェクト作成用のボタンが二つ並んでいる すると、Chromatic 用のトークンが発行されます。 Chromatic のコマンドとトークンが表示されている 画面に表示されているコマンドを taskbox ディレクトリで実行します。 $ npx chromatic --project-token=your-token すると、以下のエラーが表示されます。 $ npx chromatic --project-token=your-token Chromatic CLI v6.1.0 https://www.chromatic.com/docs/cli ✔ Authenticated with Chromatic → Using project token '********da59' ✖ Retrieving git information → Chromatic only works from inside a Git repository. プロジェクトを git 管理下に置いていないため Publish に失敗します。このため、適当なレポジトリを作成して push しましょう。 レポジトリに push した後、再度 chromatic コマンドを実行します。 $ npx chromatic --project-token=your-token Chromatic CLI v6.1.0 https://www.chromatic.com/docs/cli ✔ Authenticated with Chromatic → Using project token '********da59' ✔ Retrieved git information → Commit 'b6eb197' on branch 'main'; no parent commits found ✔ Collected Storybook metadata → Storybook 6.4.0 for Vue3; supported addons found: Actions, Essentials, Links ✔ Storybook built in 22 seconds → View build log at /Users/panda/playground/vue/test/taskbox/build-storybook.log ✔ Publish complete in 14 seconds → View your Storybook at https://61aef69049f6bb003ac71914-qocfdyaegr.chromatic.com ✔ Started build 1 → Continue setup at https://www.chromatic.com/setup?appId=61aef69049f6bb003ac71914 ✔ Build 1 auto-accepted → Tested 9 stories across 4 components; captured 8 snapshots in 9 seconds ✔ Build passed. Welcome to Chromatic! We found 4 components with 9 stories and took 8 snapshots. ℹ Please continue setup at https://www.chromatic.com/setup?appId=61aef69049f6bb003ac71914 ログの中で表示されている https://61aef69049f6bb003ac71914-qocfdyaegr.chromatic.com/?path=/story/example-introduction--page という URL にアクセスすると、Storybook を閲覧できます(プロジェクトを削除したため、現在は表示されません)。 Chromatic のトップページに taskbox プロジェクトが追加されていることが確認できます。 Chromatic のプロジェクト一覧 taskbox プロジェクトを見る taskbox プロジェクトをクリックすると、次の画面に遷移します。 Chromatic の Build ページ メニューの内容は以下の通りです。 Builds:過去のビルド履歴を閲覧できる Library:Storybook のストーリー単位でコンポーネントを表示できる Manage:Slack 連携や URL からアクセスできる Storybook の公開範囲設定、コラボレーション相手の選択などができる メニューの中から Builds を開いてみましょう。 Builds の中を覗く Builds をクリックします。すると、以下の画面が表示されます。 コンポーネント一覧 Storybook のファイル単位ではなく、ストーリー単位でコンポーネントが区切られています。 一番上の Button をクリックします。すると、Button コンポーネントのキャプチャが撮影されていることがわかります。 ボタンコンポーネントのスクリーンショット また、右下に DOM が表示されていることがわかります。こちらは Header コンポーネントです。 ヘッダーコンポーネントのコードが表示されている 差分を追加する 次に、差分確認をします。Header コンポーネントと Page コンポーネントのコードを変更します。 変更点は以下の3つです。 Header で右上のボタンの位置を入れ替え Page のタイトルの Storybook を Chromatic に変更 Page の文章の一部を削除 変更前 変更後 変更前の Page コンポーネント Page コンポーネント変更後 この変更を加えた後、再度 Chromatic でビルドします。 $ npx chromatic --project-token=your-token すると、ビルド2 が追加されました。 Build 一覧 差分をレビューする ビルド2 を表示すると、変更があるコンポーネントがピックアップされています。 Build 2 のコンポーネント一覧 一番下の Page の「Logged Out」を表示してみます。すると、コンポーネントの Build1 と Build2 の見た目とコードの差分がハイライトされています。 単体 比較 Page コンポーネントの差分ハイライト Page コンポーネントの変更前と変更後が比較されている もし差分が意図通りであれば、右上の Accept ボタンをクリックします。そうでなければ Deny をクリックして、コードを変更した人に再度確認して貰います。 変更点を Accept するボタンが表示されている Accept をするのはコードを改修した人ではなく、別の人にレビュワーとしてクリックしてもらうのが望ましいです。BASE の BBQ の開発フローでも、コードレビューをするレビュワーが Accept や Deny をすることにしています。 全てのコンポーネントを Accept すると Build2 の表示が緑になります。 Build 2 のコンポーネントの差分が全て緑色になっている GitHub Actions で Chromatic を実行する Chromatic は GitHub Actions で実行できます。実際、 公式で yml の書き方が紹介されています。 # .github/workflows/chromatic.yml # Workflow name name : 'Chromatic' # Event for the workflow on : push # List of jobs jobs : chromatic-deployment : # Operating System runs-on : ubuntu-latest # Job steps steps : - uses : actions/checkout@v1 - name : Install dependencies run : yarn - name : Publish to Chromatic uses : chromaui/action@v1 # Chromatic GitHub Action options with : token : ${{ secrets.GITHUB_TOKEN }} projectToken : ${{ secrets.CHROMATIC_PROJECT_TOKEN }} CHROMATIC_PROJECT_TOKEN は、冒頭で取得したトークンのことです。このトークンをレポジトリの Secrets に設定すると、GitHub Actions から読み取ることが可能です。 Chromatic を CI に組み込むことで、PR から Chromatic のステータスをチェックすることができます。 GitHub の CI の結果一覧 差分がなかったり、あるいは全て Approve された場合には緑のチェックマークがつきます。差分がある場合は、黄色い丸のマークがつきます。 これで Chromatic をレビューの工程に組み込めました。 Chromatic のその他の機能 Chromatic は差分を表示する画面にコメントできたり、ビルド結果を Slack に通知したり、コメントをメール通知可能です(コメントの Slack 通知は未対応)。 また、プランをアップグレードすると、Chrome だけではなく Firefox、IE11 でのスクリーンショットを撮影可能になります。BASE では Starter プランに加入しています。 Chromatic の価格プラン一覧 また、Chromatic 上の Storybook に対して GitHub 認証を設定できるので、万が一 URL が漏れて外部の人が覗いてしまうということも防止できます。 おわりに Chromatic はセットアップや GitHub 連携がとても簡単です。無料プランもあるので、 Storybook を活用している会社・組織・チームであればぜひ活用を検討してみてはいかがでしょうか。
この記事はBASE Advent Calendar 2021の8日目の記事です。 こんにちは Slackの好きなショートカットは Shift + Esc の横山です。 SRE Groupに加わって4ヶ月が経ちました。 こちらの記事が書かれてからも4ヶ月が経ちました。 devblog.thebase.in ↑を未読の方がいましたらぜひぜひ読んでください。 同じ4ヶ月という事で振り返ってみたいと思います。 ちなみにこの記事を読む前に入社を決めていますがカジュアル面談の時に同じように説明して頂けました。 チームで大事にしていることは? ・「信頼性=ユーザの期待値を超え続けること」としてこれを維持し続ける ・BASEの機能等々の価値を高めるための時間を多く作っていけるようにする この2つを大事にしていると書いてあります。 私はOKRを決めるのが苦手なので、このようにチームで大事にしているものがわかっていると目標が立てやすかったです。 チームの働き方は? 3ヶ月経ちましたが、COVID-19の影響でリモートで業務を行なっております。 基本的にはSlackやZoomを使用してコミュケーションを取っています。 また、朝会(月〜金、12:00〜12:30)とSync SRE(隔週金曜)を行なっていると上記の記事には書かれていましたが、現在は少し変わっています。 Sync SREが隔週金曜から月〜金の16:30〜17:00になりました。 変わった理由ですが、これは私が入社するにあたって朝会だけではなく夕方にも情報共有や質問をしやすいようにと配慮してくださいました。 Slackで気軽に聞くことが出来る環境が整っているとはいえ、文字するのと口にするのとでは違うと思っているのでこういう場を設けてもらえたのはとても感謝しています。 ※Sync SREとは 進捗の確認、相談をしてチームに情報共有します。 チームの業務は? トイル削減/サービスの運用効率化のためのTerraformを使用してInfrastructure as Codeが進んでいます。 各開発プロジェクトからのAWS上のインフラリソースの作成、変更の依頼があり対応をしていると本来進めたい業務が滞る事があります。 なので、それら依頼からインフラリソースのコード化をしています。 このまま進めると作成、変更の依頼から作成、変更が記述されたPull Requestをレビューするという形になる予定です。 ただ、いきなりTerraformを書くことを強要するのは正しくないので、他チームや開発プロジェクトと上手に連携してSREだけではなく、みんなでInfrastructure as Codeが出来るとより良くなると思っています。 おわりに 前職ではチームがなかったので、意見の交換をする機会がありませんでした。 ですので、SREチームに転職してみて色んな考えに触れ、私の考え方のアップデートする事が出来たのではないかと思います。 まだ大きな成果は出せていませんが、コツコツと積み重ねて気がついたら大きくなるように貢献していけたらと思います。 拙い文章ではありましたが、最後までお付き合い頂きありがとうございました。 明日は「Docker 環境から webpack-dev-server に繋いで HMR する」になります。楽しみですね。 おまけ Youtubeで「大規模サービスの運営を支える、BASEのSREとして働く醍醐味と目指すチームのあり方」という内容でインタビュー動画を公開しています。 宜しければご覧ください。 【前編】大規模サービスの運営を支える、BASEのSREとして働く醍醐味と目指すチームのあり方 【後編】大規模サービスの運営を支える、BASEのSREとして働く醍醐味と目指すチームのあり方 また、少しでも興味が湧きましたらさくっとカジュアル面談が出来ますのでいつでもご連絡ください。 こちらよりお願いします 応募要項なども こちら にありますのでどうぞ。
この記事は BASE アドベントカレンダー と Looker アドベントカレンダー 8 日目の記事です。 はじめに BASE BANK 株式会社にて事業開発を担当している猪瀬 ( @Masahiro_Inose )です。 私達のチームでは、BASE ショップを運営しているショップオーナー様が簡単に資金調達をできる「 YELL BANK 」というサービスの開発・運営しています。 thebase.in 今回の記事は以下の二部構成となります。 前半部分は私から Looker という BI ツールを使って、サービス利用者の利用状況や関連情報を一元的に把握できる、「ショップカルテ」なるものを作成したことについて紹介します。 後半部分は Looker で扱いやすくするためのデータの加工を担当した永野( @glassmonekey )から、データ基盤周りやデータ加工の工夫した部分について解説します。 ちなみに過去の記事の宣伝ですが、弊チームでは BigQuery を活用して簡易的な CRM を作ったりもしました。 devblog.thebase.in ショップカルテとは 「ショップカルテ」とは、「YELL BANK」というサービスを利用しているショップの「サービスの利用状況」「アクセスログ」「ショップの運営状況(GMV や在庫の変動など)」等の複数のデータを 1 つのダッシュボードで一元的に把握できる仕組みのことです。ショップの健康状態を把握できるようなイメージから「カルテ」と呼んで愛用しています。 なぜ作ったのか プロダクトの機能改善を進めていく上では、「利用してくれているユーザーを ありありとイメージできるようになること 」が大変重要だと思っています。 例えば以下のようなことが具体的なアクションとして挙げられます。 BASE ショップを自分で作って販売をしてみるといった、いわゆるドッグフーディングをする 積極的にユーザーインタビューをしたり、オーナーさん直接会いにいく ショップ名を見るだけで「どのようなオーナーさんが、どんな気持ちで、どんな理由でサービスを使ってくれているか」が浮かんでくるようになると、日々のサービス改善案の仮説精度が高まります。 しかし、私が携わっている「YELL BANK」というサービスは、将来債権の買取という仕組みによってショップの資金調達を実現するという、世の中にあまり事例のないサービスです。自分自身も気軽に毎日利用できるようなサービスではないので、どうしてもユーザーの体験が想像しにくいという特徴がありました。 また、何かサービス改善のヒントになりそうなオーナさんの行動を発見した際に、いざその行動について分析を進めるとなると以下のような作業が必要でした。 複数のクエリを書いてデータを抽出 スプレッドシートでデータを加工 比較 分析 etc … これらの工数がかかってしまうこともオーナーさんの体験を想像しにくい問題に拍車をかけていました。 そこで、「ショップカルテ」を作成し、閲覧したいショップの ID をフィルター指定するだけで、「サービスの利用状況」や「ショップの運営状態」などの複数のデータをダッシュボード上にまとめて可視化する仕組みを整えました。これによりショップの行動とその理由をパッと簡単に把握しやすくしました。 「ショップカルテ」の構成 サービスの利用状況に関連する複数のデータを 1 つのダッシュボードにまとめています。 4 つのデータカテゴリによって構成されています。 閲覧できるデータカテゴリ 目的 基本的なサービス利用情報 利用開始日や累計の利用金額など、基本情報の把握 各種機能の利用推移、履歴 「YELL BANK」をどの程度使いこなしているかの把握 サービスページアクセスログ推移 サービスへの興味関心、利用熱意の移り変わりの把握 ショップの販売状況推移 ショップの運営状況とサービス利用状況の相関把握 を直感的にパッと把握できるようになります。 ※実際の Looker ダッシュボードの画像。データ部分はマスクしております。また、本当は縦に繋がっているのですが、画像が縦長になってしまうので半分に分割して横に並べています。 これによって、「このショップはなんでこういう動きをしたんだろう」という疑問が浮かんだ時に、ショップの ID を指定するだけでパッと仮説につながるインサイトを得ることができるようになりました。 データ基盤の構成 こんにちは、BASE BANK 株式会社にてアプリケーションエンジニアをしている永野( @glassmonekey )です。 私からはデータ基盤の構成やどのようにデータを準備したのか、 Looker で扱うために工夫した点を紹介します。 「YELL BANK」の業務データは基本的には MySQL 上に永続化しており、Fargate 上で Embulk のコンテナを動かして日時で BigQuery に同期する仕組みを取っています。 同期する仕組みの詳細は後日別の記事で公開予定です。乞うご期待!! ちなみに、バージョンは安定版の 0.9.23 を使用しています。 Embulkとは Embulk とは様々なデータソース(s3, RDB, etc…)からデータを加工しつつ転送できる、並列バルクデータローダーです https://www.embulk.org/ プラグイン形式で様々なデータ形式に対応しています。 プラグイン自体は Gemfile で指定します。 詳細はこちらのスライドシェアに載っています。 Embulk, an open-source plugin-based parallel bulk data loader from Sadayuki Furuhashi 必要なデータの種類 ショップカルテは基本的にはショップオーナの状態を我々が個別で見ることを目的としています。 それにあたっては大きく2種類のデータが必要になることがわかりました。 データは BigQuery に集約してるので、クエリなどは BigQuery のものになります。 それぞれで集計する単位や起点が変わってくるのでそれぞれエクスプローラを別に作りました。 現在の状態 (ショップ単位で集計したいデータ) 時系列の状態 (ショップと日時単位で集計したいデータ) 現在の状態について 「現在の状態」のデータというのは更新されうるデータのことを指します。 例えば、「YELL BANK」 の場合だと「現在の資金調達回数」という指標はこれに該当します。 ショップのユニーク性を担保する行が起点 になるようにエクスプローラを設計します。 時系列の状態 「時系列の状態」というのはある時点でスナップショット的に扱うデータのことを指します。 これらのデータはそのまま業務用テーブルを join すればいいわけではありません。 特に Looker においてはディメンションに対してメジャーを用意することになるので、 日付 + ショップのユニーク性を担保する行を起点 にデータを設計する必要があります。 そこで日時のカレンダーテーブルをショップごとに Cross Join して用意することにしました。 カレンダーテーブルは以下のような形で作りました。 2018 年 12 月 1 日から現在まで 1 日単位で、利用者を示すテーブルの ID と日付のペアを作っています。 ユーザーテーブル.作成日 <= timestamp(d.n)  で作成日以降のみ作られるようにしてるなど、不要なデータは極力作らないようにしています。 WITH date_map (SELECT day FROM UNNEST(GENERATE_DATE_ARRAY( ' 2018-12-01 ' , CURRENT_DATE , INTERVAL 1 day)) AS day ) SELECT ユーザーテーブル.id, TIMESTAMP (d.day) AS user_day FROM ユーザーテーブル CROSS JOIN date_map AS d WHERE ユーザーテーブル.作成日 <= TIMESTAMP (d.n) ORDER BY user_day 一度このデータを作ってしまえば Looker には dimension groupのtimeframe を用いて月ごとの集計や曜日などの集計にも 使い回せるのでかなり便利です。 dimension_group: created { type: time timeframes: [time, date, week, month, quarter raw] sql: ${TABLE}.user_day ;; } Lookerでのデータの扱い 基本的に BQ には業務データとほぼ同じテーブルスキーマを当て込んでいます。 そして、 Looker 上では派生テーブルを用いて BigQuery のクエリから中間データを生成して Looker で扱いやすくしています。 基本的に前述した集計する単位でエクスプローラをそれぞれ切りつつ、 JOIN が多段にはならないように view を作るのが良いと考えています。 基本的には 一意性を担保するカラムに primary_key を設定してやりつつ、メジャーには distinct 系を使ってあげれば重複を避けて集計を行えます。 しかし、多段的なJOINデータを扱うことは複雑性から意図せぬ挙動に繋がることが度々あり、メンテナンスも大変なので予めデータを集計しやすく派生テーブルを使っておくことが無難です。SQL からも派生テーブルは作れるので大変便利です。 docs.looker.com そこで設計する派生テーブルの構成は可能ならばスタースキーマやワイドテーブル(one-big-table)で作って上げるのが良いでしょう。 特に現代ならマシンパワーもあるので、個人的には設計コスト的や扱いやすさ的にもワイドテーブルが良いのではと思っています。実践はそこまでできてはいませんので今後の課題です。 スタースキーマとワイドテーブルの比較は Fivetran 社の Michael Kaminski 氏の記事に詳しく載っています。 fivetran.com 最後に Looker のダッシュボード作成の一例の参考になったのなら幸いです。 ショップカルテを作ることで、当初目的通り属人化せずにショップごとの状態を把握できるようになりました。これがデータの民主化なんだと改めて実感しました。 また 1 データごとにデータを検証しつつ作業を進めることができたので、 Looker 初心者の我々が最初に作るものとしては良かったなと思います。 最後に宣伝ですが、まだまだやれてないことがたくさんあります。一緒にプロダクトを成長させていく仲間を募集中です。 open.talentio.com
この記事は BASE Advent Calendar 2021 の7日目の記事です。 話題の「メタバース」を体験したい こんにちは、BASE株式会社でデザイナーをしている渡邊です。 最近なにかと話題になっている「メタバース」。『コンピュータやコンピュータネットワークの中に構築された現実世界とは異なる3次元の仮想空間やそのサービスのこと(Wikipediaより引用)』だそうで、オンライン空間で色々な活動ができることを想定しているそうですね。 私もVRには以前から興味があり、先日ついにOculus Quest2を購入しました。プライベートで音楽ゲームや釣りゲーム、VRSNSを存分に楽しんでいます。 特に仮想空間で色々な人とコミュニケーションを取る体験は最高に楽しく、「一度こっちの世界にハマったら戻れない」と言われる理由を痛感しています。 コミュニケーションがこんなに盛り上がるなら、仕事にも活かせるんじゃないか?というわけで、実際にVR仮想空間上でお仕事をしてみました! VR空間でデザイン相談会はできるのか? 同じデザインチームでVR環境を持っている人がいないか聞いたところ、Oculus Quest2を持っているメンバーが1人いたので、VRお仕事実験に協力してもらうことにしました。 デザインチームでコミュニケーションが必要なお仕事と言えばもちろんレビューです。 弊社ではデザインのフィードバックを気軽にもらってブラッシュアップする会のことを「デザイン相談会」と呼んでいます。 いつもはZoomと画面共有を使ってやっていますが、仮想空間ならより捗るのでしょうか...? 「horizon Workrooms」でチームメンバーに会ってみた 今回利用した仮想空間ツールは「horizon Workrooms」。最近Facebookから社名変更したMeta社の提供するオンライン会議ツールです。 www.oculus.com リモートでも直接会っているような体験が得られるそうですが果たして... あっ!!いた!!!! 今回協力してくださったチームメンバーのnomuraさんが、仏のような穏やかな顔(退席状態)で目の前に現れました。 音声が入らないトラブルがあったものの、なんとか仮想空間に集合することができました。リアルタイムで動くメンバーが視界に入ると、本当に「いるな...!」という実感がありました。私の手の動きも細部までVR化。けっこう感動します。 意外と快適!仮想空間でのデザイン作業 「horizon Workrooms」ではPCの画面をVR空間上にミラーリングすることができ、ヘッドセットを外さなくてもPCを操作することができます。 コントローラーを使わずに手のジェスチャーのみで操作できるようになっており、UIも丁寧に設計されています。 また、現実世界の手元のデスクの映像がモノクロで表示されるため、キーボードやマウス操作もちゃんとできます。入力のラグもほぼなく、ストレスは感じませんでした。 手元カメラの画質は荒いので、タッチタイピングができないと少し厳しいかもしれません。(録画映像には表示されないようで画像では真っ黒になっています) 録画のキャプチャなのでこの記事の画質はやや荒いのですが、実際に私が見た映像では画質が想像以上に高く、画面の小さい文字も問題なく読むことができました。 画面を大きく共有することもできるので、快適でした! トラブル発生と仮想空間故の移動の手間 が、ここでメンバーにトラブルが発生...。デスクトップのミラーリングがうまくいかなかったようです。 手元のPC画面が見れないので、画面共有の目の前に立ち一緒に見ながらフィードバックをもらうことに。 ホワイトボードや画面共有の前に立つには、現実世界で実際に席を立ち、別の方向に向かい直す必要があります。 画面共有前に立っている間はPCの操作ができないので、FBをもらうたびに席に戻る手間が発生してしまいます...。この手間がけっこうめんどくさく、「現実ならすぐなのにな」と思わざるを得ませんでした。 VR空間上で感じたメリット VR空間でレビューをはじめてすぐは「正直zoomとあんまり変わらないな...」という感じだったのですが、ある程度動作に慣れ見る余裕ができたとき、相手が傾聴姿勢なのがすごくリアルに伝わるのがとても嬉しかったです。 今回のツールでは顔の表情まではトラッキングされないのですが、体の傾きや身振り手振りだけでもなんとなく内容へのリアクションが伝わってきました。(気のせいかもしれませんが) ホワイトボードの前に立ち、説明した後振り返って「ここまではわかりましたか?」と確認するコミュニケーションがスムーズにできました。 画面共有とビデオの場合、この「ここまではわかりましたか?」のやりとりで反応が返って来ずちょっと寂しい思いをしたこともあるのですが、こちらを注視して、軽く頷いてくれるだけで聞いてくれている実感が得られるのはVR空間上ならではのメリットだなあと感じました。 現実世界の自分の体問題 VRを30分以上続けていると、ヘッドセットの重みでじわじわと頭が痛くなったり、怠くなったりしてきました。 チームメンバーのnomuraさんは、おそらく視力?の問題で、終始画面がややぼやけていたそうです。 仮想空間では没入感ある体験ができますが、こういったリアル体力の問題で目の前のことに集中できず、だんだん注意力が散漫になっていきました。 またVR特有の問題で目の前でチームメンバーの挙動がバグるという現象が多発し、真面目な話をしていてもどうしてもウケてしまいます。 私も一定時間腕が3mぐらいに伸びていたそうですが、それに気付かず熱弁していたため、話がひと段落着くまでnomuraさんは静かに耳を傾けてくれていました。VRで仕事をするには新時代の大人な対応が必要になってくるのかもしれません。 まとめ VR空間でデザイン相談会はできるのか? 今回の検証の結果「 できないこともないが、まだ早い 」という印象でした。わざわざやるほどではない...。現実快適すぎ...。というのが正直な感想です。 自分1人が気分転換がてらVR空間で作業をすることは問題なく出来そうでした。ぽつんとPC画面だけがある環境に放り込まれるので、サボりようがないのも良いです。 しかしながら、複数人で作業をするとなると急に難易度が上がります。 チームメンバーが全員万全の環境を整えない限り、ストレスなくコミュニケーションを取ることはできなさそうでした。 もしツールの機能をフル活用できたとしても、ヘッドセットの重さがある以上長時間の会議は難しいと感じました。 ただ、ハード面・ソフト面どちらもここから順当に進化していけば、近いうちに同僚と本当にオフィスで働いているような体験が出来そうだという可能性も感じました。 今回の体験からメタバースの最前線はどうなっていくのかさらに興味が強くなったので、未来に期待しながら引き続きVRに触れ続けたいと思います! 明日のアドベントカレンダーはエンジニアの横山さんです!お楽しみに!
この記事は BASE Advent Calendar 2021 の6日目の記事です。 フロントエンドエンジニアの @rry です。 今年の4月に BASE にジョインしてから、アクセシビリティに関する取り組みを少しずつ行ってきました。 BASE ではこれまでアクセシビリティに関する取り組みは局所的にしか行われておらず、また私自身もアクセシビリティについて知見が全くない状態でした。このような状態からアクセシビリティやっていきを具体的にどのようにして進めているかについてご紹介したいと思います。 アクセシビリティとは?何故アクセシビリティに取り組むのか? アクセシビリティとは | ウェブアクセシビリティ基盤委員会(WAIC) 一般にアクセシビリティとは、アクセスのしやすさを意味します。転じて、製品やサービスの利用しやすさという意味でも使われます。 似た意味をもつ言葉にユーザビリティがありますが、アクセシビリティはユーザビリティより幅広い利用状況、多様な利用者を前提とします。 アクセシビリティとは何か?については、こちらの URL に書いてあるとおりですが、 幅広い利用状況、多様な利用者でもアクセス(利用)しやすいか について言い表す言葉です。 よく誤解されやすいのが、「アクセシビリティは障がい者や高齢者のための取り組み」という捉え方をされがちですが、これはアクセシビリティ本来の主旨からずれた理解です。 例えば幅広い利用状況の例で言うと、 電車やオフィスなどでイヤホンがなく、音声が聞けない 眼鏡を忘れてしまい小さい文字が読みづらい パソコンのマウスが壊れてしまいキーボードでしか操作できない など、健常者であったとしても状況によって様々な問題が起こりえます。 もちろん障がい者や高齢者であっても、アクセス(利用)しやすい Web サービスが優れているということに変わりはないのですが、アクセシビリティとは本来そういったユーザーだけでなくもっと幅広いユーザーに対して有用な取り組みです。 BASE は BtoC なサービスのため、様々な状況で色んなユーザーがサービスを利用しています。 また、BASE のミッションである「 Payment to the People, Power to the People. 」にもあるとおり、BASE が作っているサービスは世界のすべての人に向けたサービスです。 このような背景があって、できることから少しずつ、アクセシビリティを向上させていきたいという思いがありました。 社内にアクセシビリティを取り入れたきっかけ BASE に入社してから、Slack 上でちらほらアクセシビリティに関する話題が上がっているのを観測していました。 これらの機運があり、まずはできることから始めてみようということで Slack に #study-a11y チャンネルを作り社内勉強会を開催することにしました。 エンジニアだけでなくデザイナーの方もたくさんチャンネルにジョインしており、みんなでアクセシビリティに関する話をする場を作れたのが良かったです。 余談ですが、 :a11y: というスタンプが押されたら Reacji Channeler でチャンネルに通知するように設定しておくのも、アクセシビリティに関する話題が拾いやすくてオススメです。 アクセシビリティ勉強会の開催 BASE では読書会の文化があるので、アクセシビリティに関しても読書会形式で知見を深めていこうと思い、以下の2冊を参考図書にしています。 デザイニング Web アクセシビリティ 読書会で読了 コーディング Web アクセシビリティ 次回読書会開催予定 デザイニング Web アクセシビリティについては、全8回の読書会が終わり色々と学びがありました。読書会はデザイナーさんがメインに参加しており、BASE の現状と照らし合わせながら具体的に見えてきた改善ポイントについてまとめたりしました。 コーディング Web アクセシビリティは、これからフロントエンドエンジニアがメインとなって読書会をしていく予定です。コーディングの観点からまた改善ポイントが出てくると思うので、そちらもまとめていきたいと思っています。 勉強会を通して気づいた改善ポイント 勉強会をしていると、回を重ねるごとにたくさん改善ポイントが出てきました。 たくさんある中からそのうちいくつか問題となっていることを挙げます。これらの問題は、同じように Web サービスを開発していく上でありがちかもしれないので、他の開発者にとっても参考になるのではと思い挙げてみました。 ※ これらは2021/12時点での問題で、今後改善されていく可能性が高いです カラーコントラスト比が足りていない 達成基準 1.4.3 を理解する | WCAG 2.0解説書 1.4.3 コントラスト (最低限) : テキスト及び文字画像の視覚的提示に、少なくとも 4.5:1 のコントラスト比がある。ただし、次の場合は除く: (レベル AA) 大きな文字: サイズの大きなテキスト及びサイズの大きな文字画像に、少なくとも 3:1 のコントラスト比がある。 付随的: テキスト又は文字画像において、次の場合はコントラストの要件はない。アクティブではないユーザインタフェース コンポーネントの一部である、純粋な装飾である、誰も視覚的に確認できない、又は重要な他の視覚的なコンテンツを含む写真の一部分である。 ロゴタイプ: ロゴ又はブランド名の一部である文字には、最低限のコントラストの要件はない。 デザインに関する問題で、今はデザイナーの方が主体となって具体的にどこがコントラスト比が足りていないか調査したりしている段階です。 カラーコントラスト比をチェックするツールは様々な用途からいくつかあります。 Accessibility Insights for Web - Chrome ウェブストア A11y - Color Contrast Checker – Figma Accessibility Addon | Storybook これらを利用して、実際の Web ページやコンポーネントのカラーコントラスト比をチェックすると、基準に達していない部分が分かります。 これらをすべて改善していくというのはとてもサイドプロジェクトでは収まりきらないのですが、問題として認識してはいるので、どこかのタイミングで改善に向けて動けないかなと機会を狙っています。 ユーザーに伝えたい文章が画像になっている(スクリーンリーダーで読めない) 達成基準 1.4.5 を理解する | WCAG 2.0解説書 1.4.5 文字画像: 使用している技術で意図した視覚的提示が可能である場合、文字画像ではなくテキストが情報伝達に用いられている。ただし、次に挙げる場合を除く: (レベル AA) カスタマイズ可能: 文字画像は、利用者の要求に応じた視覚的なカスタマイズができる。 必要不可欠: テキストの特定の提示が、伝えようとする情報にとって必要不可欠である。 注記: ロゴタイプ (ロゴ又はブランド名の一部である文字) は必要不可欠なものであると考えられる。 BASE のページでは、具体的にはこちらの404ページなどがそうなのですが、ユーザーに向けて伝えたい文章が画像になっています。 指定されたページが見つかりません | BASE 「404 PAGE NOT FOUND. お探しのページは存在しません」 スクリーンリーダーで訪れた場合など、上記の文言は読み取られない状態です。 BASE では今404ページのリデザインの話も上がっているので、こちらは案外早く改修するかもしれません。 とはいえこういう、本来テキストで表示するべきところが画像になっている、というケースは Web サービスを利用していてよく見かける気がします(特にレガシーな Web サービスや LP に多い印象) CSS や SVG や Web フォントなどを利用して、マシンリーダブルなコードを書いていきたいと思いました。 フォーカスが当たっているか分からない・そもそもフォーカスが当たらない 達成基準 2.4.7 を理解する | WCAG 2.0解説書 2.4.7 フォーカスの可視化: キーボード操作が可能なあらゆるユーザインタフェースには、フォーカスインジケータが見える操作モードがある。 (レベル AA) フォーカスが当たっているにもかかわらず、フォーカスが当たっていないときとスタイルが全く変わっていない BASE のカート(決済画面)の入力要素など、複数箇所あります。 こちらの改修はコンポーネントを作るときやコーディングするときにきちんとフォーカスがあたったときのスタイルをつけよう、で終了するお話ですが、 :focus や :visited のスタイルについてはなかなか見落とされがちであり、実装についても少し考慮が必要です。 まず分岐として、以下の2点で実装の難易度やデザインのクオリティが分かれてきます。 ブラウザが自動でつけるフォーカスをそのまま利用する メリット: フォーカスが当たったときのスタイルを書く必要がないため実装工数がかからない デメリット: デザインの雰囲気がブラウザによって左右されてしまう フォーカスが当たっているときのデザインを用意する メリット: どのブラウザで見ても統一性のあるデザインのフォーカスのスタイルが当たる デメリット: 実装工数がかかる・フォーカススタイルの付け忘れに気をつけなければいけなくなる 具体的に「フォーカスが当たっているときのデザインを用意する」場合は(例えばですが)以下のようなことを考えながら開発することになりそうです。 まずブラウザのデフォルトのフォーカスのデザインが邪魔になるため、CSS ですべての要素のフォーカスのスタイルを打ち消します。その状態でフォーカスのスタイルをうっかり付け忘れてコンポーネントを作ったりすると、例のフォーカスがどこに当たっているのか分からない残念なコンポーネントのできあがりです。 Figma などでデザインを作るときにフォーカスが当たったときのデザインをセットで作るなどでスタイルの付け忘れは防ぐなりする必要がありそうです。 では、次に outline プロパティを使ってフォーカスをつけるとします。 .outline : focus { outline : black solid 2px ; } 一見これで問題なさそうに見えます。しかし、もし要素が border-radius で丸められていたとしたらどうでしょう?試しに Safari で要素にフォーカスしてみましょう。 Safari(IE も?)では、outline が丸まらずに角張って表示されてしまいます。 そのためここで Safari だけ角張っても問題ないかデザイナーの方とすりあわせる必要があります。 それか角張りを許容できない場合は box-shadow を使ったスタイルを当てれば意図したスタイルになります。 .boxshadow : focus { box-shadow : 0 0 0 2px black ; } See the Pen Untitled by rry ( @ryamakuchi ) on CodePen . このようなことを考慮しなければならないため、フォーカスのスタイルは少し注意が必要です。 ちなみに tailwindcss もフォーカスのスタイルは box-shadow で実装されており、こういったフレームワークを利用するのも一つの手かもしれません。 Ring Width - Tailwind CSS そもそも div や span、href のない a タグなどで要素が作られておりフォーカスが当たらない こういった要素は、非常に多くの Web サービスで目撃しますが、BASE も例に漏れず沢山あります。ショップの管理画面の SP レイアウトのハンバーガーボタンなどがこれにあたります。 いわゆる「虚空問題」(1の虚空)です。 noteの虚空ボタンを顕現させる社内勉強会を開催しました|sawa / note inc. さっき会社で、アイコンフォントだけがリンクやボタンになっていてテキストノードがないものを「虚空」と呼ぶことになった — Rikiya Ihara / magi (@magi1125) March 1, 2019 note さんの記事に書かれているような取り組みまではまだ実施できていませんが、まずクリッカブルな要素は基本的に button や label に置き換えられそうなので、button や label を使った実装に置き換えていきたいと考えています。 来年以降にやっていきしたいこと 年末なので来年の抱負を掲げたいと思います。 まずは「コーディング Web アクセシビリティ」読書会を、フロントエンドエンジニア中心のメンバーで行うことです。 このブログでとりあげたこと以外にも、アクセシビリティを意識したコーディングの知見はたくさんあるため、開発メンバー主体となって学んでいければと思います。 また、本格的にアクセシビリティのチェックを行いたいとなったときには、 アクセシビリティ試験 を実施するのが良さそうです。 試験についてのQ&A | ウェブアクセシビリティ基盤委員会(WAIC) アクセシビリティ試験なるものはこちらの勉強会で知ったのですが、具体的にどこが問題となっているのかをチェックすることができてとても役に立ちそうです。 もしあなたが『アクセシビリティ試験』をやることになったら WP ZoomUP #68 - connpass おわりに BASE で取り組んだアクセシビリティに関してまとめてみました。 アクセシビリティ専門のチームがあるような会社でない限り、 「アクセシビリティについて気になってはいるけれどどこから手をつけたらいいか分からない」 「アクセシビリティは組織に浸透させるのが難しい」 というお気持ちがある方のほうが多いのではないかと思います。でもまずは できることから少しずつやっていこう というマインドで、他の人の力も借りながらやっていくのが良いと思いました。 読んでいただいて分かるとおり、まだアクセシビリティに対してほんの少ししか取り組めておらず、課題がたくさん見えてきた状態です。 どうしたらうまくやっていきできるか試行錯誤しているため、他の会社ではどんな取り組みをしているのかぜひお話を聞きたいです 🙌 ぜひ @rry まで気軽にお声がけください。 明日のアドベントカレンダーはデザイナーの @Fuki さんです!お楽しみに〜 🎅
この記事は BASE Advent Calendar 2021 の 5 日目の記事です。 基盤チームの右京です。 最近ひょんなことから browserslist の設定を見返したのですが「babel や autoprefixer で必要になったので導入した」以上はあまり触れられていなかったため、この機会にいちから見直してみようと思いました。 browserslist? https://github.com/browserslist/browserslist 簡単に言えば、クエリを書くとそれに該当するブラウザをリストで取得できます。babel(preset-env) や autoprefixer はここから取得出来るリストを利用して、必要な変換内容を決定しています。単純にバージョン指定でのクエリが記述できるだけではなく、利用統計に基づく絞り込みも可能となっています。例えば、0.2% 以上のシェアがあり、メンテナンスが行われているブラウザは次のクエリで取得できます。 > 0.2%, not dead このクエリは package.json の browserslist として指定するか、 .browserslistrc を作成して記述すると認識されるようになります。今回は package.json に書く形で進めます。 { " private ": true , " browserslist ": [ " > 0.2%, not dead " ] } この状態になればブラウザのリストを CLI か JavaScript で取得できます。試しに CLI を使って取得してみます。 $ npx browserslist and_chr 96 and_ff 94 and_uc 12.12 chrome 95 chrome 94 chrome 93 chrome 92 chrome 87 chrome 61 chrome 49 edge 95 edge 94 firefox 93 firefox 92 ie 11 ios_saf 15 ios_saf 14.5-14.8 ios_saf 14.0-14.4 ios_saf 13.4-13.7 ios_saf 12.2-12.5 ios_saf 9.0-9.2 opera 79 safari 15 safari 14.1 safari 14 safari 13.1 samsung 15.0 babel からの利用 取得できたリストを babel(preset-env) がどのように利用しているかを確認してみます。利用には @babel/preset-env を設定する必要があるので、これを追加して設定します。 { "presets": ["@babel/preset-env"] } 次のようなコードを例に考えてみます。このコードは古いブラウザでは async/await が実装されていないため、このままでは動作できず何かしらの形に変換される必要があります。 (async () => { const response = await fetch( 'https://api.github.com/users/yaakaito' ); console.log(response) } )() 先程の browserslist の結果には async/await を実装していない IE11 が含まれているため、babel で変換すると置き換えがおこるはずです。 Async functions | Can I use... Support tables for HTML5, CSS3, etc > npx babel src/index.js "use strict"; function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } } function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; } _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee() { var response; return regeneratorRuntime.wrap(function _callee$(_context) { while (1) { switch (_context.prev = _context.next) { case 0: _context.next = 2; return fetch('https://api.github.com/users/yaakaito'); case 2: response = _context.sent; console.log(response); case 4: case "end": return _context.stop(); } } }, _callee); }))(); このように IE11 でも動作可能なコードに変換されました。ですが、BASE では IE11 のサポートを終了したため、対象ブラウザから IE11 を除外したいと思います。これもクエリを書くことで表現することが出来ます。 not IE 11 を追加します。 { " private ": true , " browserslist ": [ " > 0.2%, not dead ", " not IE 11 " ] } この状態で babel で改めて変換すると、対象にしているブラウザすべてで async/await が実装されているため変換が不要となり、出力されるコードが変化していることがわかります。 $ npx babel src/index.js "use strict"; (async () => { const response = await fetch('https://api.github.com/users/yaakaito'); console.log(response); })(); BASE の設定を見直す さて、きっかけとなった今現在 BASE で設定されている browserslist を見ていきます。browserslist は複数のリポジトリで共有しているため、プライベートな GitHub Packages として配信しており、このような形で利用できるようにしています。 " browserslist ": [ " extends @baseinc/browserslist-config " ] そしてその中身は...ちょっと頭が痛くなる感じでした。 ['ie >= 11', 'safari >= 7', 'iOS >= 10.0', 'and_chr >= 5.0', 'last 1 version', '> 1%'] and_chr 91 and_ff 89 and_qq 10.4 and_uc 12.12 android 91 baidu 7.12 bb 10 chrome 91 chrome 90 chrome 89 edge 91 edge 90 firefox 89 firefox 88 ie 11 ie_mob 11 ios_saf 14.5-14.6 ios_saf 14.0-14.4 ios_saf 13.4-13.7 ios_saf 13.3 ios_saf 13.2 ios_saf 13.0-13.1 ios_saf 12.2-12.4 ios_saf 12.0-12.1 ios_saf 11.3-11.4 ios_saf 11.0-11.2 ios_saf 10.3 ios_saf 10.0-10.2 kaios 2.5 op_mini all op_mob 62 opera 76 safari 14.1 safari 14 safari 13.1 safari 13 safari 12.1 safari 12 safari 11.1 safari 11 safari 10.1 safari 10 safari 9.1 safari 9 safari 8 safari 7.1 safari 7 samsung 14.0 明らかに内容が古いので、現在推奨している環境に合わせてクエリを見直します。 対応している利用環境・ブラウザは何がありますか – ヘルプ | BASE これをもとに考えると、このようなクエリが適用できそうです。 " browserslist ": [ " > 0.2%, not dead ", " not IE 11 ", " Safari > 11, iOS > 11 " ] 他のツールにも活用したい 最近、TypeScript の変換を速度面の課題から tsc → esbuild に置き換えたいと考えていて、実は今回見直すきっかけになったのもこれでした。Vue.js 2x での TSX サポートのために Babel を通す必要があるのですが、それ以外のものに関してはできれば esbuild のみで解決したい。esbuild の target はブラウザの指定が可能ですが、browserslist には対応していません。 esbuild - API Support browserslist · Issue #121 · evanw/esbuild これに browserslist を使えるようにしてみます。すべて対応しようとすると大変なので、ここでは chrome のみで考えます。 #!/usr/bin/env node const browserslist = require( 'browserslist' ); const { buildSync } = require( 'esbuild' ); const browsers = browserslist(); const target = [ browsers.reverse().find(t => t.startsWith( 'chrome' )).replace( ' ' , '' ) ] ; console.log(buildSync( { entryPoints: [ 'src/index.js' ] , target } )) 簡易的ですが、browserslist に含まれている chrome の一番古いバージョンを esbuild で解釈できる形に変換しています。これをビルドしてみると、先程と同じ結果になります。 > ./build.js { errors: [], warnings: [] } (async () => { const response = await fetch("https://api.github.com/users/yaakaito"); console.log(response); })(); 試しに async/await が実装されていないバージョン(chrome54)が含まれるクエリに変更してみます。 " browserslist ": [ " > 0.2%, not dead ", " chrome 54 ", " not IE 11 ", " Safari > 11, iOS > 11 " ] > ./build.js { errors: [], warnings: [] } var __async = (__this, __arguments, generator) => { return new Promise((resolve, reject) => { var fulfilled = (value) => { try { step(generator.next(value)); } catch (e) { reject(e); } }; var rejected = (value) => { try { step(generator.throw(value)); } catch (e) { reject(e); } }; var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected); step((generator = generator.apply(__this, __arguments)).next()); }); }; (() => __async(this, null, function* () { const response = yield fetch("https://api.github.com/users/yaakaito"); console.log(response); }))(); yield を使った結果に変化しました。簡単にではありますが、browserslist から esbuild の設定ができるようになりました。 おわりに なんとなく設定していた browserslist ですが、最初から見直してみることで考えられる活用の幅が広がったように思います。 今回は、すでに掲載している推奨環境からクエリを逆算しましたが、関係を入替えて browserslist から推奨ブラウザを生成し更新を半自動化するような運用も考えられそうです。 明日は @rry です、お楽しみに!
この記事はBASE Advent Calendar 2021の4日目の記事です devblog.thebase.in ごあいさつ はじめましての人ははじめまして、こんにちは!フロントエンドエンジニアのがっちゃん( @gatchan0807 )です 今回は、フロントエンドエンジニア界隈で話題になっているDenoについて調査し、プラスでSlack Botを書いてみることで現時点のDenoはどんな感じで使えるのかを検証したので、その経験をシェアできればと思っています!(実際にSlack Botを書くのは次回の記事の予定です) つらつらとDenoを触っていて気になったポイントを書いたためにかなり長い記事になってしまいましたが、お付き合いいただけますと幸いです! Denoの基礎知識 DenoはNode.jsの作者、Ryan Dahl( https://github.com/ry )さんがNode.jsの反省点を活かして よりセキュアで生産的になるように 開発されているJavaScirpt / TypeScriptランタイムです 2018年5月14日に Initial commit が発行 され、執筆時現在の最新版は v1.16.3 が最新となっていますが、開発スピードは非常に速く、執筆開始時点から約1週間でminorバージョン1つ、patchバージョンが3つ進んでいるので最新バージョンの情報は 公式ページ を確認してください⚠ 個人的には、日本Node.jsユーザグループ代表の古川さんの記事をきっかけに国内では話題になっていて知った記憶があります( Node.js における設計ミス By Ryan Dahl ) ※上記の記事の元になった、Ryan Dahlさん本人による発表・資料はこちら( YouTubeの発表動画 / 発表資料PDF ) また、国内コミュニティには日本語Slackチャンネルがあり、日本人Denoコミッターの kt3k さんもいらっしゃって、そこで色々情報交換などもされているので、少しでもDenoに興味があればぜひ参加されることをおすすめします( Deno-ja Slackへの参加方法 ) 公式でとりわけ特徴として上げている部分 ここからは公式ドキュメントで取り上げられているDenoの推しポイントリストを紹介しつつ、普段Node.js環境で開発をしている自分が触っていて特徴的だなと感じた部分も合わせて紹介していきます。 特徴的な機能 可能な限りWeb互換で利用できるようにする。例えば、ESModuleやfetchの対応など 明示的に指定しない限りネットワークやファイル、その他環境へのアクセスは行えず、セキュアに保つ インストールしてすぐにTypeScriptが使える(サポートしている) 単一の実行可能ファイル( deno ) ビルトインで様々なユーティリティツールが利用可能。( deno fmt : フォーマット機能、 deno lint : リンター機能、 deno test : テストランナー機能) Denoチームによって動作保証(レビュー)されている 標準ライブラリ 単一JavaScriptファイルへのバンドル機能 原文: - Web compatible where possible, for example through usage of ES modules, and support for fetch. - Secure by default. No file, network, or environment access (unless explicitly enabled). - Supports TypeScript out of the box. - Ships a single executable (deno). - Has built-in utilities like a code formatter (deno fmt), a linter (deno lint), and a test runner (deno test). - Has a set of reviewed (audited) standard library that are guaranteed to work with Deno. - Can bundle scripts into a single JavaScript file. https://deno.land/manual#feature-highlights 他にも概要を調べていて個人的に特徴的だな〜と思ったのが↓のあたりのポイントです URLによる依存ライブラリの取得と隠蔽されたパッケージファイル管理(No package.json & npm )。 一度ロードされたパッケージはローカルにキャッシュされている(バージョンごとに自動で入るので、手でパッケージ内の一部ファイルを消してしまったとかのよほどのことが無いと気にすることはなさそう) Comparison to Node.js (Node.jsとの比較)内にかかれている Deno always dies on uncaught errors. の通り、適切にキャッチして処理されないエラーは常に死ぬようになっている点 適切にキャッチして処理されないエラーは常に死ぬようになっている点に関しての補足(クリックで開閉) これを見て、最初は「とはいえ、死なないことなんて起きるのか…?」と思って調べていたのですが、Node.jsでは uncaughtException / unhandledRejection のイベントリスナーを設定することで、どこからもエラーがキャッチされずにprocessまで上がってしまった場合の後処理が書けることを知りました。 uncaughtException / unhandledRejection のイベントリスナーは本来、メモリリーク防止の為の後処理や、開いたまま終わると問題になるネットワーク・ファイルのクローズ処理を書くためにあるもので、Node.jsの公式ドキュメントにも注意して利用するように記載されています ( https://nodejs.org/api/process.html#process_event_uncaughtexception ) デフォルトではprocessまでエラーが上がった場合にはスタックトレースに出力してexit-code 1で終了させるだけですが、 ここを書き換えることによってexit-codeを0で終了させたり無理やり復旧させることができてしまうのが問題になっていました そのため、Denoでは適切に処理されない場合には常に死ぬように設定されているようです また、ちょうどこの辺りを調べていた時にDeno側でちょっと面白い議論を見つけて、記事執筆時の2日前に動きがあったのであわせて共有しておきます( https://github.com/denoland/deno/issues/7013#issuecomment-737621469 ) ざっくりいうと、「ライブラリ側で何らかの理由でキャッチされずに放置された Reject ( Promise の失敗時の対応)があった場合に有無を言わせずに死ぬので、どこで死んだかの調査がとても大変&アプリケーションになると現実的に無理ぽ。」というお話 これはLinterでもチェックされないし、人間は完璧にコードを書けるわけじゃないから対応してほしいよ〜😢というIssueがあり、ちょうど2日前に、ひとまずNode.js互換モードとして onunhandledrejection の実装が決まったようです https://github.com/denoland/deno/issues/7013#issuecomment-974256318 Denoの性質上、すべての非同期処理がPromiseで返されるので Unhandled Rejection を対応できるようにする方法は Uncaught Exception を対応できるようにする方法と同義になるんじゃね?とかのコメントを見て全体像が理解できたので、個人的にすごく学びがあったIssueでした あとついでに Foot-gun というワードとその意味を知れたのも学び https://twitter.com/MengTangmu/status/1069751647771877376?s=20 Deno CLIから提供されるツール群 Denoの基本的な思想も面白いのですが、CLIコマンドが豊富でNode.js環境だと乱立しているテストランナーやフォーマッターなどがランタイムに標準装備されているのも面白いポイントです(JSConfでも、yosuke_furukawaさんとkt3kさんが「揺り戻しのよう全部入りのランタイムになってきているというのが面白い」という話がありました) 主だったツールのコマンドと利用シーン 定義されたテストを実行する deno test chaiやsinonなどと同時に使うことも可能 指定のコードをフォーマットする deno fmt Lint実行を行う deno lint CLI上でJS/TSの実行結果をインスタントに試せる deno repl 依存ライブラリ一覧を表示する deno info ファイル指定を行わずに実行すればライブラリキャッシュの在り処を知れる スクリプトをCLIコマンドとしてインストールする deno install インストール時にきちんと --allow-net などで必要な権限を付与した上で実行する必要があるので注意 複数JS/TSファイルを実行可能・インストール可能な形にコンパイルする deno compile コンパイル時にも必要な権限を付与して実行する必要があるので注意 複数JS/TSファイルをES Module形式でまとめられる deno bundle 出来上がったファイルはES Moduleなので、直接ブラウザに読み込むことも可能 ドキュメントを自動生成する deno doc Denoの環境を作って触っていく ここまででざっくり基礎知識を得たので、実際にDeno環境を作って肌感覚を得ていこうと思います 今回は最近GAになり、なおかつ個人アカウントの場合に無料で利用できる GitHub Codespaces に環境構築を行おうと思います 雑なコードが残っているだけですが、こちらの リポジトリ に実際に試した設定ファイルなどが残っているので良ければご覧ください Codespacesの準備 この辺りはおなじみの光景なのでサクッと飛ばしますが、まずリポジトリを作成します 適当に README.md ファイルなどを置いて、ファイル一覧の右上に表示される < > Code ボタンを押下するとCodespaceを作成するボタンが出てくるので New codespace ボタンをポチッとしてください しばらく待つと、下記のように見慣れたVS Codeの画面がブラウザに表示され、ターミナルも起動されるので準備完了🙆 (作成中に表示される緑色の Open this codespace in VS Code Desktop ボタンを押すと、ブラウザではなく手元のPCのVSCodeが起動して、そこからCodespacesに接続する形になります) Denoのインストール ここからはCodespaces上に構築されたLinux環境にDenoをインストールしていきます https://deno.land/ まずは公式ドキュメントに書かれている通りにCurl / ダウンロードされるShellScriptを実行してダウンロード → deno -V でインストールの完了を確認します $ curl -fsSL https://deno.land/x/install/install.sh | sh サンプルコードの実行 動作環境が用意できたので、サンプルコードの実行を行っていきます ドキュメントに書かれている通り、 deno run コマンドの引数にURLでTypeScriptファイルを指定し、実行してみましょう $ deno run https://deno.land/std/examples/welcome.ts すると下記のように自動的にファイルがダウンロードされ、実行するファイルに必要な権限などのチェック後、そのファイルに書かれたコードが実行されます このようにコマンド一つでファイルのダウンロードから実行まで行ってくれるのもDenoの特徴ですね ※今回の例で使っているコマンドにはバージョン指定が入っていないので、「自動で最新のバージョンのコマンド実行するよ。」というWarningが出ていますが基本問題はないのでスルーでOKです Codespacesのコンテナ設定でDeno環境を整えるように変更 続いて、毎回Codespacesの環境にDenoのインストールするところから始めるのは面倒なので、 devcontainer.json という仕組みを使ってDenoを利用する設定をコード化し、Git管理化に設定ファイルを置いておきましょう やることはVSCodeコマンドを実行して、選択肢をポチポチするだけでOKです まずCodespacesが起動している状態で、 Cmd + Shift + P でCommand Palleteを起動し、 add development と入力して下記の Codespaces: Add Development Container Configuration Files... のVS Codeコマンドを実行します 続いて、(最初は deno を入力しても、よく使われるDefinitionsの選択肢には出てこないので、) Show All Definitions... をクリックして再度 deno を検索してください 最後に、ベースになるDebianのOSバージョンを聞かれるので、デフォルトの最新安定版を選択することで… ※ bullseye はDebianのバージョン11のコードネームです( https://www.debian.org/releases/index.ja.html ) 完成! .devcontainer ディレクトリと、その配下に devcontainer.json / Dockerfile ができているはずなので、それらをコミットしておいてあげれば次回Codespaceを起動しなおしたときに自動的にDenoの環境構築がされたコンテナが適用されます ちなみに、テンプレで作成されるDockerfileも基本やってることは一緒 # [Choice] Debian OS version: bullseye, buster ARG VARIANT=bullseye FROM --platform=linux/amd64 mcr.microsoft.com/vscode/devcontainers/base:0-${VARIANT} ENV DENO_INSTALL=/deno RUN mkdir -p /deno \ && curl -fsSL https://deno.land/x/install/install.sh | sh \ && chown -R vscode /deno ENV PATH=${DENO_INSTALL}/bin:${PATH} \ DENO_DIR=${DENO_INSTALL}/.cache/deno 便利ポイントとしては同時にDeno用の公式VS Code拡張のインストールや初期設定をしてくれる https://marketplace.visualstudio.com/items?itemName=denoland.vscode-deno 他にも README や解説記事などでは初期設定の実行 Deno: Initialize Workspace Configuration を行うように書かれている事が多いですが、下記の通りすでに設定が入っているのでスルーでOKです { "name": "Deno", "runArgs": ["--init"], "build": { "dockerfile": "Dockerfile", // Update 'VARIANT' to pick an Debian OS version: bullseye, buster "args": { "VARIANT": "bullseye" } }, "settings": { // Enables the project as a Deno project "deno.enable": true, // Enables Deno linting for the project "deno.lint": true, // Sets Deno as the default formatter for the project "editor.defaultFormatter": "denoland.vscode-deno" }, // This will install the vscode-deno extension "extensions": [ "denoland.vscode-deno" ], "remoteUser": "vscode" } 公式ドキュメントを確認していく 以上でDeno環境の構築が完了したので、公式ドキュメントのTOPページを上からなぞって、書かれている公式ドキュメント等の役割をあわせて紹介していきます! 公式Docsのローディング画面めっちゃかわいい https://doc.deno.land/builtin/stable ビルトインの関数・メソッドたちを確認できるドキュメントを開くと、可愛すぎるローディングアニメーションを見ることができます。一旦可愛い恐竜くんたちを愛でましょう その後にちゃんと内容を見て、 Deno 名前空間と WebAssembly 名前空間が使われてるんだ〜とか思ったりしましたが、細かいところは使ってみながらの方が理解しやすそうなので、自分は一旦流し見で終了しました 公式マニュアルを眺めながら構築した環境で触っていく https://deno.land/manual 続いて公式マニュアル(Denoの開発方針から何から細かいことが書かれているドキュメント)を見始めたのですが、ここはボリュームがあるので後述の項で別途まとめていきます! 標準ライブラリ(標準モジュール)のドキュメントをざっくり確認 https://deno.land/std@0.116.0 http モジュールとか crypto モジュールとか uuid モジュールとかのNode.jsと同じような各種モジュールが std 配下から色々提供されています。これらは3rdパーティモジュールと同じようにURL経由で提供されていますが、Denoチームがメンテナンスし、動作を保証してくれているものです バージョンがものすごいスピードで上がっていて、この記事を書いてる5日の間にもにょきにょきバージョンが伸びていて、直近いろいろな改修が加えられているのをひしひしと感じています👀 3rdパーティモジュールをWebから読み込める https://deno.land/x こちらは deno.land/x 配下にGitHubでホスティングされているパブリックライブラリを登録できる仕組みです 実際には直接JSをダウンロードできるURLがあれば、ここに登録せずにそのURLからライブラリのコードを配布しても良いのですが、ライブラリ提供者がここに登録して提供することで、Denoユーザーが覚えやすいURL( deno.land/x で現在は固定)を払い出すことができるので、ぜひやってね〜というテンションで運用されているようです GitHub WebhookとGit Tagを使って設定を行うことで、特定のバージョンごとにDeno上にキャッシュを残してくれる動きをします ライブラリのドキュメントを自動で書き出してブラウザからも見れる https://doc.deno.land/ ライブラリのコード内にJSDoc記法で書かれたドキュメントを取得して、Web UIとして表示できるジェネレーター機能を持ったページです 利用しているライブラリのURLを直接指定してあげれば、自動で deno doc --json で出力されたJSONファイルを元にページをジェネレートして表示してくれます(ライブラリ作者はこのJSONファイルも一緒に提供しておくのが推奨されています) 公式マニュアルをなぞってDenoを触っていくぞ!! 一通りDenoの環境構築と、Denoが提供している公式ドキュメントの全体像がわかってきたので、 公式マニュアル を参考にGetting Startedからやっていこうと思います! 流石にここまでStep by Stepで書くのはあんまり意味ない気がするので、普段JavaScript/TypeScript + Node.jsを触っている人間が気になったポイントだけまとめていこうと思います セルフアップデートできるの便利よ https://deno.land/manual/getting_started/installation#updating Node.jsだととてもカオスになってたランタイムバージョン管理がランタイム自体でできるの地味に嬉しいですね nvm やら nodenv やら nodebrew やら n やら ndenv やらが不要なのが地味に嬉しいですね 特定バージョンを指定してのインストールもここでできるみたいです👀 CodespacesでもTCPリクエストのサンプルコード全然動かせる ここはDenoがどうこうという話ではなく、Codespacesの機能としてちょっと不安だったローカルのネットワークを触るサンプルコードがちゃんと動くのかを試して、問題なく動くのが確認できて感動した話です 上記の結果は このサンプルコード を実行して出てきたものなのですが、コード実行時にポップアップが出てきて、そのままブラウザの別タブに遷移できるようにしてくれるのでほとんど気にすることがなかったのが嬉しかったです またCodespacesの場合にのみ表示されるPORTSタブでは、自分でポートフォワーディングの設定を登録することも可能で、自動でドメインを発行して指定したポートをGitHub Authで守られた状態で自動的にバインドしてくれたりもします さらに、ここで特定のポートをクリックひとつで公開状態にできるので、バインドされたURLを他の人に共有してかんたんに動作確認をしてもらうことも可能なのが便利で嬉しいポイントです! 基本的にはコンフィグファイルいらないよ!!という圧を感じる { "compilerOptions": { "allowJs": true, "lib": ["deno.window"], "strict": true }, "lint": { "files": { "include": ["src/"], "exclude": ["src/testdata/"] }, "rules": { "tags": ["recommended"], "include": ["ban-untagged-todo"], "exclude": ["no-unused-vars"] } }, "fmt": { "files": { "include": ["src/"], "exclude": ["src/testdata/"] }, "options": { "useTabs": true, "lineWidth": 80, "indentWidth": 4, "singleQuote": true, "proseWrap": "preserve" } } } https://deno.land/manual/getting_started/configuration_file#example こんな感じで設定をファイルにまとめてできる(実際のスキーマは こっち )のですが、基本的にはデフォルトを使ってね。複数人プロジェクトでどうしても必要な場合に作ってね。というニュアンスで書かれていました また、このファイル自体は将来的に自動で探してきてロードするようになる予定なので deno.json / deno.jsonc で名前つけておくのがおすすめとのこと デフォルトではできないことリスト https://deno.land/manual/getting_started/permissions#permissions-list Denoではランタイムオプションを指定しないとできないことが意外とあるので、ちゃんとリストを知っておけると良さそうだなと思いました 一応、 -A, --allow-all : 全許可 なんてものもありはしますが、せっかくDeno使っているのであんまり使わないほうが良さそうかなぁ…と思います また、このランタイムオプションには 特定のコマンド / ファイル / URL / IPのみアクセスを許可することもできて、結構柔軟 なので、ライブラリや動作環境をセキュアに保つために設定しておくのが良さそうに感じました🙆 The Runtimeに定義されているAPI群をみて感じたWeb互換の意識の強さ 全体的に、普段ブラウザJSを書いていると馴染んでいるAPIが並んでいてびっくりしました Permission APIsはPWA機能を使うときによく叩くAPIだし、Program lifecycleはページロード時の処理とほぼ一緒だし、イベントの処理とかも普段使っている感じのやつだし… 特に Location API / Web Storage API に関してはそこまで考えて挙動作ってるの…?って思うぐらいに作られていてシンプルにすごいなと思いました… ただ、この辺りで1点わからないポイントがあり、1.16で追加された fetch のファイル読み込み機能はファイルの動的インポートに使うのが想定ユースケースなのかなんなのか…?という疑問点が残っているので、有識者の方いらっしゃったらコメント等いただけますと嬉しいです…🙏 Denoで新規プロジェクトを始めるときに気になりそうなポイント ここからは、次回のカレンダー(2021/12/12の1と2がいっぱいある日に公開予定)で実際にSlack Botを書いていく予定なので、Denoでコードを書く時のポイントの理解だけではなく、プロジェクトとしてすすめるのに必要そうな部分をまとめて紹介して行こうと思います lock fileの存在 Denoはnpm / package.jsonが無い代わりにURLベースの依存ライブラリ管理方法を取っているため、パッケージ配布元のURLが変わったり、 該当URLのライブラリが乗っ取られて悪意のあるコードが埋め込まれて内容に変わってしまった場合に気づく術が無い ため、Denoではデフォルトで整合性チェック用のHashを持っています 1人で1端末で開発しているだけならDenoがデフォルトで管理されている、 /deno/.cache/deno/deps/https/deno.land あたりの [hash].meta.json ファイルたちだけで問題はありません しかし、複数人での開発の場合はそれらのバージョン情報を共有する必要があるので、プロジェクトコードに lock.json を作り、Git管理下に置く方法が紹介されていました --lock=[path/to/lock.json] --lock-write をコマンド実行時に指定することでいい感じに作ってくれるので、プロジェクトではエントリポイント指定してGit管理下に置いておいた方が良さそうですね👍 Import mapsの存在 https://deno.land/manual/linking_to_external_code/import_maps#import-maps 同様に、package.jsonがないことのデメリットとして、インポートする依存ライブラリの指定が各ファイルに散らばってしまうこと(個人的にどう解決してるのか一番気になってた問題)の解決方法として、WICGで仕様が定義されている import-maps を使って一箇所で依存ライブラリのURL・バージョン指定をできるようにしています(公式ではこの方法が推奨されているっぽいですが、他にも deps.ts というファイルを作り、そこでライブラリインポートを管理して、他のファイルからそれをまるっと読み込むパターンも有るようでした) なので、プロジェクトの初期にこのファイルを作成しておいて、ライブラリの追加時にはここに追記して実際のアプリケーション側からはバージョン情報などを省いた省略記法で記述していくのが良さそうですね この辺りを見て個人的には、 deno.json => Linter、Bundler、Formatter等の各種設定ファイルの集合体 import_map.json => package.jsonの依存ライブラリ指定部分の代替 lock.json => package-lock.json / yarn.lockの代替 として3ファイルを作るのが自然なのかなという理解をしたので、次回のSlack Bot作成ではこの形で各設定を管理し、実装を行っていこうと思います! Deno基礎知識 + 環境構築編のまとめ さて、ここまでDenoの基礎知識から実際に触れる環境を用意してマニュアルをなぞってざっくり全体感を理解してきました 2年半ほど前の発表時に話題になっているのを見つけ、そこからの進化をちょろっと横目で見ていた程度だったので、どういう機能が実装されているのかなどを知らなかったのですが、これである程度はDeno環境でコードを書けるようになりました なので、次回の記事では社内のSlackで動くBotを実際に作って、Denoの触り心地をより理解していこうと思います! 先日Slack社が Slack Appの次世代開発プラットフォームをDenoを使って提供していくことを発表 しました。 自分はこれにクローズドベータの申込みをしたものの、まだ参加できていないので、 もし次回の記事執筆までにクローズドベータに参加できれば、そちらの新しいプラットフォームを使って slack コマンドを使ったApps開発をまるっと構築をする。参加できなければ(非公式ではあるものの) deno-slack-sdk で管理されているDenoライブラリが公開されているのでそちらを使ってSlack Botを構築してみる。という形で記事を書こうと思います! では、長々とお付き合いいただきありがとうございました!次回もお楽しみに! 明日のアドベントカレンダーは基板チームの右京さんの記事です!こちらもお楽しみに! 参考 Deno公式サイト https://deno.land/ https://doc.deno.land/ https://deno.land/manual JSConfでの @kt3k さんの発表 https://jsconf.jp/2021/talk/the-past-and-future-of-deno https://kt3k.github.io/talk_jsconfjp2021/#1
こんにちは。BASE BANK 株式会社 Dev Division にて、 Engineering Manager をしている東口( @hgsgtk )です。 弊チームではプロダクト開発のリズムの中で振り返りを継続的に行っていますが、YOT という振り返りワークを作成、使用しています。BASE 社内の他チームでも「YOT っていう振り返り方法があるらしい!」と興味を持ってもらい活用されていたり、社外でもスクラムやアジャイル関連のカンファレンスでの登壇でちらっと紹介した際に「それ良さそう!」と一定の反響がありました。しかし、その一次情報はインターネット上のどこにもない状態でしたので、作成の背景も含めて参考にできる YOT の一次情報をここに記します。 TL;DR 振り返りの場のファシリテーターには、発言量が少なくてうまく場が盛り上がらない、というあるあるな悩みがありますよね 起こったことや思ったこと(お気持ち)を気軽に共有しやすくすることを目的に、YWT をフォークした振り返りフォーマット「YOT(やったこと、思ったこと、次にやること)」を作成、活用しています 「今週はこういうことしたよ!」や「なんかこういうもやもやがあるんだよね〜」というお気持ちを話題に上げるハードルが下がることで、発言量が増える効果がありました 「話題が上げづらい」という課題 以前、 @takoratta さんの ツイート から「KPT (Keep/Problem/Try) で K があまり出ずに、P ばかりになる」といった課題から振り返り全般におけるみなさんの工夫がシェアされていました。 togetter.com BASE BANK チームにおいても KPT を利用しており類似した課題がありました。振り返りを日々の仕事に取り入れられている方では共感いただけるとおもいますが、「Keep って何を書いていいのだろうか」といった参加者の迷いで気軽に話題が上がらないといった課題がありました。 これは 2021 年 8 月 13 日に実際に行われた振り返りで作成された miro の付箋です。 2021 年 8 月 13 日に実際に行われた振り返り その場ではこのような話の流れがありました。 BASE BANK チームの人数が増え、一堂に会する人数が 12 名(2021 年 12 月 01 日時点) 弊チームは 2 つのスクラムチームに分かれていて、それぞれが 2 週間スプリントを回す スクラムチームとしては別れつつも 1 つの BASE BANK チームとしてお互いの境遇を共有するために全体でも振り返りをおこなっていた KPT を利用していたが 「あれ?こういう事書いていいんだっけ?」っていうのが分かりづらい というのが声があがった ex.「チーム外とのコミュニケーションで起こったこと」は自分しか経験してないことだけど書いていいのかしら? レトロスペクティブにはさまざまな経験をしたさまざまな職種の方がクロスする場所なので、スプリント内での経験をたくさん話してほしい!どうすればよいだろうか... >< 以前は人数も少なかったのである程度「愚痴っぽい感じ」とか「起こった出来事」とか「自分だけが出会った境遇でチーム全体ではそんなに関係ないしな」っていうような一見遠い課題も、KPT のワークの中でたくさん話題が上がっていました。 しかし、そこにはチームのなかで 暗黙の前提が有った ことに気が付きました。過去の傾向では「自分の身の回りに起こった出来事」や「思ったこと」をカジュアルに Keep や Problem で喋っておりました。長く在籍しているメンバーであればこの傾向を知っており引き継いで振り返りの場に臨みますが、新しく JOIN されたメンバーであれば 暗黙知となってるがゆえに戸惑いが発生している 状況だと分析しました。 よって、 「愚痴っぽい感じ」や「起こった出来事」を話していい場であることを明示的にする ことを課題解決の方針としました。 出来事ベースでお気持ちを話しやすい振り返りワーク「YOT」 結論から言うと当チームでは YWT をフォークした YOT という振り返りワークを実施しています。当ブログでは、まず YWT という振り返りワークを紹介し、何を課題と感じてフォークしたのか説明します。こぼれ話として、その過程ではいくつかコミュニティ内でのシェアされている振り返りワークも検討したのでご紹介いたします。 YWT YWT は日本能率協会コンサルティング(JMAC)が開発した日本出自の振り返りワークです。Y が「やったこと」、W が「わかったこと」、T が「次にやること」の略です。 当ワークは様々な振り返りの現場で活用されており、後ほど紹介する FDL(Fun / Delivery / Learn)や WLT(Win/Learn/Try)など他のワークにも大きな影響を与えています。 https://www.jmac.co.jp/glossary/2021/09/ywt.html から引用 弊チーム状況に対してマッチする良い点は Y(やったこと)が具体的で出しやすい点でした。K(Keep したい)ことは抽象度が高くて特に English native ではない日本人チームではそのニュアンスが掴みづらい側面がありますが、「やったこと」は何を話していいのかわかりやすいというメリットがあります。よって、出来事ベースで話しやすい場を作るためのワークには適しています。 一方でチーム内でこのワークを検討した際に、 「こんなもやもやがあった」とか「こういう点がわかってよかった」といった、お気持ちを話す余地が少ない点 が懸念となりました。 YOT YOT は YWT をフォークして前述したお気持ちを話す余地が生まれるように調整したものです。 Y: やったこと O: おもったこと T: 次にやること YWT の W(わかったこと)は日本能率協会コンサルティングの説明では次のような狙いがあります。 W(わかったこと)は、一人ひとりの気づきや学びであり、個性の表れである。同じことに遭遇しても、人によって気づきはさまざまである。それぞれの人生経験、価値観や感性の違いから、現実に認識や体感の味わい方もひとそれぞれである。 一人ひとりの気づきや自分が感じたことを自覚して表に出すことで、内省を深め自らを変えていく思いを強める ことができる。 W(わかったこと)を振り返ることで自分が得た学び、気づき、教訓といったポジティブな面を見つけることができる一方で、ネガティブな点、ニュートラルなお気持ちについて吐き出しにくい側面があります。 ちょっとしたもやもやだったり、大変だったなぁ〜って思ったことなど、ポジティブなこともネガティブなことも両方含めて共有できる場になるのではないかという議論の結果、O(おもったこと)としています。 進め方 弊チームで行っているワークの流れは以下です。 n 分間 Y(やったこと)、O(思ったこと)、T(次にやること)を書き出してもらう Y -> O -> T と順番に区切ってやる進行方向もありますが、オンラインでは Miro などのコラボレーションツール上で、他の人が書いてる内容を現在進行系で見れるため YOT すべて書く時間にしています 残りの時間で書いたことの共有、深堀り、対話 進行方向として人数次第で複数のパターンが観測されています 人数が 6、7 人以内くらいであれば一人ずつターンを分けて話してもらう ひとりひとりガッツリ時間を使えなそうであればグルーピング、ファシリテーターがランダムにピックアップ 実際に Miro で行った YOT ボード(中身の文章はすべてマスクしています) YOTの評価 『 振り返りを積み上げて自分たちのプラクティスとして昇華•体得していくための仕組みと考え方 』で紹介した「プラクティスの種投票ゲーム」も行い YOT 自体の今後の活用について議論しました。 「プラクティスの種ゲーム」で YOT 自体を振り返った結果 「 今週良かったことも悪かったこともなんも思いつかないときにも書けるのがいい 」という声が YOT 自体の振り返りであがったので、「話題が上げづらい」という課題は当ワークによって解消できました。 また、当ワークは BASE の他チームでも活用されていて、そこでも同様の評価が得られました。 同僚の Y さんの声 YOT チームのふりかえりで使わせていただきました!! 「思ったこと」へお気持ちを書きやすく、良い雰囲気でふりかえりができました! 同僚の Gさん の声 何回か使ってみたけどふわっとした思いが書きやすかったという所感です 一方で、改善点を見つけてフィードバックループを回していくことをゴールにするともうひと工夫必要、という課題も確認されました。振り返りの中では、「 書きやすくはなった一方で、共有の意味合いが大きくなったため、課題からのアクションアイテムの発掘が難しそう 」という声が上がりました。 たとえば KPT であれば Problem を書くので、わかりやすく問題が振り返りの調理場に並ぶため、その場での深堀りの入り口が見えやすいです。一方で、YOT の場合は出来事やお気持ちなどの一次情報が列挙される形になります。そのため、次のアクションを見つけようとすると、それらを深ぼっていく「深堀り力」が対話の中でより求められます。 BASE BANK チームではこの性質に対して振り返りの主目的を週毎に切り替えることで YOT で確認された共有しやすさを普段のプロダクト開発のリズムに取り入れています。 2 週間 Sprint の最終日:「フィードバックループを回す」ことに主眼 それぞれのスクラムチーム(各 5、6 人)で行う KPT, YOT その他場に応じて 2 週間 Sprint の真ん中:「BASE BANK チーム全体のコミュニケーションとしてそれぞれの状況を共有する」ことに主眼 BASE BANK チーム全員(12 人程度)で行う 共有しやすさ、話しやすさを重視したワーク -> YOT を活用する その他検討した振り返りワーク 以上が弊チームで行っている YOT の説明でした。おまけ情報として「話題が上げづらい」という課題のソリューションとして検討したその他の振り返りワークもご紹介します。 Activity: Timeline Timeline は『 アジャイルレトロスペクティブズ 強いチームを育てる「ふりかえり」の手引き 』という書籍の中で紹介されている振り返りワークの一つです。当書籍から説明を引用します。 長期のイテレーション、リリース、プロジェクトのレトロスペクティブで、データを収集するために使用する。 目的作業のインクリメントで何が起こったのかを思い出す。多くの観点を基にして、作業の全体像を作り上げる。誰がいつ何を行ったかという前提を調査する。パターンやエネルギーレベルがいつ変わったのかを調べる。「事実のみ」あるいは「事実と感情」をデータに使う。 当ワークは次のような流れで進んでいきます。 グループメンバーが、イテレーション、リリース、プロジェクトで起きた、記憶に残ったり、個人的に意味があったり、そうでなくても重要だったりするイベントをカードに書く それらを(大まかに)時間順に並べる イテレーション、リリース、プロジェクト中に起きたイベントをチームで議論 このワークで「個人的に意味があったり、そうでなくても重要だったりするイベント」もスコープに含めて、チームで議論するワークなので出来事ベースで振り返るワークとしてはマッチする点がありました。 使用用途としては長期の時間軸なので、隔週ペースくらいの振り返りで使う場合は「1、2 週間のなかでプロジェクトで起きた、記憶に残ったり、個人的に意味があったり、そうでなくても重要だったりするイベント」をタイムライン上で埋めてみる、というワークの実施方法はありそうだとおもいました。 日本国内の事例だと freee さんが Timeline を振り返りワークとして活用した ことを紹介しています。 ただ、中長期のプロジェクトの振り返りのほうが本来のワークの目的にも近いのだろう、ということで今回の課題に対するソリューションとしては採用していません。 Fun/Done(Deliver)/Learn Fun/Done(Deliver)/Learn はこれまた KPT や YWT の改善として提示された日本出自のワークです。 qiita.com BASE 内にも FDL を振り返りワークとして実践してみたチームがあります。「楽しさ」にフォーカスした手法で、前向きに議論しやすいため、多くのチームで活用されていることを観測しています。 弊チームではありがたいことに、比較的前向きな対話の雰囲気のなかで振り返りがやれている(主観ですが)ことが多いのでまだ活用していませんが、振り返りワークの引き出しを広げると意味でも使ってみると面白いかもしれません。 Win/Learn/Try Win/Learn/Try は mercari の wako さんが紹介したワークです。 engineering.mercari.com 当記事内の説明にある通り、KPT や Fun/Done/Learn からエッセンスを抽出したものなようです。 そこで今まで経験したKPTやFun/Done/Learnからエッセンスを抽出してWin/Learn/Tryを考えつきました 筆者個人的な理解としては、ポイント語彙をポジティブにすることでよりポジティブな切り口が重視する狙いを込めたワークフォーマットだと認識しています。「どういうワークがチームにとって良いだろうか」と試行錯誤した身としては課題としてあげられている点に共感しました。 Winはポジティブな気持ちになれ、かつ分かりやすい切り口ではあるものの、 個別の具体例が列挙されるため、Tryへ昇華するタイミングで深堀り等の工夫が必要 LearnはWinとは逆に、 起きた事象をどう学びに抽象化するかが必要なのでアイディアを出すタイミングでのハードルが少し高い Win/Learn/Try固有の問題ではないが、同じフレームワークを使っていると課題発見の切り口が同じになり、飽き/賞味期限がくるのでフレームワークのアップデートは常に必要 特に 1 と 2 の課題は前述した YOT の課題と類似していますね。振り返りワークを評価/調整/作成する際の 1 つの設計考慮点として次の点が重要です。 参加者に、話題に上げるまでに必要な抽象化の労力を、どこまで求めるか たとえば Win/Learn/Try では、Win は個別な具体例を書けば良いのでそこまで抽象化することを考えなくて良くて、逆に Learn は「学びってなんだろう?」と参加者各々に抽象化する労力を一定求めるゆえアイデア出しのハードルが高くなります。 「話題が上げづらい」という課題に対するソリューションを考える際に、この考慮点にどうバランスを取るかは難しいポイントだったので、勝手ながらとても涙ぐましい共感をしております。 おわりに 今回は弊チームで作成、活用している YOT をご紹介しました。YWT を超絶リスペクトした上で、より自分たちに合わせた形で調整したのが始まりだったのですが、社内外関わらず他チームに興味を持って実践いただいてます。当ブログエントリが公開されることによってインターネットで検索した際に一次情報にたどり着けようになるので、今回はじめて目に触れた方々も安心してお使いいただければと思います。 冒頭に紹介したとおり「発言量が少なくてうまく場が盛り上がらない」というのは、あるあるな悩みですよね。そんなときに YOT は場を活性化させるのにうまくハマるかもしれません。悩める振り返りリーダーの課題に寄り添うソリューションとなれば幸いです。
BASEにおけるIT全般統制とCSEグループが取り組んだ内容 はじめに この記事はBASE Advent Calendar 2021の3日目の記事です。 devblog.thebase.in BASE Corporate Engineering CSEグループ マネージャーの小林 ( @sharakova ) です。 タイトルに記載のとおり、BASEにおけるIT全般統制とCSEグループが取り組んだ内容を説明させていただきます。 BASE株式会社は、2019年10月25日に東証マザーズに上場しましたが、上場企業は、金融商品取引法(いわゆるJ-SOX法)の遵守が求められます。そのため、2021年度末までにIT統制として不十分な項目の是正・必要書類の作成などが必要となってきます。 私は、バックエンドのアプリケーションエンジニアとしてBASEに入社をして、2020年にコーポレートエンジニアのチームに異動しました。コーポレートエンジニアが取り組むべき内容なども分からないまま異動し、J-SOXの対応としてIT統制が必要だということ、さらに期限があり大変なプロジェクトだということは、異動してから知ることになります。 devblog.thebase.in 昨年のアドベントカレンダー記事で、CSEグループがIT統制の整備に取り組んでいる事を書かせていただきましたが、この1年で取り組んできたIT統制、主にIT全般統制(ITGC)の取り組み・整備についての概要を説明させていただきます。 社内説明会 CSE・情シス内での輪読会(2020年) CSEメンバーには、IT統制や内部統制について深く理解しているメンバーがいなかったため、プロジェクトを開始する前にメンバー全員でIT統制についての輪読会を実施しました。 金融商品取引法(いわゆるJ-SOX法)は2006年に成立し、2008年に適用が開始されました。上場企業においてはJ-SOX法に遵守するために、IT統制が必要になります。 ただ、2008年以降のIT業界の変化は激しく、2020年代においてアジャイル開発、GitHub、AWSの利用も主流となり、これらの技術やSaaSなどを利用しながら、どのような方法ならJ-SOXに遵守できるのか、メンバー同士で議論することになります。 エンジニア向け説明会の実施(2021年3月) 2020年末までの課題として、売上に関わるような開発が突発的にされており、IT統制として知るべき変更内容が共有されていない状況が続いていました。 是正するため、2021年最初に取り組んだのが、バックエンドエンジニア向けに社内説明会を実施することにしました。 CSEグループが取り組んでいる内容を共有しつつ、IT統制の必要性、考え方を説明。それに伴い開発ルールも変更されることを伝えました。ほぼ全てのバックエンドエンジニアやPMの日々の業務に関わるため、協力・理解してもらう必要があります。 説明会を実施後にも、多くの機能が開発・リリースされておりますが、売上に関わるような開発は、最初にCSEグループに相談してもらえるようになり、社内にIT統制の考え方が浸透するきっかけになったと思います。 IT全般統制 IT統制の中で、基本となるのがIT全般統制(ITGC)になります。 IT全般統制で売上を管理しているシステムを特に重要視する必要があり、特に以下の項目においては、操作ミスや不正などを防止(予防的統制)・発見(発見的統制)の2つの観点から仕組み・ルールを整備してきました。 BASE管理画面 GitHub データベース サーバーログイン・設定変更 会計ソフト 決済代行会社アカウント 以降、詳しく説明します。 BASE管理画面 BASE管理画面は、BASEの社員などが利用する社内用の管理画面の事で、購入を強制的にキャンセル、売上データの変更、割引率の設定、クーポンの発行などサービスを運営する上で必要な機能ではあるが、売上に関わるような重要な操作も可能で、さまざまな仕組みやルールを整備しました。 ロール / Access Control List (ACL) BASE管理画面では、利用者全員に管理者、経理、閲覧者、外部委託者などのいずれかのロールを設定する事で、操作可能な機能を制限しています。その上で、特に気をつける必要があるのが、管理者、経理ロールとなります。 管理者ロールは、アカウント作成・他のロール設定変更を含めて全ての機能が利用可能です。 経理ロールは、売上データを操作などの売上に直接関わる操作を可能としています。 権限の付与ルールの整備などを行いながら、アカウント台帳の作成とロール権限の再定義などをしました。これにより、想定外の人への誤ったロールを設定するなどのミスをなくし、売上に関わる操作でも、申請者と承認者を適切に分離する事で、誤った操作を防ぐ設計に変更しています。 操作ログの通知 上記のロールの設定をすることで予防的統制を実施し、発見的統制として重要な操作のSlack通知機能を実装し活用しています。特にロールに関する変更操作など、影響範囲が大きく、気づいたら「売上に関わる操作が誰でもできる状態」などならないように、監視する体制づくりも実施しました。 GitHub BASEのOrganizationでは、リポジトリが350以上存在していますが、すべてのリポジトリをIT全般統制の対象とする必要がなく、まず重要なリポジトリを精査し統制対象と定義しました。対象リポジトリにおいてコードレビューを必須化、変更理由の記載などより強いルールに変更することにしました。 対象リポジトリ BASEサービスの基本となるリポジトリ 売上を編集・操作できるリポジトリ インフラの設定変更が可能なリポジトリ 共通ライブラリなどのリポジトリ いままでのBASEでは、障害時などの緊急のコード編集が必要になることが考慮されているなどの理由から、コードレビューを厳格化できておりませんでした。この数年でエンジニアの採用も進み、またコロナ禍において、テレワークが定着化し、緊急時でも複数人で対応できる体制になったおかげもあり、Pull Request のレビューを厳格することができ、対象リポジトリにおいてレビューされてないコードがデプロイされる事はなくなりました。 データベース 近年ではデータベースと一括りにできない部分ではありますが、BASEでは、Amazon Auroraをメインのデータベースとして利用しています。 このデータベースのデータの健全性を守る事が最重要の項目であり、データベースの値に間違いが無いことを証明するため、上記のBASE管理画面、GitHubなどあらゆる手段で守っていると言っていいでしょう。 しかし、現実的にはSQLを流して、データを書き換えるなどの業務も発生します。 SQLでの更新検知 SQLを手動で実行してデータ更新については、他のエンジニアのレビューしてから実行するルールが確立されていました。さらにレビューした内容でのSQLを実行したのか、不正なSQLが実行されていないかを検知することをすることにしました。 まずは、”BASEシステムが利用するDBユーザー”と、”SQLでの書き換えできる人のDBユーザー"を完全に分離し担保する必要があり、その上で、手動でデータベースを更新をした場合に、Slack通知する事を構築しています。 バックアップデータからのリストア試験 データベースを復旧試験・手順書の見直しました。実際にはデータベースだけではなく、サービス提供全体の復旧試験などを行った方がよいです。しかし、実際の復旧構築の試験となると、他のSaaSとのつなぎ込み部分など多岐にわたるため、今年度は試験を範囲を限定し、毎年復旧試験の内容を変えて実行するなどを考えていきたいと思っています。 サーバーログイン・設定変更 アカウント管理 誰がサーバーにログインできる状態なのか、台帳管理などを用いて証明する必要がでてきます。BASEではChefを利用したサーバー構築、設定変更を行っているため、Chefを管理しているGitHubリポジトリへのアカウント追加のPull Requestが申請・承認となり、コード化された設定ファイルが証跡(台帳)となるように、監査法人へ説明しています。 臨時ジョブ(バッチ)の実行 通常のジョブ実行にはcrontabを利用したジョブ実行を行なっていますが、臨時的にジョブなどを実行したりする場合などがあります。そのため、社内でバッチサーバーと呼ばれるサーバーに、SSHログインし、手動でジョブを実行できる環境などが整備されております。 しかし、ログインできるアカウントを管理しているとはいえ、誰がどのような作業をしたのか把握できている状態とはいえず、どのサーバーでどんな作業をするのか、申請・承認を経て実行していただくように仕組み・ルールを変更を行いました。 ソースファイルの改ざん検知 GitHubでコードレビューを厳格化しても、サーバーにログインできると、ソースファイルをサーバー上で書き換えが可能な状態となります。SSHログインできるアカウントを管理しているとはいえ、リスクを伴います。 Dockerなどでサーバーを構築できれば、そもそもSSHログインできないなど、ソースファイルをそもそも書き換えできない状態にするのが望ましいとは思いますが、すでに確立しているデプロイシステムなどもあり、既存のEC2サーバーにファイル改ざん検知する仕組みを導入し、Slack通知で検知する仕組みを構築することにしました。 会計システム BASEでは、会社設立当初から会計システムとして外部のパッケージソフトウェアを導入し利用しています。 BASEシステムと同様に、売上を管理する重要なシステムでありながら、社内のエンジニアは運用方法を把握できておらず、IT統制の内容確認には苦労しました。 BASE社内で開発している製品ではないものの、ベンダーとの契約内容の確認・アカウント発行ルール・ロール設定・バックアップ・復旧手順の作成など、整備する項目はあります。 バックアップ/リスト手順 利用している製品のバージョンも関係しているのですが、日次の自動バックアップなどを設定できなく、経理部門が手動でバックアップしている状態でした。 ただ、ヒヤリングしてみると同じサーバー内にバックアップをとっている事が発覚し、障害が起きた場合に、結局復旧できそうもない状況であることがわかり、サーバーからGoogle Driveをマウントする形に設定変更をし、バックアップ先の変更をしました。 システムを外部ベンダーに依頼だけで、BASEのエンジニアが積極的に運用に参加していなかったなどの課題も発見しました。 決済代行会社アカウント BASEでは、クレジットカード、コンビニ払い、Amazon Pay、PayPalなどの多様な決済方法を提供しており、各決済代行会社を通じて決済させていただいています。購入時などは各社のAPIなどを利用することで決済をし、BASEのデータベースと決済代行会社の決済のデータが常に同期されている事を担保しています。 しかし、各決済代行会社の管理画面がそれぞれ存在しており、管理画面にログインを行うと、決済にまつわるさまざまな操作が直接可能になります。 権限によっては、決済を強制的にキャンセルなども行えるため、決済代行会社のアカウントは厳重に管理する必要があり、アカウント発行のルールの作成と、申請・承認・発行フローの整備を行いました。 終わりに すごく厳格なルールを整備してしまうと、私がBASE社に入社して最初に感じたスピード感が失われてしまう可能性もあります。スピード感を保ちながら、それでいて安全にするために、何ができるのかという観点でメンバーとも議論を重ねながら、さまざまな事に取り組んでまいりました。 1年を通じて、ルールを作成するたびにエンジニアへの説明会を繰り替えしながら、時にはエンジニアからの反発などもありましたが、一度作ったルールをそのままにするのではなく、継続的にルールを見直していく事が成長をするBASEらしさだと思うので、変化できるように体制強化を目指していきたいです。 またYouTubeの方で、”Web系ベンチャー上場後の「現場のDX」の難しさ”という内容でインタビュー動画を上げさせていただいているので、合わせて見ていただければと思います。 【前編】Web系ベンチャー上場後の「現場のDX」の難しさ - YouTube 【後編】Web系ベンチャー上場後の「現場のDX」の難しさ - YouTube 明日は、フロントエンドエンジニアのがっちゃんの「Denoの今を知る」の記事を公開する予定です。お楽しみに。
この記事はBASE Advent Calendar 2021の2日目の記事です。 devblog.thebase.in はじめに こんにちは!BASEでエンジニアをやっている @kimukei です。 現在BASEの顧客管理に関係する機能の開発に携わっています。 今回は、10/14 にリリースされた「BASE」の顧客管理がどのように開発されていったかについて、その一部についてお話ししたいと思います。 顧客管理とはどのような機能か 今回リリースした顧客管理は、 BASE U でも紹介されていますが、もともとあった拡張機能である「顧客管理 App」のリニューアルとなり、簡易的に提供されていた機能をパワーアップし、標準機能としてすべてのショップに提供したものです。 今回のリニューアルで顧客の生成元を見直し、購入回数や購入期間などの条件を指定して顧客グループを作成できるようになりました。 購入回数と購入期間から顧客をセグメントに分けることができ、そのセグメントを顧客グループとして扱えるイメージです。 顧客をセグメントに分けるイメージ この機能によりショップオーナーさんによる顧客分析を可能にし、限られた範囲にリーチさせたい施策需要(例えば、離反顧客にカムバッククーポンを配布するなど)を満たす土台ができました。 現在は「メールマガジン App」から顧客グループを利用でき、顧客グループに向けてメールマガジンの配信を行うことができます。 今後も顧客管理が連携できる他のAppや機能などは増えていくと思います。 以下では、主に技術的な構成についてお話していきます。 システム構成概要 はじめに顧客管理のシステム構成の概略図です。 ご覧の通り、非同期なアーキテクチャをしています。 また、一部簡略化、省略している部分があります。雰囲気を掴んでもらえればと思います。 このシステムを採用した理由やポイントがいくつかあるため以下で説明していきます。 システム構成の概略図 なぜ非同期にしたのか いくつか理由がありそれぞれ説明します。 顧客システムのエラーを理由に注文が失敗してはいけないこと 「BASE」のようなECプラットフォームにおいて最も重要なトランザクションは言わずもがな「注文」です。 お金が発生するトランザクションはビジネス上、法律上、UX上、あらゆる点において最も整合性が保証されていなければなりません。 「顧客」は購入金額などを持ち合わせますが、仮に同期的な処理を取った場合、顧客システムで発生したシステム不整合を理由にそれらの重要なトランザクションをロールバックすることが発生するためビジネス上、大きな打撃になります。 そのため、注文のトランザクションとは別に顧客システムのトランザクションを実行する必要があり、「顧客」に関係するトランザクションを逃そうと思いました。 処理する場所は一箇所にする 「顧客」の作成や変更が伴う操作(ここではこれを「顧客イベント」と呼称する)は、BASEのシステムのあらゆる箇所に点在します。 例えば、 注文 注文編集 注文キャンセル が顧客イベントの一例にあたり、注文はweb上からもできますし、Android/iOSで提供されている購入者向けの ショッピングサービス「Pay ID」 上からもできます。注文の編集やキャンセルはショップオーナーが利用する BASE Creator からも可能です。 注文以外でも「顧客」は作成され、 コミュニティへの入会 抽選商品の当選/落選 でも作成されたりします。 そのように数多く存在する顧客イベントの処理を一元化する上でも非同期アーキテクチャにすることは有用だと考えました。 マイクロサービスアーキテクチャへの移行 BASEのシステムは現在モジュラモノリスに近いものとなっております。 もし、部分的にマイクロサービス化をしていくなら、顧客システムはマイクロサービス化されるべきものの筆頭であると考えています。 顧客システムはそれ単体で十分成り立つ単位であり、扱うドメインはある程度閉ざされていて、継続的な機能開発が見込まれる領域でもあります。 組織がグロースするとおそらく顧客管理チームが出来上がるのではないか、とも予想されます。仮にそうなったら、逆コンウェイの法則から顧客システムは自然とマイクロサービス化へ向かうことでしょう。 これらの懸念があったことも大きな理由です。 また、余談ではありますが、完全なるマイクロサービス化はしていないためsagaパターンやAPIゲートウェイパターンは実践していないものの、 マイクロサービスパターン[実践的システムデザインのためのコード解説] で紹介される顧客サービスの仕組みは、私たちがつくった顧客システムとかなり近しいものでした。 ビジネス上のOKが出た 最終的にこれは大きいですねw どうやっても同期的に更新されないといけないものはありますが、顧客情報についてはその限りではなかったためここまで考えて決断することができました。 また、非同期と言っても5秒もあればほとんどの顧客情報の反映は終わっており、そういった遅れるとしても何秒くらいだろうという検証を事前に実施し、温度感をPdMと共有し議論できたのが良かったです。 非同期な仕組みにすることで考えることは増え、設計の複雑さや難易度が上がることは事実だと思います。 ただ、上記の理由より、顧客システムをこのタイミングで非同期な仕組みにしてアーキテクチャを分離しておくのは十分有用だと考えました。 それでは次にこのシステムを構成する上でいくつかポイントがあるのでそれを紹介します。 SNS -> SQS の冗長化 顧客イベントについては、メッセージキューイングの仕組みに1段SNSを挟んで冗長化させています。 これは、顧客システムの未来が確定していない中で、システムの拡張性を持たせたかったためです。 SNSがメッセージのpublisherになることで、subscriberの変更は容易になるし、複数のsubscriberに配信することもできます。 詳しくはクラスメソッドさんの、 【AWS】SQSキューの前には難しいこと考えずにSNSトピックを挟むと良いよ、という話 がとても参考になります。 顧客イベント駆動の処理をする上で いわゆるイベントソーシングというやつです。 「顧客」に関わるイベントをいくつか定義して、SQSのメッセージに顧客システムが処理できる情報を詰める 顧客システムの worker がメッセージを受け取る 顧客イベントを処理する その顧客イベントを処理した履歴を保存する 2 の worker とは、一般的にはwebサービスのバックグラウンドで特定の処理を実行するプロセスのことですが、ここでは、SQSのキューを監視し、キューに登録されたメッセージに対して何かしらの処理を行う常駐プロセスのことを指します。 Supervisor を用いてバッチ処理をデーモン化することで実現しています。 4 が最も重要なことで、これがあるからSQSを利用できているし、過去データを元にした「顧客」の作成もオンラインで実施できました。 まず、今回採用したSQSのキュータイプはFIFOキューではなく標準キューです。顧客イベントの量的に複数の worker プロセスで並列してメッセージを取得して処理したかったためです。 SQSの標準キューは少なくとも1つ以上のメッセージを保証しています( https://docs.aws.amazon.com/ja_jp/AWSSimpleQueueService/latest/SQSDeveloperGuide/standard-queues.html )。 つまり、同じメッセージを繰り返し処理することがあり得て、処理した履歴を保存していないとそれが処理済みのメッセージかどうかが不明になり、2重計上の恐れが出てきます。 履歴を持つことで冪等性を持った処理が可能になり、2重計上を防げるということは、過去の注文データなどから顧客データの生成も安全に行えるようになります。 2重計上が防がれ安全にデータ移行が行われるイメージ図 これにより、今までのすべてのデータから「顧客」を生成する(チームではこれを通称「データ移行」と呼んでいます)大規模な操作をサービスメンテを入れずにオンラインで実施することができました。 仮にサービスメンテを入れるとなると、「BASE」ももう誕生して10年目を向かえているサービスで膨大なデータ量があり、それらをすべて処理する必要があるため1日はサービスを止めることになっていたでしょう・・ ちなみにこのデータ移行は確認を含め完了まで2営業日要しました。 データ不整合をリカバリする仕組みは必須 非同期な分、データ不整合はどうやっても起こり得ます。 SNSのpublishが失敗することがあるかもしれませんし、DB負荷など何らかの理由で顧客イベントをpublishする前に処理が落ちてしまうかもしれません。 それらのデータ不整合を検知して修正する仕組みは非同期なアーキテクチャをとる以上必要不可欠です。 また、データ不整合を検知する仕組みは、データ移行の際にも役立ちます。 データ移行を何をもって完了と定義するか、最終的な受け入れテストツールとしてデータ不整合を検知する仕組みが利用できるからです。 これは本当に大事で、データ移行ってだいたいバッチ処理とかで実行することが多いと思いますが、すべてのショップにデータ移行が正常に完了したかを別の仕組みを用いて確認するまでが1セットのデータ移行です。帰るまでが遠足みたいなことです。最初このことに気づかず慌ててデータ不整合を検知する仕組みを作りました。是非ここまで読んでくれた方が我が身となったときには慌てずにデータ移行を完遂してくれることを願っています 🙏 なので、データ生成と同じタイミングでデータ不整合を検知する仕組みを一緒に用意しておくと良いです。 おわりに BASEは現在、未来に向けた土台を作りながらも日々サービスを成長させていっている最中です。 最近ではNew Relicを用いたサービス監視も盛んになってきました! 興味のある方、やっていくぞ〜 💪 な方!お待ちしております!是非一緒に未来をつくっていきましょう! 下記のリンクや ぼく に連絡ください! https://open.talentio.com/r/1/c/binc/homes/4380 明日は弊社CSE(Corporate Solutions Engineering)チームによる、IT全般統制について取り組んだ話です。おたのしみに! それでは〜〜 👋
こんにちはBASEの開発担当役員をやっている藤川です。この記事はBASEアドベントカレンダーの一日目の記事です。今年もよろしくおねがいします。今年のスケジュールや去年までの記事に興味がある方は是非こちらをご参照ください。  devblog.thebase.in 2021年のアドベントカレンダー一記事目には、個人ブログで好評を得た「新人エンジニアリングマネージャを採用脳にする」を会社のブログ記事に書き換えて再配信したいと思います。 タイトルをエンジニアリングマネージャではなく、技術系マネージャとタイトルを変えているのは、2021年末時点の当社の組織において、EM(技術チームのリーダー)とグループマネージャ(EM of EM's)と2段階に構成されていて、現状、この記事で論点としているスカウトなどの重い採用活動はグループマネージャが担っているという状況があります。EMはよりプレイングな側で活躍してもらってることが多いです。 上記が抽象化した組織図ですが、緑色の部分がグループマネージャで、BASE事業を構成するKPIなどを元に目的別に分解をした事業ドメインのチームと一緒に動く開発チームになっています。(事業ドメイン側には、この人のエイリアスが存在していてOKRが共有されている)ある種、事業ドメインのVPoE的な役割を持っているのがグループマネージャです。本記事の対象はこのポジションの人になります。 こうすることで、Service Devと書いてあるエンジニアリングセクションは、採用、人材育成、組織構築を中心にサービスを向上するための開発目標の達成とサービス性維持を行うことをマネジメントしている組織ツリーと定義づけています。多くの会社だと採用を担う技術系チームはrecruting側に寄せたりしますが、我々はメンバーの採用、育成に責任を持つことこそが技術系マネージャの最重要課題だと定義して、技術チームの組織ツリーは構成されています。 BASE社に入社したエンジニアは、最初に藤川とCTOの川口と二人で技術に関するオリエンテーションを受けているのですが、そこでもマネージャが採用をやっているという話をしますが、よくよく考えるとマネージャが採用をやっているというのは、あまりピンとこない方も多いのではないでしょうか。昨今のネットのスタートアップ企業のCTOやマネージャ経験がある人なら当たり前になっていますが、他の業種やSIer、新卒採用を会社でやっている大企業などに関わった経験がある方だと、どちらかというと人事が人を集めてくるというイメージがあり理解し難い部分もあるかと思いまして、その活動の意義をできるだけ知ってもらおうと思って話をしています。 マネージャがスカウト行うメリット マネージャがスカウトを行う理由は、チーム組成を担う仕事として、プレーヤよりもマネージャの方が主体的に動けること、人を見る目を養っていく仕事が故に、良い人材へアプローチできることを意識してやっています。スカウトは、こちらが先んじてスクリーニングしているが故に、やはりエージェントさんの紹介よりもチャレンジングな人選も含めて人のマッチ率が高い、つまり、面接通過率が高いという特徴があります。 一方で、一言でマネージャと言っても、ずっとマネージャをやってきた人もいれば、ちょっと前までプレーヤーとして活躍して、マネジメント職について経験が少ない人達もたくさんいます。成長前提の組織においては、どこかしらどこかの役割において新人さんがいて、マネージャも例外ではなく、育成、成長を前提とした目標管理をしないと、ただそこに人がいるからと言って、特定の仕事に対して一人前のパフォーマンスを出せるわけではありません。 スカウトを行い面接を通じて人を採用するためには、テクニカルなノウハウは必要で、一例でいうと、 レジュメ等の限られた情報からの人選(見抜く目やマネジメントに自信がないとチャレンジできないかミスマッチを生む) スカウト文面の書き方 カジュアル面談や面接で聞くことの質の向上 我々の事業に共感してくれるアトラクトメッセージを洗練させる 人の特徴を知り、一緒に働ける人材であるかを見極める 採用する人の成長の道筋を見極めて、給料をあげていける人材か?それをうながせる相手であるかの覚悟ができる などなどがあります。 これはこれで重要で、実際に採用活動をしながらrecruitingチームと振り返りを行うことで精度を高めていきますが、とはいえ、PDCA的に採用の質をあげていくためにもある程度、スカウトを行い、カジュアル面談を実現し、ある程度、その人にとっての成功、失敗事例が存在してからが、この改善のチャンスだとも言えます。 よく経営者が話がうまいのは、社員、パートナー、投資家に話をする場数をこなしているからだと言われますが、採用も場数が重要です。コミュ力は生来備わってるものではなく、対話しながら培うものです。 なにより、エンジニア採用にあたって同じ目線で話すことができるのがエンジニアリングマネージャが採用に携わる理由と言えるでしょう。これまでのキャリアと自社のドメイン知識を活用しながら、やってほしいことをマネージャとしての責任を持って言葉として伝えていくことこそが内定承諾につながるわけです。 マネージャのマインドセット問題 そのためにも、まずは場数をこなしながら人をアトラクトするスキルを改善していくことが、本人の成長の早道となっています。しかし採用活動初期においては、まず、この採用活動の速度を出すのがそもそも大変だったりします。 当然、マネージャ職ですから時間の使い方は本人に委ねられているわけですが、それまで開発のプレーヤーとしてやってきているが故に、特に新人マネージャは、それまで目を配ってきた開発のことサービスのこと、そして、新しく始めたメンバーのマネジメントにマインドシェアを持っていかれがちです。ざっくり言うと、こんな脳内構成になっているでしょうか。 もっと明確にセキュリティとかサービスのパフォーマンスというキーワードを入れても良かったかもですね。僕たちは日常起きうるアラート対応が24/365で最優先にマインドシェアに存在していますよね?その時点でマインドシェアにゲタを履いているわけです。 これらをどうにかこうにか整理しながら、以下のように持っていく必要があります。 僕自身も以前CTOとして働いていた時でさえ、この状態に持ってくるのに苦労しました。 とにかく、採用活動が重要なタスクであり、チームを成長させる道筋なんだ、ということを体で理解するためのフェーズが存在します。ある種プレーヤーとしてのマインドセットをアンラーニングするプロセスが必要で、それが数ヶ月は必要です。組織の役割を整理し、自分の言葉で人を採用できるようになるまでに、人によっては半年ぐらいかかるのかもしれません。 僕の場合は、社長との1on1で、このことを言われまくって少しずつ心の中の自覚を持っていったという流れがあります。今の組織では、私であったり、セクションマネージャが担います。テクニカルな採用スキルは並行して身につけていくものの、このマインドシェアが実現できてなければ、すべては掛け算はゼロになり採用は実現できません。 僕の実体験としても、コードを書くために深く深く集中している時に違うタスクを振られるのは非常に苦手でした。一言でいうと不快になります。プレーヤとして技術に向うのが縦方向に深く集中するベクトルだとすると、人に会って面接をするのは、我々の取り組みについて表現可能なことを、心を平面方向に広げながら会話するという感じで、直行したイメージを持っていました。以前は面接の時間が来た時のスイッチングコストに苦労した時期もあり、そんな辛い思いをするならばと、段々、コードに向き合うのをやめていきました。プレーヤーとしてのマインドセットをまるごと他の人に任せていくことにしてマインドシェアの隙間を徐々に開けていったわけです。 採用活動をマネージするために それまで自身の強みとしていたプレーヤーとしてのエンジニアという仕事は、ものすごく細かいところに目を見張って、不具合発生可能性というリスクに目を向け、その改善に集中するというのをコードで表現するのがスキルセットであったりもします。自分自身も正直、採用なんてやったことなかったし、自分の得意な仕事でもなんでもないし、もっとコードやプロダクト開発、運営に集中したいと思ってしまいますし、実際、それを求めてフリーランスになる人もいてもいいぐらいのキャリアとしても大きな分岐点だとも思います。ただ、どうにかこうにか頑張って、組織が大きくなって、そこで活躍している人たちが増えてくれると、それはそれで楽しいものです。 人がいない時は、ただただ人がいないという前提に基づいた悩みしかないですが、人が増えると、コミュニケーションに関するいろんな問題や軋轢が増えたりして、今度は問題が起き続けるという状態になりますが、そっちの方が考えられることが増えて楽しい状態になったりします。 何より自分よりも優れた人材や成長期待の高い人材に自社のビジネスに共感してもらい、人材への投資の形で採用を実現し、プロダクトの質をあげ、ビジネスの成功につなげ、チームメンバーのさらなる成長や期待値のアップに連動する昇給を実現することで、自分自身の報酬自体もあがるという構図が作れます。 それまでのキャリアとは非連続な活動を求められて大変な仕事だなと思います。マネジメントに求められる、組織を作る、開発進捗の実現、採用の実現というのは、それまでやっていた要件から仕様を積み上げて実現可能性を確保するという、技術力を下地にコンピュータをロジックで制御するためにコードを書く仕事とは違って、対人に対して不確実性を御す仕事であると考えてみると、共通点が見えてくるというものではないでしょうか? 最後に繰り返しにはなってしまいますが、若手をチャレンジングに重用することも、他社での経験者を我々のスピード感にあわせてもらう(もしくは周囲がついていける形で引っ張ってもらう)のもマネージャの重要な仕事で、それを実現するためには技術力を下地にしたコミュニケーション力が必要です。採用する技術者のキャリア形成に責任を持つことは紛れもなく技術者だからこそできる仕事であること、35歳定年説で語られた「管理職」などという、どこか静的で収まった役割になるのではなく、常に動的に組織を構成していくために自分自身の成長を伴ったクリエイティブな仕事であるということを知っておいてもらえると助かります。
こんにちは。BASE BANK 株式会社 Dev Division にて、Engineering Manager をしている東口( @hgsgtk )です。 TL;DR 第 17 回 Ques にて「CI のためのテスト自動化」というテーマでの登壇依頼をいただき「Agile Testing を夢見たテスト自動化 〜ATDD への挑戦から始まる 1 年間の試行錯誤〜」というタイトルで発表しました 実際にうまく行かなかったことも含めてテスト自動化のしくじりを話しました Agile Testing・ATDD/BDD/SBE に興味がある方に参考になる資料を公開しました 第17回Quesとは Ques とは Software 品質保証に関わる QA エンジニアの活性化を目的とした QA 専門イベントです。 ques.connpass.com Software品質保証に関わるQAエンジニアの活性化を目的としたQA専門イベントです。 会社間の垣根を越え、情報交換・親交の場を提供することで 日本のSoftware品質保証を盛り上げていく場にしたいと思います。 当日のアーカイブは YouTube にて配信されています。 www.youtube.com 今回、 kyon_mm さんとのイベント『 TDDとATDD 2021 』や、公開していた資料『 実践ATDD 〜TDDから更に歩みを進めたソフトウェア開発へ〜 』を見てくださっていた QUES 事務局の方から依頼を頂き 45 分ほど講演の時間をいただきました。 登壇に至る経緯 登壇内容 個人的に 3 部構成の最終盤という意気込みで作成した資料です。 TDDからATDDへ歩みをすすめる @ July Tech Festa 2021 Winter 実践ATDD 〜TDDから更に歩みを進めたソフトウェア開発へ〜 @ PHPerKaigi 2021 Agile Testing を夢見たテスト自動化 〜ATDD への挑戦から始まる 1 年間の試行錯誤〜 @ 第 17 回 Ques <- New!! 登壇の際に用いた資料はこちらです。 タイトルの副題「ATDD への挑戦から始まる 1 年間の試行錯誤〜」にもある通り、実際に ATDD(Acceptance Test-driven Development)の取り組みとそこから始まる試行錯誤について赤裸々にまとめています。ダメだった・できなかったこと、その考察も含めているのはなかなか珍しいと思うので、参考になると思います(少なくとも挑戦前・挑戦中の自分が見て参考になるようには設計しました)。 たとえば、テスト自動化のしくじりとして次のようなことを紹介しました。 HOW から課題提起してしまう テストデータの用意が億劫で心折れる 「E2E テスト」の解像度で議論してしまう 人によって異なるE2Eのスコープ もしかしたらこの画像だけは事前に 小出しに上げていたツイート で見た方がいるかもしれません。 当日投稿した Tweet は多くの方に拡散いただき、リアルタイムで参加された方以外からも多くの反応をいただきました。 はてなブックマーク では翌日起きたら 100 はてぶ(当記事執筆時点で 155)を超えていて、自分・自チームの試行錯誤が少ならからず同じ戦場で戦う方々の刺激になるものになってよかったなと思います。 当日のツイートは QUES 事務局の方々にまとめていただいていたので、こちらも補足資料として紹介しておきます。 togetter.com Q and A 当日参加者の方に頂いた質問に対して答えたものを補足として置いておきます(その場だから喋ったみたいなクローズドなものもいくつかありましたので、それは当日参加の方だけのシークレット情報ってことで...)。 テストデータは毎回作るとありましたが、テスト終了後は消してクリーンアップするでしょうか? 実際まだテスト終了後のクリーンアップはしていません。新規にユーザーを作成すると gauge-xxx みたいなユーザーが貯まることにはなっているのが現状です。ちょうど先日、ゴミデータとして溜まってしまっていることが開発業務内で課題になったことがあり、定期的にクリーンアップするような試みを現在進行系で行おうとしています。 本筋から外れますが、Circle CI のフローで Security Scan と書いてあったところでは具体的には何を実施してるんでしょうか。差し支えなければ教えていただけたら幸いです。 Circle CI のフローとは こちら のスライドです。 Circle CI のワークフロー Security Scan という箇所では trivy というツールでコンテナイメージのセキュリティスキャンしてます。 Playwright の選定は Capture & Replay で比較的メンテがしやすそうで選定されたという認識ですが、比較的TestCodeを書くスキルのないQA専任者のような人でもメンテナンスし続けられるような狙いもあったのでしょうか? QA 専任者のような人でもってのも狙ってはいましたね。今回のペルソナになるメンバー(自分も含めて)はテスト自動化自体にしっかり時間を使って試行錯誤できないので、Capture & Replay でかつコミュニティの知見がある(ググったら出る)ってのも大事なポイントと捉えて選択しました。 おわりに ozonohiroaki さんが発表された『 これからのCI、これからのE2E自動テスト 』もとてもおもしろかったです! PR-Driven E2E Testing の構想と実行、それに至るまでの個人・チーム全体の課題意識が赤裸々に語られていて、まさに Ques でしか聞けない内容だったと思います。個人的には Pull request・CI といった用語にも丁寧に事前解説されていて職種の多様性もカバーしている良い試みだな〜とか、立って登壇することで身振り手振りがオンライン上からも分かるのいいな〜とかスピーカー目線で参考になったこともいくつかあり感謝です。この場を借りて、 ozonohiroaki さん素晴らしい発表ありがとうございました! また、参加者の皆様のリアクションがたくさんいただけたのもありがたかったです。自分は Twitter と Discord と Zoom の chat 欄を表示しながらリアクション見ながら発表していました。なのでリアルタイムでコメントがいただければいただけるほどありがたいのですが、たくさんいただけたので「この辺は参考になったんだよかった〜」とか 45 分の間も安心しながら話すことができました。参加者の皆様ありがとうございました! 最後に、QUES 事務局の皆様イベントのセッティングから事前ミーティングでの期待値調整や当日の段取り・司会、その後のアンケート・フォローバックまでありがとうございました。第 17 回 Ques での発表を通じて自分の中で 1 つ棚卸しできました。このような機会をいただき光栄でしたし感謝申し上げます。 積極採用中! おわりのおわりに、BASE では様々な職種採用募集中です! 採用情報 から BASE というサービスや組織のことや募集中の職種を確認できます。この記事に興味を持った方へのおすすめ職種を 2 点ご紹介して終わろうと思います。 [BASE BANK_Webアプリケーションエンジニア] open.talentio.com 当発表の舞台となったチームです。フルサイクルエンジニアを志向しており自分たちで開発したサービス・機能をグロース・サポートまで担当します。 BASE_QAエンジニア ネットショップ作成サービス「BASE」の品質保証業務を担っていただく役割です。
11/13(土)にオンラインで開催されたGo Conference 2021 Autumnにシルバースポンサーとして協賛し、 1名のメンバーが登壇、オフィスアワーにも参加しました。 今回は登壇した東口( @hgsgtk )とオフィスアワー枠で参加した永野( @glassmonekey ) の両名による参加レポートをお届けします。 Go Conferenceとは Go Conference 2021 Autumn | Home Go Conferenceは半年に1回行われるプログラミング言語Goに関するカンファレンスです。 前回 に引き続き、オンライン開催でした。 今回、弊社は前回のブロンズからランクアップして、シルバースポンサーとして協賛しました。 ※ 本記事のGopherアイコンのライセンスは以下の通りです。 github.com The Go gopher was designed by Renee French. ( http://reneefrench.blogspot.com ) The gopher stickers was made by Takuya Ueda ( https://twitter.com/tenntenn ). Licensed under the Creative Commons 3.0 Attributions license. 登壇内容について こんにちは。BASE BANK 株式会社 Dev Division にて、 Engineering Manager をしている東口( @hgsgtk )です。 Create Go WebDriver Client from Scratch というタイトルで発表いたしました。 Webアプリ開発をしている現場ではUIレベルのブラウザ操作を自動化することによって実現する自動テストの作成は馴染みが深いものです。 ブラウザ操作の自動化にあたり出てくる WebDriver の Client はその仕様上 net/http パッケージ等の標準パッケージを介して自作することができます。 本トークでは、W3C勧告の標準化仕様WebDriver Wire Protocol等の詳細に軽く触れた上で、net/httpパッケージ等標準パッケージを組み合わせることでWebDriverとコミュニケーションをしブラウザを動かす実装方法を解説します。 登壇資料はこちらになります。 当日登壇中のトークの動画は以下の公式ページから見れます。概要レベルを喋りで抑えていただいて、詳細をスライドで見てもらえると、読み終わった瞬間から WebDriver を「完全理解」して好きなプログラミング言語で client を自作できるかとおもいます。 gocon.jp 何週間前から事前にリハーサルで配信方法についてすり合わせていただいたり、運営スタッフのみなさまありがとうございました。こぼれ話ですが、 GopherCon 2019 で San Diego でお会いした @hatajoe さんと久しぶりにお会いしたのが年月も感じてエモい気持ちになりましたw。 #gocon スポンサーリハーサル、GopherCon 2019のSan Diego以来の @hatajoe さんとの再会があった — Kazuki Higashiguchi (@hgsgtk) October 26, 2021 当日オフィスブースにいながらスピーカーの皆様のプレゼンテーションを聞いておりました。どれも知的好奇心がくすぐられるとても刺激なものばかりでした。登壇者の皆様、運営スタッフの皆様および参加者の皆様にこの場を借りて御礼申し上げます。とても刺激的な時間をありがとうございました! オフィス・アワーについて こんにちは。BASE BANK 株式会社 Dev Divisionにて、 Software Developerをしている永野 ( @glassmonekey ) です。 私からは当日のオフィスアワーで何をしたのか?どうだったのかをお伝えします。 connpass には以下で紹介を出させていただきました。 BASE BANKではフルサイクルエンジニアをエンジニア像として掲げて様々な技術を活用していますがバックエンド開発はGo言語がメインになります。そんなBASE BANKチームから2名のエンジニアがオフィスアワーに参戦します! 1. 現場のGoエンジニアの副音声セッション、スピーカーの皆さまのセッションを一緒に見ましょう! 2. BASE BANKのプロダクトとGoで何を開発しているのか、Goに限らず様々なシステム運用のざっくばらんな意見交換! 定期イベントとして開催している Gophers code reading party のご案内などホワイトボードに耳寄り情報をたくさん乗っけています。ぜひ立ち寄ってください♪ Go Conference 2021 Autumn (Online) - connpass 来場者の方にはホワイトボードを使った会社紹介や、隔週で行っているGoのコードリーディングパーティの宣伝などさせていただきました。 それに加えて、同じようにオフィス・アワーを行っている他社様のブースにお邪魔したりと、 オフラインでしか味わえなかったカンファレンスの廊下のような交流ができて大変楽しかったです。 特に採用まわりの赤裸々なトークができたりと、今しかできない話などできたのはいい体験でした。 様々な良い発表を聞けて、ますますGoが好きになりました。色々変更が入る1.18が今にも待ち遠しいですね。 今回は私自身は登壇するネタや時間を用意できなかったので、次回のSpringでは GenericsやFuzzingでの業務ネタで登壇できたらなと妄想中です。 登壇者の皆様、運営スタッフの皆様および参加者の皆様、貴重な時間を時間を割いて準備していただいてありがとうございました。最高の1日でした。 宣伝 GoのOSSを読む Go Code Reading Party なる会を定期開催しております。 BASEBANKブースに着ていただいた方にはお伝えしたのですが、週1回OSSのコードなどを読む会とかをしてるのでもし良かったらご参加ください。(宣伝) https://t.co/qQtMG4NRIE #goconRemo — エターナル・フィールド (@glassmonekey) 2021年11月13日 直近は11/25の15:00に開催予定です。 参加方法に関しては現在以下の2種類を用意しております。気軽にご参加ください。 github issue で参加表明をしていただく github.com gophers.slack.comの専用チャンネル(#basebank-code-reading-ja)に参加いただく 宣伝その2 BASE BANKチームでは Go, Python, PHPを中心に、フロントからインフラまでを一気通貫で開発しています。 また開発だけでなく、機能をグロース・分析・サポートまで担当します。 そんな開発スタイルに興味あるぞって方は永野( @glassmonekey )にDMを送っていただくか、 下記のリンクから気軽にご連絡ください。 open.talentio.com
はじめましての人ははじめまして、こんにちは!フロントエンドエンジニアのがっちゃん( @gatchan0807 )です。 今回は社内勉強会 Frontend Weekly LT にて、WebOTP / OTPの概要と使い方について発表をしたので、その内容を皆さまにも共有できればと思って記事にしました。 (以前、同じように社内勉強会での発表内容が記事化された 「Frontend Weekly LT(社内勉強会)で「Vite」について LT しました」 の記事もぜひどうぞ) 元々、BASEのどこかに使えないかなぁと思って個人的に調べていた内容を社内共有用にまとめたものなので一部 web.dev の記事をなぞっただけの部分もあるのですが、その内容と共にBASEでのOTPの使い方やWebOTPの利用状況についてのシェアしていければと思います。 イントロ Chrome 93から新機能が追加され、Android + Chromeユーザーであればさらに便利にOTPを利用できるようになりました。 今回の発表ではそれも含めて皆さまにWebOTPの機能について知ってもらい、世のOTPを使った電話番号認証がもっと便利になると嬉しいなと思っています! Chrome 93でのWebOTPの変更点の詳細は公式で出ている 「WebOTP APIを使ったスムーズなPCでの電話番号認証」についてのChrome Developerの紹介記事 をどうぞ。 ざっくりまとめると、 Androidスマホで受け取ったSMSからブラウザ経由でPCでもOTPを自動入力できるようになった! という変更が入りました。 今回はそれらの変更にも触れつつ、OTP / WebOTPの基礎知識から紹介していこうと思います。 目次 はじめに… そもそもOTPは何なのか? WebOTP APIって何? どうやって使う? どこでつかえる? はじめに… OTP、WebOTPについての一般論や使い方に関しては主に こちらのweb.devの記事 を参考に、一部自分の知見や社内での利用箇所に関しての情報を整理してまとめています。 そもそもOTPとは何なのか? OTP(one-time password / ワンタイムパスワード)とは、 Norton先生の記事 によると ワンタイムパスワードを直訳すると「一度きりのパスワード」です。一定時間ごとに発行され、文字通り一度きりしか使えないパスワード、およびそれを採用した認証の仕組みのことです。 各種サービスへのログインだけでなく、口座からの送金など慎重さが求められる操作する際に、第三者によるアクセスを防ぐ手法として主に金融機関などで普及が進んでいます。 となっており、一度だけ利用するパスワードとそれを使った認証の仕組みの総称です。 ひとくちにOTPといってもいくつか種類があり、今回言及するのはOTPの中でも 指定の電話番号にSMSを使ってOTPを送信するSMS OTPについて で、銀行アプリやGoogle Authenticatorなどの専用のアプリがOTPを発行する仕組みなどは対象外です。 このSMS OTPは電話番号と紐付けた形でユーザーの認証や確認のために利用されていて、主だったところで言うと メールアドレスの代わりのユーザー識別子として電話番号を1ユーザー ≒ 1アカウントとして認識する材料として利用する。 通常のパスワードと別にサインイン時に要求し、二段階認証を行うことでセキュリティをより高める方法として利用する。 大きい金額が移動する決済時に、本当に購入してよいか?を確認する方法として利用する。 のようなユースケースがあります。 BASEのなかでは、ショップに紐づく電話番号が「実際に存在」して、「ショップ運営者が管理している」電話番号であるかを検証する方法 としてSMS OTPが使われています。 (それが確認できた場合、ショップの特商法ページに電話番号が認証済みであることを表示し、より安心できるショップであることを表す方法として利用されています) PC版でのショップ電話番号認証のUI SP版でのショップ電話番号認証のUI PC版でのショップ電話番号認証のUI SP版でのショップ電話番号認証のUI WebOTP APIって何? 利用ユースケースがいくつかありBASEを含む様々なWebサービスで使われているSMS OTPですが、下記のような課題があるため、それらを改善する方法が以前より求められてきていました。 利用者からするとアプリやデバイスを横断して入力するのでめんどくさい フィッシング詐欺対策として効かない場合がある 解約した電話番号を再配布することもあるため、別のユーザーに対してSMS OTPが届いてしまうことがありうる 1. 利用者からするとアプリやデバイスを横断して入力するのでめんどくさい 利用者側からすると、ブラウザとSMSを受け取るアプリを移動してSMSで届いたパスワードを一時的に記憶した上で手で入力する必要 があり、安全のためとはいえ煩わしい作業となってしまっています。 さらにPCでOTPが必要な作業を行う場合、デバイスまでもを横断して作業を進める必要があるので、非常に煩わしい作業になりがちでした。 2. フィッシング詐欺対策として効かない場合がある セキュリティ保護のために利用されていることもあるOTPですが、ID/PassだけでなくOTPも同時に盗み取る手法を取られてしまうと セキュリティ強化のためのOTPが意味をなさなくなってしまう ため、問題になることもありました。 参照: IPA オンライン本人認証方式の実態調査報告書 3. 解約した電話番号を再配布することもあるため、別のユーザーに対してSMS OTPが届いてしまうことがありうる ユーザーが解約した電話番号を一定期間経過後に別のユーザーに対してキャリアから提供されることもあるため、 誤って別のユーザーのOTPが届いてしまうリスク をSMS OTPを利用する場合、どうしても孕んでしまっています。 (※これに関しては、SMS OTPを利用している以上取り除くことが難しい問題のため、専用アプリを使ったOTPや別の手法を取ることが推奨されています) これらの課題の解決方法の一つとして、WebOTP APIが提供されるようになりました。 現時点では、 Android Chromeと一部のモバイル用ブラウザでのみ 提供されているWebOTP APIという機能で、ブラウザがSMSアプリとのやり取りを自動的にブリッジし、 任意のプログラムからSMSに届いたOTPを取得してユーザーの手をわずらわせることなく、自動的に入力までを完了する ことができるようにして、より便利なWebアプリケーションのUXを実現できるようにしています。 iOS / Safari でも実装方法は違いますが、同じような自動入力機構が提供されています。 どうやって使う? 対象のブラウザでWebOTP APIを利用する方法としては3つの対応を行うだけで利用でき、比較的シンプルに利用可能なAPIとなっています。 <input> タグに autocomplete 属性を付与する(WebOTP APIの利用に必須ではないが推奨) JavaScriptで navigator 配下にある credentials オブジェクトから otp オプション付きでデータを取得する サービスから送信されるSMSに特定のフォーマットで記述したOTPを追記する 1. <input> タグに autocomplete 属性を付与する(WebOTP APIの利用に必須ではないが推奨) iOS Safari(ver14以上)の場合は <input> タグに対して autocomplete="one-time-code" を指定することで、自動でそのフォームがOTPの受け取り口であることを認識し、SMSで受け取ったOTPを自動ペーストする作業をレコメンドしてくれる機構があるため、まずその対応をしておきましょう。 web.dev でも、iOS / Android問わず類似のUXを提供し、互換性のために強く推奨する実装として紹介されています。 < form > < input autocomplete= "one-time-code" required/> < input type = "submit" > </ form > 2. JavaScriptで navigator 配下にある credentials オブジェクトから otp オプション付きでデータを取得する コードとしてはシンプルで、① OTPCredential が使えるかの確認と ② navigator.credentials.get に otp オプションを指定して取得するだけです。 ① OTPCredential が使えるかの確認 PWAやProject Fugu系のAPIを使う場合にはおなじみの if ('XXX' in window) でチェックをし、存在しない場合はWebOTP関連のコードを実行しないようにします。 if ( 'OTPCredential' in window ) { // 以下にWebOTPを使うコードを書く } ② navigator.credentials.get に otp オプションを指定して取得する WebOTPでは、実験的機能ではあるもののIE以外の多くのブラウザで実装されている CredentialsContainer というAPIが拡張されて機能が実現されており、それを使ってSMSからOTPを取得します。 CredentialsContainer自体は認証に関連するメソッド群をPromiseベースのAPIとして提供するインターフェイスで、 Credential Management API の一部です。 このAPIは基本的にブラウザのパスワード管理システムと対話するために利用しますが、今回のテーマからは外れるので割愛します。 実装自体は下記のように navigator.credentials.get に otp: { transport: ['sms'] } オプションを指定して取得し、Promiseが解決した時に受け取るオブジェクト内から code を取得して利用する形です。 より詳しいOTP取得までのプロセスとPromiseで受け取れるオブジェクトの中身などは こちらの解説 をご確認ください。 navigator.credentials.get( { otp: { transport: [ 'sms' ] } , signal: ac.signal } ).then(otp => { input.value = otp.code; if (form) form.submit(); } ). catch (err => { console.log(err); } ); web.dev より引用したコードの全体 if ( 'OTPCredential' in window ) { window .addEventListener( 'DOMContentLoaded' , e => { const input = document .querySelector( 'input[autocomplete="one-time-code"]' ); if (!input) return ; const ac = new AbortController(); const form = input.closest( 'form' ); if (form) { form.addEventListener( 'submit' , e => { ac.abort(); } ); } navigator.credentials.get( { otp: { transport: [ 'sms' ] } , signal: ac.signal } ).then(otp => { input.value = otp.code; if (form) form.submit(); } ). catch (err => { console.log(err); } ); } ); } 3. サービスから送信されるSMSに特定のフォーマットで記述したOTPを追記する HTML / JavaScript側の修正は以上で、最後にサービス側から送信しているSMSに手を加えることでWebOTPを使ったSMS OTPの自動入力を実現できます。 この対応をしていないSMSだと、SMSで送信されたテキストの中のどこにOTPのコードが記載されているのかが把握できず、また、どのサービス用のOTPなのかも判定ができないため、 SMS OTPの最終行に特定のフォーマットで紐付けるドメインとOTPを記入して送信する ことでプログラムからOTPの内容と対象サービスが判定できるようにフォーマットを定義しています。 そのフォーマットというのは以下のような形で、 あなたの認証コードは 123456 です。 こちらの認証コードをご入力ください。 @www.example.com #123456 SMSの最終行に @ で始まる形でドメインを指定し、半角スペースで区切った後に # から始まるOTPを記載する というものです。(フォーマットの細かいNG事項は こちらの一覧 をご確認ください) このフォーマットは W3Cで定義 されており、 origin-bound-one-time-code-message (直訳すると、オリジン(ドメイン)に紐付けられたワンタイムコード付きメッセージ)という名前でiOS / Android共にこのフォーマットを前提にSMS OTPの実装をしています。 どこで使える? WebOTP APIに対応しているブラウザは前述の通り、Android Chromeと一部のモバイルブラウザのみではあるのですが、iOS Safariでも 別の実装方法 ( input タグの autocomplete 属性として one-time-code を指定する方法)を取ることが可能です。 そして前述の通り、すでにWebOTPに対応しているサービスであれば、 ユーザー側の環境がPC側はChrome、SMSを受け取るスマホ側はAndroid Chromeで双方に同じアカウントでログインしている場合に受け取ったOTPをデバイスを越えて自動入力が行えるようになったアップデート が入り、これまで以上にSMS OTP入力の手間が減るようになっています。 まとめ これらのWebOTP / autocomplete="one-time-code" を使ったオーナーさんの手間を減らす細かい改善対応をこれから行っていければ良いなぁ〜と、調べながらに思いました。 また、今回Chrome 93で追加されたWebOTPのアップデートによってデバイスをも越えた連携ができるようになりましたし、(iOSが強い日本国内だと大手を振って使っていきにくい & 特定ベンダーに依存したコードを増やし過ぎてしまうのも問題の火種になりかねないという課題はあるものの、)Googleの世界で揃えたときのWebアプリケーションでできることが増えるのは見てて楽しいものがあるなぁ〜という感想も持ちました。 これからも、ちょくちょくブラウザ関連でWebでできることが広がるアップデートがあればシェアしていければなと思います! もし、「この部分間違ってるよ」や「この方が伝わりやすいよ」等あれば Twitter 等にご連絡いただけますと幸いです! 参照 Verify a phone number on desktop using WebOTP API Verify phone numbers on the web with the WebOTP API ワンタイムパスワードとは? – 使い方と安全性を簡単解説 オンライン本人認証方式の実態調査 報告書 About the Password AutoFill Workflow Enabling Password AutoFill on an HTML Input Element MDN / CredentialsContainer W3C / Origin-bound one-time codes delivered via SMS
まえがき こんにちは。Owners Experience Backend Group の杉浦です。主にサーバーサイドのアプリケーションの実装をしています。 エンジニアとして働いていると、当然、技術的なことには意識を向けるのですが、ROI(Return of Investment = 投資利益率)を意識することはあまりないと感じたので、この観点でエンジニアリングを考察しました。 想定する読者としては、下記を意識しています。 エンジニアの方で、ROIというビジネスの概念を知りたい方 エンジニアではない方で、ROIの観点でエンジニアリングの理解を深めたい方 このため、テックブログではあるものの、エンジニアではない方にも趣旨を理解いただけるように、技術に関しては少々噛み砕いて書いています。普段、コードには触れないビジネスパーソンの方にとっても、エンジニアリングを理解する一助になりましたら幸いです。 なお、筆者である私は、エンジニアに転生する前は、上場企業を対象とした投資会社で働いていました。普段からROICや競争優位性といったテーマに想いを巡らせてきたこともあり、本稿のバックグラウンドになっています。 リーディングガイド illustrated by @yusugiura このテックブログは3章からなります。 第1章では、ROIの再定義を試みています。エンジニアリングの世界では、そもそも金額効率を見るのがナンセンスであり、時間効率を意識すべきという、少々過激なスタンスをとっています。 第2章では、ROIを意識したエンジニアリングの実践例を書いています。「Facebookドメイン認証の自動化」というプロジェクトを例に、エンジニアが意識すべきポイントを考察しています。キーワードは「不確実性の可視化」です。 第3章では、ROIを意識したエンジニアリングがビジネスに競争優位をもたらす道筋を提示します。キーワードは「綺麗なコードは見えざる資産」です。 エンジニアではない方は、第1章と第3章をお読みいただければ論旨を理解いただけます。1分で結論を知りたい方は、第3章だけお読み下さい。 第1章 ROIの本質は何か? 伝統的なROIでは資金効率をみるが・・・ ROIはビジネスにおける金額効率を判定する指標です。 分母に投資額(Investment)、分子に利益額(Return)を設定して計算します。少ない投資額に対して、利益額が多ければ多いほど良い、というわけです。 ROIの分母である「投資」を広義の意味で捉えると「ヒト・モノ・カネ」といった希少資源に対する配分と定義できます。 たとえば、製造業の場合は「工場で使う工作機械や金型」、不動産デベロッパーの場合は「土地の仕入れ」、保険会社であれば「営業人員の人件費」、流通問屋であれば「在庫の確保」といった具合です。 企業は競争に勝つために、ビジネスで欠かせない要素に傾斜投資するため、ROIは本質的に「何に重点投資をするか?」という意思を問うものになります。 つまり「利益を生み出す効率を最大化するために、どの部分に、いくら投資すべきかを、よく吟味しましょう」というのが、ROIの存在意義になります。 ソフトウェアは「ヒト・モノ・カネ」で捉えられない では、ROIの分母である「投資」について、ソフトウェアのエンジニアリングの観点では、どのように捉えたらよいのでしょうか? 伝統的なROIを踏まえると「ヒト・モノ・カネ」が分母の値として適しているように見えますが、ソフトウェアの世界にこの考えを適用するのは少々危険です。 illustrated by @yusugiura 「ヒト」に関しては、エンジニアの頭数を増やせば良いという安易な考えは禁物です。場合によっては、開発現場に混乱をもたらして、逆に生産性が下がるかもしれないからです。 「モノ」に関しては、必要最低限でまかなうことができます。ソフトウェア企業における固定資産への投資はパソコンやサーバーに限られるからです。 「カネ」に関しては、投資金額を増やせば素晴らしいソフトウェアが完成するという、単純な関係は成立しません。 優れたソフトウェアの例では、エンジニアたちに愛されている様々なライブラリーは、OSS(オープンソースソフトウェア)によって、ほぼ無償で開発されています。 逆に、悪いソフトウェアの例では、何百億円、数千億円という莫大な金額をかけたプロジェクトが「稼働したら実は致命的なバグだらけだった」「全く稼働しなかった」というのは、この業界ではよく耳にする話です。 もちろん、お金は大切であり「Cash is King」はまさしくそうなのですが、「単純にお金をかければ良いのだ!」というわけではないところが、ソフトウェアの難しいところです。 ROIを意識したエンジニアリングでは時間効率をみる では、ROIにおける分母の「投資」は、エンジニアリングにおいて、どのように捉えたらよいのでしょうか? 私の結論は「投下時間」を設定することです。 伝統的なROIが「資金効率」を測定するものであったのに対して、エンジニアリングのROIでは「時間効率」をみるということです。 illustrated by @yusugiura あえて時間軸を見る理由は、ソフトウェア企業にとって「スピード」こそがサービスの成否を決める重要な要素だからです。 この業界では、厳しいグローバル競争が繰り広げられています。変化し続けるユーザーのニーズに応えるために、素早く機能をアップデートして、サービスの改善改良を継続しないと、長期的には存続できません。 ちなみに、BASEではMove Fastを行動指針の1つとして掲げていますが、同じような方針を掲げる企業は数多くあります。 これは「スピードを意識しないと、その先にはDieが待っているから」であり、明文化して常に意識しなければならないほど、激烈な競争が繰り広げられているからにほかなりません。 利益額を意識すると間違いを犯す 次に、ROIの分子である「リターン」は、エンジニアリングにおいて、どう捉えたら良いのでしょうか? 伝統的なROIのリターンには、利益額が設定されます。当然のことながら、生み出す利益額が多ければ、これに越したことはありません。 では、ROIを意識したエンジニアリングにも、リターンに利益額という指標を使用してよいのでしょうか? 答えはノーです。なぜかといえばソフトウェア企業には「時間軸のジレンマ」が存在するからです。 たとえば、新しい機能を実装してリリースしたとしても、短期的には「ユーザーの獲得」などに大きく貢献しない場合があります。リリースした機能がユーザーにとって使い勝手が悪かった場合、ユーザーが欲している機能であっても、その目的を達成できないからです。 ところが、その後、関連する別の機能がリリースされたり、UIが改善されたときに、かつてリリースした機能がベースとなって、ユーザーの獲得に大きく貢献することは珍しくありません。 illustrated by @yusugiura ソフトウェアは連続的かつネットワーク的に発展する技術であり、各パーツ(=機能)の統合によって、ユーザーに新しい便益を提供することができるという性質があります。 よって、ある機能をリリースした直後、つまり時間軸の初期段階では「利益」を期待することができません。 このため、利益額を前提とした意思決定はミスを犯すことになります。「短期的に利益が出ないなら、その機能の開発はストップしよう」という判断は、一見すると合理的に見えますが「時間軸によるネットワーク効果」を無視した行動になり得るからです。 エンジニアリングにおいて、短期利益を重視するスタンスは「長期利益を放棄する」ことを意味します。逆説的ですが「目先の利益を最大化するぞ!」という姿勢は「ぜんぜん貪欲じゃないなぁ・・・」とさえ思えます。 ユーザーが抱える問題の解決を意識する では、ROIを意識したエンジニアリングにおいて、分子の「リターン」には何を設定すべきでしょうか? その答えは「ユーザーが抱える問題の解決」です。 illustrated by @yusugiura ただし、問題解決の内容を具体的に定義することはできません。なぜかといえば、ユーザーが抱える問題のパターンは無限に存在しており、一つには決定できないからです。 たとえば、新しくリリースする機能が「ユーザー獲得につながる」のか、「ユーザーの維持につながる」のか、もしくは「コストダウンにつながる」のか、その狙いはプロジェクトの性質や、企業の成長フェーズによってさまざまです。 遂行するプロジェクトの性質によって、リターンに設定すべき値を考えなければならないと言えるでしょう。 第2章 ROIを意識したエンジニアリングの実践 「Facebookドメイン認証の自動化」プロジェクト それでは、実際に、ROIを意識して、どのようにエンジニアリングを実践していくのがよいのかを見ていきます。 今回取り上げるのは、2021年9月8日にリリースした「Facebookドメイン認証の自動化」というプロジェクトです。BASEとInstagramを連携するために、ユーザーがFacebookを通じてドメイン認証を行う必要があり、このフローを自動化するという狙いでスタートしました。 このプロジェクトは、実装者としては、私が1人で携わる形になり、試験的にROIを意識する良い機会だと思って(こっそりと)実践することにしました。 リリースにいたる背景 このプロジェクトが立ち上がった理由は、いままでのドメイン認証というフローが、Instagramを連携したいユーザーにとって、ボトルネックになっていると考えられたからです。 従来の機能では、ユーザーに「metaタグを設定する」というアクションを求めていました。 metaタグとは、HTMLで定義される要素の1つです。metaタグに認証用のコードを埋め込むことによって、外部のシステムがサイトの所有者を確認するために使用されます。 ですが、metaタグという概念は、エンジニアリングの専門的な知識であり、本来、ユーザーが知る必要はありません。 illustrated by @yusugiura したがって、metaタグの設定を含めたドメイン認証のフローを、すべて自動化することによってユーザーの不便を解消し、「ドメイン認証が完了したショップ数を増やす」というのが目指したゴールでした。 このような事情があったため、リリースは1日でも早いほうが良い、という状況でした。 そこで、ROIを高める観点で、4つの方針を立てました。 方針1 バックエンドとフロントエンドを1人で実装する 方針2 ユーザーに通知するエラーを限定する 方針3 リリース後の分析項目を限定する 方針4 不確実性を下げるためにコミュニケーションする 方針1:バックエンドとフロントエンドを1人で実装する 素早い改修を行うために、最初に決めた方針が、実装者である私自身がバックエンドに加え、フロントエンドの実装も同時並行で行うことでした。 この理由は、ドメイン認証にあたっては、外部システムから提供される複数のAPIを叩くため、不確実性が高いことが予想されたからです。 本来は、API仕様書に記載された内容を精査することはもちろん、実際にAPIを叩いて、想定されるすべてのパターンを設計に盛り込むべきです。 ですが、限られた時間の中で、API仕様書を抜け漏れなく確認することは非現実的でした。リリースが遅れてユーザーに不便を強いる期間が長引いてしまうことも、好ましくありません。 illustrated by @yusugiura そこで、今回の実装では「外部のAPIは不確実である」という前提を置き、実装の途中で想定外の仕様を発見した場合は、バックエンドとフロントエンドへの修正が必要になることを考慮しました。 したがって、予期しない修正によって生じる調整コストを最小化するという観点から、サーバーとフロントを1人で実装することが合理的であると判断したのです。 方針2:ユーザーに通知するエラーを限定する システムが取り扱うエラーのパターンが多い場合、その全てをユーザーに表示することは現実的ではなく、取捨選択が必要になります。 ドメイン認証を実行する際も、外部システムのAPIを複数叩く必要があり、それぞれのAPIに対して複数のエラーのパターンが存在します。このため、ユーザーにエラーを伝えるという観点において、対応範囲を絞ることが有効と判断しました。 今回の実装では、ユーザーにとって遭遇しやすいエラーはその理由を画面に表示する一方で、頻度の少ないエラーが発生した場合は、ヘルプページに取るべきアクションを記載する形をとりました。 方針3:リリース後の分析項目を限定する リリースをした後に必要なのが分析です。この内容が「次にどうアクションすべきか?」という意思決定の材料になります。エンジニアとしては「分析を楽にする」ことも視野に入れて、実装しなければなりません。 そこで今回は、分析で測定すべき項目を下記に限定しました。 ドメイン認証に成功したユーザー数 ドメイン認証に失敗したユーザー数 失敗した場合のエラーの原因 これらのデータが取得できれば「失敗率」と「失敗理由」が判明するため、仮に失敗率が高かった場合に、BASEとしてとるべきアクションが明確になります。 具体的な実装方法としては、APIの実行時にログを内部出力するコードを仕込み、Elastic社が提供するログ解析ツールのKibanaによって分析できるようにしました。実際に分析結果をまとめるところまで、エンジニアとして対応しています。 なお、今回の分析では、データベースのテーブルに分析用のカラムを追加することを、あえて避けています。 その理由は、外部システムとの連携を前提とするため「認証済みである・認証済みではない」というステータスを、BASEのシステムでは管理できないからです。ユーザーが自分自身でmetaタグを埋め込んで、BASEを介在させずに、外部システムから認証することも考えられるため、この方法で認証に成功した場合、BASEのシステムは感知できません。 一方で、分析用のカラムを使わないことは、代償を伴います。テーブルをjoinするといった複雑な分析の可能性を放棄していることや、ログの分析は実装したエンジニアが行わなければならないからです。 今回は、ログを活用して十分に検証できると判断しましたが、分析用のカラムを活用すべき局面もあります。このあたりの方向性は「何を分析すべきなのか?」「誰が分析するのか?」という観点から考える必要があるでしょう。 方針 4:不確実性を下げるためにコミュニケーションする illustrated by @yusugiura あらかじめ完璧に設計したという自信を持っていても、不測の事態は起こり得るものです。 今回のプロジェクトでは、万一の事態に備えるために、頻繁にコミュニケーションを取ることを意識しました。 プロジェクトの責任者の方に毎日進捗を報告し、APIの仕様に不明点があれば詳しい方に質問し、不確実性の共有と可視化に努めました。 社内で検討してもわからないAPIの仕様があったときには、外部の企業に直接問い合わせたため、実装時間と同じくらい、コミュニケーションにも時間を割く形となりました。 つくづく、エンジニアリングとは、不確実性との闘いだと思います。 コードの品質を妥協してはいけない ROIを意識したエンジニアリングを通じて、妥協してはいけないのがコードの品質維持です。 実装時間を短縮するために、綺麗ではないコードを容認してしまうと、次のエンジニアが実装に取り掛かる際に、ROIが大きく低下します。綺麗ではないコードとは「責務が適切に分離されず、リーダブルでなく、テストが十分ではない状態」と捉えています。 今回の「Facebookドメイン認証の自動化」の実装にあたっては、既存のコードから多くの恩恵を受けました。ビジネスロジックを扱う層が的確な粒度で分離されており、テストコードも過不足なく存在していたため、実装スピードの向上が可能になったからです。 品質の高いコードを将来に継承することも、ROIを高めるうえで大切な事だといえます。 第3章 競争優位をもたらすROIエンジニアリング 綺麗なコードは「見えざる資産」 ROIを意識したエンジニアリングが、ビジネスの競争優位に結びつく道筋は次の通りです。 インターネットを取り巻く技術は常に変化する それに伴ってユーザーが求めるニーズも日々変化する したがって、リリース頻度を高めることが競争優位につながる そこで、時間効率を意識したROIエンジニアリングが有効になる 競争優位の持続には「綺麗なコード」の資産化が欠かせない このなかで、エンジニアのご経験がない方にとって理解しにくいことが「エンジニアが綺麗なコードに執着する理由」かと思います。 コードにおける「綺麗」をわかりやすく言い換えると「エンジニアの誰もが理解しやすいように書かれており、将来の改修可能性を考慮した状態のこと」をいいます。 つまり「綺麗なコード」がシステムの内部に蓄積されていれば、将来にわたってエンジニアによる実装のスピードは向上するため、リリースの頻度が高まり、結果としてビジネスにおける競争優位をもたらします。 逆に、コードを「汚くても良い。動けば何でも良いじゃないか」と捉えた瞬間、短期的にはリリースのスピードを向上できたとしても、そのシステムは長期的に競争優位を失うことになります。 なぜならば、一度でも窓ガラスが割れてしまうと「綺麗なコード」を書く規律が失われてしまうからです。こうなると、システムにおける不確実性が高まり、エンジニアは疲弊します。そして、その噂は必ず広まり、新たにエンジニアを採用できないという、悪循環に陥ります。ここまでいくと、もはやなす術がありません。 コードの品質には「普通」という曖昧な概念は存在せず、「綺麗である」と「綺麗ではない」の2つしか存在しません。いわば真偽値(boolean)です。 そして「綺麗なコード」への規律を保つことは容易ではないからこそ、規律を維持できれば差別要素になります。 この意味で「綺麗なコード」は、競争優位性を持続させるためのカギであり、貸借対照表における「見えざる資産」なのです。 illustrated by @yusugiura ROIを意識したエンジニアリングの限界 ROIを意識したエンジニアリングには、ある限界が存在します。 それは、ROIを追求した先に「ビジネスにおける長期利益の最大化が達成されなければならない」ということです。 ROIを意識したエンジニアリングには、利益額を設定できないという限界が存在します。しかしながら、サービスの提供者が株式会社である以上、長期的に利益を生み出すシナリオを描けないと、いずれ財務危機に陥ります。 つまり、エンジニアリングのROIを最大化したとしても、ビジネスを通じて長期利益を獲得する「筋書き」が存在しないと、従業員・株主・経営者の苦労は報われないということです。 この点は、ROIを意識したエンジニアリング「だけ」を追求したときの限界として、胸に刻んでおく必要があります。いくらリリースの時間効率を高めて、綺麗なコードを書き続けたとしても、その事業が長期利益に結びつかなければ意味がありません。 エンジニアリングとは、目的を実現するための手段であり、目的そのものではないのです。 終わりに & 宣伝 エンジニアリングに終わりはない ここまでROIを意識したエンジニアリングについて考察をしてきましたが、あくまで、現時点の私見であり、未完成の考察になります。エンジニアリングで投資効率を考えるという、少々突拍子のない論考であり、しかもROIの定義を強引に変更しているため、完成された思索でもないからです。 そして、web業界の激しい変化の渦中にあっては、完成することを目指すべき論考でもない、と感じています。仮に完成されたとしたら、それはweb業界の発展がストップしたことを意味しますが、当分の間、このような状況が来ることはないでしょう。 あくまでも、ユーザの抱える問題を解決するために、最適なエンジニアリングの方法を模索し続ける姿勢が大事なんだなと、日々、感じています。 いつもの宣伝 BASEではサービスを発展させるうえで、エンジニアも募集しております! カジュアル面談も実施しておりますので、お気軽にお問い合わせください! https://open.talentio.com/r/1/c/binc/homes/4380 参考文献(書籍) エンジニアリングの視点 Bill Karwin『SQLアンチパターン』オライリージャパン、2013年 Dustin Boswell、Trevor Foucher『リーダブルコード:より良いコードを書くためのシンプルで実践的なテクニック』オライリージャパン、2012年 Thomas Kuhn『科学革命の構造』みすず書房、1971年 大野耐一『トヨタ生産方式:脱規模の経営をめざして』 ダイヤモンド社、1979年 成瀬允宣『ドメイン駆動設計入門:ボトムアップでわかる!ドメイン駆動設計の基本』翔泳社、2020年 広木大地『エンジニアリング組織論への招待:不確実性に向き合う思考と組織のリファクタリング』技術評論社、2018年 投資の視点 John Kenneth Galbraith『不確実性の時代』TBSブリタニカ、1978年 Nassim Nicholas Taleb『ブラック・スワン[上]:不確実性とリスクの本質』ダイヤモンド社、2009年 Nassim Nicholas Taleb『ブラック・スワン[下]:不確実性とリスクの本質』ダイヤモンド社、2009年 Scott Kupor(2019). Secrets of Sand Hill Road: Venture Capital―and How to Get It. : Virgin Books. 中神康議『三位一体の経営:経営者・従業員・株主がみなで豊かになる』ダイヤモンド社、2021年 経営の視点 大津広一『企業価値を創造する会計指標入門』ダイヤモンド社、2005年 楠木建『ストーリーとしての競争戦略』東洋経済新報社、2013年 三品和広『高収益事業の創り方(経営戦略の実戦(1))』東洋経済新報社、2015年 社会の視点 Daniel Bell『脱工業社会の到来:社会予測の一つの試み』ダイヤモンド社、1975年 今井賢一『情報ネットワーク社会』岩波書店、1984年
この3ヶ月で開催したBDIの内容を紹介します こんにちは、Design Group Manager の小山です。 7〜9月もデザイナー発信の勉強会「BDI」を実施したので、どんなことをやっているのか内容をまとめました。 社内勉強会のネタのタネにぜひご活用ください! BDIとは? 『BDI』は「BASE Design Inspiration」の略。 2018年の秋頃から活動している、デザイナーがやりたいことを持ち寄って、 デザインに関する幅広い知見をみんなで楽しく学ぶことを目的とした任意参加の社内勉強会です。 BASEのデザイナーであれば、デザイナーだけでなく誰でも参加することができます。 Inspirationの名の通り新たなひらめきにつながる新しいトピックを取り上げることも多くあります。 過去に開催したBDIのまとめはこちら devblog.thebase.in devblog.thebase.in 7月 実践編!デザインツール「Figma」をマスターしよう 6月に実施したFigma初心者向けの使い方解説に続いて、今回はFigmaのマニアックな使い方を学べる"応用編"を実施しました。 BASEでは2021年から本格的にデザイン業務でFigmaを使い始めました。 実務でFigmaを使う中で初めてわかる「使いづらい」と感じるポイントに対して、Figmaマスターのデザイナーが、他のデザイナーにTipsを紹介する形式でBDIを開催。 この他にもSmart Animateのマニアックな使い方や、現在ベータ版で提供されているInterective Componentsの使い方などを解説してもらい、みんなで実際にデータを触りながらFigmaの使い方を学びました。 完全に余談ですが、最近公開された Cocoda Gallery でBASEのFigma移行についてを詳しく紹介しているので、よければこちらも見ていただけると嬉しいです! cocoda.design 新人×ベテランデザイナー対談「デザインここがわからない!」 去年デザイン未経験のインターンからデザイン研修を通して入社したデザイナーと当時メンターをしていた先輩デザイナーの対談を開催しました。 初学者ならではの「そもそもこれって...」という質問をきっかけにいろいろな話をする会になりました。 slackの実況チャンネルを作成して、画面共有をしているスライドにコメントを流したことで、2人の対談だけにとどまらず、参加してくれていたデザイナーもそれぞれの考えを共有してくれて盛り上がりました。 終盤では、普段参考にしているおすすめのデザイナーやポートフォリオサイトを雑談形式で紹介し合う流れになり、リモート環境下でPJに関わらずデザイナー間でコミュニケーションを取るいい機会になったと思います。 去年の記事になりますが、新人デザイナーが入社4ヶ月目に書いたふりかえりブログはこちらから見ることができます。 devblog.thebase.in 8月 404エラーページを分析してみよう 普段は意識することがないけど、意図しないタイミングでユーザーの目に触れてしまうことがある404ページ。 「そもそも404ページにはどのような役割があるのか?」 「他サービスの404ページはどのような工夫がなされているのか?」 を分析し、他社の事例を比較する会を開催しました。 この回ではFigmaのアカウントを持っていない人でも参加しやすいようにMiroを活用しました。 まずは404ページが持つ役割ってどんなものがあるかを参加者でブレスト的に付箋に書き出し、「確かにそういう役割や体験もあるね」という話をしながら整理をしました。 後半はさまざまなサービスの404ページを整理した内容を踏まえて分析していきました。 サービスごとに遊び心を持たせたページになっていたり、ターゲットユーザーによって使われている文言が違っていたりと、404ページに訪れたことがネガティブな体験にならないような工夫がされていることがわかり面白かったです。 UXライティング入門!意外と間違えやすい日本語 デザインを作成する上で切っても切り離せないUXライティング。近年UIデザイナーの中でもその大切さが改めて注目されていますね。 BASE内で日頃UXライティング視点でテキストのチェックや作成を行ってくれているメンバーにファシリテーターをお願いし、間違えやすい日本語クイズと各種サービスのUXライティング比較のワークショップを開催しました。 ↑みなさんはこの答えわかりますか...? 正しく、わかりやすい日本語を使うことは、ユーザーを思いやること。 今回はスライド内にクイズを入れることで聞いてる人も参加できる工夫を行い、楽しく「正しい日本語を使うことの大切さ」を学びました。 後半ではmiroを使ってライティングの比較をするワークショップを実施しました。 トンマナの違う複数のサービスを比較し、どのようなトンマナだとどのような印象を受けるのかをみんなで考え、付箋に書き出して行きました。 最近は社内でもUXライティングに対する意識が徐々に上がってきていたり、テキストガイドラインの運用が始まったりしています。 UXライティングの活動については去年のAdvent Calendarでも書いているのでこちらもチェックしてみてください! devblog.thebase.in 9月 ショップデザインRTA〜1時間でショップデザインできるかな〜 この回では、BASEのショップデザイン機能リニューアルを担当したデザイナーにファシリテーターになってもらい、お題に合わせたショップデザインを1時間で作成しました。 あらかじめいくつかのショップのテーマ設定を用意してオンラインのあみだくじで誰がどのテーマでショップのデザインをするかをその場で決めました。 ワークショップが始まるまでどんなショップを作ることになるかわからないドキドキ感とよーいスタートで一斉に作り始めるゲーム感がとても楽しかったです。 ショップをデザインした後はMiroにキャプチャを並べて、それぞれどんなところにこだわったかをシェアしました。 サービスをあらためて使ってみて、できること/できないことの再確認やチームとしての共通認識ができたんじゃないかなと思います。 ちなみにショップデザイン機能についてはこちらの記事で詳しく紹介しているのでよければ覗いてみてください! baseu.jp OOUIを使って経費精算UIを整理しよう この回では、立替経費精算などで利用する「経費精算ツール」のUIをOOUIで作成し直すというテーマでBDIを開催。 最近PMチームが読書会でOOUI本を読んでいるという話を聞いたので、この機会にBDIでワークショップを行うことにしました。 前半はOOUIについてのLTを行い、後半は実践ワークショップとして、タスク指向になりがちなUIを、OOUIのフレームワークに沿って整理しました。 身近な経費精算で使っているツールを題材にしたので、イメージしやすいワークショップになりました。 まとめ 7〜9月のワークショップでは、社内で話題になっているトピックを意識的にテーマに取り入れて実施することができたかなと思います。 BASEのデザインチームでは他にもいくつかの勉強会や共有会を実施しており、それぞれアップデートを行っています。 BDIも10月から開催頻度や運用を見直しながら、社内の「きっかけづくり」をしていけたらと思います。 次回のまとめも楽しみにしていただけたら幸いです!
CTOの川口 ( id:dmnlk ) です。 プロダクト開発組織を運営していく中で地味ながら無視できないものとして、「支給PCの選定」というものがあります。 PCスペックによって大きく作業効率が変わるので、なるべくで希望通りのものを支給していくというのが基本方針ではありますが同時に社内資産としての価値やチームでの伝達効率といった点は重要です。 それらを考えるなかで、1年ほど前に発売されたAppleのM1チップ搭載のMacについて解禁するまでの過程を紹介します。 この文章はもともと社内向けに公開予定でしたが、使用PCの選定基準を最も知りたいのはこれから入社される方ということに気づいたため入社前にアクセスしやすい文書として公開ブログとすることにしました。 今までのエンジニア/デザイナー向け支給PCの基準について BASEでのエンジニア/デザイナーに支給しているPCは現在下記の基準になっています。 以前よりTypeScriptのbuildなども多くなり要求マシンスペックが高くなっているなと感じます。 それ以前よりbuildフェーズの比重が大きいネイティブアプリチームにはiMac Proを支給していたりもします。 Mac/Windowsの制限はかけていませんが、大体のメンバーがMacを選んでいることで何か問題が起きたときに対応がしやすいという点でMacを推奨しており一部検証用にWindowsを複数台共用で支給しています。 特にこだわりがない場合は下記スペックでキーボードをJP/USで選んでもらっている形です。 16インチMacBook Pro 2.6GHz 6コアIntel Core i7プロセッサ 32GB 2,666MHz DDR4メモリ 512GB SSDストレージ AMD Radeon Pro 5300M 13インチMacBook Pro 2.3GHzクアッドコアIntel Core i7プロセッサ 32GB 3,733MHz LPDDR4Xメモリ 512GB SSDストレージ Intel Iris Plus Graphics M1チップ搭載のMacの登場 2020年11月にAppleが発表した次世代のM1チップを搭載したMacシリーズが発表されました。 www.apple.com M1チップがデフォルトになるであろうことは予期できましたが、発売時点では各種アプリケーションが正常に動作しない報告も多くこのまま開発者に支給してしまうと本来やるべき開発が滞ってしまうため支給は一旦控えることにしました。 CTOである自分は全く開発しないことはないですが、メイン開発者としてプロジェクトに参画することはないのでまずサブ機として一台購入し試していくことにしました。 しばらく使用してM1 Mac自体は素晴らしい機体であることがわかりました。 13インチのMacBook Airの16GB RAMにカスタマイズしたものを買ったのですがPHP StormとChromeでしっかり開発出来ますし何よりバッテリーの持ちがいいためZoomで何時間もMTGしていても殆どバッテリーは消費されていませんでした。 支給までのボトルネック とはいえ全く問題がないわけではありませんでした。 特にローカル開発で最重要であるDockerがM1対応されることが必要条件です。 対応は12月に行われましたが、BASEのローカル環境はしばらく動くまでの対応はかかりました。 とりあえず自分だけの問題かはわかりませんが、Issueを立てておきました。 github.com Issueを立てると同じような問題を持っている人達が多くコメントを頂き根本原因としてはApple側の問題ということもわかりました。 つたない英語でもIssueを立てておくということは重要なので、臆せずIssueを立てましょう(事前にissueは検索するとして)。 この問題はKnown Issueとしてもdocにリンクされています。 docs.docker.com 開発が進み、docker-desktop-rc-3によってワークアラウンド的対策が入ったようで「Use the new Virtualization framework」のチェックを外すことでローカル環境が完動するようになりました。 これにより開発者視点での支給に対するボトルネックは解消されましたがもう一点重要な点が残っていました。 それはアンチウイルスソフトが対応されていないことでした。 これはM1対応というよりはMacOS Big Sur対応すら非常に遅れており、社内のIntel Macもアップデートが出来ないという状態になっていました。 ユーザーさんが触っている環境を日常的に触れていないということはサービス開発の視点でもリスクと考え、現在社内全体のアンチウイルスソフトのリプレイス計画を進めており年内目標で別のソフトウェアにリプレイスしていく予定です。 結果として、2021年6月にBig SurおよびM1対応が行われましたのでようやく支給の準備が出来ました。 ここでデザイナーチームにも1台支給し普段利用するソフトウェアなどが問題なく動くか、といった点でテストをしてもらいごく一部の動画編集ソフトを除き問題なく動いたようでした。デザイナーからもM1 Macは好評でした。 リリース当初はHomeBrewなど動かす場合にワークアラウンドが必要でしたが現在は特に考える必要はないでしょう。 社内で使われているフロントエンドビルド用のNode.jsがv12系のためネイティブ対応しているv16にアップデートしないとビルド時に速度が出ないという問題もあるのでここはフロントエンドチームに取り組んでもらおうと思っています。 どのMacを選ぶべきか これからBASEに入社されるエンジニア及びデザイナーがどちらのMacを選ぶべきか。 M1 Macは見かけのスペック以上に快適なマシンです。今自分が選ぶなら16インチがない状態でもこちらを選ぶでしょう。しかしながらフロントエンドのbuildには相当時間かかっている感じは否めないのでMacBook Pro16インチ相当が出て欲しいとも思います。 反面、ネイティブ対応されていないソフトウェアなどもまだ多くその場合に自分で解決できるような対応力は求められるでしょう。 社内にもまだ殆ど支給されていないので助けてくれるメンバーも多くはないので、僕も手伝えますがある程度は頑張って欲しいところです。 まだ絶対にこれすべきとは言えませんが、これからの流れとしてM1 Macが主流になっていくと思いますし動かないソフトウェアがあってもIssueが上がっていくことで対応されていく速度が上がっていきますので是非ともM1を選んで欲しいなとも思っています。