TECH PLAY

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

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

226

AWS SAMとは Slack botをサーバーレスアプリケーションとして構築する理由 AWS SAMを用いたSlack botの作成 SAM CLI のセットアップ SAM CLIによるプロジェクトの初期化 Slack App の実装 SAM CLIでのビルド SAM CLIでのデプロイ Slack Appの設定 SAMを用いてSlack botを作成したときに発生する可能性のある課題 クレデンシャルをセキュアに設定する方法がわからない 解決策 bot のレスポンスが何回も実行される 解決策 SAM CLIでの後片付け おわりに こんにちは!スマートキャンプ株式会社の松下です。今日はAWS SAMを用いてSlack botを作成する方法と、発生する可能性のある課題について紹介したいと思います。 この記事は以下のZenn記事を統合し、再編集したものです。 https://zenn.dev/smartcamp/articles/f222ef915bc826 https://zenn.dev/smartcamp/articles/ead9a00fe79cab AWS SAMとは https://docs.aws.amazon.com/ja_jp/serverless-application-model/latest/developerguide/what-is-sam.html AWS Serverless Application Model(AWS SAM)は、Infrastructure as Code(IaC)を使用してサーバーレスアプリケーションを構築するためのオープンソースフレームワークです。AWS SAMの短縮構文を使用すると、デベロッパーはデプロイ中にインフラストラクチャに変換されるAWS CloudFormationリソースと特殊なサーバーレスリソースを宣言します。 Slack botをサーバーレスアプリケーションとして構築する理由 https://api.slack.com/quickstart Slack botを実装する場合の流れは基本的には下記のような形になります。 Slackのプラットフォーム上でAppを作成 Appの設定に基づいてSlackが送信してくるリクエストに対して、レスポンスを返すAPIを実装 2.のようなシンプルなAPIを作成したい場合において、サーバーレスアプリケーションはインフラをあまり考慮する必要がないという観点でメリットがあります。また、botの利用頻度があまり高くない場合、費用面でもメリットが出てくるでしょう。 AWS SAMを用いたSlack botの作成 それでは早速、AWS SAMを使用してSlack botを作成する手順を見ていきましょう。 SAM CLI のセットアップ https://docs.aws.amazon.com/ja_jp/serverless-application-model/latest/developerguide/install-sam-cli.html 上記公式ドキュメントを参照してください。IAM Userなど必要なセットアップを行い、AWS CLIが実行できる状態であることが前提です。 インストール後は下記コマンドで、インストールが成功しているか、パスが通っているかを確認します。 $ which sam /usr/local/bin/sam $ sam --version SAM CLI, <latest version> SAM CLIによるプロジェクトの初期化 https://docs.aws.amazon.com/ja_jp/serverless-application-model/latest/developerguide/sam-cli-command-reference-sam-init.html sam init コマンドを実行すると、SAMで作成したいアプリケーションについて、対話的に設定を行なうことができます。 $ sam init 1 - AWS Quick Start Templates 1 - Hello World Example Use the most popular runtime and package type? (Python and zip) → y ディレクトリ名 → sample-sam-slack-app 対話式のインターフェースで上記のように選択していくと、下記のようなファイルテンプレート群が生成されます。 $ cd sample-sam-slack-app/ $ tree . ├── README.md ├── __init__.py ├── events │   └── event.json ├── hello_world │   ├── __init__.py │   ├── app.py │   └── requirements.txt ├── samconfig.toml ├── template.yaml └── tests ├── __init__.py ├── integration │   ├── __init__.py │   └── test_api_gateway.py ├── requirements.txt └── unit ├── __init__.py └── test_handler.py 注目すべきは下記のファイルです。 samconfig.toml : SAM CLIを実行する際に参照されるSAMの設定ファイル template.yaml : SAMのデプロイ時に実行されるCloudFormationのテンプレートファイル Slack App の実装 sam init で生成したコードにSlack Appとしての実装を行なっていきます。生成されたコードに含まれる実装を削除してしまいましょう。 $ rm -rf events/ hello_world/ tests/ 次に src ディレクトリを作成し、アプリケーションのロジックを書く app.py を作成します。 $ mkdir src $ touch src/app.py app.pyではBolt for PythonというSlackが提供しているフレームワークを利用します。 https://slack.dev/bolt-python/ja-jp/tutorial/getting-started このようなフレームワークを利用することで、認証を自前で実装する必要がなくなります。 Bolt for Pythonのドキュメントに書いてあるとおり、ここではvenvを利用して仮想環境を作成して作業します。(Python環境のセットアップについては省略します) $ python3 -m venv .venv $ source .venv/bin/activate $ which python3 /Users/${ユーザー名}/sample-sam-application/sample-sam-slack-app/.venv/bin/python3 次に仮想環境内にBolt for Pythonをインストールします。 $ pip install slack_bolt なお、SAM CLIではビルド時にrequirements.txtを参照するためあらかじめrequirements.txtも出力しておきます。 $ pip freeze > src/requirements.txt 次に src/app.py を編集します。 import os from slack_bolt import App from slack_bolt.adapter.aws_lambda import SlackRequestHandler app = App( token=os.environ[ "SLACK_BOT_TOKEN" ], signing_secret=os.environ[ "SLACK_SIGNING_SECRET" ], process_before_response= True , ) @ app.event ( "app_mention" ) def say_hello (event, say): user_id = event[ "user" ] say(f "Hi, <@{user_id}>!" ) def lambda_handler (event, context): slack_handler = SlackRequestHandler(app=app) return slack_handler.handle(event, context) botに対してメンションをすると、メンションをしたユーザーに対して「Hi, @user_id」と返答メッセージを送信するシンプルなロジックになっています。環境変数の設定については後述します。 SAM CLIでのビルド ここまで設定した段階で一度SAM CLIでのビルドを試してみましょう。 sam init コマンドで生成したディレクトリ構成を変更しているため、 template.yaml の変更が必要です。 template.yaml を下記のように修正してください。 @@ -12,31 +12,31 @@ MemorySize: 128 Resources: - HelloWorldFunction: + SampleSAMSlackAppFunction: Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction Properties: - CodeUri: hello_world/ + CodeUri: src/ Handler: app.lambda_handler Runtime: python3.9 Architectures: - x86_64 Events: - HelloWorld: + SampleSAMSlackApp: Type: Api # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api Properties: - Path: /hello - Method: get + Path: /slack/events + Method: post Outputs: # ServerlessRestApi is an implicit API created out of Events key under Serverless::Function # Find out more about other implicit resources you can reference within SAM # https://github.com/awslabs/serverless-application-model/blob/master/docs/internals/generated_resources.rst#api - HelloWorldApi: - Description: "API Gateway endpoint URL for Prod stage for Hello World function" - Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/hello/" - HelloWorldFunction: - Description: "Hello World Lambda Function ARN" - Value: !GetAtt HelloWorldFunction.Arn - HelloWorldFunctionIamRole: - Description: "Implicit IAM Role created for Hello World function" - Value: !GetAtt HelloWorldFunctionRole.Arn + SampleSAMSlackAppApi: + Description: "API Gateway endpoint URL for Prod stage for Sample SAM Slack App function" + Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/slack/events" + SampleSAMSlackAppFunction: + Description: "Sample SAM Slack App Lambda Function ARN" + Value: !GetAtt SampleSAMSlackAppFunction.Arn + SampleSAMSlackAppFunctionIamRole: + Description: "Implicit IAM Role created for Sample SAM Slack App function" + Value: !GetAtt SampleSAMSlackAppFunctionRole.Arn SAM CLIでのデプロイ ここまで変更できたら、デプロイを試してみましょう。 $ sam deploy を実行しましょう。CloudFormationのStackのChangeSetが表示されます。今回は初めて作成することになるので新規作成のような表示になっていると思います。 Deploy this changeset? [y/N] と聞かれるため、 y を入力してデプロイを開始します。 Successfully created/updated stack - sample-sam-slack-app in ap-northeast-1 と表示されればデプロイ成功です。 CloudFormation outputs from deployed stack には template.yaml で設定した出力(例:API GatewayのURL)などが出力されていると思います。 SampleSAMSlackAppApi の値は、Slack Appの設定に必要であるためメモしておきましょう。 ここで試しに表示されたURLについてCurlでリクエストを投げてみましょう。 $ curl -X POST '${URL}' {"message": "Internal server error"} 環境変数の設定がされておらず、認証も当然通らないためInternal server errorが返ってきますが、APIの処理自体が動いていることはわかります。 Slack Appの設定 ここからSlackプラットフォーム上にSlack Appを作成して設定していきます。 https://slack.dev/bolt-python/ja-jp/tutorial/getting-started 上記のBolt for PythonのドキュメントにSlack Appの作成方法が記載されているので、Slack Appを作成してください。 その後、Slack Appの「OAuth & Permissions」を開いてください。 「Scopes」までスクロールし、bot tokenに下記のようにScopeを追加します。 Scopeの追加後には、「Install to Workspace」ボタンからインストールしましょう。 Bot Tokenが取得できるようになります。 これがLambda関数に設定したい SLACK_BOT_TOKEN の値になります。 次に、Basic Informationを開きましょう。 「App Credentials」の中の、「Signing Secret」が SLACK_SIGNING_SECRET の値になります。 template.yaml を編集して環境変数として設定しましょう。今回は便宜上、値を直接記載しますが、うっかりコミットしないように気をつけてください。(この問題に関しては後述します) @@ -20,6 +20,10 @@ Runtime: python3.9 Architectures: - x86_64 + Environment: + Variables: + SLACK_BOT_TOKEN: "${トークンの値}" + SLACK_SIGNING_SECRET: "${シークレットの値}" Events: SampleSAMSlackApp: Type: Api # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api 次にSlackからのイベントを受け取るための設定をします。 「Event Subscriptions」を開き、Onにします。 「Request URL」にデプロイ時に出力されたAPI GatewayのURLを、パス( /slack/events )まで含めて入力します。URLを入力した時点で、Slack側からVerifyのリクエストが発行され、うまくいっている場合はVerifiedと表示されます。 また、その下の「Subscribe to bot events」にも設定が必要です。「Add Bot User Event」ボタンを押して app:mention を設定しておきましょう。 この設定を行なうと、画面下部の「Save Changes」が有効になり、設定を保存できるようになったと思います。 ここまで来ればもうSlack Appは動く状態になっています。任意のSlackチャンネルにSlack Appを追加して、メンションを飛ばしてみましょう。 SAMを用いてSlack botを作成したときに発生する可能性のある課題 次に、SAMを用いてSlack botを作成したときに発生する可能性のある課題について説明します。 クレデンシャルをセキュアに設定する方法がわからない botのレスポンスが何回も実行される クレデンシャルをセキュアに設定する方法がわからない 「Slack Appの設定」の項目では、 template.yaml に直接クレデンシャルを記載していました。実際の本番アプリケーションとして運用する場合、クレデンシャルをコミットすることはセキュリティ上好ましくありません。 解決策 今回は、AWS System ManagerのParameter Storeを利用する方法を紹介します。 https://docs.aws.amazon.com/ja_jp/systems-manager/latest/userguide/systems-manager-parameter-store.html AWS Systems Manager の一機能である Parameter Store は、設定データ管理と機密管理のための安全な階層型ストレージを提供します。パスワード、データベース文字列、Amazon Machine Image (AMI) ID、ライセンスコードなどのデータをパラメータ値として保存することができます。 AWSのコンソールから手動で値を入れ、SAMのデプロイ時にCloudFormationの処理で値を解決するようにします。Parameter Storeを開き、一覧の右上にあるCreate parameterボタンをクリックして、パラメータを作成します。 次に template.yaml を修正します。 (省略) Environment : Variables : SLACK_BOT_TOKEN : !Sub "{{resolve:ssm:/sample-sam-slack-app/slack-bot-token:1}}" SLACK_SIGNING_SECRET : !Sub "{{resolve:ssm:/sample-sam-slack-app/slack-signing-secret:1}}" (省略) なお、CloudFormationの実行ユーザーがParameter Storeにアクセスできる必要があるため、ロールに適切なポリシーを設定してください。 https://docs.aws.amazon.com/ja_jp/systems-manager/latest/userguide/sysman-paramstore-access.html bot のレスポンスが何回も実行される 今回設定した template.yaml では、Lambdaのタイムアウトを3秒に設定していましたが、時間がかかる処理を行なう場合があるためいったん30秒に伸ばしてみます。 (省略) Globals : Function : Timeout : 30 MemorySize : 128 (省略) その後、 app.py を編集します。時間がかかる処理を擬似的に再現するために、 time.sleep(10) で10秒間のスリープを追加してみます。 (省略) @ app.event ( "app_mention" ) def say_hello (event, say): time.sleep( 10 ) user_id = event[ "user" ] say(f "Hi, <@{user_id}>!" ) (省略) その後変更をビルド・デプロイして、Slack Appにメンションを送ってみます。 すると、一度メンションを送っただけにも関わらず、3回もレスポンスが返ってきてしまいます。 https://api.slack.com/interactivity/handling#acknowledgment_response This must be sent within 3 seconds of receiving the payload. If your app doesn't do that, the Slack user who interacted with the app will see an error message, so ensure your app responds quickly. これはSlack側の仕様で3秒以内にSlack側からのリクエストにレスポンスを返すことができないと、エラーになるためです。エラーになった後は何度かリトライを行なう挙動になっているようです。 解決策 3秒以内にレスポンスを返す必要があるため、以下の二段階に処理を分けるような実装を考えます。 Slackからのリクエストに対してひとまずすぐにレスポンスを返す 時間がかかる処理を行なってから、処理結果を含むレスポンスを返す Bolt for PythonなどのBoltフレームワークでは1.を実現するために、 ack という関数が実現されています。 (省略) @ app.event ( "app_mention" ) def say_hello (event, say, ack): ack() time.sleep( 10 ) user_id = event[ "user" ] say(f "Hi, <@{user_id}>!" ) (省略) ack 関数を用いた上記のコードは、うまく動作しない例です。 AWS Lambdaは一度レスポンスを返してしまうと、その後の処理が継続することがどうやら保証されていないため、 ack() で一度リクエストに対してレスポンスを返してしまうと、その後の処理が継続されないことがあるようです。 そこでBolt for Pythonに実装されているLazy Listnerを利用します。一度レスポンスを返した後に、処理を行なうための仕組みです。 https://slack.dev/bolt-python/ja-jp/concepts#lazy-listeners まずは、ライブラリをインストールします。 $ pip install python-lambda その後、Lambdaに適用しているIAMロールの権限として "lambda:InvokeFunction" と "lambda:GetFunction" を付与してやる必要があると公式ドキュメントには記載があります。これは、Lazy Listnerの実装で、もう一度Lambdaそのものを実行し直す実装になっているからだと思われます。 (省略) Resources : SampleSAMSlackAppFunction : Type : AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction Properties : CodeUri : src/ Handler : app.lambda_handler Runtime : python3.9 Architectures : - x86_64 Environment : Variables : SLACK_BOT_TOKEN : !Sub "{{resolve:ssm:/sample-sam-slack-app/slack-bot-token:1}}" SLACK_SIGNING_SECRET : !Sub "{{resolve:ssm:/sample-sam-slack-app/slack-signing-secret:1}}" Role : !GetAtt SampleSAMSlackAppFunctionRole.Arn Events : SampleSAMSlackApp : Type : Api # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api Properties : Path : /slack/events Method : post SampleSAMSlackAppFunctionRole : Type : AWS::IAM::Role Properties : AssumeRolePolicyDocument : Version : "2012-10-17" Statement : - Effect : Allow Principal : Service : lambda.amazonaws.com Action : sts:AssumeRole Policies : - PolicyName : LambdaBasicExecution PolicyDocument : Version : "2012-10-17" Statement : - Effect : Allow Action : - logs:CreateLogGroup - logs:CreateLogStream - logs:PutLogEvents Resource : "*" - PolicyName : LambdaInvokePolicy PolicyDocument : Version : "2012-10-17" Statement : - Effect : Allow Action : - lambda:InvokeFunction - lambda:GetFunction Resource : "*" (省略) このようにして明示的にIAM Roleを新しく定義し、Lambdaに指定するようにします。ログ出力のためにCloudWatch Logs用の権限も必要なため、これも含めて定義します。 その後、下記のようにコードも修正します。 (省略) def say_hello (event, say, ack): time.sleep( 10 ) user_id = event[ "user" ] say(f "Hi, <@{user_id}>!" ) app.event( "app_mention" )(ack= lambda ack: ack(), lazy=[say_hello]) (省略) lazyの引数として、後から実行したい処理の関数を配列で渡してやります。 ただし、Lambdaの実行シチュエーションによっては、 そもそものack()を3秒以内に返せない場合もあります。 このようなときは、本質的ではありませんが、下記のようにSlackが送ってくるリトライリクエストを無視するという方法もあります。 (省略) def ignore_retry_request (request, ack, next ): if "x-slack-retry-num" in request.headers: return ack() next () app.use(ignore_retry_request) (省略) app.use() を利用することでミドルウェア的にすべてのリクエストに対して処理を通すことができます。 SAM CLIでの後片付け $ sam delete 上記のコマンドを実行することで、SAM CLIによって作成されたCloudFormationのスタックが削除されます。 Slackプラットフォーム上のSlack Appは手動で削除してください。 おわりに 以上、AWS SAMを用いてSlack botを作成する方法と、発生する可能性のある課題について紹介しました。AWS SAMを使用してSlack botを作成することで、サーバーレスアプリケーションの利点を活かしつつ、比較的簡単にbotを実装できます。しかし、クレデンシャル管理や非同期処理の扱いなど、いくつかの課題にも直面する可能性があります。これらの課題に対してはAWS Systems Manager Parameter Storeの活用やBolt for PythonのLazy Listenerの使用など、適切な解決策を実装することで対処できます。 本記事が、AWS SAMを用いたSlack bot開発の参考になれば幸いです。
アバター
初めに エンジニアを目指すに至ったきっかけ 高校時代 大学時代 「未経験者」就活時代 「経験者」就活時代 コーディングテスト対策 開発経験について スマートキャンプとの出会い まとめ 初めに はじめまして!2024年4月に新卒エンジニアとしてジョインしたcruiseです! スマートキャンプではBOXIL SaaSの開発に携わっています。 新卒エンジニアですが、大学の専攻は土木工学なので、いわば「異業種新卒エンジニア」なのかなと思っています。 今回は入社エントリということで、異業種からエンジニアを目指したきっかけと、就活の経緯をお伝えできればと思います。 エンジニアを目指すに至ったきっかけ 高校時代と大学時代の2つに分けてご紹介しようと思います。 高校時代 将来の職業など考えたこともなく、軽音楽部でギターを弾いていて部活ばっかりの高校時代でしたが、 テックに関する思い出がふたつあります。 ひとつはGAS (Google Apps Script)です。 部活の入部受付フォームをGoogle Formで作ったものの、当時のFormはメール自動返信が使いづらかったので、 GASを使って自動返信と一括送信のスクリプトを書きました。(もちろんほぼコピペですが!!) 今思い返せば初めてのプログラミングです。 もうひとつは当時流行り始めたキャッシュレス決済です。 PayPayのサービス開始以前からLINE Payを使っていたり、Money Forward MEを使い始めたり、 便利なアプリを使いこなすことが大好きな高校生でした。 ちなみにGASとMoney Forward MEは、その後就活で再会することになりますが、 この時点ではまさかエンジニアになるなんてまったく考えていませんでした。 大学時代 大学生になりアルバイトをはじめ、「ロボットプログラミング教室」という珍しい仕事を見つけました。 この教室ではLEGOでロボットを組み立て、Scratchベースの言語で制御していました。 きっかけは「LEGOが好き!」というまぁ子どもみたいな動機だけで面接を受けたのですが、 好きなもので仕事をすることは楽しく、はじめて学ぶプログラミングも楽しく、 気づいたら中学生向けのコースでPytho製のゲーム作りを教えるようになっていました。 そして就活の時期が近づき、自然と「エンジニア、目指してみるかぁ」となり、ここから厳しい異業種就活がはじまります。 「未経験者」就活時代 そんなこんなでヌルっと始まったエンジニア就活ですが、当時は経験も自信もなく、 「未経験者歓迎」的なサマーインターンを探して選考を受けていました。 そもそも募集している企業が少なく、その中で自分が興味を持てる企業となると数えるほどしかありませんでした。 そういった中でも某SaaS企業から合格をいただき、サマーインターンに参加することになりました。 3日間でGASを使った簡易アプリを作り(ここでまさかのGASと再会!!)、現役のエンジニアの方からFBをもらうありがたい会でした。 その後早期選考から最終面接に進んだのですが、残念ながら不合格。 諦めきれず人事の方に相談した結果、「リベンジするなら経験者枠で」ということになりました。 そして、本選考が本格化する時期だったこともあり、並行して他社の選考も受け始めます。 「経験者」就活時代 晴れて(?)経験者を名乗ることになりました。 これまでは、「やる気と興味、将来のビジョン」を伝えればよかったものが、今後はそれに加えて開発経験の話やコーディングテストの結果も必要になります。 それぞれ分けてご紹介しようと思います。 コーディングテスト対策 僕が受けた各社の選考で出題されたのは以下の3パターンです。 アルゴリズム問題 SQL 簡易APIを叩いてレスポンスを得るwebアプリの作成 まず、アルゴリズム問題については、「習うより慣れろ」の考えのもと、競技プログラミングをやってみました。 Atcoderの過去問を解きつつ、毎週のコンテストに参加した結果、1ヶ月でC問題まで解けるようになりました。 次にSQLですが、これは構文を知らないことにはなにも書けません。 簡単な本や解説サイトから入ったのが正解でした。 僕が取り組んだのは Progate SQL コース です。 最後に簡易webアプリですが、これは調べながら解いていました。 そんなこんなで、コーディングテストは通過できるようになりました。 開発経験について もうひとつ大きな問題は「開発経験」です。 「自分でなにかひとつ開発する」という経験を積むべく「卒業要件判定サイト」を作りました。 大学のシステムから出力する成績データ(CSV)を読み込んで結果を表示するものなので、 自分の学科の人間しか使えないものですが、5日間で実装したスピードがその後面接で評価されたように思います。 これは当時面接などで紹介していた説明ページです ↓ https://onyx-mapusaurus-e44.notion.site/f9a330e6a64e456596899c2ca1243634 スマートキャンプとの出会い スマートキャンプとの出会いは3回生の年末、マネーフォワードの説明会でのグループ企業紹介のときでした。 (ここでまさかのマネーフォワードとも再会!!) それまでSaaS企業ばかり見ていたのですが、SaaSを普及させる立場にも興味をもち、選考を受けました。 ありがたくも内定をいただき、スマートキャンプのみなさんの人柄の良さに惹かれて入社を決めました。 また、開発組織がそこまで大きくなく、フロントエンド・バックエンド双方に触れることができるのも魅力的でした。 まとめ こんな就活から1年以上経過し、内定者インターンも少し経験しつつ、 2024年4月から晴れて正社員としてスマートキャンプにジョインしました。 選考時から感じていた人の良さは、思っていた以上にいい人ばかりで、日々チームの先輩方に助けていただきながら業務を進めています。 異業種からのエンジニア就活という珍しい経験をご紹介しました。 少しでも誰かのお役にたてれば幸いです。 これからよろしくお願いします。
アバター
初めに 経歴 〜中学2年 転換期(中学2年) 暇(中学2年) プログラミング(中学2年〜3年) 高校〜大学進学 大学 大学院 就活 スマートキャンプに入った理由 スマートキャンプでのこれから 最後に 初めに はじめまして!!2024年4月に新卒としてスマートキャンプに入社したレジェンド(大塚琢生)です。 社内の方からは「レジェ」と呼ばれています。 FPSが好きで「VALORANT」というゲームをよくやっているという話から、VALORANT→Riot Games→League of Legendsとなり、レジェンドになりました。 今回は、私が趣味でプログラミングを始めるまでの話と、スマートキャンプ入社までの経緯を書いていきたいと思います。 legend 経歴 〜中学2年 中学2年まではごく普通の人生を送っており、中学の部活動では仲の良い友達となんとなくでテニス部に入部しました。 本当によくある普通の幸せな人生を送っていました。 転換期(中学2年) テニス部に入部したことが本当にすべての始まりだったように思います。 テニス部は学内でも有名なかなりハードな練習をしている部で、年中無休で声を出し続けながら走るかラケット振るかしていました。 その分だけ、部員同士の仲はとても良く、熱を持って練習に取り組んでおり、大会でもガツガツと結果を出している、絵に描いたような体育会系の部活でした。 そんな中で、仲間に支えられなんとか1年は部活を続けたのですが、結局体も精神も限界を迎え中学2年の時に骨折を機に部活を辞めることになりました。 人生で初めて悩んで決断をした気がします(笑) 暇(中学2年) 部活を辞めたことで、放課後にかなりの時間が生まれました。 この暇を埋めるために、いろいろなことを試しました。 帰宅部友達と釣りに行ったり、対人ゲームをやり込んでみたり、めちゃくちゃ勉強してみたり、ランニングを始めてみたり、色々とやってみました。 どれも最初は楽しかったのですが、すぐに飽きてしまいました。そんな中、唯一飽きなかったのがプログラミングでした。 もともと、PCゲームをよくやっていたのもあり、PCで動作するソフトウェアをどうやって作るのかが気になり、プログラミングを始めました。 プログラミング(中学2年〜3年) 色々調べた結果、C言語かJavaかJavaScriptかというところまで辿り着きました。 その上で僕が作りたかったWindowsで動作するGUIアプリケーションを作るためにはJavaのSwingというものを使えばよさそうだということがわかり、Javaから始めました。 今思うとhello worldも知らないままJDKのインストールから環境変数にパスをセットしてEclipseをインストールして〜と、よく環境構築できたなと思います。 その後どういう手順で勉強していったのか、GUIアプリケーション作成に至ったのかはもう覚えていませんが「public static void main」という文字列だけは、この時から今に至るまで1日たりとも忘れたことはありません。 java勉強中によく見た謎のキャラクター(duke) 高校〜大学進学 プログラミングで遊びつつ、地元の高校になあなあで進学しました。 プログラミングをやっている人の話も聞いたことがなく、本当に1人でただの趣味として3年間続けていました。 高校3年のタイミングでようやく進学先を考え始めるのですが、趣味に時間を割きすぎた結果勉強は目も当てられな状態になっており、大学への進学は絶望的な状態になってました。 そんな中、地元にある大学の情報学部がAO入試という形で、センター試験なしでプログラミングの実績を評価してくれるということを知り、無事にAO入試で大学に進学することに成功しました。 本当にプログラミング以外に武器は何もなかったし、その分だけプログラミングだけは誰にも負けないつもりでいたので、AO入試の話を見つけたときは奇跡だと思いました。 大学 大学に入った後は、念願の興味のある授業というものを存分に楽しみつつ、趣味を楽しみつつ、友達と遊びつつ、大学生活を満喫していました。 その中で、大学3年生のタイミングで、地元のIT企業でインターンをする機会を得ました。 このインターンも人生の大きな経験になりました。初めての実務経験、初めてのチーム開発、初めての技術など、たくさんのことを学びました。 特に"趣味での個人開発しか経験のなかった自分が仕事として開発ができるのか"という不安がずっとあったのですが、実務経験を通して少しずつ自信を持つことができました。 大学院 本当に色々あって自然言語処理の研究室で、大学院生になれました。 入学前から入りたいと思っていた研究室だったのですが、圧倒的人気No.1の研究室で、自分の成績では普通に入るのは不可能でした。 インターン先の社長やら大学4年の時の研究室の教授やら友人やら、本当にたくさんの人と運に恵まれた結果、無事に希望の研究室に入ることができました。 大学院に入ってからも色々あったのですが、長くなってしまうので「尊敬できる教授と友人のいる恵まれた環境で研究活動を頑張った」とまとめておきます。 就活 就活を本格的に始めたのは大学院1年の冬頃からでした。 「開発体験の向上に力を入れたい!」というのをメインに考えて就活を進めていたのですが、 多くの企業はユーザーに製品を届けるのが圧倒的優先事項であり、開発体験の向上はかなり後回しにされているように感じました。 商売としては当然のことなので仕方ないと理解していたので、自身の考えを変えるべきかと悩んでいたところで、スマートキャンプと出会いました。 もちろんスマートキャンプもユーザーが求めるものを提供することが最優先なのは変わらないと思いますが、届けたいものが"効率"である点が他社との大きな違いでした。 スマートキャンプに入った理由 「効率を届ける自分たちが、非効率ではいけない」という考えから、開発体験の向上に積極的に取り組んでいること、また、ユーザーに届けたい価値と自身が求めるものが一致していることがスマートキャンプに入社した理由です。 スマートキャンプが当時掲げていた「テクノロジーで社会の非効率を無くす」というミッションのためなら、全力で働けると感じました。 また、本当に色々なところで触れられている気がしますが、スマートキャンプの社員の方々の人柄がとても良い印象を受けたのも大きかったです。 入社してからもその印象は変わっておらず、会社全体にコミュニケーションが取りやすく、チャレンジしやすい環境が整っていると感じています。 スマートキャンプでのこれから 就活時に考えていた「開発体験の向上」は、スマートキャンプに入社してからも変わらずに持ち続けています。 一方で、事業への寄り深い理解や共感を持っていく中で、大学院での研究活動で培ったスキルや考え方を活かした貢献もしていきたいと考えるようになりました。 スマートキャンプが提供しているBOXIL SaaSでは、数多くの記事やガイドが提供されています。 この大量の自然言語データから、ユーザーにとってより効率的な情報提供サイトとしてのBOXIL SaaSを支えるために、研究室での知見を活かしていきたいと考えています。 最後に 今回は私の経歴とスマートキャンプに入社した理由について書いてきました。 運だけには自信があったのですが、あらためて振り返ってみると、やっぱり運いいですね!! 中でも人の運には強く恵まれていたように感じます。 プログラミングを始めるきっかけも、高校・大学に入学できたのも、卒業できたのも、希望の研究室に入れたのも、たまたま周りに良い人がいたお陰でした。 特に、プログラミングにハマったタイミングで、PCや大量の本を何の躊躇もなくすべて買い与えてくれたうえに、勉強をせずにプログラミングに没頭している自分を見守ってくれた両親には感謝しかありません。 いまだにほぼ毎日ゲームをしてくれるテニス部の友人達にも感謝しかありません。 運だけの自分は感謝しかできなかったので、少しでも自分の力で恩返しできるように、スマートキャンプで頑張っていきたいと思います。 people
アバター
初めに 自己紹介 趣味 カフェ 読書 勉強会 学生時代 N高 サイバー大学 ポートフォリオについて インターン時代 京都でインターン 何してた? スマートキャンプの面接 スマートキャンプに入った理由 現状目指しているところ 社会人楽しいよ! 初めに こんにちは!24卒のぱんち👊(a.k.a田中大貴)です! インターン時に技術の記事を書いて入社エントリーで2記事目です。どういう順番なんでしょう?入社エントリーでよく書いてあることを書いていきます。 自己紹介 出身は和歌山県です。大阪まで二時間半かかる環境で生きてきました。というか大阪駅から30分で京都、1時間で奈良、神戸行けるのになんで和歌山こんな時間かかるんですか。時空が歪み始めている……。 和歌山に住んでいたときはミカンが雪崩のように届いていたので東京に引っ越してからは少し寂しいですね。多くの家庭にミカンが届くため、直接買う機会が少なかったりします。 名前のぱんちの由来は田中大貴 → 田中みなみ → 朝倉南(タッチ) → ぱんち(朝倉南の飼っている犬)。犬の名前で呼ばれているという事実に衝撃を受けますね。 趣味 趣味はカフェ、コーヒー、読書。カフェに関しては6月時点で60回以上行ってるらしい、一日に五件行く日とかもありましたね。ソニック・OMORI・特撮、大好き人間でもあります。 たま〜に勉強会にも参加してます。 カフェ 週に3件は行っています。 基本的に本を読みに行ってるので実質勉強時間みたいなものです。個人的な意識としてはカフェを開拓して勉強できる場所をどんどん増やしていってるイメージです。そのおかげか一ヶ月で本を5冊ほど読んでおり、私にとっては大事な時間です。まぁしかし研修の昼休みにカフェを開拓する人は私だけで十分です。研修に集中してください。 passage_coffee 読書 小説、技術書、ビジネス書を満遍なく読んでいます。特に森見登美彦さんの小説が好きですね。 最近読んだ本は。 理論から学ぶデータベース実践入門 ―― リレーショナルモデルによる効率的なSQL WEB+DB PRESS plus オブジェクト指向UIデザイン──使いやすいソフトウェアの原理 ホモ・ルーデンス文化のもつ遊びの要素についてのある定義づけの試み こんな感じでかなり雑食です。 勉強会 お手軽に同業界の人と絡める場所です。その上知識もキャッチアップできる。懇親会などでは無闇に人と関わっていたら話したい人に時間を使えなくなってしまうのでその辺りは気をつけたいところですね。というか社会人になれば関わる人を選ぶのは皆やっていると思います。 勉強会は若手向けのLT会や興味のあるミートアップに参加することが多いです。自社であるイベントはマストで参加してます。 またビジネスサイドの同期も含めてと勉強会を開く機会があったんですが、エンジニアリング以外の知見も知れるのでかなり良かったです。 学生時代 N高 なんと私はN高の二期生です。昔話題になってたのが懐かしいですね。N高はドワンゴの運営する通信制の高校です。 プログラミングを始めたきっかけはYouTubeのバックグラウンドがどうなっているかが気になったことなのですが、入門自体はN高の課外授業でした。なんかドワンゴの新卒の研修と同じらしい内容を一年かけてやりました。その結果、プログラミングにのめり込み、自主的に色々開発する人間になっていました。二徹とかしました。とはいえ三年間で自作言語まで作れるようになったのでヨシ!高校生はとにかく時間をかければ上手くなるということがわかりますね。 高校生時代は就活は特に意識せずに作りたいものを片っ端から作成していました。が同級生はインターンをして働いていたので、自分も高校生ながらインターンに応募し面接を受けていました。まぁ受かりはしなかったですがもともと人見知りでコミュニケーションも苦手だったのでコミュニケーション能力がかなり改善しました。たまたまですが。 サイバー大学 色々あり通信制の大学に進学しました。 エンジニア以外のことをしたくなさ過ぎてアルバイトの経験がなく、いきなりエンジニアのインターンをしていました。スマートキャンプは二社目です。珍しいですね? 大学に入ってから外部に顔を出す機会が増えました。初めは友達と行ったLT大会で、そこから徐々に慣れていきました。ちなみに私は自作言語や自作Gitの話を発表しました。 外部に何かを発表したい場合友達とLT大会を配信するのは非常におすすめです。この辺りからセルフブランディングを意識し始めました。 Zenn も始め、 ポートフォリオ も公にし始め、友人にセルフブランディングが上手な人がいたので参考にし。まぁ上手くは行かなかったのですが外部に向けてとにかく露出しようとは意識していました。 就活に関しては一年生の最後あたりにチーム開発や面接で話しやすい、就活を意識したプロダクトを開発していました。本来ならばそんなこと意識したくないですがまぁ通信制の大学なので……。そして前年度のスケジュールを把握しておきたかったため二年生の九月に、当時の三年生向けの説明会に参加していました。就活を始めるのは早ければ早いほどいいですからね。実際は周りの環境に寄りますが私の環境ではこれが最適解でした。 就活後は努力とはなんぞやという哲学的問題にぶつかります。私は目標に対して必然的に必要になるものを勉強し行動してきたので努力をしたという意識がかなり希薄です。というか努力嫌いです。そのせいで趣味に対しての努力も熱意も少ないので箸にも棒にもかからないという……。この問題は特に解決はしておらず。 ポートフォリオについて 私はポートフォリオをコミュニケーションのきっかけとして扱っています。凄そうなことをしていると凄そうに見えるじゃないですか。大体ポートフォリオを見せると自分に興味を持ってくれます。 ポートフォリオに載せるプロダクトとしては最後まで完成してないものも多いです。ぶっちゃけ完璧に完成させる時間はないので現在地点を完成としてポートフォリオに掲載しています。私は学生時代のプロダクトにおいて重要なのは完成させることよりも、どの時点を完成とするかだと思っています。じゃないとポートフォリオに掲載できないですからね。 因みに掲載しているものとしては自作言語・自作ゲーム・自作Git・etc. です。技術において特に共通点はないですが私のエンジニアリングの考え方において核になるものに沿って作成しています。核になるものについては下記のキャリアの核と同じものです。 ポートフォリオは自分が核に持っているものを説明するための根拠となるので、できればたくさん作品を掲載しておきたかったわけです。 私のポートフォリオ portfolio インターン時代 京都でインターン 大学三年生の10月にスマートキャンプの京都開発拠点のインターンとしてジョインしました。スマートキャンプの内定者インターン以外だと初インターンらしく、京都拠点にまだ三人しかいない時期で自分が四人目として入りました。 私は和歌山在住のままだったので一ヶ月に一回京都に出社するという生活送っていました、もはやほぼ旅行です。ホテルに泊まったついでにカフェ巡りとかしていましたしね(小声)。 そんな生活をしていて森見登美彦さんの影響もあって京都を大好きになってしまいました。真夏の気温40度の中歩き回るくらいには好きです。 何してた? 最初の方の京都のインターンでは基本的に研究開発を行なっていました。調査タスクが多め、実装タスクが少なめで社内向けのAIツール群を複数人で担当しており、AIモデルの調査を行い利用可能そうだったらツール群に実装というサイクルを爆速で繰り返していました。手をとにかく動かしていたのでツール群の5/8程度は私が担当し実装しました。 四年生では業務内容が変わり業務効率化をするためのプロダクト作成していました。UI周りを担当しており一日でモックのUIを実装したりなどかなりスピードを重視していました。一社前の経験から雑でもPRを投げて皆で議論した方が良いものができるのは分かっていたのでどんどんタスクを消費してPRを投げていました。 業務時間内にタスクを消費しきるのに頭をかなり働かせた思い出があるので、成長的にも非常に良い環境だったと思います。キャリア像も卒業前にはふわふわしていたものが無くなり固まっていました。 chat スマートキャンプの面接 インターン経由で本番の面接に行ったので通常の選考フローではなくいきなり二次面接からでした。面接で話した内容はもう余り覚えていないのですが一つだけ印象に残っていることがあります。最終面接でスマートキャンプのCEOが自作ゲームを遊んでくださったことですね。感想としては難しいとおっしゃってました。 確かに私は難しく作りました、自覚しております。 game スマートキャンプに入った理由 スマートキャンプを志望した理由としてはビジネス方面を向いてエンジニアをしたかったためです。内定した当時はプロダクトエンジニアという言葉がまだ日本に来てなかったのでこういう表現をしていましたが、プロダクトエンジニアに近いです。 私はあくまで自分のやりたいことがあってプロダクトに関わるうえでの最適解がエンジニアだったという考えをしています。本来やりたいのは人の感情を動かすことです。 人の感情を動かしたいのは他人の人生に影響を与えたいからで、自身の人生でも感情により良い影響を与えられてきました。 キャリアの核となっている、人の感情を動かしたいということを達成し、業務の効率化が好きであり、プロダクトに近い場所で意思を反映できるのを満たしているのがスマートキャンプでした。 現状目指しているところ 上記にあるようにキャリアとしてはプロダクトエンジニアを目指しています。 私は人の感情を動かしたいという核があり、方法としてプロダクトに自分の意思を反映させたいです。そのためにはプロダクト自体を深く理解をして、そのプロダクトにとって良い方向を理解し提案をしなければなりません。 さらに提案を達成するためには技術的な知識は大幅に必要とし技術以外の知識も必要とします。必然的に勉強量も交流も増やさなければならないです。まぁエンジニアを職業に選んだ時点で一生勉強だとは思うのですが、自分と同じような人はたくさんいるのでその人達より何かしないといけないんですよね。 とはいえ新卒にできることはまだ少ないです。トレーナーの方から新卒はとにかく量だと聞いたので、社内外で行動量を増やすことを意識して働いています。しんどいし、社内だけでいいやと最初思いましたが社内で仕事を頑張るのは皆やっていると思うので……。 社会人楽しいよ! 何というかこう仕事を中心にすればあとは好き勝手できるんですよね。5時に起きて7時くらいにカフェに行ったり、散歩したり。昼休みに会社の周辺のカフェを開拓して周りに布教したり。本はいつでも読めます。 何が言いたいかっていうと一日の中で予定がかなり固定されていますがまだノリで動けます。それさえできれば社会人は楽しいのです。
アバター
はじめに バージョン詳細 作業内容 Railsバージョンアップ Rubyバージョンアップ new_framework_defaults_7_1.rb 大変だったこと&ハマったこと config.load_defaultsの値が5.2だった ローカルのsecret_key_baseの保存場所が変わっていた CircleCIでの"bundle install"ができなくなった 良かったこと&助かったこと テストコード 新しいデフォルト設定値 最後に はじめに こんにちは!スマートキャンプ開発エンジニアの末吉(だいきち)です。 今回は、BALES CLOUDにてRailsとRubyのバージョンアップを行いましたので、 そちらについてお話ししたいと思います! バージョン詳細 具体的なバージョンについては、以下の通りです。 Ruby on Rails: 7.0.2 -> 7.1.3.2 Ruby: 3.0.4 -> 3.3.0 作業内容 今回行った作業の概要は以下の通りです。 Railsバージョンアップ 5.2 ~ 7.0 までで変更されたデフォルト値を確認 。(詳細は 大変だったこと&ハマったこと を参照) Gemfile > rails のバージョンをあげる。 rails app:update の実行。 rails app:update によって発生したコンフリクトを解消。 config.load_defaults を 5.2 -> 7.0 に更新。 RSpecの結果を確認しながら、バージョン起因で動かなくなっているgemを適宜アップデート。( bundle update {gem名} ) ※ 最初は bundle update ですべての依存gemを一度にアップデートしてみたのですが、バージョンが大きく変わるgemが存在しており、修正内容が大きくなりそうでした。 そのため、最低限のみのアップデートに切り替えました。 RSpecが落ちている箇所を確認し、コードを修正して動くようにする。 config/initializers/new_framework_defaults_7_1.rb の設定を1つずつ有効にしていく。 (設定値の変更が必要なものは、 config/application.rb で該当の設定を記述する。) config/initializers/new_framework_defaults_7_1.rb を削除し、 config.load_defaults を 7.1 に更新する。 Rubyバージョンアップ Dockerfile および Gemfile のバージョンを更新する。 CircleCIでの buldle install コマンドに、環境変数 GRPC_RUBY_BUILD_PROCS の追加。(詳細は 大変だったこと&ハマったこと を参照) new_framework_defaults_7_1.rb Rails 7.1で変更されるデフォルト設定値について、今回いくつか設定変更したものがあったため記載しておきます。 config.active_record.marshalling_format_version この方法でシリアライズされたモデルは古いバージョンのRails(< 7.1)では読み込めなくなります。 上記記述の通り、新しい設定値にすると、万が一7.0に戻したくなった場合に苦労すると思われます。 そのため、初期段階としては 6.1 (7.0以前と同じ動作)とし、7.1状態でしばらく運用後、7.0へ戻すことが100%ない状態となってから更新することとしました。 config.active_support.use_message_serializer_for_metadata この方法でシリアライズされたモデルは古いバージョンのRails(< 7.1)では読み込めなくなります。 上記記述の通りであり、こちらの設定値も前項目と同様に、7.0へ戻すことが100%ない状態となってから更新することとしました。 config.active_support.message_serializer 7.1のデフォルト値( :json_allow_marshal )状態で ActiveSupport::MessageEncryptor を使って暗号化したものは、7.0では正常に複合化できませんでした。 新しい値( :json_allow_marshal )にすると、7.1である限りは問題ないのですが、万が一7.0へ戻す必要が出てきた場合に苦労すると思われます。 そのため、初期段階としては :marshal (7.0以前と同じ動作)とし、7.1状態でしばらく運用後、7.0へ戻すことが100%ない状態となってから更新することとしました。 大変だったこと&ハマったこと config.load_defaultsの値が5.2だった バージョンアップ前、Railsは7.0だったのですが、 config.load_defaults は 5.2 でした。 さすがに今回のタイミングで最新に更新したく、変更内容を確認していったのですが、それなりのボリュームがあり割と大変でした。 Railsバージョンアップの際は、 config.load_defaults までしっかり対応するようにして、放置しないようにすることをオススメいたします! ※ もし、過去分の config/initializers/new_framework_defaults_x_y.rb を消してしまっていて存在しない場合は、 こちら に各バージョンごとのデフォルト設定値一覧が載っているので、参考になるかと思います!(BALES CLOUDでは消してしまっていた模様で存在しなかったので、こちらを参考に確認しました!) ローカルのsecret_key_baseの保存場所が変わっていた BALES CLOUDでは ActiveSupport::MessageEncryptor を使って暗号化している処理があるのですが、7.0で暗号化したものが7.1で複合化できない事象が発生しました。 いろいろ調べて見ると、7.1からローカルでの secret_key_base 保存場所が変わっているようでした。 そのため、新しいファイル( tmp/local_secret.txt )の値を古いファイル( tmp/development_secret.txt )の値で上書きすることで、解消できました。 [参考] Rails 7.1: ローカル環境のsecret_key_baseの保存場所がcredentialsに変わる(翻訳)|TechRacho by BPS株式会社 CircleCIでの"bundle install"ができなくなった Rubyのバージョンアップを行なうと、CircleCIで Received “killed” signal のエラーが発生し、 bundle install が最後まで到達しない問題が発生しました。 以下を参考に、 GRPC_RUBY_BUILD_PROCS の環境変数を追加することで解消できました。 例) command: | bundle install --jobs=4 --retry=3 --path vendor/bundle environment: GRPC_RUBY_BUILD_PROCS: 4 [参考] Florian Josef Reheis - CircleCI - Received killed signal 良かったこと&助かったこと テストコード 今回コード修正が必要となった箇所のほとんどを、テストコードで検知しました。 テストコードで検知できたおかげで、開発プロセスのより早い段階で対応を行なうことができて、総工数の削減ができました! テストコードを書いてくれていた現在および以前の開発メンバーに感謝です! 新しいデフォルト設定値 実は一度本番リリースの際に想定外の挙動が見つかり、リリースを中止したことがありました。 要因としては新しいデフォルト設定値が関係していたのですが、事前のデフォルト設定値確認で怪しいと思いながら新設定値を採用した箇所であったため、すぐに原因について見当がつき迅速に対応ができました。 今回 config.load_defaults が最新になっていなかったため(詳細は こちら ) 、確認に時間を要しましたが、疎かにせずに確認してよかったかと思います! 最後に 長くなりましたが、これからRails 7.1またはRuby 3.3へアップデートする方へ、少しでもお役に立てれば幸いです!
アバター
はじめに やったこと わかったこと 話す力・聞く力ってすごく大事かも いいね!!なところ たくさんアウトプットした 貪欲に学べた もうちょっとなところ 〇〇したい!〇〇じゃないですか?が言えるようになる 遠慮している 知識・経験不足 伝えたいことを簡潔かつ正確に伝える 適した言葉・表現を探しながら話している 見切り発車で話し始めている 入社エントリを読み返して 2年目どうするの おわりに はじめに みなさんの今年の目標はなんですか? 自分の目標は 「好きなものは好きと言う」 です。23卒エンジニアのmarioです。 社内ではオタクであることを隠し通したかったんですが無理でした。隠したそばから滲み出ます。だったらもうオープンにして繋がりを作っちゃおうと思った次第です。 さて、今回は前回の入社エントリを書いてから約1年経過し、この1年の振り返りをブログとして発信する機会をいただけたので前回同様、自分語りしていきたいと思います。 拙い文ではありますが最後までお付き合いいただけると幸いです。 やったこと 普段の業務は BOXIL SaaS における新機能の開発、バグ修正などがメインに行っています。たまに開発改善としてコードのリファクタリングやもう使われなくなった機能の削除対応なども行なっています。 その他にも最近だとインターン生のサポートなども任せられるようになりました。すごく緊張します。 また、今期から自分のやったことを集計するようにしてみたのですがおおよそ半期経過時点で以下のような結果になりました。 FY24でやったこと 一時期、レビューを最優先で行なう施策に取り組んでいました。 その施策が終わってからも自分のタスクよりもレビューを優先するようにしていたのですがこのように数字に現れるとなんだか嬉しいですね。本当に頑張れていたんだと実感が湧きます。 集計は今後も続け、数字に誉められるように頑張っていきたいです。 わかったこと 話す力・聞く力ってすごく大事かも 1年間でわかったこと。 話す力・聞く力ってすごく大事かも です。 BOXIL SaaS 開発チームではスクラム開発を導入しているため、デイリースクラムやレトロスペクティブ、プランニングなどで必然的にコミュニケーションの機会が多くなっています。 加えて、それらのイベントのほとんどはオンラインオフィス上で行われるので音声でのコミュニケーションになります。(カメラもONにはしているが資料などを見ているので音声ベースになりがち) そのような環境下で円滑に連携をとりながら開発を進めていくには話す力・聞く力が不可欠になってきます。 話す力・聞く力ってなんなんでしょうね?僕はこう思います。 話す力:間を作れる、間を察して話し始められる、ゆっくり話せる、脱線しない、 繋ぎ言葉を多用しない 聞く力:聞きながら咀嚼できる、目の前の会話だけに集中できる、遮らない、定期的に認識を合わせられる こんな感じでしょうか。 ※学生の頃、だいぶ吃音に悩まされていたのでだいぶ吃音バイアスがかかってるかもしれません、あしからず。 学生の頃からそうだろうな〜とはうっすら感じていて、実際に1年間働いてそれが裏付けされたので「わかった」よりも「再認識」の方が近いかもしれません。 頭では分かっていても中々完璧にはできないです。ですがこれらのことを意識することでチームの心理的安全性向上にも繋がりますし(基本リモートだし尚更)、常日頃から意識していきたいお気持ちです。 いいね!!なところ たくさんアウトプットした 1年を振り返って自分を褒めたいところ1つ目。 たくさんアウトプットした です。 自分は何か物事を咀嚼して、理解するまでに結構時間がかかってしまいます。脳内だけでそれを完結させようとするともう終わりが見えません。エンドレス。 なので FigJam や、 Notion を使って図示したり、整理したりしていました。 目に見える形で残しておくことによって、後から人に教えるときや自分で振り返るときなどにすご〜く役立ちます。 今ではだいぶ育ってきて見返すたびにニヤけてます。嘘です。 いいね!自分! 1年間育てたFigJam 貪欲に学べた 1年を振り返って自分を褒めたいところ2つ目。 貪欲に学べた です。 誰しも得意な分野、苦手な分野はもちろんあります。ですが、嫌いなものは少ないに越したことはないはずです。 自分はこのように考えました。 好きな分野は 一応得意ではありますと言えるレベル を、苦手な分野は 人並みレベル を目指せばいいじゃん! 好きな分野に全振りしてもいいのですがそのままだと苦手な分野はずっと伸びません。それならちょっと、ほんのちょっとだけ苦手なことを頑張って人並みレベルを目指した方が良いと自分は判断しました。今自分が身を置いているwebの世界では1つの分野だけ伸ばしても、いずれ頭打ちになることをこの1年で学んだからです。学ぶことたくさん!!! こうではなく… こう!! いいね!自分! もうちょっとなところ 〇〇したい!〇〇じゃないですか?が言えるようになる 1年を通してもう少し頑張れそうだったところ1つ目。 〇〇したい!〇〇じゃないですか?が言えない です。 MTG等で他の意見に同調しかできない自分に嫌気が差しました。 同調するだけでは良いモノは作れないはずです!たぶん! なぜこのようになってしまうのか、考えてみました。結論が出ました。以下のとおりです。 遠慮している 知識・経験不足 遠慮している 割とありがちな問題かもしれません。「年齢的にも勤務年数的にも1番下である自分が口出しして良いのか?」と萎縮してしまうのです。意見を出して実際に睨まれたのであれば致し方ないかもしれないですが自分の場合は完全な思い込みによるものです。 この問題に対するアプローチはかなり難しいですが1on1を重ね、心理的安全性を高めたり、日頃から自分の意見を持つことを意識し発言に対するハードルを下げていこうと考えています。 知識・経験不足 意見を出そうにも話されている物事に対する理解がなければ中身のない薄いものしか出てきません。 単純ですが臆せずさまざまなことにチャレンジし、知識の定着をインプット・アウトプットの繰り返しによって図っていきたいです。省みると今までの自分が内気すぎた..。 伝えたいことを簡潔かつ正確に伝える 1年を通してもう少し頑張れそうだったところ2つ目。 伝えたいことを簡潔かつ正確に伝えられない です。 自分はよくMTG中に「アレはアレなんでアレってことですよね?」みたいなことを言ってしまうときがあります。何を言ってるんでしょうねこの人は。 先ほどと同様に原因を考えてみました。結論が出ました。以下のとおりです。 適した言葉・表現を探しながら話している(語彙が乏しい) 見切り発車で話し始めている どうやら世の中には見切り発車で話し始めても話しながらステキな文章を構成できる人もいるみたいなんですが自分はそうもいかないみたいです。 適した言葉・表現を探しながら話している 単純ですが活字にたくさん触れることで徐々に語彙を増やしていきたいと考えています。 ちなみに最近は伊坂幸太郎の『 クジラアタマの王様 』を読みました。「胡蝶の夢」とか「一瞥をくれる」とか「訝しむ」とか、自分の知らなかった言葉がたくさん出てきて調べながら読むのが楽しかったです。 見切り発車で話し始めている 事前に内容をまとめたり、図示したりすることによって要領よく、時には文章以外のアプローチでも伝えたいことを伝えられるようにしたいと考えています。 ただ実際、急に意見を求められて考える時間がないときなど、どうしようもない場面は少なくないと思いますが少しずつそういった場面にも対応できるようにしていきたいですね。 入社エントリを読み返して 入社エントリは こちら いや〜恥ずかしいこと書いてますね。何ですか「青いバナナくらい未熟」って。 入社エントリは入社経緯だとか自己紹介だとかに重きを置いてたのであまり比較できそうなこと書いてなかったんですが1番最後にいいこと書いてました。 周りよりも経験が少ないと卑下するのではなく、自分には周りよりも時間があるんだと、そう捉えてポジティブに生きていきたいと思います。 ...全然できてないかも!めっちゃ卑下しまくってた! 本記事でも触れたとおり 、未だ自分ができていないことの1つです。そういう性格なのかもしれないです。 2年目こそはポジティブに生きていこうと思います。 2年目どうするの 後輩もでき、コミュニケーションに悩む日々は変わらなそうです。(周りの問題ではなく自分が苦手なだけ) 2年目どうするかですが、今回の振り返りからスローガンを立てました。(スローガンという言葉、中学校の文化祭くらいでしか使わない気がします。) たくさんチャレンジして自信を持てるようになりましょう 得た知見は溜め込まず、外部に発信しましょう ロジカルシンキングで心地よいコミュニケーションを心がけましょう 好き嫌いせずになんでも学びましょう 忘れないように毎日音読します。 おわりに 今回の内容はエンジニアとして、ではなく社会人1年目としての振り返りに近かったような気がします。 また来年、振り返りの時期にこの記事を読み返すと思います。2年目はエンジニアの視点から振り返ることができたらいいな。(2年目が終わって振り返り書いてる僕へ。元気してる? 好きなものは好きって言えた?) 最後に、ここまでお付き合いいただき、ありがとうございます!! また何か機会があればお会いしましょう!!
アバター
ご挨拶 経歴とスマートキャンプに入社するまで 料理人からエンジニアへ エンジニアになってからスマートキャンプに入るまで 業務委託で 雑なメモをとる タスクの解像度を上げる 余力を残す (番外編)メモはちょっと楽しくなるようにとる 気づいたらスマートキャンプへ入社 入社した理由 入社してから変わったこと これから色々やってくぞーーー!!!! ご挨拶 こんにちは!2024年3月にジョインしました池上です! 以前は料理人として働いていましたが、現在はエンジニアとしてスマートキャンプに所属しています。このブログでは、僕のこれまでのキャリアの変遷と、フリーランスでジョインしてスマートキャンプで正社員になった経緯についてお話ししようと思います。 フリーランス目指している方や転職を考えている方の参考になれば幸いです。 経歴とスマートキャンプに入社するまで 料理人からエンジニアへ 日本料理学校を卒業したのち懐石料理の企業に入社し、料理人として約2年ほど働いた後に、エンジニアへキャリアチェンジをしました。 初めは趣味としてプログラミングに興味を持ち、独学で学んでいました。学習に詰まる部分も多くありましたが、四苦八苦して書いたコードが実際に動いているのを見ることに楽しさを感じ、エンジニアへキャリアチェンジをしました。 エンジニアになってからスマートキャンプに入るまで エンジニアになってからは、SESや受託の企業でWebに限らずたくさんのプロジェクトに関わりました。 「株式情報の管理社内システム」「クレジットカードシステムのリプレイス」「プレスリリースサイトの運用・開発」…etc その中でJava, Python, JavaScript, Dart, Rubyなど幅広く技術に触れました。 その経験を活かして貢献するとともに、エンジニアリング以外の経験の幅を広げるために2023年の6月にフリーランスになり、スマートキャンプへ業務委託としてジョインしました! 業務委託で 初のフリーランスで戸惑うこともありましたが、業務委託でジョインするからには「長期的に質の高いアウトプット+αを出し続ける」ことが、クライアントとの信頼の構築をするうえで重要だと思っていました。クライアントとの信頼関係は、長期的な契約を通じて安定した収入源を確保するための基盤であり、そのためにはただ納期を守るだけでなく、クライアントの期待を超える品質の成果物を提供し続けることが必要です。さらに、信頼があることで新たな領域を任せていただくことにもつながり、自己成長やスキルアップを図る良い機会にも恵まれると思っています。 そこで今回は、僕がどのようなことを意識して働いていたかについてお伝えしようと思います。 雑なメモをとる 長期的に質の高いアウトプット+αを維持するためには、必要な情報にすぐアクセスできること、そして経験から得た知識をプロジェクト全体に展開できることが重要です。メモを取ることは、これらのプロセスを助け、情報に対する「インデックス」を作成して迅速なアクセスを可能にします。 しかし、メモを丁寧に取ることは時間がかかります。そのため、効率を優先し” 雑な” メモの取り方を実践しています。雑なメモとは、他人に見せる必要がなく、15分後に見返したときに内容をある程度思い出せる程度のものです。この方法を意識することで、必要以上に詳細を書きすぎることを防ぎ、質より量を重視しています。 これをすることで、情報を素早く記録し、再整理に必要なデータを大量に蓄積できます。結果として、知識の横展開をより速く、効果的に行なうことが可能になります。 タスクの解像度を上げる 新しいタスクに取り組む際、その背景や関連情報を十分に理解することは、システム全体を考慮した設計ができ、タスク外の情報を得る手助けにもなります。タスクの解像度を高めることで、単なる個別作業を超えた視点を持ち、プロジェクト全体の質を向上させ、将来的な課題への対応も可能になります。 タスクに対する理解を深めるためには、他人に説明できるレベルまで情報を整理し、テキストや図で具体化することが効果的です。ただし、プロジェクト参画後1〜2ヶ月は、すべてを詳細に理解するのが難しいため、初期段階では大まかな理解に留めてタスクに臨むことがあります。 このような作業は、”長期的”に”効率的”で”高品質”なアウトプットを生み出すうえで不可欠だと思っています。 このような形で視覚でイメージできると解像度がかなり上がります。考慮もれも細かく見つけられるのでおすすめです! 活用イメージ 余力を残す すべての稼働時間を埋め尽くすようにタスクを割り当てがちですが、実際には30分から1時間の空き時間を意図的にタスクの調整をしています。この余裕を持つことで、緊急の問題への対応だけでなく、+αのアウトプットを出すための時間を確保できます。 空いた時間を利用して解像度を上げる作業やメモの整理を行なうことで、次のタスクに役立つ資料を作成します。また、この時間を使ってプロジェクト内で感じている課題、例えば不安定なテストの修正や開発フローの改善など、プロジェクト全体の効率を向上させる活動にも充てることができます。 このように余力を残すことは、緊急時の対応を容易にするだけでなく、プロジェクトの持続可能性と効率を根本から向上させる戦略となり、計画的に余裕を持たせることでより柔軟かつ効果的に進行が可能になります。 (番外編)メモはちょっと楽しくなるようにとる 正直、メモを取るのは面倒なこともありますよね。だからこそ、メモのタイトルで遊んだり、楽しいフレーズを加えることで、気分転換を図っています(笑)。 このちょっとした工夫で、メモを取る作業自体が少し楽しいものに変わります。僕のモットーは「楽しく仕事する」こと。何となくでも楽しい気持ちになる方法をおすすめします! 初めてのフリーランスでのジョインで戸惑う部分もありましたが、このようなことを意識した結果、フィードバックの場では「期待以上の貢献をしてくれた」とお褒めの言葉もいただけました! こんな感じでタイトルで遊びます(笑) メモ 気づいたらスマートキャンプへ入社 入社した理由 今回、スマートキャンプに入社したいと思ったのは、「開放的で協力的なカルチャー」に興味を持ったからです。 週に1度の振り返りMTGでは、業務の話だけでなく「口内炎の数が増えた」や「美容師難民になっている」など、ユーモアのある話題も交えながら進むので、これまで多くのプロジェクトに参加してきましたが、ここまでフランクで雰囲気がいいチームは初めてです。 振り返りイメージ 入社してから変わったこと 正社員になってからは、見渡せる範囲が広がり、プロダクト改善のためにより積極的に動くことができるようになりました。 情報の流れも増え、システム作りだけでなく組織作りにも近い位置で関与できるようになったことは、非常に楽しく、多くを学ぶことができます。 業務委託では参加できなかった全社会や AWARD(半期の振り返りLT会)などのイベント にも参加するようになりました。これにより担当する業務範囲が広がりましたが、それに伴う経験は大変貴重で、仕事に対する意欲をさらに高めています。 これから色々やってくぞーーー!!!! 今後は、新規PJをさらに成功させることはもちろんのこと、チームの成長にも寄与できるよう尽力していきたいと思います。今までの経験を活かしながら、たくさんの新しいことも学んでより良いアウトプットを出していきたいと思います!!!! また、新規プロジェクトはユーザーにとって価値あるものを提供し、技術的にも革新的な取り組みや負債の解消を目指すものです。エンジニアとしてこのプロジェクトに大きく貢献できることを楽しみにしています!
アバター
はじめに 想定読者 背景 ABDとは 進め方 メリット 実際の進め方 効果 参考: 実際に使った資料 まとめ はじめに 皆さんこんにちは、米元です。 ここ半年ほど生成AI等を用いた社内業務の生産性向上プロジェクトに取り組んでいますが、そのプロジェクトチームで最近試した勉強会がとてもよかったので紹介したいと思います。 想定読者 リモートワークでの勉強会の手法を知りたい人 勉強会の準備が大変で続けられないと悩んでいる人 積読を消化したい人 いろんな人の観点を取り入れて効率良く学びたい人 背景 現在私が関わっているプロジェクトは以下の図のようにメンバーが東京と京都という複数の拠点に分かれています。 チーム体制 特に京都開発拠点の出社日(現在は週2日程度)は私は東京のオフィスまたは自宅からリモートで京都のオフィスメンバーとやりとりを行なうという体制です。 そのため普段のコミュニケーションはリモートを前提としており、ツールとしてはSlack・Google Meet・Figma等を利用しています。 この体制で業務を遂行することに基本的には大きな問題は無いものの、チーム作りにおいて以下のような課題を感じていました。 リモートメンバーとオフィスメンバー間の情報共有が不十分になりがち オンラインでのコミュニケーションでは非言語的なニュアンスが伝わりにくい コミュニケーションの頻度や質に差が出る可能性がある 特に私自身はチームをマネジメントする立場のため、1人だけリモートの状態で他のメンバーに対してプロジェクトの方針や進め方・それらの軸となる考え方を細かく共有したり、メンバーの考えていることをキャッチアップすることが難しく感じていました。 その対策として、勉強会を実施して知識の共有やプロジェクトの進め方についての認識を擦り合わせることにしました。 ただ勉強会と言っても座学形式でリモートで一方的に話すだけだと集中しづらいし、頭に入りづらいです。また、勉強会によって共通認識が作れたかを把握することも難しいです。 また、(これはハイブリッド環境かどうかは関係ないですが)勉強会の準備が大変過ぎると開催自体が億劫になり続けられないなどの問題がありました。 何か良い進め方が無いかいくつか勉強会の事例等を調べていくうちに、 「アクティブ・ブック・ダイアローグ®(以下ABD)」 というものの存在を知り、今の体制向けにカスタマイズして運用してみました。 ABDとは ABDとは複数人で行なうワークショップ形式の読書手法です。 以下は開発者の竹ノ内壮太郎さんによる紹介の引用です。 アクティブ・ブック・ダイアローグ®は、読書が苦手な人も、本が大好きな人も、短時間で読みたい本を読むことができる全く新しい読書手法です。 1冊の本を分担して読んでまとめる、発表・共有化する、気づきを深める対話をするというプロセスを通して、著者の伝えようとすることを深く理解でき、能動的な気づきや学びが得られます。 またグループでの読書と対話によって、一人一人の能動的な読書体験を掛け合わせることで学びはさらに深まり、新たな関係性が育まれてくる可能性も広がります。 引用元: https://www.abd-abd.com/ 進め方 おおまかな流れは以下のとおりです。 1冊の本を複数人で分担して読む それぞれが読んだパートを要約する 要約した内容を順番にプレゼンする 感想や疑問点について話し合う 振り返りする これによって1人で読書をするよりも短い時間で内容を把握でき、また参加者それぞれの視点や考えから多くの気づきが得られます。 メリット また、同サイトにはABDの8つのメリットも紹介されています。 短時間で読書が可能 サマリーが残る 記憶の定着率の高さ 深い気づきと創発 個人の多面的成長 共通言語が生まれる コミュニティ作り 何より楽しい! ただABD公式のマニュアル(191017ABDマニュアルver1.5: 公式サイトからダウンロードできます)に記載されている標準的な進め方はオフライン前提で、かつ時間もチェックイン・チェックアウトを含めると150分程度とそれなりのボリュームです。 以下は公式マニュアルに記載されているアジェンダです。 1.オープニング (20分~) 1-1.チェックイン(1分×人数) 1-2.オリエンテーション (10分) 2.メイン (120 分~) 2-1.サマライズ(30~60分) 2-2.リレー・プレゼン (2~3分 × 人数) 2-3.ダイアローグ (30~60分) 3.エンディング(10分~) 3-1.チェックアウト(1分×人数) 一方でリモート(私)とオフィス組(京都メンバー)とのハイブリッドな環境であること・業務時間内に実施することを考慮するとそのまま現在のプロジェクトに適用するのは難しそうです。 公式マニュアルには以下のような記載もあるため、自分達に合った進め方に変えてみることにしました。 ※上記は、スタンダードな時間と進め方を記入しています。時間は本の内容や人数規模によって大きく 変わります。また、現在でも、様々な工夫やパターンが誕生しています。ぜひ皆さんもこの型にこだわらず、 工夫してみてください。そして良いものが生まれたら、ぜひ教えてください。 実際の進め方 前提として拠点が東京と京都に分かれているためリモートで実施しました。 ツールはNotionとGoogle Meetを利用し、勉強会のアウトプットはNotionに集約しています。 事前準備としてはファシリテーターがNotionに勉強会のページを作成し、目的・進め方・対象書籍または対象資料・感想欄などを記載しておきます(テンプレート化すると良さそうです)。 また、少しでも多く読書や議論のための時間を確保するため、参加者の担当ページも「XXさんはP10〜P30まで」など具体的に決めておきます。 参加者は事前準備は不要としました。 おおまかな流れは以下のとおりです。 1. 目的の共有、進め方の説明 5分 2. 読み込みタイム 15分 3. リレー・プレゼン 25分(5分目安×5人) 4. ダイアローグ 15分 私達の場合は普段から同じチームで働いているということもあり、チェックイン・チェックアウトは省略しました。プレゼンやダイアローグを活性化したい場合や別チームのメンバーやいろんな会社の参加者がいる場合など参加者の属性やチームの状況に応じて変えた方がよいとは思います。 全体の時間についても検討しましたが、リモートでの長時間の勉強会は集中力が続かずダレてしまう可能性もあるので、60分で実施してみることにしました。 ただ60分で1つのコンテンツを読み切るのは難しいことが多いため、その場合は2〜3回に分けて実施しています。参加人数やコンテンツの内容にもよるかもしれませんが、リモートの場合は1回の時間は長くても90分くらいが限度かなという印象です。 また、時間が限られていることや各自が担当するページ数を少なめに設定したことから、読んだ内容の要約は省略し直接ページ内容を画面共有しながら説明する形式を採用しました。 事前に準備したアジェンダ もう一つ大きく変えたポイントとして、発表者以外のメンバーはプレゼンを聞きながら気になった部分や共感したポイントについてNotionにメモを取り、最後のダイアローグの時間でそのメモに対してさらに各自がコメント(Notionのコメント機能)をする形式にしました。 疑問点や共感ポイントとそれらに対するコメント 効果 良かった点は以下のとおりです。 気になっていたコンテンツを短時間で読み込めた あらためて他の人に説明することで理解が深まった 他の人の意見や考えを聞くことで新たな気づきが得られた グループで同じものを読み、議論することで共通認識・共通言語ができた 他の人の説明を聞きながら自分の感想や疑問をコメントする形式だったので集中して聞くことができた 他のメンバーからもポジティブな意見が多く、やってみて良かったなと思っています。 改善点があるとすると、参加者がオンラインでのワークショップのような高い熱量にはならなかったという点です。コミュニティ作りを目的とする場合はもう少し全体の時間を長くとり、チェックイン・チェックアウトも十分に行った方が良いかもしれません。 参考: 実際に使った資料 実はここまで紹介したカスタマイズ版ABDでは書籍は使用しておらず、Web上に公開されているスライドなどの資料を使用しました。実際に使用させていただいた資料の一部を紹介します。 speakerdeck.com 株式会社コパイロツトさんが公開しているスライド。 リモートでの会議やプロジェクトの進め方について、共通認識を作るために使用しました。有料書籍として販売できそうな質とボリュームです。リモートワークをしているすべての人に読んでもらいたいくらいの神資料だと思います。 speakerdeck.com 東京大学の馬田さんが公開しているスライド。 仮説検証の進め方について、チーム内での知識や考え方を揃えるために使用しました。 こちらはご存じの方も多いかと思いますが書籍も発行されており、より理解を深めるためにもあらためて書籍版で実施してみたいと思っています。 まとめ 以上、ABDを自分達の環境に合わせてカスタマイズした内容の紹介でした。 同じような課題に悩む皆さんの参考に少しでもなれたら幸いです。 今回は自チームのメンバーのみでの実施でしたが、業務で関わりのある他部門のメンバーと一緒に行なうことで事業部内での共通認識や共通言語を作ったり、社内での事業横断的なコミュニティ作りにも活用できそうです。 そのうち公式マニュアルに沿った形(オフライン)でのABDもやってみたいなと思いますので、社内外問わずこの記事を読んでABDに興味を持たれた方はぜひお声がけください。
アバター
何をしたのか? やったこと 前提 0. ねんのためステップ1〜10を確認 1. braking changes x eslint 1-1. eslint-plugin-vueの導入 1-2. 自動修正 1-3. ルールの一時緩和 1-4. 残りを手動修正 2. Vue依存パッケージのアップグレード Nodeをアップグレードしておこう 3. Migrate Buildを除去 4. 動作確認 5. リリース おまけ: どれだけかかったの? おまけ: vue-i18nについて 〆 毎度どうも。 BALES CLOUD (以下BC)エンジニアのてぃがです。 Vue2のEOL が昨年末に過ぎ去りましたね。 BCもそれに伴って、フロントエンドのVue3への移行を完了しました。 今回はその移行についてお話しします。 何をしたのか? 「移行ビルドを除去して、完全にVue3で動く状態にする作業」です。 Vueの移行ビルドについてはこちらの記事をご参照ください: Vue3にアップグレードしてフロントエンドを改善した話 - SMARTCAMP Engineer Blog ポイントを先に書いておきます。 できるだけ、先にNodeをアップグレードしておこう Element Plusには要注意 eslint-plugin-vueを活用しよう 「どうなればリリースしていいのか?」をしっかり握っておこう 工数をたっぷり準備しよう やったこと braking changesへの対応 eslintによる自動対応 手動対応 Vue依存パッケージのアップグレード Migrate buildの除去 動作確認 リリース 前提 移行作業は、基本的に公式の アップグレードのワークフロー に従います。 今回は移行ビルドの導入が完了しているため、ステップ11からの開始です。 また、移行作業はメイン担当者1人とその時々に手の空いているメンバーで対応しました。全体の作業量と効率のバランスをとった形です。 初期段階でわかった作業量を元にタスクを分割し、そのうちのいくつかを他のメンバーに対応してもらいました。 詳しくは後述しますが、移行ビルドは破壊的変更に1つずつ対応できるように設計されているため、タスクもこの粒度にあわせることで容易に分割できました。 0. ねんのためステップ1〜10を確認 移行ビルド導入時の詳細な作業記録が見つけられなかったのもあり、一応確認しました。 こういう作業は面倒でも1つずつ対応した方が、のちに「全然ダメだけど何が問題なのかわからん!」とならずおすすめです。 今回はステップ8が未対応だったので、ここで対応しました。 1. braking changes x eslint 破壊的変更を対応していきます。 eslint-plugin-vue のVue3対応版ルールを活用すると、作業を効率化できます。 (eslintについての説明は割愛させていただきます。ざっくりいうとコードの静的解析です。) 作業は以下の流れです。 eslint-plugin-vueの導入 自動修正 ルールの一時緩和 残りを手動修正 1-1. eslint-plugin-vueの導入 通常通り yarn add でプラグインを導入し、設定を書きます。 BCでは、eslintのルールは各種プラグインの推奨に従うという方針を設けています。 よって今回は vue/vue3-recommended を設定しました。 .eslintrc.js extends : [ 'plugin:vue/vue3-recommended', ] , 1-2. 自動修正 eslint --fix を実施します。 これだけで一部の破壊的変更が自動で修正されます。すばらしい。 1-3. ルールの一時緩和 さて、この時点で eslint をかけると、大量のerrorが発生します。 vue/vue3-recommended がVue3の破壊的変更をerrorとして定義しているためです。 errorの出ているルールをメモしておきつつ、Vue3関連はいったんすべてwarnに設定します。 (Vue3関係なくerrorになっているルールもありましたが、説明割愛。) .eslintrc.js rules : { 'vue/no-deprecated-destroyed-lifecycle' : 'warn' , // ... } , 1-4. 残りを手動修正 ここが一番時間と根気のいる作業です! 破壊的変更を1つずつ修正していきます。 破壊的変更のリスト と errorの出ていたルール を突きあわせて確認しながら進めます。 各破壊的変更ごとの作業手順は、ざっくり以下のような感じです。 1: 対応するeslintルールをerrorに戻す eslintrc.js rules : { // 'vue/no-deprecated-destroyed-lifecycle': 'warn', // 一時緩和を解除 // ... } , 2: 破壊的変更の修正 3: eslintでerrorにならないことを確認 4: 移行ビルドの グローバル設定 で該当機能の互換を無効化し、動作確認 main.ts configureCompat ( { OPTIONS_BEFORE_DESTROY: false , OPTIONS_DESTROYED: false } ) すべての互換処理を無効化したら完了です。 2. Vue依存パッケージのアップグレード Vue2に依存しているパッケージをVue3対応版にアップグレード、または代替パッケージへリプレイスして除去します。 package.jsonのパッケージを1つずつ、 真心込めて 依存性をチェックします。 「Vue3で動かなくなるかどうか」は案外npmの依存性だけでは判断がつかないので、その時は各公式のリリースノートとかを見ましょう。 対応した限り、一定のバージョン以上からVue3に対応しているものが多かったです。 対象のパッケージがわかったら、パッケージごとの対応内容(アップグレードorリプレイス)を決め、対応していきます。 アップグレード時に破壊的変更があれば、それも対応します。 この際、「いらないパッケージは単純に除去する」という選択肢も持つと良いです。将来のメンテナンスコストが安くなります。 要注意事項ですが、名前に vue がついているパッケージ以外も、Vueのバージョンに依存していることがあります。 各種SaaS用のライブラリなどが入っている場合は要注意です。BCではbugsnagなどが動かなくなりました。 Nodeをアップグレードしておこう ちなみにですが、BCでは当時Node14が利用されていました。 (もちろん、これがまずいことはわかっています。) これにより、一部パッケージでは一定バージョン以上からNode14のサポートが終了しており、アップグレードできないという問題が起きました。 BCではこのとき、Vue3対応を優先するため、Nodeのアップグレードは後回しにすることを決定しました。 影響のあったパッケージは、Node14サポートかつVue3対応の最低バージョンまでアップグレードすることで、なんとか対応できたためです。 しかし、これは当時の話です。 現在も、さまざまなパッケージでNode14/16のサポートが終了しつつあり、Nodeのアップグレードを後回しにすることはより困難になっていると思われます。 よって、Vue3アップグレードの前にNodeのアップグレードを完了することを強く推奨します。 また、「対応できた」と言ったものの、実はi18n関連の最新化のみこの問題によって対応しきれませんでした。 幸いクリティカルな問題ではなかったため、Nodeのアップグレードと合わせて後回しとすることになりました。これはおまけとして後述します。 3. Migrate Buildを除去 ここまでの対応で、Vue3への移行は完了しました。 最後に、パッケージ @vue/compat を除去します。 BCでは、ワークフローのステップ4で対応した型の加工を除く必要もありました。 この作業が完了した時点で動作確認し、対応漏れをつみとります。 BCでは、 Element Plus が大問題を引き起こしました...。 実はこのパッケージ、移行ビルドに対応していません。 うまく動かない箇所を個別に対応してなんとか動作させていたため、その対応を除くという作業が必要でした。 4. 動作確認 テストフェーズです! 動作確認の前に、「これをクリアすればリリースできる」という基準を設けておきます。 サービスにとって「クリティカルな問題」がなければリリース可能、という基準になるかと思います。 これがなんなのかはサービスによって異なりますが、例えば、ログインができない、商品が表示されない、などです。 基準が決まったら、まずはチーム総動員で動作確認をします。 今回の影響範囲はフロントエンド全般であるため、人海戦術が効きました。 検証用環境で、とくにテストケースは用意せず、全員でいわゆるアドホックテストを実施します。 その結果をまとめ、修正、再度全員で動作確認...というサイクルを、クリティカルな問題がなくなるまで回します。 次に、本番相当環境での動作確認をします。 サービスに「各種SaaS用のライブラリ」など、本番でしか動かないツールが入っていませんか? 忘れずに動作確認しましょう。いや、本当に...🤕。 5. リリース お疲れ様です、リリースです! 影響範囲が広いため、できるだけユーザーのいない時間帯にリリースするとよいです。 リリースが済んだら速攻で動作確認、あとはお祈りに移行します。 この時点でいくら頑張っていても、細かなバグは出るものです。 くよくよするよりも、なるべく早くバグを発見し、修正、速やかにリリースすることに注力しましょう。 前述の通り bugsnag などのツールを入れて、エラーを監視できる状態を作っておくのが吉です。 おまけ: どれだけかかったの? 丸三か月です。 移行ビルド除去の調査開始からリリースまでに、これだけかかりました。 通常業務と並行し、主に空き時間に対応していたこともありますが、軽くない工数だと思います。 覚悟と準備の上で臨みましょう。 おまけ: vue-i18nについて 前述の通り、i18n関連の最新化のみ後回しにすることになりました。 さっくりとだけ触れます。 あなたの環境に @intlify/vue-i18n-loader や vue-cli-plugin-i18n が入っているなら、その構成は古いです。 前準備として、Nodeをアップグレードします。 Node14や16が入っていたら、最低でも18まで上げましょう。 i18n関連のパッケージがNode18以上にしか対応していないためです。 今上げるならNode20くらいがおすすめです。 準備が整ったら、i18n関連の構成を最新にしましょう。 BCではこうしました。 Before After BCでの役割 vue-i18n vue-i18n i18n core @intlify/vue-i18n-loader @intlify/unplugin-vue-i18n i18n設定をファイルから読み込めるようにする vue-cli-plugin-i18n @intlify/eslint-plugin-vue-i18n i18nの静的検査 〆 まとめ&おさらいです! できるだけ、先にNodeをアップグレードしておこう Element Plusには要注意 eslint-plugin-vueを活用しよう 「どうなればリリースしていいのか?」をしっかり握っておこう 工数をたっぷり準備しよう いろいろ書いてきましたが、不可能なほど難しいことはなく、地道にやれば必ず完遂できます。 なにより、静的検査はあなたの味方です。eslintを信じ、一緒に歩みましょう。 今回は触れませんでしたが、Vue3にはComposition APIをはじめとした素敵な新機軸がたくさんあります。 早くこちらの世界に来てください。お待ちしております! 以上です。お読みいただきありがとうございました!
アバター
ご挨拶 これまでの経歴と出向に至るまで 新卒でSIerへ マネーフォワードへ転職 そして、スマートキャンプへ出向! スマートキャンプにジョインしてみて ユーモア溢れる開発チーム オンボーディングでの有り難いサポート 2ヶ月半でYATTEKITAこと チームジョイン後に意識して取り組んだ7つのこと 1. レスポンスのスピード感 2. Slackでのリアクション・スタンプ、ピアボーナスの活用 3. チームメンバーとの1on1 4. ドキュメントづくり 5. 気づきの丁寧な共有と、仕組み化の提案・実行 6. マネーフォワードとの積極的な連携、グループ知見の有効活用 7. なんでもござれの姿勢 チームの皆さんからの反応 これからYATTEIKUこと とある新規PJの成功と組織づくり IPOに向けて まとめ ご挨拶 はじめまして!2024年1月にジョインしました 黒沢 です! スマートキャンプでは、 BOXIL SaaS の開発に携わっております。 今回は入社(出向!?)エントリということで、 簡単な経歴や、スマートキャンプにジョインしてみてどうだったか、 入社して2ヶ月半でやってみたこと・これからのことなどをご紹介できればと思います。 ジョインしてすぐに取り組んだことは、この春に異動や転職をする方にも参考になれば幸いです。 これまでの経歴と出向に至るまで まずは、簡単に現在までの経歴を紹介したいと思います。 新卒でSIerへ 新卒では、大手SIerに入社しまして、システムエンジニア(SE)として政府系金融機関や中央省庁向けの実証実験やシステム開発に携わりました。 小さな技術検証案件から重厚長大な年単位のプロジェクトまで、要件定義・設計・実装・試験・保守運用を幅広く経験しました。 そんな中、社外の勉強会に参加したり、ベンチャー企業で楽しく働く友人の声を聞いたりする中で、徐々に外の世界にも目を向けるようになり、勇気を持って新しい環境へ飛び出す決意をし、転職活動を始めました。 マネーフォワードへ転職 事業会社でエンジニアをやってみたいという思いや、家計簿アプリ「 マネーフォワード ME 」のリリース当初からのユーザーであり、そのミッションやビジョンへの共感から、マネーフォワードにジョインしました。 入社当初は、バックオフィス向けSaaS領域において、「 マネーフォワード クラウド勤怠 」のサービス立ち上げに、エンジニアとして携わりました。 その後は、「 マネーフォワード クラウド給与 」の機能開発や、「 マネーフォワード クラウド人事管理 」のリリースにも関わりつつ、QA組織作りやSREの導入を進めながら、エンジニア組織の急拡大を主導し支えるべく、プロダクト開発チームのリーダーや、部長としてエンジニアリングマネージャーの役割に邁進しました。 このあたりの詳しい内容は、マネーフォワードのエンジニアブログやnoteに投稿しておりますので、是非ご覧ください。 HR領域急拡大で爆誕!立ち上がったQA・SRE組織や新サービスをふりかえる。Money Forward Developers Blog なぜ いまHR領域でQA組織を立ち上げるのか?立ち上げに込めた想い - note そして、スマートキャンプへ出向! マネーフォワードでの経験は、そのフェーズでしか体験し得ない貴重なもので、どれも素晴らしい財産になりました!そして、もはや戦友とも言える皆さんと出会えたことに何よりも感謝しています。 しかしながら、育休取得を経て、マネジメント層の採用も進み、一定の目処がついたこのタイミングで、次の方々へバトンを渡し、新たな機会をいただくことにしました。 スマートキャンプを含むマネーフォワードグループは、全体で2100名を超える組織規模であり、グループ内でのチャレンジ機会も多数あります。今回は、私も社内での異動だけでなく、グループ会社への参画という選択肢も含めて検討し、2024年1月にスマートキャンプへ出向という形でジョインするに至りました。 組織の規模や文化、技術スタック、事業領域など、マネーフォワードでの経験とはまた異なる挑戦ができると感じており、新たなステージにワクワクしています。 スマートキャンプにジョインしてみて 現在は、 BOXIL SaaS の開発チームにジョインして、半分プレイヤーに戻ったような状況です。まずは、そこで感じたこと・有り難いと思ったことを簡単に共有したいと思います。 入社のお祝いケーキをボスが用意してくださったものの 家族全員で体調を崩してしまい参加できなかった懇親会の一幕 ユーモア溢れる開発チーム ジョインしてみて、まず感じたのは、ウェットな組織雰囲気の中で、非常にユーモアがあふれるチームだということです。 20代〜30代前半を中心とした若いメンバーで構成されていますが、リモートワークでもオフィスでも、いつも冗談が飛び交い、笑いの絶えないチームで、とても楽しく思います。これは、チームメンバーの個々の人柄や、チームの文化によるものだと思いますが、それが相談しやすい雰囲気や、スムーズなコミュニケーションにつながっており、少なからずエンジニアリングにも良い影響を与えている部分もあると感じています。 加えて、オフィスに出社する機会をより有意義に活用しようと、出社日には技術選定の議論やワークショップの会などをメンバー同士で企画して取り組んでいます。これは、仕事を楽しむ姿勢という点でも刺激になっています。 オンボーディングでの有り難いサポート 私自身、久しぶり(約3年ぶり!)にプレイヤーの役割にも入るということで、感覚を思い出すことに苦労しました。 そんな中、 braavaさん をはじめとするチームメンバーの皆さんが、丁寧にサポートしてくれたことが、非常に有り難かったです。 また、チームには、単なるドキュメントだけなくFigma上に図を書いて互いに共有する文化(些細な課題でも図示していく姿勢でいくから、認識ズレを起こしにくい!)や、気軽にペアプロをしていく文化もあり、キャッチアップに非常に役立ちました。 最初の頃のペアプロでは、まるでリハビリをしているような状態で申し訳なかったですが、話しながら概要をおさえ、実際のコードを追って理解を確かめながら、加速度的にキャッチアップが進みましたし、皆さんのサポートのおかげで、少しずつですがプレイヤーとしての感覚を取り戻していると思います。 今後は、SaaS Marketing領域のドメイン知識や、新しい技術スタック(特にフロントエンド)についても、議論に追いつけるよう精進して参りたいと思います! 2ヶ月半でYATTEKITAこと チームジョイン後に意識して取り組んだ7つのこと 新しい組織やチームに移ることは久しぶりだったため、自分の中で意識して取り組んでいたことがあります。 これらは、自分自身のキャッチアップや初期の信頼獲得としても、大事なことのひとつかと思いますので、 異動や転職をする方にとっても、参考になればと思い、ここでご紹介します! 1. レスポンスのスピード感 Slackでメンションをもらったもの・もらってないけど自分に関係ありそうに感じたものは、できるだけ早く返すようにしていました。 チームにジョインすると、アカウント発行後のログイン確認や、資料の確認などオンボーディング上の細かな対応があると思いますが、それらに対してもできるだけタイムリーに返すようにしていました。 また、「ざっと確認して内容について不明点があるときには、返答の中で質問する(自分がボールを持たない)」「すぐ確認できないときは、確認できる時間の目処を伝える」など、社会人1年目に戻ったつもりで意識しました。 2. Slackでのリアクション・スタンプ、ピアボーナスの活用 レスポンスの早さと近い部分ですが、チームメンバーの投稿に対して、いちはやくスタンプを押すことで、盛り上げ役となり、自分の存在をアピールするようにしていました。必要に応じて、Slackにスタンプも追加してみました。 これは、リモートワークが主体となる働き方であるチームにおいては、新参者の自分が相手に対して気持ちや好意(一緒に頑張っていきたい気持ち・貢献欲)を示す機会のひとつとして有効だと思っています。そして、ジョインしたタイミングに限らずですが、直接相対していないリモートワークでは、返信などのリアクションをするときは、3割増くらいで気持ちを込めるのがちょうど良いと考えています。 また、ピアボーナスと呼ばれる「従業員同士がお互いに仕事の成果や貢献に対して賞賛したり認めたりするだけでなく、それとともに少額の報酬を送り合う仕組み」が導入されているため、オンボーディングなどのサポートをいただいたときは、隙かさず感謝を伝えるようにしていました。こうしたツールも組み合わせて活用することで気持ちを伝えるハードルも下がる気がしています。 3. チームメンバーとの1on1 メンバーの皆さんとの相互理解を深めるため・プロジェクトや組織の状況をより正確に把握するために、1on1をお願いしました。 1on1では単純な自己紹介だけでなく、 スキキライマップ といったコンテンツを用意して、より有意義に深く理解していくための流れを作りました。 また、1on1を自分だけの場にするのではなく、相手からの相談や質問がないかを必ず毎度確認しました。些細なことでも良いので、一緒に何かを考えていくキッカケにしたいと思っていました。 4. ドキュメントづくり オンボーディングの流れや、取り組んでみたタスクの中で不足していたドキュメントや、整備されていないフォーマットなどがあれば、たたき台を積極的に例示して共有しました。 意外とメンバーの皆さんには新しい視点だったり、資料化を忘れている内容だったりすることがあります。 5. 気づきの丁寧な共有と、仕組み化の提案・実行 ジョインしたチームでは、毎週振り返りの時間があり、KPTを使った手法で実施していました。 これまでの経緯や背景知識が少ない状態なので、フラットな目線でチームに新たな気づきを与えやすいと思いますので、素直に提案していきました。 一方で、既存のメンバーから見れば、いまの仕組みやルールを批判されていると感じやすい場合もあるので、内容によっては丁寧に経緯を確認・質問するような投げかけをする方が良いこともあります。せっかく築いている小さな信頼関係をより大きく育てるためにも大切な視点だと思います。 また、気づきに対する具体的な解決策(仕組み化など)の提案も、これまでの経験を元に行いました。さらに、解決策としてのTRYをチームとして合意したら、アクション実行のボールも自分が持つようにして、口だけで終わってしまわないようしました。 6. マネーフォワードとの積極的な連携、グループ知見の有効活用 せっかくグループ会社間で異動しているため、それを活かすためにマネーフォワードでの経験や事例を、タイミングを見て共有するようにしました。 加えて、新規PJが始まるタイミングでもあったため技術選定の場面などにおいては、マネーフォワードとのSlack共有チャンネル(技術領域ごとに相談や知見共有をする場)で私から質問を投げかけてみたり、特定の技術スタックに詳しいメンバーに繋いだり、といった「橋渡し役」を意識しました。 グループ会社と言えど、組織が異なると最初の相談や質問のハードルはやはり高く感じるものです。複数の組織に跨っている自分が、その最初のステップを軽くするような行動をとることで組織間の交流も進むものだと思います。 7. なんでもござれの姿勢 最後は、取り組みではなく姿勢のお話ですが、「これをやりたい」「これはやりたくない」という選り好みせずに、手を挙げる・首を突っ込んでみる・依頼を受けたら断らない(無理のない範囲でw)という姿勢で業務に入りました。 自分自身がもともと責任領域や仕事内容に対して、強いこだわりを持つ方ではないところもあり、プレイヤーとしての機能開発だけなく、社内の問い合わせ対応や、採用活動、その他組織運営に関わることなど、比較的幅広く取り組めました。 そうすることで、技術的な理解やドメイン知識が深まるだけでなく、この事業に関する業務の全体フローはどうなっているのか、いつも他部署とどのように連携しているのか、といったことを(点と点が線になり、面になり)より早くキャッチアップできたと思いますし、課題発見・改善提案もしやすくなると感じています。 チームの皆さんからの反応 メンバーの皆さんとの1on1などでいただいた反応やフィードバックとしては、以下のようなものがありました。 これまで曖昧だった課題がハッキリして、仕組み化が進んだ。 グループ間の知見共有が有り難い。繋がりができた。 課題を提起するときの、コミュニケーションの仕方が勉強になる。 マネジメント経験者がプレイヤーに入ったことで、中間的存在として1on1などで相談しやすい。 いままで素通りしていたところ、手に負えないところとなっていた課題の発見・拾い上げが進んだ。 チーム全体でSlackでのリアクションが増えた気がする。 というようなポジティブなコメントをいただきまして、プロジェクトや組織に少しでも良い影響を与え、ちょっとずつ信頼獲得もできていそうで、良かったです。 新しい組織に入るのは緊張もしますし、プレッシャーもあったので、新参者としてはホッとひと安心です! これからYATTEIKUこと 今後は、以下のことに特に注力していきたいと思います! とある新規PJの成功と組織づくり スケジュールがとてもタイトですが、ユーザーに向けて良い価値が提供できるようになり、技術的にも新しい取り組みや負債の解消にも繋がるプロジェクトです。これをチームの皆さんとともに成功させるために、あらゆる取り組みを行っていきます。 また、直近で少しずつチームの人数を増えてきていて、これからもジョイン予定のメンバーが決まっているという状況で、組織拡大が進んでいます。そんな中でより自己組織化され、成果を出せるチームを一緒に作りたいと思っています。 IPOに向けて CEOの林から発表しているとおり、 スマートキャンプはIPOを目指しています。 こちらに向けたシステム関連の課題整理や仕組み化などに取り組んでいきます。泥臭いところを含めて整えるのは好きなので、どんどんやっていきたいと思います。 また、IPOは目指す中では、それのみが目的ではなく、世の中への良い影響(事業成果)を加速させる手段であると認識して、全体を俯瞰して取り組むことを忘れないようにしていきたいです。サービス開発・運用上の成果だけでなく、ビジネス全体の目線をも持って、事業成果にも貢献していきたいと思っています。(そのためにも、まずはドメイン知識などのキャッチアップをがんばらねば!) まとめ 今回は、マネーフォワードから出向した黒沢が、この2ヶ月半を振り返り、スマートキャンプにジョインしてみての感想や、自分自身が入社当初に意識して行なった取り組みなどを入社エントリとしてご紹介しました。 これからも精進してまいりますので、皆さん!よろしくお願いします!!
アバター
はじめに 私の仕事内容 新卒入社から半年間の振り返り キャッチアップが追いつかず、タスクが遅れる 心がけたこと 年齢差による意見の遠慮 心がけたこと ドキュメントによるコミュニケーションの難しさ 心がけたこと 成果 まとめ はじめに こんにちは!開発エンジニアの小宮です! 私は 入社エントリ で、述べたとおり、 23年新卒でスマートキャンプに入社し、早いもので半年が経過しました。今回は、半年間の振り返りを書く機会をいただいたので新卒ならではの 挑戦や困難などについて書いていきたいと思います。あまりテックな話は少ないかもしれませんが、 最後までお読みいただけると幸いです! 私の仕事内容 私は、入社してから BALES CLOUD (以下「BC」)という事業部でエンジニアとして働いています。業務内容は主に、新規機能の開発、テスト、バグ調査などの保守運用開発です。 BCのエンジニアチームは、社員が自分を含めて4人、業務委託の方が1人で構成されています。 みなさん経験豊富な方が多く、私がみなさんに比べ一回りほど年下という、組織です。 新卒入社から半年間の振り返り 新卒入社して半年間を振り返ると、大きな悩みなどはなかったと思いますが、新卒ならではの細かい課題にはたくさん直面しました。 それらの課題について振り返ってみたいと思います。 新卒ならではの直面した課題は以下の3つです。 キャッチアップが追いつかず、タスクが遅れる 年齢差による意見の遠慮 ドキュメントによるコミュニケーションの難しさ キャッチアップが追いつかず、タスクが遅れる 新卒研修が終わり、BCに配属後にはじめて立てた目標は、毎週のタスクを約70%完了させることでした。これだけ聞くと、「70%?100%が当たり前じゃないの?」と思われるかもしれませんが、 BCでは1スプリントを一週間で行ないその間に要件の詳細決め、開発、テスト、レビュー、リリースを行ない、 複数人が1つのタスクに携わるため、当時のチームの完了率見ても80%程度でした。 そのなかで70%という目標を立てたのですが、当初の私としては「どうせやるなら100%目指そう」と思っていました。 ただBCの技術スタックは今まで触れたことがないものも多く、キャッチアップしながらタスクを進めていくという流れでした。 入社して右も左もわからない私は、まずはとにかくコミット量を増やしていこうと思いました(脳筋)その結果、配属直後はタスクの量や難易度が調整されていたので、コミット量だけで順調に進んでいました。 しかし、タスクの量が増えたり、難易度が上がると、完了率がだんだんと下がり、キャッチアップの時間もほとんど確保できず、タスクが遅れることが増えました。 その時は、自分のかけれる時間をとにかくかけてもタスクが終わらないという状況に陥り、かなり焦っていたのを覚えています。 心がけたこと 上記の課題に対しては、抜本的な解決策はなく、地道にタスクをこなして少しずつドメイン・技術の理解を深めるしかないと思いました。ただ、そのなかでも意識的にやっていたことが3点あります。 1点目は、最初から全部をやろうとせず、段階を踏んでからタスク量を増やそうと決めたことです。私の場合は、コードレビューをすることに配属当時に多くの時間を使ってしまっていたので、 期限を決め、ある程度業務に慣れるまで、担当から外れるようにを上長に相談し、一定期間コードレビューから外れました。コードレビューを通じて、他の人のコードを見ることで、成長できるというメリットもあると思いますが、 私の場合、新卒で技術もドメインも知識が乏しい場合は、ある程度タスクベースでキャッチアップすることの方が効率的だと感じました。 2点目は、技術のキャッチアップをする際に公式ドキュメントを読むことです。 私はもともと技術書などを読むことに苦手意識があり、公式ドキュメントを読むこともなんとなく避けていてネットにある記事やQiitaなどを読むことやchatGPTに聞くことが多かったです。 しかし、トレーナーからのアドバイスもあり少しずつ公式ドキュメントを読む癖をつけていきました。読む習慣がついてからは開発で困ったときに、自分の力で必要な情報にたどり着けるようになったと思います。 3点目は、質問をする際にwhyを意識するようにしたことです。スマートキャンプでは、新卒のエンジニアにトレーナーという業務上のサポートしていただける方がついてくれます。 そのトレーナーに実装に関することを質問する際に、入社当初は、自分で考えてわからないことを、「どうやって実装するのか?」というHowばかりきいてました。 もちろんその形式の質問でも問題は解決されるので、満足感がありそれ以上調べたりせずにその場しのぎで終わることが何度もありました。その結果同じような質問を期間を開けてからまたしてしまうことがありました。 その経験から質問する際には、「なぜそのような実装をするのか?」どのような思考のプロセスでその実装にするかなど、答えだけではなく、答えに至るまでの過程まで聞くようにしました。 whyを意識して質問するようになると、自分との考えの違いや、プロセスを知ることなり今まで以上に理解が深まるようになりました。 年齢差による意見の遠慮 私が所属しているBCのエンジニアチームは、冒頭でも少し述べたとおりエンジニア経験が豊富で、社会人経験も長い方が多く、私との年齢差が少しあります。 そのため私が配属したときは、リファイメントやプランニングなどのスクラムイベントで、自分の意見を遠慮していました。コードレビューにおいても、コメントを書くのを遠慮していてLGTMおじさんになっていました。 理由としては、年齢差により過剰に技術・ドメインの知識の差があると考えていて、自分が発言してMTGを止めない方がいいのではないか?という思いや、コードレビューでベテランだからこのような実装にしているかもしれない?と考えていました。 心がけたこと こちらの課題に関しても、抜本的な解決策はないのですが、どんな切り口でも良いので自分から関わっていく意識を持つようにしました。 まず、MTGにおいては、質問ベースでもいいので発言すること・ファシリなどを積極的に行なうことをしてMTGに少しずつ参加していく意識を持つようにしました。 その際に、どんな質問や意見なども丁寧に対応していただいたチームメンバーの方々には感謝です。 また、コードレビューにおいても、なぜこのようなコードになったのかなどの質問書くことから始めました。 そこからはじめ、今ではバグなどを未然に防ぐようなレビューもできるようになったと思います。 ドキュメントによるコミュニケーションの難しさ 社内で利用しているツールの分類 BCでは、連続した開発時間を確保するために一部のMTGの非同期化しています。 上の画像は緊急度や発散性に応じて、コミュニケーションを取るツールを使い分ける際に参考にしている図です。 図から分かるように緊急度が高くないやり取りはドキュメントベース(Notion)でのコミュニケーションが多くなります。新卒として入社した当初、この取り組みにすぐには慣れませんでした。 その時の私の思いとしては、「ドキュメントベースでやり取りするより、直接話した方が早く解決できるのではないか?」という疑問がありました。 さらに、単純にドキュメントを書く習慣がなかったため、書くことに対する苦手意識がありました。 心がけたこと まず、ドキュメントを書く習慣を少しずつ身につけていきました。ただいきなり書けと言われても、どのようにを書けばいいのかわからないので、とにかく他の人のドキュメントを真似るところから始めました。 BCのチームの皆さんはドキュメントを書くのが上手で、1人1人のドキュメントのスタイルが若干違うので、それを見て良いと思った箇所を真似ていくことで、自分も少しずつ書けるようになってきました。自分はテーブルなどの表を用いてドキュメントをまとめるのがかなりお気に入りですw。 また、自分がドキュメントを書けるようになってくるとMTGの非同期化への理解が深まってきました。開発時間の確保もそうですが、ドキュメントを書くことで自分の考えを整理してから伝えることができ、 記録にも残るので後から見返すことができるというメリットなどを感じることができました。 成果 新人賞の景品 タスクの完了率は結果として、目標の70%を超えて、111%に到達しました。さらに、FourKeysのリードタイムも86.9時間とgoogleが定める ハイパフォーマーに分類される数値 に到達しました。 その成果が認められ、年に2回開催されるスマートキャンプ全体のキックオフで新人賞をいただくことができました。中途の方々も対象になる中で賞をいただいて、自分が向き合ってきたことをしっかりと認められたことがとにかく嬉しかったです。画像は新人賞の賞品でいただいた本です。 ただ自分が選ばれると思っていなかったので、表彰の場にジャージで行なってしまったことが少し後悔ですw。 まとめ 半年間での振り返りをしてみると、新卒ならではの課題にぶち当たりながらも、それに対して解決策を見つけ、少しずつ成長できたと思います。 前期は自分自身のパフォーマンスを上げることに必死でしたが、今期は主語を大きくして、チームのパフォーマンスを上げるために行動していきたいと思います! また、エンジニアとして技術的にインパクトがあることにも挑戦していきたいと思います!これからもよろしくお願いいたします!!
アバター
どうも、職人です! バージョンアップ?なにそれおいしいの? バージョンアップの何が辛い? メインタスクとの兼ね合い そのライブラリがどこで使用されているか バージョンアップをして問題ないだろうか バージョンアップするときの面倒な作業 どこを効率化できるだろうか Dependabot Dependabotを導入した結果 あれ、このライブラリ既視感あるな... GitHub Actionsで過去のPRを漁る その結果 まとめ どうも、職人です! スマートキャンプでBOXIL SaaSのエンジニアをやってます職人こと袴田です! 最近は趣味のサウナが好きすぎて、熱波とアウフグースに目覚めました。 気がついたらとある温浴施設で熱波師としてデビューしたのをお知らせいたします。 そんなことはさておき、弊社サービスのBOXIL SaaSはRuby on Railsを使用して開発しています。 サービス保守運用に欠かせない作業といえばバージョンアップですよね。 皆さん大好きバージョンアップ作業を少し効率化した話を書かせていただきます! バージョンアップ?なにそれおいしいの? 「バージョンアップとは、ソフトウェアのバージョンを上げることです。」ってCopilotが教えてくれました。 なんでバージョンアップなんかしないといけないんや! って思いますが、バージョンアップをしないとセキュリティリスクがあったり、新しい機能を使えなかったり、開発体験も悪くなる一方です。 デメリットしかないですね。 大変な作業ですが、サービスを保守運用していくためには必要な作業です。 バージョンアップの何が辛い? バージョンアップは一般的に後回しにされるイメージがありますが、何がそんなに辛いのか考えてみました。 メインタスクとの兼ね合い もちろんバージョンアップタスクのために、メインタスクをおざなりにすることはできません。 効率化をしなければどうしてもバージョンアップタスクは後回しになってしまいがちです。 そのライブラリがどこで使用されているか バージョンアップ対象のライブラリがサービスのどこで使用されているかを確認する必要があります。 使用箇所が確認できなければ、動作確認対象もわかりません。 バージョンアップをして問題ないだろうか バージョンアップをしたらok!リリースしまーす! なんてことはもちろんできません。 バージョンアップをしたことによって、サービスに影響がないか、バグがないかを確認する必要があります。 バージョンアップするときの面倒な作業 まず何がバージョンアップのネックになっているか、面倒な作業を考えてみました。 どのライブラリがバージョンアップできるのか調査する 単純に調査自体が面倒です。 使用箇所を特定する バージョンアップしたいライブラリの使用箇所を確認する必要があります。 動作確認をする ライブラリをバージョンアップし、ライブラリ同士の依存関係の解決します。 それができたらライブラリを使用している箇所の機能が正常に動作するか確認する必要があります。 PRを作成する 弊社はGitHubを使用しているので、バージョンアップのPRを作成する必要があります。 レビューする バージョンアップのPRをレビューする(してもらう)必要があります。 リリースする バージョンアップしたライブラリをリリースします。 どこを効率化できるだろうか まず どのライブラリがバージョンアップできるのか調査する 作業をどうにかしたいので、GitHubが提供しているDependabotを使用してみることにしました。 Dependabot Dependabot はライブラリをバージョンアップするPRを自動で作成してくれるbotです。 BOXIL SaaSはRuby on Railsを使用しているため、DependabotがGemfile.lockを監視し、バージョンアップが必要なgemのPRを自動で作成してくれます。 こちら を参考に、 .github/dependabot.yml を作成します。 version : 2 updates : - package-ecosystem : "bundler" directory : "/" open-pull-requests-limit : 5 # 作成するPRの最大数を5に設定 schedule : interval : "daily" time : "10:00" timezone : "Asia/Tokyo" labels : - "レビュー求む" ignore : - dependency-name : "*" update-types : [ "version-update:semver-major" ] # メジャーアップデートは無視する これで、毎日10時に5個のバージョンアップのPRが作成されるようになりました。 いきなりメジャーアップデートのPRが連続して作成されると、大変なのでいったん無視するように設定しました。 Dependabotを導入した結果 どのライブラリがバージョンアップできるのか調査する PRを作成する という作業はDependabotに任せることができました。 しかし、PRは上がってきたものの、そのPRのライブラリはどこで使用されているのかがわかりません。 こればかりはどうしようもないので、重い腰を上げて片っ端から確認することにしました。 確認をしていくうえで、PRのコメントに使用箇所や動作確認に必要な情報を書き込んでいくことにしました。 あれ、このライブラリ既視感あるな... 同じライブラリのバージョンアップPRが何度か上がってくることがあり、その度に「あれ?これ前にもあったような?」と思いながら確認していることに気づきました。 過去に対応した内容はPRのコメントに残しているので、それを確認すればいいのですが 「これ毎回過去のPRを漁りにいくのしんどいな・・・」と思ったので、この作業自体をGitHub Actionsで自動化できないか考えてみました。 GitHub Actionsで過去のPRを漁る 実現したいことは DependabotがPRをオープンしたタイミングでGitHub Actionsを実行する Dependabotが作成したPRのライブラリのバージョンアップを過去に行っているかを過去のPRを漁って確認する 過去に行っている場合は、そのPRのリンクをコメントする になります。 GitHub Actionsで過去のPRを漁るためには、GitHubのAPIを使用する必要があります。 こちら のページに使えそうなAPIがあったので、活用してみます。 そして作ったのがこちらのGitHub Actionsです。 .github/workflows/fishing-for-past-upgraded-prs.yml name : Fishing for past upgraded PRs on : pull_request : types : - "opened" - "reopened" permissions : pull-requests : write jobs : fishing_for_past_upgraded_prs : runs-on : ubuntu-latest steps : - name : Add Links Comment uses : actions/github-script@v6 with : script : | const { owner, repo, number } = context.issue; const pr_title = context.payload.pull_request.title; const regex = /Bump (.*) from (.*) to (.*)/; const match = pr_title.match(regex); if (match) { const gem_name = match [ 1 ] ; let previous_prs = [] ; for (let page_number = 1; page_number < 11; page_number++) { const { data : prs } = await github.rest.pulls.list( { owner, repo, state : "closed" , sort : "updated" , direction : "desc" , per_page : 100 , page : page_number } ); const target_prs = prs.filter( (pr) => pr.title.includes(`Bump $ { gem_name } `) ) previous_prs = previous_prs.concat(target_prs) } if (previous_prs.length > 0) { let comment = "過去の関連PR: \n " ; previous_prs.forEach((pr) => { comment += `- [ #$ { pr.number }] ($ { pr.html_url } )\n`; } ); await github.rest.issues.createComment( { owner, repo, issue_number : number, body : comment, } ); } } 過去PRを釣り上げるということで、 fishing-for-past-upgraded-prs という名前にしました。 やっていることを簡単に説明すると Dependabotが作成したPRのタイトルからライブラリ名を取得する 過去に作成したPRを更新日時の降順で取得する 過去に作成したPRのタイトルにライブラリ名が含まれているものを抽出する 抽出したPRのリンクをコメントする ということをしています。 このコードのポイントは2つあります。 1つ目はGitHub APIが1回のリクエストで100件のPRしか返さないことです。 BOXIL SaaSを管理しているGitHubリポジトリではPRが約10000件あります。 それらのPRをすべて取得してしまうと、1回のアクションで約100回リクエストを送信することになり、時間がかなりかかってしまいます。 これでは現実的ではないので、リクエストの回数は10回に制限し、1000件のPRを取得するようにしています。 1000件というのは「1000件くらい漁れば、過去のPR見つかるっしょ!」くらいの感覚です。 仮に1000件を超えたところにPRがあったとしても、PRがコメントを通じて数珠つなぎになって辿れるので、問題はなさそうです。 (健全にバージョンアップ作業を続けられているのであれば、1000件より少なく、500件でも300件でも良さそうな気はしています) 2つ目はpermissionsの設定です。 permissions: pull-requests: write この部分です。 こちら を参考にpermissionsの設定をしないと、 RequestError [HttpError]: Resource not accessible by integration Error: Unhandled error: HttpError: Resource not accessible by integration このようにGitHubのAPIを実行したときにエラーが発生してしまいます。 レスポンスのHTTPステータスコードは403なので、閲覧権限がないようです。 permissionsの設定をwriteとすることで、PRへの閲覧権限を付与するようにしています。 (readだとPRにコメントを書くときに、権限がないと怒られます) こちらを導入した結果、DependabotがPRを上げると、過去に作成したPRのリンクがコメントされるようになりました。 あくまで過去の参考情報なので鵜呑みにするのはよくありませんが、 使用箇所を特定する 作業に関して、少し負荷の軽減ができた実感があります。 その結果 前期ではバージョンアップのためのノウハウを蓄積するという意味でひたすらにDependabotが上げたPRを対応していました。 その結果6ヶ月間で40PRほど対応できたのですが、今期が始まって2ヶ月ほどの間にすでに40PRほど対応できています。 直近のバージョンアップの状況(バージョンに関わる部分は黒塗りにさせていただきました。) とはいえ、 Dependabotが上げたPRのみGitHub Actionsが実行されるようにする ラベルでGitHub Actionsの実行対象のPRかを判別する などまたまだ改善の余地はありそうです。 ちなみに Dependabotが上げたPRのみGitHub Actionsが実行されるようにする に関しては、Dependabotではない誰かがPRを上げた場合にも実行するようにあえて、DependabotによるPRの制限は行っていません。 まとめ 明らかに状況が変わってきている実感があり、バージョンアップ作業の定常化に向けて大きな前進になったと思います。 引き続きバージョンアップ作業を進めて、常に最新のバージョンを維持できるようにもっと効率化していきたいと思っています。
アバター
まえがきのまえがき まえがき 入社半年編 期待と不安の滑り出し BIへの不信感を払拭 既存を大事にしすぎた問題 各部署からのお使いクエスト 転職を機に新しくはじめたこと 半年のふりかえり SMARTCAMP AWARD 入社1年編 淡々とお仕事をこなす生活 エースの喪失 1年編をふりかえる SMARTCAMP AWARD 入社1年半まで さらに淡々とお仕事をこなす生活 業務幅の広がり 入社1年半ふりかえり SMARTCAMP AWARD 最後に まえがきのまえがき 駅そばなどでよく見かけるコロッケがのった酔狂なメニュー、コロッケそば。 ジャガイモのホクホク感や肉の旨み、玉ねぎの甘み。そういったものが一切ない、よくわからないパサパサした食感の冷凍コロッケ、自称コロッケがのったコロッケそばこそが至高の存在だと思う。 そして正直、どう食べたらいいのかわからない食べ物である。汁が染みないようにちょっと避けて食べるか、浸して崩して食べるか、半分だけ崩したり、がっつり底において育てたり。 まえがき こんにちは、くまのみです。 以前はマッチングアプリのエンジニアとして働いていました。 スマートキャンプにはデータアナリストとしてジョインし1年半が経ちました。 ひとくちにデータアナリストといっても業務内容や必要なスキルはコロッケそばの食べ方のように振れ幅があり、それぞれの会社の環境や事業フェーズに合わせて、働き方を変える必要があります。 データアナリストに求められるスキル 出典:データサイエンティスト協会 ぼくの入社とともにデータチームも動き出し、たまたまですが社内で評価していただく機会もありました。ここまでの うまくいったこと 、 だめだったこと を含めてふりかえろうと思います。 個人的には成功体験より失敗体験を聞く方が好きです。 この記事はテックなブログではなく、完全にポエム記事になります。 入社半年編 期待と不安の滑り出し 採用面談で出会ったエンジニアの方と、同じチームで働けることを期待して入社しました。 しかし、入社当日にはその方は京都支社へ移動してしまっていた。 「早々にひとりぼっちなのかな」と不安になったが、気軽でいいわぁなんて浮かれていました。 実際にはひとりぼっちではなく、上長がいて社外のデータPMもいました。 データチーム紹介 データチームは4人で構成されており、データPMがロードマップの策定や方針を決めていました。 その中でぼくはデータ民主化に注力していくことになりました。 ありがたいことにデータ基盤周りはすでにエンジニアの手によってほぼ完成していたので、これを使えばサクッといけそうだと楽観視していました。しかし、実際にはそう簡単にはうまくいきません。苦戦する半年を過ごすことになります。 BIへの不信感を払拭 親会社でLookerを使っていることから Looker を使うことのできる環境がすでにありました。 LookerとはDBのカラム定義、集計定義、データの関係をLookMLというもので記述できSQLの知識が浅くてもある程度のデータの可視化ができるようになるBIツールです。そのLookMLもかなり定義されていました。 主要なKPIと呼べそうな数字を把握するためのダッシュボードもいくつかつくられており、あとは利用するだけの状態に見えました。 Looker上でダッシュボードがいくつか作られているものの、なぜか全く利用されていませんでした。各部署の方たちはRedashを利用して数字の確認をしているようでした。 Lookerを利用していない理由を聞くと 「なんか数字がズレてるんですよね」 と返ってきました。この数字の「ズレ」に苦しめられることになります。 僕は正しい数字をちゃんと出すことができればLookerを利用してもらえるのではないかと考えていました。ズレる原因はいくつか存在し、集計軸や条件の考慮漏れ、考慮過多、集計範囲の絞り込み等ありました。 正しい数字を出そうとすればするほど、過去に書かれたRedashのクエリ結果とは乖離が大きくなったのです。 「この数字はここの部分の考慮が漏れているので数字が合わないんです」 のように伝えていたのですがデータ利用者の方には 全く響きませんでした 。 「そうなんですね、わかりました」という返事をいただくものの、Lookerを一向に見ようとはしてもらえません。 主要KPIや各種数字に関して、Redashの数字を正として扱ってきたこれまでの流れがありました。 ズレとはRedashとLookerでの数字のズレであり、正しいか正しくないかはさほど重要ではなかったのです。 ぼくはアプローチを変えることにしました。 たとえ若干間違っていたとしてもRedashとLookerの両方で同じ数字を出せるようにしようと。 正しい数字が出ていようと使われないダッシュボードは無用の長物だからです。 Redashを正として数字を見ているためLookerの数字を同じ調整する Redashと同じ数字がLookerで確認できていることを担当者と一緒に確認する 同じ数字にしたのち、Redashのクエリを本来あるべき条件を考慮した正しいデータに近づける Redashのクエリはこの点が考慮もれている等と全体に周知したうえでRedashのクエリを修正して良いかの可否を問う Redashの修正をしたのち、Lookerにも条件を反映していく この1~5を何度か繰り返しLookerでも同じ数字がでていることを意識づけ、ズレはなさそうだという認識合わせをデータ閲覧者に向けて実施しました。 これによって抱いてしまったLooker(BI)への不信感をすこしづつ払拭していきました。 既存を大事にしすぎた問題 データ基盤周りはすでにエンジニアの手によってほぼ完成 と先に書きました。ほぼなのです。 ちゃんと運用されれば運用フロー込みで仕上がっていくものであった広告パラメータ管理用の一部のデータが、Lookerが利用されず運用されていなかったため頓挫していたものがありました。 これまでにほぼ作られていたものがあったので流れに乗っていこうとしましたが、うまくいきませんでした。ちゃんと整備しなおし運用フローを移譲したとしても、担当者が運用してくれるかどうかは別なのでした。 使われないものはちゃんと捨てるという選択をするのに時間がかかりました。 各部署からのお使いクエスト 今までプロダクト開発サイドに来ていたデータに関する依頼を巻き取るようになります。 ゲームのお使いクエストのような感覚で進めていくのですが、依頼者の所属部署ごとに必要なドメイン知識が異なり、キャッチアップするだけでヒィヒィいう生活を送ります。 各部署の方からかなり助けてもらうことも多々出てきます。依頼者の方に「私に聞かれても」というような質問を投げつけることも多々していきます。ブチギレられてもしょうが無いと思うのですが、優しい方ばかりだったので経験値をちびちび貯めていくことができました。 転職を機に新しくはじめたこと 上長は他の部署の方も見ている関係でぼくに割ける時間は限られていると認識していました。 コミュニケーションの場は1回15分、週2回行われる1on1がメインでした。 ある程度ぼくが何をしているのか分からないと自由に行動させてもらえないのではと考えました。 そのため1on1の前に現在実施中のタスク、今後やりそうなタスク、共有事項、困ったこと等を書いておき共有するようにしました。 正直業務をサボっていても誰かにバレるわけでもないです。ただ真面目に働いていたとしても活動は見えにくいので自分の行動ログの可視化は必須でした。 この記事の執筆時では139回の1on1のログが残っています。週に2度の能動的とも受動的ともとれるふりかえりによってDCAPサイクル(PDCAサイクルの逆順で、実行(Do)→評価(Check)→改善(Action)→計画(Plan)の4つのステップを繰り返し、業務改善や課題解決を図るフレームワーク)が割と早く回るようになりました。 あと記憶力が良くないので、土日を挟んでしまうと先週やったことでさえほぼ覚えてなかったりします。自分がやってきたことをちゃんとふりかえるうえで大事な作業になりました。 半年のふりかえり 入社早々のどこの誰かもわからない人間が今まで正しいとしていた数字に対していちゃもんをつけても「そうだよね!正しくしようね!」みたいな流れになるのは稀かなぁと思います。 結局データを扱うのは人なので人に寄り添い、人のこころを動かさないと何の意味もなさないこと気づきました。 データはただしく扱わないと!!のような情熱がちょっとだけぼくの活動の邪魔をしました。 SMARTCAMP AWARD スマートキャンプでは半年間で自身がVMV(VisionMissionValue)を体現した取り組みを発表をする場があります。 役職者を除いた全社員がそれぞれの事業部ごとにトーナメント方式で発表し、1次予選、2次予選を勝ち抜くと半期に一度の会社のキックオフで発表をしMVPを決める社内発表会です。100人くらいから1人のMVPが決まります。 半年間のぼくの成果は、これといって目立ったものがなく1次予選で敗退でした。 正直に言って自分の成果をドヤるというかアピールするのは得意ではありません。恥かしがり屋なので発表するのもちょっと億劫なタイプです。ひっそりとぼくの半年は幕を閉じました。 入社1年編 淡々とお仕事をこなす生活 データチーム紹介 気づくとデータチームが一人減ってました。さみしい。 今期もデータ民主化をがんばるぞ!と意気込んでいました。 半年かけて事業のドメイン知識もちょっと溜まり、Lookerへの不信感も払拭されつつある中で問題がおこります。 Lookerの費用が高いため、他のBIへの移行も検討しなくてはならなくなりました。 これにより1ヶ月の間、ぼくは右往左往することになります。 各部署にデータに関するユースケースをヒアリング実施、ドキュメント化し最終的にはLookerを使い続けて良いことになりました。 1ヶ月ほどロスしたおかげでフラストレーションがかなり溜まりました。これが良い方向に発散され、ここからひたすらアウトプットを出し続けることになります。 ユースケースのヒアリング時にぽろっと出てきた課題の解決や、各部署へヒアリングしたことで距離感がすこし近づき新たにデータの依頼をいただくことも増えていきます。 施策の事前調査、効果検証、データ抽出、業務効率化に寄与していきました。 それと並行してデータPMの方で推進していた「データを用いて事業への直接的な利益向上実績を創出」するための施策の実施しました。CVRの改善だったのですが良い結果が生まれました。 エースの 喪失 データ基盤周り を率先して整備してくださっていた通称エースさんが退職されました。 ちょっと困ったなぁというときに助けてもらえていたのですが「だって他に頼りがいねぇ」状態になります。 もっと迷惑かけるくらい頼っておけばよかったような気もしてます。 1年編をふりかえる キャッチアップする力が非常に高い人が周りにチラホラいるのですが、ぼくはそういうタイプではないです。あとからふりかえると入社半年のさえない期間も非常に大事でした。 ちびちび消化していったお使いクエストでも経験値が少しづつ溜まり、レベルがいくつか上がったと思います。自分の知らないデータを布教し民主化していくことは難しいでレベル上げは必須でした。複数部署からの依頼をこなしていった結果、ある程度データに関する理解が進んでいきました。 もしかしたら会社が想定していた速度でのデータ活用はできなかったかもしれません。 コツコツと積み重ねていくことしかぼくにはできませんでした。 スマートキャンプ内にデータアナリストの枠はぼくひとりです。成果がちゃんとだせていなければ追加で人が増えることもないでしょうし、データアナリストの枠自体も不要だと思われても仕方がありません。 データPMの方がデータ民主化やデータを用いた施策等で、進むべき道を示して走り方を教えてくれるような環境がスマートキャンプにはありました。この点が非常にありがたいと感じました。 SMARTCAMP AWARD SMARTCAMP AWARDの1ヶ月ほど前にプロダクト開発の部長?と1on1することがありました。 その時にエンジニアの方には他部署向けに成果をちゃんとアピールするためにも発表を頑張って欲しいみたいな話を聞いたのがきっかけで、発表するための資料をちゃんと書こうと思うようになります。 データチーム紹介 データチームで実施したことを社内に展開しようと思うと、上長は役員でPMは業務委託なこともあり、ぼくが成果を発表しなくては成果が埋もれてしまう状態でした。 💡 役員を除いた全社員がそれぞれの事業部ごとにトーナメント方式で発表を行なう 社内でのデータチームの認知度の向上をし、データ民主化をより推進していくために自分が変わらないといけないときがきたんだなと実感しました。 トロフィー 結果としては普段そこまで目立たない人間が一生懸命話したところが功を奏したのか・・・。 半期MVP 🏆 社員賞 SCBB賞(VisionのSmall Company, Big Business) という3つの賞をいただくことができました。 💡 プレゼンも非常に秀逸でしたが、チャットのコメントを見ていたとおり、テック、データといえばくまのみさんという他者評価が高くて素晴らしいなと感じていました。ぜひ他カンパニー向けにアウトプットいただけたら嬉しいです! 💡 データはこれからのスマートキャンプが仕組みで勝っていくための鍵だと思っており、半期でしっかり整備し、かつ売上インパクトまで出していただきありがとうございました!これからもデータドリブンの会社づくりをよろしくお願いします! 💡 プレゼンテーションの面白さだけではなく、淡々と語っている内容の中にビジョン・ミッションの体現があり素敵でした! データ絡みで困ることしかない毎日だと思っているので、いずれコラボレーションする機会が来るのを楽しみにしております!! のようなコメントもいただけてとても嬉しかったです。 さらに嬉しかったことは データチームの告知 を全社的にでき、来期以降のデータ民主化活動を円滑に行なうための地盤の強化ができたことでした。 入社1年半まで さらに淡々とお仕事をこなす生活 前期のAWARDで全社的に活動を告知したことで、今まで依頼が来なかった人たちからも依頼が来るようになりました。これは、データ民主化の最終形である社員自らがデータを取得できるような体制を整えるためには、まずはデータが欲しい、データを使いたいと思ってもらうことが大事なため、大きな一歩となりました。 これまでの1年間に及んだお使いクエストによるレベル上げと、前期の活動結果が評価されたことで、円滑な業務処理ができるようになりました。持ち前のうっかりさで、定期的にうっかりしたり、早とちりしたり、誤字脱字はしょっちゅうあるものの非常に円滑でした。 より多くの人にデータを目にする機会を増やし、ほんの少しでもデータを元にプロダクトを考えてもらえれば、組織全体のデータリテラシーが少しずつ向上する良いサイクルが生み出せるのではないかと考えています。 鉄は熱いうちに打てに近い考えで、データが欲しいと感じた方に向けて、可能な限り早くデータを手にしてもらうように活動してきました。時間が少しあいてしまうと見ている方向が変わったり、関心が薄れたりしてしまうからです。 また、能動的に他部署の方に1on1を実施しながら困りごとがないかヒアリングを進め、データ活用を推進してきました。 半年間の活動を振り返ると、目立った失敗も目立った成功もなく、ちゃんとやれているのかの実感が湧かないまま過ぎていきました。 業務幅の広がり 施策する際にデータを元に意思決定する習慣ができている状態を定着させていこうと考えました。 仮説検証用のデータ抽出はどのくらいきているのか、どのくらいで対応完了しているかなど、ぼくの手元にくるタスクを分類化し来期以降どういうふうに進めていくかを考えるための土壌整備も進めました。 施策後の効果検証を円滑に行なうためのトラッキングログの設計等も対応していき、業務の範囲というか組織貢献の範囲が広がっていった気がします。 入社1年半ふりかえり 困っていると言われて可視化してみたところ、使われなかったり、作った後に音沙汰がなかったりすることもありました。しかし、意外にも他で活用できたりと無駄になることがありませんでした。 正直この半年間は山あり谷ありといったことはなく、平坦な道をまっすぐ走るような、ひたすらまっすぐ走っていた感覚でした。 目標設定でしっかりとゴールが決まっていたことも大きかったと思います。 SMARTCAMP AWARD アートボード なんやかんやあったようななかったような。 運が良かったので半期MVPをいただくができました(2度目)。 MVPを取るとアートボードを親会社のマネーフォワード総会の時にいただけるみたいでした。 半期MVP 🏆 💡 くまのみさんは、スピード感を持ってアウトプットされているだけでなく、哲学を持ってプロジェクトを進められている点で、大変刺激を受けましたし、素晴らしいと感じました。素敵なプレゼンテーションをありがとうございました。 💡 昨年以降Collaborationのレベルが上がっており、それを定量アウトプットに繋げるOwnershipとSpeedが頭抜けていると感じました。さらに、それがメンバーとの信頼関係・感謝につながっていることを特に高く評価させて頂きました! 💡 くまのみさん日々のアウトプットの量・質そしてスピード感がBOXILのビジネス成長につながっています!全メンバーのリテラシーを高め、データドリブンな意思決定が促進されていけるよう引き続き頑張っていきましょう! MVP受賞のコメントをもらえたことも嬉しかったのですが、一番嬉しかったのはMVP受賞のちょっと後に起きた出来事でした。 1次予選の発表をした際に、別のプロダクトの新人エンジニアの方が発表されました。その方の発表がとにかく素晴らしかったので、今回のMVPはその方だと思い込んでいました。 いい発表だったなぁとぼくのモチベーションも上がり、日報で「来期ももっと頑張ろうと思った」みたいなことを書きました。 打ち上げの際に、その新人エンジニアの方から「日報で褒めてもらったことがとても嬉しかったです」と言われました。その言葉を聞いたとき、 どこの誰かもわからない人間 からちゃんと脱却できた気がして、とにかく嬉しかったです。 最後に エンジニアからデータアナリストに転職して1年半が経ちました。悔いも後悔もありません。 正直、パッとしないエンジニアだったので、今の方がのびのびと働けています。 データアナリストとしての仕事は、データの収集・分析・可視化・活用の4つに大きく分類されます。ぼくは分析よりもデータの可視化と活用に重きを置いて活動してきました。 ただあらためて考えると、ぼくがこうして活動できたのは 各部署各メンバーの人たちが助けてくださったこと が一番大きくありがたかったです。かけだしデータアナリストがちゃんと活動できるよう成長するまで面倒みてくれることは稀有だからです。 これからはプロダクトに関わるすべてのメンバーのデータリテラシーを向上、データドリブンな意思決定を促進させ、プロダクトの成長に貢献することで恩返ししていきます。 これまでデータアナリストというロールがなかった会社に、データアナリストではなかったぼくを雇い入れることは、そばの上にコロッケを乗せちゃおうと考えることくらいかなり挑戦的なことだったんじゃないかと思います。そして、どう扱って良いのかも迷ったんじゃないかとも思います。 入社直後の社員リレー記事で、「 スマートキャンプは意外とちゃんとしていた場所だった 」みたいなちょっと失礼なことを書いているのですが、そんなぼくでもちゃんとチャレンジさせてくれて、評価までしていただけたことにとても感謝しています。
アバター
はじめに 前提 FourKeysとは FourKeysを横に広げるとは 横に広げるために必要な要素 橋を作ってくれる協力者 FourKeysの目的を明確にする FourKeysが与える身近な効果を伝える FourKeysへの取り組みをしやすくし習慣化する 今後目指したいところ はじめに こんにちは!スマートキャンプ開発エンジニアの井上です。 スマートキャンプでは少しずつFourKeysを活用し始めており、その中でも今回はプロダクト間の横の連携でFourKeysを広げた話をしていきます。 eyecatchの画像は生成AIにキャンプ✖️FourKeys✖️横へ広げる✖️テックブログで生成したらブログの内容には合わない壮大なものができてしまいました。。 前提 スマートキャンプはBOXIL SaaS、BALES CLOUDと複数のプロダクトが存在します。 この開発組織はそれぞれ独自の成長を速い速度で行えるようにそれぞれの開発組織が存在します。 このため共通の文化もあれば独自の文化が存在する開発組織です。 FourKeysとは DORAが実施した6年間の研究からソフトウェア開発チームのパフォーマンスを示す4つの指標があることがわかりました。 これらの指標をFourKeysと呼びます。 また、このFourKeysの指標の中でも下記のように速度と安定性を示すものと理解しています。 ソフトウェアデリバリの速度 デプロイの頻度: 組織による正常な本番環境へのリリースの頻度 変更のリードタイム: commitから本番環境稼働までの所要時間 ソフトウェアデリバリの安定性 平均復旧時間: 組織が本番環境での障害から回復するのにかかる時間 変更失敗率: デプロイが原因で本番環境で障害が発生する割合(%) FourKeysを横に広げるとは もともとFourKeysはBALES CLOUDでTry的に計測や改善を行い運用していました。 ただこの取り組みはプロダクト組織が改善していることをデータで語れるようになるための大事なことになるためプロダクト組織全体でやるべきだと考えました。 考えてからは開発組織のリーダー層で相談しBOXIL SaaSとBALES CLOUDでFourKeysを導入を提案し実行していきましたが、上手く広げていくためにはどのようなことが必要なのかは私も迷いながらも進めていった経験を共有したいと思います。 横に広げるために必要な要素 振り返るとですが横に広げる際に下記の要素が大切だなと思っています。 ただ初めから気づいて定義できていたわけではなく探索と検証を繰り返した結果この要素だなと感じている部分です。 ここは実際は色々試行錯誤をしました。 橋を作ってくれる協力者をつくる FourKeysの目的を明確にする FourKeysが与える身近な効果を伝える FourKeysへの取り組みをしやすくし習慣化する 橋を作ってくれる協力者 これは横という表現をした意図でもありますが、FourKeysはトップダウンでやることが決まったものではなく1プロダクトが初めてFourKeysを活用し横に広げました。 私はBALES CLOUDのエンジニアなのでBOXIL SaaSチームの解像度は低くどのように伝えればいいかを考えるための情報が足りない状態でした。 このためプロダクト組織として横に広げるには隣のプロダクトの知識や現場の声を拾い上げることが可能で、チーム状況の解像度が高い人に一緒に作っていってもらう必要があります。 この一緒にやってもらう協力者を作るためにはまずは1人に共感してもらい仲間になってもらうことがとても大事です。 このため下記の部分を資料を元に丁寧に伝えました。 FourKeys導入の意図 期待する効果 今後どのようにしていきたいか? 実際かなり助けてもらうことが多くBOXIL SaaSチームにはとても感謝しています! FourKeysの目的を明確にする FourKeysは生産性の指標としてよく使われますが、実現したい状態は生産性を上げた先にあります。 あくまで私も含めてFourKeysは目指す場所に到達するために見るべき指標の1つとして捉えています。 また一緒に目指していく人たちにも生産性を上げた先でやりがいのあるものがちゃんとある状態にしたかったというのもあります。 実際にこの定義は「生産性をあげ試行回数を上げることで価値への到達を早くすること」を目的としました。 プロダクト作りは一度作って終わりではなく、FBサイクルを回して研ぎ澄ませて価値あるものにしていくのでそのためにはFBを受ける回数を増やすことが大事という考えを元にしてこの定義にしました! この部分認識が異なってはやる意義が薄れてしまうので伝える際には資料を作成したうえで説明しました。 FourKeysが与える身近な効果を伝える 生産性は人によってはすこし遠いように思えるかもしれません。 そうなるとFourKeysを元に改善することでの報酬がわからなくなり、良い取り組みでも取り組まれなかったり 人によって取り組みへの温度差が発生しチームとしての取り組みができない状態になってしまいます。 この問題はFourKeysというものが開発という活動の身近な部分にどんな影響を及ぼすかのが明確になっていないことが問題になり起きていました。 そのためこの対策としてFourKeysを元に改善することで起こる身近な改善や問題があることを伝えました。 さまざまな要素はある中で開発において身近であろう点を下記のスライドで伝えました。 うまいこと伝えられたかはわかりませんが、反応が良かったので伝わったのかなと思っています。 FourKeysへの取り組みをしやすくし習慣化する どんないい取り組みも一歩目が難しく重くなります。 このためいかに簡易的に確認できるかと最初にやることのハードを下げるかが重要です。 またハードルを下げたうえでやることが当たり前になるように習慣化していくことが大事になります。 ハードルを下げる こちらは協力してくれたBOXIL SaaS側のリーダーに相談しチーム状況やチームのFourKeysへの認知度を確認しアクションを一緒に考えました。 別チームのことは完全には理解できないため一人で考えずに協力を依頼したことで適切なハードル設定にできたかなと感じています。 課題に近く簡易的に確認可能なものを用意する 取り組む人たちも自分たちへのメリットがないものや課題感が無いものには取り組めないかと思います。 取り組むためには自分たちの視界に入っている身近な課題を解決できる手段の一つであることを説明したうえで関係性のある数値を出すことが重要でした。 直近の課題としてはレビューの時間が長くなっている傾向があるという話を聞いていたので、FourKeysのリードタイムの中でもレビューからApproveまでに時間がかかっていることをデータで示し 追加でレビュー時間とレビューが分散されているかをダッシュボードで可視化しました。 習慣化する BALES CLOUD BALES CLOUDではFourKeysのリードタイムに関連する指標として完了率というものを定義しスプリントごとに開発アイテムの完了率を見るようにしました。 まずはスプリントごとに完了することを意識したうえで、FourKeysのダッシュボードは誰でも見れる状態にしました。 これによりチームが達成意識をもつとともにチーム全体でリードタイムを改善する意識がついたと思います。 言葉として「今週はレビューが遅くなってしまった」や「全体の完了率が悪かったなど」が振り返りででてきたのが印象的でした。 習慣化する BOXIL SaaS また、BOXIL SaaSはレトロスペクティブで毎回FourKeysを確認するというアジェンダを入れたところFourKeysを意識する習慣ができたそうです。 実際にデータで見るだけでもリードタイムは順調に改善しており、コードレビューも分散されている状態になっていました。 これはとても嬉しかったです。 今後目指したいところ データで生産性や価値を語れる組織を目指していくとともに自分たちが生産性をあげることで企業の競合優位性になっていきたいなと考えています。 どんなプロダクトでも価値につながるものを作り続けられるわけではなく、いくつかの試行を繰り返し価値へ到達すると思います。 なのでこの試行回数をいかに早く実行し価値にたどり着くのを早くするかはプロダクトや事業の競合優位性になりうると考えています。
アバター
遭遇してしまった問題 解決策 おわりに こんにちは!! BOXIL SaaSのエンジニア兼テックブログチームの平社員をしているブラーバです。最近は働きが認められ、テックブログチームで確固たる地位を築きつつあるとかないとか...。 今回は以前公開した React Hook Form、Zod、Recoilを組み合わせたフォームを作る! にならい、React Hook FormとZodを使ったフロントエンド開発の第二弾です!! 本記事では、APIリクエストが必要なバリデーションをReact Hook FormとZodを使って実装しようとした際に、遭遇した問題とその解決策について話します。 同じような問題に直面している人、あるいはReact Hook FormやZodに自体に興味がある人の参考になると嬉しいです!! 遭遇してしまった問題 BOXIL SaaSでは一部フォームをReact Hook Formで作り、バリデーションにはZodを使用しています。 そのバリデーションの中には、ユーザーが入力した内容ががユニークかどうかを確認するために、バックエンドに問い合わせる項目がありました。 Zodの仕様では、いずれかの項目が入力されるたびに都度バリデーションが行われるため、問い合わせが必要な項目以外を入力していても、APIリクエストをしていました。React Hook Formのリポジトリでも同様の議論がされています。 https://github.com/orgs/react-hook-form/discussions/9005 以下のコードは isUniqueName という関数で入力された name がユニークかどうかを確認するために、入力ごとにAPIリクエストを送信しているコードです...。 import { zodResolver } from "@hookform/resolvers/zod"; import { useForm } from "react-hook-form"; import { z } from "zod"; const isUniqueName = async (name: string) => { console.log("isUniqueName: ", name); // ここでAPIリクエストを飛ばし、DBに保存されている同一のnameがあるかを確認したい return true; }; const useUserForm = z.object({ id: z.string(), name: z.string().refine(isUniqueName), }); type UseUserForm = z.infer<typeof useUserForm>; const defaultValues: UseUserForm = { id: "", name: "", }; export function Hoge() { const { register, handleSubmit } = useForm<UseUserForm>({ resolver: zodResolver(useUserForm), mode: "onChange", defaultValues, }); return ( <> <form onSubmit={handleSubmit((data) => console.log(data))}> <input {...register("id")} /> <input {...register("name")} /> <button type="submit">submit</button> </form> </> ); } 実際に上記のコードを動かしてみると、 id など他項目を入力していても isUniqueName が呼ばれてしまい、実際に console.log の部分をAPIリクエストに置き換えたとすると、計10回もAPIリクエストが飛んだことになります。 改修前のconsole.log 上記画像だと、本来ならAPIリクエストは最大でも4回に抑えたいです。 解決策 そこでブラウザのメモリ上にすでにAPIコールしたユーザー名を保持するという方法でAPIリクエストの回数を減らしました。 具体的には、APIに問い合わせをしたユーザー名をMapオブジェクトで管理し、再度同じ名前でバリデーションが走ったときにはAPIリクエストをせずに false を返すようにしました。 これでAPIを叩く回数を大幅に減らすことができました。 import { zodResolver } from "@hookform/resolvers/zod"; import { useForm } from "react-hook-form"; import { z } from "zod"; const existsName = new Map<string, boolean>(); const isUniqueName = async (name: string) => { if (existsName.has(name)) { return false; } console.log("isUniqueName: ", name); existsName.set(name, true); return true; }; const useUserForm = z.object({ id: z.string(), name: z.string().refine(isUniqueName), }); type UseUserForm = z.infer<typeof useUserForm>; const defaultValues: UseUserForm = { id: "", name: "", }; export function Hoge() { const { register, handleSubmit } = useForm<UseUserForm>({ resolver: zodResolver(useUserForm), mode: "onChange", defaultValues, }); return ( <> <form onSubmit={handleSubmit((data) => console.log(data))}> <input {...register("id")} /> <input {...register("name")} /> <button type="submit">submit</button> </form> </> ); } 実際に上記のコードを動かしてみると重複したログは出力されず、無駄なAPIリクエストが減っていました。 改修後のconsole.log おわりに 以上、React Hook FormとZodを使って非同期バリデーションを最適化した話でした。 また、実際にBOXIL SaaSの開発時にこの問題に遭遇したときには React Hook FormでZodを使うときの5つパターン を参考に本記事のような実装をしました。 今回は、React Hook FormとZodを使ったフロントエンド開発の第二弾でしたが、第三弾も近いうちに公開する予定ですので、お楽しみに!!
アバター
挨拶 初めに 対象読者 実行環境 Mojoとは 現状のMojoの導入方法 MojoとPythonの実行時間の比較 Pythonのコード Mojoのコード 結果 Local LLMの実行 llama2.py llama2.c llama.mojoの実行時間の比較 llama2.py(Python) 実行コマンド 生成された文章 llama2.c(C) 実行コマンド 生成された文章 llama2.mojo(Mojo) 実行コマンド 生成された文章 結果 結論 挨拶 京都開発拠点でインターンをしてるぱんちです(a.k.a 田中 大貴) 拙い文章ですが初めて記事を書かせてもらいました! 業務でAIの調査などをしておりその過程でMojoでLLMを動作させたので最後までお付き合いください! 初めに 京都開発拠点のインターンではAIの調査、AIを用いた開発を行っています。 デバッグ時はローカルでAIのモデルを動作させることも多い上にPythonなので、高速に動作させる方法を模索しています。 その過程で速度の問題を根本的に解決できそうなプログラミング言語Mojoを見つけたので、LLMを動作させてPythonと速度を比較したのでまとめてみたいと思います。 対象読者 Mojoの速度が気になる人 Local LLMに触れたいけど重いから実行できない人 実行環境 Intel MacBook Pro 2.3GHzクアッドコアIntelCorei7 メモリ32GB Docker Ubuntu(イメージ:mcr.microsoft.com/devcontainers/base:jammy) Mojoとは MojoはPythonの代替のAIを拡張するプログラミング言語。Pythonと比較して68000倍の速度が出るらしい……。 Pythonと互換がありPythonのライブラリをMojoで使うことができます。ただし、Pythonのライブラリはインタプリで動作するので高速化はしないようです。 型は強く構文などはPythonに似ています、ありがたい。 現状のMojoの導入方法 現状MojoをインストールするにはModulerにアカウント登録、ログインが必要です。 https://www.modular.com/mojo OSによって導入方法が違いますがIntel Macでは動作しないようなので、今回はDockerで導入します。 DockerでUbuntuのコンテナを立てて必要なものをインストールします。 apt-get install -y apt-transport-https && keyring_location=/usr/share/keyrings/modular-installer-archive-keyring.gpg && curl -1sLf 'https://dl.modular.com/{hash}/installer/gpg.0E4925737A3895AD.key' | gpg --dearmor >> ${keyring_location} && curl -1sLf 'https://dl.modular.com/{hash}installer/config.deb.txt?distro=debian&codename=wheezy' > /etc/apt/sources.list.d/modular-installer.list && apt-get update && apt-get install python3.10-venv && apt-get install -y modular # Moduler CIのインストール Moduler CIを認証してMojoをインストールします。 modular auth {token} && modular install mojo 最後にパスを通します。 echo 'export MODULAR_HOME="/home/ubuntu/.modular"' >> ~/.bashrc echo 'export PATH="/home/ubuntu/.modular/pkg/packages.modular.com_mojo/bin:$PATH"' >> ~/.bashrc source ~/.bashrc MojoとPythonの実行時間の比較 MojoとPythonの実行時間を試し割り法を使い、素数を一億個まで求めてるコードの実行時間を比較してみます。 参考: https://transparent-to-radiation.blogspot.com/2023/05/20235.html Pythonのコード def calc (upper: int ): prime_numbers = [] def is_prime_number (n): for pn in prime_numbers: if pn * pn > n: break if n % pn == 0 : return False return True for n in range ( 2 , upper + 1 ): if is_prime_number(n): prime_numbers.append(n) calc(100_000_000) Mojoのコード from utils.vector import InlinedFixedVector fn calc(upper: Int): var prime_numbers = InlinedFixedVector[Int](100_000_000) for n in range(2, upper + 1): if is_prime_number(n, prime_numbers): prime_numbers.append(n) fn is_prime_number(n: Int, prime_numbers: InlinedFixedVector[Int]) -> Bool: for i in range(0, len(prime_numbers)): let pn = prime_numbers[i] if pn * pn > n: return True if n % pn == 0: return False return True fn main(): calc(100000000) 結果 実行時間はtimeコマンドのrealの数値です。 | | Python | Mojo | | ---- | ---- | --- | | 実行時間(一回目) | 7m28.549s | 0m12.865s | 実行時間(二回目) | 7m42.270s | 0m12.370s 7分ほどかかっていたものが12秒に短縮されました。 思っていた以上に速い〜! Local LLMの実行 Hugging Face(モデル,ライブラリ)などは現状インタプリタで動作するためllama2.mojoを実行してみます。 llama2.mojoはMetaのllama2をmojoで実装したものになります。 まずllma2.mojoを git clone してきます。 git clone https://github.com/tairov/llama2.mojo.git cd llama2.mojo 次にモデルをダウロードします。 wget https://huggingface.co/karpathy/tinyllamas/resolve/main/stories15M.bin モデルのダウンロードが終われば以下のコマンドで実行できるようになります。 mojo llama2.mojo stories15M.bin -s 100 -n 256 -t 0.5 -i "Mojo is a language" llama2.py llama2.c llama.mojoの実行時間の比較 llama2は言語ごとに実装されているのでPythonとC,Mojoでの実行時間を比較します。 llama2.py(Python) 実行コマンド time python3 llama2.py stories15M.bin 0.8 256 "Dream comes true this day" 生成された文章 <s> Dream comes true this day. Behinda's eyes, there is a boo-boo from a bird. It is happy. It has a big black stuff on its head. It can move in the night and that is when it is full. It can use necks and sticks and touch its wings. It can make sounds and pull its hair. Be careful, and be gentle. Beak to it. Beak to be gentle. Beak's friend is a firefighter. He helps put out fires with a hose. Beak and his dog are safe. Beak is very brave. <s> Once upon a time, there was a little girl named Lily. She loved to play games with her friends. One day, they decided to play a game of hide and seek. Lily was very good at hiding and she won the game. After the game, Lily's friends noticed that she didn't have many toys to play with. They asked if she could share her toys with them. Lily was happy to share and said yes. But then, Lily's mom came and told her that it was important to share and be kind to others. Lily achieved tok/s: 0.6090875867949811 llama2.c(C) 実行コマンド time ./run stories15M.bin -t 0.8 -n 256 -i "Dream comes true this day" 生成された文章 Dream comes true this day. A young girl named Amy and her best friend John are playing together in the park. Amy has her doll and John has a different doll. He has a short doll. Amy is the one who comes to the park and wears her doll. "Wow, Lucy, look at my doll. She is very pretty and Anna is very nice. She can sing and dance," Amy says. "Hi, Anna. You are very pretty and smart. I like your doll. She is very kind. She can sing and dance too," Lucy says. They smile and hug each other. They are happy to meet each other and their doll. They decide to play a game with their doll. They take turns holding their doll and making her sing and dance. They have fun. achieved tok/s: 30.661249 llama2.mojo(Mojo) 実行コマンド time mojo llama2.mojo stories15M.bin -n 256 -t 0.8 -i "Dream comes true this day" 生成された文章 num parallel workers: 8 SIMD width: 64 checkpoint size: 60816029 [ 57 MB ] | n layers: 6 | vocab size: 32000 Dream comes true this day! Someone had managed to use the magic language. A prince appeared with a smile on his face and a beautiful cape. The prince smiled and said, "You are a very special prince." Dream and the prince were in a big fight. But no one wanted to fight and they all said, "You are too lazy!" They kept arguing until the prince was tired. He said, "If you don't fight, I will come and get you!" The prince said, "Ok, I will fight you and you cannot do it!" So they all fought and laughed. But the prince was too lazy to fight and said: "No, I won't fight you!" The prince was so mad and he left and never talked to the prince again. The prince was sad but he was glad he was safe. He still had a tiny piece of the magic language that he wanted to remember, and he was never lazy again. achieved tok/s: 34.066479245907061 結果 Python C Mojo 実行時間(一回目) 7m0.722s 0m6.186s 0m6.902s 実行時間(二回目) 7m13.986s 0m3.542s 0m7.099s 結論 生成された文字数にもよるがPythonと比較するとかなり速いです。約35倍速。 流石にCよりは遅いです。 今回は言語モデルがパラメーターが少なく軽い物なので全体的に短時間ですが、他のモデルがMojoで動作すればローカルLLMが軽快に動作しそうというロマンを感じます!!! Ptyhonの速度に満足できない人はぜひ使ってみてください。
アバター
はじめに まずは結果 遅かった要因と改善方法 対応1:生成するテストデータを減らした。 背景 対応 結果 対応2:観点外の処理をMock化し、テスト観点において不要な処理が実行されないようにした。 背景 対応 結果 対応3:テストの実行を並列化した。 背景 対応 なぜparalell_testsにしたか? 導入 ~ 手順 ~ 導入 ~ 困ったこと ~ 導入 ~ 並列数の決定 ~ 結果 今回の改善を通してわかった、今後RSpec記述時に気をつけたいこと テストデータは最小限にしよう。 大量件数での"create_list"はやめよう(大量件数では"create_build + import"を使おう)。 テスト観点を分け、観点に関係ない部分で時間がかかる処理がないか考えよう。 今後の展望 おわりに はじめに こんにちは!スマートキャンプ開発エンジニアの末吉(だいきち)です。 私がチームメンバーとして日々開発している「BALES CLOUD」では、RSpecを割としっかりと書いております。 ただ近頃、RSpecの実行時間が長くなってきており、開発に少し支障をきたすようになってきました。 そこで開発の生産性を上げるべく、RSpecの実行時間短縮を試みたので、 今回は、こちらの件についてお話ししたいと思います! まずは結果 Before: 40〜50[min] After: 11〜18[min] 上記通り、30[min]ほど短縮できました! 参考までに、circleciでのテスト実行時間推移を貼っておきます。(対応時期:9月14日〜10月3日) circleciでのテスト実行時間推移 またこれは狙ったわけではないのですが、テスト実行時間を短縮できたことにより、circleciのcredit使用量も減らせました! 参考までに、circleciのcredit使用量推移を貼っておきます。(対応時期:9月14日〜10月3日) circleciのcredit使用量推移 遅かった要因と改善方法 今回行った大きな対応が3つあるため、その対応と背景について書いていきたいと思います。 対応1:生成するテストデータを減らした。 まずは1つ目。 背景 ある特定クラスのテストにおいて、一通りの標準的なデータを、すべてのケースで作成する構成となっておりました。 具体的には、最上位階層部分で let! が使用されていたり、FactoryBotのコールバックでデータを生成していたりという感じです。 このような構成にすることで、DRYに書けることがあったりなどのメリットはあるかと思います。 しかし今回は、データ準備に1ケースあたり30[sec]近くかかっていたため、過剰な状態となっておりました。 対応 以下を行い、各テストケースで作成するデータを削減しました。 最上位階層の let! を let に変更し、データが必要なケースでのみデータを生成するようにした。 FactoryBotコールバックで生成するデータを減らした。 (今回は行いませんでしたが、Active Recordのコールバックでデータ生成している場合は、テストにおいてはそちらを停止することも検討してみて良いか思っております。) 結果 これで8[min]ほど削減できました! 対応2:観点外の処理をMock化し、テスト観点において不要な処理が実行されないようにした。 次に2つ目。 背景 バリデーション観点のテストで2000件のデータを作成しており、 これによってテスト対象の処理が2000件分行われていました。 対応 バリデーション観点のテストでは、メイン処理部分をMockにしました。 これによって、大量データで確認する部分を最小限にできました。 また大量データを生成する際は、 create_list をやめて、 build_list + import を使うようにしました。 create_list では指定した件数分のクエリが発行されます。 build_list + import にすることで、発行されるクエリの件数を減らすことができ、データ準備の時間を削減できました。 結果 これで7[min]ほど削減できました! 対応3:テストの実行を並列化した。 一番効果のあった対応がこちらであり、導入も簡単であったためおすすめです! 背景 「 対応1 」「 対応2 」によって、特定のテストに極端な時間がかかることは無くなりました。 ただし実行時間がゼロになることないので、テストの総量が多い場合は、 「書き方を工夫しての実行時間の削減」には限界があります。 BALES CLOUDでは冒頭では書いたとおり、割としっかりRSpecを書いております。 そのため、「書き方の工夫」だけでこれ以上大幅な改善をするのは、難しい状況にありました。 対応 paralell_tests を使用してテストの実行を並列化しました。 なぜ paralell_tests にしたか? 並列化は circleci で行なう方法もあったのですが、今回は paralell_tests による並列化を選択しました。 理由としては、以下です。 導入が簡単そうであったこと。 circleci で並列化する場合は、使用クレジットの増加が考えられるが、 paralell_tests で並列化する場合は、使用クレジットの削減の可能性もあること。 circleci から別のサービスへ乗り換えた場合に、 paralell_tests で並列化しておくと、無駄にならないこと。 導入 ~ 手順 ~ 手順については、すでにさまざまな方が紹介しているためここでは省略しようかと思います。 導入 ~ 困ったこと ~ 並列化の導入準備が終わり、いざ実行してみると、テストが落ちてしまいました。 「直列で全テストを流した場合」および、「単体でテストを実行した場合」では成功するテストが、 「並列で全テストを流した場合」には、失敗してしまう状況です。 原因 BALES CLOUDではテストに webmock を導入しているのですが、 after で WebMock.disable! している箇所があり、後続のテストでスタブが使われておらず、落ちておりました。 対応 スタブが必要な箇所に WebMock.enable! を追加して対応しました。 after での WebMock.disable! を取り除く方法もあり、最終的には方針を決めてコードも揃えたいと思っておりますが、 今回の目的とは異なるため、いったん WebMock.enable! を追加する方法で対応しました。 導入 ~ 並列数の決定 ~ 並列数は結果としては「4」にしました。 決定方法としましては、「2 -> 4 -> 6」と試していき、「4」が頭打ちとなりそうであったため決定しました。 結果 これで20[min]ほど削減できました! 実は「 対応1 」「 対応2 」の前にいったん試したときは、7[min]ほどしか改善しませんでした。 しかし、「 対応1 」「 対応2 」によって極端に重いテストを取り除くことで、大幅な改善ができました。 おそらく処理詰まりが解消されたためかと思われます。 もし並列化をしても思ったより改善しない場合は、極端に重い処理の見直しをすると良いかもしれません! または、 paralell_tests には「runtime log」というものがあり、 これを残すことで、次回のテストの振り分けを実行時間がなるべく均等になるようにしてくれる機能もあるようです。 そのため、そちらを試してみても良いかもしれません! 今回の改善を通してわかった、今後RSpec記述時に気をつけたいこと 最後に、今回の対応を通して今後気をつけたいと感じたことを書いていきたいと思います。 テストデータは最小限にしよう。 DRYに書くためなど、さまざまな理由でロジックに直接関係のないテストデータを用意したくなることもあるかと思います。 しかし、そのようなコードが大量に生成されてから後々削ろうと思うと、 1つ1つの効果が薄くて速度改善に時間がかかったり、FactoryBotコールバックの場合は修正範囲が大きくなってしまったりする可能性も考えられます。 そのため、直接ロジックに関係ないテストデータを作成することは安易にせず、よく考えてから行った方が良いかと感じました。 特に、最上位階層で let! を使用していたり、FactoryBotのコールバックでデータ生成している場合は要注意かなと感じました。 大量件数での"create_list"はやめよう(大量件数では"create_build + import"を使おう)。 create_list で大量のテストデータを作成すると、件数分のクエリが発行されてしまってデータ生成時間が少し多くなるため、 大量データ生成では、 create_build + import を使った方が良いと感じました。 テスト観点を分け、観点に関係ない部分で時間がかかる処理がないか考えよう。 重たい処理におけるバリデーションのテストなど、観点に関係ない部分で時間がかかる可能性を考えて、もし存在する場合は、観点外の部分をモックにすることを積極的に検討してみても良いかと感じました。 今後の展望 今回大幅に実行時間を短縮できたRSpecですが、 まだ短縮の余地はあるので更なる短縮に努めたいと思っております! また今後機能拡張と共にRSpecを書いていく中で、書き方によっては実行時間が再度増大することも予想されます。 レビューやチームへの知見展開でもカバーできますが、 より安定した維持を実現するためには、静的解析などの「人依存」でない方法を取れると良いかと思っております。 そのため次は、短縮した実行時間をより簡単に維持できる方法を模索していきたいと思っております! おわりに ここまで読んでいただきありがとうございました! この記事が皆さまの参考になれば幸いです! また更なる進展がありましたら、ご紹介できればと思っております! それでは!
アバター
はじめに 対象読者 理想のディレクトリ構成 取り組んだこと リファクタリングに至った背景 チームで決めたこと、行ったこと 現状把握 理想の構成 トライ 結局シンプルがいい デザインパターンを積極的に取り入れた結果 取り除いたもの Interactor Facade Query View Component Service リファクタリングしてどうなった? 小 ~ 中規模であれば おわりに はじめに こんにちは。 イベントプラットフォーム「 BOXIL EVENT CLOUD (以下、BECといいます)」開発エンジニアの石井です。 今回はRuby on Rails(以下、Railsといいます)アプリケーションにおけるコードリファクタリングについてお話します。 BECはオフショア開発でずっと進めていたのですが、1年半ほど前からオンショア(社内)開発に切り替えました。 そこからシステム統合、Railsのバージョンアップ対応を経て、少し落ち着いてきた頃から開発メンバーから「可読性が悪く開発し辛い」と声があがり、リファクタリングする流れになりました。 本稿では、チームでのリファクタリングの取り組み方を中心に話したいと思います。 対象読者 チーム開発におけるリファクタリングへの取り組み方に悩んでいる方 中規模のRailsアプリケーションディレクトリ構成のオススメを知りたい方 デザインパターンを積極的に取り入れた結果について知りたい方 理想のディレクトリ構成 はじめに、チームで決めたリファクタリング方針(ディレクトリ構成)をお見せします。 appディレクトリ構成 app/ アプリケーション用のディレクトリ app/assets/ アプリケーション用のリソースを置くディレクトリ app/cache_storages/ キャッシュデータ操作用のディレクトリ app/channels/ Action Cableファイル用のディレクトリ app/controllers/ コントローラ用のディレクトリ app/decorators/ ModelとViewの中間に位置しており、データの装飾用のディレクトリ app/helpers/ ヘルパー用のディレクトリ app/javascript/ JavaScript関連のスクリプト用のディレクトリ app/jobs/ Active Job用のディレクトリ app/mailers/ Action Mailerファイル用のディレクトリ app/models/ モデル用のディレクトリ app/models/modules/ データ加工処理用のディレクトリ app/reflexes/ stimulusReflex用のディレクトリ app/views/ ビュー用のディレクトリ app/workers/ sidekiq用のディレクトリ ↑を目指すうえで削除すると決めたもの # システム統合時の負債 app/models/concerns/event_management_concerns app/models/event_management_models lib/event_management # デザインパターン app/queries/ app/components/ app/interactors/ app/facades/ app/services/ ディレクトリ構成は Railsの標準フォルダ構造 を参考にしました。 削除対象や理由については、後ほど説明します。 取り組んだこと リファクタリングに至った背景 「はじめに」でも触れましたが、システム開発をもともと100%オフショアで行っており、その体制を2022年7月から本格的にオンショア開発に切り替えました。 当初は、引き継いだインフラ構成やコードを元に軽微な不具合改修および追加機能実装を進めていく予定でした。しかし「クリティカルな不具合が頻発する」「応答速度が遅くユーザー離脱の可能性が高い」など、追加機能実装より優先して取り組まないといけない課題がたくさん出てきました。 それらを解決するために、不具合修正を進めつつ、不必要に分かれていたシステムのシステム統合、RubyおよびRailsのバージョンアップ対応など大きな改修を中心に取り組んできました。 大きな改修を経てコードと向き合う時間が増えてきた頃、「コードの可読性が悪い」「影響範囲が追いづらい」など、チーム内でコードに対する不満の声が大きくなっていきました。 引き継いだコードは、デザインパターンを積極的に取り入れた構造になっており汎用的に使える一方、抽象度が高いのでコードリーディングのコストが高い課題がありました。 そういった背景から、開発メンバーで集まって「開発しやすい状態・構成」について議論するミーティングを行ったのがきっかけで、リファクタリングが本格的にスタートしました。 チームで決めたこと、行ったこと 現状把握 課題に対して開発チームで認識のすり合わせを行い、2つの理由で開発体験が損なわれていることが分かりました。 ■システム統合時の負債が残っている BECは過去の開発事情から2つのシステムに分かれていましたが、これが負債の一つになっており、2つのシステムを片方に寄せる形でシステム統合しました。 システム統合で不具合があった際に、システム統合起因による原因か切り分けるため、可能な限りコードをそのまま移行しました。 その結果、動作的には問題ないものの「データ連携処理」「連携データ加工クラス」「連携データを扱うモデルおよびライブラリ群」など、本来1つのシステムを動かすには不要な構成が残っている状況となりました。 システム統合前のイメージ図 システム統合前のイメージ図 イベント視聴とアーカイブ視聴でシステムが分かれており、APIを利用してデータ連携をしていました。 ■デザインパターンを使うことで複雑になっている 「Query」「View Component」「Interactor」「Facade」4つのデザインパターンが使用されており、データ取得処理・加工処理が複数ファイルに分散している状況でした。 また、データの受け渡しは基本的にインスタンス変数を用いているので、インスタンス作成しているコードとインスタンスメソッドを実行しているコードが離れていることが多く、ファイルを行き来するのに時間がかかっていました。 理想の構成 「システム統合の負債によるコードの冗長化」「デザインパターンによる複雑化」「インスタンス変数によるコードの読みにくさ」を解決するため、できる限りシンプルな構成を目指してチームで議論しました。 その結果、現状のBEC規模のWebアプリケーションであれば Railsの標準フォルダ構造 に「デコレーター(app/decorators)」「モジュール(app/models/modules)」を追加した形で問題なさそうだという結論になりました。 再掲:理想のディレクトリ構成 app/ アプリケーション用のディレクトリ app/assets/ アプリケーション用のリソースを置くディレクトリ app/cache_storages/ キャッシュデータ操作用のディレクトリ app/channels/ Action Cableファイル用のディレクトリ app/controllers/ コントローラ用のディレクトリ app/decorators/ ModelとViewの中間に位置しており、データの装飾用のディレクトリ app/helpers/ ヘルパー用のディレクトリ app/javascript/ JavaScript関連のスクリプト用のディレクトリ app/jobs/ Active Job用のディレクトリ app/mailers/ Action Mailerファイル用のディレクトリ app/models/ モデル用のディレクトリ app/models/modules/ データ加工処理用のディレクトリ app/reflexes/ stimulusReflex用のディレクトリ app/views/ ビュー用のディレクトリ app/workers/ sidekiq用のディレクトリ ■デコレーター(app/decorators) 引き継いだコードから組み込まれていたもの。 ModelとViewの中間に位置しており、Modelの値をViewで使用したい形に装飾処理が入っています。 必須で必要ではないものの、削除する必要もなかったので残しました。 デコレーターを使用しない場合は、Modelクラスに装飾処理を追加するといいと思います。 ■モジュール(app/models/modules) ファットモデルを回避するためのコード置き場で、新しく追加したディレクトリです。 簡単なデータ操作はモデル(app/models) 、 複数テーブルを跨いでデータ操作を行なうなどの複雑な処理はモジュールに切り出しました。 モジュールの導入ですが、Rails経験が10年以上あるベテランエンジニア(以下、Mさんといいます)から提案頂きました。 ネット検索しても特にヒットしないので一般的な考えではないかもしれませんが、BECのコードにおいて存在感は大きく、リファクタリングで一番恩恵を感じている概念になります。 トライ 2023年6月に方針を固めて、7月からリファクタリング着手予定でした。 方針を固めたタイミングで、リファクタリングへのモチベーションが高まっており「不具合タスクの中で取り組みはじめよう!」ということで前倒しでスタートしました。 不具合タスクのコードレビューコストは大きくなりましたが、コードレビューを通じて、具体的な対応方法についてのメンバー間の認識ズレが次第に無くなっていくのを感じました。 「鉄は熱いうちに打て」というように、方針決めてから取り組みまでの期間は短いほうが良いと思いました。リファクタリングをスケジュールする際は「方針決め ~ トライ」のセットが短いスパンで実行できるようにスケジュール調整することをオススメします。 結局シンプルがいい デザインパターンを積極的に取り入れた結果 うまく活用できず、メリットよりデメリット(可読性の悪さ)が目立ちました。 活用できなかった原因として、引き継いだコードだったことも関係してますが、アプリケーション規模が大きくない内はデザインパターンは使わなくていいと感じました。 「Query」「Facade」の処理はモジュール(app/models/modules)に置き換え、「View Component」は部分テンプレート(render partial)に置き換えることで開発体験が向上しました。 取り除いたもの Interactor ユーザー登録動線のワークフローを中心に使用。 ワークフローの各ステップのデータ連携にcontextを使用していた。 context起因でエラーが発生した場合、contextの状態がパッと見で判断できない、ユーザー登録なのでエラー発生時はロールバックしてほしいなどの理由で廃止。 Facade データ取得・データ整形など複数モデルのデータ操作をしており複雑で読み辛かった。 インスタンスをビューに渡し、ビューからインスタンスメソッドを実行するのでクエリ制御があまく、無駄なクエリ実行によりパフォーマンスを悪くしていた。 モジュールに切り出すことで上記の問題を解決。 Query 本来、Active Record::Relationに対して操作し、Active Record::Relationを返す必要がある。 しかし、実装はそうなっておらずActive Record::Relationやデータ整形後のhashを返却する状態になっていた。 hash利用がパフォーマンス悪化・可読性悪化の原因になっていたので修正しつつ、一度に置き換えることができないのでデータ操作はモジュールに切り出すことにした。 View Component 再利用性が高いView Componentが、実装上そこまで再利用されていなかった。 機能部分と描写部分を分けられることがメリットですが、機能部分をほとんど活用しておらず部分テンプレートを使用するのと変わりがなかった。 コードを読む際に、機能部分を冗長に経由するので可読性が悪く部分テンプレートに置き換えた。 Service インスタンスメソッドになると処理が追いづらく、インスタンスのライフサイクルも考えないといけない。 ベテランエンジニアのMさんの経験上、Serviceクラスを上手く活用できているプロジェクトを見たことがなく、Serviceクラスが存在することで困ったケースが多かった。 以上の理由から、処理をモジュールに切り出した。 リファクタリングしてどうなった? 直感的にコードが追えるようになり、コードレビューや調査タスクにかかる時間が短縮されました。 データ取得処理が分散していることで複雑になり「N+1問題」を抱えているコードが多い印象でしたが、モジュールにまとめることで見通しがよくなり発生頻度を減らすことができたと感じています。 小 ~ 中規模であれば 小 ~ 中規模のWebアプリケーション開発であれば、標準的なRailsの構成(MVC)で問題ないと思いました。 ファットモデルになりそうであればモジュール(app/models/modules/)を追加して、簡単なデータ操作はモデル、複雑なデータ操作はモジュールで行なうことで解決できます。 デザインパターンの導入は、規模が大きくなりモジュールの運用で課題が出てきた際に検討し始め、モジュールをデザインパターンにどう落とし込めばいいのかチームで話し合って決めることをオススメします。 おわりに 引き継いだコードのリファクタリングだったので、少し特殊なケースのご紹介となりましたが、「Railsの標準構成 + モジュール」にすることで可読性がグッと上がり生産性が向上しました。 今回のリファクタリングは、ベテランエンジニアのMさんがチーム内にいることで、方針決めや実装がスムーズに対応できました。 経験豊富なエンジニアはデザインパターン・アンチパターンに精通していると思うので、積極的に意見を伺うことをオススメします。 また、Railsのアーキテクチャは調べてみると考案された様々なものが出てきます。 プロダクトの規模や特性にあったものを取り入れることも大事だと思います。特にServiceクラスの利用は意見が分かれるので、チームの意見・プロダクトとあっているか調べてみると良さそうです。 リファクタリングは現在も進めており、削除対象をすべて置き換えれていない状況です。 今後、モジュールの肥大化など新しい課題が出てきた際は随時アップデートしつつ、このような形でアウトプットできたらなと思います。
アバター
こんにちは、職人です! BOXIL SaaSとは 新規のフォームを作る React Hook Formとはなんぞや 基本的な使い方 Recoilとはなんぞや 基本的な使い方 Zodとはなんぞや 基本的な使い方 React Hook Form & Recoil & Zod を組み合わせると 苦労したこと 入力した値がRecoilのステートに保存されない select boxのonChangeが機能しない 今入力している項目以外の項目のバリデーションが実行されてしまう 次回へ続く こんにちは、職人です! スマートキャンプでBOXIL SaaSのエンジニアをやってます職人こと袴田です! 今回は新規会員登録の画面に関してUI/UXの向上のための施策を対応したことについて紹介します。 BOXIL SaaSとは BOXIL SaaSはSaaSを導入したいユーザーとSaaSを提供しているベンダーをつなぐリボンモデルのプロダクトです。 新規のフォームを作る BOXIL SaaSでは一部Reactを使用しています。 React Hook Form Zod Recoil 今回はこちらのライブラリを組み合わせてフォームを作成しました。 React Hook Formとはなんぞや React Hook Form とは、Reactでformを作るときに便利なライブラリです。 基本的な使い方 useFormから register と handleSubmit を取得し、それぞれinputタグのpropsとsubmit時の処理に渡します。 registerはスプレッド構文で展開されて、onChangeやonBlurなどのイベントハンドラーに渡されます。 // react-hook-formからuserFormをimport import { useForm } from "react-hook-form"; type FormValues = { firstName: string; lastName: string; }; function MyForm() { // useFormからregisterとhandleSubmitを取得 const { register, handleSubmit } = useForm<FormValues>(); // submit時の処理を定義 const onSubmit = (data: FormValues) => console.log(data); return ( // onSubmitにhandleSubmitを渡す <form onSubmit={handleSubmit(onSubmit)}> <input {...register("firstName")} /> <input {...register("lastName")} /> <input type="submit" /> </form> ); } Recoilとはなんぞや Recoil とは、Reactで状態管理をするときに便利なライブラリです。 基本的な使い方 1.RecoilRootを設定する RecoilRootを使用して、Recoilの状態を管理します。 下記の例ではAppコンポーネントに包括されるコンポーネントでRecoilの状態を取得できるようになります。 これが設定されていないと、Recoilの状態を取得できません。 import React from "react"; import ReactDOM from "react-dom"; import { RecoilRoot } from "recoil"; import { BrowserRouter } from "react-router-dom"; import { App } from "./App"; ReactDOM.render( <React.StrictMode> <RecoilRoot> <BrowserRouter> <App /> </BrowserRouter> </RecoilRoot> </React.StrictMode>, document.getElementById("exampleApp") ); 2. atom を定義する atomを使用して、管理したい状態を個別に定義します。 atomとはアプリケーションの状態を管理するための単位と思っていただければいいと思います。 import { atom } from 'recoil'; export const countState = atom({ key: 'countState', default: 0, }); 3. useRecoilState で状態を読み書きする useRecoilStateを使用して、値とセッターを取得できます。 セッターを使用し、値を更新できます。 import { useRecoilState } from 'recoil'; import { countState } from './atoms'; function Counter() { // countが値 // setCountがセッター // というイメージ const [count, setCount] = useRecoilState(countState); const increment = () => { // セッターを使って、countを更新する setCount(count + 1); }; return ( <div> <p>Count: {count}</p> <button onClick={increment}>Increment</button> </div> ); } Zodとはなんぞや zod とは、Reactでバリデーションをするときに便利なライブラリです。 基本的な使い方 z.objectを使用し、バリデーション実行対象の項目を設定したスキーマを定義します。 import { z } from "zod"; const schema = z.object({ email: z.string().email().max(10), name: z.string().max(10), }); type Data = z.infer<typeof schema>; const data: Data = { name: "tarou", age: 20 }; React Hook Form & Recoil & Zod を組み合わせると これらを組み合わせたフォームの作成例を簡単ではありますが、以下にまとめました。 フォームの状態、バリデーションなどはカスタムフック(useUserForm.ts)としてまとめており、MyForm側ではimportして使用しています。 useUserForm.ts import { zodResolver } from "@hookform/resolvers/zod"; import { useForm } from "react-hook-form"; import { atom, useRecoilState } from "recoil"; import { z } from "zod"; const userSchema = z .object({ email: z.string().nonempty().email().max(10), name: z.string().nonempty().max(10), company: z.string().nonempty() }) type UserForm = z.infer<typeof userSchema>; const defaultValues: UserForm = { email: "", name: "", company: "" } const form = atom<UserForm>({ key: "useUserFormAtom", default: defaultValues, }); export const useUserForm = () => { const [formValues, setFormValues] = useRecoilState(form); const { register, handleSubmit, getValues, control, trigger, formState: { errors }, } = useForm({ resolver: zodResolver(userSchema), mode: "onSubmit", defaultValues: formValues, }); const handleSetFormValues = () => { console.log(JSON.stringify(getValues())) setFormValues(getValues()); }; return { handleSubmit, register, control, trigger, setFormValues, formValues, handleSetFormValues, errors }; }; MyForm.tsx import { useUserForm } from "./useUserForm"; import { Controller } from "react-hook-form"; import Select from "react-select"; export function MyForm() { const userForm = useUserForm(); const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => { e.preventDefault(); await userForm.trigger([ "email", "name", "company" ]); userForm.handleSetFormValues(); }; const OPTIONS = [ { label: "A社", value: "1", }, { label: "B社", value: "2", } ] return ( <form onSubmit={(e) => handleSubmit(e)}> {userForm.errors.email?.message && <p>{userForm.errors.email?.message}</p>} <input {...userForm.register("email")} /> {userForm.errors.name?.message && <p>{userForm.errors.name?.message}</p>} <input {...userForm.register("name")} /> {userForm.errors.company?.message && <p>{userForm.errors.company?.message}</p>} <Controller name="company" control={userForm.control} render={({ field }) => ( <Select options={OPTIONS} value={OPTIONS.find( (option) => option.value === field.value )} {...userForm.register("company")} onChange={async (e) => e != null ? field.onChange(e.value) : null} /> )} /> <input type="submit" /> </form> ); } 苦労したこと ここからは実際にプロダクトに落とし込む際に試行錯誤したことをご紹介します。 入力した値がRecoilのステートに保存されない 例えば入力したフォームの値を次の画面やフォームに移動したときも保持したい場面があるかと思います。 しかし何も工夫せず画面遷移をしてしまうと、フォームに入力した値は保持できません。 Recoilの状態にも保存されていない状態になります。 Recoilを使用する場合はフォームに入力した値をRecoilの状態に保存するため、セッターを必ず呼ばなければいけません。 const handleSetFormValues = () => { console.log(JSON.stringify(getValues())) // ここで入力した値を出力 setFormValues(getValues()); }; const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => { // 省略 userForm.handleSetFormValues(); }; 前述の例ではonSubmitが発火したときに、handleSetFormValuesを経由してsetFormValuesを呼び出しRecoilの状態に保存しています。 select boxのonChangeが機能しない 選択式の入力項目には react-select を使用していました。 しかしReact Hook Formと組み合わせると、registerで展開されたonChangeは機能しなくなります。 react-selectはcontroledなコンポーネントであり、React Hook Formのregisterはuncontroledなコンポーネントにしか対応してないためです。 これを解消するためにReact Hook Formの Controller という機能を使用します。 Controllerでselect boxを囲い、renderの中でreact-selectを使用するとonChangeが機能するようになります。 <Controller name="company" control={userForm.control} render={({ field }) => ( <Select options={OPTIONS} value={OPTIONS.find( (option) => option.value === field.value )} {...userForm.register("company")} onChange={async (e) => e != null ? field.onChange(e.value) : null} /> )} /> 今入力している項目以外の項目のバリデーションが実行されてしまう React hook formではバリデーションの実行タイミングを指定できる モード があります。 これはonChange, onBlur, onSubmitなどのモードがあり、デフォルトではonChangeになっています。 onChangeを使用している場合は、入力している項目の値を変更するとバリデーションが実行されますが、フォームに存在する他の入力項目に対しても実行されています。 これは今現在仕様のようですので、あきらめましょう。 解決策はonSubmitにすることです。 const { register, handleSubmit, getValues, control, trigger, formState: { errors }, } = useForm({ resolver: zodResolver(userSchema), mode: "onSubmit", // ここをonSubmitにする defaultValues: formValues, }); trigger を使って任意のタイミングでバリデーションを実行できます。 何かのボタンをクリックしたときにバリデーションを実行する場合は以下のような実装になります。 <button type="button" onClick={() => { userForm.trigger([ "email", "name", "company" ]); }} > 次回へ続く 今回はReact Hook Form、Zod、Recoilを組み合わせてフォームを作成したときに苦労したことを紹介しました。 実は今回紹介したこと以外にもまだ苦労したことがいくつかあります。 次回引き続きご紹介したいと思います!
アバター
はじめまして、もしくはまたお会いしましたね。 BALES CLOUD (以下BC)エンジニアのてぃがです。 BCでは、最近フロントエンドのテストを始めました。 また、個人としても社内でフロントエンドのテストの普及啓蒙活動をやっております。 今回はこれらについてお話ししたいと思います。 ※注意※ はじめに 補記 BCはフロントエンドのユニットテストをどう始めたのか 1. 各種決め事 2. 手段の決定・詳細化 3. やってみる とはいえ、各ステップをどう流したのか? そうしてどうなった? おわりに ※注意※ この記事で取り扱う「フロントエンドテスト」は主に「フロントエンドのユニットテスト」です。 ご了承ください。 はじめに これまで、BCにはフロントエンドのユニットテストがありませんでした。 とはいえ、かわりにE2Eテストがあり、また、プロダクトの意品質に大きな問題もありませんでした。 しかし、BCの開発フェーズは「立ち上げ」から「機能強化」に移り変わっています。 その過程で、重要機能のまわりでちらほらとバグが見つかる、Vueのメジャーバージョンアップが必要になる(※)、などのことが起きはじめていました。 ところで、私はひとの十倍くらいの不安症です。 「石橋をたたきまくる」ように日々を過ごしているのですが、そんなエンジニアが、フロントエンドのリファクタや新規機能開発を、ユニットテストなしで実施するとどうなるでしょうか? そうですね、不安で爆発します。 これをおりにつけてぼやいていたところ、 「じゃあつぎの半期から正式に時間取ってやってみようか!」 と上長に声をかけてもらい、「BCのフロントエンドテスト始めようプロジェクト」がはじまったのでした。 ※Vueのバージョンアップについての詳細はこちらの記事が詳しい Vue3にアップグレードしてフロントエンドを改善した話 - SMARTCAMP Engineer Blog 補記 詳細は省きますが、BC開発チームでこのプロジェクトに着手できたのには理由があります。 MISSION制と呼ばれている、 「各自がやりたい×チームが必要なことを、やりたい人が目標(※)に持って主導する」 仕組みがあったことです。 (※人事評価上の目標) やりたいことをやって価値を出しつつ評価ももらえるって、とってもいいことです。 BCはフロントエンドのユニットテストをどう始めたのか では、前提からあらためてお話しします。 動き始めの時点で、BCは以下のような状態でした。 BCはSaaSで、定期的なアップデート・機能追加が必要不可欠 フロントエンドのユニットテストがまったくない(E2Eはある) Vue3へのリプレイスの途中である 重要機能でのバグがちらほら出ているが、これをできるだけ防ぎたい また、筆者は過去フロントエンドのユニットテストのコーディング経験がそこそこありました。 これを踏まえ、BCでは以下のような流れでフロントエンドテストを始めることにしました。 各種決め事(課題出し、目的の設定など) 手段の決定・詳細化(技術選定など) やってみる(コードを書く) ではここからは、各ステップで実施したことをかいつまみつつご紹介します。 1. 各種決め事 プロジェクトの動き出しには「なぜやるのか」が必要です。(※諸説あり) ということで、各種決め事をしていきます。 まずは、課題と目的を設定しました。 現状の具体的な分析と、理想など抽象的な思考を反復横跳びしつつ決めていきます。 BCでは以下のようになりました。 課題 開発時の開発者の不安・精神的負荷が高い 開発速度を守りつつも、品質を担保する必要がある 目的 開発者の不安・精神的負荷を軽減し、BCとしてはやくコンスタントな価値提供ができるようにする しかし、これでは抽象度が高いです。 理解しやすいよう、もう少し噛み砕いて具体化します。 フロントエンドの実装追加・変更を安全に・気楽に行えるようにする BCはプロダクトの特性上、はやく・たくさん試す(PDCAを回す、失敗する)ことが求められる そのための一つの手段として、主に開発者の以下の負荷を軽減する システムの挙動(ロジック)が保証されていないことによる、リリースに対する不安感 手動テストにかかる工数の削減 テストの再現性の保証 最後に、このプロジェクトの「やるやら」を決めます。 このあとのステップでの動き方を明確にするためです。 なお、「やるやら」はまず「やらない」ことを明確にすると、決めやすくていい感じです。 部分抜粋ですが以下の通りです。 やらないこと 「見た目」(デザイン・レイアウト)に関するテスト テストがあったとしても、最終的には人間の目でチェックする必要がある また、実装変更でテストコードが壊れやすく保護しづらい 〜略〜 やること 「ロジックの動作を保証する」テストをかく ※初期は、特に複雑なロジックについてはテストを書きたい これらの決め事はすべてドキュメントにまとめておき、いつでも参照できるようにしました。 2. 手段の決定・詳細化 ここからはよりエンジニア的なお仕事です。 まずは技術選定をし、その後コーディングに関わるこまごましたルールを決めていきます。 技術選定については、特殊な要件がなかったためデファクトスタンダードに従いました。 これは、導入保守のしやすさなどに利点があります。 技術ベースはこんな感じです。 Vitest Vue Testing Library MSW (今回の記事はあくまでテストの「始め方」にフォーカスしますので、これらの詳しい話は致しません。あしからず。) その後はこまごましたルール決めですが、コーディング規約から「どういう点はテストを書いて欲しいのか?」という考え方まで多岐に渡り、どこまで決めるべきかが難しいところです。 筆者はある程度ドキュメントをだ〜〜〜っと書いたところで、この沼にハマってしまいました。 そこで弊社技術顧問に相談し、もらったアドバイスが以下です。 「ある程度決まってるから、もう動いてみて考えたら?」(※要約) ですよね。 3. やってみる さて、楽しい開発のお時間です。 いきなり全員に「やってくれ!」とも言いづらいので、まずは首謀者である筆者が走ってみることにしました。 「みんながテストコードを書ける」ところまでを整えていきます。 書けることはたくさんあるのですが、ざっくり紹介します。 環境構築 技術選定したいろいろを入れる 責任を持ってサンプルコードを書く 必要となりそうなテストパターンを洗い出し、該当するサンプルコードを書いていく ロジックのみのテスト componentのrenderのテスト componentのmethod, computedのテスト API callのテスト などなど やってみる&やってみてもらう 勉強会などで基本の知識を共有 準備したサンプルコードも提供 こんな感じで走り回りました。 やってみると思ったより難しくなく、急に方針転換を強いられるようなこともありませんでした。 (もちろん、こまごまとハマるところはありましたが...。) 筆者の実力というよりは、Vitestが良かったのです。 公式ドキュメントがしっかり書かれていたり、JestのコンパチなのでJestの知見が流用できたりと、救われる点は色々ありました。 公式ドキュメント大好きエンジニアとして、Vitestのドキュメントは推せます。 また、実際にコードを書くにあたって、「どこからテストを書いていくか」について追加で少しだけ決め事をしました。 優先度の高いテストから書く BCとして重要な機能まわりから書く 書きやすいテストから書く 新しく作った機能から書く とはいえ、各ステップをどう流したのか? ここまで各ステップについてご説明しましたが、何事もフローなので、各ステップをどう「流した」かも大切です。 各ステップごとに以下を繰り返し、物事を進めていきました。 チームに匂わせ(頭出し)しつつ、首謀者がガーっとうごく チームに共有・説明 チームの合意形成とフィードバックの受け取り 気をつけていたポイントは以下の通りです。 まず、チームメンバーを不安にさせないこと。 「知らない」不安感をなるべくなくすよう、情報を早い段階で共有し合意をとるよう心がけました。 一方で、「フロントエンドのテストが必要」という点も認識を合わせておいたため、全員の意見を全部聴くのではなく、動くところははやく動いて先に進めることができました。 何かあったらその時考えればよいのです。 次に、とにかくドキュメントを書くこと。 筆者は兎角忘れっぽいので、ドキュメントが好きです。 ドキュメント化することで情報は民主化されます。 後からチームに参画した人も情報を見ることができ、適宜まとめておくことで必要な情報を見つけやすくもなります。 幸いメンテナンスが必要な類のドキュメントでもないので、この時点で書いておくのはいいことづくめです。 とくに、目的や方針のドキュメントは必要でした。 進んでいく過程で悩んだときに、立ち戻って考えるポイントになりました。 最後に、「なにはともあれやってみよう!」という心意気を共有すること。 できるだけ早く価値を提供し、失敗するなら失敗してリカバリし、少しでも前に進めることを意識しました。 これは、BCで大切にしているアジャイルの考え方でもあります。 先ほども書きましたが、何かあったらその時考えればよいのです。 通常の開発だとここまで気楽にもいられませんが、これはテストの話なので、失敗したとてプロダクトが壊れるわけでもありません。 フロントエンドテストドキュメントまとめページ そうしてどうなった? このプロジェクトをへて、BCでは「みんながテストコードを書ける」ところまでは整いました。 テストコードも少しずつ増えてきており、テストを書く雰囲気はぼちぼちですが醸成されてきているように思います。 コードレビュー時に筆者が「ここフロントエンドテストチャンス!」などと煽って書かせているのも否めませんが(笑) ただし、今もなお残っている課題もあります。 まず、フロントエンドのテストの正解がわかりません。 手探りに書いている感じがあります。 とはいえ「私が『これが正解だ!』とか言い出したらひっぱたいてくれ」という気持ちもありますので、これはこれでいいのかもしれません。 また、既存のコードのテストがなかなか書けません。 空き時間があればまとめて書きたいところですが、現実としてそんなものはなく、テストを書くだけのタスクを積むのは難しいです。 これは、大きめのリファクタをする前にはテストを書く、勉強会(後述)でもくもくするなどの手段で地道に対応しています。 おわりに 長々お話ししましたが、フロントエンドテストの始め方の話はこの辺で終わりです。 自ら旗を振ってやり始めたことですが、「まあなんとか軌道に乗ってよかったよかった」というかんじです。 Vueでは、Composition APIの導入によりComponentとロジックが分離される傾向にあります。 フロントエンドのテストはComposableと大変相性が良く、個人的には「今後需要も増えてくるんだろうな〜」とふくふくしております。 また、おまけ話ですが、布教活動の一環としてフロントエンドテストの勉強会を始めたりもしました。 テーマは「大人の児童館」。 フロントエンドテストに親しみ、これについて話す場の提供を目的に、社内誰でも参加OKのオンラインイベントを毎週開催しています。 毎回お楽しみコンテンツ(※フロントエンドかテストに関わっているなにか)を提供してはいます。 が、コンテンツに参加してリアクションする、もくもくする、ラジオ感覚で聴き流す、なんでもよしでゆるくやっています。 しかし、こんなことをやっていたら社内で「なんかフロントエンドの人」のような立ち位置になってきたので、ちょっと役者不足が不安ではありますが...(笑) では、またお会いしましょう!
アバター