TECH PLAY

BASE株式会社

BASE株式会社 の技術ブログ

587

はじめに フロントエンドエンジニアの @mk0812 です。自分は普段BackOfficeというチームで新規機能開発を担当しています。 この記事ではBASEのフロントエンド周りの事例として「 Monorepo 」を紹介します。 エンジニアの皆さんなら1度は聞いたことあるかもしれませんが、BASEではここ最近Monorepoにしていきました。 具体的にどこをMonorepoにしてるかというとBASEの管理画面にある基本機能とBASE Appsです(下図)。 前者はBASE管理画面で使用する機能を指しており、後者はショップにより充実した設定を追加したり、新しい機能が必要となる時に追加できる機能で、数多く提供されていて、その機能単位もしくはApp単位でMonorepo化されています。 基本機能とBASE Apps Monorepo とは 「Monorepo」とは単一のリポジトリである特定のプロジェクトコードを全て管理する方法を指します。下図はMonorepo構成であり、リポジトリに対して BASE App1 , BASE App2 ...とApp単位、機能単位でディレクトリがあり、その単位でビルド等ができるようになっています。 Monorepo とは Monorepoのメリットは下記になります。 依存管理がわかりやすくなる Monorepo単位で依存パッケージ(dependencies)が選択できる 関心ごとを分けられる Monorepoにした背景 元々フロントエンドの構成としてはマルチエントリーポイントであり全てのコードは1つに合わせて扱われます。個別のビルドはできず毎回全てをビルドする必要がありました。当たり前ではありますがコードが増えていくとビルド時間も増えていきます。 上記で説明したマルチエントリーポイントの構成は下図のようになります。BASE Appごとのディレクトリ(appディレクトリ)にそれぞれのAppや基本機能があります(図でいうApp1、App2、基本機能1)。そのディレクトリに必要な components , domain , store などのそれぞれの役割に応じたディレクトリにわけてエントリーポイントを配置しています。結果的にそれらをまとめてビルドしているので当然ビルド時間はファイル数の増加に伴い増えていきます。 Monorepo 前のBASE フロントエンドの構成 基本的に右に向かって依存していきます。図は一部の構成にすぎないもののどうしても良くない依存関係が発生したりします(例: domainなどのコードはその特定のAppのみで参照するべきで上図の構造だとどのAppでも参照可能である)。 この上図の状態に対して、整理を行い下図のようなMonorepo構成にしました。BASEで使用する基本機能およびappは frontend/shopadmin 配下に配置し、その機能およびapp単位で特有の components , domain , store を配置しました。また、app共通で使う components や api-client は共通ライブラリとして frontend/package に置きました。こうすることでMonorepo前の依存関係を整理し、マルチエントリーポイントを剥がして単体ビルドが可能となります。これらのことを踏まえてフロントエンドの構成をMonorepoへと移していきました。 Monorepo 後のBASE フロントエンドの構成 Monorepoツールの選定 次はMonorepoをする上でのツールのお話です。 様々なMonorepoツール がある中でBASEでは turborepo を採用しました。 turborepoを採用した理由は下記になります。 ローカルキャッシュ、リモートキャッシュの生成・利用 turborepoでは node_modules/.cache のキャッシュを見て、次のビルド時の復元をすることでキャッシュが利用され、不要なビルドがスキップされる(下図) ビルドパイプラインの実行 依存しているタスクを並列実行し、処理の最適化が可能 パッケージ間の依存関係を考慮した再ビルド パッケージごとに設定されたビルドコマンドを呼び出すことで個別のパッケージでビルドが可能 ⁠キャッシュなし ⁠キャッシュあり Monorepo化してみた 実際にMonorepo化の手順を以下に示します。 scaffdogでプロジェクトの雛形を作成 monorepo用のディレクトリを作成するのですが、 scaffdog を使用してプロジェクトの雛形を作成します。ここでは仮のAppとして「app-sample-a」を作ります。 ? Please select the output destination directory. shopadmin/ ? アプリの名前を設定してください。App管理画面では app- プレフィックスを使用してください。 app-sample-a 🐶 Generated files! ✔ shopadmin/app-sample-a/package.json ✔ shopadmin/app-sample-a/tsconfig.json ✔ shopadmin/app-sample-a/webpack.config.js ✔ shopadmin/app-sample-a/src/index.tsx ✔ shopadmin/app-sample-a/src/components/xxx.vue ✔ shopadmin/app-sample-a/src/store/index.ts これでMonorepoの雛形が完成しました。 引越し作業 「Monorepoにした背景」で記載した部分の対応です。下図はイメージ図でありBeforeにある components , domain , api-client を目的に沿ってmonorepo単体のパッケージ( app-sample-a )もしくは共通パッケージ( frontend/package )に配置して、Afterにあるような形にもっていきます。配置変換に応じてimport先の修正等もやります(後述するeslintでもimport先が間違っている場合はエラーとして検出されます)。 Monorepo 移行のイメージ図 足りない依存パッケージがないか検出する 上記の引越し作業を行うとビルドは可能となります。しかし、turborepoに依存関係を認識させるために使用しているパッケージを package.json に記述する必要があります。依存パッケージがないと判定されると本来依存してるパッケージより先にビルドが走ってしまい、ビルドが失敗します。 turborepoにあるeslintコマンド( turbo run eslint )を行うと、足りないパッケージを検出できます。その検出されたパッケージを追加することでエラー周りを解消していきます。(下記はエラーが出た時のイメージ) # app-sample-a配下でeslint $ turbo run eslint エラーが検出されると下記のように足りないパッケージを指摘します。 6:1 error 'date-fns' should be listed in the project's dependencies. Run 'npm i -S date-fns' to add it import/no-extraneous-dependencies 7:1 error 'lodash' should be listed in the project's dependencies. Run 'npm i -S lodash' to add it import/no-extraneous-dependencies 上記のエラーが解消されて、動作確認完了したらMonorepo移行は完了です。 結果はどうなったか 先日あった Vue Fes Japan Online 2022 にて弊社のスポンサーセッションとして「 BASEのフロントエンド組織の人数が2.5倍になって起きた変化 」という発表がありました。こちらを引用して結果的にどうなったかというと turborepoを導入したことによってローカル環境・デプロイサーバー共に 高速化 フルビルドの時間はそれなりにかかるが、フルキャッシュ、一部だけshopadminの変更があった時のビルド時にかかる時間を削減した。 フルビルド フルキャッシュ時 ⁠一部だけshopadminの変更があった時 567sec 12sec 85sec まとめ 以上、Monorepoの紹介をしました。Monorepoツールは様々あってBASEのフロントエンドはturborepoを採用しましたが、他ツールも検証しながらプロジェクトにあったMonorepoツールを採用してみてください。 またMonorepo化は対応しましたが、まだまだやることはたくさんあります。Monorepo化が完了したこの先としてはパッケージ単体での技術選定などが可能になったのでパッケージ毎で検証しやすくなったと思います。例えば特定のライブラリのアップデート(例: Vueのアップデート)もしくはそのライブラリの代役を探して検証したり(例: Vueのアップデートによって動かないライブラリがあるので代役ライブラリを探す)と、エンジニア目線で色々試行がしやすくなったのではないかと思います。 引き続きBASEではこれからのBASEを一緒に盛り上げて行ってくれる方を随時募集中です!カジュアル面談も実施しておりますので、お気軽にお問い合わせください。 BASE株式会社 の全ての求人一覧 以上、最後までお読みいただきありがとうございました。 ⁠参考 https://monorepo.tools/ https://turborepo.org/
この度、 2022/10/16(日)に開催された Vue Fes Japan Online 2022 に BASE から2名のエンジニアの登壇およびゴールドスポンサーとして協賛しました! 本記事では2名の登壇者のコメントと、参加したメンバーの感想・コメントをお届けします! Vue Fes Japan Online 2022 とは vuefes.jp Vue Fes Japan Online 2022 は Vue.js 日本ユーザーグループが主催する日本最大級の Vue.js カンファレンスです。 今回 BASE ではゴールドスポンサーとして当カンファレンスに協賛しました。 右下にBASEのサービスロゴ 今回の登壇者 Vue Fes Japan Online 2022 にはプロポーザル採択から 1 件、スポンサーセッションで 1 件登壇させていただきました。 発表内容の詳細と発表資料のリンクは以下をご参照ください。 共通コンポーネントのテスト実装方法にあえてVRTを選択した話 vuefes.jp Vue Fes Japan 2022 での発表スライドです。フロントエンドでの各種テスト実装方法を比較しているので、フロントでどんなテストをしようか悩んでいる方におすすめです! 「共通コンポーネントのテスト実装方法にあえてVRTを選択した話」 #vuefes https://t.co/UQcDNfrFdF — プログラミングをするパンダ (@Panda_Program) 2022年10月16日 BASEのフロントエンド組織の人数が2.5倍になって起きた変化 vuefes.jp ご視聴ありがとうございました〜! たくさんいただいた質問とか感想とか観測したので、質問・疑問は追って回答していきます👀👀 ちょっと早口で喋ってしまってたので、聞き逃した部分とかは↓の資料を見ていただけると嬉しいです! #vuefes #vuefes_cloudsign https://t.co/6pvoUFyPs3 — がっちゃん@えんじにゃ⛺ (@gatchan0807) 2022年10月16日 スタッフ参加 また、登壇・協賛だけでなく Vue Fes Japan Online 2022 運営スタッフとして @rry が参加しました。 上から4段目、左から3つ目にある @rry のアイコン 参加者のコメント プログラミングをするパンダ( @Panda_Program ) Vue Fes の存在はずっと知っていたのですが、BASE に入社してから Vue を触り始めたためいい機会だと思ってプロポーザルを提出しました。 「共通コンポーネントのテスト実装方法にあえてVRTを選択した話」ということで、フロントエンドの各種テスト方法を比較した後、VRTを採用したという話をしました。当日は「テストの導入を検討していたので、知りたかった情報がまとまっている」など嬉しいコメントを頂き、登壇してよかったなと思いました。 今年の Vue Fes はオンライン開催ながらお祭りのような雰囲気があり、Vue のクリエイターである Evan 氏に質問できたり、Vue のエコシステムで活躍されている方が取り組んでいるプロジェクトが紹介されたり(Peephole)、海外の方の登壇など、他のカンファレンスでは見ないようなプログラムがあって充実しているなと思いました。 登壇者の方の発表内容自体もレベルが高くとても勉強になりました。またイベント終了後、会社の同僚と Slack の Huddle をつないで Peephole で紹介された reTypewriter の最初の10コミットをコードリーディングしたり、Vue Fes に刺激を受けた別の同僚が BASE の共通コンポーネントライブラリ(BBQ)のバージョンを 2.7 に上げるプルリクエストを作成したり、会社としてもいい影響をたくさん受けました! 登壇者の皆様、運営の皆様、イベントを盛り上げてくださいありがとうございます。お疲れ様でした! @gatchan0807 初めてVue Fesに参加することができましたが、非常に楽しかった & 勉強になることがたくさんあって、参加してめちゃ良かったです! (個人的に、2019年度に当日スタッフとして参加予定だったものが残念ながら台風の影響で中止になってしまった思い出があります 😢) 今回、私は BASE のスポンサーセッションを担当して 「フロントエンド組織の拡大について」と「Vue.js アセットの運用・改善(Vue 2 から Vue 3 へのマイグレーションも含む)のアプローチ方法」について 話しをさせていただきました。 他の方々の発表を見ている中で、今回の Vue Fes の1つの大きなトピックとして「Vue 2 から Vue 3 へのマイグレーション」に関する話題が多く、 少数名でパワーでやりきるアプローチやチームで施策と並行して進めていくアプローチなどなど、様々なアプローチ方法を見てとれて BASE でのアプローチと比較しながらとても興味深い内容 だなと思って聴講していました。 また、Peepholeのセッションで紹介された reTypewriter は、プロダクトとしてとてもおもしろいと思いましたし、 Vue Fes 終了後に一緒に参加していたメンバーと中のコードを読みながらワイワイ学ぶことができるきっかけになって、とても楽しい時間を過ごすことができました! また来年も開催されればぜひ参加したいです! 最後に、運営の皆さま、登壇された皆さま、このような機会を提供してくださって本当にありがとうございました! @rry 今回コアスタッフとしてコンテンツの企画、託児サポートおよびハンズオンサポートなどいろいろなことをさせていただきました。 まずは無事に Vue Fes が終わったことについてほっとしています。 また来月11/2に Reject Conference も開催するのでぜひ参加してみてください! vuejs-meetup.connpass.com 謝辞 協賛・社員のスピーカー参加を通して Vue.js コミュニティの盛り上がりに貢献でき、弊社としても大変有意義な時間となりました。 スタッフの方々には業務でお忙しいにも関わらず、多くの時間をカンファレンス準備へ注いでいただいたかと思います。この場を借りて御礼申し上げます。 最後に 今回、BASE から 2 名登壇する機会をいただき、運営スタッフの皆様には改めてお礼を申し上げます。本当にありがとうございました。 来年の Vue Fes で、また皆様にお会いできることを弊社メンバー一同楽しみにしております。
2022/10/16(日)に開催される Vue Fes Japan Online 2022 で BASE に所属する2名のエンジニアが登壇します。 vuefes.jp BASE では 2018年頃から Vue.js を使ったプロダクト開発を行ってきており、これまでいくつか Vue.js に関連するテックブログ記事やイベントへの参加を行ってきました。 そして今回は Vue Fes Japan Online 2022 のスポンサーとなり、またひとつ Vue.js コミュニティへの貢献ができて大変嬉しく思います。 BASE はゴールドスポンサーとして当カンファレンスに協賛しています。 セッションの内容について 共通コンポーネントのテスト実装方法にあえてVRTを選択した話 2022/10/16 17:15〜 メドピアトラック セッション(20分) 所属:BASE株式会社 Product Dev Division シニアエンジニア vuefes.jp Vue を取り巻くエコシステム(ツール)とその運用の話です。 ■ 概要 社内のコンポーネントライブラリに対してStorybookとChromaticでビジュアルリグレッションテストを導入して、見た目のデグレを防止している話をします。 ■ 詳細 Chromaticとは、Storybookのメンテナーが作成しているStorybook用のツールです。 ストーリーごとのスクリーンショットを撮影し、差分を画像で比較してくれる機能を備えています。 以下の課題を解決することを目的にして Chromatic を導入しました。 既存のコンポーネントを改修した際に発生する DOM、CSS に起因する表示崩れを自動で検知できないこと 依存モジュールのバージョンアップに時間がかかること その結果、両方の課題を解決できた上に作業が楽になったという話をします。 また、なぜ他のテストではなくてビジュアルリグレッションテストを導入したのか、その意思決定の過程についても紹介します。 なお、本発表は以下の3記事の内容を合わせて再構成したものになる予定です。 「Storybook と Chromatic でビジュアルリグレッションテストを実施する」 「ビジュアルリグレッションテストのツールを導入するまでの意思決定プロセス」 「TypeScript Compiler API で40の Storybook コンポーネントを storiesOf から CSF(Component Story Format)に置換した」 Software Design 2022年3月号のTDD特集への寄稿なども行われた、 プログラミングをするパンダ さんによる発表です。 社内のコンポーネントライブラリに対してのテストがどのようなものが必要なのかを問い直してから、意思決定をした過程について発表予定とのことで、今から発表が楽しみですね! 社内コンポーネントライブラリに対してのテストに限らず、普段のコーディングの中で私はどこまでテストをすればいいんだろう?と悩まれている方にぜひご覧いただきたいです! BASEのフロントエンド組織の人数が2.5倍になって起きた変化 2022/10/16 12:45〜 クラウドサイントラック スポンサーセッション(10分) 所属:BASE株式会社 Product Dev Division エンジニア vuefes.jp 2020年9月に公開されたテックブログ 「BASEにおけるVue.jsのこれまでとこれから」 から2年経ち、当時から比べるとフロントエンド組織の人数が2.5倍に増加しました。 本セッションでは、記事公開から2年の間の組織の変遷と合わせて、人数増加に伴って拡大したBASEのVue.jsのコードベースのmonorepo化と、既存のVue資産の運用と今後の展望についてお話をします。 私、 @gatchan0807 による発表です。 これまでの2年間の BASE の変化をフロントエンド組織の拡大と、コードベースの拡大の両面からお話します。 また、近年話題に上がることも多い monorepo についても BASE の monorepo の特徴的な部分と合わせて、選択したツール・それらの運用における工夫についてお話します。 今後の BASE のフロントエンド組織・技術についても触れられればと思うので、 BASE に対して少しでもご興味をお持ちでしたらぜひご覧いただきたいです! 最後に Vue Fes Japan Online 2022 の事前登録チケットは下記よりお申し込みいただけます。 カンファレンスへの参加に事前登録は必須ではありませんが、事前登録をしていただくと、Vue Fes Japan の最新情報や当日の配信 URL、セッションに関する情報などをメールでお知らせされるので便利です! passmarket.yahoo.co.jp それでは、みなさまにお会いできることを楽しみにしております!
XP祭り 2022 XP 祭り 2022 は、XPJUG(日本 XP ユーザーグループ)主催のベントです。2002 年から毎年行われていて、今年 2022 年は、10 月 1 日(土)にオンラインで開催されました。 http://xpjug.com/xp2022/ 今回の XP 祭り 2022 に BASE・BASE BANK から 3 名が登壇しましたので、その参加レポートをお届けします。 セッション内容 エンジニアが新規事業に取り組むところから始めPdMとしてプロダクト開発に向き合う組織を作り続けるまで 2022/10/01 13:00 - 13:20 B Track 所属:BASE, Inc. / New Division / BASE BANK 今回はこれまでの自分の経験をお話させていただきました。なぜ事業責任者を志向するのか、どういう考えでチームをつくっているのか、どのようにプロダクト開発に向き合うのか。 そのようなことを自由に話させていただきました。 結論としてはプロダクト開発が大好きで、これからもやり続けていきたいというものになります。 プロダクト開発のプロとしてプロダクト開発力の向上に向き合う。チームが発展し続ける仕組みをつくって、困難なプロダクト開発に挑み続けようと改めて思いました。 キャリアを考える 1 つの参考になればと思い発表させていただきました。 個人的には発表をする利点は、人生のスナップショットが取れることだと持っているので、スナップショットのつもりでお話させていただきました。 ぜひスライドご確認ください。 confengine.com XP祭り2022 エンジニアが事業責任者になる.pdf from KeitaYanagawa 不確実性に向き合うために、チームのアジリティを高める開発タスクの切り方 2022/10/01 13:00 - 13:45 I Track 所属:BASE, Inc. / BASE Depertment / Product Dev Division この発表では、ユーザーストーリーで最低でも受け入れテストを含むようにタスクを切るとチームのアジリティを高めることができることを発表させていただきました。開発チームのアジリティを高めるための一助となれば幸いです。 当日はトラック数も多い中、想像していたよりも沢山のかたにお越しいただきました。また、発表中に多くのコメントをいただいたことも感謝しています。本当にありがとうございました。 confengine.com オーナーシップを持ち自己組織化するチームに必要な、Engineering Program Managerという役割 2022/10/01 14:25 - 14:45 B Track 所属:BASE, Inc. / New Division / BASE BANK アジリティの高い組織づくりのために、今回自分はロールという観点、それも Engineering Program Manager という比較的新しいロールを基にアジリティの高いチームづくりについて発表させていただきました。 日々要求やアウトプットも複雑化していくこのソフトウェアの世界において、高度な技術スキルと同じくらい、ステークホルダーを接続しなめらかにプロダクトアウトプットし続けることが重要だと考えています。 今回の発表や資料を見てくださった方々の日々の改善の一助となれば幸いです。 confengine.com 最後に 今回、BASE・BASE BANK から 3 名登壇する機会をいただき、運営スタッフの皆様には改めてお礼を申し上げます。本当にありがとうございました。 来年の XP 祭りで、また皆様にお会いできることを弊社メンバー一同楽しみにしております。
PHPカンファレンス2022 BASEスポンサーブースでの集合写真 こんにちは!さて、この度は、2022/09/24(土)~2022/09/25(日)にオンラインで開催された PHP カンファレンス 2022 にゴールドスポンサーとして協賛し、5名のメンバーが登壇しました。 今回は、登壇者 5 名からコメントと、会場でのスポンサーブースの様子をお届けします! PHP カンファレンス 2022 とは 2022/09/24(土)~2022/09/25(日)の 2 日間にわたって PHP カンファレンス 2022 がオンライン開催されました。BASE はこれまでにも開催されている PHP カンファレンスへの登壇並びにスポンサーをコミュニティ貢献活動として行って参りました。今回はゴールドスポンサーとして当カンファレンスに協賛しています。 右上にBASEのサービスロゴ 登壇者のコメント 松田 ( @tadamatu ) Platformグループ で Group Managerをしている 松田( @tadamatu ) です。 やっぱりオフラインのカンファレンス楽しいですね! さて、今回は 「10年後のBASEとリアーキテクチャ」 と題して、 「我々は、なぜリアーキテクチャしようとしているのか?」にフォーカス して、以下の3段がまえでスポンサーLTとして参加させていただきました。 リアーキテクチャ開始前にしたこと 取り組み中のリアーキテクチャ 10年後のBASE 「リアーキテクチャ自体」の記事や登壇はたくさんあると思うのですが、 「リアーキテクチャ開始前にしたこと」について の語りはあまり多くないと思うので、ご参考にしていただけると嬉しいです。 BASEでは、リアーキテクチャを私の所属するPlatformグループが主導して、現在進行形で 絶賛取組中 なので、興味のある方はDM・カジュアル面談依頼などいただければと思います! 川口 ( @dmnlk ) FLEXY様にお誘い頂いて「急成長3社サービスの開発ストーリー〜継続成長を支える技術と仕組みのお話〜」というパネルディスカッションに参加させていただきました。 Makuake様、Pixiv様とPHPのみというよりは組織戦略や課題などをざっくばらんにお話させていただきました。 時間が足りないくらいに盛り上がり、また別の機会にでもお話できたらいいなと思いました。 改めてご招待ありがとうございました。 glassmonkey ( @glassmonekey ) こんにちは ( @glassmonekey )こと永野です。 今回は体調不良もありオンラインで登壇しました。 2~3日前の急な要請にも関わらずオンラインの登壇でチャレンジできたことは。ひとえに運営の皆様の対応力の高さには度肝を抜かされました。改めてお礼を申し上げます。 ただ、オフライン参加の様子を見ていると羨ましい気持ちはあるので、次回はオフライン登壇のリベンジをしたいなと思っております。 登壇の内容自体は「PHPerから見たWebAssemblyってどうなんだろう?」というテーマで発表しました。 最近は wordpressをWebAssembly で動かすといったものもちょうど出てきてたりで、コミュニティとしては加速していきそうだなと思っています。 WASM使って、REPLとかも作れそうだなと思い近いうちに公開できたらなと思っています。乞うご期待!! プログラミングをするパンダ ( @Panda_Program ) プログラミングをするパンダ ( @Panda_Program ) です。今年、PHPerKaigi のアンカンファレンスに登壇した感想を以下のように書いていました。 発表中に自分のエンジンがかかり、発表後もすぐには熱が冷めなかったため、ライブコーディングに失敗しても、オフラインで参加するのがよかったかなと思いました。オンラインだと「カンファレンスの廊下」(立ち話)もできないですしね。 また参加する機会があれば、次はオフライン参加してみたいと思います! 「PHPerKaigi2022のアンカンファレンスでメンバーが登壇しました」 ということで、PHP カンファレンスでは「実践!ユニットテスト入門」というタイトルでオフラインで登壇してきました。資料は以下です。 25分で収まらなかった内容は最後に付録として掲載しています。 ブースの手伝いをしてBASEというサービスの認知度はまだまだ高くないので気を引き締めていかねばならないと感じたり、カンファレンスの廊下で初めましての方とも技術的な話をして刺激を受けたり、前職の同僚や上司が自分の発表を見にきてくれたので前職の経験が現職で活きているとお礼を伝えられたり、自分がまさにPHPコミュニティにいることを実感しました。 また、自分が尊敬するエンジニアの方の素晴らしい発表 「MVCとはなにか」 (2019年のペチコン)とたまたま自分の登壇する部屋が同じで嬉しくなったり、会社の他部署のメンバーと交流を深めたり、突発的な飲み会で他社のぶっちゃけた内情を聞けたり、オフラインだからこその経験をたくさんしました。 さらに、発表後は「PHPUnitによるテスト実装を切り口としたセッションだが、基礎や座学の部分と実践の部分とのバランスが非常によく取れていたと思う」( PHP Conference 2022 セッション紹介・感想 )というような嬉しい感想を頂けたり、スライドの view 数が SpeakerDeck で 3000 を超えたり、多くの方に見ていただいてとてもありがたいです。 準備の時間や登壇のプレッシャーなど費やしたこと以上に、得ることが多かったカンファレンスでした。来年も登壇ネタを探してプロポーザルを出してみたいと思います。 02 ( @cocoeyes02 ) こんにちは! 02 こと大津です。 今回はちょっと特殊な条件をつけたコードリーディングの話をしました! 今回の題材である assertObjectEquals の話は、 PHPでテスト駆動開発本の写経をした時 にPHPUnitのassert系メソッドに関するコードリーディングをして知ったものでした。OSSのコードリーディングやPull Requestのレビュー、業務コードの調査などテストコードを読むことが活かせる場面はたくさんあります。ぜひプロダクトコードだけでなく、テストコードも読む習慣をつけてみることをオススメします。 発表の最後に「コードリーディングに自信がないと思う人」と書いてありますが、これは私自身のことも指し示しています。コードリーディングに自信がないからこそ、様々な方法でコードリーディングの難易度を下げよう!という結論に行き着いたわけです。これからもコードリーディングに関する登壇は続けていきたいなと思っています! スポンサーブースの様子 初めての技術イベント参加となりました。 海野です。 ブースの様子は以下です。 PHPカンファレンス2022の2Fのスポンサーブースご紹介です! BASEさんのスポンサーブースは、カワイイPHP公式マスコットキャラクターを飾っています!🐘🐘🐘 楽しい BASE Apps クイズも開催しています! ぜひ、お立ち寄りください!😀 #phpcon2022 #phpcon pic.twitter.com/9meQx3WnF1 — PHPカンファレンス2022 (@phpcon) 2022年9月25日 初日は雨の中でしたがその中でも印象的だったのは BASEって知ってはいるけどメイン言語はPHPなんだ! と言われることが多かったことです。 実際にどのようなことをしたかというと、Appsで機能拡張ができることを更に知っていただくために、ご来場いただいた方に2日間はAppsクイズを使った会社紹介や登壇者が載ったチラシ配布などさせていただきました(私は最終日のみの参加)。 相手が技術者かそうでないか(お子さんもいらっしゃいました)、他社様のブースのクイズを経験しているかどうか、タイミング的にこちらの登壇者のスケジュール前か後か。 こういった要素によってどのような声かけや話題が好まれるのかが常に変わるため一筋縄ではいきませんでしたが、参加者の方にチラシを手に取っていただいた時はうれしかったです。 開催イベントごとに色が違うPHP公式マスコットキャラクターElePHPantぬいぐるみに助けられたりもしました。 紹介するにあたって瞬間的に興味を抱いていただくことも大切だなと感じました。どこまで可能かはわかりませんが次は何かしらノベルティ的なものも駆使できたらなと思います。 また、ブース作業についてではないのですが、一参加者として隙間時間には登壇者の方々の発表やLTを見ることができとても刺激になりました。まずはLTに出られるようにしたいなと密かに思っています。カンファレンス中や後に他部署の方々と交流できたこともよかったです。 PHP Conference Japanチャンネル では皆さんの発表を見ることができます。 謝辞 協賛・社員のスピーカー参加を通して PHP コミュニティの盛り上がりに貢献でき、弊社としても大変有意義な時間となりました。 スタッフの方々には業務でお忙しいにも関わらず、多くの時間をカンファレンス準備へ注いでいただいたかと思います。この場を借りて御礼申し上げます。 最後に 来年の PHP カンファレンスは 10/8(日)に開催される予定 だそうです。 PHPカンファレンス2023は2023/10/8開催予定です!! それでは、また来年の PHP カンファレンスでお会いしましょう!
はじめに みなさんはじめまして。BASEでエンジニアをしております田村 ( taiyou )です。 先日、BASEではショップオーナー向けのコミュニティサイト「BASE Street」にログインするための機能としてSSOログイン機能をリリースしました。 SSOログインを実現するための認証方式はいくつかあるのですが、弊社ではSAML認証方式を用いて実現しました。 そのため、この記事ではSAML認証機構のIdPとしてOSSを使わずにSAML認証機能を実装する方法を紹介します。 前回のテックブログで、このSSOログイン機能のフロント側を開発したPJメンバーの若菜が「 サーバーサイドエンジニアがフロントエンドに挑戦して最高の経験になった話 」を執筆したのでこちらも見てみてください! SAML認証機能を提供しているOSSには、 Keycloak などがありますが、BASEでは以下の理由により自前実装することにしました。 既に大量のユーザー情報を管理しており、Keycloakなどにユーザー情報の連携を行う必要がある 弊社で採用しているPHP, Goで実装された有名なOSSがないため、弊社エンジニアで管理・運用するハードルが高い SAML認証機能を有するライブラリ( lightSAML )があり、自前実装のコストが高くなかった 以上の理由により、OSSを使わずにIdPとしてSAML認証機能を開発しました。 対象読者 SAML認証についてこれから調べようと思っている方 IdPとしてkeycloakなどのOSSを使わずにSAML認証機能を開発するエンジニア SAML認証とは? SAML認証とは、シングルサインオン(SSO)を実現する一つの認証方式です。 シングルサインオン(SSO)とは? Single sign-on (SSO) is an authentication scheme that allows a user to log in with a single ID to any of several related, yet independent, software systems. True single sign-on allows the user to log in once and access services without re-entering authentication factors. Single sign-on - Wikipedia より引用 シングルサインオン(SSO)とは、ユーザーが1つのIDで複数のサービスにログインできるする認証方法です。 このシングルサインオンによりユーザーは一度ログインすれば、認証要素を再入力することなくサービスにアクセスすることができます。 SAML認証はシングルサインオン(SSO)を実現するための認証方式 上記のシングルサインオン(以下、SSOと呼称)を実現する認証方式は、 Single sign-on - Wikipedia で記載されている通りいくつかあります。SAML認証は、そのSSOを実現するための一つの認証方式です。 SAML認証はシングルサインオン(SSO)を実現するための認証方式 SAML認証方式以外の方法については、以下の文献を参照してください。 Single sign-on - Wikipedia#Common_configurations シングルサインオン(SSO)とは-概要・仕組み | GMOトラスト・ログイン(SSO/IDaaS) SAML認証のフロー それでは、SAML認証方式でSSOを行うためのフローを説明します。以降でSAML認証のフローについて説明する前に、サービスプロパイダー(SP)とアイデンティティプロパイダー(IdP)について説明します。 サービスプロバイダー(SP) 文字通り、ユーザーに対してアプリケーションサービスを提供するものです。ユーザーがサービスプロパイダー(以降、SPと呼称)にログインする際、後述するアイデンティティプロパイダーにユーザー認証を行ってもらいSPにログインします。 サービスプロバイダー(SP) アイデンティティプロパイダー(IdP) ユーザーの認証に必要な情報を管理しているのがアイデンティティプロバイダーです。アイデンティティプロバイダーでは、SPから送信された認証リクエストを処理し、ユーザー情報をSPに返却します。 アイデンティティプロバイダー(IdP) SAML認証フロー 詳しい内容は、 Security Assertion Markup Language - Wikipedia を参照してください。 Security Assertion Markup Language - Wikipedia より引用 1. SPのページへアクセスする まずユーザーはSPのページにアクセスするとします。このとき、ユーザーが既にSP側で認証済みの場合はSAML認証を行う必要がないため、ページが表示されます。認証されていない場合は、IdPへリダイレクトされます。 2. IdPへリダイレクトする ユーザーがまだ認証されていない場合は、IdPへリダイレクトされます。IdPへリダイレクトされる際に、クエリパラメーターに SAMLRequest パラメーターが付与されます。この SAMLRequest はIdPに認証の要求をする際に必要となる以下のようなxml形式の情報を圧縮した文字列になります。 < samlp : AuthnRequest xmlns : samlp = "urn:oasis:names:tc:SAML:2.0:protocol" xmlns : saml = "urn:oasis:names:tc:SAML:2.0:assertion" ID = "{認証要求ID}" Version = "2.0" ProviderName = "{サービスプロバイダー名}" IssueInstant = "{SAMLRequestの生成日時}" Destination = "{SAMLRequestの送信先IdPのURL}" ProtocolBinding = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" AssertionConsumerServiceURL = "{認証結果のPOST先SPのURL}" > < saml : Issuer> http://sp.example.com/hoge/metadata.php </ saml : Issuer> < samlp : NameIDPolicy Format = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress" AllowCreate = "true" /> < samlp : RequestedAuthnContext Comparison = "exact" > < saml : AuthnContextClassRef> urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport </ saml : AuthnContextClassRef> </ samlp : RequestedAuthnContext> </ samlp : AuthnRequest> 3. SAMLRequestを検証/ユーザー認証を行います IdPでは、まずユーザーのブラウザ(ユーザーエージェント)を経由してSPから送信された SAMLRequest の検証を行います。この検証では、電子署名付きの SAMLRequest が送信される場合では、電子署名を行います。 SAMLRequest の検証が終了したら、ユーザー認証のためにログインページを表示します。ユーザーはIdPに登録したメールアドレスやパスワードを入力します。 4. SAMLResponseを生成します ユーザー認証のための情報をフォームに入力して送信したら、IdP側でログイン処理を行います。そして、ログインに成功したら、SPに送信する SAMLResponse を生成します。この SAMLResponse はSP側でユーザー認証を行う際に利用する情報をxmlに格納します。 SAMLResponseの例 <? xml version = "1.0" encoding = "UTF-8" ?> < samlp : Response xmlns : samlp = "urn:oasis:names:tc:SAML:2.0:protocol" xmlns : saml = "urn:oasis:names:tc:SAML:2.0:assertion" Destination = "{SAMLResponseの送信先SPのURL}" ID = "{IdP側で発行するID}" InResponseTo = "{SPから送信されたSAMLRequestに含まれる認証要求ID}" IssueInstant = "{SAMLResponseを発行した日時}" Version = "2.0" > < saml : Issuer> {IdPのURL} </ saml : Issuer> < samlp : Status> < samlp : StatusCode Value = "urn:oasis:names:tc:SAML:2.0:status:Success" /> </ samlp : Status> < saml : Assertion xmlns = "urn:oasis:names:tc:SAML:2.0:assertion" ID = "{IdP側で発行するID}" IssueInstant = "{SAMLResponseを発行した日時}" Version = "2.0" > < saml : Issuer> {IdPのURL} </ saml : Issuer> < dsig : Signature xmlns : dsig = "http://www.w3.org/2000/09/xmldsig#" > < dsig : SignedInfo> < dsig : CanonicalizationMethod Algorithm = "http://www.w3.org/2001/10/xml-exc-c14n#" /> < dsig : SignatureMethod Algorithm = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256" /> < dsig : Reference URI = "#ID_b93d4d7d-1937-474f-84df-2f3440025a3c" > < dsig : Transforms> < dsig : Transform Algorithm = "http://www.w3.org/2000/09/xmldsig#enveloped-signature" /> < dsig : Transform Algorithm = "http://www.w3.org/2001/10/xml-exc-c14n#" /> </ dsig : Transforms> < dsig : DigestMethod Algorithm = "http://www.w3.org/2001/04/xmlenc#sha256" /> < dsig : DigestValue> {ハッシュ値} </ dsig : DigestValue> </ dsig : Reference> </ dsig : SignedInfo> < dsig : SignatureValue> {電子署名の値} </ dsig : SignatureValue> < dsig : KeyInfo> < dsig : X509Data> < dsig : X509Certificate> {IdP側で発行した証明書} </ dsig : X509Certificate> </ dsig : X509Data> </ dsig : KeyInfo> </ dsig : Signature> < saml : Subject> < saml : NameID Format = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress" > {ログイン対象となるユーザのメールアドレス} </ saml : NameID> < saml : SubjectConfirmation Method = "urn:oasis:names:tc:SAML:2.0:cm:bearer" > < saml : SubjectConfirmationData InResponseTo = "{SPから送信されたSAMLRequestに含まれる認証要求ID}" NotOnOrAfter = "{SAMLResponseの有効期限}" Recipient = "{SAMLResponseの送信先SPのURL}" /> </ saml : SubjectConfirmation> </ saml : Subject> < saml : Conditions NotBefore = "{SAMLResponseの有効期限開始日時}" NotOnOrAfter = "{SAMLResponseの有効期限終了日時}" > < saml : AudienceRestriction> < saml : Audience> {SPのドメイン} </ saml : Audience> </ saml : AudienceRestriction> </ saml : Conditions> < saml : AuthnStatement AuthnInstant = "{SAMLResponseを発行した日時}" SessionIndex = "{IdP側で発行するID}" SessionNotOnOrAfter = "{IdP側のセッション有効期限}" > < saml : AuthnContext> < saml : AuthnContextClassRef> urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified </ saml : AuthnContextClassRef> </ saml : AuthnContext> </ saml : AuthnStatement> < saml : AttributeStatement> ... </ saml : AttributeStatement> </ saml : Assertion> </ samlp : Response> 5. SPへSAML認証情報をPOSTします IdP側で SAMLResponse の生成が完了したら、次のようなHTMLをレンダリングし、POSTします。 < form method = "post" action = "https://sp.example.com/saml2/sso/post" ...> < input type = "hidden" name = "SAMLResponse" value = "{XML形式のSAMLResponseをbase64エンコーディングした値}" /> ... < input type = "submit" value = "Submit" /> </ form > レンダリングした際、JavaScript側で画面が表示されたらsubmitボタンを自動で押下する処理を行うことでユーザーはSP画面へ自動遷移するようになります。 6. SPのページが表示される SP側でIdPから送られた SAMLResponse の検証が終了し、ログイン処理が終了したら、SAML認証は成功です。これで、ユーザー認証が必要なSPのページが表示されます。 SAML認証を実現する方法 上記で説明したSAML認証を実現するための方法として、次の2つが考えられます。 方法1. OSSを利用する SAML認証を実現するための代表的な方法として、OSSライブラリを利用する方法があります。現在、SAML認証機能を提供できるOSSとして以下のものがあります。 Keycloak : https://github.com/keycloak/keycloak これらのOSSを利用することで、ユーザーに対してSAML認証機能を提供することができます。 メリットとデメリットはそれぞれ以下の通りです。 メリット OSSをcloneし、SAML認証用サーバーを用意し、起動すればSAML認証機能を提供できること デメリット SAML認証用サーバーの保守が新たに必要になる BASEのように既にユーザーデータが管理されている場合は、BatchやAPI, MQなどを利用してSAML認証用サーバーにデータを連携する必要がある 方法2. 自前でSAML認証機能を開発する 別の方法として、既に提供しているサービスの1機能としてSAML認証機能を開発し、提供する方法があります。つまり、SAML認証用のエンドポイントとSAML認証用のログインフォームなどを開発することで提供する方法です。 メリット 稼働中のサービスの1機能として提供するので、新たにSAML認証用サーバーを用意する必要がなく、運用コストが抑えられる ユーザー認証に必要な情報をOSSなどに連携する必要がない デメリット SAML認証機能を開発する実装難易度が比較的高い BASEでは、次の2つの理由から方法2を採用しました。 SAML認証機能を利用するユーザーが限られているため、keycloakのメンテナンス・ランニングコストとユーザーへの費用対効果が釣り合わない BASEでは既に大量のユーザー情報を管理しているため、これをkeycloakに連携する必要がある 弊社で採用しているPHP, Goで実装された利用実績のある有名なOSSがないため、弊社エンジニアで管理・運用するハードルが高い IdPとしてBASEではどのような設計になったか? BASEのように既にサービスに登録しているユーザーのSAML認証を行う際に、BASEではどのような設計になったのか紹介していきます。 SAML認証のための必要な機能とページ 具体的な設計の紹介に入る前に、IdPとしてBASEでは、そもそもどのような機能が必要なのかを明らかにしてきます。SAML認証のための必要な機能は以下の通りです。 機能 説明 SAMLRequestの検証機能 SPからクエリパラメーターで送信されたSAMLRequestを検証する機能です。SP側で電子署名を行っている場合など検証が必要な場合に呼び出される機能です。 ログイン判定機能 SPからリダイレクトされたユーザーがBASEに既にログインしているか判定する機能です。もしログイン済みの場合は、ログイン処理をスキップするようにします。 ログイン機能 メールアドレス、パスワードなどユーザー情報を指定することでログイン処理を行う機能です。機能自体は通常のログインと差異はありません。 SAMLResponseの生成機能 メールアドレスやユーザーネームなどのユーザー情報とSPから送信されたSAMLRequestからSAMLResponseを生成する機能です。 また、必要なページは以下の通りです。 ページ 説明 SSOログインページ SSOログインのために必要なフォームを有するページです。 SPリダイレクトページ SAMLResponseをSPに送信するためのフォームページです。通常、このページではユーザー自身がフォームの送信ボタンを押すのではなく、ページがロードされたらjsで送信ボタンを押すように実装されます。 SPからBASEにリダイレクトされた際の画面遷移としては、SSOログインページでログインを行い、SAMLResponseをSPにフォームPOSTするSPリダイレクトページを表示するような画面遷移になります。 これらの機能とページがそれぞれどのようにやりとりするのか詳細の設計を紹介します。 処理の流れ 1. SPからBASEにリダイレクトされる SPからBASEにリダイレクトされたら、次の処理を行います。 SAMLRequestの検証処理 ログインの判定処理 既にBASEにログインしている場合は、「3. SAMLResponseを生成し、SPにリダイレクトする」へ進みます。 BASEにログインしていない場合は、ログインページを表示します。 2. BASEにログインする ログインページが表示されたら、ログイン処理に必要なメールアドレスとパスワードを入力し、submitします。今回BASEで開発したSAML認証機能ではすでにBASEサービスに登録しているユーザーにのみ提供する機能なので新規登録フォームは除外しています。 3. SAMLResponseを生成し、SPにリダイレクトする ログイン処理が正常に完了したら、SPへ返す認証情報であるSAMLResponseを生成します。SAMLResponseを生成したら、SPリダイレクトページを表示します。このSPリダイレクトページは、次のようなSPへPOSTするフォームページです。 < form method = "post" action = "https://sp.example.com/saml2/sso/post" ...> < input type = "hidden" name = "SAMLResponse" value = "{SAMLResponseをbase64エンコーディングした値}" /> ... < input type = "submit" value = "Submit" /> </ form > この場合、ユーザーが手動でsubmitボタンを押すことでSPへ遷移することも可能ですが、SAML認証機能を提供する多くのサービスはページがロードされたら自動でsubmitボタンを押下するjavascriptコードを実装することで自動遷移するようにしています。 おわりに この記事では、OSSを使わずにIdPとしてSAML認証機能を開発する方法について紹介しました。 IdPとしてSAML認証機能を提供する方法として、それぞれ ①OSSを利用する ②自前実装する 方法があります。今回、BASEでは②自前実装する方法を採用し、開発を行いました。その開発中に私が苦労した点として「 SAML認証についての資料が少なかった 」があげられます。そのため、この記事がみなさんの参考に少しでもなれれば幸いです。 参考文献 Security Assertion Markup Language - Wikipedia Using HTTP POST binding in OpenSAML 4 | SAMLSecurity How to turn your PHP website into a SAML Identity Provider in 30 minutes | by Duarte Garin | Prosple Engineering | Medium RFC 7522 - Security Assertion Markup Language (SAML) 2.0 Profile for OAuth 2.0 Client Authentication and Authorization Grants シングルサインオン(SSO)とは-概要・仕組み | GMOトラスト・ログイン(SSO/IDaaS)
⛰ はじめに こんにちは。Owners Marketing所属の 若菜 です。 今回は、普段サーバーサイドエンジニアとしてプロダクト開発に従事している私が、BASEのフロントエンド開発に携わった経験をお話しさせていただきます。 結論、 付加価値がいくつもあった非常に良い経験であった と言えます。 BASEでの働き方や開発組織の雰囲気を少しでも伝えることができましたら幸いです! 🙋‍♂️そもそもなぜフロントエンド領域を担当することになったのか 私の所属するOwners Marketingでは、新規ショップオーナーの方によりよくBASEを使ってもらえるための機能改善や、 もっとたくさんの人にBASEを使ってもらえるようにするための機能提供に取り組んでいます。 先日、「オーナーズコミュニティ BASE Street へSSOログインできるようにする」というリリースを行いました。 BASE Streetは、ショップオーナー同士で意見を交わすことのできる場を、外部サービスを用いて提供しています。 そちらのサービスにBASEのアカウントでログインできるようにすることでより利便性を向上し、 ショップを開設して間もない方から既にBASEを使ってくださっているベテランの方までさまざまな利用者様の交流を通じ、 お悩み解決などに役立てていただきたいという目的があります。 BASEでは、従来であれば技術領域にあったエンジニアをアサインしてプロジェクトを進行しますが、 今回のプロジェクトにおいては 私自身がフロントエンドの開発に興味があった そこまで技術難易度の高い開発になる見込みはなく、スケジュールにも多少余裕があった 直近で似たような機能をほかプロジェクトでリリースしたので参考にしやすい 上記の理由をもとに、プロジェクトの人員配置を検討する際に相談をさせていただきました。 BASEには、 手を挙げることでやりたいことに積極的にチャレンジさせてもらえる という社風がある こともあり、今回の挑戦も歓迎していただけました。 BASEではフロントエンドフレームワークとしてVue.jsが採用されています。 私自身のフロントエンド経験はjs・jQueryで止まっていたため、Vue.jsを業務で学ぶことができる絶好の機会をいただくことができました。 🏃‍♀️ どのように進めたか 社内ドキュメントに沿って環境構築を行った 社内にはフロントエンド開発の手引きとなるドキュメントが用意されています。 こちらにはフロントエンド開発における前提知識からプロジェクトの作成の仕方、進め方までがハンズオン形式で解説されています。 こちらを参考にし、Vue.jsのインストールからプロジェクト用のディレクトリ作成、ルーティング設定などを行いました。 ディレクトリ構造などをざっくりと理解しつつ手を動かしてみた 他プロジェクトのコードや、社内で用意されているドキュメントに目を通して、 ディレクトリ構造や今回触っていくことになるファイルの役割、書き味を理解しました (エントリファイルとは、シングルファイルコンポーネントとは、というレベルから学びました) 周りのフロントエンドエンジニアたちに協力してもらいながら開発を進めた 自分だけではプロダクト品質をリリース水準まで満たすことができないので、周りの方にご協力いただき以下の体制で進めました。 フロントエンド開発の経験があるEMにデイリーMTGを設定してもらった 進め方がわからないところ、環境構築で詰まっているところなどを画面共有で教えてもらった レビューの際、他のフロントエンドエンジニアに入ってもらった プルリクエストを出しておくことで他チームの方もレビューしてくれた BASEではエンジニア全員参加のチャンネルでPRが流れてくる仕組みになっている 他チームのエンジニアがコメントをくれることがよくある 🖼 完成した画面 以下、作成した実際の画面および機能です。 ログイン画面の表示・装飾 入力値のバリデーション制御 ログインする ボタンを押下するとログインAPIをコール ログイン処理に成功したらローディング画面へ遷移 時間はかかりましたが、自分で1から関わった実装が形となって世に出ていく様は特別な嬉しさがありました。 🔥 苦労したところ 無事何事もなくリリースすることができた本プロジェクトですが、実施している中で主に以下の点に苦労しました。 アーキテクチャやディレクトリ構造、データフローの理解 BASEのフロントエンド領域のコードにまともに触れたことが初めてだったため、ディレクトリ構成やデータの流れ・処理の流れをコードを読みながら掴むところが初めの難関でした。 こちらは前述した社内ドキュメントにハンズオン形式で丁寧に解説されているため、これを読みながらドキュメント内のサンプルソースコードを手元に書き起こし、デバッグコードを仕込んだりして地道に体に浸透させていきました。 このアーキテクチャ構造が理解できた時、自分の行いたい処理をどこに書けばよいかがわかり、すごくコーディングしやすくなった感覚がありました。 Vue.jsとTypeScriptの記法をまとめて覚えようとしてしまったところ フロントエンド実装を行うにあたり、他プロジェクトでのソースコードなどをたくさんみて参考にしましたが、 Vue.jsとTypeScriptに触れることがほぼ初めてだったため、 実装の仕方や参考にすべきリファレンスがわからず、もどかしい思いをしました。 こちらはVue.jsとTypeScriptそれぞれの初学者向けのリファレンスを広く読んで自分なりに咀嚼していったことと、周囲のフロントエンドエンジニアの方にデイリーMTGやSlackでこまめに質問させていただき、解決することができました。 👍 こういうところが良かった・今後にこうやって活かしたい フロントエンド開発に1から携わり、上述した点などで苦労も多くありましたが、何より「挑戦してよかった」と思えることがいくつもありました。 中でも、個人の観点とチームの観点からそれぞれ抜粋させていただきます。 個人の観点では サーバーサイドからフロントエンドまでの処理・データの流れを体感できた 日頃の開発でBASEというプロダクトにサーバーサイド観点のみで触れてきた自分にとって、フロントエンドの処理は正直わかっていない点が多くありました。 今回、自分でVue.jsのコードを書くことでサーバーサイドからフロントエンドまで一つの流れとして追うことができ、自分の作った機能のすみずみまで自分で把握することができました。 これによって、何か問題があった時の対応を素早く検討することができたり、新たな改善を行う際にもサーバーサイドとフロントエンドの両面からアプローチを考えることができます。 Web開発・サービス提供に従事するものとして、こうでありたいと改めて再認識しました。 社内調査でカバーできる範囲が格段に増えた 社内の調査タスクにおいて、フロントエンド領域のコードを読めるようになったことでいい影響がありました。 具体的には、今まではフロントエンドエンジニアの方に聞かなければ調査ができないようなものも、まず自分で調査に着手することができ、わからないところだけ質問する方法をとることができるようになりました。 これは、読解し理解できる範囲の母数が単純に増えたこともありますが、Vue.jsのディレクトリ構成やフォルダの役割を知ることができたおかげで、 フロントエンドのコードを読む心理的障壁がなくなった と思っています。 jQueryで止まっていたFEキャリアを再開できた 新卒から3年ほどjQueryを扱っていましたが、それ以降モダンなフロントエンドフレームワークにほぼ触れてこなかったので、今後のキャリア形成にとてもいいチャンスをいただけました。 チーム・組織の観点では 後続も挑戦しやすい雰囲気作りに励みたい 今回自分が心置きなくフロントエンドの開発に挑むことができたのは、周りのエンジニアたちがサポートしてくれたからに他なりません。 そのため、これから同じようにサーバーサイドエンジニアだけどフロントエンドの開発に携わりたい!と思った人がスムーズに開発できるように、 社内ドキュメントの最適化やレビューに積極的に参加していきたいと思っています。 BASEの「チャレンジ大歓迎」な社風をより活発にしていきたい 前述の通り、BASEの開発組織では やりたいことに手を挙げることで積極的にチャレンジできる 社風があります。 私は、自らの担当領域にとらわれず、様々なドメイン領域・技術領域にチャレンジできるBASEの空気がとても好きです。 それらの体験は開発者体験を向上させ、組織の生産性向上にも寄与し、ひいてはサービス利用者の方々への高品質な価値提供に直結すると思っています。 この最高な好循環を自分で体感できたことで、今後のBASEの開発組織をもっともっとよくしていきたいと思いました。 📢 お知らせ 次回は、同じプロジェクトに携わった田村より SSOログインに関する記事を公開予定です! SAML認証によるSSOログインをどのように自前で実装したのか?を解説する、非常に充実した内容となっておりますのでご期待ください! また、BASEではこれからのBASEを一緒に盛り上げて行ってくれる方を随時募集中です! カジュアル面談も実施しておりますので、お気軽にお問い合わせください。 open.talentio.com
XP祭り 2022 XP 祭り 2022 は、XPJUG(日本 XP ユーザーグループ)主催のベントです。2002 年から毎年行われていて、今年 2022 年は、10 月 1 日にオンラインで開催されます。 xpjug.com 今回、BASE・BASE BANK から 3 名が登壇します。 セッション内容について エンジニアが新規事業に取り組むところから始めPdMとしてプロダクト開発に向き合う組織を作り続けるまで 2022/10/01 13:00 - 13:20 B Track 所属:BASE, Inc. / New Division / BASE BANK BASE BANK 所属の、柳川さんによるセッションです。新規事業を 1 から立ち上げ、現在グロースフェーズを迎えるまでの失敗と成功の経験を発表します。熱い話が聞けそうですね。 confengine.com 不確実性に向き合うために、チームのアジリティを高める開発タスクの切り方 2022/10/01 13:00 - 13:45 I Track 所属:BASE, Inc. / BASE Depertment / Product Dev Division BASE 所属の @tanden が発表させていただきます。不確実性に振り回されることなく、チームのアジリティを高めるために、開発タスクをどう切り出していくとよいのかについて発表します。 confengine.com オーナーシップを持ち自己組織化するチームに必要な、Engineering Program Managerという役割 2022/10/01 14:25 - 14:45 B Track 所属:BASE, Inc. / New Division / BASE BANK BASE BANK 所属の、 松雪さん によるセッションです。最近、Engineering Program Manager(EPM)というロールが注目を集めていますが、BASE BANK チームにおける EPM の役割とその働き方について発表します。自律的なチームをどう作っていくのかのヒントを聞けそうで楽しみです。 confengine.com 最後に XP 祭り 2022 への参加は下記よりお申し込みいただけます。 xpjug.connpass.com それでは、オンラインでの開催にはなりますが、みなさまにお会いできることを楽しみにしております。
2022/09/24(土) 〜 2022/09/25(日)の日程で開催される PHP Conference Japan 2022 で BASE に所属する4名のエンジニアが登壇します。 phpcon.php.gr.jp BASE はこれまでも PHP カンファレンスへの登壇並びに協賛をしています。 PHPカンファレンス2021に5名のメンバーが登壇・プラチナスポンサーとして協賛しました - BASEプロダクトチームブログ PHP Conference Japan 2020に4名のメンバーが登壇!プラチナスポンサーとして協賛しました! - BASEプロダクトチームブログ PHP Conference Japan 2019に3名のメンバーが登壇・プラチナスポンサーとして協賛しました - BASEプロダクトチームブログ 「PHP Conference 2018」にゴールドスポンサーとして参加しました - BASE Book(ベイスブック) PHPカンファレンス2015 - #phpcon2015 今年で通算6回目のスポンサーとなり、PHP コミュニティへの貢献を続けることができ大変嬉しく思います。 2022年はゴールドスポンサーとして当カンファレンスに協賛しています。 また、スポンサーブースでは BASE Apps クイズ の催しを行います。ぜひお越しください! セッションの内容について PHP で始める WebAssembly 入門 2022/09/24 14:40〜 Track1 レギュラーセッション(25分) 所属:BASE, Inc. / NEW Division / BASE BANK fortee.jp みなさんは WebAssembly はご存知でしょうか? ブラウザで利用するケースはもちろんのこと、最近ではサーバーレスアプリケーションで活用できるようになってきました。 このトークでは PHP と WebAssembly に関連するエコシステムの紹介と可能ならデモも紹介します。 Go Conference でも登壇実績のある glassmonkey さんによる発表です。 WebAssembly(WASM)は最近フロントエンドまわりでよく耳にする話題ですね。 PHP x WebAssembly の組み合わせではどういったものがあるのか気になります! 実践!ユニットテスト入門 2022/09/24 16:00〜 Track3 レギュラーセッション(25分) 所属:BASE, Inc. / Product Dev Div / Service Dev Sec / CRM fortee.jp テスト書いてますか? テストを書く理由と実際のテストコードを紹介する実践編に分け、TDD を3年間実践してきた経験に基づいてお話しします。 テストを書いたことのない方が、テストを書いてみたいと思ってもらえることを目指します。 サンプルコードは PHP + PHPUnit ですが、他言語でも通用する考え方を紹介します。 ■ 概要 ・なぜテストコードを書くのか ・レガシーコードとは、テストのないコード ・テストはコストが安いフィードバックループである ■ 実践編 ・テストケースは日本語で書こう ・いろんな assertion を知ろう ・arrange / act / assertion のテストコード実装パターン ・set up / tear down を使って前処理/後処理をする ・dataProvider でテストをまとめる(ただし早すぎる抽象化に気をつけよう) 登壇者のプログラミングをするパンダさんは以前 TDD の話題で Software Design に寄稿 したりしています。 テストを書くのにハードルを感じている方にもおすすめのセッションです! テストコードリーディングのみで PHPUnit の仕様を理解してみる 2022/09/25 13:55〜 Track4 レギュラーセッション(25分) 所属:BASE, Inc. / Product Dev Div / Service Dev Sec / Sales Promotion fortee.jp みなさんは「初めて見るコードの仕様を爆速で理解したい!」と思うときはありませんか? 例えばプロジェクトに初参画したときや、使用している OSS ライブラリの調査をするときなど・・・ そんな時には既存のテストコードを活用すると便利です。 見通しの良いテストコードやテストケース管理は、テスト対象となるコードの仕様理解を手助けします。 今回のセッションでは、PHPUnit の実装コードや公式ドキュメントサイトを一切見ずに、 PHPUnit 内で書かれているテストコードを読みながら PHPUnit の仕様を理解していきます。 当セッションはこんな方におすすめです! ・自分以外の人が実装したテストコードをあまり読まない人 ・PHPUnit のコードを読んだことがない人 PHP カンファレンスではおなじみの 02 さんによる発表です。 去年は Composer2.0 についての発表 でしたが今年は PHPUnit についてです!テストについての関心が高まります。 10年後の BASE とリアーキテクチャ 2022/09/25 16:00〜 Track1 小展示ホール ゴールドスポンサートーク(5分) 所属:BASE, Inc. / Product Dev Div / Service Dev Sec / Platform fortee.jp BASE では現在進行形でリアーキテクチャに取り組んでいます。 何時間にも渡り議論を繰り返し、どのような経緯でモジュラモノリス採用に至ったのか。 また、BASE のどんな将来像を見据えてリアーキテクチャに取り組んでいるかといったことを話します。 Platform グループでマネージャーをしている tadamatu さんの発表です。最近弊社 Tech Blog にて OpenAPI 関連の記事 を書いていただきました。 サービスリリースから長い年月を経て、今どのようにしてリアーキテクチャへ取り組んでいるのかについてです。 同じような規模のサービスを開発しているエンジニアには気になる話題ではないでしょうか? 最後に PHP Conference Japan 2022 の当日のチケットは下記よりお申し込みいただけます。 phpcon.connpass.com それでは、みなさまにお会いできることを楽しみにしております!
こんにちは。Pay ID Devの大木 ( @roothybrid7 )です。 今回外部スクリプトとして読み込み利用する外部SDKを、Reactに組み込むためのラッパーライブラリを作ったので、その開発事例を紹介します。 今回、 SWC(Speedy Web Compiler) や Release Please を利用して開発したので、主にそれらをどう使ったのかを紹介いたします。 背景 去年12/16に開催しましたオンラインイベント 「BASE Tech Talk #1 〜Next.jsを使ったカート大規模リプレイスPJの裏側〜」 の通り、BASEカートシステムのFrontendアプリケーションは、Next.jsで動作してます。 さて、アプリケーションでは、Amazon PayやPayPalなど様々な外部の決済サービスを利用しており、それらのJavaScript SDKをいくつか利用しています。 これらのSDKは、幅広いWebサイトで動作するように作られており、ドキュメントも用意され、各種APIを簡単に利用できるようになっています。 利用方法に関しては決められており、バンドルしたり自分自身でホスティングして利用することはできず、 <script> タグを使って直接読み込む必要があります。 また、利用登録をして、API Keyといったものも必要になったりします。 SDKの機能利用の方法は、サービスによって様々あり、スクリプト読み込み後のグローバルオブジェクトのメソッドを使うだけのものであったり、一度しか作れないインスタンスを取得しそれによって始めて機能を使うことができるものもあります。 Reactに組み込むのは、 他のライブラリとのインテグレーション で述べられているように確かに可能です。 そこでは、他ライブラリのスクリプトが組み込まれた前提の話で勧められていますが、 <script> で直接読み込むSDKの場合、以下の点を考慮する必要があると思っています。 バンドルして利用することはできないため、スクリプトが読み込まれているかや重複して読み込まないか、SDKの初期化方法の確認 SDKのI/Fを使って、Reactのライフサイクルに応じた処理の実行、SDKから取得した値やインスタンスを失わないように保持 小規模なアプリケーションなら、グローバル変数やSDKを利用するReactコンポーネントで保持すれば問題ないのかもしれません。 ただし、規模が大きくなれば、Reactコンポーネントツリーの末端でしかも複数の離れた箇所で利用することもあるため、組み込むのは難しくなります。 問題に関しては、 react-paypal-js に記載されている通りで、他のSDKでも同じような問題が起こります。 The Problem Developers integrating with PayPal are expected to add the JS SDK <script> to a website and then render components like the PayPal Buttons after the script loads. This architecture works great for simple websites but can be challenging when building single page apps. React developers think in terms of components and not about loading external scripts from an index.html file. It's easy to end up with a React PayPal integration that's sub-optimal and hurts the buyer's user experience. For example, abstracting away all the implementation details of the PayPal Buttons into a single React component is an anti-pattern because it tightly couples script loading with rendering. It's also problematic when you need to render multiple different PayPal components that share the same global script parameters. それらの本質的でない複雑な処理は、アプリケーションに実装するよりライブラリにまとめてしまった方が、アプリケーション実装は簡単になるので、検討に値すると思います。あとは、新しいツールを試す絶好の機会にもなります。 また、実装には、以下のライブラリを参考にしました。 react-paypal-js react-google-recaptcha-v3 react-stripe-js プロジェクト構成 さて、実装したライブラリは、外部SDKをロードして利用するラッパーであり、ReactのI/Fに準拠しているが単体では動作しないプラグインと呼ぶようなものです。 以下に package.json の内容を抜粋します。 // package.json { " name ": " @baseinc/react-payid-js ", " version ": " 0.1.0 ", " main ": " dist/index.js ", " types ": " dist/types/index.d.ts ", " publishConfig ": { " registry ": " https://npm.pkg.github.com/ " } , " files ": [ " dist ", " README.md ", " CHANGELOG.md " ] , " scripts ": { " bundle ": " spack ", " build ": " yarn bundle ", " test ": " jest ", " type:check ": " tsc --noEmit ", " type:declarations ": " tsc --emitDeclarationOnly --outDir dist/types ", " validate ": " concurrently yarn:format:check yarn:type:check yarn:lint yarn:test yarn:build ", " sync ": " concurrently yarn:build yarn:type:declarations ", " prepack ": " yarn clean && yarn sync ", " clean ": " rimraf dist " } , " devDependencies ": { " @swc/cli ": " ^0.1.57 ", " @swc/core ": " ^1.2.203 ", " @swc/jest ": " ^0.2.21 ", " @testing-library/react ": " ^13.3.0 ", " @types/react ": " ^18.0.14 ", " @types/react-dom ": " ^18.0.5 ",, " concurrently ": " ^7.2.2 ", " jest ": " ^28.1.1 ", " jest-environment-jsdom ": " ^28.1.1 ", " jest-mock-extended ": " ^2.0.6 ", " react ": " ^18.2.0 ", " react-dom ": " ^18.2.0 ", " rimraf ": " ^3.0.2 ", " typescript ": " ^4.7.3 " } , " peerDependencies ": { " react ": " >=17.0.0 ", " react-dom ": " >=17.0.0 " } } peerDependencies で、ホストとなるアプリケーションなどが利用を想定されるReactの最低バージョンを指定 開発時やテスト実行時にも Reactが必要となるので、 devDependencies でも明示的に指定 TypeScriptのコンパイルとbundleは、SWCで行う TypeScriptの型チェックと型定義ファイルの出力は、 tsc で行う GitHub Packagesで、privateな npm packageとして利用できるように SWCについて SWCは、最新機能を使ったJavaScript、TypeScriptファイルをブラウザ互換のJavaScriptに変換することができます。 Next.js や Deno でも使われています。 設定 は、 .swcrc に記述します。 Babelの置き換え も目標としているようなところもあり、設定にはそれと同等の設定も見られます。 また、 spack(swcpack) という複数のJavaScript、TypeScriptファイルを一つにbundleできる機能があります。 rollup.js や webpack を利用したことがあるという人は、それらと似たようなツールだと考えてもらえればいいです。 bundleの実行 bundleツールの設定 は、 spack.config.js というファイルに、 .swcrc に追加するような設定とあわせて記述できますが、開発途中だからなのか、コンパイル周りの設定のいくつかは、 .swcrc ファイルに追加しなければならないという罠があります。 設定ファイルは以下の通りです。 // spack.config.js const { config } = require( '@swc/core/spack' ); module.exports = config( { entry: { web: __dirname + '/src/index.ts' , } , output: { path: __dirname + '/dist' , name: 'index.js' , } , module: {} , externalModules: [ 'react' , 'react-dom' ] , // [...snip...] } ); react や react-dom は、ライブラリを利用するホスト側のアプリケーションなどで解決するため、Bundleに含める必要がないので、 externalModules で除外します。 rollup.jsのexternal と同じような指定方法ですね。 spack.config.js のあるディレクトリで、 spack コマンドを実行すると、 output に指定したディレクトリにbundleファイルが出力されます。 $ spack Bundling done: 0s 138.030983ms Done: 0s 0.351072ms ✨ Done in 0.49s. Bundleツール利用時のコンパイル設定 さてこの spack は、 .swcrc に追加するようなコンパイル周りの設定も options に追加することができます。 これによりJavaScriptへの変換やminify、難読化も行うことができます。 // spack.config.js const { config } = require( '@swc/core/spack' ); module.exports = config( { // [...snip...] options: { minify: true , jsc: { target: 'es5' , minify: { compress: true , mangle: { keepFnNames: true , } , } , parser: { syntax: 'typescript' , tsx: true , decorators: true , dynamicImport: true , } , transform: { legacyDecorator: true , decoratorMetadata: true , react: { runtime: 'automatic' , useBuiltins: true , } , } , } , } , } ); しかし、未実装な箇所があるのか一部の設定は追加しても動きません。そこで、 .swcrc を併用するわけなのですが、デフォルトだと暗黙的にこれを利用してくれます。 利用してくれるのであれば、 .swcrc に全て定義すればいいのでは? と思うかもしれませんが、JSON形式で設定を扱うと記述ミスが怖いので、できるだけJavaScript形式の設定ファイルを使いたいです。 以下に、 .swcrc の設定内容を載せます。 // .swcrc { " exclude ": [ " .*.test.tsx? ", " .*.js$ " ] , " module ": { " type ": " commonjs ", " lazy ": true } , " jsc ": { " transform ": { " optimizer ": { " globals ": { " vars ": { " ENTRYPOINT_FUNCTION_NAME ": " '__payid%example%v1__' " , } } } } } } exclude は、適用しないファイルのリストを設定します。ソースコードはTypeScriptなので、JavaScriptの設定ファイルやテストコードのファイルを除外しています。 コンパイル時にコードを置き換える jsc.transform.optimizer.globals も、 spack.config.js に指定しても動かない設定の一つで、コンパイル時に指定した変数を置き換えることが可能です。 WebpackのDefinePlugin と同じようなことができます。 // .swcrc { // [...snip...] " jsc ": { " transform ": { " optimizer ": { " globals ": { " vars ": { " ENTRYPOINT_FUNCTION_NAME ": " '__payid%example%v1__' " , } } } } } } 今回利用するSDKは、使うのに用意されたグローバル関数を実行するのですが、今後後方互換性のないアップデートなどが発生した場合、関数名が変更されうるなど、名前が可変なため、コンパイル時に指定の名前に置き換えるという処理に使いました。 ソースコードで以下の記述があったとして、 declare const ENTRYPOINT_FUNCTION_NAME: string ; // [...snip...] return window [ ENTRYPOINT_FUNCTION_NAME ] ; swcでコンパイルすると、以下のように変換されます。 return window [ '__payid%example%v1__' ] ; コンパイル、Bundle設定の調整 また、Bundleファイルが動作するかは、リポジトリに別ディレクトリを切って、最小限のサンプルアプリケーションを作って確認しました。 今回の想定環境は、Next.jsアプリケーションで、Reactのバージョンがv17以上なので、以下のようなプロジェクトの設定を用意しました。 { " name ": " next ", " version ": " 0.1.0 ", " private ": true , " scripts ": { " dev ": " next dev ", " build ": " next build ", " start ": " next start ", " lint ": " next lint ", " dev:sync ": " cd ../../; yarn sync " } , " dependencies ": { " @baseinc/react-payid-js ": " latest ", " jose ": " ^4.8.1 ", " next ": " 12.1.6 ", " react ": " 17.0.2 ", " react-dom ": " 17.0.2 " } , " devDependencies ": { " @types/node ": " 18.0.0 ", " @types/react ": " ^17.0.0 ", " @types/react-dom ": " ^17.0.0 ", " eslint ": " 8.18.0 ", " eslint-config-next ": " 12.1.6 ", " typescript ": " 4.7.4 " } } commonjs形式にして難読化などもするため、これで、Bundleファイルで提供するライブラリが正しく動作するかを確認して、コンパイル設定を試行錯誤していきました。 型定義ファイルの出力 TypeScriptでコーディングするアプリケーションに組み込んでもらうのに、コード補完や型チェックに利用する型定義ファイル(.d.ts)を用意した方が便利です。 それには、 tsc コマンドを使って出力します。 $ tsc --emitDeclarationOnly --outDir dist/types ✨ Done in 3.65s. テスト 外部SDKをロードして利用するラッパーなので、スクリプトのロード管理まわりの動作確認は、自動化しておきたいところです。 テスティングフレームワークとして、 Jest を使うことにしました。 また、ソースコードのコンパイルとバンドルに swc を利用しているため、TypeScriptで書いたテストコードの変換に @swc/jest を利用することにしました。 Jest transformerとして、 @swc/jest を指定するのですが、swcの設定を jest.config.js に記述することができます。 bundleツールの spack(swcpack) と異なり、設定は全てJestの設定にまとめることが可能です。 また、 .swcrc がある場合、暗黙的に利用するため、使わないように設定すると、テスト用のswc設定をまとめられます。 /** @type {import('@jest/types').Config.InitialOptions} */ module.exports = { testEnvironment: 'jest-environment-jsdom' , transform: { '^.+ \\ .(t|j)sx?$' : [ '@swc/jest' , { swcrc: false , sourceMaps: true , jsc: { parser: { syntax: 'typescript' , tsx: true , } , transform: { hidden: { jest: true , } , react: { runtime: 'automatic' , } , optimizer: { globals: { vars: { ENTRYPOINT_FUNCTION_NAME: "'__payid%js%test__'" , } , } , } , } , } , } , ] , } , } ; パッケージ公開・管理 作ったライブラリを簡単に導入するためには、npm標準のパッケージ管理システムの仕組みで扱えると便利です。ソースコード管理にGitHubリポジトリを利用しているため、簡単に統合できる GitHub Packages を利用することにしました。 というのも、GitHub Packagesへの公開は、GitHub Tokenを利用したり、リポジトリの権限と可視性(public/private)を継承できるし、コミットのプッシュやデフォルトブランチのマージをトリガーに、GitHub Actionsを使って自動化が簡単にできます。 パッケージの公開の準備やリリースのような定型作業の自動化というのは、さまざまなツールが公開されています。 今回は、CHANGELOG、GitHubリリースの作成、バージョン番号の更新を自動化できる Release Please とそのGitHub Actionである Release Please Action を利用しました。 なお、パッケージの公開自体は、このツールで直接行うという訳ではなく、ツールで作成されたGitHubリリース作成をトリガーとして利用します。 Release Pleaseについて Release Please を使うと、以下の作業を自動化できます。 CHANGELOGの生成 GitHubリリースの作成(とそれに紐づくコミットへのGit Tagの作成) プロジェクトのバージョン番号更新 これらの作業をリリースPRと呼ばれるPull Requestを作成し、それをマージすることによってリリースが実行されます。 デフォルトブランチにマージされたら自動リリースするのと異なり、リリースPRを挟むことによって、事前に変更内容やリリースノートを確認でき、自分のタイミングでリリースすることができます。 また、PRにラベルをつけ、そのリリースがどの状態かを記録しています。 リリースPRは、 fix: 、 feat: といった Conventional Commits 対応したメッセージが含まれるコミットが、マージされると作成されます。 feat: エラーコードの追加 のようなメッセージで、コミットしマージすると、CHANGELOGへの変更内容の追記とバージョン番号の更新がされます。CHANGELOGの追記内容は、コミットメッセージから自動生成されます。また、バージョン番号は、たとえば、 feat: というプレフィックスを含むと、セマンティックバージョンのマイナーバージョンが、インクリメントされるという仕組みです。プレフィックスによって、どこがインクリメントされるかは異なります。 package.json { "name": "@baseinc/react-payid-js", - "version": "0.4.0", + "version": "0.5.0", "main": "dist/index.js", そして、PRをマージすると、GitHubリリースが作られるという仕組みです。 Release Please Actionを使ったパッケージ公開 前述のRelease Pleaseを用いてリリース作業を自動化するためのActionが、 Release Please Action です。 これを使うと、リリースPRとGitHubリリース作成を行う本来の処理の他に、GitHubリリース作成をトリガーにGitHub Packagesへの公開を行うことができます。 それには、Action実行後のoutputsを利用します。 release_created という変数にGitHubリリースを作成したかがboolean値で格納されています。 release_created=true であれば、GitHub Actionのjobで、コマンドを使って公開を行います。 name : release on : push : branches : - main jobs : release-please : runs-on : ubuntu-latest steps : - uses : google-github-actions/release-please-action@v3 id : release with : release-type : node - uses : actions/checkout@v2 if : ${{ steps.release.outputs.release_created }} - uses : actions/setup-node@v1 with : node-version : "16.x" if : ${{ steps.release.outputs.release_created }} - name : Resolve dependencies run : yarn install --frozen-lockfile if : ${{ steps.release.outputs.release_created }} - name : Configure git user run : | git config --global user.email ${{ github.actor }}@users.noreply.github.com git config --global user.name ${{ github.actor }} if : ${{ steps.release.outputs.release_created }} - name : Set GitHub packages env : GITHUB_TOKEN : ${{ secrets.GITHUB_TOKEN }} run : | npm config set //npm.pkg.github.com/:_authToken=$GITHUB_TOKEN if : ${{ steps.release.outputs.release_created }} - run : yarn publish env : NODE_AUTH_TOKEN : ${{ secrets.GITHUB_TOKEN }} if : ${{ steps.release.outputs.release_created }} 最後に、パッケージには、bundleファイルや型定義ファイルを含めたいため、 publish時に動作するnpm script を使って、bundleファイルと型定義ファイルの出力を実行します。 prepack というスクリプトで実行します。 // package.json { " name ": " @baseinc/react-payid-js ", " version ": " 0.1.0 ", " main ": " dist/index.js ", " types ": " dist/types/index.d.ts ", " publishConfig ": { " registry ": " https://npm.pkg.github.com/ " } , " files ": [ " dist ", " README.md ", " CHANGELOG.md " ] , " scripts ": { " bundle ": " spack ", " build ": " yarn bundle ", " type:declarations ": " tsc --emitDeclarationOnly --outDir dist/types ", " sync ": " concurrently yarn:build yarn:type:declarations ", " prepack ": " yarn clean && yarn sync ", " clean ": " rimraf dist " } , // [...snip...] } Actionにて、GitHub Packagesに公開されたパッケージは、リポジトリから参照することができます。 おわりに SWCとRelease Pleaseを利用したReact TypeScriptライブラリ開発と公開の事例を紹介しました。 アプリケーション上で新しい技術を導入するのに躊躇われるケースでも、特定の機能を別途ライブラリ化することによって、小さく始められるので、何かアプリケーションから分離できそうな機能がある場合は、検討してみてはいかかでしょうか?
Platformグループ でマネージャーをしている 松田( @tadamatu ) です。 この記事に書いてあること GitHub Actions を利用し 「OpenAPI の自動バージョニング」から「API Clientのnpmパッケージ生成」までを完全自動化 したのですが、その際に ハマったこと、工夫したこと が結構あったので、シェアしておきたいと思い書かせていただいた記事になります。 具体的には以下のような内容について書いてあります。 Branch protection rulesを維持した状態で、workflowからだけはcommitをさせたい(bypass機能を利用) → 文中の(3-2) 別ブランチの GitHub packages に npm publish したい(通常は何もしなければGitHub Actionsからは同じリポジトリのGitHub packagesにしか npm publish できない)→ 文中の(2) Branch protection rulesを維持した状態で、PRが生成されたときに Auto Merge されるようにしたい → 文中の(4) 特にbypass機能に関しては、 GitHub Community で出されていたリクエストが今年の5月に利用できるようになったようで、早速利用してみたものになります。 github.com github.blog そもそも何を解決したかったのか(目的) PlatformグループではBASE全体のリアーキテクチャを進めているのですが、その課題の1つに BackendとFrontendの分離 というものがあります。 現在はOpenAPIを採用しているのですが、 WebAPIスキーマの管理とClientの生成フロー を適切にしたいと言うのが目的でした。 ※これまではBackendとFrontendが同じリポジトリ内にあったため、リポジトリ内部でよしなに生成し融通を利かせて開発を行っていたのですが、物理的にリポジトリが別れるとそれが難しくなるためです。 最終的には以下のような構造とし、期待値は以下のようなものです。 スキーマ定義ファイル openapi.yaml はBackend側のリポジトリにて管理をする WebAPIスキーマ自体はFrontend・Backend開発者の合意で決定されるが、Backendの開発者が素案を作る文化が強いため、それに合わせてチューニングした仕組みとした (もちろんモックサーバをたててFrontend開発したりする場合もある) openapi.yaml を修正すると、GitHub Actionsを通してClientコードを生成しnpmパッケージとして自動リリースする フロントエンド開発者は npm install で簡単にClientコードを取得できる ほとんどはmasterへのpushトリガーで処理されるが、Clientコードが生成されるまでフロントエンド開発者が開発することができないため、手動で開発用パッケージを生成できるようにして解決している 今回はバージョンもワークフロー内で自動採番しており作業衝突が回避できる 「 openapi.yaml のバージョン番号」と 「 npm package のバージョン番号」を一致させる これによりBackendとFrontend が同じスキーマを利用していることが明確になり、Frontendもpackage.jsonを見るだけで判断できるようになる 全体フロー 全体フローはこのような感じになっています。 のマークの付いているところが、 マニュアル操作 の部分になります。 それ以外の部分は ワークフロー(GitHub Actions) により処理が行われます。 【開発中フェーズ】 (1) スキーマ定義ファイル openapi.yaml を実装して開発ブランチ へ push (2) 開発用のnpmパッケージ出力 → (a) Generate Client for Dev openapi-generetor により openapi-client (typescript-fetch) 生成 TypeScriptトランスパイル 開発用パッケージ公開 開発中はこの開発用パッケージを利用してFrontend側の開発を行います 【本番リリース】 本番リリースのため masterブランチへの merge(push) をトリガーにGithub Actionsが実行されたあとは、以下のような処理が全て自動で処理されます。 (3) 本番リリース(merge) → (b) Update Version (3-1) 新しいバージョン番号の発行( major / minor / patch のコントロール含む) (3-2) backendリポジトリ(selfリポジトリ) の openapi.yaml を新しいバージョン番号に更新しコミット (3-3) openapi-clientリポジトリ へPR生成 package.json を新しいバージョン番号へ更新 openapi-generetor により openapi-client (typescript-fetch) 生成 (4) (c) Auto merge (PR自動マージ) 生成されたPRの自動マージ (5) (d) Build & Publish package (正式版パッケージ公開処理) TypeScriptトランスパイル 正式版パッケージ公開 新しいパッケージが生成されたことをslackへ通知 以降に各処理を詳しく書いていきます。 注意) ・以降に出てくるコードは、ブログ用にコメントを追加し、適時編集したものです。ご了承ください。 ・BASEでは適切な単位で openapi.yaml が複数存在するのですが、以降はcartをサンプルにしたものですので、適時読み替えてください。 事前準備 GitHub Actions では通常、実行開始時に GITHUB_TOKEN が発行され、 ${{ secrets.GITHUB_TOKEN }} に保存されたTokenを利用して基本的な操作をすることが可能です。 自動トークン認証 - GitHub Docs しかし、リポジトリを跨いでActionしたり、今回のようにbypass機能を利用したい場合は、 GITHUB_TOKEN ではActionできません。 リポジトリを跨いだActionなどは PAT(Personal Access Token) でも可能です。 個人用アクセス トークンを管理する - GitHub Docs しかし、PATは個人に依存してしまいますので、組織配下で作成した GitHub Apps を利用し生成したToken を利用した方が良いでしょう。 また、今回のbypass機能もGitHub Appsでないと機能しません。 GitHub Appsを作成 では、GitHub Appsを作成して、インストールしていきます。 (a) GitHub Appsを作成 Organizationページ から [Settings] - [Developer settings] - [GitHub Apps] - [New GitHub Apps] と進み... 以下のように入力し、 [Create GitHub App] クリックで GitHub Apps を作成します。 # 今回の対応で変更が必要な部分だけ記載 GitHub App name : BASE openapi client generated App Homepage URL : https://binc.jp/ (適当で大丈夫) Webhook-Active : 不要なのでoff Contents=Access : Read and Write Pull requests=Access : Read and Write (b) 作成したAppページで以下の情報を拾う 作成したAppページに表示されている 【App ID】 [Generate a private key] 押下し private key を生成 その時にダウンロードされる 【pemファイル】 (c) 必要なリポジトリにインストール 作成したAppページの左メニュー [Install App] から、今回利用するリポジトリ backendリポジトリ , openapi-clientリポジトリ で有効になるようにインストールする (d) 各リポジトリページで Sercretsを入力 [Settings] - [Sercrets] - [Actions] と移動し、(b)で取得した 【App ID】 と 【pemファイル】の内容 を設定します。 この記事では以下のように設定し、workflowから利用しています。 OPENAPI_CLIENT_GENERATED_APP_ID : 【App ID】 OPENAPI_CLIENT_GENERATED_PRIVATE_KEY : 【pemファイル】の内容 (1) スキーマ定義ファイルを実装して開発ブランチへ push この章では赤枠の部分を説明しています まずはスキーマ定義ファイル openapi.yaml をローカルで実装します。 ここは言わずもがなだと思いますので省略しますが、API仕様に合わせて以下のようなyamlファイルを修正しGitHubへpushします。 openapi-generator/samples/yaml/pet.yml at master · OpenAPITools/openapi-generator · GitHub 1つ注意が必要なのは、 このタイミングで openapi.yaml のversionを実装時に変更しない ということです。 同時に別の開発者によってスキーマ修正が行われている可能性もありますし、hotfixで急遽変更になる場合もあります。 あくまで 本番デプロイ時にバージョンが自動的に割り当てする ことで、バージョン番号の調整にいちいちコミュニケーションを必要としなくてもよい、というメリットが得られます。 今回のフローでは (3-1) でバージョン番号が発行されます。 (2) 開発用のnpmパッケージ出力 この章では赤枠の部分を説明しています (1)でpushされた openapi.yaml をもとにFrontendで開発するためには、 pushされたスキーマ定義のWebAPIクライアントパッケージをFrontendにインストールする 必要があります。 今回の場合、パッケージを生成するためには、pushされたブランチを指定し 「 (a) Generate Client for Dev」 をマニュアル実行することで 開発用のnpmパッケージファイル が生成されます。 このときバージョン番号は更新されません。 npmパッケージにはbeta版やrc版といった 任意のプレリリース識別子 をつけることができます。 また、同じプレリリース識別子でも build番号 をその後ろにつけることができます。 例) ex. 1.2.0-rc.8 (識別子= rc , build番号= 8 ) config | npm Docs 今回はdev版として「識別子= commit short_sha ,build番号= 日時 」として、いつのどの時点のpublishしたものかを理解しやすくしました。 # 例: <version>-<short_sha>.<yyyymmddhhmm> 1.2.3-de0b012.202209050312 また、公開のコマンドでは npm publish --tag dev とし、latestバージョンが移動しないようにもしています。 workflowの中で、トランスパイルやパッケージ公開をしていますが、 package.json tsconfig.json の設定に対して特殊な設定はしていないので、今回は省略します。 #### (a)Generate-Client-for-Dev #### name : "OpenAPI: Generate Client For Dev" on : workflow_dispatch : jobs : generate-openapi-client : runs-on : ubuntu-latest steps : # selfリポジトリ以外の取得に利用するため GitHub Apps の Token を取得 - name : Generate apps token id : generate_token uses : tibdex/github-app-token@v1 with : app_id : ${{ secrets.OPENAPI_CLIENT_GENERATED_APP_ID }} private_key : ${{ secrets.OPENAPI_CLIENT_GENERATED_PRIVATE_KEY }} # backendリポジトリ(selfリポジトリ) のチェックアウト (openapi.yamlのありか) - name : Checkout Current Repogitory uses : actions/checkout@v2 # openapi-client リポジトリのチェックアウト (パッケージをpublishするリポジトリ) - name : Checkout Client Repogitory uses : actions/checkout@v2 with : token : ${{ steps.generate_token.outputs.token }} repository : baseinc/openapi-client path : client # nodeセットアップ - name : Setup node uses : actions/setup-node@v1 with : node-version : 16.x registry-url : https://npm.pkg.github.com # yqセットアップ (openapi.yamlの分析に利用) - name : Setup yq uses : chrisdickinson/setup-yq@latest # 開発用パッケージバージョン作成のための情報を取得 - name : Get vars id : vars run : | echo "::set-output name=api_version::`yq r './openapi.yaml' 'info.version'`" echo "::set-output name=sha_short::$(git rev-parse --short HEAD)" echo "::set-output name=dev_version::`date +" %Y%m%d%I%M"`" # https://openapi-generator.tech/docs/usage/#generate - name : Generate OpenAPI Client for typescript-fetch uses : openapi-generators/openapitools-generator-action@v1 with : generator : typescript-fetch generator-tag : latest command-args : | -i=openapi.yaml \ -o=client/packages/typescript-fetch/cart/src/ \ --generate-alias-as-model \ --enable-post-process-file # npm install と TypeScriptトランスパイル - name : install node_modules & build run : | cd client/packages/typescript-fetch/cart npm install npx tsc # NODE_AUTH_TOKEN には backend リポジトリの GITHUB_TOKEN を使用している # backend から openapi-client リポジトリの package へ `npm publish` することになるが、 # これをするには package setting より Manage Actions access の設定に backend を設定する必要がある - name : Publish npm package for DEV run : | VERSION=${{ steps.vars.outputs.api_version }} cd client/packages/typescript-fetch/${{ github.event.inputs.target }} npm version ${VERSION}-${{ steps.vars.outputs.sha_short }}.${{ steps.vars.outputs.dev_version }} npm publish --tag dev env : NODE_AUTH_TOKEN : ${{ secrets.GITHUB_TOKEN }} ここでのはまりポイントとしては、BASEではpackageの管理をGitHub pakages で行っているのですが、 実行されるworkflowがbakendリポジトリ なのに対し、 publish先が openapi-clientリポジトリ となっており、通常は単純にpublishはできません。(これはTokenに PAT や GitHub Apps を利用してもダメでした) これをするためには、 GitHub pakages のリポジトリ (今回の場合はopenapi-clientリポジトリ)に、 Manage Actions access という設定がありますので、そこで backendリポジトリ からの書き込みを許可します。 こうすることで、 backendリポジトリの GITHUB_TOKEN で publishすることが可能 になります。 (3) 本番リリース この章では赤枠の部分を説明しています openapi.yaml が確定し、開発が完了すると本番にリリース(masterブランチにmerge)をします。 merge(pushトリガー)により 「 (b) Update Version」 が実行されて (3-1)(3-2)(3-3) が処理されます。 (3-1) 新しいバージョン番号の発行 この章では赤枠の部分を説明しています ここでは 新しいバージョン番号の発行 を行います。 特別な処理がなければ、 通常はpatchバージョンを更新 します。 minor, major バージョンを更新 するためには、 merge対象のcommit comment のprefix に [openapi/major] [openapi/minor] を入れておく と該当のバージョンを更新してくれます。 複数ある場合は、一番直近のprefixが有効になります。 #### (b)Update-Version-<1> #### name : "OpenAPI: [Cart] Update Version" on : push : branches : - master paths : - "openapi.yaml" # 複数回連続実行された場合、直列に実行されて、最後のワークフローが有効になるようにconcurrencyを設定 concurrency : ${{ github.workflow }} jobs : update-openapi-version-cart : runs-on : ubuntu-latest # selfリポジトリ に対してコミットを追加するため、対象のコミットコメントの場合、workflowは無視する if : ${{ !startsWith(github.event.head_commit.message , 'chore(release):' ) }} steps : # backendリポジトリをチェックアウト - name : Checkout Current Repogitory uses : actions/checkout@v2 # nodeセットアップ - name : Setup node uses : actions/setup-node@v1 with : node-version : 16.x registry-url : https://npm.pkg.github.com # yqセットアップ (openapi.yamlの分析に利用) - name : Setup yq uses : chrisdickinson/setup-yq@latest # 現在のバージョン番号を取得(例. current_version = 1.2.3) - name : Get vars id : vars run : | echo "::set-output name=current_version::`yq r openapi.yaml 'info.version'`" # 基本はpatchバージョンが更新されます # commit comment を遡り、prefix に特定の文字列があれば minor や major バージョンを更新 - name : Get version type uses : actions/github-script@v6 id : version-type with : script : | const commits = ${{ toJSON(github.event.commits) }} for (const commit of commits.reverse()) { if (commit.message.startsWith(' [ openapi/major ] ')) return ' major' if (commit.message.startsWith(' [ openapi/minor ] ')) return ' minor' if (commit.message.startsWith(' [ openapi/patch ] ')) return ' patch' } return 'patch' result-encoding : string # patchバージョンを更新(例. 1.2.3 → 1.2.4) - name : Version Up (patch) if : steps.version-type.outputs.result == 'patch' run : | VERSION=${{ steps.vars.outputs.current_version }} && a=( ${VERSION//./ } ) && a[ 2 ]=$((a[ 2 ] + 1)) echo "new_version=${a[0]}.${a[1]}.${a[2]}" >> $GITHUB_ENV # minorバージョンを更新(例. 1.2.3 → 1.3.0) - name : Version Up (minor) if : steps.version-type.outputs.result == 'minor' run : | VERSION=${{ steps.vars.outputs.current_version }} && a=( ${VERSION//./ } ) && a[ 1 ]=$((a[ 1 ] + 1)) echo "new_version=${a[0]}.${a[1]}.0" >> $GITHUB_ENV # majorバージョンを更新(例. 1.2.3 → 2.0.0) - name : Version Up (major) if : steps.version-type.outputs.result == 'major' run : | VERSION=${{ steps.vars.outputs.current_version }} && a=( ${VERSION//./ } ) && a[ 0 ]=$((a[ 0 ] + 1)) echo "new_version=${a[0]}.0.0" >> $GITHUB_ENV # (b)Update-Version-<2> に続く ==> (3-2) backendブランチ(selfブランチ)の openapi.yaml を新しいバージョン番号に更新しコミット(bypass機能を利用) この章では赤枠の部分を説明しています (3-1)で発行されたバージョン番号を、 backendブランチ(selfブランチ)の openapi.yaml へ更新する(書き戻す) 必要があります。 しかし、 ここで問題になるのは Branch protection です。「masterブランチへの直接push禁止」「1人以上Approveしていないとマージできない」などといったあれです。 workflowで何も気にせずにコミットをしても、通常はこのBranch protectionに弾かれてエラーになってしまいます。 しかし bypass機能 を利用することで、 特定のGitHub Apps からの命令をbypassして実行させる ことができます。(つまり、Branch protectionをスルーしてコミットができるようになります) 設定は簡単で、GitHub Apps を設定してあれば、bypassさせたいリポジトリ(今回の場合はbackendリポジトリ)に設定するだけです。 [Settings] - [Branchs] - [Branch protection rules] と進み、 Protect matching branches 内の Allow specified actors to bypass required pull requests をONにし、該当する GitHub Apps を追加します。 これにより、 人による誤操作はBranch protectionによって守りつつ、該当する GitHub Apps のワークフローでだけ実行したいアクションができる ようになりました。 以下のワークフローで実際に利用しています。 #### (b)Update-Version-<2> #### # ==> (b)Update-Version-<1> の続き # openapi.yaml のバージョンを上書きする - name : OpenAPI Versioning run : | sed -i -e "s/version: ${{ steps.vars.outputs.current_version }}/version: ${{ env.new_version }}/" openapi.yaml # GitHub Apps は branch protection を bypass する設定することで、commit を master に直pushすることができる # まずは GitHub Apps のtoken を取得 - name : Generate apps token id : generate_token uses : tibdex/github-app-token@v1 with : app_id : ${{ secrets.OPENAPI_CLIENT_GENERATED_APP_ID }} private_key : ${{ secrets.OPENAPI_CLIENT_GENERATED_PRIVATE_KEY }} # 次に backendリポジトリ(selfリポジトリ) へ commit - name : Commit New Version to baseinc/backend repo uses : stefanzweifel/git-auto-commit-action@v4 with : commit_message : "chore(release): openapi.yaml update version from ${{ steps.vars.outputs.current_version }} to ${{ env.new_version }}" github-token : ${{ steps.generate_token.outputs.token }} # (b)Update-Version-<3> に続く ==> (3-3) openapi-clientリポジトリへPR自動生成 この章では赤枠の部分を説明しています 以下のワークフローでは、 openapi-clientリポジトリに対し、新しいスキーマ定義のクライアントのPRを自動生成 します。 同時に、(3-1)で発行されたバージョン番号を、packageにも反映させています。 #### (b)Update-Version-<3> #### # ==> (b)Update-Version-<2> の続き # PR生成のために、openapi-client をチェックアウト - name : Checkout Client Repogitory uses : actions/checkout@v2 with : token : ${{ steps.generate_token.outputs.token }} repository : baseinc/openapi-client path : client # https://openapi-generator.tech/docs/usage/#generate - name : OpenAPI Generate typescript-fetch uses : openapi-generators/openapitools-generator-action@v1 with : generator : typescript-fetch generator-tag : latest command-args : | -i=openapi.yaml \ -o=client/packages/typescript-fetch/cart/src/ \ --generate-alias-as-model \ --enable-post-process-file # openapi.yaml で更新したバージョン番号と同じものを、package.json に対して適用する - name : Update release version run : | cd client/packages/typescript-fetch/cart npm version ${{ env.new_version }} # PRを生成する # [automerge] というprefixをcommit commentにつけることで、次の工程(4)で自動マージが発火する - name : Create pull request to baseinc/openapi-client repo uses : peter-evans/create-pull-request@v4 with : token : ${{ steps.generate_token.outputs.token }} path : client delete-branch : true commit-message : "chore(release): npm package update version from ${{ steps.vars.outputs.current_version }} to ${{ env.new_version }}" title : "[automerge] chore(release): npm package update version from ${{ steps.vars.outputs.current_version }} to ${{ env.new_version }}" body : | openapi-typescript-fetch-cart : ${{ env.new_version }} branch : chore/openapi-client-cart branch-suffix : short-commit-hash # 同じプルリクは作らない (4) PRの作成によりAuto Mergeされる この章では赤枠の部分を説明しています openapi-clientリポジトリ側でPRが生成されたら、以下のworkflowが発火します。 このworkflowでは [automerge] というprefix がついているときだけ、自動マージが行われるようになっています。 AutoMerge には、GitHubのAutoMerge機能を利用しています。 AutoMerge機能 は [Settings] - [General] の Allow auto-merge をONにすることで、利用ができます。 これを利用する利点は、全てのcheckが通った時にだけ発火することです。たとえば万が一CIでエラーになれば、MergeされずにPRは残ることになります。 Branch protection には「マージするには1人以上のApproveが必要」が設定してあり、workflow内でApproveをするようにしてあります。 これにより、 人による誤操作はBranch protectionによって守りつつ、workflowによる自動マージだけが動作する ようにしてあります。 #### (c)Auto-Merge #### name : Auto Merge on : pull_request : types : - opened branches : - main jobs : release : name : Auto Merge runs-on : ubuntu-latest # `[automerge]` というprefixがある場合にだけこの workflow が発動する if : ${{ startsWith(github.event.pull_request.title, '[automerge]' ) }} steps : # GitHub Apps により該当PRの GitHub AutoMerge を ON にする - name : Generate apps token id : generate_token uses : tibdex/github-app-token@v1 with : app_id : ${{ secrets.OPENAPI_CLIENT_GENERATED_APP_ID }} private_key : ${{ secrets.OPENAPI_CLIENT_GENERATED_PRIVATE_KEY }} - name : Enable Pull Request Automerge uses : peter-evans/enable-pull-request-automerge@v2 with : token : ${{ steps.generate_token.outputs.token }} pull-request-number : ${{ github.event.number }} # AutoMerge 条件(つまり branch protection)は「1人以上のApproveが必要」であるため、 # Approve する → これにより GitHub Auto Merge が発動 - name : Approve uses : hmarr/auto-approve-action@v2 with : pull-request-number : ${{ github.event.number }} (5) openapi-clientを生成し、npmパッケージを生成 この章では赤枠の部分を説明しています 最後の工程として、(4)で自動マージされると、以下のworkflowがpushトリガーにより発火します。 ここでは、 TypeScriptトランスパイルをした後、パッケージとして公開する といった作業をしているだけになります。 #### (d)Build-&-Publish-package #### name : Publish packages on : push : branches : - main jobs : release : name : Build and Publish package runs-on : ubuntu-latest steps : # openapi-clientリポジトリ をチェックアウト - name : checkout uses : actions/checkout@v2 # nodeセットアップ - name : setup node uses : actions/setup-node@v1 with : node-version : 16.x registry-url : https://npm.pkg.github.com # npm install と TypeScriptトランスパイルをした後、パッケージとして公開 - name : build & publish package (cart) id : publish_package_cart run : | cd packages/typescript-fetch/cart npm install npx tsc npm publish ./ echo ::set-output name=version::$(npm version --json | jq '."@baseinc/openapi-typescript-fetch-cart"' ) env : NODE_AUTH_TOKEN : ${{ secrets.GITHUB_TOKEN }} # 生成完了をslackに通知 - name : Success Slack Notification if : contains(steps.changed-files.outputs.all_changed_files, 'packages/typescript-fetch/cart/package.json' ) uses : rtCamp/action-slack-notify@v2 env : SLACK_MESSAGE : "新しい openapi-typescript-fetch-cart: ${{ steps.publish_package_cart.outputs.version }} が生成されました" SLACK_TITLE : "openapi-client 生成通知" SLACK_USERNAME : "openapi-client-generated-bot" SLACK_WEBHOOK : ${{ secrets.SLACK_WEBHOOK_CART }} まとめ 今回は、アーキテクチャ変更に合わせて、 OpenAPI Client 生成の自動化した話 を、ワークフローを中心に書いてきました。 GitHub Actions は本当に良くできている と思います。 こんなことできないかなと Marketplace を検索してみると、大体のものは見つかったりします。 これからも 作業効率を上げるため に、また 人による誤操作をできるだけ減らす 意味も含め、GitHub Actions を使い倒していきたいと考えています。 長文を最後までお読みいただき、ありがとうございました。 最後に 私が所属する Platformグループ は、今年の7月に新設されたばかりのグループで、 仲間を絶賛募集中です! Platformグループでは、 「オーナーやカスタマーの良い体験、エンジニアの開発体験をプラットフォームで守る」というビジョン のもと、リアーキテクチャをメインに、作業効率化やパフォーマンス改善、組織との親和性について考えるといった作業まで、様々な改善を行っています。 もしご興味あれば、カジュアル面談も実施しておりますので、ぜひお気軽にお問い合わせください。 open.talentio.com open.talentio.com
基盤チームの右京です。 昨今はフロントエンドのアプリケーションもリッチになり、ブラウザ上で実行されるコードが行うことの範囲も増えてきました。一方で多くのことを実装できてしまうのはリスクでもあり、BASE でも問題となることがあります。 その中でも「開発環境の URL」や「デバッグ機能の存在」ような環境毎に異なる情報は、特に意図せずに漏れやすいものだと考えています。これらはコードを記述する際に、実装方法を知識として知っていればその多くが回避可能です。この記事ではその実装例を解説しています。 コードから漏れる情報 例えば、次のようなコードがあるとします。 function debug() { // 開発環境の host であればデバッグ機能を有効にする return location .host === 'dev.example.com' ; } なんの変哲もないようなコードに見えますが、ブラウザ上で実行されるコードとしては非常に良くないものだと言えます。通常 JavaScript をブラウザで実行する場合、ソースコードが直接取得されるため、その内容は利用者が閲覧することできます。つまり「秘匿情報の開発環境の URL が、比較的誰でも簡単に入手できるものに記載されてしまっている」ということになります。 Minifier や Compressor を用いて難読化を行ったとしても、(このままでは)この URL の情報が消えることはありません。 location.host という変数と比較をするため、 "dev.example.com" は実行時まで必要なコードだからです。 防ぐにはどうするか? 殆どの場合は次のうちいずれかの方法で解決できるでしょう。 情報をサーバーサイドのプログラムから受け取る 環境毎にビルドで使用する定数を入れ替える ビルドフェーズで不要になるコードを確定して削除する 前者の 2 つは馴染みのある方法で、最後の 1 つはフロントエンド特有のものです。 1. 情報をサーバーサイドのプログラムから受け取る HTML を配信するサーバーサイドのプログラムがある場合、環境による変化はそちらでコントロールします。今回の例では、デバッグ機能を利用したい場合にそのフラグを input で埋め込むような形にします。 < input type = "hidden" id = "debug" value = "1" > JavsScript からはこの値を取得してデバッグ機能を有効にするかを決定しますが、このままではデバッグ機能自体は残ってしまうので、API のエンドポイント URL などの単純に値を切り替えたいものに向いているでしょう。 2. 環境毎にビルドで使用する定数を入れ替える SPA などで HTML が静的な場合は、ビルド時に 1. と同等の置き換えを行うと良いでしょう。できることは 1. と同様なため、デバッグ機能そのものはコードとして残ってしまうことは変わりません。 環境毎に API の URL が変更されるこのようなコードがあるとします。 function baseURL() { return API_BASE_URL; } fetch( ` ${baseURL()} /api` ) 今回は esbuild の CLI を用いてこれをビルドします。esbuild には define オプションがあるのでこれを使って環境毎の値をコードへ埋め込みます。webpack を使用している場合は DefinePlugin など、それぞれに対応したものがあるはずです。 // $ esbuild index.js --define:API_BASE_URL=\"http://dev.example.com\" function baseURL() { return "http://dev.example.com" ; } fetch( ` ${baseURL()} /api` ); 環境によって define に渡す値を変更してビルドすることで、最終的なコードには開発環境の情報が残らなくなりました。 3. ビルドフェーズで不要になるコードを確定して削除する これは、2. の define を利用して意図的にデッドコードを生みだすことで、Minifier や Compressor でそのコードを削除する方法です。この方法はデバッグ機能そのものをコードから削除できます。 説明を簡単にするため、次のコードを例にしていきます。 function test() { if (APP_ENV === 'production' ) { return 'production' } else { return 'development' } } console.log(test()) APP_ENV はビルド時に決定される値で、それが置き換えられます。production を設定してビルドした場合 esbuild ではこのようになります。 // $ yarn esbuild index.js --define:APP_ENV=\"production\" function test() { if ( true ) { return "production" ; } else { return "development" ; } } console.log(test()); APP_ENV が "production" === "production" となり、 true となることが確実なのでそれに置き換わり else 側が完全なデッドコードとなりました。これに --minify オプションを追加すると、デッドコードが削除されます。 // $ yarn esbuild index.js --define:APP_ENV=\"production\" --minify function test() { return "production" } console.log(test()); 逆に APP_ENV が production 以外なら else 側だけが残ります。 // $ yarn esbuild index.js --define:APP_ENV=\"development\" --minify function test() { return "development" } console.log(test()); このようにしておくと else 側には安全に開発環境向けのデバッグ機能を記述できます。この方法は terser の README.md でも紹介されています 。 まとめ 情報を守るためのコードの記述方法や、ビルドツールの機能について紹介してきました。ここには書いていませんが、例えば CI で特定の URL を記述を検出するなども有効な手段の 1 つでしょう。フロントエンドのコードは「すべて公開状態」なことを常に忘れずコードを記述することを心がけましょう。
こんにちは。バックエンドエンジニアの髙嶋です。 今回は、私が BASE に転職するに至った動機の一つでもある「開発力をあげたい」ということについて、実際入社してどうだったかを半年が経過した今、あくまでも私個人の経験としてお話しさせていただこうと思います。 結論から言うと「開発力」が向上した実感は確かにあり、 DDD や TDD、あるいはスクラムといったものを使っていこうという機運が開発組織的にもあった ただしやり方がガチガチに決まっているということはなく、チームごとに合った方法を考えていく余白が十分にあった それをするための Be Hopeful(楽観的)で Move Fast(速く動く)かつ Speak Openly(率直)であるという、会社としての風土が自分に合っていた といったことが振り返ってみると挙げられると思っています。 先に結論を言ってしまったので、ここからは少し時間を巻き戻しながら順にお話ししていこうと思います。 なかなか一般化できるような内容ではありませんが、一つの参考として読んでいただけると幸いです。 そもそも開発力とは 「開発力」のまま話を進めるとスコープが広すぎるので、今回の話における定義を決めておこうと思います。 一言でいうと、「設計力」となります。 そして「設計」という言葉自体も非常に様々な捉えられ方をする、広い意味を持つ単語です。 そのためここでは 企画担当から提示されたやりたいこと(要件)の解像度を高め どう既存プロダクトの中に落とし込むかあらゆる選択肢を洗い出し その中から様々な要素を加味して現状における最適解と思われるものを一つ選び それをどのようにユーザーに見える形で実現するかを考え、実行する力 と表現しておきます。 なぜ開発力もとい設計力をあげたいと思ったか 前提として、私の転職理由をお話ししておかなければなりません。 前職では Web アプリケーションを開発するチームのグループマネージャーを任せていただいており、それ自体は非常によい経験ではありました。 一方で、エンジニアとして自信を持てるほどの知識や経験がないまま、その時の状況もあって役職に就かせていただいたという背景がありました。 その結果、いわゆるマネジメント業務に割く時間が多くなっていく中で、チームメンバーから技術的なアドバイスを求められても、引き出しが少ないがために確固たる根拠を持って助言できない自分に対してもどかしさを感じていました。 そういった背景もあり、一度環境を変えて技術的な幅を広げる経験を積みたいと思った際に、ご縁があって BASE に入社させていただく運びとなりました。 入社して何をやっているか ネットショップ作成サービス BASE の、ショップオーナー様向けの機能開発に主に取り組んでいます。 特にショップ開設直後、スムーズにショップ公開まで行うことができるよう、そのオンボーディングを手助けする領域を担当させていただくこととなりましたが これまで会社としても抜本的な施策をそれほど多くうってこなかったドメインである 歴史があり、技術的負債も少なくない 多くの機能に影響しうるため、開発上の影響範囲が広いというだけではなく関係各所との連携も必要になる という背景もあり、自分にとってもチャレンジングで面白みの感じられる業務と思っています。 加えてプロジェクトチーム自体も新たに発足したこともあって、そういった意味でも一から作り上げていくようなわくわく感がありました。 一方で開発の進め方にフォーカスすると、 プロダクト開発に取り組む、プロジェクトチームとしてのフレームワークとしては「スクラム」 いわゆるソフトウェア設計手法としては「DDD(ドメイン駆動設計)」 を採用しました。 これは既に社内でも活用事例があり、参考にしやすかったことが前提としてはあります。 そして自身としても今までやってみたいとは思いつつも、業務の中でしっかりと取り組んだ経験がなかったものなので非常に前のめりではありました。 冒頭でも触れましたが特に新参者の自分にとっては、オープンにコミュニケーションをとり、前向きにどんどん試していこうという雰囲気がベースにあったことは大きかったと思います。 新しいチームで新しいことに取り組むことのハードルがぐっと下がり、その結果様々な知識を吸収し、価値のある経験を多く積むことができていると感じています。 どんな成長があったか しかしながら、もちろん全てが最初から順調に進んだわけではありません。 特に印象的だったことがこの2つです。 抜本的に画面構成やユーザー導線を見直すような施策を打とうとしたが、レガシーなアプリケーションの作りが制約となり、リファクタリングから始めざるをえなかった スクラムで開発に取り組もうとしたものの、チームメンバー内の認識がバラバラで円滑に進まなかった 以下、それぞれについて簡単にその経緯を説明します。 まず1つ目のリファクタリング対応が必要になったことについて。 これは本当に想定外で、蓋を開けてみたら判明したことではありました。 プロダクトオーナーから「こんなことがやりたい」という企画があがってきて、最初のうちは開発チームとしても「多少大変だろうがまあできるだろう」という感覚ではありました。 ただ実際にソースコードを見てみると、現状の構成のままでは実現自体が難しい、あるいは実現できたとしても相当な工数がかかり複雑な対応になってしまう、ゆえに不具合につながる可能性も高まるといったところです。 そのためリファクタリングすることに関しての合意形成はそこまで難しくありませんでしたが、当然ながら無尽蔵にコストや期間をかけられるわけではありません。 どこまでやるかのスコープを明確にし、そのうえでボトルネックとなっている問題を解決できる設計方針を練ることには非常に頭を悩ませました。 やったことの全てを挙げるのは難しいですが、 Web API の構成見直し(OpenAPI を利用しており、その開発体験については こちらの記事 も参照ください) それに伴い、MVC 構成でバックエンド側に偏っていた処理の一部をフロントエンド側に委譲 ユニットテストが十分になかったこともあり、責務を適切に分解してテスタブルなコードに修正 といったことに取り組み、そのリリースを経て施策が打ちやすい状態にすることができました。 リファクタリングという性質上、いわゆる目に見えるような形での成果はなかなかありません。 ただ今後の改修を安心かつ高速に実施できるようになったことは非常に重要であり、エンジニアとしても一回り成長できた、非常に有意義な体験となりました。 そして2つ目、スクラムでの開発がうまくいかなかったことについて。 こちらについてはやり方うんぬんの問題はありつつも、結論としては最初にチームメンバーでスクラムに対する認識を揃えておけなかったことが一番の問題であったと思います。 まず起こっていたことしては、デイリースクラムやスプリントプランニング、ふり返り会(スプリントレトロスペクティブ)といったイベントについてもある程度形式に沿ってやってはみたものの、どこか進め方や生み出されるアウトプットがしっくりこない、結果として「なんかやりにくいなあ」といった状況が続いていました。 そしてスクラムマスターがいなかったこともあり、その軌道修正を図る動きも起こりづらくなっていたという背景もあったかと思います。 どう改善を試みたかというとトリガーとなったのはふり返り会で、上述したような「スクラムにおける動き方についてそれぞれの頭の中に正解があり、そもそもそのすり合わせができていない」といった課題が挙がったことでした。 そういった意味では、ふり返り会が一定機能していたことが最後の砦となり、そこから改善の動きにつながったことは重要なターニングポイントであったと思います。 その後の流れとしては以下のようなことをやりました。 対象図書を決めて読書会を開き、チームとしての認識を揃える 仕事の進め方やチームとしての考え方をドキュメントに明示し、各イベントの進行方法などについてもフォーマットを固める 少しでも疑問に思うようなことがあれば、タイムリーに相談して話し合う 話し合った結果変わったことがあれば、それをすぐドキュメントなどに反映し、実際に試してみる そして少しずつ改善を重ねながら、以前と比べるとチームとして同じ方向を向いてメンバーが自立してスムーズに動けるようになったことで、アウトプットの質と量の向上にもつながってきていると感じます。 ただ形式どおりにやるのではなく、自分たちのチーム状況にあったやり方を都度考えていく中で悩み抜いたことが、確かな財産となって今があるんだろうなあと実感しています。 いわゆる個に閉じた開発スキルといったものとは少し性質が異なりますが、チームとして開発を進めるという点において、これも非常に大きな学びとなりました。 おわりに とは言え、全てを自分たちで一から解決しなければならないということは決してなく、困ったことがあれば先輩社員が気軽に相談に乗ってくれたり、壁打ち相手になってくれたりといったことも支えになりました。 また Slack におけるコミュニケーションも活発であり、スクラムについてはアジャイル開発に関するあれこれを相談できる #iikanji-agile といったチャンネルでのアドバイスも参考になりました。 そういった中で失敗も成功も経験し、開発に取り組む際に半年前の自分では絶対に出てこなかったであろう選択肢が自然と出てくるようになったときに、少しは力がついてきたんじゃないかな?という自信が生まれてきていると今では感じます。 まだまだ貪欲にインプットとアウトプットを繰り返していく必要はありますが、 新しいフレームワークや開発手法に取り組むことでエンジニアとしての幅を広げることができた その前提として、本記事で挙げたものに限らず新しいことに取り組むことへの理解や活発なコミュニケーションがあった ということを、結びの言葉とさせていただきます。 BASE では、このように一緒に試行錯誤しながら、会社としても個人としても成長できる環境で、一緒にサービスを発展させていく仲間を募集しております。 カジュアル面談も実施しておりますので、ぜひお気軽にお問い合わせください。 binc.jp
PHPカンファレンス沖縄2022のOGP画像 この度は、8/27(土)に開催された PHP カンファレンス沖縄 2022 にゴールドスポンサーとして協賛し、また 3 名のメンバーが登壇しました。 登壇者 3 名からオンラインで参加した感想や発表内容の補足など、PHP カンファレンス沖縄 の参加レポートをお届けします! 発表内容と補足 プログラミングをするパンダ( @Panda_Program ) セッション内容について @Panda_Program です。「実践!ユニットテスト入門」というタイトルで、テストを書いたことがない方向けの発表をしました。 テストを書いたことがない方が、テストを書いてみようと思うことを狙いとしています。経験ゼロから入門者・初級者になれるような内容となっています。 これは @hgsgtk さんのスライド「『質』の良いユニットテストを書くためのプラクティス」がテストの初級者を中級者にすることを目的として作られているため、それならば自分はテスト経験ゼロの人が初級者になるために必要な知識を得られる資料を作ろうと思ったことがきっかけでした。 このような温かいコメントを頂いたので、狙いは一定程度成功したかなと思っております。 登壇おつかれさまでした! こちらこそ有難うございました🙏🙏 まさに入門者にもとても分かりやすかったです! — haruna akashi (@harutp1001) 2022年8月27日 上記の2つの資料でユニットテストを書いてみて、次に質の良いユニットテストを書くコツを知った後、ぜひTDDを試してみてもらえたら嬉しいですね。 panda-program.com そして ATDD へ...!開発者にとってもテストの世界は奥が深いですね。 イベントの感想 今回はオンライン参加でした。事前に登壇動画を収録するのは初めてでしたが、現地に行きたい気持ちと登壇は録画だと楽だな〜という気持ちがせめぎ合っています(笑)。 Twitter で感想を貰い、Discord では質問や議論をするというようにツールによって役割が分かれていたように思いました。反応を頂けることは嬉しいですし、自分としても別の方の登壇内容に Discord で反応できたのは Twitter でのやりとりより身近に感じてよかったです。 また、めもりー( @m3m0r7 ) さん、ひさてるさん( @tanakahisateru )と Twitter / Discord で会話する、 @tadsan さんとPHPer Roomで雑談をしてコードゴルフを実演してもらう、 @mpyw さんにリプを飛ばす、ゆうきゃん( @ucan_lab )さんのお顔を拝見するという実績を解除しました!次はどこかで @mpyw さんに感謝を伝えたいです。私はあなたの記事を読んで育ちました。 PHP カンファレンス沖縄では PHP の話題に限らず、DevOps やスクラムといった開発の話、HTTP を見直す話であったり Laravel のコードリーディングといった技術の話などとても勉強になりました。話題が広いのは PHP カンファレンス沖縄のいいところですね。 来年はぜひ現地で参加したいです! 02 ( @cocoeyes02 ) セッション内容について はいさい!02です! 今回は以前記事に書いた「 登壇するぞ!って決めてからトークするまでの流れ - BASEプロダクトチームブログ 」をベースにしつつ、マイナーアップデートをした内容になりました。 今回の内容は、自身の登壇の話や登壇応援活動をしている中でFBした内容をまとめた内容になります。登壇応援活動については以下の記事も読んでみてください。 devblog.thebase.in また「プロポーザルやトークをレビューして欲しいけど、レビューしてくれる人がいないよ!」という方は、ぜひ僕に声をかけてみてください!微力ながらお力添えできればと思っています! ご静聴ありがとうございました〜 ちなみに社内に限らず、登壇前にプロポーザルやトークをレビューして欲しい!と言う場合はいつでもレビューしますので、是非メンションツイートやDMへどうぞ〜 #phpcon_okinawa #track_b — 02 (@cocoeyes02) 2022年8月27日 イベントの様子について 今年も興味深い発表が多くて良かったです! 去年と同じくオンラインでの参加になったのですが、オフライン会場の様子を見るととても楽しそうで行きたかったな〜〜〜と強く思いました! 沖縄会場の様子 #phpcon_okinawa pic.twitter.com/07QeX3u1er — カンボ🏝沖縄 (@kanbo0605) 2022年8月27日 来年もプロポーザルを応募をして、現地で登壇を目指したいと思います! キュアセブン ( @cureseven ) セッション内容について 今回はゴールドスポンサーセッションとして、「Gather × Code With Me × ペアプロのお誘い で最高です」というテーマのLTをさせていただきました。 使ったスライドはこちらです。 リモート下での開発を成功させるために、私のチームで3つのツールを使いながらコミュニケーションしている様子とその方法についてお伝えさせていただきました。 DiscordやTwitterの実況を通して、Gather使ってるよ!の声や、タスクの重さでどう取り組むか選択できる良さへいいねの声をいただきました。 また、タイトルにも入れた「最高です」の言葉でチームの雰囲気についても感じてもらえて嬉しかったです。 鍵は振り返りだと思います。気になっていることややってみたいことをチームメンバーにオープンにしながら、よりよい開発体験をチームで作っていけると良いと思います。 私たちのリモート開発はまだまだ続きます。更にいい開発ができるように頑張ります! 最後に 今回計 3 名のメンバーが登壇する機会をいただき、とても充実した時間を過ごすことができました! また多くの発表を通して様々な知識にふれることができ、各々が新たな知見や視点を持ち帰って来れたと考えております。 それもひとえに PHP カンファレンス沖縄実行委員会の皆様のおかげです。心より感謝申し上げます。 それでは、来年もまた皆様にお会いできることを楽しみにしております!
基盤チームの右京です。 BASE ではショップのデザインを誰でも簡単にできるような、いわゆるノーコードな機能を提供しています。 デザイン | BASE (ベイス) ネットショップを無料で簡単に作成 この記事では、そんなノーコードなシステムの裏側について、簡単にですが解説しています。 ショップページ配信の基盤システム ノーコードの前にまず BASE のショップページ(ShopFront と呼んでいます)がどのようなシステムかを知っておく必要があります。BASE のショップページは特定の URL にアクセスすると必要なデータをデータベースから取得し、テンプレートエンジンを使ってサーバーのプログラムで HTML を生成して返す、よくある伝統的な Web ページとして実装されています。ただし、ショップ毎にページのデザインは全く異なるため、1 つの固定のテンプレートを利用しているわけではなく、ショップ毎に動的に切り替えるような仕組みになっています。 このショップ毎のテンプレートを作る手段の 1 つが今回紹介するノーコードでのデザイン編集機能になります。ノーコードの他にも HTML に近い形式でテンプレートが記述できる仕組みもあり、現在ではそのどちらかを選択できるような形になっています。いずれかの方法で生成された何かしらのデータは「変換エンジン」を通して、ShopFront で実行可能なテンプレートとブラウザから呼び出されるアセット類に変換されます。 複数の形式から実行可能なテンプレートを生成する「変換エンジン」 「変換エンジン」はショップの管理画面と ShopFront をつなぐ中核的な存在で、例えば BASE Template として記述されたショップページのテンプレートを ShopFront が実行可能な形式へ変換します。ShopFront 自体はここまでにあったようにテンプレート自体は持っておらず、テンプレートのレンダリングの際に使用される変数の生成のみを行っています。この変数は JSON で表すと以下のようになっており、URL 毎に共通部分以外の内容が異なります。 { // この URL で表示しているページ種別の識別子 "page": "item", // どの URL でも使用できる、ショップの基本的な情報 "shop": { "name": "テストショップ", "logo": "https://...", "description": "テストで作成したショップです。" }, // 商品ページの URL で使用できる、その URL の示す商品の詳細な情報 "item": { "title": "テスト商品", "image": "https://...", "stock": 10, "price": 500 } , // ... 他にも多くの変数が存在する } ShopFront で使用できるテンプレートエンジンは Twig に似た形式で、社内では Cot と呼んでいます。一方で BASE Template は独自の記法を採用しているため、ShopFront で実行するには Cot の形式へ変換する必要があります。そこで、次のような変換行うのがこの変換エンジンです。 BASE Template 変換後(Cot) < html > {LogoTag} {block:ItemPage} < h2 > {ItemTitle} </ h2 > < img src = "{ItemImage0URL-origin}" > < p > ¥ {ItemPrice} </ p > < p > 残り {ItemStock} 個 < p > {/block:ItemPage} {block:AboutPage} < p > {ShopIntroduction} </ p > {/block:AboutPage} </ html > < html > < img src = "{{ shop.logo }}" > {% if page == 'item' %} < h2 > {{ item.title }} </ h2 > < img src = "{{ item.image }}" > < p > ¥ {{ item.price }} </ p > < p > 残り {{ shop.stock }} 個 < p > {% endif %} {% if page == 'about' %} < p > {{ shop.description }} </ p > {% endif %} </ html > これによって BASE Template という形式から、ShopFront で実行可能な形式へ変化しました。これをストレージに保存し、ショップを表示する際に ShopFront がテンプレートして利用することでショップ毎のデザインの切り替えが実現されています。ようするに、とにかく Cot という形式にさえしてしまえば、元のデータはなんでもよいような設計になっています。ノーコードも変換エンジンを利用する実装の 1 つで、BASE Template とは全く異なるデータをもとにして Cot を生成しています。 1 ノーコードでのデザイン編集 コンセプトと概念 「デザインをかんたんに、もっと自由に。」というコンセプトで開発されたデザイン編集機能は、「パーツ」と呼ばれる小さな部品をショップページに配置することで、ノーコードで自由な構成のショップページを作成できます。これはページ全体を細かく GUI から変更できるような機能ではなく、ある程度デザイン済みのページに対して必要な要素を追加、組み合わせていくものです。 これを設計していく上で「全体を大きく編集可能な部分とそうではない部分に分離し、可能な部分に対して任意の子要素を追加できる」を最も基本的な考え方としています。これは右の図のような概念になっていて、赤い枠で囲われている編集可能な部分を「コンテナ」、そこへ追加される黄色い要素を「パーツ」と呼んでいます。 「パーツ」はそれぞれが独立してデザインされており、例えばリストのカラム数や画像の回り込みのようなスタイルを個別に変更することも可能です。「コンテナ」の配置パターンと灰色の編集不可能エリアのデザインを複数用意し、それにショップに合わせた必要なパーツを乗せることで様々なデザインのショップを実現しています。 レイアウト用テンプレートと複数のパーツ用テンプレートを組み合わせて表現 これの実現方法として 2 種類のテンプレートを用意して、その組み合わせを Web 上で編集したものを(ここがノーコードな部分)、変換エンジンで 1 つのテンプレートとしてまとめています。1 つは大枠のデザインとコンテナを定義する「レイアウト」、もう 1 つは「パーツ」でそれぞれは次のようなコードになっています。 レイアウトは HTML 全体と、編集可能な箇所とする「コンテナ」を独自で定義した <cot-container> というタグを使って表現しています。この <cot-container> の子要素として様々なパーツが追加されていきます。 < body > < h1 >< img src = "{{ shop.logo }}" alt = "{{ shop.name }}" ></ h1 > < div > < div class = "main" > < cot-container name = "main" /> </ div > </ div > </ body > パーツはメタ情報が含まれているため少し長めになっていますが、商品パーツの例です。Vue.js の SFC と似たような構造になっています。 props はこのパーツに渡すことができる変数で React や Vue.js でいう props と同等のものとイメージしてください。この例では layout に row を設定することで、縦方向と横方向でレイアウトを切り替えられるような実装をしています。 < script name = "metadata" > { name: "item" , props: { layout: { ui: 'select' } } } </ script > < template > < div class = "container" data -layout= "[[layout]]" > < div class = "head" > < h2 > {{ item.title }} </ h2 > < img src = "{{ item.image }}" > </ div > < div class = "body" > < p > ¥ {{ item.price }} </ p > < p > 残り {{ shop.stock }} 個 < p > </ div > </ div > </ template > < style > .container { display : flex ; } .container [ data-layout = "row" ] { flex-direction : column ; } </ style > < script > function main ( $el ) { $el.addEventListener ( ... ) } </ script > これらを GUI 上でどのように組み合わせているかについては後ほど記載するのですが、最終的には次のような JSON として管理しています。デザイン編集という機能は、この JSON のエディターだと考えると今後がイメージしやすくなります。 { " containers ": { " main ": [{ " name ": " image ", " props ": { " src ": " https://.... ", " fit ": " cover " } } , { " name ": " item ", " props ": { " layout ": " column " } }] } } そして、この JSON を変換エンジンに渡すことで ShopFront で利用可能な Cot を生成しています。 <cot-container> のあった場所に、 props を展開したパーツのテンプレート部分が追加されています。 コードで見る < style > .container { display : flex ; } .container [ data-layout = "row" ] { flex-direction : column ; } </ style > < body > < h1 >< img src = "{{ shop.logo }}" alt = "{{ shop.name }}" ></ h1 > < div > < div class = "main" > < div name = "main-container" > <!-- 画像パーツ --> < div id = "zzzz" data -fit= "cover" > < img src = "https://...." > </ div > <!-- 商品パーツ --> < div id = "xxxx" class = "container" data -layout= "column" > < div class = "head" > < h2 > {{ item.title }} </ h2 > < img src = "{{ item.image }}" > </ div > < div class = "body" > < p > ¥ {{ item.price }} </ p > < p > 残り {{ shop.stock }} 個 < p > </ div > </ div > </ div > </ div > </ div > < script > ( function ( $el ) { $el.addEventListener ( ... ) } )( document .querySelector ( '#xxxx' )) </ script > </ body > このテンプレートを ShopFront で変数を用いてレンダリングすると、デザイン編集の GUI で作ったデザインが反映される、という仕組みです。 コア部分以外のデザインやデータは変換時点で静的に解決する 1 つに結合されたテンプレートには、デザインに関する CSS と必要な JS の殆どが埋め込まれているような形になります。これは ShopFront 単体では変数は動的な必要があるものに絞る、特にデザインに関する変数をほとんど持たせないような設計を取っているためです。デザインに関する部分は変換エンジンの時点でほとんどが静的な HTML となるため、ShopFront の仕様を小さく保つことにも一役買っています。 例えば「リアル店舗の地図を表示するパーツ」を作りたい場合は、地図のウィジェット埋め込みまでをパーツとして完結させることで ShopFront に変数を追加することなく機能を増やしていく、という方針を取っています。 JSON エディターとしてのデザイン編集 先程、ノーコードでのデザイン編集はようするに JSON エディターだと説明しました。BASE のフロントエンドは現在主に Vue.js で実装されており、デザイン編集も例外ではありません。では、レイアウトやパーツといった独自の形式のものを Vue.js 上でどのように扱っているかといいますと、それぞれを Web Components (!) へ変換し、Vue.js を使ってそれを動的に組み合わせるようにして実装されています。Web Components として、実際のショップページで使用されるものと同等のものをショップ管理画面でもレンダリングしてしまうことで、実際のショップページに近い見た目を再現しています。(説明のしやすさのため、この JSON をデザインデータと呼ぶことにします。) Web Components の slot の内容を Vue.js で制御し、編集を可能に 実装がどうなっているかを少し解説しますと、コンテナへ動的にパーツを追加できるよう、コンテナを Web Components の slot に置き換えて、その中にパーツの Web Components を追加しています。 <cot-container> を slot に置き換えた Web Components を作成し( layout-component )、その slot の内容を Vue.js で生成しています。 <body> <h1><img src="{{ shop.logo }}" alt="{{ shop.name }}"></h1> <div> <div class="main"> - <cot-container name="main" /> + <slot name="main"></slot> </div> </div> </body> そしてこの Web Components をプレビュー兼編集可能な GUI としてそのまま表示します。少しややこしいのですが、次のような構造になっています。 < VueContainer > < layout-component > < div slot= "main" > < DraggableContainer > < component :is= "`${p.name}-component`" v- for = "p in designData.container.main" :props= "p.props" /> </ DraggableContainer > </ div > </ layout-component > </ VueContainer > DraggableContainer は DnD を実装するためのラッパーだとしてください。新しいパーツを左側のエリアからここへドラッグすると、デザインデータへパーツの情報が追加されます。追加されると再レンダリングが起こり、 v-for によってそのコンテナに追加されているすべてのパーツが Web Components で表示されます。 パーツの見た目の変更 props に変更があった場合も同じように再レンダリングが起こり、Web Components が更新されます。これは Vue.js の機能ではなく Web Components で attribute の変更を監視し、必要なら自身の内容をレンダリングしなおすように実装されています。 class extends HTMLElement { // ... static get observedAttributes() { return [ 'props' ] ; } attributeChangedCallback(attrName, oldVal, newVal) { if (attrName === 'props' && this ._shadow) { this ._props = newVal this ._shadow.innerHTML = this .render() } } } 最後にここで編集したデザインデータを保存すると、変換エンジンがデザインデータをもとに ShopFront 用の Cot を生成して ShopFront から参照できるストレージへ保存します。これで、ノーコードでのデザイン変更が完了します。 まだまだたくさんの課題があります ざっくりではありますが、BASE が提供するノーコードなデザイン編集機能ついて解説してきました。この機能はリリースされてからもうすぐ二周年となるのですが、実現していきたい機能や解決していきたい課題がまだまだあります。技術的なチャレンジで言えば..。 そもそも Web Components としてパーツを作ることができればよいのでは?しかし SSR も捨てがたい... 商品パーツのように、どうしても機能が山盛りになりがちなものをどう開発保守していこうか? 外部デベロッパー向けに仕様を公開して、よりカスタマイズやサービス連携がしやすいようなプラットフォームにできないか? などなど..。 サービスとしても自由度と制約による保守のしやすさのバランスが大事だったり、ショップのブランディングがより幅広く可能なものを考えたりと、非常にやりがいのある分野となっています。そんなわけで BASE ではあらゆる方面のエンジニアを募集していますので、興味を持っていただたら幸いです。 採用情報 | BASE, Inc. 今回は省略していますが、テンプレートエンジンだけでは補いきれないものに関しては WebAPI が ShopFront に実装されています。商品リストのページングや、関連商品、レビューの遅延取得などはこの WebAPI 群を使用しています。 ↩
2022/08/27(土)に開催される PHPカンファレンス沖縄 2022 に BASE がスポンサード及び所属するエンジニア 3 名が登壇いたします。 PHPカンファレンス沖縄 2022 は、新型コロナウィルス(COVID-19)感染症拡大防止のため、オンラインおよびオフラインでのハイブリット開催となります。 また、BASE に所属するエンジニアは、オンラインによる参加となります。 https://phpcon.okinawa.jp/ BASE は、ゴールドスポンサーとして当カンファレンスに協賛しています。 ゴールドスポンサーの一覧(下から2番目にBASEロゴ) セッションの内容について プログラミングをするパンダ( @Panda_Program ) 「実践!ユニットテスト入門」15:30~ Track A テスト書いてますか? テストを書く理由と実際のテストコードを紹介する実践編に分け、TDD を3年間実践してきた経験に基づいてお話しします。 テストを書いたことのない方が、テストを書いてみたいと思ってもらえることを目指します。 サンプルコードは PHP + PHPUnit ですが、他言語でも通用する考え方を紹介します。 ■ 概要 なぜテストコードを書くのか レガシーコードとは、テストのないコード テストはコストが安いフィードバックループである ■ 実践編 テストケースは日本語で書こう いろんな assertion を知ろう arrange / act / assertion のテストコード実装パターン set up / tear down を使って前処理/後処理をする dataProvider でテストをまとめる(ただし早すぎる抽象化に気をつけよう) 02 ( @cocoeyes02 ) 「カンファレンススピーカー入門〜登壇するぞ!って決めてからトークするまで〜」14:30~ Track B みなさんはカンファレンスで登壇したことがありますか? カンファレンスで登壇をしているスピーカーは、様々な過程を経てみなさんの前でトークをしています。 例えば採択前ならネタ決めやプロポーザル、採択後ならスライド作成・トーク練習などの準備・・・ このトークするまでの過程は、人によって違うところもあり暗黙知であることが多いように思えます。 そこで今回は、過去に私がPHP系カンファレンスにて登壇した内容を例にしつつ、自分がカンファレンスで登壇するまでに準備していることを話します。 まだ登壇したことがない方はもちろん、登壇したことがある人も良いところを取り入れられるきっかけになれば幸いです! 具体例を交えつつ、以下のことについて触れる予定です! プロポーザルを出す時にどんなことを考えているのか? トークするまでの準備はどういうことをしてるのか? キュアセブン ( @cureseven ) 「Gather x Code With Me x ペアプロのお誘いで最高です」13:50~ Track A ゴールドスポンサートーク 私が所属しているチームでは、リモートワークでペアプロ・モブプロによる開発を行っています。 隔週の振り返りでネクストアクションをみんなで考えて実践し続けた結果、リモートワークでもコミュニケーションが充実し、手戻りが減るなどの効果が現れました。 そんな私たちが半年かけて洗練したペアプロ・モブプロの運用方法をお伝えします! 具体的には以下のような話をします。 PhpStorm の Code With Me 機能の活用でリモートワークでも快適に Gather の活用でフィードバックを素早くもらえる 「ペアプロのお誘い」Slack ワークフローの活用で柔軟にペアを組む 最後に PHPカンファレンス沖縄 2022 の当日のチケットは下記よりお申し込みいただけます。 https://techplay.jp/event/866236 それでは、みなさまにお会いできることを楽しみにしております。
こんにちは!BASE BANK Sectionの松雪( @applepine1125 )と清水( @budougumi0617 )です。 去る2022/08/04(木)にオンラインで開催された株式会社UPSIDERさまと株式会社Kanmuさまとの合同Meetup 「Tech Meetup 〜Goで作る決済サービス〜」にBASE BANK Sectionから2名登壇しました。 upsider.connpass.com youtu.be どんなイベント? 今回のイベントはGo×決済というテーマで、開発時の工夫やこれまで直面した課題などについてGo言語を駆使してカード決済事業を行う3社がそれぞれ事例を紹介、ディスカッションするものでした。 ご一緒させていただいたUPSIDERさま、Kanmuさま改めて感謝申し上げます。ありがとうございました! 当日は参加した3社の発表と、Goや決済についてのパネルディスカッションが行われました。 当日のハッシュタグは #go_tech_meetup でした。当日のツイートを遡ると発表時の盛り上がりが伝わるかと思います。決済ならではのコアな質問も飛び交っていたのでぜひ眺めてみてください! LTした内容について 今回LTは BASE BANK Sectionの松雪( @applepine1125 ) がBASEカードというプロダクトで行っている決済シミュレーションテストについて発表しました。 basecard-lp.thebase.com speakerdeck.com 複雑なカード決済の仕様が存在する中で、質の高い決済機能を提供するためにどのようなテストを行っているかについて発表しました。 今回発表した決済領域に限らず、複雑なドメイン知識や仕様が存在する事業では、どのようにプロダクトの質を担保するのか、そのためにどういったテストを行うべきなのかについて熟考する機会が多いかと思います。 今回の発表では、シミュレーションテストの実現方法だけでなく、 なぜシミュレーションテストを行おうと思ったのか? について、その背景にある複雑な決済の仕様も踏まえて話をまとめました。 なぜテストをするのか?という点については決済領域に限らずどのプロダクトでも考える必要があると思っており、今回の発表を通してその1事例をご提供できればと思って発表をしました。 パネルディスカッションの内容について 各社の発表後、合同でパネルディスカッションが行われ、松雪( @applepine1125 )と清水( @budougumi0617 )が参加しました。 当日は以下の様なテーマでディスカッションが行われ、パネラーだけでなくtwitter上でも大きな盛り上がりを見せていました。 各社のアーキテクチャ紹介 Goを書いていてよかったと思うこと 残高管理や決済などコア機能のテスト戦略 開発チーム構成や開発プロセスで気をつけている事 最後に宣伝 BASE BANK Sectionでは、今回発表させていただいたBASEカードだけでなく、BASEを利用するショップオーナーさんのキャッシュフローを改善するための様々なプロダクト開発を行っています。 Goを中心にPython, PHPなどの言語を駆使し、フルサイクルエンジニアというスタンスでフロントからインフラまでを一気通貫に触るだけでなく、一連の開発プロセスにエンジニアが主体的に関わり質の高いスピーディなアウトプットを行っています。 basebook.binc.jp そんなエンジニアがオーナーシップをもったチームでプロダクトのグロースに挑戦したい!と思った方や今回の発表で金融決済領域に興味を持った方はぜひ下記の紹介資料や募集をご覧ください! 今回参加したメンバーへのtwitter DMからでもお待ちしております! speakerdeck.com open.talentio.com
2022/07/23(土)に開催された ISUCON 12 *1 予選に BASE から8名のエンジニアが6チームに分かれて参加しました。 参加者の感想をお届けします! isucon.net チーム「Speed of Sound」 最終スコア 4,711点 およそ210位でした。当日の作業リポジトリは以下です。 github.com 清水(@budougumi0617 ) BASE BANK Section テックリードの @budougumi0617 です。 ISUCON11に引き続き2回目の参加でした。前回同様環境構築、ミドルウェアの設定変更、監視用のツールの導入などを主に対応しました。 構成管理をしたい欲を出しすぎてかなり足を引っ張ってしまいました。 /etc ディレクトリ以下の設定ファイルのパーミッションを変更、シンボリックリンク化した副作用で PAM まわりの挙動をおかしくした sudo コマンドも動かなくなってCloudFormationをやり直すはめに MySQLの my.conf 周りのファイルをシンボリックリンク化したためスロークエリ設定の有効化に時間を溶かす AppArmorも切っていたはずなので謎 スロークエリログを確認してMySQLの PREPARE を抑制する お決まりの変更 をしたあとはほとんど有効な一手を打てませんでした。 alp コマンドや netdata の導入もしていたのにスロークエリ結果からしかアプローチを考えない狭窄的な行動になっていました。 また、最近のISUCONで多用されていた env.sh やsqlファイルに ALTER や INDEX を追記すれば initialize で毎回テーブルが初期化されるような構成に馴れていたため手間取っていたところも多かったです。 終盤ではベンチマークの検証が通らなくなってしまい復旧に手間取りました。シードデータファイルが見当たらなかったので安易にDB上のデータを消したのが原因でした。他のインスタンスからデータをダンプして復旧しましたが、競技とはいえ安易にデータベースのデータをふっ飛ばしてはいけないということを再確認しました。 sqliteを意識するところまでボトルネックの解消ができず非常に悔しい結果になったので来年はリベンジしたいです。 永野(@glassmonekey) こんにちは @glassmonekey です。今回初参加です。同僚に誘われたのでいい機会と思い参加してみました。 特に分担する予定はなかったのですが、MySQLだけとりあえず剥がすといった若干インフラ寄りの作業をしたりしました。 サーバーにsshして/etc以下を触る経験は久しくしてなかったので懐かしい気持ちになりました。 反省点としては、サーバー構成の反映漏れとかで時間が溶けるタイミングとかあったので、アプリケーションの改修にそこまで時間を取ることができなかったことがありました。インフラ構成はAnsible化するなりしていきたいなあと思ったりしました。 短期間でドメイン知識の吸収からサービス特性を読み解くところも求めれるので、楽しい時間でした。次回は予選突破を目指すぞ。 ⁠吉田 ( @yopidax ) 今回初参加でした。前々からISUCON自体は気になっていたのと同僚の清水さんに誘わたのがきっかけで参加してみました。 チームでの作業分担は特に無かったのですが、私はMySQLのスロークエリを見つつボトルネックを特定しindex貼ったりアプリケーション側の改善をしていました。対策として練習なしで過去問題と解説を眺めるくらいしか行わず挑んだのですが、結果あまり手を付けることが出来ず準備不足を痛感しました。 短い時間で仕様を読み解きつつ普段一緒に働く同僚とワイワイしながら進めること自体は楽しかったので、事前練習を行いつつ次回も是非参加したいと思います! チーム「ngsw」 最終スコア 3,614点 @ngsw インフラ側の問題に至る前のボトルネックをそもそもどうにもできませんでした。 なので以下を行なったのですが、点数的にはほぼ意味がありませんでした。 Dockerはがし sqliteを/dev/shm 上に配置(これしたからOS再起動試験が1.5hかかった) ISUCON参加(および前回予選問題を利用しての事前の社内練習会)で自身の苦手な領域が、目に見えてわかるのでそこが非常に面白かったです。 なので答え合わせ重要と思いました。予選通過者のエントリを読んだりしながら、ベンチマーカー公開されるのを待ちたいです。 dockerが起動してるEC2で、それをはがして云々みたいなのはパズル的な面白さがありました。 オペレーションが身体性を伴ってきたときに訪れる万能感があり、そのために素振り重要なんだろうなって感じました。 チーム「牡蠣食えば 金がなくなり リボ払い」 最終スコア 7,472点 @cureseven 今年もISUCON10で本戦に一緒に出場した社外の友人と参加しましたが、7,472点133位で敗退しました。 私は今年もアプリケーションを担当しました。今回の問題はsqliteをmysqlに置き換える作業が終わってからindexを貼り、その上でボトルネックの特定をしようと思い、N+1を潰したり早期returnさせ無駄な計算を省いていたりしていたら時間が来てしまいました。 練習の成果もあり初動は毎回上手くいき、数分は1位にいたりもしたのですが、sqliteの置き換えにおいてのinitializeが時間内に終わらない問題などにぶつかり後半は点数が伸び悩みました。 簡単に解決できないとき、別の手を考えたり一緒に方針を考えたりするなど、チームの柔軟な動き方が課題でした。個人的には去年よりも手数を打てて成長を感じました。 本戦問題として出てもおかしくないような難易度の高い問題で大変刺激になりました。運営の皆様ありがとうございました。 チーム「example.com」 最終スコア 3,119点 横山 今回で3回目の参戦です。 クエリ中心に最初は見ていきexplainなどのクエリ実行しながらindexの効果を確認していきました。 そのあとはN+1の実装部分を修正してループしたinsert処理をバルク処理に書き直したりしました。 結果としてスコアは伸びず3,119点となりました。 前回、前々回は3人で参加していましたが今回は諸事情で1人での参戦となりその面でモチベーションを保って対応するのが難しかったです。 次回はまた3人で参戦してワイワイしながらやりたいと思います。 チーム「今すぐダウンロード」 最終スコア 0点 (計測失敗前の最高スコアは1,400点台(1,466?) 田中 @tenkoma 初めてISUCONに参加しました。 まず、参考実装をPHPに変更後の初期スコアが1,000点くらいになりました。 その後、MySQLサーバをアプリケーションサーバと分けて1,400点くらいに。 SQLで複数回呼び出しているものをまとめたり、インデックスを追加してみたものの効果が出ず。 最後に、SQLiteファイルをMySQLに移行するために以下の作業をしましたが、ベンチマーク前の初期化が終わらなくなってしまい、0点で終了しました。 アプリケーションから tenant DBの参照先を admin DBと同じ接続に変更 SQLite ファイルをdump して1ファイルにし、初期化シェルスクリプトでMySQL DBに投入 また参加する機会があればチームで参加したいです。 チーム「IMS」 最終スコア 0点 炭田( @tac_tanden ) 今回、社外の友人と3人で初参戦でした。最終スコアは0点(0点になる前の最大スコアは4,821点でした)で非常に悔しく感じています。 競技中は主にクエリのチューニングとアプリケーションコードの変更、nginxのチューニングなどもトライしていました(が、全然スコア上がりませんでした...)。MySQLの移管に関しては試みましたが、時間内に完了させることができませんでした。 ボトルネックを冷静に見定めるためのツールを使えるようにするのを、序盤に素早く設定できるようにするのが次回の課題なので、来年出場する前に練習しようと思います! 最後に 今年も挑戦のしがいのある問題、当日の快適なコンテスト環境を準備していただいたISUCON運営の皆様に感謝いたします。 社内では #club-isucon チャンネルでISUCONに向けた情報交換や合同練習をしていました。 残念ながら今回の予選を突破できた弊社メンバーはいませんでしたが、『 達人が教えるwebパフォーマンスチューニング 』の勉強会などを企画して来年のリベンジに向け動き出したいと思います! 宣伝 BASEでは購入者やショップオーナーの皆様のことを第一に考え、パフォーマンスチューニングにも興味があるサーバーサイドエンジニアの仲間を募集しております。 カジュアル面談も実施しておりますので、ぜひお気軽にお問い合わせください。 open.talentio.com open.talentio.com *1 : 「ISUCON」は、LINE株式会社の商標または登録商標です。
2022/7/21(木曜日)にオンラインで開催された「 Looker User Meetup Online #8 」に BASE BANK Dev Groupの永野(@glssmonekey)が登壇しました。 looker-japan-user-group.connpass.com イベントについて 今回のテーマは「Lookerまでのデータデリバリー、みんなどうしてる?」でした。 各社のLookerまでのデータパイプラインの構成やチーム構成など、Lookerを中心にしたデータに関する話題でわいわいしました。 当日は弊社含めて次の4社(敬称略)の発表が行われました。 Unipos株式会社 株式会社タイミー 株式会社フィードフォース BASE株式会社 当日のTwitterハッシュタグは #lookermeetup でした。 登壇内容について 今回、永野( @glassmonekey ) が発表した内容は次のスライドです。 今回はアプリケーションエンジニアの視点から、「リリースサイクルにデータ分析基盤の構築を含めて行うこと」の重要性というコンセプトでトークしました。 日々変化するビジネス要件に応じて、見たいデータは変化していくことでしょう。 そのような要望があると以下のような方法が多いのではないでしょうか? データ基盤のスキーマを変更する 分析のためのSQLを変更する etc... 多くの場合で専門のチームがこれらをメンテナンスしていくケースが多いように思われますが、 その場合だとビジネスの変化という速度に適応することが難しいのではないでしょうか? そのため私達のチームでは、データ分析基盤の構築もリリースサイクルに含めて構築するようにしています。 この「リリースサイクルにデータ分析基盤の構築を含めて行うこと」の実現に、Lookerはとても役立っています。 現状は 派生テーブル を使うことで、日々変化するビジネス要件を我々自身で見たいデータを構築しています。これにより、リリースに応じた指標をスピーディーに分析する体制を実現しています。 感想 派生テーブルを活用しているものの、今後の展望やデータの粒度で迷ってる点が色々あったので、イベントにて dbt や PDT の活用事例を実際に聴くことができ、最高にわくわくする時間でした。 現状CDCにembulk+マージクエリで頑張ってたのですが、dbtを使うともう少し楽にできそうみたいなところを知ることができたので、実際に業務でも取り入れてみたいなと思いました。 他にも話題としてデータのテスト戦略が上がっていたので、改めて他社さんの事例をとても聞いてみたいなと思いました。 データ基盤のテスト設計は皆目検討についてない状態なので、ぜひ学びたい!! 宣伝 BASE BANKチームでは Go, Python, PHPを中心に、フロントからインフラまでを一気通貫で開発しています。 また開発だけでなく、機能をグロース・分析・サポートまで担当します。 basebook.binc.jp そんな開発スタイルのチームで新規事業開発に挑戦してみたいぞ!と思った方はぜひ下記の募集をご覧ください。 open.talentio.com
2022/6/28(火)にオンラインで開催された「 新規事業プロダクト開発時の技術選定どうやった? -カケハシ×LINE×BASEの開発者が振り返る技術選定プロセスと内省- 」にBASEから2名が登壇しました。 techplay.jp イベントについて 今回登壇させていただいたイベントは新規事業プロダクト開発時の技術選定について各社の事例を紹介するものでした。主催の株式会社カケハシさまお誘いありがとうございました。 当日は弊社含めて次の3社(敬称略)の発表とパネルディスカッションが行われました。 株式会社カケハシ LINE株式会社 BASE株式会社 当日のTwitterハッシュタグは #TechKKHS でした。 登壇内容について 今回、BASE BANKチームの柳川( @gimupop )と清水( @budougumi0617 )が合同で発表した内容は次のスライドです。 登壇内容について(柳川) 新規事業を行う上での心構えやマインドを中心に、経験から話しました。 そしてそれが現在にどうつながっているかの振り返りも含めて話させていただきました。 持ち帰っていいただきたいものは勇気という言い方をしました。新規事業のおいては、やはりある意味属人的な人の決断というものが必要です。 今振り返ってみると、技術選定時の決断が、その後のチームの精神の核になっていると気が付きました。 引き続きアジリティを落とさない形で、チーム拡大をし、アウトプットのクオリティをあげていくことに取り組んで行こうと思いました。 登壇内容について(清水) 新規事業を複数リリースしグロースさせつづけていくためにチームで大事にしていることとそのために選択した技術について話させていただきました。 技術選定はチームが何を大事にしているかによって大きく変わってくるかと思います。 今回の登壇では特定の技術の紹介というよりも 「何を基準に技術を選んだのか?」「リリースから4年間開発し続けていてその効果をどう感じているか?」 を中心に話をまとめました。 また、自分としても今使っている技術のどこに良さを感じて活用し続けているのか見つめ直すいい機会になりました。 これからも「プロダクトやドメインに集中する」という目的を忘れずに技術と向き合っていこうと思います。 パネルディスカッションについて 各個人の発表後は3社合同でパネルディスカッションが行われ、当日は次のような話題が挙がりました。 エンジニア以外も含めたらチームにはどんなロールの人が何人くらいいるのか 「やっぱりああしておけばよかったな」と後悔していること 新規開発に向いていそうな人 パネルディスカッションの内容はTECH PLAYさまのイベントレポートで確認できます。 techplay.jp 宣伝 BASE BANKチームでは Go, Python, PHPを中心に、フロントからインフラまでを一気通貫で開発しています。 また開発だけでなく、機能をグロース・分析・サポートまで担当します。 basebook.binc.jp そんな開発スタイルのチームで新規事業開発に挑戦してみたいぞ!と思った方はぜひ下記の募集をご覧ください。 open.talentio.com