TECH PLAY

BASE株式会社

BASE株式会社 の技術ブログ

587

こんにちは、NEW Dept/Pay ID Dev/Web Backendエンジニアをしている金子です。普段はPay IDに関するバックエンド周りの開発をしています。 3/14, 3/15の2日間に渡って開催された AWS JumpStart 2024 にBASEから4名のエンジニアが参加しました。普段はバックエンドを中心に業務しているエンジニアが、AWSの主要サービスを学び、アーキテクチャの検討をする貴重な経験ができましたので、感想を交えつつレポートしていきます。 AWS JumpStartとは AWS JumpStartは、新卒を含むAWS初学者のエンジニアを対象とした、クラウドネイティブなテックリード人材を育成するための第一歩となる実践的な研修プログラムです。事前学習用動画と2日間の集中的なワークショップを通して、皆様が自走できる状態までシステムアーキテクチャ設計やAWSの理解度を一気に引き上げることを目的としています。 https://aws.amazon.com/jp/blogs/news/aws-jumpstart-2024/ AWSが開催する実践的な研修プログラムで、BASEでは今年も アプリケーションエンジニアのインフラ知識の底上げのため に希望者が参加し、AWSサービスやアーキテクチャについて2日間学んできました。 使用するツール Zoom 今年はZoomでの開催でした Slack 他の参加者やAWSの運営メンバーとコミュニケーションを取る際に使用します Miro オンラインホワイトボードでアーキテクチャ検討時に使用します スケジュール 2日間 9:00~18:00で学習+アウトプットする充実した内容になっていました。 事前学習 参加前に事前学習動画と資料の案内があり、 AWSの基礎を学ぶのに非常に分かりやすくまとまっている内容 でした。これだけでも十分価値のある内容で、自分の場合は今まで体系的に学べてなかった部分が、するすると腑に落ちていく感覚でとてもありがたかったです。 アーキテクティング基礎(1時間) クラウドにおける設計原則、アーキテクティング AWS 入門(3時間) AWSの基本的な知識 Webサービス超入門(pdf資料) Webアプリケーションの仕組みについて 1日目 1日目は講義+ハンズオン中心でした。 講義全体の説明、自己紹介 はじめに全体スケジュールや本プログラムのゴールについての説明、Slack上での自己紹介の時間があります。また、プログラム全体を通してSlack上で自由に質問ができるので、気になった箇所があれば気軽に書き込み、この質疑応答を見るだけでも勉強になる内容が盛りだくさんでした。 講義 WEBアプリケーションの説明からHigh-Levelなアーキテクチャ図について等、丁寧に説明が進みます。 フェーズや目的によって目指すべきアーキテクチャは異なる ので、それらに合わせて最適なアーキテクチャを検討する必要があるわけですね。単にスペックの良いサーバーを大量に用意するだけでは、いくらでもコストがかかってしまうので、サービスの規模や要件に応じた適切なアーキテクチャの検討は非常に大切です。 ハンズオン ワークショップ用のデモアカウントを使って実際に環境を構築します。このデモアカウントはワークショップ中は自由に使用して良いので、環境を破壊して再構築を繰り返しできます。後からやり直して復習もできるので嬉しいですね! EC2の構成 素朴な構成ですが、VPCやセキュリティグループの設定など基礎的なサービスを使用します。 ALB+ECS+RDSの構成 先ほどのEC2よりもスケーラブルで冗長化された構成です。私自身はEC2の構築経験しかなく、ECSは初めての体験で勉強になりました。 挙動確認 ECSのタスクやAurora MySQLインスタンスに問題が生じた際の挙動確認を行いました。 個人的には1日目の一番楽しみにしていたところで、定期的なリクエストを送りつつECSのタスクを1つ停止したり、DBをフェイルオーバーさせて挙動を確認しました。余裕を持ちながら監視するのは本番環境ではできないことですね! CloudWatchの各種メトリクス確認 CloudWatch上で、先ほどまで動かしていたサービスのリクエスト数の変化やCPU使用率、メモリ使用率の確認を行いました。 ALBのリクエスト数 AuroraのCPU使用率 2日目 2日目はアーキテクチャの個人検討とグループワークが中心でした。 Daily Check-in, クイズ 2日目のスケジュールの確認やクイズを行い、前日の復習をします。 アーキテクチャ検討(個人ワーク・グループワーク) ここからはより実践的な課題をしていきます。既存のチームメンバーが全員退職してしまう ブラック 企業の社員になり1日でアーキテクチャを検討します。この会社を救わなければ!という使命感から俄然やる気が出てきます。 個人ワーク まずは個人検討で、提供規模・システム要件・必須機能から適切なアーキテクチャをMiroを使って作成します。実際に図に落とし込むのは、詳細を理解していないとなかなか難しい内容でした。 グループワーク Zoomで各チーム3~5名ほどに分かれてアーキテクチャをブラッシュアップしていきます。他の参加者と意見交換をしながら検討することで更に理解が深まりました。 成果発表と解説 代表して3チームから最終成果の発表がありました。各チームこだわりがあり、それぞれ違ったアーキテクチャ図になっているのも面白かったです。 そして最後にAWSのSolutions Architectによる解説があり、クロージングとなりました。 感想 参加メンバーから感想をいただいたので紹介します! AWSを用いて0から何かを作っていく機会が長いこと無かったので、良いタイミングと捉えて参加しました! 前半の基礎知識の学習やハンズオンは、進み方がとても確実かつ丁寧で、初学者の方にはぜひおすすめしたい内容と感じました! 後半は前半で学んだことを用いてアーキテクチャの設計を実際に行う演習形式でしたが、知識の再確認としての良さだけでなく、他の人と作成したアーキテクチャについて意見交換や議論する機会があったことで、考えていなかった視点が知れたり共同でブラッシュアップしていく経験ができたりと、とても刺激的でした! 今後業務のなかでチャンスがあったときは、イベントを通じて得たものを活かしたいなと思います! Pay IDでバックエンドエンジニアをしている 竜口 です。 今回参加した目的は、インフラに関する経験や知識が不足していることでどうしても開発のアプローチが少ないと感じることがありインフラの理解を深めることで、より幅広いアプローチができればと思い参加しました。 イベントでは、インフラの基礎から実際に構築する過程を体験し、全体像の理解を深めることができました。 さらに、課題に取り組む中で生じた疑問点や、AWSの仕組みについての詳細な説明を受けることができたのがとてもよかったです。 また、Slackで他の参加者の興味深い質問とその回答をみれたのは楽しかったし、より理解が深まりました。 イベント後に自社のコンソールを見て、今まで以上に自社の構成の解像度が上がったのを感じられて嬉しかったです! 今回の経験からまたステップアップして今後の開発に活かしていきたいです! BASEでバックエンドエンジニアをしています。 Futoshi Endo です。 参加しようと思ったきっかけは、直近の業務でAWSが提供している一部サービスに触れてみて、ちゃんと体系的に理解しようと思ったのが理由でした。その前にもAWSの資格試験の対策で問題集を解いてみたりした事はあったのですが、各サービスの特徴だったり、なぜこの構成で作るのかの背景について中途半端に学んで終わっていました。 AWS JumpStart2024に参加して良かったのは、まさにそこの理解が不十分なままで終わっていた箇所が、実際にアーキテクチャ図をかく作業を通じて、理解できた事でした。 また、他の参加者の方が考えたアーキテクチャ図を見ながら、AWSの運営メンバーとの議論されている内容も良かったです。 自分自身、参加する前はAWSについては苦手な傾向があったのですが、少しだけ自信がついたので、業務で触れる機会があれば積極的に挑戦しようとおもいました! まとめ BASEではtmpチャンネル(ワークショップ用の一時的なチャンネル)を作って、「この構成はBASEだとどうなっているんだろう」など内部でもワイワイしながら講義を受けました。自社のサービスに落とし込んだ場合のアーキテクチャがどうなるかを想像しながら進められるのも、普段の業務ではあまり考えることのない部分なので有意義で面白かったです。 AWSの苦手意識を払拭できたメンバーもいたりと、総じて非常に勉強になる貴重な体験でした。今後バックエンドエンジニアとして仕事するうえでも、webの開発観点だけでなくインフラ観点やAWSサービスへの理解を深めつつ、より良いサービスの提供に繋げていければと思います! 最後に、BASE では絶賛採用活動をしています。 興味がある方は以下のリンクからご覧なっていただけると幸いです! binc.jp
はじめに こんにちは!Data Platformチームでデータエンジニアとして働いている @shota.imazeki です。 分析基盤の構築・運用などの側面から社内のデータ活用の促進を行っています。 BASEではAurora MySQLにあるデータをEmbulkを用いてBigQueryに連携しています。BigQueryへ連携されたデータは分析基盤としてLookerなどを通して社内利用されています。 このデータ連携処理にはいくつかの課題があり、それを解決するためにEmbulkからAurora S3 Export機能を用いた連携処理に切り替えることにしましたので、それについて紹介していきたいと思います。 ※この切り替えについては現状、試験的に一部のDBのみの切り替えとなっていますが、運用上の大きな課題が出てこなければ徐々に切り替えていく予定です。 切替前のデータ連携処理 先述した通り、BASEでは主にEmbulkを利用してAurora MySQLにあるデータをBigQueryに連携しています。以下のようなアーキテクチャです。 本番DBからレプリケーションされた分析用DBに対して、EmbulkからSQLをDaily実行する形でデータ連携を行なっています。ワークフロー管理にはAirflowを利用しています。 Embulkによる主な連携パターン EmbulkからBigQueryにデータを連携する際は、テーブルの仕様やデータ量に応じて主に3パターンに分けています。 DELETE INSERT BigQuery側で対象日付分を削除し、その後に対象日付分をINSERTするパターンです。事前に削除を行うのは処理の再実行などによって同じデータが入り込まないようにするためです。主にログデータなどの更新されないデータがこれに当てはまります。 REPLACE テーブルの全量洗い替えです。更新や物理削除が行われるテーブルは基本的にこの方式になります。例えばユーザー情報などです。 MERGE 対象日付以後に更新のあったデータを対象に差分更新を行うパターンです。基本的にはREPLACEやDELETE INSERTで対応したいですが、テーブルのデータ量が大きい場合にこちらを選択します。例えば注文や発送関連のテーブルが該当します。 課題 Embulkを用いたデータ連携処理の課題について触れていきます。 レプリケーション遅延によるデータの不整合 連携元のデータとして本番DBからレプリケーションされてきた分析用DBのデータを使用しています。この分析用DBは別の処理としても使われていることがあり、その負荷などによってか時折レプリケーション遅延が発生します。この遅延が連携処理のDaily実行時に発生していると、本来更新されるべきはずのデータが更新されなくなります。これによって一部データの不整合が発生することがありました。 物理削除を検知できない Embulkでは連携するデータをSQLで取り出すため、レコードが物理削除されていたとしても、それを検知することができません。なので基本的には物理削除が発生するテーブルについてはREPLACEで対応することになります。ただし連携元のテーブルのデータ量が大きすぎる場合、Embulkサーバーのディスク容量を圧迫してしまいます。そのため、物理削除が発生する且つテーブルのデータ量が大きいものに関しては連携が厳しい状態です(MERGEによって連携すること自体は可能だが信頼できるデータではなくなってしまう)。 select column1, column2, column3 from table where modified >= TARGET_DATE -- 更新日時が対象日以降 新しいデータ連携処理 上記のような課題を踏まえて、新たに構築したデータ連携処理が以下です。Aurora S3 Export機能を利用し、parquet形式でS3にスナップショットを出力したのちに、BigQuery Data Transfer Serviceを用いてS3から直接BigQueryへデータをインポートしています。 ここからはこの処理を採用する前に調査したことについて触れていきます。 調査・検証内容 Aurora S3 Export機能を採用するにあたって、調査や検証したことについて触れていきます。 出力元DBへの負荷 スナップショットとして出力するDB側への負荷がどれだけあるのかをまずは確認しました。AWSで公開されている ドキュメント にて、以下の記述があるように、DBクラスターをクローンすることで出力元DBへの負荷は起きないようです。 Amazon Aurora は DB クラスターをクローンして、クローンからデータを抽出し、そのデータを Amazon S3 バケットに保存します。 ここについてはAWS SAの方に伺い、認識の齟齬がないかを確認させて頂きました。 連携処理にかかる時間 データ連携処理が深夜に開始して、翌朝の9時以降にまでかかってしまう場合は新しいデータ連携処理に切り替えることはできません。間に合うかどうかを確認するために、実際の分析用DBと同じインスタンスタイプ、同じデータ量のテーブルを2つ用意し、検証を行いました。選んだテーブルはEmbulkだと連携に1つ目が約15分、2つ目が約30分程度かかる重たいテーブルになります。各処理にかかった時間は以下の通りで、合計で30分程度です。 最初にインスタンスを起動する時間が20分と長いですが、これはDB Cluster単位で必ず最初に行われるものになっています。その後続の処理はいずれも数分で終わっており、全体として30分程度でEmbulkを用いた時と大差ない時間です。最初の起動時間を考慮しなくていいのであればEmbulkよりも短い時間でデータ連携が可能になりそうです(Exportするテーブル数やEmbulk側での並列実行数次第)。この結果から新しいデータ連携処理に切り替えても時間がかかりすぎることはないだろうと判断しました。 コスト コストについても念の為確認しておきましたが、大きくかかりすぎるということはないだろうという判断に至りました。 Aurora S3 Export : 100GBで$1.2~1.3程度 S3からのデータ転送 (アウト): 100GBで$11.4程度 parquet形式でデータ圧縮されるため、思ったほどのコストにはならないと考えてます BigQuery Data Transfer Service : $0 実装時のポイント ここからは新しいデータ連携処理を実装していくにあたって考慮が必要だった点について触れていきます。 Aurora S3 Export 今回はboto3を用いてS3 Exportの処理をキックすることにしました。 タスク識別子(ExportTaskIdentifier)に日付を入れる S3 Export処理をキックするには以下のparameterを指定して start_export_task を実行します。 response = client.start_export_task( ExportTaskIdentifier= 'string' , SourceArn= 'string' , S3BucketName= 'string' , IamRoleArn= 'string' , KmsKeyId= 'string' , S3Prefix= 'string' , ExportOnly=[ 'string' , ] ) ExportTaskIdentifierには任意のtask idを指定する必要があるのですが、以前実行した時と同じ値を指定することはできません。またこのExportTaskIdentifier(及びS3Prefix)はS3 Bucket内のURIにもなります。そのため、ExportTaskIdentifierの値の末尾にyyyymmdd形式で日付を入れることにしました。イメージは以下です。 # 理想はこちら。 s3://S3BucketName/table_name/.../*.parquet # 実際はこちら。S3 URIが日付で異なる。 s3://S3BucketName/ExportTaskIdentifier_yyyymmdd/table_name/.../*.parquet これ自体は大したポイントではないのですが、後続のBigQuery Data Transfer Serviceにおいて考慮すべき点になります。 S3 Export処理の完了を定期的に確認する start_export_task関数を実行すると実行指令が飛んだ直後にresponseが返ってくるため、Export処理が完了したかどうかが分かりません。完了を確認するためには describe_export_tasks を数分おきに実行します。 response = client.describe_export_tasks( ExportTaskIdentifier= 'string' , SourceArn= 'string' ) 今回、describe_export_tasksを定期的に実行するのにAirflowの Sensorクラス を使うことにしました。以下に実装例を示しておきます。このように定義することで5分間隔(poke_interval)で10回(timeout)、python_callableに渡した関数が実行されます。その関数でdescribe_s3_export_taskを実行させ、trueが返って来れば後続の処理に進むという形です。 def poke_describe_s3_export_task (**kwargs): # describe_s3_export_taskを実行 # 完了したかどうかでtrue, falseで返す return true poke_describe_s3_export_task = PythonSensor( task_id= "poke_describe_s3_export_task" , mode= 'poke' , poke_interval= 300 , timeout= 300 * 10 , soft_fail= False , python_callable=poke_describe_s3_export_task, provide_context= True , dag=dag ) BigQuery Data Transfer Service S3にparquet形式でデータが出力されれば、次にS3からBigQueryにインポートする処理を行います。今回はGoogle Cloud CLI コマンドでの実装としました。主に [bq mk](https://cloud.google.com/bigquery/docs/reference/bq-cli-reference?hl=ja#bq_mk) コマンドを使っています。 --transfer_run ****を指定することでBigQuery Data Transfer Serviceを実行することができます。 bq mk --transfer_run [--run_time=RUN_TIME | --start_time=START_TIME --end_time=END_TIME] CONFIG runtime parameterによるS3 URIの日付指定 BigQuery Data Transfer Serviceの転送設定を定義する時にS3 URIを指定するのですが、上述した通り、S3 URIは日付別に異なる点を考慮しなければなりません。その時に使うのが runtime parameter です。これを使うことで日付部分を動的に変更することができます。{run_time-9h|"%Y%m%d"}のように実行時刻から時間をずらしたり、フォーマットを変更することができます。例えばruntimeが 2024-04-01 05:00:00 の場合に {run_time-9h|"%Y%m%d"} を指定すると 20240331 という値になります。 # 定義する際のS3 URI s3://S3BucketName/ExportTaskIdentifier_{run_time-9h|"%Y%m%d"}/table_name/*.parquet # 実際に参照されるS3 URI(2024-04-01 05:00:00に実行された場合) s3://S3BucketName/ExportTaskIdentifier_20240331/table_name/*.parquet 今後の課題 試験的な運用からの脱却 負荷やコスト、連携にかかる時間が試算でしかできないので、一部のDB(プロダクトへの影響が極力低いDBを選定)に限定しての運用になっています。今後は他のDBへの展開を行っていきたいと思っています。 Airflowのバージョンアップ 現状、boto3やGoogle Cloud CLI コマンドでの実装となっていますが実はAirflowには以下のようなオペレーターがあり、boto3などを使わずとも実装が可能です。 RdsStartExportTaskOperator BigQueryCreateDataTransferOperator 今回使わなかった理由はBASEのAirflowサーバーのバージョンが低くて利用ができなかったからです。マネージド( Amazon Managed Workflows for Apache Airflow )への移行を考えていたり、バージョンを上げるための他ワークフローへの影響範囲が確認できていないため、一旦は今回の処理の実装を優先しました。今後はより実装をシンプルにするためにAirflowのバージョン上げを行い、上記オペレーターに切り替えていきたいと思っています。 最後に 試験運用ではありますが、データ連携処理の切り替えを行うことができました。これによってより多くのデータを分析基盤に集めていくことが可能になったと思っています。またEmbulkへの負荷が下がったおかげで全体としての連携時間も早まっています。 最後となりますが、弊社ではデータエンジニアを募集しています。上記で述べた課題以外にもBASEの分析基盤には多くの課題があって、とてもやりがいのある仕事かなと思っております。ご興味のある方は気軽にご応募ください! https://open.talentio.com/r/1/c/binc/pages/94358
2024/03/07(木)~2024/03/09(土)に開催された PHPerKaigi 2024 にて、BASE株式会社から2名のメンバーが登壇しました! 登壇者 2 名からコメントと、会場の様子やセッションについてお届けします! 登壇者のコメント Futoshi Endo ( @Fendo181 ) speakerdeck.com BASEでバックエンドエンジニアをしています、遠藤です。 2日目に「PHP8の機能を使って堅牢にコードを書く」というタイトルで発表させて頂きました。 業務でもPHPを書いているのですが、PHP8で提供されている機能は使ってより堅牢に書くにはどうすればいいのか? が気になってそこから実際に業務を通じて得た経験だったり、自分で調べた内容をまとめて発表しました。 個人的には「堅牢」という壮大なテーマを選んでしまった事に若干後悔もしたのですが、資料を作るにあたって、 t_wada さんの動画 だったり、書籍を読み漁った影響で、自分なりに整理できたポイントが多く勉強になりました。 登壇後の質問では、実際にPHP8.2から導入された「読み取り専用クラス(readonly class)」の活用方法の質問だったり、実際に堅牢なコードを書く上で考えている事のポイントを聞かれて、参加者の方と意見効果が出来て良かったです。 今回発表して思ったのは、まだまだ自分の中でPHP8を使って堅牢なコードを書く上での経験が浅い部分があると自覚したので、今後も沢山コードを書いて学んで行こうと思いました。 02 ( @cocoeyes02 ) fortee.jp BASE BANKチームでフルサイクルエンジニアをしている大津です。 今回はアンカンファレンスコーナーで「PHP系カンファレンス反省会&壮行会」という、2024年に行われた/行われるPHP系カンファレンス(北海道、関西、PHPerKaigi、小田原、香川、福岡、東京)の実行委員長によるパネルディスカッションに参加させていただきました。 話していたテーマには 「どこからコンテンツのアイディアを思いついているか?」 「開催を決めた瞬間のことを教えて?」 「新規コミュニティ参入者を増やすための工夫があれば聞きたい!」 などがありました。 どのカンファレンスも創意工夫がたくさんあったのもそうでしたが、開催場所の地域を盛り上げようという熱意や各委員長のパワーをひしひしと感じ、これは負けてられないぞ・・・!という気持ちにさせてくれました。 今回登壇した委員長のカンファレンス以外にも沖縄があり、2024年はPHP系カンファレンスがたくさん開催される年です。ぜひ各PHP系カンファレンスへの参加お待ちしております! 現地で見たセッションを一部紹介 privateメソッドのテストって書かない方がいいんだっけ? speakerdeck.com asumikan さんによる発表で、テストコードで、privateメソッドで書かれた処理を書かない理由についての発表でした。 privateメソッドは書かない方がいい…というのはよく聞くけどその理由を、ご自身の開発での体験談を込めて説明がされており良かったです。 ここらへんのお話は t_wada さんのブログ でも以前紹介されていたのですが、asumikan さんの発表ではECサービスの発注処理を題材に説明されていて、そこから良いテストを書くための道筋が書かれていてイメージが湧きやすかったです。 後、余談としては発表が抜群にうますぎたので、登壇する際にはぜひ参考にしたい気持ちになりました。 どうやってWebサービスのページ表示速度を1/3にしたか / how-to-reduce-display-speed speakerdeck.com ぴんくもひかん さんによる発表です。Webサービスのページ表示速度を改善した話で、まず導入から「なぜページ表示速度は大事なのか?」の丁寧な説明から、その後の改善方法まで細かく書かれておりわかりやすかったです。 個人的にはここらへんパフォーマンス改善系だと事前に測定でDevtoolを使うのは聞くのですが、プロファイラでXdebugが使えるのは知らなかったです。 後半では実際にパフォーマンス改善でクエリのチューニングを行ったり、php.inc の opcacheの設定を有効したりで、どんどん改善されていくのを見て「おぉ!」となりました。 はじめてのメンバー育成。マネージャーとメンバー視点で振り返る1年間 speakerdeck.com 永井美波 さん による発表です。はじめてのEMとなり、メンバー育成を行うにあたってご自身が経験を説明されています。 最初の「知り合い期」からの「探り合い期」、「あれこれも期」と、各期間毎でEMとしてメンバーといっしょに取り組んだ内容が変わっていて勉強になりました。この発表は現地で聞いていたのですが、メンバーと同じ目線にたって一緒に走っている感が伝わってきて良かったです。育成する側と、育成される側でちゃんと認識を合わせるのが大事だと思った発表でした。 まとめ PHPerKaigi 2024 の会場が「中野セントラルパークカンファレンス」に変わったのですが、スタッフの方々の素晴らしいオペレーションで今回も楽しく参加ができました。スタッフの方々には業務でお忙しいにも関わらず、多くの時間をカンファレンス準備へ注いでいただいたかと思います。 この場を借りて御礼申し上げます。 また来年の PHPerKaigi でも積極的に参加して行こうと思います! ありがとうございました! 最後に、BASE では絶賛採用活動をしています! 興味がある方は以下のリンクからご覧なっていただけると幸いです! binc.jp
こんにちは、BASE BANK Divisionで事業責任者をしている柳川と申します。 今回はROSCAさん主催のROSCAFE TECH NIGHT #5で登壇させていただきました。 その登壇記録とレポートです。 rosca.connpass.com 柳川の登壇内容 イベントタイトルに反し、僕はCTOではないのに登壇させてもらうという若干の出オチ感のある登場でしたが、元気に発表させていただきました。 内容的に若干浮くかなとおもったんですが、今回参加者の選定がマッチしたのか、意外にも同系統の発表が並びました。 発表スライドは以下になります。 speakerdeck.com 発表の趣旨は、エンジニアのキャリアパスには事業責任者もあるよという話です。 僕のキャリアパスとしてはエンジニア→PdM→事業責任者なのですが、 なんでそのようなキャリアパスを辿ったのだろうかと考えていた時に以下のようなつぶやきをしました。 1人で100人分の仕事するエンジニアみたいなものに憧れがあって、自分なりにそれを体現しようとした結果、プロダクトから事業まで見れるように自分の関わる範囲を広げたって感じなんだよな。気質的に突き詰めるより繋げるみたいなほうがあってたというか。 — 柳川慶太@BASE BANK (@gimupop) 2024年2月22日 (いいねください) この「1人で100人分の仕事するエンジニアみたいなものに憧れ」という言葉が出てきた時に、憑き物が取れたような気持ちになり、この部分を膨らませて話してみようということで作った資料になります。 自分が実際に事業責任者になった経緯としては、足りないスペースを埋めていったらそうなった、という表現が正しいです。 ただ後から考えた時に、「自分のパワーを最大限事業のためになることに使おう」と思ったからこうなったんだなということに気がつきました。 エンジニアだからこそできるアプローチ、だからこその事業の伸ばし方という部分を信じていたのだと思います。 発表の中で出てきた事業計画を引いてみるはマジでおすすめです。その計画の中で自分にできることはなんなんだろうと考えてみるとめちゃくちゃ視界がクリアになると思います。 反省点としては、なんで事業責任者になることが「1人で100人分の仕事すること」に繋がるのかという掘り下げが甘かったなというのがあるので、次回はそこを補足した発表にしたいなと考えています。 このテーマ擦る気満々です。 プロダクトで勝負する会社にとって、事業計画から企画から開発から全て一貫してみれるということはものすごく強みになります。この点だけで6時間語れますが泣く泣くカットしました。 この発表で一番伝えたかったこととしては、「誰しも戦い方を考えれば最前線で戦える」ということです。自分の強みを考え続けて、それをうまく取り組むことに紐付けることがとても大切なことだと考えています。 BASEは「自分の好きなことをしてそれを仕事にしたり経済活動を行っている」人々を応援するサービスです。 だからこそ、そのサービスを作っている我々も、それぞれの人が持つ可能性を信じて、日々日々レベルアップしながら仕事しいるんだという矜持が伝わればという発表でした。 個人ブログにも補足記事を書きましたので、お時間ある方はぜひ。 note.com 各発表への感想 株式会社タイミー 執行役員CTO 亀田 彗さん 発表タイトルは「タイミーの歴史に起きたこと、そこから抽象化してCTOに求められること」でした。 生存性バイアスというキーワードもありましたが、赤裸々な実体験が綴られており、事業を作って成長させていくってこういうことだよなと深く首肯してしまいました。 CTOとして必要な要素に 要素① 変化を好むこと 要素② 諦めないこと 要素③ ものづくりと事業への熱いハート を挙げており、マジで全部わかるなと思いました。特に諦めないことが僕のテーマにも入っていてテンションが上がりました。 事業を作っていくって総合格闘技ですよねという言葉で勝手に納得しました。 株式会社tacoms CTO 井上 将斗さん 発表タイトルは「CTOとしてどのように役割を変えてきたか / 4年間を振り返ってみた」でした。 speakerdeck.com 実際のハードシングスに対する乗り越え方が赤裸々に書かれており、興奮しました。 そしてネタの中で、まさかの事業計画被りということで大変失礼しました。 発表内容に深く勇気づけられました エーテンラボ株式会社 共同創業者&取締役CTO 山口 信行さん 発表タイトルは「持続的成長のためのプロダクトと組織づくり」でした。 speakerdeck.com 発表の中で会社の行動指針を作ったら、プロダクトが達成すべきミッションと共通していたということを発表の中で仰っており、すごくいい話だなと感じました。 とてもミッションファーストな会社だなと思い参考になりました。 総括 「事業が死なないようにできることをやり切る」という観点において、CTOも事業責任者も共通項があるなということが強く認識できた会になりました。 僕も「ちゃんとエンジニアとして進化した結果の事業責任者なんだよ」ということを勝手に語るつもりでしたが、はからずもみんな同じなんだと思え、エモい気持ちになったすごく良い会でした。 生き残ってやるぞという思いが感じ取れて最高でした。新規事業とかスタートアップってこれだよなと思い、めちゃくちゃパワーをもらえました。 主催いただきましたROSCAさん、ありがとうございました。 最後に、BASE BANK事業部は絶賛採用活動をさせていただいています。 採用資料も最近新しくして読みやすくなったと思うので、ぜひご気軽にカジュアル面談から、お声がけお願い致します。 人のチャレンジを支援し、可能性を広げる活動をぜひご一緒に!よろしくお願いします! speakerdeck.com
こんにちは。 BASEの ProductDevでエンジニアをしています、遠藤( @Fendo181 )です。 今回、2024年2月11日に開催された「PHPカンファレンス関西2024」にコアスタッフとして参加してきました! 2024.kphpug.jp 2018年から6年越しに開催された「PHPカンファレンス関西2024」でしたが無事に開催できました。蓋を開けてみれば431名という多くの方が参加された大きなイベントだったと思います。 この記事ではそんな「PHPカンファレンス関西2024」にコアスタッフとして参加した背景や、自分の役割や、コアスタッフを経験して思ったことなどをまとめてみようと思います。 また、この記事をきっかけにコアスタッフでなくても、なんらかの技術コミュニティに参加するきっかけになれば幸いです。 コアスタッフに参加しようと思ったきっかけについて 「PHPカンファレンス関西2024」のコアスタッフの募集が始まったのが去年の8月頃でした。 元々PHPに関わらず、なんらかの技術イベントに参加するのが好きで当日スタッフを経験した事はありました。 ただ、数年ぶりに「PHPカンファレンス関西」を開催するということで、コミュニティへの貢献も含め「なにか少しでもお手伝いができれば!」と思ったのが参加したきっかけの1つです。 また関西でのPHPコミュニティの方とは知り合いが少ないので、せっかくなら現地で交流もできればと考えてコアスタッフとして参加してみました。 コアスタッフでの自分の役割について 自分は「一般参加者対応チーム」に配属となりました。 このチームでは一般参加者へ向けたチケット販売の運用、当日の会場運営、スタッフの管理、懇親会でのケータリング周りの準備、会場連携などを担当するチームで、自分は主にケータリング周りと会場連携を行っていました。 他のコアスタッフの役割については以下のnote記事でもまとまっていますので興味があればご覧ください。 note.com 具体的に自分が対応した事を洗い出すと以下の作業をやっていました。 懇親会スポンサー企画の準備 懇親会でのケータリングの手配 会場で参加者に配布するドリンクの手配 当日スタッフ向けのお弁当の手配 カンファレンス会場を提供する企業様の連携 カンファレンス会場との連携については、Wifiや照明など会場からレンタルする機材や、事前にスポンサー側から送られてくる荷物が無事に受け取れるよう書類を用意するなど動いてました。 カンファレンス会場を用意する上で、当日問題なく実施するために提供元企業様との連携が重要でした。開催前日までに、会場の設備の使用については何度も連絡をとりながら準備をすすめました。 当日の動きについて 当日は主に会場側での設備の配置や、なにか問題が起きたときに備えて見回りをしてました。 開催前に、メールでのやりとりでカンファレンス会場の資料には何度も目を通してましたが、自分が東京に在住していたため、実際の会場は見れてなく、現地にいるスタッフが撮影してくださった会場の写真を見ながら配置など、イメージトレーニングをしてました。 したがって、当日の朝一番に会場に入ってから、すぐさま部屋の配置の把握や、ちゃんとメールで申請した通りにレンタル機材や荷物が届いているかを確認してホッとしたのを覚えています。 その後は会場での予期せぬトラブルが起きたときに備えて、適宜カンファレンス会場を提供する企業様と連絡をしながら、会場を見回りしてました。数年ぶりの開催で過去のナレッジも少なく、当日の予期せぬトラブルが一番の心配でした。 しかし、特に大きなトラブルもなく最後まで進行できたのは本当に良かったと思います。 振り返ってみて 「PHPカンファレンス関西2024」が無事に開催できたこともそうですが、6年ぶりの開催にも関わらず、沢山の人が参加して頂き、交流する場を提供できたことがなによりもスタッフとして嬉しかったです。 また、ここまで運営に関わっていたコアスタッフや、当日スタッフの皆さんとも、終わってからの懇親会で交流できたことも良い思い出になりました。 まとめ コアスタッフとして参加したとき、担当した仕事が無事にできるか不安だったのですが、無事にやりきれてよかったです。 これまでは一人のエンジニアとしてアウトプットをするためにカンファレンスで登壇したり、勉強会等で発表をしてましたが、コアスタッフとして参加してみると その場所を提供するにあたって沢山の有志の情熱によって支えられていることがわかります。 その一員として、今回、PHPカンファレンス関西2024の開催をお手伝いできたこと、心より誇りに思います。 今後もPHPに関わらず、カンファレンスは開催されるので、もしコミュニティを盛り上げてみたいと思った方はスタッフに応募してみてください!
こんにちは!BASEでエンジニアをしている竹本です。 2/10(土)に広島国際会議場で開催された YAPC::Hiroshima 2024 に参加してきたので、当日聞くことができたセッションの感想をイベントレポートとしてお届けします。 会場の様子 BASE はYAPC:Hiroshima 2024にプラチナスポンサーとして協賛していました。 協賛の背景については以下の記事をご覧ください。 YAPC::Hiroshima 2024にBASE がPlatinumスポンサーとして協賛します 印象に残ったセッション 経営・意志・エンジニアリング キャリアの初めから12年間CTOという役割を担っていた松本さんが、ソフトウェア開発の延長にある経営というテーマで発表されていました。 経営者というtech lead とも Engineering Managerとも向きの違う役割のあり方とそれがエンジニアリングとどう繋がっているかという話で、大変興味深く聞かせていただきました。 以下は自分の要約memoになります。 経営は3つの変数(事業・組織・技術)と4つの制約(お客様, 社員、投資家、社会)がある。 大切な前提として「みんなの楽しい」がある 楽しいを実現するために3つの変数(アーキテクチャ)を同時に成立させ、4つの制約(ステークホルダー)を満足させることを技術経営者は考えている。 技術だけがエンジニアリングの対象ではなく、事業・技術・組織をソフトウェア的に設計・運用できる 長期的であるほど楽しい 根幹は意志 平成のエンジニアから令和のエンジニアへの遺言〜技術情報を伝達する手段の変遷〜 発表者: - 櫛井 優介( @941 )さん - 伊藤 直也( @naoya_ito )さん - 和田 裕介( @yusukebe )さん - 和田 卓人( @t_wada )さん - 馮 富久( @tomihisa )さん この発表はランチ後のパネルディスカッションでした。 941さんという方がファシリをしながら事前に用意していたテーマだったり、会場内で出たテーマについてディスカッションされていました。 ファシリの941さんの話運びが秀逸で、常に話が途切れず会場が笑いに包まれていました。 「コードを書かないCTOは認めない」「個人開発が最近ファッションになってきている」「使っているエディタときのこ・たけのこどちらが好き?」みたいなやや攻めた発言も飛び交い、非常に楽しく聞くことができました。 「とほほのWWW入門」お好み よもやま話 資料: https://www.tohoho-web.com/ot/yapc-2024-tohoho.pdf 発表者: 杜甫々さん 「とほほのWWW入門」を運営している杜甫々さんによる発表でした。とほほのWWW入門は1996年から現在に至るまで更新が続けられているサイトで、当日の会場・Twitterは大盛り上がりでした。長きにわたってWebサイトを運営されているとほほさんから見た現在のWeb開発について思うところやとほほさんの人となりがわかるエピソードを聞くことができて、感激でした。 カンファレンススポンサーのススメ 発表者: 川口 ( id:dmnlk ) さん 弊社CTOの川口さんのスポンサーLTは「カンファレンスにスポンサードするにはどうするか」というテーマでした。 これが答えです。 決裁権、欲しい! このスライドでひと笑いとった後に、稟議を出して勝ち取る、社の全体予算を確認する、仲間を集めるなど具体的な手法が紹介されていました。BASEが今回YAPCにスポンサードしたのは主催の方の熱意に触れたこと+キーノートが杜甫々さんだったからだそうです。自分が所属する会社が技術コミュニティに貢献できる文化があることを改めていいなぁと感じました。 終わりに YAPCめちゃくちゃ楽しかったです! 当日は会場で知り合いにばったり会ったり、懇親会で全然知らない人と話すことができたりと、とても楽しい時間を過ごせました。 運営・参加者の皆様、改めてお疲れ様でした。
CTOの川口 ( id:dmnlk ) です。 これはBASE Advent Calendar25日目の記事です。 今年も僕は立候補してないのに勝手に日程が組み込まれてました。毎年書いてくれるメンバーが増えていってくれているのになぜ。 CTOについて 自分は2019年からBASE株式会社のCTOをやっています。 気づいたら4年近くやっていることになっていて驚いています。 たまに社外のエンジニアの方とお会いするとCTOになるにはどうすればいいかということを聞かれることがあります。 僕個人のサンプルではありますが、少し書いてみようかと思います。 なぜCTOが必要なのか 必ずしもCTOが必要なのでしょうか。 これに関しては僕は必要であると考えます。 自分がWeb企業にいるという前提条件はありますが、システムやテクノロジーが非常に重要な市場優位性となります。 ただプロダクト開発をするだけでなく最新の技術トレンドを把握し、それらを企業のビジネスモデルに統合することが重要です。CTOは、企業が市場で競争力を保ち、革新的な製品やサービスを提供できるようにするための鍵となる役割を果たします。 CTOは、技術とビジネスの両方の言語を理解し、両者の間のコミュニケーションと協力を促進します。これにより、技術的な洞察がビジネス戦略に統合され、より効果的な意思決定が可能になります。 技術は常に進化しているため、CTOは最新の知識とスキルを維持し、組織を適応させる必要があります。これにより、企業は変化する市場環境に柔軟に対応し、持続可能な成長を達成できます。 CTOの役割は、単に技術的な専門知識を超え、戦略的な思考、リーダーシップ、そしてビジネスと技術の統合を通じて、企業の成功と革新を推進する重要な要素です。 こう書くと自分がこれをちゃんと実行できているかというと大分疑問になりますが、責務としてはこれらがあげられるでしょう。 役割と責任について 会社のフェーズによってここは大きく変わると思います。 初期スタートアップのCTOは経営責任というよりもテックリードに近いポジションになるでしょう。 ひたすらに機能実装をすることが責務となり場合によっては情シス的なポジションも兼任するかと思います。 創業数年経ち、フェーズが変わっていく中で改めてCTOというものの重要性が高まっていくでしょう。 事業戦略と技術戦略を統合し、中長期的視点で技術開発を進めていくことになります。 プロダクトはまだまだグロースさせていくのでメンバーは開発に手一杯でしょう。 その中でCTOは次の未来の開発のためのボトルネックを見つけそれを改善していくための技術検証やロードマップを描いていく必要があります。 組織に関しても着目しなければなりません。 技術チームのリーダーとして、CTOはチームの育成、スキルの向上、そして生産性の最大化を担います。また、組織全体の技術的ビジョンを共有し、異なる部門間での協力を促進することで、企業の目標達成に貢献します。 VPoEのような存在がいると分業しやすいかもしれません。 スキルセットについて ここが一番よく聞かれるのですが、どのような技術を持っているとCTOになれますかといわれます。 個人的には、特定の技術の専門性というよりは一通りの技術に60点くらいの理解度があることの方が重要です。 特定技術の深掘りは現場のメンバーに任せ、市場の技術動向と自分達のプロダクトやシステムに何が必要かを考えるために分野に寄らない興味関心があるといいでしょう。 インフラやセキュリティについての知識はどちらかというと重要視されると思います。 インフラに関してはサービスキャパシティのプランニングやコスト管理意識を持つのに重要ですし、セキュリティはどうしてもサービスグロースの中で後回しにされがちなので先見性を持って問題意識を持つのに必要となります。 frontend技術に専門性を持っている人がCTOに多くないのは、AWSやGCPのようなクラウドインフラサービスのroot権限を渡しづらいからではと思っているところはあります。感覚値でしかありませんが。 経営スキルについては最初から持っている人は少ないでしょう。僕も当初全く持ち合わせていなかったと思います。 長期的なビジョンを持ち企業戦略を実行するための戦略的思考、コスト効率の高いソリューションを開発していくための財務管理、様々並行で行われる開発を管理するためのプロジェクト管理などが求められていきます。 とはいえこのあたりは、やっていく中で学んでいくしかないように思いますし他の企業の先輩CTOの方と話して学んでいくのがオススメです。 ソフトスキルに関しても重要です。 技術チームを牽引しメンバーのモチベーションを高め生産性を向上させる能力としてのリーダーシップ、技術的な概念を非技術者にもわかりやすく伝え、異なるステークホルダーと効果的にコミュニケーションを取れるようにするなどです。 特にコミュニケーションについては、エンジニアにとっては重要だよねという概念をしっかりと非技術者に伝える事が必要です。 そうでない場合、リファクタリングやアーキテクチャ変更などについて経営陣に伝わらずシステムに問題が起きてプロダクトの成長を阻害するタイミングになってからしか対応が許されないといったことになりがちです。身に覚えがある人も結構いるのではないでしょうか。 CTOになるためには、これらのスキルと経験をバランス良く持ち合わせ、常に最新の技術トレンドに敏感であることが求められます。また、ビジネスと技術の両面で企業をリードするための幅広い知識と洞察が不可欠です。 キャリアパスの通過点としてのCTO CTOの役割は、単なる終着点ではありません。 それはむしろ、私のキャリアパスにおける重要な通過点であり、成長と発展の旅の一環です。このポジションは、私がこれまでに積み重ねてきた経験と知識を活かし、さらに拡張する機会を提供してくれます。 正直な話をすれば、 IC(Individual Contributor )として働いているときよりも苦手なこともやる必要がありストレスを感じる面も一定あります。 ですがやってみて4年続けてみて、自分がただ働いているだけでは知り得なかった世界や考え方に多く触れ悩み自分の得手不得手を改めて実感できていると感じています。 なってから悩んだときの一つアドバイスをするとすれば、とりあえずCTOが集まるようなイベント(吉祥寺にp2bhausという良いお店があります)などに顔を出して様々なフェーズのCTOに会いざっくばらんに悩みを話してみると良いでしょう。どうしてもこのようなポジションの悩みはオープンにならないことが多く、孤独な戦いになりがちです。会社の他のメンバーに悩んでいるところを多く見せるわけにもいかないでしょう。 その時に同じような悩みを持ち、現在進行系で悩んでいる人や既に解決して次のフェーズを見据えている人たちの話を聞くことは大きなプラスになるでしょう。 まとめ 「CTOになる」という目標は、ただの職業的な到達点ではなく、技術とビジネスの世界で自己を実現する旅の一部です。この役割では、最新の技術トレンドを駆使し、革新的な解決策を生み出し、チームをインスパイアすることが求められます。これは、あなたの技術キャリアにおいて、新たな視野を開き、影響力を拡大する絶好の機会です。 なるチャンスは多くないかもしれませんが、もしCTOになっていきたいという方は自分の意志で掴み取って逃さないようにしてください。 なにより重要なのは、自分がその会社の一部として大きな時間を賭けられるだけ気に入った会社やプロダクトがあるということかと思います。非常に困難な状況になっても、会社やプロダクトがあれば割りとなんとかなります。 というわけでそんな自分がCTOをやっている価値があると思っているBASEではメンバーの募集をしています。 下記からご応募をお待ちしております。 open.talentio.com
この記事は BASE アドベントカレンダー 2023 の24日目の記事です。 基盤グループ エンジニアの田中 ( @tenkoma ) です。 2023年5月から8月にかけて、書籍「単体テストの考え方/使い方」の読書会を社内有志でしました。 読書会の様子や感想をまとめます。 書籍「単体テストの考え方/使い方」について 単体テストの考え方/使い方 プロジェクトの持続可能な成長を実現するための戦略 | マイナビブックス 単体テストの考え方/使い方 プロジェクトの持続可能な成長を実現するための戦略 | 達人出版会 2022年12月に出版されました。 2020年1月に出版された Unit Testing Principles, Practices, and Patterns (Manning) の翻訳書です。 単体テストについて定義し、その価値を最大限に高めるための方法について解説されています。 書籍への期待 テストコードを構成する概念やそれを書くテクニックがまとめられているところが気になりましたが、1.2 なぜ、 単体テストを行うのか?(7ページ)で紹介された「 持続可能 」という言葉に惹かれました。 それでは、単体テストをすることで何を成し遂げたいのでしょうか?その答えは、ソフトウェア開発プロジェクトの成長を持続可能なものにする、ということです。そして、この 「 持続可能 」という言葉が鍵となります。と言うのも、プロジェクトは簡単に大きくなってしまうものであり、特に、プロジェクトをゼロから作り始めた場合、その傾向が強くなります。 しかしながら、その成長を長いあいだ持続させることは簡単なことではありません。 例えば、テストコードも量が増えてくるとさまざまな問題が起こることがあります。テスト実行が遅くなる(Slow Tests)、テスト結果が不安定になる(Flaky Test / Erratic Test)などは代表的かと思います。 それらの兆候を発生しにくくし、プロジェクトの持続可能性を高めるテストにしたい。そのための知見が得られそうだと期待しました。 読書会の経緯と進め方 4月下旬くらいに個人としてテストコードの書き方をレベルアップする方法やら、知識をうまく他人に説明できるようになりたいと思い 何か参考になりそうな資料はないか検索したところこの書籍を見つけました。 内容をみてみると、未だ日本語訳が出版されていない xUnit Test Patterns の知識も多く取り込まれてそうと感じました。 話題共有できるSlackチャンネルを作ったところ、興味がある方、積読してた方などが集まったため、読書会を行うことにしました。 書籍の内容で分からない箇所や話したいトピックをNotionに書いてもらう(事前に読まなくても参加OK) 事前に読んでもらうのは少しハードルが高いかもしれませんが、グループ横断でメンバーが集まったので、スムーズに進行するために可能なら読んでもらう感じにしました 毎週月曜日9:30から1時間、Slack Huddleで話す時間を用意する 1章分のテーマについてその時間で話す 章末に書かれている2〜3ページのまとめを音読 その後、Notionに書かれたトピックについて話す 最小実施人数は3名で集まらなかったらスキップ。祝日の場合もスキップ 1章を読み始める前に、進め方について話す回を用意して大枠を決めました。 読書会の結果 参加者は日頃からプロダクションコードとテストコードの両方を書く開発者が多かったですが、そのノウハウや課題がチームをまたいで共有される良い場になったと思います。 また、プロジェクトのプロダクションコード・テストコード改善にこの書籍の内容や、Slackチャンネルが紹介されることもあり、やってよかったなと思いました。 書籍についての感想 まずは、書籍の中で定義されている様々な用語を普段テストコードを書いたりコードレビューするときに活用できるものが増えたと思います。 例えば、以下のような用語たちです。 テスト対象システム(System Under Test: SUT) テスト・ダブル(モック・スタブ・スパイなど) AAAパターン( 3Aパターン とも) 用語自体は知ってはいたのですが、これらをテストコードに使ったり、コードレビューで用いなくてもテストコード運用に支障なく、説明するコストも必要なので、使わなくていいのでは?と思っていました。 しかし、用語の意味を理解し、テストコード中の変数名に sut や stub を使ってみると、効果があると思い直しました。 テストメソッド中で変数名が $sut なら、それがテスト対象であり、 $**Stub なら、テスト対象に間接的に入力を与える依存であるとわかります。 $**Spy なら、メソッド呼び出しを記録して、あとでアサーションするんだろうな、と推測できます。テストコードを理解するコストを下げる効果がありそうです。 次に以下のようなトピックについて丁寧な解説があり、読み進めているときはやや過剰さを感じていました。 網羅率(coverage) 単体テストにおける古典学派とロンドン学派 その後の章を読むために必要な基礎知識であるとわかります。 古典学派とロンドン学派については、 テスト駆動開発 - オーム社 で紹介されていましたが、正直知識も興味もなかったです。しかしテストダブルの使い方の違いがテストコードの書き方にも大きな影響を与えることが理解でき、テスト戦略を考える上で必要な解説なのか、と思い直しました。 第4章からは「良い単体テストを構成する4本の柱」として以下を軸に詳細な議論を進めていきます。 退行(regression)に対する保護 リファクタリングへの耐性 迅速なフィードバック 保守のしやすさ モックの使い方や統合テストの章も頻繁にこの軸を使って説明が進むため、理解しやすいと思います。 依存、偽陽性・偽陰性、テストダブルなども図を多く使って解説されるためわかりやすいです。 以下のポストを紹介します。 単体テストの考え方を読んでいるけど、著者は知識の整理がめちゃめちゃ上手い。スタブとモックをコマンドクエリ分離の原則と結びつけたり、良いテストが備える性質のトレードオフをわかりやすく図解してくれたり、とてつもなくわかりやすい。必読の一冊になる気がする。 https://t.co/Y3bhkSrp7x — ひらまつ@Linux200本ノック📘発売中丨ひらまつしょうたろう (@hiramatsuu) April 28, 2023 ここまで特長や良い点について書きましたが、一方で鵜呑みにできない点はありそうです。 関数型アーキテクチャについては僕にその知識がほぼなく、活用できるか分からなかったこと、紹介されるサンプルの多くがバックエンドのロジックであり、 フロントエンドアプリケーションに適用できるかについても網羅的ではないかもしれません。 以上、この書籍だけでテストについて網羅できるわけではない部分はありそうですが、日常的なテストの書き方、コードレビューから、テスト戦略を組み立てる上で参考にできる部分が多く、良書だと感じました。 明日は、川口 ( id:dmnlk ) さんの組織の話です。お楽しみに! おまけ github.com/tenkoma/utppp-php いくつかの章についてPHPで写経しました
はじめに この記事は BASE Advent Calendar 2023 の23日目の記事です。 こんにちは、BASE株式会社のBASE BANK Divisionというチームでエンジニアをしている大垣( @re_yuzuy )です! 今日で20歳になりました。 今年はインターンから正社員になったり、1人暮らしを始めてみたり、色々と新しいことを始められた1年だったかなと思っています。 その中でも印象に残っているのは、BASEのエンジニアインターン採用を再開できたことです。 この記事ではインターン採用を再開しようと思った理由やインターンの概要などについて書いていきます。 インターン採用を再開しようと思った理由 前提としてBASEにおけるエンジニアインターン採用は僕が入社した2021年2月ごろから閉じられており、記憶の限りでは僕が最後のエンジニアインターンでした。 僕が正社員になった現在、BASEにはエンジニアインターンとして働いている方は0人です。 個人的にインターンでの経験がとても楽しくためになったと思っており、 他の人たちにも同じような体験をしてほしい と思ったのがインターン採用を再開しようと思った1番の理由です。 僕は高校生のときからエンジニアインターンとして働き始めましたがこれは完全に運がよかったとしか言いようがなく、今より多くの人にそういった機会が訪れるようになればと思っています。 自分のインターン体験談 実際インターンでどんなことができるのかというところで、少し前までインターンだった僕の体験談を1例として軽く紹介したいと思います。 僕は2021年2月にインターンとして入社し、オンボーディングタスクを終えてからすぐにBASEカードの開発チームにアサインされました。 BASEカードについての詳しい説明は こちらのページ を参照していただきたいのですが、BASEで作ったショップの売上をデビットカードのような体験でカード経由で使える機能です。 当時はBASEカードのリリース前で、たまたま入ったタイミング的に新規プロダクトかつカード決済というちょっとニッチなドメインの開発に関われたことはとても運が良かったと思っています。 BASEカードの立ち上げでは決済コア機能の実装やキャッシュバックの設計など、結構重要な部分を任せてもらいました。 BASEカードDay1リリース後はリアルカード機能に向けた開発やDay1リリースの後片付け、施策に向けた開発など色々やっていました。 手を挙げれば大抵やらせてくれる環境で、いい意味でインターンだから~と特別扱いされなくてとてもありがたかったなと思っています。 また気持ち的にも、みんなと一緒の目線でBASEカードというプロダクトに向き合えていたと感じています。 改めてインターン時代を支えてくださったみなさんに感謝を。 インターンの概要 open.talentio.com 今回募集しているのは僕の所属するBASE BANK Divisionでの長期インターンです。 BASE BANKはBASEの中でも金融ドメインの開発を担当しており、主に「YELL BANK」「BASEカード」「振込申請」の3つの機能・プロダクトを開発しています。 インターンではいずれかの開発チームに1メンバーとして参画していただき、様々な課題解決に向けた開発に携わっていただきます。 プロダクト開発への関わり方について BASE BANKのエンジニアは全員がフルサイクルエンジニアとして働いています。 フルサイクルエンジニアという言葉に聞き覚えがない方も多いと思いますが、1文で説明するなら「設計・開発・保守・運用など開発サイクルすべてに主体的に取り組むエンジニア」かなと思います。 僕はフルサイクルな開発ができることがBASE BANKで開発する上で最も大きい魅力の1つだと思っています! もちろんインターンの方も同様に、フルサイクルエンジニアとして特定の開発サイクルや分野に固執しないような働き方をしていただくことになります。 仕様が決まっているものを実装しておわり!ではなく、プロダクトの仕様検討やシステムの設計から実装、リリース後のバグや問い合わせ対応まで幅広く行います。 フルサイクルエンジニアについての詳しく知りたい方は、昨年のアドベントカレンダーで同じくBASE BANKの松雪さんがブログを書いているのでそれを読むのがおすすめです: Real World Full Cycle Developers 応募条件について 前述した通り、より多くの人に機会が訪れてほしいという思想のもと、今回のインターンは実務未経験でも応募可能にしています。 とはいえ習熟度と環境にあまりに乖離があるとお互いに不幸になってしまうので一定の基準を設けています。 技術的な基準についての詳細は募集要項を参照してください。 また出勤頻度等についてですが、現状週3日以上かつ週15時間以上という条件にしています。 これは能動的で自立した開発を任せられる最低限の頻度かつ時間ということで定めています。 さいごに BASE BANKでのインターンに少しでも興味を持っていただいた方は、ぜひ募集ページから応募していただきたいです! いきなり面接若干ハードル高いよ、という方は @re_yuzuy にDMしていただければと思います!ぜひカジュアルに話しましょう! open.talentio.com 明日は @tenkoma さんの社内読書会についての話です。お楽しみに!
はじめに 本記事は BASE アドベントカレンダー 2023 の22日目の記事です。 おはようございます、こんにちは、こんばんは。 BASE BANK DivisionでPMM(Product Marketing Manager)を務める@usui_daisukeと申します。 記事タイトルは『 ぼくらが旅に出る理由 』をオマージュしました。服とお酒と音楽が好きな人間です。 私が所属しているBASE BANKとは、BASEの中でもショップオーナーさん向けの金融系プロダクトを担当するチームで、私は「振込申請」というショップの売上金を引き出す機能を担当しています。 今回は、だいぶ世の中に浸透してきた気はしつつ*1、まだまだイメージが付きづらいであろうPMMという職種が何をやっているのか?私がなにを面白いと思ってPMMをやっているのか?をお話できればと思います。 *1 過去5年間の「PMM」に対する検索トレンド推移を参照&自身の肌感。定期的に波が来ているように感じる https://trends.google.co.jp/trends/explore?hl=ja&tz=-540&date=today+5-y&geo=JP&hl=ja&q=PMM&sni=3 BASE BANKにPMMが必要な理由 弊社はグループミッションとして「Payment to the People,Power to the People.」を掲げており、決済の簡易化を通じて、人々をエンパワーメントすることを目指しています。 それを補完する手段のひとつとして、「金融の簡易化」が必要であるという仮説を持ち、ショップ作成サービス「BASE」を運営するチームとは別に、BASE BANKという金融に特化したチームや各プロダクトが出来上がりました。 BASE BANKチームのミッション。弊社採用資料より引用 https://speakerdeck.com/base/basebank 現在、BASEを使ってくださるショップオーナーさんは、70%以上が個人事業主であり、4名以下で運営されているショップがほぼ100%を占めています*2。 *2 弊社 オーナーズ調査2023 より 個人やスモールチームの中で、金融知識に明るい方というのは多くはなく、そういった知識よりも「どうしたらいいものを作れるか?」「どうしたらもっと商品が売れるか?」という部分にフォーカスしたい方が多いと、過去のリサーチから明らかになりました。 他方で、商売をする上でお金まわりというのは切っても切れない関係で、否が応でも考えさせられる事柄です。 BASE BANKチームは、個人やスモールチームにとってわかりづらい「金融」というドメインにおいてプロダクト開発を行っており、「いかにわかりやすく、利用価値や方法を伝えられるか?」という点が、プロダクトそのものと同じくらい重要。 そのため、「ユーザーとプロダクトの『橋渡し役』」として、PMMという職種が必要とされています。 BASE BANKにおけるPMMとは 「PMM」と検索すると、さまざまな媒体・人の記事が出てきますが、BASE BANKではPMMに対して以下の3つが求められています。 チーム内で最もユーザーへの深い理解・インサイトを持つ プロダクトマーケティングにおける施策実行および数字に責任を持つ プロダクトマーケティングで得たインサイトを基に、PdMを中心に各ステークホルダーと協同し、プロダクトへ反映させる BASE BANKでは、ひとつのプロダクトに対してPdMとPMMがひとりずつ紐づいており、担当プロダクトをどう伸ばすか?を二人三脚で考えています。 担当によって異なる部分もありますが、PMMがどんなことをやっているか?を3つほどご紹介。 ユーザー理解のためのリサーチ ユーザーのことを理解していないと、「ユーザーにとってのわかりやすさ」がわかりません。 そのため、アンケートやユーザーインタビューを定期的に実施することでユーザー理解に努め、後述するキャンペーンの企画や効果検証、プロダクトニーズの模索、新機能検討の一助としています。 例えば、「キャンペーンを行っても参加率が伸びない」という問題に対して、キャンペーン告知媒体ごとの認知率や、キャンペーン内容の是非についてアンケートをとってみたり、「プロダクト利用頻度の高いユーザーが、過去と比べて減ってきている」という問題に対して、頻度が落ちた・まったく使わなくなったユーザーにその理由や、使わなくなったあとの移行先についてインタビューしてみたり。 このような形で、各プロダクトが抱えている問題をベースに、アンケートやインタビューを実施しています。 実施したインタビューは、ショップオーナーさんの許諾をいただき記事化することも https://baseu.jp/26724 プロダクトマーケティング ユーザーにプロダクトの価値を届ける手段のひとつとして、かかせないのがキャンペーン。BASE BANKのPMMは企画の立案から進行、分析までを一気通貫で行います。 企画フェーズでは、前述のアンケートやユーザーインタビュー、 Looker から抽出した実績値をもとに、仮説・データを基に企画を立て、PdMと壁打ちしながら企画をまとめ、進行へと移っていきます。 進行フェーズでは、アナリスト・エンジニア・デザイナー・オペレーションといったPdMやPMM以外の職種、BASE BANK以外のメンバーも巻き込みながら進めます。直近私が担当しているキャンペーンだと、12部署・30人前後のメンバーと協同。 PMMは、プロジェクトオーナーとして全体の進行管理をしつつ、このフェーズではキャンペーンの情報を届けるために、プロダクトの管理画面上の文言や、LPやメール、SNSなどの外部に発信する文章を検討・作成し、ユーザーコミュニケーションをリードしていきます。 分析フェーズでは、数値目標の達成有無だけではなく、実施目的に即してユーザーに利用してもらえたか?上振れ・下振れ要因はなんなのか?を明らかにし、次回以降のキャンペーンに役立て、PDCAを回していきます。 プロダクト企画 ユーザーリサーチやプロダクトマーケティングで得たインサイトを基に、PdMを中心に各ステークホルダーと協同し、プロダクトへ反映させるのもPMMの業務。 例えば先日、 BASEカード (BASEの売上金を全国のVISA加盟店で即時に使えるカード)チームで行った「BASEカードの活用事例」をテーマとしたユーザーインタビューの中で「発生している売上に対して、カードの限度額が少なく、使いたい時に使えない場面がある(要約)」といったご意見をいただきました。 話を紐解き、現状のプロダクト体験に照らしてみると、「まとまった仕入れを行いたい時に限度額に達してしまうと、売上金は残っているのに他のカードを使わざるを得ない」というよくない体験となっていたことがわかりました。 その後、インタビューで得たインサイトを踏まえ、想定されるニーズや体験変更において必要な要素を担当PMMが整理。結果として、インタビューから2ヶ月で限度額の引き上げをリリースするに至りました。 baseu.jp 私が思うPMMの面白さ 「ビジネス総合格闘技」という側面 私のキャリアのスタートは、Webメディアの広告営業でした。そこからリサーチャーをやったり、広告プランナーをやったり。インサイドセールスの立ち上げをやったり、カスタマーサクセスの立ち上げをやったり。PdMだったときもあれば、BiZDevだったときもあり、UXコンサルだったときもあり、BASEに入るまで3社10職種以上の業務を行ってきましたが、あの経験使えないな~というものが一切ありません。 ユーザー理解においては、リサーチャーやUXコンサルの経験が活きているし、プロダクトマーケティングやプロダクト企画においては、PdMやBizDevの経験が活きています。細かな業務も含めると、これまで培ってきたスキルが存分に活かせていると感じています。 他方で、数字面ではリサーチャー時代を含めて、定量分析においてはかんたんなクロス集計程度しか行ってこなかったので、数字まわりは入社当初ちょっと苦労しました。ただ、マネージャーやチームメンバーを頼りながら業務を進めていくうちに、今では一定こなせるようになっています。 過去身につけたスキル、新たに身につけた・伸ばしたスキル、すべてを使って仕事できるのが、PMMとしてのやりがいに繋がっています。 ステークホルダーの多さ 前述通り、PMMはさまざまメンバーと連携しながら仕事を進める職種です。私自身、同期・非同期問わず人とコミュニケーションを取るのが好きで、「ひとりではできないことでも、複数人ならできる」ことに尊さを感じています。 抽象的な話になりますが、日々議論し、それを形にしてアウトプットし、目に見えた結果として返ってくるというのは、ある種の創作行為だと私は思っています。 そこに対して産みの苦しみ・喜びみたいなものを、ひとりでなにかするときよりも大きく感じられるところが、ステークホルダーの多さに面白さを覚えるポイントなのかもしれません。 こういった面白さが、ぼく(ら)がPMMをやる理由です。 おわりに あくまでも「私個人の」「BASE BANKにおけるPMM」の話ではありますが、PMMをやっている・やりたいと思っている方、PMMと働いている他職種の方などにとって、PMMという職種の理解が少しでも進んでいたら嬉しいです。 昨年のアドベントカレンダーでは、私以外のPMMがユーザーリサーチとABテストを実施してCVRを向上させた話を語ったり、担当プロダクトについて語ったりしているので、ぜひ今年のアドベントカレンダーとあわせてご一読くださいませ。 devblog.thebase.in devblog.thebase.in また、現在BASE BANKでは、エンジニアや事業企画といった職種の採用を行っています。この記事を読んで面白そうだなと思っていただけた方は、ぜひお気軽にご応募いただけたら嬉しいです。 open.talentio.com 明日は、私と同じくBASE BANK Divisionに所属している 大垣さん の記事が公開予定です!お楽しみに。
はじめに この記事は BASE アドベントカレンダー 2023 の22日目の記事です。 こんにちは。 Communication Design Groupデザイナーの藤井です。 プロダクトをはじめ、LP、メール、またイベントや営業資料など、直接間接を問わずBASEのテキストコミュニケーション全般のユーザー体験を向上させる、UXライティング/コピーライティングを担当しています。 「テキストコミュニケーションをデザインする」をキーワードに、テキスト版デザインシステムを構築・運用し、あらゆるタッチポイントにおいて、ターゲット・目的・ゴールにもっとも最適化されたテキスト体験を設計、デザインしています。 テキスト品質担保施策の実施から見えてきた、次の課題 これまで、トーンオブボイスを策定したり、ガイドラインを構築したり、テキスト入力支援ツールを開発したりと、テキストの品質を担保するためのさまざまな取り組みをおこなってきました。 そのなかで見えてきたのは、こんな課題でした。 組織の拡大にともなうPJの増加により、テキストレビュー/UXライティングの合計時間はそれほど変わらない テキスト体験の設計ナレッジの共有による、プロダクトチーム全体のテキストコミュニケーション力のボトムアップが必要 テキスト入力支援ツール・textlintの導入、運用開始により、たしかに本質的な体験のデザインに多くのリソースをかけられるようになり、飛躍的にBASEのテキストコミュニケーションは向上しました。 一方、BASEのプロダクトもオーナー数、また組織が急拡大するなか、テキストレビュー担当者のリソース自体は急に増えるわけではなく、結果としてまだまだレビュアーの属人性に拠っているのもまた現実なのです。 UXライティング力の組織全体におけるボトムアップが必要 では、この課題に対してどのようなアプローチが有効なのでしょうか? 私たちは、テキストをコミュニケーションに用いるBASEのすべてのメンバーに、UXライティングのもっとも本質的な部分、「相手にことばを届けるためのコツ」をシェア、活用してもらうことによる、全体的なボトムアップを図ること、と考えました。 そしてそれは、ターゲット・目的・ゴールにもっとも最適化されたテキスト体験の設計・デザインのためのメソッド(方法論)、つまりUXライティングの本丸にほかなりません。 かくして2023年は、社内LTへの登壇、ワークショップの実施、ライティング駆け込み寺とそのまとめメソッドの共有など、とにかくシェアに明け暮れた1年でした。 取り組んだ、3つの手法 今回は、その取り組みの一部をご紹介してみます。 1. 社内LTへの登壇 まずはプロダクトチームに限らず、会社全体に向けて実施させていただいたLT、「BASE、UXライティングの流儀」。 そもそもユーザー体験のよい日本語とは、 意味がわからず、モヤモヤさせない 不愉快な思いをさせない どうすればいいか、すぐわかる つまり「相手を思いやる気持ち」をことばに込めることであり、気持ちよくコミュニケーションするための作法であるということ、それはプロダクトのタッチポイントに限らず、生きていくなかで誰にとっても基本となるたしなみ、であることを発信。 その上で、BASEのことばがよりどころとしている3つの信念(Principle)、 シンプル&プロフェッショナル →誰でもわかる/誰でも満足できる オーナーシップ →自分らしいまま自信が持てる 全員、使える →リテラシーフリー を共有し、だからこそ自分ごと化してもらえる、行動してもらえる、ということをお伝えしました。 2. ワークショップの実施 そんな信念を実際にテキストに落とし込むためのメソッドを、ワークショップで実践。 書き方の基本5か条、 とにかく短く、簡潔に(一文一義) 語りかけるように 何ができるようになるのかを伝える いい感じのさじ加減の丁寧さ 何が起きたのか、何をしたらいいのかを伝える をシェアしてから、テキストレビュー担当者がおこなっているテキストレビューに、実際に挑戦。 お題のテキストのターゲット/目的/ゴールを書き出し、そのターゲットに向けた言葉使いで、どうすればこちらが描いたゴールにつながるか考え、文章を整える作業を体験してもらいました。 3. オープンテキストレビュー・ライティング駆け込み寺の開催 また、毎日14:00〜14:30にはオープン・テキストレビュー/ライティング駆け込み寺も開催するように。 さまざまなチーム、PJから日々依頼いただいているテキストレビューを、画面を共有してライヴで実施しつつ、「割り込みで急ぎのレビューを依頼したい」「どうやって書けばうまくまとまるのかわからない」「イチから書いてほしい」などの、テキストにまつわるお悩みを解決する、駆け込み寺をオープン。 たとえば、 「『こちら』の禁止」 →「こちら」では、何を指すのかわからない。読み手の行動に齟齬がないよう、遷移先ページタイトルからテキストリンクさせる 「句読点がリズムを作る」 →人はアタマで音読している。息継ぎできるよう句読点を活用すると、読み心地も上がる 「総量を明示」 →先に「◯つのコツ」と言われると、「それくらいなら読めるかな」という心理になって向き合ってもらいやすくなる。オススメは人間の脳の特性上、3がいいと言われる など、テキストガイドラインではわからないレビュワーの視点・メソッドを、実際のテキストのビフォーアフターを見ながら詳らかにすることで、より体感してもらえるようにしています。 おわりに 2023年11月に11周年を迎えたBASEは、ありがたいことに、200万を超えるショップオーナーの皆様にお使いいただいているプラットフォームに成長いたしました。 BASEのテキストコミュニケーションをデザインする取り組みは、その効果測定、アップデート、最適化はもちろんのこと、コピーライティングの分野でも最高のユーザー体験をデザインできる組織へと進化してまいります。 すべては、「最小の力で、最大の成果を出す、ネットショップ作成サービス」でありつづけるために。 そして、「多くの進化と改善で、ショップオーナーの皆様の成功を後押し」するために。 2024年のBASEにも、どうぞご期待ください。 さて、明日のアドベントカレンダーは @Ren Ogaki さんです! お楽しみに。
本記事は BASEアドベントカレンダー2023 の21日目の記事です。 はじめに こんにちは、BASE BANK Division(以降BASE BANKと略記)にて振込申請周りの開発を担当している umi です。 全く畑違いのところから行き着いた前職のWeb制作会社から転職し、BASE BANKにてWeb開発を行うようになり1年とちょっと経ちました。Web開発エンジニア歴は浅いですが、エンジニアという領域に閉じず、他職種と積極的にコラボレーションしながら、プロダクトの開発を行なっています。 突然コラボレーションと表現しましたが、BASE BANK Devでは「ショップオーナーに対する直接の価値提供」「社内外様々なステークホルダーに対する価値提供」のためにコラボレーションすることを2023年の行動指針にしており、その一例として、振込申請における振込先口座名義の訂正に対する改善を取り上げようというのが今回の記事になります。 振込申請における振込先口座名義の訂正を改善 振込申請 とは、ショップの売上金を引き出すための機能です。 売上金を振込先口座に振り込む仕組み上、ショップオーナー様に口座情報を入力していただく必要がありますが、この際、ショップオーナー様が誤った口座名義を入力してしまうケースがあります。 この場合、ショップオーナー様に口座名義を訂正していただくと売上金の振込が可能となります。しかし、改善前の状況として、口座名義を訂正しても振込ができなかったという問い合わせが1週間で約10件ありました。振り込みできなかった理由は、訂正した口座名義が再び誤っていたためです。 今回の改善における問題・仮説・改善策を挙げると以下のようになります。 問題 「ショップオーナー様が口座名義の訂正時に誤入力してしまう」 口座名義の誤入力が続くと、振込までの時間が伸びてしまい、ショップオーナー様の出金が遅延してしまいます。 仮説 「ショップオーナー様のネクストアクションを促す適切な文言になっていないのではないか」 どのように訂正すれば口座名義が訂正できるのかがうまく伝わっていないのであれば、むしろショップオーナー様が誤入力することを促す状態となってしまいます。 改善策 「適切な文言によって、ショップオーナー様に次のアクションを丁寧に促す」 仮説通りであり、文言修正で改善できるのであれば、相談から実装、リリース含め1日で対応できると判断でき、コスパよく改善できます。 全体の経緯としては、一次問い合わせ対応を行うカスタマーサポートチーム(以降CSと略記)より具体的な改善の方向を含め連携をいただき、影響度と工数の観点からチームで相談し優先度を上げ対応する運びでした。 また、改善の結果として実際にどれくらい効果があったのかはDevとしてもCSとしても知っておきたい情報です。CSにお願いし「 改善前後でどれくらい問い合わせが変化したか 」を計測する依頼をこちらからはさせていただきました。 結果 改善前は問い合わせが1週間あたり約10件ありましたが、改善後は1週間あたり約2件に減少しました。つまり「1/5に減少」です。 ショップの売上金の引き出しというネットショップのコアとなる機能において、相談から実装、リリース含め1日以内で対応を行い問い合わせ件数を1/5に減らすことができたため、とてもコスパのよい改善となりました。 本件はちょっとした改善でありながらも、口座名義訂正時おけるUX向上・ショップの売上金を引き出すまでの時間短縮という点で「ショップオーナーに対する直接の価値提供」、CSに来る問い合わせ件数の減少という点で「社内外様々なステークホルダーに対する価値提供」をするというコラボレーションになりました。 チームを跨いでスピード感を持って改善できるのは、自社プロダクトを持つWeb開発会社ならでは特徴の1つではないかと思います。 おわりに 日々の業務では大変なこともありますが、それを通じてチームの垣根を越え開発する楽しさやショップオーナー様へ価値提供できることの素晴らしさを感じています。 というところで若干急ハンドルですが、上記のような私含め様々なバックグラウンドを持つ方々がいるBASE BANKでは、一緒に頑張っていく人を募集しています。少しでも気になった方は、どのような人が中にいるか知ることができるため、「 社内公募制度を使ってみました!〜Youは何しにBASE BANKへ?〜 」なども合わせて読んでみるとおもしろいかもしれません。ぜひ、カジュアル面談などお越しください! devblog.thebase.in open.talentio.com 明日は usui さんの記事です、お楽しみに!
この記事は BASE Advent Calendar 2023 の20日目の記事です。 こんにちは!BASE BANK Divisionでエンジニアをしている大津(@cocoeyes02)です。 今回は社内公募制度と、社内公募制度を使ってBASE BANK Divisionにジョインした人たちの声をお届けします! 社内公募制度とは? 社内公募制度とは、社内で求人の募集をしている部署に、社員が自ら部署への異動に応募することができる制度です。この制度は、組織の活性化や、社員の主体的なキャリア形成を目的として誕生しました。 社内公募制度は、求人の募集を終了しない限りは基本的に通年で応募できます。よくある選考フローとしては 書類選考 面接 現所属部署の直上長のリファレンスチェック 最終面接 オファー面談 と、リファレンスチェックを書く人が上長になる点を除けば、社外から求人に応募した人の選考フローと同じフローを通って、異動が決定します。 社内公募制度を使った人たちの声 実際に社内公募制度を使ってBASE BANK Divisionへ異動した人に、社内公募制度を使った背景などを語ってもらいました! 機械学習エンジニア Saito Yusuke さんの場合 BASEの機械学習エンジニアとして入社してから約6年になります。これまでBASEの様々なサービスに対して、機械学習モデルの開発や運用・データ整備・分析のサポートを行なってきました。各チームやサービスの課題を解消するためにプロジェクトに参画する機会を多く頂き、どんなアプローチで解決するべきかを模索し、提案、開発していく過程はとても満足のいく経験でした。 ただ、既に顕在化している課題を対処するだけでなく、課題発見から価値創造までを責任を持てるようになりたいという意欲が生まれていたタイミングで、社内公募に挑戦してみました。 今回はキャリアチェンジというより、新規事業部という新しい環境での挑戦でした。部署を移動してからも期待通りのタスクを担当できて満足しています。また、転職とは違い部署の雰囲気を間近で感じていたので、あまり不安要素なく決断できました。 デザイナー Hayakawa Muneaki さんの場合 自分は2014年12月にBASEに入社したので、BASE歴は結構長い方になります。 デザイナーとしてBASEプロダクトに携わってきた8年間は、常に新しいことが待ち受けていて飽きる暇もない日々だったのですが、2021年の12月、翌年からのことを考えていた時に、在籍丸8年が経ち9年目に突入するというタイミングで、思い切った新しいことにチャレンジしたいという気持ちが強くなっていました。ちょうどその時期に社内公募のBASE BANKデザイナー募集の説明会を聞き、強く興味を惹かれました。 自分としては9年目というこのタイミングで、これまでとはもう一段別の切り口でショップオーナーへの価値提供ができるBASE BANKというフィールドで新しいチャレンジをしてみたいという気持ちに火がつき、応募に至りました。 入ってみてからの感想としては、自分がBASEに入社した2014年12月当時、組織としては30名弱くらいの規模感だったのですが、今のBASE BANKのメンバー数もそれに近く、雰囲気としても当時のBASEに近いものを感じています。 少人数の新規事業部ならではの機動力の高さ、意思決定の早さはとても刺激的な環境で、デザイナーとしてもどんどん新しいチャレンジができるので日々楽しく仕事ができています。 フロントエンドエンジニア Koga Yuki さんの場合 私は2021年1月にBASEに入社し、BASE BANKには2023年11月付で異動しました。BASEではフロントエンド主体の業務を行っており、直近だとメンバーシップAppの開発に携わっていたりしました。 BASEのフロントエンドの開発領域としては、Vue.jsを使ったショップオーナーさん向けの画面を作る部分、ノーコードでショップの画面を作ることができる「デザイン編集画面」の機能拡張などなど、多岐にわたります。こういった開発はフロントエンドエンジニアとしてはチャレンジングで他では経験できないような開発を行うことが出来ており、正直、直近で転職や部署異動などを検討していたわけではありませんでした。 しかし、社内のイベントなどでBASE BANKチームの話を聞いている中で、チームにフロントエンド主軸のメンバーが足りておらず、まだまだやりたいことがやりきれていないこと、BASEのフロントエンド領域との連携に課題があることを知り、「自分の純粋な技術力アップだけではなく、自分のスキルを活かして組織のパフォーマンス向上にも寄与できる動きをやってみたい!」と思ったことから社内公募での異動を志願しました。 現在ではフロントエンドに関するタスクだけではなく、フルサイクルエンジニアを目指してGo / Python製のAPIの実装をしたりAWSの設定を触ったりもできていて、自身のいちエンジニアとしての見識が広まってきており、とても楽しく仕事ができています。 実際に異動してどんなタスクの対応をしたのか?などを書いた記事が BASE Advent Calendar 2023の9日目の記事 にもあるので、そちらもぜひご覧ください! バックエンドエンジニア Otsu Kazuki の場合 2021年2月にBASEに入社したので、入社してからもうすぐ3年が経ちます。これまではバックエンドメインのエンジニアとして、BASEで提供している様々なAppの開発(Google商品連携・広告Appや、Tiktok商品連携・広告Appなど)に携わってきました。 開発していく中で自分が目指したいエンジニア像を考えていると、ソフトウェアサイクルの全てに責任をもちたいことや色々な事業フェーズを経験してみたいという欲求があることに気がつきました。しかし、BASEというプロダクトは好きであるため転職したいわけではないとも思っており、どうしたものかと考えていたところで、社内公募制度の存在を思い出しました。 結果としては、社内公募をしていた部署の中の1つであるBASE BANKへと応募するに至りました。 BASE BANKは現在少人数の新規事業部であり、事業をグロースしていく中でフェーズがどんどん変わっていく環境に身を置けるのは得難い経験だと思ったのが大きな要因です。また、BASE BANKではソフトウェアサイクルの全てに責任を持つフルサイクルエンジニアの思想があり、今よりももっと広範囲にわたってエンジニアリングの経験を積めると思いました。フルサイクルエンジニアの話は以下の記事でも触れられていますので、併せてご覧ください。 devblog.thebase.in 社内公募制度は応募先も当然社内になるので、応募する前に普段どんなコミュニケーションが行われているのか話したり、slackやドキュメントなどを見て具体的にどんな業務をしているかなど情報収集が可能です。そのため、単に転職するよりもずっと、ジョインした後の動きが想像しやすかったです。 現在では長期PJにジョインし、設計や実装はもちろん、QA、運用の設計や実施、ステークホルダーとのコミュニケーションやチームの越境によるコラボレーションなどソフトウェアサイクルの全てにまつわる業務に触れる日常を送っています。また、主導して進めていく機会も日毎に増えており、チャレンジの連続による自身の成長も感じています。 最後に 社内公募制度とは、Be Hopefulにチャレンジができる機会を提供する、そんな制度だと個人的には思っています。誰かのチャレンジを後押しする、そんな文化でありたいですね。 BASE BANK Divisionでは社内公募に限らず、社外からもメンバーを募集しております。一緒にチャレンジしたい!という方は、ぜひ一緒にチャレンジしましょう!お待ちしております! open.talentio.com さて、明日のアドベントカレンダーは Unno Yuki さんです!お楽しみに!
はじめに この記事は BASE Advent Calendar 2023 の19日目の記事です。 こんにちは!Data Strategyチーム(以下、DSチーム)でデータエンジニア兼データアナリストの @shota.imazeki です。 今回は全社的なデータ活用を推し進めていくために、いくつかの施策を行ってみたので、それらを紹介していきます。その際に上手くいかなかった点やこうすればよかった点なども紹介していきます。「データを整備したけど使ってくれない」といった課題に直面している方々の参考になれば嬉しいです。 結果 まず最初に結果から話していきます。以下のグラフはBASEで使用しているLookerというBIツールのMAU(月次アクティブユーザー)です。昨年の同時期に比べて利用者数が1.5倍以上伸びています! Looker アクティブユーザー数の推移 「ユーザー数が増えただけでは?」という声も聞こえてきそうなので割合(MAU / その月の全Lookerユーザー数)も一緒に載せています。割合としても50%前後から65%前後にまで上がっています。またBASEの従業員数は276名(2023年9月末時点)なので半分以上の方が毎月Lookerを利用していることになります。 もちろんDSチームで行った施策以外に色々な要因があるとは思っていますが、結果として社内でのデータ活用が昨年よりも進んでいることは事実で、これに関しては素直に喜んでいいのかなと思っています。 施策 施策の方向性としては2種類あります。 全社向け チーム向け 広く浅くが前者、狭く深く進めていくのが後者かなと思っており、それぞれDSチームでやったことを紹介していきます。 全社向け 全社向けに行う施策として意識しているのは、データを見る際のハードルを可能な限り下げることです。難しい技術を扱えるようにするよりも気軽にデータを見れるような環境作り、データリテラシーの向上を目的として施策を実施しています。今回は2つ紹介します。 1. 勉強会の実施 社内のデータリテラシー向上を目的としてLookerやSQLの勉強会を実施しました。Looker勉強会は春から夏にかけて3回、SQL勉強会は秋から冬にかけて5回ほど実施しました。どちらも基礎的な内容になっています。参加人数は回によってばらつきがあるのですが20~40名ほどでBASE全体の1割~2割ほどの方が参加してくれました。 工夫した点としては以下になります。 勉強会の資料を色々な方にレビューしてもらう DSチームに限らず社内で詳しい方や初学者の方に内容を相談したり、レビューをお願いして、勉強会の中身をより良くできるように心がけてました 実データを使って、BASEのデータにも詳しくなる BASEにある実際のデータを使い、よく見られている指標を出すなどして、BASEのデータにも詳しくなれる機会にしました 講義中のクイズや勉強会後の練習問題で実際に手を動かしてもらうことで、能動的に学べるようにもしました オンライン実施且つ録画をすることで、誰でも受けられるようにする 勉強会に都合が合わなかった方、開催当時に入社していなかった方々のためにweb会議ツールの機能で録画をしました 後述するチーム向けの施策において、導入時に見ていただくこともあります。新入社員のオンボーディング時に見るようにしているチームもあるそうです 反省点としては、告知が充分にできなかったことかなと思います。全体Slackチャンネルに投稿しても皆さん忙しかったり、他の投稿に埋もれてしまって見落とすことがどうしてもあります。 実際、以下のような声を聞きました。 そもそも勉強会知らない人多そうな気がしてます・・・ 〇〇チーム内でDSチームが勉強会してますよ!!!って布教してたら、知らなくて、感動してくれる人けっこういて・・・ Slackだけだとどうしても見逃してしまうと思うので、各チームのどなたかに宣伝をお願いするのも一つの手かなと思ってます。100%周知は難しいと思うのですが、できる限りニーズのある方々には伝わるようにしていきたいです。 2. 週一の相談窓口の開設 「Looker / SQLカフェ」と題して、週に1回1時間程度、データ関連の相談をなんでも受け付ける相談窓口の開設を8月に行いました。 社内のフリースペース席に張り紙を貼って、そこで相談を聞くというスタイルです。 業務上困っていることやラフな相談、暇だから話を聞きに来たといった方々もいて、相談自体も幅広く受け付けています。実際に来た一例を列挙しておきます。 Lookerでこんな数値が見たいけど、どうすれば出せますか?? SQLのここについて分からないので教えて欲しいです 勉強会のここについて不明点があるので質問したい ダッシュボードを作る時に意識することや注意点を教えて Looker、何も知らないので教えて欲しい! その場で解決できるなら解決しますし、時間がかかるものは適切な申請先や参考になるドキュメントを紹介しています。 反省点としては、あまりないと思ってますが強いて言うなら、悩みがない人も参加できる形にしてみたいなと思っています。カフェという名前が入っているので、コーヒーを淹れて誰でも参加しやすくして、DSメンバーと交流する機会があっても面白いかなと思ってます。 チーム向け こちらは全社向けとは対照的に、特定のチームに参加してデータ活用を進めていくものです。立ち回りとしてはデータアナリストに近いです。 こちらで意識しているのは、最終的にDSチームのメンバーがいなくなってもそのチーム自身でデータを見ていけるような下地を作っていくことです。正直、ここはとても難しくて暗中模索な部分もあります。データアナリストとしてのメイン業務(分析や効果検証など)の部分は本旨と異なるので割愛して、もう一つのメイン業務であるダッシュボード構築時に意識的に取り組んでる部分について紹介していきます。 ニーズに合わせたLookerハンズオンの実施 チームのニーズとしてダッシュボード構築があった時、DSチームが構築するのではなく、チームから担当者を選出していただき、その方に教える形でハンズオンを実施しています。 DSチームが一番Lookerの扱いに慣れているので短期的に見れば、自分たちで作ってしまった方が早いのですが、長期的な目線で考えてこのような形で取り組んでいます。 チームのニーズに合わせてハンズオンを行うので勉強会以上に実践度合いが高くて、大変ではあるのですが効果は高いかなと思っています。 実際の声がこちら。 勉強会も参加したのですが、自分のPJでやりたいことベースの事例で実践を一緒にやっていただいたことで理解度がかなり上がるな~と思いました🙏 〇〇プロジェクトでダッシュボードの作り方を手取り足取り教えてもらったためか、別件でダッシュボード作る時も驚くほど簡単でした! 反省点としては、導入として全社向けで紹介した勉強会動画の視聴をお願いしているのですが、SQLの知識がある方向けのハイレベルな部分の動画がないことです。LookerではLookMLという特殊な言語を使う場面があって、ハイレベルな方にはそこの部分まで担当いただいてます。ただそこの導入については動画が用意できておらず現状DSチームメンバーが直接教える形になっており、少々効率が悪いです。早めに導入資料や動画を作成するか、参考図書を社内に用意したいと思っています。 Slackチャンネルにダッシュボードを定期配信する 要件によって差はありますが、作成したダッシュボードはSlackの各チームチャンネルなどで日次で配信するようにしています。データを見にいくハードルを可能な限り下げるために、社員皆が毎日必ず使うツールからの導線を作っています。 実際、ダッシュボードを定期的に配信することでSlack上でチームメンバーから反応があり、そこから「こういう分析もしてみよう」といった話に発展することもありました。以下に一例を列挙しておきます。 機能リリース直後に利用状況やユーザーの反応をデータから見てみる 各チームのKPIを日次で配信する ある指標が閾値を超えた場合にSlackに通知する Slackに気軽にデータを見に行ける導線を置くのは、データを見る意識も上がって効果的かなと思っています。 反省点というわけではないのですが、意識の話ではあるので効果が出ているのか分かりづらいという点です。この話だけではないのですが、データを専門的に扱う職業として自分たちの施策の効果はある程度は計測できるようにしたいなと思ってます。 最後に 上記で述べてきた施策や組織的な変化もあり、社内のデータ活用はここ一年で大きく進んでいきました。 またこのような取り組みを行う中で、自分の意識も少しずつ変化がありました。BASE入社前までは自分のスキルばかりに意識が向いてましたが、段々と自分だけではなく組織として分析基盤や分析組織がどうあるべきかに目線が移っていっています。自身初めての自社開発企業だからなのか、BASEの文化がそうさせたのかは分かりませんが、自分にとってはとても良い変化だったと思います。 今後もこのような取り組みをどんどん進めていきたいのですが全体的な反省として、その場の思いつきで動いてしまうことが多々ありました。来年はゴールや理想像をちゃんと考えた上で計画的に取り組んでいきたいと思っています。 最後となりますが、弊社ではデータエンジニアやデータアナリストを募集しております!組織的にも技術的にもまだまだ課題はたくさんあって、とてもやりがいのある仕事かなと思っております!ご興味のある方は気軽にご応募ください! https://open.talentio.com/r/1/c/binc/homes/4380 明日は @02 さんの記事です!お楽しみに!!
この記事は  BASE Advent Calendar 2023  の18日目の記事です。 Pay ID Appグループの北川です。ショッピングアプリ「Pay ID」の開発チームでエンジニアリングマネージャーを担当しています。 iOSアプリ開発で依存管理に使っている Mint のバージョンアップデートを GitHub Actions を使って自動化した話をします。 Mint とは Mint は Swift 製のライブラリのパッケージマネージャです。 https://github.com/yonaskolb/Mint 私たちのiOSアプリのプロジェクトでは、以下のようなビルドツール系のライブラリの依存管理に1年ほど利用しています(アプリ本体の依存管理には Swift Package Manager を使っています)。 SwiftLint Mockolo IBLinter SwiftFormat XcodeGen periphery LicensePlist SwiftGen もともと、プロジェクトリポジトリの中に BuildTools というディレクトリをつくり、そのなかにアプリケーション本体では利用しないツール群を Package.swift で定義しビルドしていました。 しかし、複数のライブラリを利用している場合、それぞれで利用している依存関係のバージョンが衝突することがたびたびおこり、よりシンプルな方法を検討した結果 Mint を採用することにしました。 サードパーティ製のライブラリではありますが、シンプルな定義ファイルでバージョンを固定することができ、かつキャッシュも扱いやすいことがメリットと捉えています。 候補としてビルド不要で利用できる Homebrew も上がりましたが、チーム開発をする上で各メンバのマシンや実行環境で利用するバージョンを固定したいため、私たちは選択しませんでした。 重視したポイント バージョン固定 依存管理難易度 CIでのキャッシュ管理 SPM ⭕️ ❌ ⭕️ Homebrew ❌ ⭕️ ❌ Mint ⭕️ ⭕️ ⭕️ ちなみに Mint 自体をどうやってインストールするかという課題もあるのですが、それは Homebrew を使ってインストールすることで妥協しています。 バージョンの更新を自動化したい さて、Mint では Mintfile というファイルでパッケージのバージョンを固定することができます。 yonaskolb/xcodegen@2.18.0 yonaskolb/genesis@0.4.0 これらのパッケージは定期的にアップデートをしたいとは思いつつ、 Linter などが管理したいパッケージであったのでアップデートしなくともある程度動くため後回しになりがちという課題がありました。 Renovate を使ってアップデートするPRを作れそうだとわかりましたが、弊社では現時点で導入しておらず、 別の方法を検討しました。 Mintfile 自体はシンプルなフォーマットであり、Swift Package Manager も tag を使ってバージョン管理を行う仕組みなので、GitHub Action で文字列操作と GitHub API を組み合わせることでサクッと実現できるのではと考えました。 パッケージの分だけ更新チェックをするために動的に matrix を設定する チームでは過去に CocoaPods や Swift Package Manager で管理しているライブラリの定期的な更新チェックを CI で自前で実現していたのですが、複数のライブラリのアップデートを1つの Pull Request で行うような仕組みになっていました。 このような仕組みだと、A、Bという更新可能なライブラリがあり、Aは更新が容易だがBはすぐには更新できない、というような場合にAも更新できない、という点で使いづらさを感じていました。 そこで、以下のステップのように各パッケージ毎に更新をチェックし必要なら Pull Request を作成できるようにする仕組みを目指しました。 Mintfile に定義されている依存を取り出す 1で取り出したパッケージ毎にジョブを実行する 各ジョブでパッケージの最新バージョンをチェック Using a matrix for your jobs - GitHub Docs GitHub Actions には matrix キーワードを使って、定義されたパターン分の変数を使ってジョブを実行することができます。 jobs : example_matrix : strategy : matrix : version : [ 10 , 12 , 14 ] os : [ ubuntu-latest, windows-latest ] この matrix は動的に設定することができるので、 Mintfile で定義した分のパッケージを matrix として設定するようにします。 まず、 Mintfile を1行毎に分割し配列の JSON 形式にします。 jobs : # Mintfile から依存関係を取得して、それを Matrix にするために JSON に変換して出力する prepare-mint-dependencies : runs-on : ubuntu-latest steps : - uses : actions/checkout@v4 - name : make matrix for each mint dependencies id : set-matrix run : | MINT_DEPENDENCIES=$(cat Mintfile | sed -e 's/#.*$//' | sed -e '/^$/d' | jq -R -s -c 'split("\n") | map(select(. != ""))' ) echo "dependencies=${MINT_DEPENDENCIES}" >> $GITHUB_OUTPUT outputs : dependencies : ${{ steps.set-matrix.outputs.dependencies }} 例として JSON はこのようになっているイメージです。 [ " realm/SwiftLint@0.51.0 "," IBDecodable/IBLinter@0.5.0 "," nicklockwood/SwiftFormat@0.50.3 "," yonaskolb/XcodeGen@2.25.0 "," uber/mockolo@2.0.0 "," mono0926/LicensePlist@3.23.4 "," peripheryapp/periphery@2.12.3 "," SwiftGen/SwiftGen@6.4.0 " ] 次に、出力した outputs を使って matrix を定義します。 # 各依存関係のリポジトリを参照して、最新のバージョンを取得する check-update : runs-on : ubuntu-latest needs : prepare-mint-dependencies strategy : matrix : mint-dependency : ${{fromJson(needs.prepare-mint-dependencies.outputs.dependencies)}} fail-fast : false steps : - name : print dependencies run : | echo ${{ matrix.mint-dependency }} こうすることで1回のワークフローを起点に依存しているパッケージ毎に更新をチェックすることができるようになります。 あとは、各ライブラリのリポジトリを見に行って最新バージョンがあるか確認し必要なら PR を作成すれば良いという流れになります。 ちなみに、最新バージョンを取得する場合は github-script が便利です。 # GitHub Scriptで最新のバージョンを取得する - name : find latest version from GitHub API id : get-latest-version uses : actions/github-script@v7 with : script : | const { data } = await github.repos.getLatestRelease({ owner : '${{ env.REPO_OWNER }}' , repo : '${{ env.REPO_NAME }}' , }) return data.tag_name result-encoding : string おわりに このようにして Mint で管理しているパッケージの更新チェックを GitHub Actions を使って実現することができました。 matrix を使うことである種プログラミング的にワークフローを制御できるので、自動化の幅が広がると感じています。 明日の19日目は ImazekiShota さんの記事です。お楽しみに!
この記事は  BASE Advent Calendar 2023  の18日目の記事です。 はじめに こんにちは、Pay IDアプリ開発チームでエンジニアをしている小林( @eijenson )です。 ショッピングアプリ「Pay ID」のAndroid版アプリの開発を担当しています。 本アプリでは一部機能でWebViewを使って実装しています。 そこで少し厄介だった仕様とそれをどう実装したかを紹介していこうと思います。 使用言語/ライブラリのバージョン Kotlin 1.7.10 Jetpack Compose BOM 2023.06.01 今回の画面設計 よくあるMVVMの設計です。 UIはJetpack Composeで書かれています。 WebViewに関してはJetpack Composeでは用意されていないので、AndroidViewを使ってWebViewクラスをCompose内に定義しています。 AACのViewModelを使っていて、WebViewClient.shouldOverrideUrlLoadingやdoUpdateVisitedHistory等々のタイミングでViewModelのメソッドを叩いてタイミングに応じた処理を行い、その結果をFlowのイベントとしてUIクラスに伝えて結果を反映させます。 _blankのリンクをタップしたときだけ別ブラウザを開きたい WebViewで開くリンクで、ブラウザで新しいタブを開きたい時に設定する <a href=”https://〇〇” target=”_blank”> といった_blankの設定は無視されます。 WebView でウェブアプリを作成する  |  Android Developers 無視されると、WebView上でこのリンクをタップした際にはWebView内で遷移します。 Pay IDアプリでは購入操作をWebViewで行い、ヘルプページ等へのリンクは購入画面を表示しているWebViewではなく別ブラウザで表示したいということになりました。 そのために実装したコードは以下のようなものです。 fun Screen( // target="_blank"のリンクをタップした時の画面遷移操作をActivity等で実装してListenerに設定する onClickTargetBlankLinkListener: (Uri) -> Unit , ){ //... 省略(ツールバーの設置とかテーマの設定とか) AndroidView( factory = { val webView = WebView(it) // target="_blank"のリンクをタップしたときにonCreateWindowが反応するようにする webView.settings.setSupportMultipleWindows( true ) webView.webChromeClient = createCheckoutCartWebChromeClient(onClickTargetBlankLinkListener) } ) //... 省略 } private fun createWebChromeClient( onClickTargetBlankLinkListener: (Uri) -> Unit , ): WebChromeClient { return object : WebChromeClient() { // target="_blank"のリンクをタップした際の挙動を実装 override fun onCreateWindow( view: WebView?, isDialog: Boolean , isUserGesture: Boolean , resultMsg: Message? ): Boolean { view ?: return false val webView = WebView(view.context) webView.webViewClient = object : WebViewClient() { override fun shouldOverrideUrlLoading(view: WebView?, request: WebResourceRequest?): Boolean { val url = request?.url ?: return false // _blankのリンク等新しいタブが作られる際だけ発火する onClickTargetBlankLinkListener(url) view?.destory() // メモリリーク防止/警告が出ますので後述します return true } } val transport = resultMsg?.obj as WebView.WebViewTransport transport.webView = webView resultMsg.sendToTarget() return true } } } setSupportMultipleWindows(true) と設定してますが、これだけだと_blankのリンクは遷移せず何もしなくなってしまいます。 そこで、 WebChromeClient.onCreateWindow にて一時的に使うWebViewを作成します。 そのWebViewの shouldOverrideUrlLoading メソッドから_blankで遷移するURLを受け取り、Listenerに投げます。 そうすることでtarget=”_blank”のリンクがタップされた時だけ動く処理が描けるようになります Pay IDアプリでは、その処理の中で別ブラウザでWebページを開くようにしました。 onCreateWindow メソッド内で getHitTestResult() からURLを取得する方法もあるのですが、新しいタブを開く操作はWebページにあるJavaScriptが行うこともできて、その場合動かない可能性があったので今回の方法を取りました。 一つ解決していない問題として、この方法を使った際に_blankのリンクをタップしたタイミングで以下の警告が表示されます。 W Application attempted to call on a destroyed WebView java.lang. Throwable at org.chromium.android_webview.AwContents.s(chromium - TrichromeWebViewGoogle6432.aab - stable - 604519333 : 10 ) at WV.s8.loadingStateChanged(chromium - TrichromeWebViewGoogle6432.aab - stable - 604519333 : 4 ) at J.N.MlAm1rvf(Native Method) at org.chromium.android_webview.AwContents.Q(chromium - TrichromeWebViewGoogle6432.aab - stable - 604519333 : 56 ) at com.android.webview.chromium.WebViewChromium.b(chromium - TrichromeWebViewGoogle6432.aab - stable - 604519333 : 21 ) at com.android.webview.chromium.N.handleMessage(chromium - TrichromeWebViewGoogle6432.aab - stable - 604519333 : 44 ) at android.os.Handler.dispatchMessage(Handler.java: 106 ) at android.os.Looper.loopOnce(Looper.java: 205 ) at android.os.Looper.loop(Looper.java: 294 ) at android.app.ActivityThread.main(ActivityThread.java: 8194 ) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java: 552 ) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java: 971 ) こちらは一時的に作成したWebViewを view?.destory() で後処理をした場合に発生します。 destroy()をしないとAndroid StudioのProfilerで確認するとメモリが_blankのリンクをタップするたびに増えていって減らないため、例外が出るのは許容しています。 一部ページが全て表示されず見切れてしまう こちらはリリース後に一部のページが見切れて表示されるという不具合が発生しました。 調査の結果、Jetpack Compose上で.fillMaxSizeを使っていたのですが、そのほかにWebViewの設定でちゃんとmatch_parentを設定する必要がありました。 Box( modifier = Modifier.fillMaxSize(), ) { AndroidView( factory = { val webView = WebView(it) webView.layoutParams = ViewGroup.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT ) // こちらの設定を追加 //... 省略 }, update = { //... 省略 }) } Boxの部分にModifier.fillMaxSize()で画面サイズいっぱいにWebViewを表示しようとしていたのですが、確かにこれでWebviewは画面サイズまで広がります。 しかし、WebView側の設定もしないと、以下のようなcssが設定されている画面では見切れてしまいました <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> < html xmlns= "http://www.w3.org/1999/xhtml" > < head > < style type = "text/css" > body { <!-- この 3 つの記述のあるcssを使っているページだと見切れてしまう --> display : flex ; align-items : center ; height : 100 VH; } </ style > </ head > < body > < div > ここが見切れます < br > ここから見えるようになります </ div > </ body > </ html > MATCH_PARENTを設定してない場合 設定した場合 WebViewで不特定多数のWebページを表示することはあまりないと思うので、特定の画面を表示するならそれらのページは事前にチェックする必要があると痛感した出来事でした。 さいごに 今回はWebViewに関することを書いていきました。 同じような所で困った人の一助になれば嬉しいです。 明日の19日目は ImazekiShota さんの記事です、お楽しみに!
はじめに 本記事は  BASE Advent Calendar 2023  の17日目の記事です。 こんにちはPay IDでフロントエンドエンジニアをしているnojiです。普段はあと払い(Pay ID)に関するフロントエンド周りの開発をメインに行っています。 payid.jp 11月6日にあと払い(Pay ID)は口座振替機能をリリースしました。これにより、今まではあと払い(Pay ID)を利用した翌月に、コンビニに支払いに行く必要がありましたが、金融機関口座を登録することでコンビニに行かずに手数料無料で自動で引き落とされるような支払い方法を選べるようになりました。 ( https://payid.jp/atobarai より) 口座振替登録時には以下のような画面を経て、金融機関のページに遷移し、金融機関と連携をする必要があります。 自分は口座振替機能におけるアプリでの口座申込画面の開発をWebViewで行ったのですが、本記事ではそのWebViewに新規でReact + Viteを導入した話を紹介します。 なぜWebViewを利用したのか なぜ、今回iOSやAndroidのアプリで実装するのではなく、WebViewを利用したのかというと、 「金融機関」というBASEやPay ID管理外の外部のシステムと連携するにあたり、不確実性が高い 金融機関との連携で異常があった場合など、WebViewであれば緊急で修正のリリースが可能 iOS/Androidエンジニアの対応が不要で、フロントエンドエンジニアが対応したものがiOS/Androidの両方で動く などの理由がありました。 WebViewでやるにあたっての技術選定 実は以前からアプリの一部ページでは、WebViewが使われていました。しかし、CakePHP + jQuery or JavaScriptで書かれていました。 今回、WebViewで実装するにあたり、既存の仕組みでやることもできなくはなかったですが、以下の理由で違う方法を試すことにしました 今回の機能要件に入力中のtextからこちらが用意している項目の一致で絞り込みやサジェストを行う機能があったが、jQuery, JavaScriptだとやや実装が大変 保守やレビューなども含めたメンテナンス性(TypeScriptも使われていないので。。) 今後Pay IDアプリの機能拡充で追加のWebViewを作る可能性があるので違う方法を試すチャンス 断念したことにより、フロントエンドライブラリを導入して実装することにしました。今回はReactを導入することにしたのと、サーバーサイドは既存のCakePHPのサーバーを利用するようにしました。 Reactの理由 開発者本人が書き慣れている BASEのフロントエンドでReact化が進行している preact, solid, svelteなど他の軽量めなライブラリ導入も考えたが、学習コストとなにかあった際にほかメンバー対応できない可能性がある TypeScriptで書きやすい サーバーサイド現状維持の理由 実装する数ページのためにサーバーを立てるのは管理コストなども含めてオーバースペックになる ページに認証が必要で、認証をPHP側で行っているが、サーバーをたてるとそこでも認証を考慮する必要が出てくる 開発着手からリリースまでのスケジュール的な部分であまり大規模な導入を行いたくなかった また、Reactのビルドツールについては Vite を利用することにしました。 webpackよりも高速 HMRが標準で使えるので、開発しやすそう GitHubのstar数でwebpackに追いついていきそう(2023年12/13日現在 webpack =63.8k, vite =61.8k) 今回利用する対応量でビルドツールによる大きな差分が出ることはほぼなかったと思いますが、勢いのあるツールだと思ったのでやや勢いで導入しました。 構成的なところ Viteの導入とSPAの作成については割愛します。 Reactで作ったSPAとバックエンドをどのように統合したかについてを書こうと思います。 Viteではビルド時の設定をvite.config.jsに記述します。 vite.config.js const react = require('@vitejs/plugin-react'); const { defineConfig } = require('vite'); module.exports = defineConfig({ ... build: { outDir: 'path_to_manifest', // manifestファイルの出力ディレクトリ manifest: true, rollupOptions: { input: 'src/index.tsx', }, modulePreload: { polyfill: false, }, }, ... }); frontendをビルドすると以下のようなファイル郡が作成されます。manifest.jsonにはビルド済みのindex-〇〇.jsやindex-△△.cssファイルのpathが格納されています。(〇〇、△△にはランダムな文字列が入ってます。以下は一例です) ├── assets ├── index-〇〇.js └── index-△△.css └── manifest.json (outputに関しては、フロントエンドや設定の構成などによって、内容が変わることがあります) PHPの例は以下の通りでサーバー側でmanifestファイルからcssとjsのファイル名を取得し、テンプレートエンジンに渡すパラメータに入れます。 class SampleController { ... public function v1_hogehoge() { // 何かしらの処理...(認証など) $this- > set_assets(); } private function set_assets() { $filePath = 'path_to_manifest'; // manifestファイルへのpath $manifest = json_decode(file_get_contents($filePath), true); $jsUrl = $manifest['src/index.tsx']['file']; $cssUrl = $manifest['src/index.css']['file']; $this- > set([ 'js_url': $js_url, 'css_url': $css_url ]) } } テンプレートエンジンはtwigを利用しています。 ローカル環境でHMRを利用するために、サーバーから返すファイルtwigファイルは以下のようにしています。 {% if env == 'local' %} < script type = "module" > import RefreshRuntime from 'http://localhost:5173/@react-refresh' RefreshRuntime.injectIntoGlobalHook ( window ) window .$RefreshReg$ = () => {} window .$RefreshSig$ = () => ( type ) => type window .__vite_plugin_react_preamble_installed__ = true </ script > < script type = "module" src = "http://localhost:5173/@vite/client" ></ script > < script type = "module" src = "http://localhost:5173/src/index.tsx" ></ script > {% else %} < link rel = "stylesheet" href = {{ css_url }} /> < script src = {{ js_url }} defer ></ script > {% endif %} < div id = "root" ></ div > ローカルのときはローカルの開発環境のサーバーを参照し、デプロイした際にはビルド済みのassetを読み込むようにしています。 クライアント側では、以下のようにreact-routerでルーティングを行い、CSRしています。 src/index.tsx const router = createBrowserRouter([ { path: '/path_to_page', element: <PageComponent />, }, { path: '*', element: <NotFoundPageComponent />, }, ]); const root = ReactDOM.createRoot( document.getElementById('root') as HTMLElement ); root.render( <React.StrictMode> <RouterProvider router={router} /> </React.StrictMode> ); 導入しての所管 ページ数やファイル数が多くないので、Viteのスピード面でとても大きいメリットを享受出来たとまでは言えないですが、ローカル環境ではHMRが効いていて更新がすぐに反映するので、細かい調整などとても開発がしやすく感じました。 設定が多くなく、Vite自体の導入して実際に開発できるようになるまでがスムーズに感じました。 ファイル数が増えてくるなど、今後WebViewでやることが増えてきたときによりメリットを享受できるかもしれないなと思いました。 まとめ 今回はPay IDアプリのWebViewにReact+Viteの導入についてお話しました。 明日はeijiさんの記事です。お楽しみに! 参考 https://ja.vitejs.dev/guide/backend-integration.html https://payid.jp/atobarai
はじめに この記事は BASEアドベントカレンダー2023 の16日目の記事です。 こんにちは! Cart DevチームでBackendエンジニアをしている @endu です。 気づけば入社してからそろそろ1年が経ちます。 この1年をふりかえると、最初のオンボーディング期間を経てはひたすらに、既存コードを読んでは仕様を把握してコードを書く1年だったかなと思います。 今回は既存コードを理解するにあたっての、 ドメイン知識を素早くキャッチアップする事 に焦点をあてて、自分が心がけている事をご紹介できればと思います。 ドメイン知識とはなにか? ここで話す「ドメイン知識」についてですが、調べてみると以下のように定義されています。 ドメイン知識(英: Domain knowledge)または領域知識は、はっきり限定された、ある専門分野に特化した分野の知識であり、一般知識またはドメイン独立の知識と対比される。 ref: ドメイン知識 - Wikipedia この定義を元に「ドメイン知識を習得している状態」がどういう状態なのか?を考えてみます。 自分が所属しているCartDevチームですと、ECにおける基本的な決済の流れだったり、決済後の商品の在庫の裏側の処理について専門的知識がわかっている状態が「ドメイン知識を習得している」と定義できるかもしれません。ドメイン知識を習得していると何が良いのか?というと、機能を要件定義する際の精度だったり、他メンバーとのコミュニケーションの効率化や、実装速度の向上に関わります。 ドメイン知識を素早くキャッチアップしたいと思った背景 ドメイン知識をキャッチアップする事自体は特別の事ではありません。 ソフトウェアエンジニアでしたら誰しもが経験をする事であり必ず行う作業です。 自分がドメイン知識を素早くキャッチアップしたいと思った背景については、所属しているCart Devチームに理由があります。 Cart DevチームはBASEの決済領域に関わる問題に対して責任を持ち、運用を行うチームになっています。 具体的には購入者様が商品を購入する際の決済だったり、オーナー様が利用するカート機能周りの開発、保守運用を行ってます。 CartDevチームの紹介に関しては以前、テックブログで記事を書いたのでよければご覧ください。 チームの取り組みを社内に紹介する「月刊CartDev」を始めた話 - BASEプロダクトチームブログ この「決済を行う」というのは幅広い領域をもっており、あるチームが特定の機能開発で決済領域に変更が加えるとなった場合にはそのチームのサポートに入り一緒に開発を行うスタイルとなっています。 なので、他のチームが開発をした機能を見る機会が多く必然とドメイン知識をキャッチアップ機会が増えました。 しかし、開発にはリリース期限があるのでキャッチアップだけに長く時間をかけすぎても意味がないです。 その為、素早くキャッチアップする上で試した事を紹介します。 処理の流れをドキュメントにまとめる 一番最初のステップとしては、変更を加える機能の概要を調査してドキュメントにまとめる所から作業を行います。自分がよく行う作業としては過去のドキュメントを見るのもそうですが、既存のコードを見て、処理のおおまかな流れを自分がわかる内容で1つのドキュメントにまとめます。 実際の取り組んだ例をあげると、BASEで提供している「 抽選販売App 」の当選処理を改修する機会がありました。 最初はコードを眺める前に、過去のドキュメントを一通り目を通した上で、1つ1つ処理を追っていきます。 この時、リクエストからレスポンスを返すまでの処理を一通り書き出す所まで把握できれば良いです。 ここにどれだけ時間をかけるかについては悩みますが全部を調査するとキリがないので、ある程度区切りがついたら、細かい単位でPRを切って実際にコードに手を加えながらデータの流れをおっていきます。 デバッグツールを使ってデータの流れを追う 前述のドキュメントにまとめる作業だと、データ構造などの細かい仕様まで把握ができないので、デバッグツールを使ってデータの流れを追う作業を行います。 BASEのBackendは主にPHPで開発が行われており、エディターとしては PHPStorm を使ってコーディングしています。PHPStormには標準でデバッグツールとしてXdebugが備わっており、ブレイクポイントを貼りながらデバッグしていきます。 https://xdebug.org/ デバッグ方法としてはどのようなデータを保持するか?に注目し、ブレイクポイントを貼って1つ1つステップオーバーしながら見ています。特にDBに対して操作を行っているのであれば、実行前に用意されるデータと、実行後に書き出されるSQL、そしてどのカラムに変更が加えているかを見て、処理の解像度をあげます。 データベースに関しては Sequel Aceを使って、期待されたデータが保持されているかを見ます。 https://sequel-ace.com/ Sequel AceはGUIでデータ構造を変更できるので、特定のデータが入った場合の処理をデバッグしたい時によく使います。 以上のように、データの流れを追う際にはXdebugやSequel Aceなどのデバッグツールと、早めに仲良くなっておくと良いでしょう。 積極的にペアプロ、モブプロを活用する ここまでの「処理の流れをドキュメントにまとめる」と、「デバッグツールを使ってデータの流れを追う」まで出来た段階ですぐに実装に入れるのですが、自分はそこで実装に入る前にペアプログラミング、モブプログラミングを通じて作業内容を見てもらいます。 なぜ、そんな事を行うのか?というと複雑かつ規模が大きい機能だと細かい仕様まで気づけなく、手戻り発生する為、早い段階で有識者の方と作業するようにしています。 またペアプログラミング、モブプログラミングを活用する事で、どのような背景で実装されてきたかだったり、設計思想についてもメンバー同士の理解が深めれるので、より素早くドメイン知識がキャッチアップしやすくなります。 まとめ 「ドメイン知識を素早くキャッチアップする時に心がけている事」に関して、3つ方法を紹介しました。 ここに書かれている内容自体は当たり前の事を書いているだけなのですがこの1年で新しいPJにアサインされて、既存コードを調べる機会が多くあったので特に意識してやっている事をまとめてみました! これらの方法が参考になれば嬉しいです! 明日は @noji さんの記事です! お楽しみに!
本記事は  BASE アドベントカレンダー 2023  の15日目の記事です。 はじめに こんにちは。BASEのデータ分析チーム(Data Strategy Team)で不正対策を行ったり、機械学習を触ったりしている竹内です。 ChatGPT(GPT-3.5 Turbo)が2022年の11月に公開されてから、だいたい1年以上が経ったことになります。 そしてこの1年近くでChatGPTに匹敵する多数のLLMの公開や国産LLM作成の動き、拡散モデルを主軸とした画像生成AIの台頭など様々なムーブメントがあり、それを経て「生成AI」という単語は2023年の流行語大賞に選ばれるほど人口に膾炙する結果となりました。 生成AI、特にChatGPTをはじめとする対話用にチューニングされた大規模言語モデル(以下チャットLLMと表記します。)の実応用という面に関していうと、人の代わりに文章を作成させたり、知りたい情報を提示させたり、アイデアを提案させたりといった人間のアシスタントのように振る舞える性質を活かした使われ方が多い印象で、BASEにおいてもチャットLLMのそういった側面を活用したサービスの開発、リリースが進んでいます。 一方で少し見方を変えると、チャットLLMは任意のテキストを入力として受け付け、指定した手順に従ってタスクを実行し、出力のフォーマットもある程度コントロールできる、世界知識をもった自然言語処理ツールであるという側面も存在します。(精度はタスクの難易度やモデルの性能に依存します。) ChatGPTによるクラス分類 チャットLLMのこうした側面に着目した実応用例やツール化に関しては、もしかするともうすでにいろいろな場所で検証され、実際に運用されているのかもしれませんが、様々な事情から前者の「アシスタントAI」的な例と比較してあまり表立って取り上げられていないような印象があります。 そういった背景もあり、直近で商品テキストのカテゴリ分類という典型的なタスクを現実的なコストで実行するために、チャットLLMを利用したちょっとしたツールを作る機会があったため、それについてまとめていきたいと思います。 チャットLLMによるテキスト分類 BASEでは検索、レコメンドシステムの改善や不正決済対策、不適切なコンテンツへの対策などに活用するため一部機械学習ベースのテキスト分類モデルの作成を行っています。具体的には以前も紹介した 商品カテゴリの分類モデル などが挙げられます。 このようなテキスト分類モデルを作成するには、従来であれば人の手によってラベル付け(アノテーション)された十分なサイズの学習/テストデータセットを用意する必要がありました。しかしながら十分なサイズとラベルの品質が担保されたデータセットを作成するのは容易ではなく、クラウドソーシングなどを利用した場合でも相当な時間的、金銭的コストがかかることになります。商品カテゴリの分類という、内容自体は比較的取り組みやすいタスクについてもデータセットの作成がネックとなっていました。 一方でChatGPTのようなチャットLLMは、適切な指示文(プロンプト)を与えるだけで様々なタスクを解くことができるという非常に高い汎用性、柔軟性をもっているため、テキスト分類においてもファインチューニング無しで十分な力を発揮することができ、ドメインによっては人間と比較しても遜色ない性能を発揮することも可能です。[1] しかしながらチャットLLMをそのままテキスト分類モデルとして利用し続ける場合、ファインチューニングしたモデルで推論を行う手法と比較して 推論速度 コスト APIの利用制限(ChatGPTやBARDのようなAPIを利用する場合) 出力のフォーマットの安定性 などの面で課題が発生します。 特に推論速度の面で言えば簡単なテキスト分類においても1件あたり数秒〜十数秒は要するケースが多く、モデルの最適化もバッチ処理も基本的には不可能であるために、データの量によってはそもそも推論が追いつかないことがあります。 こうしたスケーラビリティ上の課題を解決する方法としては、チャットLLMを用いてテキスト分類を行った結果を教師信号とし、それを用いてBERTなどのより軽量な事前学習モデルをファインチューニングするという方法が考えられます。 要するに人間の代わりにチャットLLMにアノテーションさせるということになりますが、複雑で巨大なモデルの有する知識を、よりデプロイメントに適したモデルへと移転させる技術である知識蒸留(knowledge distillation)[2]を行っているという見方もできるかもしれません。 (ただしChatGPTの場合、出力をOpenAIとの競合となるようなモデルの学習目的で利用することが規約で禁止されている[3]ため注意が必要になります。) 分類タスクを実行するためのプロンプトのテンプレート化 データセットへのアノテーションを目的としてテキスト分類を実行するという工程については、ある程度ツール化した方が便利であると考えました。 テキスト分類を実行させるための指示や結果のフォーマットの指定などに必要なプロンプトをいちいち書かずに、scikit-learnやPyTorchなどと同じような振る舞いをするPythonのライブラリとして使用できればドメインごとの取り回しが良いです。 例えばニュースのトピック分類やツイートのネガポジ分類など異なるドメインのテキスト分類タスクを解くためにチャットLLMを利用する際、いちいち「クラス分類してください」的なプロンプトや「この例のようにjson形式で出力してください」的なフォーマット部分や、APIを叩く部分を書くのはなかなかに煩わしいものとなります。タスクを解く上で必要最低限となる情報や要件のみを直接引数などで設定できることが望ましいです。 クラス分類タスクを実行させるためのプロンプトに関して言えば、ドメインに関わらず共通している部分と、ドメインごとに指定する必要のある部分に分けることができ、前者をある程度テンプレート化した上で後者をそのテンプレートに当てはめることが可能です。 ドメインに関わらず共通化できる部分としては テキスト分類を実行させるための指示部分 理由や確度、マルチラベルとするかなどの有無の指定 各種設定に応じた出力のフォーマットとその例示部分 などが挙げられ、 テキスト分類においてドメインごとに指定する必要のある部分としては どのような文脈のテキストを分類するか。(例: Webサービスの利用者のコメント) どのようなラベルに分類するか。(例: 利用時の印象) それぞれのラベルはどんなものか。(例: 「ネガティブorポジティブ」) などが挙げられます。 以上を踏まえて、結果的に以下のように動作するチャットLLMによるテキスト分類ツールを作成しました。 from llmtask.tasks import ClassificationTask # クラス分類されるテキストの説明 input_description = "EC review comments" # クラスの説明 label_description = "Impressions from the comments" # ラベル名: ラベルの説明や具体例 labels = { "Positive" : "Receive a good impression(endorsement, praise, support, recommendation)" , "Negative" : "Receive a bad impression(disagree, criticize, attack, slander)" , "Neutral" : "Neither positive nor negative" } task = ClassificationTask( input_description=input_description, label_description=label_description, labels=labels, require_reason= True , llm= "openai" , language= "en" , ) output = task( input_text= "The order was delivered right away!" , model= "gpt-3.5-turbo-1106" , ) labels = output.label label_index=output.label_index print (label_index, labels) # >> 0 Positive reason = output.reason print (reason) # >> The comment 'The order was delivered right away!' expresses satisfaction and a positive impression. 内部ではテンプレートに従ってだいたい以下のようなイメージのプロンプトが作成され、パースされた出力結果が戻り値として返るようになっています。 """ Perform text labeling according to the following instructions. Let text X be EC review comments. Let label Y be Impressions from the comments. Select one of the labels Y that corresponds to text X. Briefly describe the reason for the labeling. The output format is json format as follows: {"reason": "Reason for labeling (string type)", "label": "Label to be assigned (string type) "} for example {"reason": "Because it is xx.", "label": "label"}. label Y=['Positive', 'Negative', 'Neutral'] Details of each label: {'Positive': 'Receive a good impression(endorsement, praise, support, recommendation)', 'Negative': 'Receive a bad impression(disagree, criticize, attack, slander)', 'Neutral': 'Neither positive nor negative'} text X='The order was delivered right away!' """ 別の言語の分類においても同様な形式でタスクを実行することができ、exampleとしてのデータの追加やマルチラベル出力の指定なども可能となっています。 input_description = "ECサイトのレビューコメント" label_description = "コメントから読み取れる印象" labels = { "ポジティブ" : "良い印象(賞賛、支持、推奨など)" , "ネガティブ" : "悪い印象(批判、攻撃、中傷など)" , "ニュートラル" : "どちらでもない中立的な印象" , } task = ClassificationTask( input_description=input_description, label_description=label_description, labels=labels, multi_label= False , require_reason= True , require_confidence= True , llm= "openai" , language= "ja" , ) task.set_examples( example_inputs=[ "とても良い品質でした。" , "包装が破れていました。" , "六本木に本店があります。" ], example_labels=[ "ポジティブ" , "ネガティブ" , "ニュートラル" ]) output = task( input_text= "注文された商品がすぐに届きました!" , model= "gpt-3.5-turbo-1106" , ) labels = output.label label_index=output.label_index print (label_index, labels) # >> 0 ポジティブ reason = output.reason print (reason) # >> 「すぐに届きました!」という内容から、賞賛の意味でポジティブな印象に該当します。 プロンプトのテンプレートは以下のような形式で言語ごとに実装しています。(あまり洗練されていません。) class _ClassificationPrompt : role: str task_description: str input_definition_holder: str ... def __init__ (self, input_description: str , label_description: str , labels: dict [ str , str ], multi_label: bool = False , require_reason: bool = False , require_confidence: bool = False ) -> None : self.instruction = self._build_instruction() ... def _build_instruction (self) -> str : instruction = "" instruction += self.task_description instruction += self.input_definition_holder.format(input_description=self.input_description) instruction += self.output_definition_holder.format(label_description=self.label_description) ... return instruction class _EnglishClassificationPrompt (_ClassificationPrompt): role = "Machine learning model for labeling text" task_description = f "Perform text labeling according to the following instructions." input_definition_holder = "Let text X be {input_description}." ... class _JapaneseClassificationPrompt (_ClassificationPrompt): role = "テキストのラベル付けを行う機械学習モデル" task_description = "次の指示に従いテキストのラベル付けを実行せよ。" input_definition_holder = "テキストXを{input_description}とする。" ... 実際に上記のツールを使用してテキストデータのラベル付けを行い、非常に低コストで十分な量および質(人手でのアノテーション結果と比較して遜色ない程度)のデータセットを作成することが可能となりました。 例えば商品カテゴリの分類に関しては最終的に百を超えるラベル数を設定する形になりましたが、ファインチューニングを行ったモデルのメトリクスに関しても十分な数値を出すことができています。 課題や展望など 出力のフォーマット 柔軟性の高さと引き換えに、出力が100%期待した形式になるとは限らないという点はチャットLLMのよく知られた欠点の1つであり、今回のようなテキスト分類においても例外ではありません。 上記のツールではラベル名を直接回答する形でプロンプトを作成していますが、与えられた選択肢に適切なものがなかった場合などは、選択肢にないラベル名を回答することも多々ありました。 ラベル名を回答させるのではなく、{1: ”ラベル1”, 2: “ラベル2”, 3: “ラベル3”}といった形式の選択肢に対して該当するラベルの番号を返すようにプロンプトを実装することも可能で、当初はこの形式を採用していました。この場合出力のフォーマットは安定するものの、精度が下がる(reasonを見る限り正しく理解はできているが、インデックスを選ぶところで間違えている)ケースが見られたため、ラベル名を直接答える方式を採用しました。 一方で選択肢にないものが多数回答されている場合には、ラベルセットを見直す参考にもなるためその点では役に立つ側面もありました。 プロンプトの長さ 現状の仕組みでは1データの推論を行う度にそのデータのテキストだけでなく、共通の指示やラベルの説明といったタスクの説明を行うシステムプロンプトも逐一投げる必要があります。 そのため特にラベル数が百を超えるような日本語のタスクでは全体のトークンサイズが非常に大きくなってしまうため、ラベルの説明部分に細かい具体例を記述せず、簡素なものにせざるを得ないということもありました。 一度ベースとなる指示を与えた後で複数データのテキストをまとめて入力することも可能ではありますが、トークン数が長くなることによって精度が下がる可能性やそもそもトークン数のサイズ制限に引っかかる可能性があるという点を留意すると、一筋縄ではいかないかもしれません。 あるいはChatGPTのファインチューニング機能をうまく活用できればこの点を解決できるかもしれず、その辺りは要検証といったところです。 閾値の設定 テキスト分類モデルと同じような振る舞いをしていますが、出力が確率ではなくラベルそのものであるため、閾値の調節ができない点は欠点として挙げられます。 一応プロンプトの中に確度(confidence)として0〜100の数値を出力させるようなオプションにも一応対応させてはみましたが、あまり信頼できない印象がありました。(全体で一貫した基準が保たれていない印象でした。) おわりに 冒頭でも触れた通りチャットLLMに関しては、情報検索の代替的な使い方や、要約、翻訳、相談役、アイデア創出、語学学習といった「話し相手」「アシスタント」的なものが代表的なユースケースとして取り上げられることが多いですが、見方を変えれば、定義した問題を解釈し、自動的に解きながら指定したフォーマットで出力してくれるツール的な使い方も可能です。 人間の話し相手になってくれたり、人間が解けない難しい問題を解いてくれたりするAIという側面の他に、従来人間が行なっていた「単純で答えもある程度決まっているが世界知識がないと解けない」自然言語の絡むタスクを自動的に処理してくれるAIという側面に着目すると、業務の大幅な効率化やコスト削減が図れるかもしれません。 また、実際プロンプト次第ではテキスト分類のアノテーションだけでなくテキストのクラスタリング[4]やデータの拡張[5]などについても応用例が考えられており、従来の機械学習技術とうまく組み合わせることでも新しい価値や、インパクトのある使い道が発見できるかもしれません。 最近ではチャットLLMは画像とテキストの入出力対応によるマルチモーダル化という次のステップに進みつつあり、LLMを利用する人間側も頭を柔らかくして、いろいろな利用可能性を模索、検証していくことが求められます。 最後となりますが、弊社では機械学習エンジニアを募集しております! ご興味のある方はお気軽にご応募ください! https://open.talentio.com/r/1/c/binc/homes/4380 明日は@endu さんの記事です。お楽しみに! References [1] Fabrizio Gilardi, Meysam Alizadeh, Maël Kubli, ”ChatGPT Outperforms Crowd-Workers for Text-Annotation Tasks”, Mar 2023. [2] G. Hinton, O. Vinyals, and J. Dean, “Distilling the knowledge in a neural network”, Mar. 2015. [3] https://openai.com/policies/terms-of-use [4] Yuwei Zhang, Zihan Wang, Jingbo Shang, ”ClusterLLM: Large Language Models as a Guide for Text Clustering”, May 2023 [5] Zhihong Shao, Yeyun Gong, Yelong Shen, Minlie Huang, Nan Duan, Weizhu Chen, ”Synthetic Prompting: Generating Chain-of-Thought Demonstrations for Large Language Models”, Feb 2023
はじめに 本記事は BASE アドベントカレンダー 2023 の14日目の記事です。 こんにちは!NEW Dept/Pay ID Dev/Web Backendエンジニアをしている@zanです。 主にPay IDの機能開発を担当しています。 SMS OTPで用いられるメッセージの形式を題材に、 どのような経緯で形式が決まったかを調べてみました。(ちょっとした考古学?みたいなものです。) SMS OTPとは SMS OTPと関連技術について振り返ってみましょう。 ...と思いましたが、 過去 @gatchan0807 が書いた 今度は「WebOTP」についてFrontend Weekly LT(社内勉強会)でお話しました に詳しく書かれているので、気になる方はご一読いただけると幸いです。 (※2021年の記事なので一部情報が古くなっている可能性があります。) 簡単に言ってしまうと SMS OTP = 指定の電話番号にSMSを使ってOTPを送信する 関連技術 = WebOTP API, autocomplete="one-time-code" ...etc です…! SMSの形式 さて、本題のSMS OTPで用いられるメッセージの形式です。 Origin-bound one-time codes delivered via SMS#authoring によると In the following origin-bound one-time code message, the top-level host is "example.com", the code is "747723", no embedded host is specified, and the explanatory text is "747723 is your ExampleCo authentication code.\\n\\n". "747723 is your ExampleCo authentication code. @example.com #747723" と定義されていますね。 実際にSMSに @example.com #747723 形式で送信されてきたメッセージを見たことがある方も多いと思います。 また、 Enabling AutoFill for domain-bound SMS codes でも同様に Your Example code is 123456. @example.com #123456 と定義されていますね。 ここらへんは形式が統一されているようです。 クロスオリジンのiframeはどうなる? では、クロスオリジンのiframeのなどの場合のメッセージの形式はどうなるのでしょうか? あまりユースケースがないのかもしれませんが、 WebOTP API やSafariでは、クロスオリジンのiframeでのSMS OTPの入力をサポートしています。 前述のメッセージの形式と異なるので、それぞれ見ていきましょう。 Origin-bound one-time codes delivered via SMS#authoring では、 In the following origin-bound one-time code message, the top-level host is "example.com", the code is "747723", the embedded host is "ecommerce.example", and the explanatory text is "747723 is your ExampleCo authentication code.\\n\\n". "747723 is your ExampleCo authentication code. @example.com #747723 @ecommerce.example" と定義されています。 一方、 Enabling AutoFill for domain-bound SMS codes では、 Your Example code is 123456. @example.com #123456 %iframe-auth.example.org と定義されています。 あれ、と思ったかたもいるかもしれません...! よくよく見てみると、微妙に形式が違います。 @example.com #747723 @ecommerce.example @example.com #123456 %iframe-auth.example.org 気づきましたね...! embedded hostが @ か % かの違いです。(typoではありません...!) なぜこの形式になったのか時系列で整理してみた。 なぜこのような違いがあるか疑問に思ったので、時系列で整理してみました。 Apr 30, 2020 Third-party authentication / nested iframes #4 を見てみると 元々はクロスオリジンのiframeをサポートする思想ではなかったようです。 (むしろ、意図的に無視していたようです。) August 4, 2020 Enhance SMS-delivered code security with domain-bound codes の提供開始。 このときはクロスオリジンのiframeに関する記述はありませんね。 123456 is your Example code. @example.com #123456 との記載があるように @example.com #123456 形式のみのようです。 Oct 16, 2020 https://github.com/WICG/sms-one-time-codes/issues/4#issuecomment-709557866 この時点で既にクロスオリジンのiframeをサポートするためにembedded hostに % 形式を用いていたことがわかります。 議論が再燃して、embedded hostに % 形式を採用するかなど、議論がなされていました。 他にも @top #code %iframe or @top #code @iframe or @top @iframe #code などなど...。 Mar 24, 2021 https://github.com/WICG/sms-one-time-codes/pull/5 最終的に @example.com #747723 @ecommerce.example 形式でマージされたようです。 Dec 13, 2023(※執筆時点) Enabling AutoFill for domain-bound SMS codes によると @example.com #123456 %iframe-auth.example.org 形式がいまだに使われています。 ほうほう...。 紆余曲折経て、最終的に @example.com #747723 @ecommerce.example に着地したことがわかり面白いですね。 そして先駆者はembedded hostに % 形式を採用しつづけていると...。 つまり、紆余曲折あって @example.com #747723 @ecommerce.example @example.com #123456 %iframe-auth.example.org に分かれたわけですね。 さて実動作はどうなのか。 気になるところ実動作はどうなのか、というところです。 実動作を見てみるか、と思って調べてみるとブラウザ/OSの違いでそもそもお困りの方が多いようです。 ですので、調査結果はまた別の記事にしたいと思います。(iOS + Chromeェ...。) (また、いろいろ整理できたので、個人的にも議論に参戦してみたいな、とも思いました。) Domain bound SMS Otp autofill not working in WKWebView (but does in Safari) Format is inconsistent with iOS docs #18 まとめ SMS OTPで用いられるメッセージの形式の考古学をしてみました。 こう見てみると、どのような目的があって、今の形式になったのかがわかって面白いですね。 フォーマットや形式はbikeshedな議論になりやすい文脈かと思いますが、最終的な着地点が気持ちいいですね。 何気なく使っている技術も深掘りしてみると面白いものですね。 明日は @TakeuchiSho さんの記事です!お楽しみに。 参照 今度は「WebOTP」についてFrontend Weekly LT(社内勉強会)でお話しました Origin-bound one-time codes delivered via SMS#authoring Enabling AutoFill for domain-bound SMS codes Domain bound SMS Otp autofill not working in WKWebView (but does in Safari) Format is inconsistent with iOS docs #18