TECH PLAY

KINTOテクノロジーズ

KINTOテクノロジーズ の技術ブログ

936

はじめに こんにちは! KINTOテクノロジーズの新車サブスク開発グループに所属している丁(Jeong)です。 私たちの日々の業務は、ただコードを書くだけにとどまりません。技術の進化に伴い、マイクロサービスやサーバーレスアーキテクチャのような新しいトレンドに適応し、システムの健全性を維持することが重要になってきています。この記事では、Observability(オブザーバビリティ/可観測性)の重要性を理解し、Grafanaを活用してシステム監視とパフォーマンス最適化を行っているかを共有します。 Observabilityとは Observability、または可観測性は、システムの状態やパフォーマンスを監視し、理解する能力を指します。この概念は、システム内で発生する問題を早期に特定し、解決するために不可欠です。特にマイクロサービスやクラウドベースのアーキテクチャでは、多数の動的コンポーネントが関連し合って動作するため、システム全体を継続的に監視する必要があります。 Observabilityの主な要素には、以下の三つがあります: ログ: システムのアクティビティやエラーを記録する詳細なデータ メトリクス: システムのパフォーマンスや状態を示す定量的データ トレース: E2E[^1]のリクエストやトランザクションのパスを追跡するデータ [^1]: E2E (End-to-End):システムやプロセスが最初から最後まで完全に連携して機能すること Grafanaとは Grafanaは、Observabilityのための強力なオープンソースツールです。データの可視化、監視、および分析を行うために広く使われています。Grafanaの最大の特徴は、その柔軟性とカスタマイズ可能なダッシュボードにあります。ユーザーは、異なるデータソースからのメトリクスやログを統合し、簡単に理解できる形で表示することができます。 Grafanaの主な利点は以下の通りです: 多様なデータソースへの対応: Prometheus, Elasticsearch, InfluxDBなどと統合可能 データ分析とアラート: システムの異常を即座に検出し、アラートを通知 ダッシュボード: ユーザーのニーズに合わせてダッシュボードをカスタマイズできる Grafanaでの実践例:分かりやすさを重視 新車サブスク開発グループでは、技術的な背景が異なるメンバー全員がデータを理解しやすいよう、Grafanaのダッシュボードを活用しています。ここでは、実際ダッシュボードのパネルで使用しているPromQLのクエリ例とその機能をご紹介します。 APIリクエストの監視 ![Statパネルを使ってリクエスト数を表示](/assets/blog/authors/jeong/grafana_promql1.png =500x) 契約数 #値はサンプルです 最も基本となるクエリで、URIパターンに対するHTTPリクエストの数を追跡します。このクエリは、時間範囲内でのリクエスト数の増減を示し、トレンド分析に役立ちます。 sum( increase( http_server_requests_seconds_count{ uri=~”/foo”, method=“POST”, status=“200” }[$__range] ) ) or vector(0) sum(...): 集計関数で、結果を合計します。ここでは、条件を満たすリクエストの総数を計算しています。 increase(...): 時間範囲でのメトリックの増加量を計算します。リクエスト数の増減を捉えることができます。 vector(0): 結果がない場合に0を返します。データがなくてもダッシュボードに表示するためのものです。 1時間ごとの申込数の計測 ![Time Seriesパネルを使って1時間ごとの申込数の計測を表示](/assets/blog/authors/jeong/grafana_promql2.png =500x) 時間帯別契約数(サンプル) #夜はお眠り APIリクエストの監視 のPromQLを活用して1時間ごとにリクエスト数を計測し、時間帯による需要の変化を把握します。時間帯に応じて動的にリソースを割り当てるなどの参考データとして使います。 最も時間がかかるリクエストの特定 ![Tableパネルを使って最も時間がかかるリクエストを表示](/assets/blog/authors/jeong/grafana_promql3.png =500x) 4秒以上かかるものもあります パフォーマンス分析において、時間がかかるリクエストを特定することは、システムのボトルネックを見つける上で重要です。以下のPromQLクエリは、この目的を達成するのに役立ちます。 平均応答時間の計算 各リクエストの平均応答時間を計算します。応答時間の合計とリクエスト数をそれぞれ求め、その後これらの数値を使って割り算を行います。 sum by(application, uri, outcome, method) ( increase(http_server_requests_seconds_sum[$__range]) ) / sum by(application, uri, outcome, method) ( increase(http_server_requests_seconds_count[$__range]) ) sum by(...): ラベル(ここではapplication, uri, outcome, method)に基づいて結果をグループ化し、それぞれのグループの合計を計算します。 increase(...): 時間範囲($__range)内でのメトリックの増加量を計算します。ここでは、応答時間の合計とリクエスト数を計算しています。 リクエスト数の集計 リクエスト数を集計します。 sum by(application, uri, outcome, method) ( increase(http_server_requests_seconds_count[$__range]) ) 最も長い応答時間を持つリクエストの特定 時間範囲内で最も応答時間が長かった上位10のリクエストを特定します。 topk( 10, max( max_over_time(http_server_requests_seconds_max[$__range]) ) by(application, uri, outcome, method) ) topk(10, ...): 最も大きい値を持つ上位10の要素を返します。ここでは、最も応答時間が長かったリクエストのトップ10にしています。 max(...): 各グループの最大値を計算します。 max_over_time(...): 時間範囲内で各メトリックの最大値を計算し、ラベルで結果をグループ化します。これにより、応答時間が最も長かったリクエストを抽出できます。 クライアント側のエラーの監視 ![Tableパネルを使ってクライアント側のエラーを表示](/assets/blog/authors/jeong/grafana_promql4.png =500x) 認証トークンの期限切れ クライアント側で発生するエラーを監視し、原因を分析します。 label_replace( sum by (application, method, uri, exception, status) ( increase(http_server_requests_seconds_count{ status=~”5..|4..”, exception!~”None|FooException” }[$__range] ) ) > 0, ‘uri’, ‘$1/*$2’, ‘uri’, ‘(.*)\\/\\{.+\\}(.*)’ label_replace(...): ラベルを変更または追加を行います。 '> 0': 合計された値が0より大きい場合に結果を返します。つまり、エラーが発生している場合のみデータを表示します。 Grafana導入のメリットと実践事例 新車サブスク開発グループがGrafanaを活用し、どのようにシステム監視の質を向上させたかを詳しくお話しします。 包括的なシステムの可視化: AWS Managed Grafanaを使うことで、私たちはAWSサービス(RDS、SQS、Lambda、CloudFrontなど)からのデータと、アプリケーションのメトリクスを同時に可視化できるようになりました。これは、システム全体のパフォーマンスやボトルネックを一目で把握できることを意味し、問題解決へのアプローチが大幅に加速されました。 効率的なデータ分析とトラブルシューティング: 異なるデータソースを統合することで、問題発生時に必要な情報をすばやく取得できます。これにより、問題の根本原因を迅速に特定し、効率的な対応が可能になりました。 APIの監視: Grafanaを通じて、特定のExceptionが異常に多発していることを発見しました。調査結果、フロントエンドで想定外のAPIが呼ばれていることが判明。この問題をフロントエンドチームへ伝え、修正を行った結果、APIの効率が向上し、システムの全体的な安定性とパフォーマンスが大きく改善されました。 さいごに Observabilityは、ただシステムを見ること以上の意味を持ちます。それは、システムの健全性を保ち、問題が生じた際に迅速に対処できるようにするためのキーです。Grafanaの活用により、我々は複雑なデータを簡単に理解し、システム全体のパフォーマンスを効果的に管理できるようになりました。APIリクエストの監視からエラーの特定まで、Grafanaは多様なニーズに対応しています。 これは、SREや開発者に限らず、全てのチームメンバーが協力して取り組む価値があります。結局のところ、システムの透明性と信頼性の向上は、より良いサービスを提供し、お客様の満足度を高めるために不可欠です。 我々の経験が、皆さんの業務において新たな視点を提供し、より効率的なシステム監視とパフォーマンス最適化の手助けになれば幸いです。
アバター
こんにちは! 人事採用グループ労務総務チームのつんつんです。 私たちは社員の声を聞きながら、オフィス環境整備に取り組んでいます。本日は2023年に実施した内容をご紹介いたします。 日本橋室町オフィス 受付にTOYOTAのミニカーを設置 KINTOテクノロジーズは、クルマのサブスクリプションサービスをはじめとするモビリティサービス「KINTO」を主に開発している会社です。ある日、社員から「クルマの会社だけど、クルマに関するものが少ないよねー」と言う声がありました。「それならミニカーでも置いてみよう」と思い、設置したのがこちら↓ ↓16階受付 こちらに写っているのはTOYOTAの車種です。受付が賑やかになりました。 ごめんなさい! こちら非売品です。 ちなみに車種わかる方、いらっしゃいますか? 上段左から:GR スープラ・シエンタ・ヴォクシー・GR ヤリス・ハリアー・ランドクルーザー 下段左から:プリウス・クラウン b Z4X・カローラスポーツ・RAV4 ↓7階受付 左奥より ハリアー・ヤリス・カローラクロス・パッソ・アルファード 左奥より:カローラ・アクア・GR ヤリス・CーHR・ヤリス・ルーミー コロナ5類移行後、働き方の見直し 新型コロナウイルスが5類に移行したタイミングで、出社制限を一部緩和し、在宅勤務と出社勤務を融合したハイブリットな働き方になりました。 出社制限が緩和されたことにより、オフィスに来る社員が増えました。社員からは「個室ブースが欲しい」、「会議スペースが足りない」といったリクエストをいただきました。そこで会議室の増設と、気軽にミーティングできるスペースを2つ作りました。 会議室は2つの使われてない部屋に、スモークガラス、エアコン、会議室予約機能を追加しました。 ![](/assets/blog/authors/tsujimoto/6.jpg =500x) ![](/assets/blog/authors/tsujimoto/7.jpg =500x) 2部屋とも会議室として快適にミーティングできるようになりました。 そして気軽にミーティングできるスペースは、2種類あります。 一つ目は個室ブース。KOKUYO foreシリーズです。 ![](/assets/blog/authors/tsujimoto/8.png =500x) ![](/assets/blog/authors/tsujimoto/9.png =500x) 弊社は個室ブースの設置場所が窓際にあるため、夏でも暑くならないよう4面囲まれていないタイプを選びました。 二つ目は昇降机。KOKUYOのjionというシリーズです。 ![](/assets/blog/authors/tsujimoto/10.png =500x) 底面写真 ![](/assets/blog/authors/tsujimoto/11.png =500x) この椅子は底面が丸くなっており、「ゆらゆら」とバランスボールみたいに動きます。 神保町オフィス緑化計画 室町オフィスには休憩スペースやエントランスなど、観葉植物が点在していますが、神保町オフィスはほとんど緑がありません。 社員から「植物がなくて寂しい・・・」という声が上がり、神保町オフィスにも観葉植物を増やす「緑化計画」が動き出しました。 ↓下記の写真は執務室と会議室の緑化予想図です。 ![](/assets/blog/authors/tsujimoto/12.png =500x) ![](/assets/blog/authors/tsujimoto/13.png =500x) オフィスの環境整備はまだまだ続きます 来年は、さらにオフィスの改善に取り組む予定です。 現在決まっているのは、コミュニケーション施策の一環として休憩室を改造予定です。完成したらまたこちらのテックブログなどでお伝えできればと思っております。 日々オフィスを走り回っていますが、会社が成長している事を実感しています。 時代の変化と共にオフィスも変化する コロナ禍を経て、ハイブリットな働き方を導入する企業が多くなったと感じています。 今後は、ハイブリットな働き方に対応したオフィスや、出社したくなるオフィスなどが求められることを予想しています。特に、弊社は年齢や国籍が様々な社員で構成されているため、多様な価値観にフィットしたオフィス環境づくりをしていきたいと考えています。 最後になりましたが、ファシリティは他社がどのようにやっているかの情報がつかみにくいため、私たちの取り組みが少しでもご参考になればと思い、今回ペンを握りました。 KINTOテクノロジーズのアドベントカレンダーはまだまだ続きますので、明日以降も是非お楽しみに!
アバター
はじめに 「私は、それほど賢くはありません。ただ人より長く、ひとつのことと付き合ってきただけなのです。(筆者翻訳)」 — アルバート・アインシュタイン (物理学者) 問題によって、とても落ち込んでしまうことがあります。しかし、自分の陽気(merry)な性格が、自分の名前にぴったりで、課題の克服に役立ってきたとも思います。こんにちは、Maryです。KINTOテクノロジーズグローバル開発グループの一員です。 フロントエンドの経験はあまりなかったのですが、あえて挑戦したく、2022年1月に入社しました。様々なツールの使い方や、様々なプログラミング言語の作業方法まで、前から試してみたかったことの一環として取り組みました。時間が経つにつれて、ウェブ開発できるようになった自分に驚きました。 グローバルチームの一員として、各種スキルを習得しながら、いろいろな人々の文化、歴史、信念を理解できるようになりました。初めに担当したプロジェクトでの使用言語は英語で、馴染みのある言語だったので、通常どのようにウェブサイトが書かれるかを理解するのは簡単でした。しかし、その後あまり馴染みのないアラビア語を使うカタール向けプロジェクトにアサインされました。アラビア語は左から右に書くのです。とはいえ、私はこれをアラビア語の読み書きシステムを学ぶだけではなく、自分が作成するWebページでどのように対処するかを学ぶチャンスだととらえました。 KINTOテクノロジーズは昨年設立されたばかりで、改善の余地や学ぶべき点、克服すべき課題がたくさん残っています。課題をチャンスとしてとらえるタイプなので、私はコンフォートゾーンから飛び出る選択をいつもします。自分の成長に繋がると信じているからです。 概要 言語は、水平方向、垂直方向、右から左、左から右など、様々な方向に書くことができます。ある説によれば、このちがいは昔の人々がどんな媒体を使用して筆記作業を行っていたかと関係があるようです。 アラビア語やヘブライ語など、右から左に書く中東の言語は、古代には石に刻んで記述されていました。ほとんどの人は右利きで、ノミを使うとなれば右から左に書く方が自然に思えたのでしょう。東アジアの言語は、巻物に書かれたと言われ、左から右に書く方が簡単です。さらにインクを使う場合は、左から右に書くことでにじみが防げます。 これらはあくまで仮説に過ぎず、古くから使われていた手段の中には今や一般的ではなくなったものもありますが、新聞、本、さらにはウェブサイトなどを見れば、さまざまな媒体に記述方向が適応されているのがわかります。言語の記述方向をサポートできれば、読者はその言語を元の形式のまま読めるので内容が理解しやすくなります。 課題 エンドユーザーの立場だと、私は通常、自分で使い慣れた言語、つまり英語でウェブを閲覧しています。しかし、来日して、日本のすべてのサイトで言語が選択できるわけではないことがわかりました。そこでネットで調べてみると、ウェブサイト上での設定で、馴染みのない言語も自動翻訳する設定ができることがわかりました。 エンドユーザーのニーズには自動翻訳で十分だと思いました。しかし、機械翻訳はまだ完璧ではないため、誤訳があったり、ひどい場合には、未翻訳のまま単語が残ったりします。このような背景から、私はこの課題を解決する方法を見つけることに興味を持ちました。 運よくカタール向けのタスクをアサインされ、以下の課題に直面しました。 ブラウザ言語の検出 ブラウザ言語の変更 特定ページの言語設定 $vuetify.rtl と document.dir を使用した表示 付記:私の担当はフロントエンドですから、今あるリソースを使ってウェブサイト自体を開発することが主な仕事です。英語からアラビア語への翻訳は別のお話です。その方法については次のリンクを参照してください。 Language Localization at KINTO Technologies ソリューション 企業がグローバル化をするにあたり、「真のグローバル化をどう実現するか」は課題の一つです。サービスや製品を紹介する方法として、情報をWeb上で公開することが考えられます。しかし、これは単純な話ではないのです。読み手がたとえ世界のどこにいたとしても、伝えたいメッセージを理解されるよう気をつけないといけません。 ウェブサイトを希望する言語で表示するには、まずエンドユーザーが使用するブラウザ言語を把握する必要があります。これにより、読み手が希望する言語でサイト閲覧ができるようになります。 ブラウザ言語の検出 @nuxtjs/i18n オプションの使用 @nuxtjs /i18n の設定方法については、 こちら の手順を参照ください。 nuxt.config.js 内で i18n locale を入力し、 detectBrowserLanguage: true を設定します。 i18n: { locales: [ { code: 'en', iso: 'en', file: 'en.json' }, { code: 'ar', iso: 'ar', file: 'ar.json', dir: 'rtl' }, ], detectBrowserLanguage: true, }, code (必須) - ロケールの固有識別子 iso (SEO機能を使用する場合は必須) - SEO機能に使用される ISO コードで、 detectBrowserLanguage 機能を使用する際、ブラウザロケールのマッチングに使用される。形式は次のいずれかにする必要があります。 ISO 639-1(例: 'en' ) ISO 639-1 及び ISO 3166-1 alpha-2をハイフンで区切る(例: 'en-US' ) file - ファイル名ファイルからロケールメッセージを読み込む際、 LangDir パスを基準に決定されます dir ( v6.19.0 以降) dir プロパティが要素とコンテンツの方向を指定します。 値は 'rtl' 、 'ltr' 、 'auto' のいずれでもかまいません。 記述方向の設定にあたり、レイアウトで $nuxti18nHead メソッドを使用します。 <script> export default { head() { return this.$nuxtI18nHead() } } </script> window.navigator.language を使用する const lang = window.navigator.language console.log('language:' + lang) // language: ar ブラウザ言語の変更 上記の言語検出が機能しているかどうかをテストするために、ブラウザの言語設定を明示的に変更する場合があります。広く使用されているブラウザでの言語の変更手順を以下に示します。 (以下の手順は Windows 向けです。) Chrome パソコンで Chrome を開きます。 右上のその他 [⋮] > [設定] をクリックします。 下部にある [詳細] をクリックします。 [言語] > [言語] をクリックします。 使用したい言語の横にあるその他 [⋮] をクリックします。言語がリストにない場合は、[言語を追加] をクリックして追加します。 [Google Chrome をこの言語で表示] をクリックします。(このオプションは、Windows パソコンでのみ使用できます。) Chrome を再起動して変更を適用します。 Firefox メニューボタン ☰ をクリックし、[設定] を選択します。 [一般]パネルの言語セクションで、ドロップダウンメニューから言語を選択します。 Firefox を再起動します。 Edge [設定など] > [設定] の順に移動します。 [設定] の一覧から [言語]を選択します。 [優先する言語] の一覧に言語を追加するには、[言語を追加する] を選択します。 言語を追加したら、言語の横の[...]を選択し、[Microsoft Edge をこの言語で表示] を選択します。 特定ページの言語設定 KINTOはまだ新しい会社ですが、お客様のニーズとご要望に基づいたサービスを提供しようと最善を尽くしています。グローバル展開のためには、国ごとに十分に時間をかける必要があります。そこで気づいたのは、KINTOではそのときに必要なことにリソースを集中させ、残りはあとで行う方針を採っていたことです。つまり優先順位の問題だと気づきました。 私が携わったプロジェクトでは、まず英語とアラビア語の両方を読み込めるページを設定しました。その際は、 Get the KINTO App - Mobility for all のページが、カタール向けのランディングページの役割をしていました。そのため、サービスのリリース前にまず翻訳しておく必要がありました。 特定のページだけ言語設定することは一般的ではありません。通常、翻訳が完了すると、ウェブサイト上のページがすべて翻訳されます。ただし、こういうシナリオの場合、次のようなソリューションが適用できます。 状況 :アラビア語に設定されたブラウザを使って /sample ページに移動する場合のみ、ページをアラビア語に変換し、言語方向を右から左 (RTL)にする //アラビア語に設定されたブラウザで/sample に移動しているかどうかを確認 const toArabic = this.$route.name === 'sample' && window.navigator.language === 'ar' //ドキュメント文書の方向を設定 document.dir = toArabic ? 'rtl' : 'ltr' //Vuetify の RTL 方向を設定(true or false) this.$vuetify.rtl = toArabic //@nuxtjs/i18n を使用してロケールを設定 //nuxt.config.js で使用するコードを選択 this.$i18n.setLocale(toArabic ? 'ar' : 'en') 上のコードを見ると、ブラウザ言語が特に ar を使用している場合にのみ機能することが分かります。ブラウザが ar-XX 形式の場合はどうなるでしょうか? 言語の決定方法はいくつかありますが、その1つが split() の使用です。この方法を使えば、ロケールの設定も簡単です。 const x = "ar-XX" console.log(x.split("-")[0]) //ar this.$i18n.setLocale(x) $vuetify.rtl と document.dir を使用した RTL 表示 開発中に RTL のレイアウトが全コンポーネントで正しく機能しない理由を時間をかけて調べてみました。 document.dir = ‘rtl' を設定してみたり、 this.$vuetify.rtl = true も試しましたがうまくいかず、最終的には両方を設定することで問題を解決しました。手探りで色々と試しただけでしたが、両方を使用することでコンポーネントが正しく表示されるということが確認できました。ただそれでもこの課題の背景を知りたいという思いが残っていました。 問題の原因は次のとおりです。 $vuetify.rtl のみを使用する <template> <v-icon class="left-arrow" size="100px">mdi-chevron-double-left</v-icon> </template> ... <script> export default { mounted() { this.$vuetify.rtl = true } } </script> ... <style lang="scss" scoped> [dir='rtl'] .left-arrow { transform: rotateY(180deg); } </style> 状況 $vuetify.rtl = true を設定し、同時に [dir='rtl'] を使用する 結果 アイコンは左向きで反転していない 原因 ドキュメントを点検すると、html の方向が示されていないことがわかります。これでは [dir='rtl'] は動作しません ソリューション HTML の方向を RTL に設定するには document.dir = 'rtl' を設定する document.dir のみを使用する <template> <v-expansion-panels> <v-expansion-panel v-for="(item,i) in 5" :key="i" > <v-expansion-panel-header> Item </v-expansion-panel-header> <v-expansion-panel-content> Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. </v-expansion-panel-content> </v-expansion-panel> </v-expansion-panels> </template> ... <script> export default { mounted() { document.dir = 'rtl' } } </script> 状況 document.dir = 'rtl' を設定し、vuetify コンポーネントを使用 結果 一部のコンポーネントが正しく表示されない 原因 RTL がブートストラップ中に正しく動作しない ソリューション this.$vuetify.rtl = true を設定する。画像を見ると、矢印は左側に移動しています。 <script> export default { mounted() { this.$vuetify.rtl = true document.dir = 'rtl' } } </script> まとめ ほとんどのプログラミングフレームワークが複数言語をサポートしていますが、正しく動作させるためには微調整が必要です。 上で書いたような課題に直面しましたが、予定通りにタスクを終えることができて良かったです。ぜひご覧ください! https://www.kinto-mobility.com/app/ KINTOテクノロジーズのサービスを導入する国が増えるにつれて、KINTOのその他プロジェクトでも翻訳が必要になるでしょう。そういった機能を実装する基礎ができたことをうれしく思います。 思い フロントエンドの役割は、息をのむようなデザインやレイアウトでウェブサイトを作り上げることだけではありません。ユーザーエクスペリエンスについても考える必要があるのです。デザインは優れているのに、ターゲットのお客様に満足してもらえない状況を想像してみてください。私たちは本当にお客様のニーズや要望を満たせていると言えるでしょうか? [Desire Path] (/assets/blog/authors/hundanmz/desirePath.png =750x) 参考画像: https://images.squarespace-cdn.com/content/v1/5c6afc627eb88c46e4f41468/1563183013744-RWOU1N19RO77MT0EAH3M/IMG_0873.jpeg 参考 Overview of Language Directions Why Do We Read English From Left To Right? Right to Left Languages | Why are they written this way | Pangeanic Omniglot index by writing direction @nuxtjs/i18n Introduction window.navigator.language Navigator.language - Web APIs | MDN Updating Browser Language Settings Change Chrome languages & translate webpages - Computer - Google Chrome Help Use Microsoft Edge in another language - Microsoft Support Use Firefox in another language | Firefox Help Vuetify Vuetify — A Material Design Framework for Vue.js
アバター
こんにちは! KINTO ONE開発部 新車サブスク開発グループ バックエンドチーム所属の朝日です。 弊グループではKINTO ONEサービスを提供するためのシステムの運用開発をしています。 2023年8月に大規模なシステムリニューアルのリリースを行いました。 長期間にわたるシステム開発の中でチーム一丸となり切磋琢磨・試行錯誤を繰り返してきたわけですが、その中でチームで導入した プルリクエスト(以下PR)レビューを促進するGitHub Actionsのワークフロー がとてもお気に入りなので紹介します。 処理詳細は後述 後回しにされてしまうコードレビュー みなさん、コーディングとコードレビュー、どちらがお好きですか。私は圧倒的にコーディングです。 コードレビューがシステム品質を保つ上でとでも重要な工程ということは重々承知しているものの、タスクの期限が迫っていると自身のコーディングを優先させてしまい、ついついレビューを後回しにしてしまうこともあるのではないでしょうか。 システムリニューアルプロジェクトで実装に着手し始めた当初も、コーディングは終わっているのにレビューがされずOpen状態のPRが多く残ってしまうことがありました。 その結果、以下のような事象が多く起こりチームの課題となっていました。 タスクを完了にすることができずチームとしての進捗状況が把握しにくい 依存関係のあるタスクに着手できない or 影響を与えてしまう mergeの際にコンフリクトが発生しやすくなる これらの事象を防ぐために、今回紹介するPRレビュー促進するGitHub Actionsのワークフローを導入しました。 GitHub Actionsとは 弊社ではソース管理ツールにGitHubを使用しています。 GitHub ActionsとはGitHub上で利用できる自動化ツールで、自動化したい処理をワークフローに記述、リポジトリ内に配置しておくことで任意のタイミングで処理を実行することができます。 GitHub上の機能ということもあり、GitHubのイベントと親和性が高い特徴があります。 @ card 自動レビュワー設定ワークフロー 本題です。 今回紹介するのはPRを作成したタイミングでレビュワーをランダムで自動設定、Slack通知するGitHub Actionsのワークフローです。 ついでにPR作成者の自動アサインとPR上にコメントも残します。 ワークフロー処理詳細 PRが作成されたタイミングでワークフローが実行されます。 ワークフローからAWS Lambda(以下Lambda)[^1]にリクエストします。 Lambda処理の中にチームのメンバー情報を事前に定義しておきます。その際に"仕様担当"と"技術担当"の属性を持たせています。自分以外のチームメンバーの中から仕様レビュー担当と技術レビュー担当のレビュワーを一人ずつランダムで選択します。計二人を選択しているのはチームのルール(PRマージには二人以上のレビュー必須)にも由来しています。 Lambdaから取得した二名のメンバー情報をPRのReviewersに、自身の情報をAssigneesに自動設定、レビュワーにメンション付きのコメントを残します。 設定されたレビュワーに指定したSlackチャンネルでお知らせします。 Slack Webhookに連携 [^1]: AWS Lambdaは関数として定義した処理をサーバーレスで実行できるAWSサービスの一つです。 おすすめポイント レビュワー設定を最小人数に絞る レビュワーをメンバー全員でなく最小人数を絞ることで「自分がレビューしなくても誰かが見てくれるだろう…」という他力本願を排除し、レビューの後回しを抑制します。 チームメンバーに"仕様担当"と"技術担当"の属性を持たせる レビュワー人数を絞るとアサインされたメンバーの得意不得意によってレビューの品質が左右されてしまうことが懸念されました。 そのため、メンバーそれぞれに"仕様担当"と"技術担当"の属性を事前に設定し、ドメイン知識を得意としているメンバーには仕様中心に、技術が得意なメンバーや新規参画したばかりで仕様を勉強中のメンバーには技術面を中心に見てもらうようにしました。 リポジトリを跨いだチームメンバー情報の共有 複数のリポジトリで同じ処理を実行するには、各リポジトリに同じワークフローを配置する必要があります。 メンバー情報の定義をワークフロー内に記述した場合、メンバー新規参画などでメンバー情報を更新したい際に設定している全てのワークフローを修正しなければなりません。 そのためレビュワーランダム選択処理をLambdaで実装し、各リポジトリから参照できるように共通化にしました。 メンバー情報を更新の際はLambda処理の修正のみで完結するようにしています。 Slack通知で即確認可能 ワークフロー内でSlack通知処理を行うことによって、設定されたレビュワーに即時に確認してもらえます。 GitHubもメールでの通知をしてくれますが、利用するコミュニケーションツールを統一することによって通知の見落としを防ぐ効果があります。 自動化による"手間"の削減 GitHub ActionsはCICD自動化ツールとして注目されることが多い機能です。しかしそれ以外にも、今回のワークフローのようなちょっとした手間を自動化することもできます。 元々はPRレビューの促進を目的としたツールでしたが、実際使ってみることにより自動化のメリットを実感することができました。 もし今回のワークフローの処理を自身で行うとなると、チームメンバーの状態によってレビューの依頼に忖度してしまったり、別途Slackでメッセージを送る必要があったりします。 それぞれ小さな手間ではありますが、私のチームでは2年以上今回のワークフローを利用しており、"削減した手間"は相当数に上ります。今ではなくてはならないツールです。 紹介したワークフローのサンプルコード 今回紹介したワークフローのサンプル(Lambda不要ver.)を紹介します。 カスタマイズして使ってみてください。 name: "Auto Assigning Reviewers" on: pull_request: types: [opened, ready_for_review, reopened] jobs: assigning-reviwers: runs-on: ubuntu-latest if: | github.event.pull_request.draft == false steps: - uses: actions/checkout@v3 - uses: actions/setup-node@v3 with: node-version: 16 - run: npm install @slack/webhook - uses: actions/github-script@v6 with: github-token: ${{ secrets.GITHUB_TOKEN }} script: | const { IncomingWebhook } = require('@slack/webhook') // メンバー情報を定義 const MEMBER_INFO = [ { slackName: "@仕様把握子", speciality: 'spec' ,slackMemberId: "XXXXXX", githubName: "XXXXXX"}, { slackName: "@仕様得夫", speciality: 'spec' ,slackMemberId: "XXXXXX", githubName: "XXXXXX"}, { slackName: "@仕様得得", speciality: 'spec' ,slackMemberId: "XXXXXX", githubName: "XXXXXX"}, { slackName: "@技術剛", speciality: 'skill' ,slackMemberId: "XXXXXX", githubName: "XXXXXX"}, { slackName: "@技術任路", speciality: 'skill' ,slackMemberId: "XXXXXX", githubName: "XXXXXX"}, { slackName: "@新参者", speciality: 'skill' ,slackMemberId: "XXXXXX", githubName: "XXXXXX"}, ] // PR作成者を取得 const user = context.payload.sender.login; const author = MEMBER_INFO.find(member => member.githubName === user); // レビュワーをランダムで二人選出 const skills = MEMBER_INFO .filter(member => member.githubName !== author.githubName) .filter(member => member.speciality === 'skill'); const specs = MEMBER_INFO .filter(member => member.githubName !== author.githubName) .filter(member => member.speciality === 'spec'); let getRandomNumber = (min,max) => Math.floor(Math.random() * (max - min + 1)) + min; const chosenSkillMember = skills[getRandomNumber(0, skills.length - 1)]; const chosenSpecMember = specs[getRandomNumber(0, specs.length - 1)]; const issue_number = context.issue.number const pull_number = context.payload.pull_request.number const { repo, owner } = context.repo // PR Reviewersの設定 await github.rest.pulls.requestReviewers({ owner, repo, pull_number, reviewers: [chosenSkillMember.githubName, chosenSpecMember.githubName] }); // PR Assigneesの設定 github.rest.issues.addAssignees({ owner, repo, issue_number, assignees: [user] }); // Slack通知 const title = context.payload.pull_request.title const html_url = context.payload.pull_request._links.html.href const message = `オッス! <@${chosenSkillMember.slackMemberId}> <@${chosenSpecMember.slackMemberId}> \n <@${author.slackId}> の新しいPRのレビュアーに選定されました 🥳 \n <${html_url}|${title}>` // 通知したいSlackチャンネルのwebhook URLを指定 const webhook = new IncomingWebhook('https://hooks.slack.com/xxxxxxxxx') await webhook.send({ text: message, }) // PRにコメント await github.rest.issues.createComment({ owner, repo, issue_number, body: `@${chosenSkillMember.githubName},@${chosenSpecMember.githubName} レビュアーに選定されました 🚀` }) 最後に 今回我が物顔で紹介しましたが、いつも便利ツールを導入してくれる同じチームの丁さん、素敵なツールとアイデアをありがとうございます。 スーパー丁さんの記事は明日公開なのでお楽しみに。 みなさま、GitHub Actionsで素敵な自動化ライフをお過ごしください!
アバター
はじめに KINTOテクノロジーズで、 モビリティマーケット の開発・運用兼テックブログの運用を担当しているリナ( @chimrindayo )です。普段はフロントエンジニアとして、主にNext.jsを用いて実装しています。 最近は、おでんが美味しい季節がやってきてワクワクしています🍢 今年はトマトおでんが食べたいな・・・🤤 さて、KINTOテクノロジーズでは、社外のイベントへの登壇やテックブログの執筆など「習得した知識・スキルのアウトプット」を全社でサポートしています。 今回はテックブログの記事がリリースされるまでにどんなことをやっているか、公開されるまでの過程と各工程でアウトプットを推進するための取り組みをご紹介します! テックブログ運用プロジェクト まずはじめに、KINTOテクノロジーズの「テックブログ運用プロジェクト」というチームが存在します。 テックブログ運用プロジェクトではテックブログの運用を始めとして、社員の知識のインプット・アウトプットを推進することを目的としています。 所属するメンバーは8名で、全員が兼任者です。 他プロジェクトでPdMやエンジニアとして活動する傍ら、楽しくアウトプットできる方法を日々模索しています! https://www.wantedly.com/companies/company_7864825/post_articles/510568 これからご紹介する取り組みは、テックブログ運用プロジェクトに所属するメンバーがアウトプットを推進するために実施している取り組みの一部です! テックブログの公開フロー テックブログの公開フローは、大きく3つのフェーズに分かれています。 1. 執筆 1つ目は、記事を執筆するフェーズです。記事のネタ探しおよび執筆テーマの決定、プロットの作成から記事に落とし込んで執筆するまでが執筆フェーズに該当します。 2. レビュー 2つ目は、執筆された記事のレビューフェーズです。KINTOテクノロジーズでは3段階のレビューを行い、誤字脱字チェックや記事の内容を担保しています。 3. リリース 3つ目は、記事をリリースするフェーズです。リリースフェーズでは、記事の翻訳やGitHubを利用したリリース作業を実施しています。 では、各フェーズでどんなサポートをしているのか、実際に取り組んでいる内容をご紹介します! テックブログの記事が公開されるまで まずはじめに、執筆フェーズでのテックブログ運営チームの取り組みをご紹介します。 相談窓口 アドベントカレンダーの執筆期間は、毎日1時間テックブログチームのメンバーがSlackチャンネルのハドルミーティングで待機し、執筆者が気軽に相談できる体制を作りました。 執筆する上での悩み・マークダウンの書き方など、ちょっとした悩みを解消できる場として、多い時は1日に5~6人ほどが利用しています。 実際に「他の人にも相談窓口勧めておきましたー!」などの声があり、思ったより好評だったと思います✨ 執筆者インタビュー 「ネタが思い浮かばないけど、記事を書いてみたい!」「ネタはあるけど、記事にうまくまとめられるか不安」と考えている執筆者向けに、テックブログの運営メンバーがインタビューする時間を設けています。 インタビューは30分ほど行い、入社してから現在までどんな業務に取り組んできたのか、また各業務における課題や問題の解決方法などを中心にヒアリングしています。 そして、ヒアリング結果をもとに記事のプロット作成までをインタビューの時間内で行います。 インタビューを通して「記事書くの不安だな・・・」と思っている人に「思ったより書けそう!」と記事を書くことに対するハードルを下げること、そして日々取り組んでいる業務にいかに価値があるか再認識してもらうことで、執筆者の魅力を最大限に引き出すことを目的としています。 また、記事の内容は業務で携わった内容に絞っています。 そして、技術に携わる人全員がアウトプットできるよう、いわゆる「技術」だけでなく、最大限に技術を活かすためのマネジメントやオフィスの環境など、技術を支えるスキルをアウトプットすることも推進しています。 レビュー 記事を執筆した後は、異なる観点で3段階のレビューを行います。 3段階のレビューを通して記事のクオリティを担保し、読み手にわかりやすい記事を作成することを目的としています。またレビューをする上で「執筆者へ感謝すること」を大切にしています。 コンテンツレビュー まずはじめに、記事の内容の正誤をチェックするためのコンテンツレビューを実施しています。 主に以下の観点で執筆者のチームメンバーやマネージャーがレビューします。 多くの記事は2~3名以上のレビュアーがつき、より読みやすい表現の提案や記事のGood Pointなどもフィードバックをしています! レビュー観点 ・ 専門家として内容の正誤を確認 ・ 秘匿情報の有無を確認 テックブログチームレビュー 次にテックブログ運用チームによるレビューです。 テックブログチームでは、以下の観点でレビューを行っています。 執筆者の文調を大切にしつつ、読みやすい文章・記事構成の提案、誤字脱字や「てにおは」をチェックします。 また読み手の立場として「この情報も知りたい!」と内容の追加を提案する場合もあります。 レビュー観点 ・ 誤字脱字 ・ 著作権 私自身テックブログチームとしてレビューすることがほとんどなのですが、レビューを通じて「JIRAでGitHubのデプロイ履歴が追えるのか・・・!」「このレビューガイドラインは自分のプロジェクトにも導入したい」など、新たな学びや発見があっておもしろいです💡 CIOレビュー さいごに CIO 景山さん によるレビューです。 すべての記事を景山さんがレビューしており、景山さんの承認をもって記事のリリース準備が完了します🎉 私自身このレビュー工程があるおかげで、執筆者が自信をもって記事をリリースできるようになっていると考えています。 リリース すべてのレビューが終了した後は、リリースに向けて最終調整をします。 今回は特に力を入れている「記事の翻訳」についてご紹介します! 記事の翻訳 KINTOテクノロジーズに在籍する社員のうち約25%が外国籍の社員です(2023年11月時点) したがって「母国語で執筆したい」「日本語で書くのが不安」と考えているメンバーもいます。 そういったメンバーのニーズに応えるためにすべての記事を日⇔英翻訳し、執筆者が日本語 or 英語どちらの言語で執筆するか選択できるようにしています🔤 翻訳のベースはLSP (Language Service Provider)、外部協力会社を使用し、最終的にLQAの作業を内部で実施しています。 LQAとは『Linguistic Quality Assurance』の略で「言語品質保証」のことを指しています。 外部リソースだけで構成された文章では、どうしても不適切な言い回しになってしまったり、執筆者の意図とは異なる文章になってしまうことがあります。 こうした不自然な表現やスペルミスをLQAの段階でチェックしています。 (参考: 外国籍社員の活躍 ) さいごに 今回は、テックブログが公開されるまでに実施しているアウトプット推進の取り組みをご紹介しました。引き続き、KINTOテクノロジーズのメンバーがアウトプットがしやすい環境を作るために試行錯誤しながら改善してきたいと思います! また、弊社のテックブログによるアウトプットがみなさまのお役に立てれば幸いです。 テックブログの運用や技術広報について、みなさまと意見交換ができれば嬉しいです! ご意見は X までご連絡ください🕊 https://twitter.com/KintoTech_Dev
アバター
こんにちは、Shweta Oza です。 2022年4月に、KINTOグローバル開発グループに加わりました。アプリケーション開発担当で、最近は DevOps に興味を感じています。現在の所属はクーポンチームです。 私たちのチームは、世界中のお客様/取引先向けにクーポン API の開発・管理を行っています。各種機能の開発と展開を継続的に進めています。どのプロジェクトにおいても、データのバックアップやロールバックを含むデータベースのメンテナンスは重要です。メンテナンスしておけば、新バージョンのリリース時に問題が発生しても、変更を安全にロールバックし、バックアップを復元することができます。 オープンソースのデータベース移行ツールとして、当社では Flyway などのソリューションを使用しています。Flywayは、コンフィギュレーションよりもシンプルさと規則性を重視しています。基本のコマンドは7つあります:Migrate、Clean、Info、Validate、Undo、Baseline、Repair 以下に、スクリプトを使用してデータベースを手動でバックアップする方法について、いくつかの概念と簡略化した手順を示します。 概要 クーポンシステムは、KINTO のサービスや提携先のサービスで利用できるクーポンを簡単に発行・管理できるツールです。クーポンシステムでは、新しい機能を継続的に開発することで改善やアップグレードを行っています変更が発生するたびに、クーポンシステムで使用するデータベースを管理する必要があります。これはソフトウェアプロジェクトにおいて非常に重要な作業です。私たちの場合は MySQL を使用しています。 クーポンシステムの新しいリリースがある場合は、毎回次の手順を踏んでいます: 機能を開発する。 ローカルでテストしてから AWS テスト環境でテストを行う。 リリース前に DB のバックアップを取る。 新機能をリリースする。 問題があれば、リリース前のバージョンにロールバックする。 これらを実施するため、バックアップとロールバックのスクリプトを用意し、CLI でのインストラクション数を減らしています。これで時間の節約にもなりますし、バックアップやロールバックで構造の一貫性も維持できます。 基本を知る データベースのバックアップとは? データベースのバックアップとは、サーバーに保存したデータのコピーのことです。 データベースのバックアップは何のため? バックアップを取るのは、予期せぬデータ損失を防ぐためです。 障害が発生した場合、元データが失われたり破損したりしても、バックアップがあれば簡単にデータを復元することができます。 データベースのバックアップにはどんなタイプがあるの? 物理バックアップ ^1 物理バックアップは、物理的なファイルのバックアップで、データベースの保存と復元に使用されます。これらには、様々なデータファイル、コントロールファイル、アーカイブ REDO ログなどが含まれます。通常、物理バックアップのデータは、クラウド、オフラインストレージ、磁気テープ、ディスクに保存されます。 物理バックアップには方法が2つあります: オペレーティング・システム・ユーティリティ リカバリマネージャー メリット: データの完全管理。 保管コストが低い。 バックアップデータの迅速な取得。 デメリット: どんなデバイスに保存してもデータは破損する可能性があり、データ復旧が困難になることがある。 自然災害でデータが破壊される可能性がある。 記憶装置自体の紛失。 論理バックアップ ^2 論理バックアップには、データベースから取得した論理データが含まれます。ビュー、プロシージャ、ファンクション、テーブルなどです。 これは、ユーザーがデータベースのコピーを復元したり、別の場所に転送したりする場合に便利です。 論理バックアップは、データ損失の防止では物理バックアップほど安全ではありません。構造の詳細を提供するだけです。そのため毎週、完全な論理バックアップを実行しなければなりません。 論理バックアップは物理バックアップの補助として使用されます。 メリット: データベース全体を以前のインスタンスに復元する必要がある場合に便利。 より複雑で、細かなリカバリ機能を備えている。 デメリット: 特別なコンポーネントのリカバリには利用できない可能性がある。 物理バックアップに比べて安全性が低い。 構造の詳細を提供するのみ 論理データベースのバックアップについてローカル及び AWS CLI の両方で見ていきます。DB でのデータ操作は、開発者の観点でも重要なステップだからです。 AWS CLI を使用して Amazon RDS(リレーショナルデータベースサービス)を管理する Amazon RDS [^3] [^3]: https://aws.amazon.com/rds/ Amazon RDS はサーバーを用意することなく、クラウド上の RDB を安全に利用できるサービスです。Amazon RDSは、オープンソースで広く使われているMySQLやMariaDB, PostgreSQLをはじめ、Oracle Database, microsoft SQL serverなどもサポートしています。支払いは使った分だけです。 Amazon CLI [^4] [^4]: https://aws.amazon.com/cli/ AWS CLI は、AWS サービスを管理するための統合ツールで、Linux や macOS ではターミナルから、Windows ではコマンドプロンプトから実行します。 基本的には専用ツールからコマンド入力で実行しますが、スクリプトに記述することで処理の自動化もできます。 AWS CLI から Amazon RDS を管理する場合は、コマンドを実行してもすぐ反映されるとは限らない点に注意が必要です。 というのも、Amazon RDS には複数の障害対策が施されており、設定変更がすべての対策に反映されるまでに時間がかかるからです。 同様に簡単なスクリプトを各種環境で書くことで、バックアップとロールバックのタスクを実行することができるでしょう。 開発者レベルで CLI から DB のバックアップやロールバックを行うにはどうすればいい? SQL フォーマットで mysqldump の使用 mysqldump とは [^5] [^5]: https://dev.mysql.com/doc/refman/8.0/en/mysqldump.html mysqldump クライアントは、 Igor Romanenko が最初に書いた論理バックアッププログラムです。データベースは通常、バックアップや別のデータベースサーバー(MariaDB や MySQL とは限りません)に転送するための SQL ステートメントのリスト(「SQL ダンプ」)の形をしています。ダンプには通常、テーブル作成やテーブル入力を行うための SQL ステートメントが含まれています。 ダンプファイルの使用 ダンプファイル [^6] とは [^6]: https://dev.mysql.com/doc/refman/8.0/en/mysqldump.html ダンプファイルは、リリース後のアップグレードの際にも便利です。アップグレードの際に、古いリリースを使用しているデータベースをダンプし、新しいリリースでロードすることができます。 ハイライト データありの状態(つまり DDL 及び DML の両方)で DB ダンプを取得する場合 mysqldump -u root -p DB_Name > backup.sql # AWS CLI mysqldump -u $user -p$password -h $rds_endpoint > backup.dump # To avoid warnings like below you can use the following options and add the login credentials in a CNF file. # mysqldump: [Warning] Using a password on the command line interface can be insecure. # --set-gtid-purged=OFF # Warning:A partial dump from a server that has GTIDs will by default include the GTIDs of all transactions, even those that changed suppressed parts of the database.If you don't want to restore GTIDs, pass --set-gtid-purged=OFF.To make a complete dump, pass --all-databases --triggers --routines --events. # --defaults-extra-file=path/to/extra.cnf # cat << EOF > path/to/extra.cnf # [client] # user=${MYSQL_USER} # password=${MYSQL_PASSWORD} # host=${RDS_ENDPOINT} # EOF mysqldump --defaults-extra-file=path/to/extra.cnf --skip-column-statistics --single-transaction --set-gtid-purged=OFF --databases $DB > backup.dump データなし(つまり DDL のみ)で DB ダンプを取る場合 mysqldump -d -u root -p DB_Name > backup.sql # AWS CLI mysqldump -d -u $user -p$password -h $rds_endpoint > backup.dump # Below are some useful options to consider # --no-data, -d # Do not write any table row information (that is, do not dump table contents).This is useful if you want to dump only the CREATE TABLE statement for the table.For example, when you want to create an empty copy of the table by loading the dump file. # --defaults-extra-file=file_name # Read this option file after the global option file but (on Unix) before the user option file.If the file does not exist or is otherwise inaccessible, an error occurs.If file_name is not an absolute path name, it is interpreted relative to the current directory. # The --single-transaction option and the --lock-tables option are mutually exclusive because LOCK TABLES causes any pending transactions to be committed implicitly. mysqldump --no-data --defaults-extra-file=path/to/extra.cnf --skip-column-statistics --single-transaction --set-gtid-purged=OFF --databases $DB > backup.dump 以前のバージョンにロールバックするには mysql -u root -p DB_Name < backup.dump # AWS CLI mysql -u $user -p$password -h $rds_endpoint < backup.dump バックアップ用スクリプト ディレクトリを使用する前にスクリプトを編集する必要があるもの: pathToParameterStoreKeyValueMYSQL_USER pathToParameterStoreKeyValueMYSQL_PASSWORD pathToParameterStoreKeyValueRDS_ENDPOINT DB_Name ######################################################################### ######################################################################### ##### ##### Description:Shell Script to take backup of existing data AWS ##### 1.Choose the env and switch the db connection ##### 2.Create backup directory with today's date ##### 3.Create dump file to backup folder with current db ##### 4.Check dump file size ##### ######################################################################### ######################################################################### read -p "Enter Your Env(env): " x echo "Welcome to ${x} Env!" # MySQL server credentials MYSQL_USER=$(aws ssm get-parameter --with-decryption --name /${x}/pathToParameterStoreKeyValueMYSQL_USER | jq -r .Parameter.Value) MYSQL_PASSWORD=$(aws ssm get-parameter --with-decryption --name /${x}/pathToParameterStoreKeyValueMYSQL_PASSWORD | jq -r .Parameter.Value) RDS_ENDPOINT=$(aws ssm get-parameter --with-decryption --name /${x}/pathToParameterStoreKeyValueRDS_ENDPOINT | jq -r .Parameter.Value) # Create a configuration file to maintain MySQL login details cat << EOF > mysql-dbaccess.cnf [client] user=$MYSQL_USER password=$MYSQL_PASSWORD host=$RDS_ENDPOINT EOF # Set the folder name with date format (example:2022-08-15) DATE_FORMAT=$(date +"%Y-%m-%d") TIMESTAMP=$(date +%H%M%s) # Path to local backup directory BACKUP_DIR="tmp/release/backup/${x}/dbbackup/${DATE_FORMAT}" # Use database's names DB="DB_Name" echo "########################################DATABASE########################################" echo "Using Database (DB_Name)" echo "########################################DATABASE########################################" # Create backup directory with today's date mkdir -p ${BACKUP_DIR} FILENAME_PREFIX="backup_${x}_DDL_DML_${TIMESTAMP}_" FILENAME_POSTFIX=".dump" read -p "Enter version eg: v0.0.1: " d FILENAME=$FILENAME_PREFIX${d}$FILENAME_POSTFIX echo "########################################FILEPATH########################################" echo "Created directory" ${BACKUP_DIR} echo "File will be saved as ${FILENAME} " mysqldump --defaults-extra-file=mysql-dbaccess.cnf --single-transaction --set-gtid-purged=OFF --databases $DB > ${BACKUP_DIR}/${FILENAME} echo "File saved at ${BACKUP_DIR}/${FILENAME}" echo "########################################FILESPATH########################################" # check File size file=${BACKUP_DIR}/${FILENAME} filesize=$(ls -lh $file ) echo "########################################FILESIZE########################################" echo "$file has a size of $filesize" echo "########################################FILESIZE########################################" # Remove the file after executing shell rm mysql-dbaccess.cnf バックアップの手順 AWS CLI にシェルスクリプトを配置する AWS にログイン AWSマネジメントコンソール → AWSシステムマネージャー→ セッションマネージャー →セッション開始の順にクリックする バックアップを取りたい env を入力する 例: {env}-{project_name}-{maintenance_server_name} セッション開始 dbbackupDDL_DML.sh ファイルが存在するかチェックする ファイルが存在しない場合は、AWS CLI に dbbackupDDL_DML.sh ファイルを配置する ファイルを実行する sh-4.2$ ls dbbackupDDL_DML.sh mysql.sh tmp sh-4.2$ sh dbbackupDDL_DML.sh Enter Your Env(env): env Welcome to env Env! ########################################DATABASE######################################## Using Database (db_name) ########################################DATABASE######################################## Enter version eg: v0.0.1: v0.0.1 ########################################FILEPATH######################################## Created directory tmp/release/backup/env/dbbackup/2022-08-12 File will be saved as backup_env_DDL_DML_06311660285870_v0.0.1.dump File saved at tmp/release/backup/env/dbbackup/2022-08-12/backup_env_DDL_DML_06311660285870_v0.0.1.dump ########################################FILESPATH######################################## #######################################FILESIZE######################################## tmp/release/backup/env/dbbackup/2022-08-12/backup_env_DDL_DML_06311660285870_v0.0.1.dump has a size of -rw-r--r-- 1 ssm-user ssm-user 1.7M Aug 12 06:31 tmp/release/backup/env/dbbackup/2022-08-12/backup_env_DDL_DML_06311660285870_v0.0.1.dump ########################################FILESIZE######################################## sh-4.2$ ダンプファイルの内容を確認するには sh-4.2$ less tmp/release/backup/env/dbbackup/2022-08-12/backup_env_DDL_DML_06311660285870_v0.0.1.dump sh-4.2$ バックアップは tmp/release/backup/env/dbbackup/{currentDate}/{FileNameWithTimestamp&Version} のフォルダに入ります。 同じ日に複数回、同一フォルダへダンプしたい場合、タイムスタンプ付きのファイルを準備します。 sh-4.2$ cd tmp/release/backup/env/dbbackup/ sh-4.2$ ls 2022-08-09 2022-08-10 2022-08-12 sh-4.2$ cd tmp/release/backup/env/dbbackup/2022-08-12/ sh-4.2$ ls backup_env_DDL_DML_06311660285870_v0.0.1.dump backup_env_DDL_DML_06371660286257_v0.0.1.dump sh-4.2$ ロールバック用スクリプト ディレクトリを使用する前にスクリプトを編集する必要があるもの: pathToParameterStoreKeyValueMYSQL_USER pathToParameterStoreKeyValueMYSQL_PASSWORD pathToParameterStoreKeyValueRDS_ENDPOINT DB_Name ######################################################################### ######################################################################### ##### ##### Description:Shell Script to rollback to target SQL ##### 1.Choose the env and switch the db connection ##### 2.Create rollback directory with today's date ##### 3.Choose and input the backup file ##### 4.Input the version of dump file ##### 5.Copy backup dump file to ROLLBACK folder ##### 6.Create dump file to ROLLBACK folder with current db ##### 7.Rollback db with backup dump file ##### 8.Comparison.... ##### ######################################################################### ######################################################################### read -p "Enter Your Env(env): " x echo "Welcome to ${x} Env!" # MySQL server credentials MYSQL_USER=$(aws ssm get-parameter --with-decryption --name /${x}/pathToParameterStoreKeyValueMYSQL_USER | jq -r .Parameter.Value) MYSQL_PASSWORD=$(aws ssm get-parameter --with-decryption --name /${x}/pathToParameterStoreKeyValueMYSQL_PASSWORD | jq -r .Parameter.Value) RDS_ENDPOINT=$(aws ssm get-parameter --with-decryption --name /${x}/pathToParameterStoreKeyValueRDS_ENDPOINT | jq -r .Parameter.Value) # Set the folder name with date format(eg:2022-08-15) DATE_FORMAT=$(date +"%Y-%m-%d") TIMESTAMP=$(date +%H%M%s) # Path to local rollback directory history ROLLBACK_DIR="tmp/release/rollback/${x}/dbRollback/${DATE_FORMAT}" # Use database's names DB="DB_Name" echo "########################################DATABASE########################################" echo "Using Database (DB_Name)" echo "########################################DATABASE########################################" # Create rollback directory with today's date mkdir -p ${ROLLBACK_DIR} read -p "Enter full dumpFile Path to which you want to rollback: " df echo "dumpFile ${df} selected!" FILENAME_ROLLBACK_PREFIX="rollback_${x}_DDL_DML_${TIMESTAMP}_" FILENAME_BACKUP_PREFIX="backup_${x}_DDL_DML_${TIMESTAMP}_" FILENAME_POSTFIX=".dump" read -p "Enter version eg: v0.0.1: " d FILENAME_ROLLBACK=FILENAME_ROLLBACK_PREFIX${d}$FILENAME_POSTFIX FILENAME_BACKUP=FILENAME_BACKUP_PREFIX${d}$FILENAME_POSTFIX echo "########################################FILEPATH########################################" echo "Created directory" ${ROLLBACK_DIR} # copy dump file to backup folder cp ${df} ${ROLLBACK_DIR}/${FILENAME_ROLLBACK} ROLLBACK_FILE=${ROLLBACK_DIR}/${FILENAME_ROLLBACK} BEFORE_ROLLBACK_DUMP=${ROLLBACK_DIR}/"BeforeRollback_${FILENAME_BACKUP}" AFTER_ROLLBACK_DUMP=${ROLLBACK_DIR}/"AfterRollback_${FILENAME_BACKUP}" mysqldump -u $MYSQL_USER -p$MYSQL_PASSWORD -h $RDS_ENDPOINT --databases $DB > ${BEFORE_ROLLBACK_DUMP} echo "Dump Before Rollback ${BEFORE_ROLLBACK_DUMP}" echo "Rollback to DDL_DML of sql file located at ${ROLLBACK_FILE} " mysql -u $MYSQL_USER -p$MYSQL_PASSWORD -h $RDS_ENDPOINT --databases $DB < ${ROLLBACK_FILE} echo "Rollback successfully done with ${ROLLBACK_FILE}" mysqldump -u $MYSQL_USER -p$MYSQL_PASSWORD -h $RDS_ENDPOINT --databases $DB > ${AFTER_ROLLBACK_DUMP} echo "Dump After Rollback ${AFTER_ROLLBACK_DUMP}" echo "########################################FILESPATH########################################" # check File size before Rollback fileBeforeRollback=${ROLLBACK_DIR}/${BEFORE_ROLLBACK_DUMP} filesizeBeforeRollback=$(ls -lh fileBeforeRollback ) echo "########################################FILESIZE BEFORE ROLLBACK########################################" echo "$fileBeforeRollback has a size of $filesizeBeforeRollback" echo "########################################FILESIZE BEFORE ROLLBACK########################################" # check File size after Rollback fileAfterRollback=${ROLLBACK_DIR}/${AFTER_ROLLBACK_DUMP} filesizeAfterRollback=$(ls -lh fileAfterRollback ) echo "########################################FILESIZE AFTER ROLLBACK########################################" echo "$fileAfterRollback has a size of $filesizeAfterRollback" echo "########################################FILESIZE AFTER ROLLBACK########################################" Footer ロールバックの手順 AWS CLI にシェルスクリプトを配置する AWS にログイン AWSマネジメントコンソール → AWSシステムマネージャー→ セッションマネージャー →セッション開始の順にクリックする ロールバックしたい env を入力する 例: {env}-{project_name}-{maintenance_server_name} セッション開始 dbRollbackDDL_DML.sh ファイルが存在するかチェックする ファイルが存在しない場合は、AWS CLI に dbRollbackDDL_DML.sh ファイルを配置する ファイルを実行する sh-4.2$ ls dbRollbackDDL_DML.sh mysql.sh tmp sh-4.2$ sh dbRollbackDDL_DML.sh Enter Your Env: env Welcome to env Env! ########################################DATABASE######################################## Using Database (DB_Name) ########################################DATABASE######################################## Enter full dumpFile Path to which you want to rollback: tmp/release/backup/env/dbbackup/2022-08-12 Enter version eg: v0.0.1: v0.0.1 ########################################FILEPATH######################################## Created directory tmp/release/rollback/env/dbRollback/2022-08-13 Dump Before Rollback tmp/release/rollback/env/dbRollback/2022-08-13/BeforeRollback_backup_env_DDL_DML_06311660285870_2022-08-13.dump Rollback to DDL_DML of sql file located at tmp/release/rollback/env/dbRollback/2022-08-13/backup_env_DDL_DML_06311660285870_2022-08-13.dump Rollback successfully done with tmp/release/rollback/env/dbRollback/2022-08-13/backup_env_DDL_DML_06311660285870_2022-08-13.dump Dump After Rollback tmp/release/rollback/env/dbRollback/2022-08-13/AfterRollback_backup_env_DDL_DML_063116602859099_2022-08-13.dump ########################################FILESPATH######################################## ########################################FILESIZE BEFORE ROLLBACK######################################## tmp/release/rollback/env/dbRollback/2022-08-13/BeforeRollback_backup_env_DDL_DML_06311660285870_2022-08-13.dump has a size of -rw-r--r-- 1 ssm-user ssm-user 1.7M Aug 13 06:31 tmp/release/rollback/env/dbRollback/2022-08-13/BeforeRollback_backup_env_DDL_DML_06311660285870_2022-08-13.dump ########################################FILESIZE BEFORE ROLLBACK######################################## ########################################FILESIZE AFTER ROLLBACK######################################## tmp/release/rollback/env/dbRollback/2022-08-13/AfterRollback_backup_env_DDL_DML_063116602859099_2022-08-13.dump has a size of -rw-r--r-- 1 ssm-user ssm-user 1.6M Aug 13 06:31 tmp/release/rollback/env/dbRollback/2022-08-13/AfterRollback_backup_env_DDL_DML_063116602859099_2022-08-13.dump ########################################FILESIZE AFTER ROLLBACK######################################## sh-4.2$ 普段の業務でどう活かすか? 上記のようなスクリプトやコマンドを使用することで、入力やログインの手間を減らし、また多くのドキュメントを何度も参照する必要がなくなります。これにより、迅速なバックアップの取得が可能になります。 コマンドを手入力する際の時間とエラーを削減できます。 必要なときにいつでも参照できる、体系的で整ったフォルダ構造が提供されます。 DB のアップグレードの際にデータの不整合が発生した場合や、次のバージョンのシステムをリリースする必要がある場合、障害が発生しデータをロールバックする必要がある場合など、これらの手順が必要になります。
アバター
あなたのプロダクトマネージャーとしての「説明」を考えよう -- 書評:プロダクトマネージャーのしごと第2版 自己紹介 Woven Payment Solution開発Gの亀井です。 我々のチームの仕事を一言でいえば「Woven Cityにおける決済システムの開発」になります。 私はWoven by Toyotaというトヨタグループの別会社に出向しており、普段はそちらで活動しています。 Woven by Toyotaはいくつか事業を持っているのですが、Woven City開発はそのうちのひとつです。 この記事はプロダクトマネージャー発売記念プレゼントに応募した際の、書評を書いて公開するというお約束に対する回答でもあります。 https://www.attractor.co.jp/news/product-management-in-practice/ PdMになったきっかけ 実は私はプロダクトマネージャー(Pdm)ではありません。現時点でもPdMという肩書きはもっていません。自称PdMです。 もともとはバックエンド中心のソフトウェアエンジニアで、求めに応じてフロントエンドも書いたりするユーティリティプレイヤーでした。 コード決済システムの経験もあり、それが今の仕事にもつながっています。 その後エンジニアリングマネージャー(EM)もやってきたので、評価とか組織とか採用とかそういった仕事もできます。 ですが、このプロジェクトのために出向するにあたって私に期待されている役割は技術寄りのPdMみたいなものと言われました。 今になって思えば、この時もう少し具体的に期待値を聞いておくべきでしたが、おそらく誰も明確な答えは持っていなかったでしょう。 自分自身でもよくわからないなあと思いながら、技術選定を行い最初のプロトタイプを構築し、採用活動をしてメンバーを増やしと リードエンジニアとEMを混ぜたようなことをやっていました。 一方で他のチームの人から質問が来ることも多く、それに対して資料を用意したり対面で会議(トヨタ用語で面着といいます)をしたりと説明することにも多くの時間を割いていました。 それでも自分で手を動かすということは続けていたのですが、メンバーが増えてくると明確に私が作業のブロッカーになりはじめました。 そこでメンバーとも話し合い、決済について知見のある自分が作るべき機能を説明して、それを他のメンバーが作るという役割分担のほうが生産性があがるだろうということになり、手を動かさないことを決断しました。 この時から自分自身でも、自分はPdMであるという自己規定ができるようになったと思います。これがだいたい一年ほど前ぐらいのことです。 手を動かすのを止めるという決断については、以前EMをやったときも似たような判断をしていたので、そこに対する葛藤はありませんでした。 また機会があれば自分で手を動かす日もくるでしょう。 この本に興味を持ったきっかけ PdMという自覚を得たのはよいのですが、具体的にPdMって何するんだっけ?ということについて確信が持てずにいました。 目先でやることはあるので、何もしないということは無くなにかしら出来上がってはいくのですが、自分自身がどんな成果を出しているのか説明しきれないというもやもやがありました。 プロダクトマネージャーについて書かれた記事や本を乱読するもののいまいち腹落ちはできず、消化不良が続きました。 この本の第1版は読まなかったあたり、そこまで本気で調べてもいなかったともいえますが。 そんな折、この本の感想がチラチラ目に入るようになりました。どれも評価が高く、サブタイトルの「1日目から使える実践ガイド」というのも心に響いたのですが、そのうち買うかーぐらいの気持ちでした。 完全に言い訳ですが、その時みかけた感想はどれも現役バリバリのプロダクトマネージャーのもので、わかっている人に伝わるタイプの本なのかなーという疑念があったのです。 そんなときプレゼント企画を発見し、ここまで気になっていて応募しない手はないなと申し込んだのでした。 ここから書評がはじまります 結論から書くと、この本はとてもよい本です。私のようなPdMになりたての人、PdMを目指す人だけでなく、これからPdMという役割を導入したい組織にも役立ちます。 あるいは自分たちの組織にいる肩書きのないPdMを発見するのにも役立ちますし、自分たちの開発プロセスにプロダクトマネジメントを導入するためにも読んだ方がよいです。 その意味ではこの本は0日目から使えるとも言えます。 プロダクトマネジメントとは この本の偉大なところはPdMという役割は組織によって異なり、世の中の組織が多様であるのと同様に、あるいは世の中の組織が立ち向かう問題が多様であるのと同様に、PdMに求められる役割も多様であるということを認めているところです。 ですがそれだとPdMという役割は存在しないということになってしまいます。この本ではPdMのあるいはプロダクトマネジメントの定義を断念しています。 その代わりに 説明 によってその輪郭を描こうとしています。優れた 説明 の例として別の本『 プロダクトマネジメント ―ビルドトラップを避け顧客に価値を届ける 』から引くかたちで、『ビジネスと顧客のあいだの価値交換の管理人』をあげていますが、PdMやそれを必要とする組織はこれや他の 説明 を元に自分たちの組織にあった 説明 を構築する必要があるでしょう。 これは本には書かれていませんが、それぞれの考える 説明 が互いにマッチするかどうかというのは、PdMを採用したり内部から登用にするにあたって重要なポイントになるのだと思います。 もちろんこれは他の職種にも言えることです。ただPdMという役割は他の職種より抽象的であるだけ 説明 の重要度が大きくなると考えます。 あるPdMがどんな経歴だったとしても、 説明 にミスマッチがある組織では活躍できないでしょう。 COREスキル ではそのような抽象的なPdMという役割にどのようなスキルが必要なのかについて、この筆者は次の4つの能力をあげています。 Communicate (コミュニケーション) Organize (組織化) Research (リサーチ) Execute (実行) そして、この4つの能力の頭文字をとって COREスキル と名付けています。 この本のほとんどはこのCOREスキルをどのように発揮するかについて書かれています。 といっても、例えば「Communicateの章」みたいなものがあるわけではありません。 開発を行う上でのあるあるネタに対して、こんな感じにCOREスキルを発揮できるといいんじゃない?みたいな 説明 がされるというのがこの本の構成です。 焦点をコミュニケーションに当てたり、組織作りに当てたりと章によってテーマはあるのですが、発揮すべきCOREスキルは混然として描かれます。 ところで自分の問題は解消されたのか 自分の肩書きはともかくとして、やっていることの半分ぐらいはPdMと言えるなという実感を持てています。それだけでもこの本を読んだ価値はありました。 チームに自分以外のPdMを入れるとしたら、こういうことをやってほしいという 説明 を作ることができたら、より 組織化(Organize) 出来そうなので、挑戦してみてもよいかもしれません。 一方でもし自分が本気で今後もPdMを続けていく、目指すというのであれば自分流のPdMの 説明 をCOREスキルを絡めて構築するとよさそうなのですが、自分の中にはまだその覚悟はありません。 おわりに 改めてこの本はPdMという役割について考える上でとてもよい導き手となってくれる良い本です。 PdMの人もそうでない人も、PdMがあるいはプロダクトマネジメントが自分たち組織にどのように関わっているのか、 説明 を構築するよい機会を与えてくれると思います。 この本というプロダクトを届けてくださった、著者の Matt LeMay 様、訳者のみなさま、O'Reilly 社様、本をプレゼントいただいた株式会社アトラクタ様ありがとうございました。
アバター
はじめに KINTOテクノロジーズの新車サブスクグループに所属している森本です。バックエンドエンジニアとして日々開発しています。 2022年度のアドベントカレンダーでは、社内の若手メンバーでGraphQLの勉強会を実施したことについて書きました。 今回は、入社した2021年8月から2023年8月のリリースまでの2年間携わった、サブスクサイト(KINTO ONE)のリニューアルについて記載します。その中でも、入社して初めての仕事であるテーブル設計の見直しについて記載します。 テーブル設計で盛り込んだこと 以下の表はリニューアルする前後のテーブルのイメージです。実際に業務で使用しているテーブルではありません。 このテーブルは社員情報を格納するもので、更新があった場合はそのレコードを論理削除し、新しいレコードを追加することで履歴管理できるようになっています。 ※レコードを削除せず、フラグを立てることで削除扱いにすることを論理削除と呼びます。大切な値の更新履歴を追うことができるというメリットがあります。 リニューアル前 id employee_id mail_address pref group_and_role delete_flag 1 E-001 email1@XX.XX 23 HR_Manager false 2 E-002 email2@XX.XX 25 HR_Staff true 3 E-002 email2@XX.XX 14 Development_Manager false 4 E-003 email3@XX.XX 1 Development_Staff false リニューアル後 id employee_id mail_address prefecture group staff is_deleted 1 E-001 email1@XX.XX AICHI HR Manager false 2 E-002 email2@XX.XX SHIGA HR Staff true 3 E-002 email2@XX.XX KANAGAWA Development Manager false 4 E-003 email3@XX.XX HOKKAIDAO Development Staff false 各カラムは以下の情報を表します。 id :社員テーブルでレコードを一意に特定するID employee_id :社員を一意に特定するID mail_address :メールアドレス pref / prefecture :都道府県 group_and_role / group ・ role :所属グループとロール delete_flag / is_deleted :そのレコードが削除されているかどうか 4つの見直しポイント カラムに略称を使用しない 値を数字に置き換えない 1つのカラムに複数の意味をもたせない フラグ系のカラムは _flag と命名しない 1. カラムに略称を使用しない カラム名 pref のように、カラム名を省略しないようにしました。 これが都道府県を表すことは何となく理解できるでしょう。 しかし、これをドキュメントやソースコードで使用する場合、 pref や prf などの表記揺れが発生する可能性が高くなります。また、そもそも何の略称かわからなくなってしまう場合もあります。 そのため略称は使用せず、長くても prefecture のように表記するようにしました。 2. 値を数字に置き換えない リニューアル前のテーブルを見ると、 pref の値として23が入っています。47あるうちの都道府県のどこかを表すと推測できますが、23番目は一体どこなのでしょうか。 直感的でないため値が何を表しているかが分かりません。値を見るたびに数字と都道府県の組み合わせを参照する必要があり、可読性が下がります。また誤りに気づきにくくなります。そのため AICHI のように、都道府県をそのまま格納するようにしました。 そして不正な値が格納されている場合は、プログラムで読み込み時にエラーになるので気づくことができるというメリットもあります。 言語はJava、O/RマッパーはJPAを使用する場合を例として挙げます。 prefecture のenumクラスを以下のように定義し、Userエンティティではその型を使用することで AICHIA のような不正な値があればエラーになり、気づくことができるというメリットもあります。 public enum Prefecture { HOKKAIDO("北海道"), . . . AICHI("愛知県"), . . . OKINAWA("沖縄県"); private String value; } @Entity public class Employee { /** ID */ @Id private Long id; . . . /** 都道府県 */ @Enumerated(EnumType.STRING) @Column private Prefecture prefecture; . . . } マッピングできない場合に、以下のようなExceptionが発生します。 java.lang.IllegalArgumentException: No enum constant com.example.demo.values.Prefecture.AICHIA 3. 1つのカラムに複数の意味をもたせない リニューアル前の group_and_role ようなカラムは、1つのカラムに所属グループとロールの2つの情報を持っていました。一見すると問題ないように見えますが、値を追加・削除・変更する場合の柔軟性が下がります。 元々以下のような4つの定義があった場合を想定します。 HR_Manager HR_Staff Development_Manager Development_Staff 仮に Development グループの名称を NewDevelopment へ変更しようとなった場合、更新SQLの条件が複雑になります。 HR_Manager HR_Staff Development_Manager → NewDevelopment_Manager Development_Staff → NewDevelopment_Staff また検索やソートをする観点でも問題があります。検索のクエリが複雑になったり、ソートをする場合も_(アンダースコア)以降で区切るという手間が必要になってきます。もちろん、可読性も下がります。 そのためサイトリニューアルする際には、1つのカラムは1つの意味を持たせるように分離する作業を行いました。 4. フラグ系のカラムは _flag と命名しない delete_flag は削除したことを表すカラムだと理解できます。しかしtrueは削除済を示しているかどうか迷ってしまいます。 そのため is_deleted のようにbe動詞+過去分詞の形式にしました。そうすることでtrueは削除されたこと、falseは削除されていないことを理解できます。 ここで大切なことは、カラム名と中身の値の認識に相違がないことです。 例えば is_deleted は、社員情報に更新があった場合に古いレコードを論理削除するためのカラムとなります。 true:削除済み false:未削除 フラグ系のカラムでtrueかfalse以外の第3の状態が出てきた場合は、フラグ系のカラムから変更すべきです。 改善し続ける テーブル設計は1回だけではなく、リリースする数ヶ月前まで継続して行いました。 さまざまな種類のテストが行われる中、テーブルを変更することは大きな影響を持ちます。開発チーム内の修正だけでなく、場合によってはバックエンド・フロントエンドを結合したテストを再実施しなければなりません。 バックエンドのプログラムとテーブル間だけで修正を完結することもできましたが、リニューアルするにあたってはAPIのインターフェースまでも一貫して綺麗にしたいと考え、APIを利用する各チームと連携して足並みを揃えて修正しました。 プロジェクト全体で、目先の大変さより、リニューアルする目的を意識していたことが共通認識にあったと思います。 またバックエンドチーム内で改善事項を積極的に伝え合える雰囲気もありました。テーブル設計だけではなく、開発全体で改善し続けられたことが、リニューアルプロジェクトを成功に導くことができた1つの要因だと考えています。 さいごに 実際にはテーブルの構成自体の変更や、使用していないカラムの削除など、大きな構成変更をしました。 これは入社して初めて携わったタスクで、サブスクサイトの構成や機能を100%理解できていない状態で設計することとなりました。開発途中で動作確認をして永続化できていない値に気づいたり、テストなどで指摘があって初めて定義が誤っていることを知ったりすることがありました。 リリースの終わった今でさえ、 「なぜこのような命名をしてしまったのか?」 「ここのテーブルは分離しておくべきだったかも…」 と思うことが多くあります。 しかしながら、チームメンバーをはじめプロジェクト全体でテーブル変更を快く受けれてくださった方々のおかげで無事リリースすることができました。 今後も、わかりやすいテーブルを引き続き目指して改善していきます。
アバター
Hello I'm Koyama from the KINTO Technologies Mobile App Development Group. I work on mobile app development and maintenance as an iOS engineer. Today, I'd like to talk about how MVVM was adopted for Global KINTO's mobile app development. MVVM and Combine There are a few things I'd like to touch on briefly before I begin. MVVM MVVM is an architecture in software development. It consists of Model-View-ViewModel, which is derived from the MVC model. The details are a little complex, so a simple overview is as follows: Model The part that's responsible for collecting data and carrying out simple processing. It doesn't do any complicated processing. View The part responsible for rendering the screen. It receives the data needed to produce the screen, then simply draws it. ViewModel This part is in the middle between the Model and View, and is responsible for bringing together multiple pieces of Model data and holding values needed to maintain the View. That's the general idea (or at least my personal take on it). I found even this much of it difficult to understand when I first started working for iOS. So, if you apply this to iOS development, this is how it looks: Model Brings together all of the data that needs to be handled. Rather than using a dictionary-based approach, you might as well create a single Model instead. The Model only contains simple methods related to the data inside it. View This can be a ViewController or an individual View. It only contains code related to rendering, and simply displays the data it receives without manipulating it at all. Basically, the aim is to have no if statements. ViewModel Receives triggers from the ViewController and returns data. A ViewModel is in a one-to-one relationship with a ViewController, and in relation to an individual View, it can also take on the role of a higher-level ViewController's ViewModel, depending on the situation. This is what I'd like to achieve. In today's article, I particularly want to talk about the differences between a View and a ViewModel. Combine First launched in June 2019, Combine is a framework for creating Apple's official reactive architecture. Before then, reactive architectures were mostly achieved using the third-party RxSwift, but now, an official, strongly supported library has been released. "Hang on a moment. What is a reactive architecture anyway?" Some of you may be wondering this. (I certainly did.) Here's what Wikipedia has to say: reactive programming is a declarative programming paradigm concerned with data streams and the propagation of change. -- Wikipedia | Reactive programming Not very easy to understand, is it? I've heard that to easily understand this, you should picture it in terms of spreadsheet software. Suppose you enter a simple formula like this: A B C 10 20 =A1+B1 Once you've entered it, the result goes in cell C1 (of course). A B C 10 20 30 But what do you think will happen if you change the value in cell A1 to 20? A B C 20 20 40 Of course, cell C1 will change to 40. A change in cell A1 has spread to cell C1 without us directly modifying C1 at all. This is what's called reactive architecture. Based on this, I could now clearly see that you just declare the data processing ( =A1+B1 ) first, then run the actual data through it (assigning 20 to A1) at the end. Background The preamble got a bit long. This is why we decided to adopt Combine and MVVM. Back when I first started working on a Global KINTO development project, we'd just switched from using outsourced code to producing our own in-house. The codebase that had already been written were on a “spaghetti code” state, so it was extremely difficult to maintain. Global variables were being called all over the place, and there was no way to tell which class depended on which. Fortunately, we had a fairly small amount of code and screens, so we tried to solve the problem by reviewing the overall architecture. At the same time, the Mobile App Development Group had some knowledge of using Combine-based MVVM. So, we decided to proceed based on the MVVM architecture. The aim was to design things thoroughly based on the architecture so that even new team members could easily understand the projects and safely modify them. And now comes the implementation! Since we've decided to use MVVM, we'll separate the roles between the View and the ViewModel. This implementation uses UIkit. View - Part1 Instead of doing any processing in the View, we only want to tell the ViewModel the timing of event firings. To do this, we'll use Combine's Publisher. For example, if you need to send the timing of viewDidLoad, define the PassthroughSubject using the Void type. private let didLoad: PassthroughSubject<Void, Never> = .init() Make it so that this gets sent when viewDidLoad is called. override func viewDidLoad() { super.viewDidLoad() didLoad.send() } It's also a smart approach to use tapPublisher[^combineCocoa] when a button is tapped. First, connect the button to the Outlet and prepare the Publisher used for sending. If you want to pass a String value from the displayed screen when the button is pressed, define Publisher as String instead of Void. @IBOutlet weak var button: UIButton! @IBOutlet weak var textArea: UITextField! private let tapButton: PassthroughSubject<String, Never> = .init() Then, use tapPublisher to detect the event and send it to the ViewModel. button .tapPublisher .sink { [weak self] in self?.tapButton.send(textArea.text ?? "") } .store(in: &cancellables) Next, let's take a look at the ViewModel. ViewModel The ViewModel retrieves and generates information based on the event triggers received from the View, then returns the necessary information to the latter. Here, we'll create it using Combine's Operator and Subscriber. First, to prepare to connect it with the View side, define Input and Output structs separately from the ViewModel. Input is the data transferred from the View, and Output is the resulting data you want to pass from the ViewModel. When exchanging data, processing will be possible only if the data and error types have been properly set, so use the type AnyPublisher across the board. AnyPublisher<String, Never> // Want text when the button is tapped! } struct ViewModelOutput { let onShowWelcome: AnyPublisher<String, Never> // Want it to display the Welcome text on the screen let onShowText: AnyPublisher<String, Never> // Want it to display arbitrary text on the screen let onShowOnlyFirst screen: AnyPublisher <Void, Never> // Want it to display a fixed value on the screen only when the button is pressed for the first time } ``` Next, let's create the part that processes the data. This amounts to adding the part to receive input and turn it into output. For example, if you want viewDidLoad to trigger a specific API, connect viewDidLoad's Publisher to Operator and run an API request. ```swift func transform(input: ViewModelInput) -> ViewModelOutput { let apiResponse = input .viewDidLoad .flatMap { api.requestData() } Similarly, when a button is tapped, if you want to generate data based on the text on the screen at the time, use map. let text = input .tapButton .map { createText(text: $0) } You might also want something to happen when viewDidLoad is called and a button is tapped at the same time. In cases like that, Publishers can be connected to each other. There are many ways to connect them, but we'll use zip this time. let buttonTap = input .tapButton let didLoadAndButtonTap = didLoad .zip(buttonTap) .map { _ in } Operators are already available in Combine and its extended libraries, so complex processing can be achieved while still keeping the code simple. There are a lot of Operators, but I've listed the ones I think it's useful to remember below. map flatMap compactMap filter share materialize [^combineExt] merge zip combineLatest withLatestFrom [^combineExt] And finally, add the proper return that goes with the output's type. When you use Combine, the types will keep getting expanded each time an Operator is connected, so in the end, return the created data to View after gather it all together into AnyPublisher. return .init( onShowWelcome: apiResponse.eraseToAnyPublisher(), onShowText: text.eraseToAnyPublisher(), onShowOnlyFirst: didLoadAndButtonTap.eraseToAnyPublisher() ) } View - Part 2 The ViewModel side is now complete, so connect it to the View and render the screen. Let’s call the transform method we prepared earlier to get the results of the processing carried out in ViewModel. let output = viewModel.transform( input: .init( viewDidLoad: didLoad.eraseToAnyPublisher(), tapButton: tapButton.eraseToAnyPublisher() ) ) Once we've gotten the processing results and decided what sort of screen rendering to do for each, we're all done. output .onShowText .receive(on: DispatchQueue.main) .sink { [weak self] in self?.textArea.text = $0 } .store(in: &cancellables) output .onShowOnlyFirst .receive(on: DispatchQueue.main) .sink { [weak self] in self?.showCustomMessage("First!") } .store(in: &cancellables) ``` # Finished code Here's the finished code. ```swift:ViewController.swift class ViewController: UIViewController { @IBOutlet weak var button: UIButton! @IBOutlet weak var textArea: UITextField! private var viewModel: ViewModelProtocol! private let didLoad: PassthroughSubject<Void, Never> = .init() private let tapButton: PassthroughSubject<String, Never> = .init() private var cancellables: Set<AnyCancellable> = .init() func configure(viewModel: ViewModelProtocol) { self.viewModel = viewModel } override func viewDidLoad() { super.viewDidLoad() bind() didLoad.send() } func bind() { let output = viewModel.transform( input: .init( viewDidLoad: didLoad.eraseToAnyPublisher(), tapButton: tapButton.eraseToAnyPublisher() ) ) button .tapPublisher .sink { [weak self] in self?.tapButton.send(textArea.text ?? "") } .store(in: &cancellables) output .onShowWelcome .receive(on: DispatchQueue.main) .sink { [weak self] in self?.textArea.text = $0 } .store(in: &cancellables) output .onShowText .receive(on: DispatchQueue.main) .sink { [weak self] in self?.textArea.text = $0 } .store(in: &cancellables) output .onShowOnlyFirst .receive(on: DispatchQueue.main) .sink { [weak self] in self?.showCustomMessage("First!") // Assumptions for which methods are prepared in advance } .store(in: &cancellables) } } ``` ```swift:ViewModel.swift protocol ViewModelProtocol { func transform(input: ViewModelInput) -> ViewModelOutput } struct ViewModelInput { let viewDidLoad: AnyPublisher<Void, Never> let tapButton: AnyPublisher<String, Never> } struct ViewModelOutput { let onShowWelcome: AnyPublisher<String, Never> let onShowText: AnyPublisher<String, Never> let onShowOnlyFirst: AnyPublisher<Void, Never> } struct ViewModel: ViewModelProtocol { let api: SomeApiProtocol init(api: SomeApiProtocol) { self.api = api } func transform(input: ViewModelInput) -> ViewModelOutput { let didLoad = input .viewDidLoad share() let apiResponse = didLoad .flatMap { api.requestData() } // Future<String, Never> return is assumed let buttonTap = input .tapButton share() let text = buttonTap .map { createText(text: $0) } let didLoadAndButtonTap = didLoad .zip(buttonTap) .map { _ in } return .init( onShowWelcome: apiResponse.eraseToAnyPublisher(), onShowText: text.eraseToAnyPublisher(), onShowOnlyFirst: didLoadAndButtonTap.eraseToAnyPublisher() ) } func createText(text: String) -> String { "\(text) show!!" } } ``` Clearly dividing the roles between the View and ViewModel like this helps keep the code nice and organized. You can use the same structure for any View, which makes the code easier to read and maintain. Also, having the roles all set in stone makes forcible coding less likely. However, one con is that if you haven't done much reactive programming with (e.g.) Combine or RxSwift before, you'll find it **as difficult as to understand as a totally different language** from the Swift you've known up to then. In Global KINTO's iOS projects, all Views are created based on this structure. It's easy to read and maintain, making it less likely to deviate from even when creating new Views. # In conclusion The Mobile App Development Group will share knowledge like in this article with other products besides Global KINTO ones, and take on the challenge of new architectures that are completely different from this one. I want to keep rising to the challenge of using various iOS development methods as part of my work in the Mobile App Development Group. [^combineCocoa]: 拡張ライブラリの[CombineCocoa](https://github.com/CombineCommunity/CombineCocoa)を使用しています [^combineExt]: 拡張ライブラリの[CombineExt](https://github.com/CombineCommunity/CombineExt)を使用しています
アバター
はじめに こんにちは。プラットフォームGのPlatformEngneeringチームでPlatformEngineeringの考え方をベースにツール周りの開発・運用・展開の役割(とチームリーダーのようなことをしている) 島村 です。 この記事は、 KINTOテクノロジーズのAdventCalendar2023 の技術側1日目の記事です。 CICDツールとして、GitHubに付属しているGitHub Actions。 特にPushと手動(WorkFlow_dispatch)をよく使うと思いますが、これが有効になったり動いたりするのはどういう時なのか、たまに混乱しませんか? 私はします。メンバーとどうだっけ?と話をしたこともよくあります。 なので整理しようというのが本記事です。 背景 PushしたときとかMergeしたときとか手動とか、起動するEventsのうち、 on.push on.workflow_dispatch の際の挙動を整理してまとめたいと思います。 テスト準備 ケース on.push default(main)ブランチにMergeしなくても動くか default(main)ブランチにMergeしたあと、Pushのブランチを変更するとどうか Pushの際に他のブランチに影響がないか on.workflow_dispatch deafult(main)ブランチにMergeしなくても動くか Merge後に別ブランチを作成して修正、Pushしたら反映されるか 基本コード(echo.yaml) name: "TestWorkFlow" on: push: branches: - main - 'test/**' workflow_dispatch: jobs: TestWorkflow: name: "TestWorkFLow Echo" runs-on: ubuntu-latest steps: - name: Echo Branch run: | echo "Branch:${{ GITHUB_REF_NAME }}" 結果まとめ 一枚っぺらのイラストにまとめたのがこちら。 詳細 on.push Mergeしなくても、以下の条件にマッチするならPushしたタイミングで動く 構文などのミスがないこと(ただ、構文ミスがあったとしてもFailで表示される) pushしたブランチとworkflowのon.pushのブランチの条件が合致している on.push.branchesをtest/**のままで"feature/trigger-test"でブランチを切ってpushしても動かない on.push.branchesを"feature/xx"に変更してpushしたら、feature/trigger-testのWorkflowが動く 以下の2つのブランチがある場合の挙動 on.push.branchesを"feature/xx"としているtest/trigger-test on.push.branchesを"test/xx"としているfeature/another-trigger 何か修正してtrigger-testでPushしても、feature/another-triggerのWorkFlowは動かない 何か修正してfeature/another-triggerでPushしても、trigger-testのWorkFlowは動かない on系パラメータはその発生したブランチにあるファイルの内容を見て起動するかを判断している on.workflow_dispatch defaultブランチにファイルが存在しないと、手動で動かすボタンは表示されない Mergeした後に新しくブランチを作成してInputsを追加した 手動起動の場合、ブランチを選択できるので、新しいブランチを選ぶとInputsが増える pushと同じで、ブランチにあるファイルの内容を反映している 検証 on.push default(main)ブランチにMergeしなくても動くか まずは、echo.ymlを作成してPushする。 junpei@:test-trigger-repo$ git add .github/ junpei@:test-trigger-repo$ git commit -m "create workflow" [test/trigger-test 43be511] create workflow 1 file changed, 17 insertions(+) create mode 100644 .github/workflows/echo.yml junpei@:test-trigger-repo$ git push origin test/trigger-test ・・・・・・ Total 5 (delta 0), reused 0 (delta 0), pack-reused 0 remote: remote: Create a pull request for 'test/trigger-test' on GitHub by visiting: remote: https://github.com/junpeishimamura-kinto/test-trigger-repo/pull/new/test/trigger-test remote: To https://github.com/junpeishimamura-kinto/test-trigger-repo.git * [new branch] test/trigger-test -> test/trigger-test junpei@:test-trigger-repo$ 想定だと動かないかなと思っていたが、Actionが走る。 Mergeした後にPushのブランチを変更するとどうか トリガー条件を「test/**」としたmainブランチから「feature/trigger-test」を作成しても動かない=当然。 ワークフローのファイルを on: push: branches: - main - 'feature/**' workflow_dispatch: と「feature/trigger-test」で修正してPushしたら、もちろん動く。 他のブランチへの影響はないか mainブランチから、「test/trigger-test-other-branch」を作成して、適当なファイルを作成する junpei@:test-trigger-repo$ git status On branch test/trigger-test-other-branch Untracked files: (use "git add <file>..." to include in what will be committed) test-branch.txt nothing added to commit but untracked files present (use "git add" to track) junpei@:test-trigger-repo$ git add test-branch.txt junpei@:test-trigger-repo$ git commit -m "create test-branch.txt" git p[test/trigger-test-other-branch 0c738b1] create test-branch.txt 1 file changed, 1 insertion(+) create mode 100644 test-branch.txt junpei@:test-trigger-repo$ git push origin test/trigger-test-other-branch ・・・・ Total 3 (delta 0), reused 0 (delta 0), pack-reused 0 remote: remote: Create a pull request for 'test/trigger-test-other-branch' on GitHub by visiting: remote: https://github.com/junpeishimamura-kinto/test-trigger-repo/pull/new/test/trigger-test-other-branch remote: To https://github.com/junpeishimamura-kinto/test-trigger-repo.git * [new branch] test/trigger-test-other-branch -> test/trigger-test-other-branch junpei@:test-trigger-repo$ でPushしたら、もちろん動く。この場合のワークフローファイルは、「test/trigger-test-other-branch」のものでMainではない。 追加で、mainブランチから「feature/another-trigger」を切る。この時、前に検証していた「feature/trigger-test」ブランチは残している。 junpei@:test-trigger-repo$ git switch -c feature/another-trigger Switched to a new branch 'feature/another-trigger' junpei@:test-trigger-repo$ git branch * feature/another-trigger feature/trigger-test main test/trigger-test test/trigger-test-other-branch junpei@:test-trigger-repo$ で、ファイルとかを作って、「feature/another-trigger」のワークフローの条件は変更せず(test/**)でPushする。 Actionsは走らない。操作ブランチにあるワークフローの条件に合致するかどうか?が基準となっている模様。 on.workflow_dispatch deafult(main)ブランチにMergeしなくても動くか on.pushで作成したecho.ymlをMainブランチにマージしなくても手動できどうできるか? workflow_dispatchがYamlにあったとしても、Mainブランチにマージしないとボタンが表示されず動かせない。 ボタンがないので、指定するブランチを切り替えるとういうこともできなさそう。 Merge後に別ブランチを作成して修正、Pushしたら反映されるか よく使うので、検証しませんでした。が、反映されます。 RunWorkflowのボタンを押すと、ブランチ指定ができるので、選ぶと、変更点などが反映されたものが動かせます。 所感 on.pushにおける、「defaultブランチにMergeしなくても動くか」以外は想定通りでした。PushしただけではGitHub Actionsとして登録されない=Mergeして初めて登録されると認識していたので、整理して認識が違っていたのを把握できて良かったです。今回はBranch条件としましたが、Tagもイベント駆動の種別が同じなので、同様の挙動をすると思います。 本当はCircle.CIとか他のCICDサービスとの比較もしたいと思っていたのですが、多分、同じ形で動くのかなと思っています。GitHub Actionsと違い、SaaS系CICDツールはイベント駆動でソースコードを引っ張ってくるので、イベントが発生したブランチにあるものを判断するんじゃないかな?と。 さいごに PlatformEngneeringチームは、社内向けの横断ツールを統制して必要なものを開発しています。 Platformグループの他チームが作ったものを受け入れたり、必要なものを新規作成や既存のものをマイグレーションしたりしています。CDKにも手を出そうとしてるので、ManagedService以外にもプログラミングも行い始めました。 こういった活動に少しでも興味を持ったり話を聞いてみたいと思った方は、お気軽にご連絡いただければと思います。 @ card
アバター
はじめに こんにちは、KINTOテクノロジーズの開発編成本部に所属するカンです。2022年1月に入社し、これまでサイト再構築というプロジェクトを進めてきました。 KINTOをご利用いただくお客様が増えるにつれ、様々なサービスを拡充しようとしており、既存サイトでは迅速な機能追加を行う上で様々な問題点を有していたため、これらの問題の解決と改善のためのサイト再構築プロジェクトが今年 8月にリリースされました。 本稿では、私が所属しているサイト再構築のフロントチームについて紹介したいと思います。 サイト再構築FEチームの目標 新規開発をスピーディーに提供出来る環境の実現を目指す 複雑な環境構築から、シンプルな環境構築の実現を目指す 複雑に絡み合っているcssソース、jsソースを定義し直す 会員管理機能を会員情報プラットフォームへ移譲する クリエイティブグループと連携し、デザインから実装に落とし込む(UI/UXの改善提案も含む) 再構築FEチームは、上記の項目を達成するためにプロジェクトを進めました。 開発段階で改善できる部分について毎スプリントの会議で話し合い、効率的なプロジェクトを構築するために他のチームと連携して改善していきました。 コードの再利用性を高め、メンテナンスに容易な直感的なコードを作成しました。 TypeScriptを使用することで予期せぬエラーが発生することを防止し手軽なデバッグで作業生産性を高めました。 どのような技術スペックをプロジェクトに取り入れたのか プロジェクトを進めながら悩んだもの 既存プロジェクトの仕様を再構築するだけでなく、既存の機能改善や新機能追加、コードリファクタリングも同時に進めていたため、各メンバー間で進行中の業務に対して把握が難しい 連携するチーム(BEチーム、デザインチーム、インフラチーム、QAチームなど)と仕様および開発について効率的なコミュニケーションをとるための方法 良い開発環境を構築していくためにはどんな方法があるか メンテナンス性が高く再利用性の高いコードを作成するためには プロジェクトの仕様と技術に対する各メンバー間の理解の差を解決し、コードの統一性を高めるための方法 解決するために FE チームは 開発を進めるにあたって、レビューは重要性が高いと認識していたためレビューに関するルールを作成しました。また、毎日レビュー時間を作ってレビューを進行するように努力しました。 特定のレビューに対して担当者を指定せず、自由にいろんなPull Requestに対してレビューする方法で進めました。その結果、他のメンバーが進んでいるタスクの仕様やコンポーネントなどの実装について確認しながらお互いの業務内容を把握しやすくなりました。 連携するチームとConfluenceへ変更仕様のページを作ってコミュニケーションし、また業務のツールを利用して効率的なコミュニケーションをとることができました。 BEチームが提供するswaggerとAPIの変更仕様についてまとめたConfluenceのページを通じてAPIの仕様を素早く理解することができ、変更点も明確に確認して対応することができました。 クリエイティブチームとはAdobe XD、Figmaなどのデザインツールを通じて協業しました。 導入するUI/UXに関してよりわかりやすく理解することができ、ユーザーが理解しやすくて操作しやすい直感的なコンポーネントを作成することができました。 コミュニケーションも自由に取りながら協業し不明点や変更点があれば、その場ですぐにハドルmtgを開催して解決していきました。この結果、開発過程で発生しうるバグを減らすことができました。 より良い開発環境を作るためにチーム内の業務に関する様々なガイドラインを作成しました。 Outlook、Slack、Confluenceなどでコミュニケーションを取り、デイリー会議を通じて互いに現在進行中の業務に対する状況を理解することができました。タスクを進行しながら悩むところについてはお互い相談し、チーム内で一緒に問題を解決していきました。プランニングの会議で各メンバーの進行状況を確認しながら次回のスプリントの作業分担を行い、過度な業務負荷がないようにしました。 また、スプリントごとに振り返り会議を進め、該当スプリントの惜しかったところや良かったところと、改善していきたいところについてお互いに相談しながらもっと良い開発環境を構築していきました。 Atomic Designパターンを導入し、持続的なリペクトリングを通じてコード再利用性を高めました。 Atom/Molecules/Organismsの区分に対してConfluenceのワークスペースに定義をまとめ、会議を通じてメンバー間の認識を合わせていきました。 新しい機能やUIの開発を進めながら、独立的でピュアなコンポーネントを作成していきました。 また、コンポーネントの品質を保証するためStorybookとReactのJestライブラリを導入してテストを行いました。 その結果、より高い再利用性を持つコンポーネントを作成することができました。 Confluenceの再構築ワークスペースに既存プロジェクトおよび新たに追加される機能について仕様をまとめて共有しました。 また、FE開発環境に関する参考資料を作成し、新規参加者がより迅速に開発環境に適応できるようにしました。レビューの流れやブランチ運用方法、コードの作成方法などのコード管理に関するルールを設定し、統一性のあるコードを作成しました。 各メンバーごとに順番を決め、現在使用中の技術について輪読会をを開催して知識を共有しました。 まとめ 今回のサイト再構築プロジェクトを通じて良いプロジェクトを進めるために重要なものは何なのか改めて振り返ることができました。 個々人のプログラミングパフォーマンスも重要な要素だと思いますが、一つのチームとして絶えず疎通していくのが重要だと思いました。 チーム内でより良い開発環境を作るためにお互い努力し、柔軟な考え方で失敗を恐れずに様々なトライをすることが重要ではないかと感じました。。 何でも試したいことがあれば、自由に発言して試してみる明るい雰囲気の再構築チーム環境で開発を進めることができたので、個人的にもチーム的にも多くの成長ができたと思います。 長期間のプロジェクトを進めながら一緒に苦労した再構築チームと連携したすべてのチームの努力のおかげで、プロジェクトが今年 8月に無事リリースすることができました。 最後まで読んでくださってありがとうございます。
アバター
Introduction Hello. Ryo here, an ID Platform developer in the Global Development Group. KINTO services have already been rolled out in several countries, and we're planning to spread them to even further afield. Our mission in the ID Platform Development Team is to create an ID system that enables customers all over the world to use all of KINTO's services smoothly with a single ID, from any country. Today, I'd like to tell you about our FIDO proof of concept (PoC). Identity provider/ ID provider, or IDP An identity provider / ID provider (IdP or IDP for short) is a system entity that creates, maintains, and manages principal identity information, and provides authentication services for dependent applications in a federated or distributed network. A concept like the "identity provider / ID provider" above (IDP from here on) might be difficult to understand if you've never heard of it before, but to put it simply, it's a system that manages, authenticates, and authorizes users. Overview of FIDO Maybe you know what FIDO (Fast IDentity Online) is already, but in case you don't, it's a new authentication standard that — unlike the ordinary ID and password authentication methods— is all about doing password-free ID authentication for online services. It uses a key pair consisting of a private key and a public key, which is a more secure approach than the ordinary one based on sharing a common key. FIDO authentication procedure The procedure for registering is as follows: Login procedure for registered users Comparison with the ordinary way The ordinary way: ID and password pairs are set when users create accounts The IDs and encrypted passwords are recorded in a server-side database The server gets sent IDs and passwords, and is asked to decide if they're correct Pros of FIDO authentication FIDO authentication is a new password-free, secure authentication method that enables authentication to be done via public key cryptography instead of using passwords. It has the following advantages for users: The servers only store public keys, so they don't store users' private information. The public keys are encrypted, which makes it much safer than using common keys. The users can log in without having to remember a password. Fingerprint and face authentication are more convenient than manually entering passwords. What we have achieved Broadly speaking, we had the following 2 goals for our FIDO PoC: Achieving the necessary tuning and support in the current ID system Coming up with a plan that will enable us to easily adopt FIDO for both internal and external ID systems if desired. In line with these goals, we successfully achieved the following 4 things: Analyzing ECC and RSA public keys (Goal 1) Taking the public keys created using biometric authentication and storing them in the RDS (Goal 1) Being able to correctly authenticate users when needed (Goal 1) Being able to support both web- and API-based paradigms (Goal 2) Neat approaches we devised Covering both web- and API-based paradigms in the basic design stage Rather than simply creating an IDP, we also want to deploy it globally as KINTO's IDP, and get it used for other services as well. For KINTO's services, we're going for easy adoption of FIDO functionality with an API-based paradigm, and we're envisaging being able to adopt it for other companies' ones as well, using a web-based, one-click paradigm. If we can figure out neat ways to support both of these, it'll become more convenient. Changing the programming language The sample code we got from the official website is written in JavaScript for both the frontend and the backend. We're deploying services with Spring Boot, so if we want to add it to the IDP as a new feature, we'll need to change it from JavaScript to Java. If there's no Java library with the same functionality as the external JavaScript-based one, we'll need to do some coding in order to make the change. Issues we encountered during the PoC When authenticating a user, a bug sometimes arose where the public key didn't match the one stored in the RDS. On trying to debug it, we found that the public key IDs stored in RDS were encoded as base64, but the Java and JavaScript base64 encoding functions were giving different results. We'd run it in JavaScript with auto-padding off, but when we moved the backend logic to Java, the auto-padding setting didn't get picked up, and the Java auto-padding got used anyway. Turning off auto-padding fixed the bug. + Base64.getUrlEncoder().encodeToString(ID string) - For patterns using auto-padding, the part of the encoding result that's less than a multiple of 4 gets automatically padded with equals signs ("=") + Base64.getUrlEncoder().withoutPadding().encodeToString(ID string) - With auto-padding off, the encoding result gets left as is.    Problems FIDO has made it easier and more convenient for users to use online services securely, but there are still a few things that need to be taken care of. Public key management issue: Multiple public keys can be added on the same device ** The server can't tell whether the same device is being used, so you can register a public key the same device as many times as you like. This means garbage data gets stored in the RDS. ** Reregistering public keys by switching devices, etc. ** If you lose your device, get a new one, etc., you need to do the FIDO registration again. When you reregister, you need to verify your identity another way. This is less convenient than with ID-and-password authentication. ** Managing unwanted public keys ** The above will result in unwanted public keys. Users can't manage their public key IDs, so the backend can't tell whether public keys are valid when they become unwanted. Public keys still have to be stored on the RDS for a long time even if they become invalid. ** Summary We wanted to give users a better password-free experience with authentication, so we decided to do a PoC for FIDO. As a result, we feel that it's a powerful solution but there are still issues with things like public key management. So, we want to continue to develop things having soundly verified that they're suitable. References What is FIDO Authentication? What is OpenID Connect?
アバター
自己紹介 KINTOテクノロジーズでモビリティマーケットの開発・運用を担当しているリナです。 普段はフロントエンジニアとして、主にNext.jsを用いて実装しています。 最近のマイブームは、Minecraftです🎮今更始めました。 さて、KINTOテクノロジーズは今年もアドベントカレンダーを実施します🎄 今回の記事では、昨年よりアップグレードしたKINTOテクノロジーズ Advent Calendar2023を先んじてご紹介します!! これまでのAdvent Calendar KINTOテクノロジーズは2021年よりアドベントカレンダーを実施しており、今年で3回目です🎄 2021年のアドベントカレンダーは、まだKINTOテクノロジーズのテックブログがなく、有志のメンバーが集まってQiita Advent Calendarを執筆していました。 https://qiita.com/advent-calendar/2021/kinto-technologies 次に2022年は、7月にKINTOテクノロジーズのテックブログをローンチし、正式にアドベントカレンダーをスタートできた年です👏 記事の内容は「個人が習得した技術の発信」と「各部署(グループ)紹介」2つのテーマで、KINTOテクノロジーズの社員が持つ技術や社員の働き方を中心に発信しました。 https://blog.kinto-technologies.com/posts/2022-12-01-advent_calendar/ (参考: Advent Calendarとは ) Advent Calendar 2023 そして今年のアドベントカレンダーは、例年と同様にシリーズを2本立て公開します! 前年は2つのアドベントカレンダーを作成していましたが、今年は1つのカレンダーで2シリーズ・計50記事を公開予定です。 https://qiita.com/advent-calendar/2023/kinto-technologies-tech そしてなんと・・・ プレゼント企画 も実施します🎅✨ みなさま、ご友人や同僚をお誘いあわせの上ご参加くださいませ! https://qiita.com/advent-calendar/2023/kinto-technlogies それでは、2023年の記事のテーマをご紹介いたします! 1. 技術記事 KINTOテクノロジーズの社員個々人が習得したスキル・技術を発信します。 インフラ・バックエンド・モバイル・組織開発など、多岐にわたる分野の技術記事を公開予定です。 記事を通して、KINTOテクノロジーズで働く社員が日々どのように課題解決やスキル習得に励んでいるか、業務に取り組む姿勢を知ることができます! 2. 記事リレー 今年は、5つのテーマで記事リレーをします! テーマは、今年ローンチした大規模プロジェクトや社内の勉強会で話題のトピックなどを選定しました。 ①新車のサブスク サイトリニューアル 2023年8月に新車のサブスクサイト(KINTO ONE)の完全リニューアルをローンチしました🎉 開発期間約2年半の大規模プロジェクトです。 サイトリニューアルによって新機能の追加や改修がしやすく、よりモダンなサイトを構築できました。 今回はプロジェクトに携わったメンバーが、よりよいサイトを構築するために行った改善や技術スタックを記事にまとめます! ②アジャイル開発 弊社では、ソフトウェア開発手法であるアジャイル開発を複数のプロジェクトおよびプロダクト開発で取り入れています。 以前からアジャイル開発の勉強会は行われていましたが、部署単位などクローズドな環境での情報共有が主だったため、今年から「 組織横断 でアジャイルな活動を推進すること」に力を入れています。 #help-agile-scrum というアジャイル開発の情報交換ができるSlackチャンネルを作成したり、他チームのスプリント見学会を実施するなど、組織横断で気軽に情報交換ができる状態を目指しています。 今回は、各プロダクトで取り組んでいるアジャイルの事例や工夫をご紹介します! ③KINTO FACTORY KINTO FACTORY とは、すでに購入されたトヨタ・レクサス車に、その後の技術革新や経年劣化に合わせてソフトウェア・ハードウェアの機能やアイテムをタイムリーに反映することで、お乗りのクルマを最新の状態に「進化」させるサービスです。 KINTO FACTORYではマスタデータをトヨタやトヨタの販売店各社から取得し、トヨタの関連各社と適切なデータ構造を一緒に考えながら実装を進めているのが特徴です。 ECサイト受付~部品発注~納品まで、バックオフィスの運用全体を一部を垣間見ることができる記事も予定されています。 他にも先日リリースされた車検証の分割QRコード読み取り機能など、ユーザー様にとって使いやすいサイトを目指して日々「進化」に取り組んでいる様子もフロントエンドの技術を中心に公開予定。 他にも運用体制の構築や新機能追加など、いくつかの記事を執筆します! ④QA KINTOテクノロジーズが提供する各種サービスのリリース前検証を担うグループの記事連載です。 トヨタグループに属するKINTOテクノロジーズでは、ソフトウェア開発においても高い品質管理基準が要求されます。 また、KINTO ONEやKINTO FACTORYなどの各プロダクト間ではデータ連携が行われており、複数プロダクトにまたがった検証も品質管理で重要なポイントとなっています。 各プロダクトの開発メンバーは、自身が担当するプロダクトの範囲内でテストを実行しています。 しかし、QAグループではプロダクト単体の検証だけでなく、プロダクト間の検証も担い、プロダクト(プロジェクト)全体のコミュニケーションのハブとして情報収集・連携も行う重要なグループです。 そんなKINTOテクノロジーズには欠かせないQAグループが、提供するプロダクトの検証を進める上での開発チームとのコミュニケーションや品質管理の重要性について執筆いたします! (参考: QAグループ紹介 ) ⑤Diversity & Inclusion 弊社はさまざまな国籍の多様なバックグラウンドを持つメンバーが多く、約25%のメンバーが外国籍です(2023年11月時点) また、幼い子どもを抱えながら働くワーキングマザー・ファザーが多く在籍しており、それに伴い男性の育児休暇取得者も増加しています。 今回は、それぞれのライフステージ・ライフスタイルに合わせてどのような働き方をしているのか、また多国籍チームでどのようなマネジメントを行っているのかなど、外国籍メンバーのキャリアや働き方に焦点をあて、リーダシップ・育児休暇・多国籍チームのコミュニケーションのテーマで執筆します! (参考: 外国籍社員の活躍 ) https://qiita.com/advent-calendar/2023/kinto-technologies-tech プレゼントカレンダー そして、今年はQiita Advent Calendarのプレゼント企画に参加します! 記事を投稿いただいた方の中から、3名様にプレゼントをお渡しする予定です✨ テーマは「 CCoEクリスマス!クラウド技術を活用して組織をカイゼンした事例を投稿しよう! 」です。 様々な活動の中で、今回は「CCoE活動」にフォーカス! 私たちは、AWS Summit Tokyo 2023・AWS Autotech Forum 2023をはじめとする外部イベントへの登壇や、DBRE Summit 2023の開催など、トヨタグループ横断でのCCoE活動をリードしています。 そこで、今回はエンジニアのみなさまから、クラウドに関わる「カイゼン」事例を広く募集します! クルマ好きの人もそうでない人も、日々の業務の中での小さな「カイゼン」を教えてください。 プレゼント内容🎁 KINTO Unlimited賞(1名) Apple AirPods Max - スペースグレイ KINTO ONE賞(1名) Anker PowerExpand Elite 12-in-1 Thunderbolt 4 Dock (APEX) ドッキングステーション KINTO FACTORY賞(1名) BenQ ScreenBar Halo モニターライト スクリーンバー ハロー USBライト デスクライト https://qiita.com/advent-calendar/2023/kinto-technlogies さいごに 今回は、2023年のアドベントカレンダーの概要をご紹介しました🎄 KINTOテクノロジーズのアドベントカレンダーが、みなさまの新たな気づきに繋がれば幸いです。 また、アドベントカレンダーおよび登壇情報などの最新情報は X で配信します! ぜひ、フォローお願いいたします。 https://twitter.com/KintoTech_Dev それでは、2023年のアドベントカレンダーもご期待ください✨
アバター
Introduction Hi. I'm Nishiguchi, the manager of the KINTO Technologies Analysis Group. Some of you might think it's strange that a tech company has an analysis group. So, that's what I'd like to talk about today. The Analysis Group's role KINTO Technologies' business includes the following: information processing services such as designing, developing, and operating sales and information systems in the digital field; and work related to creating and proposing plans for and providing consulting services for corporate management and marketing strategies. The Analysis Group handles the latter half of this, and of course, KINTO's business in Japan makes use of data from, cooperates with, and participates in the various services run by Toyota Financial Services Corporation group companies, and aims to use data in order to give business value. Giving data value (The scope of the Analysis Group) Are you familiar with the DIKW pyramid? This concept, which stands for data , information , knowledge , and wisdom, revolves around the idea of harnessing and processing vast amounts of information. At our core, we're committed to figuring out how to create value in business while transforming huge volumes of information into new systems. In order to make the DIKW pyramid a reality, we broadly divide our work into 4 areas. 1. Designing and carrying out data acquisition Business data typically consists of the following: transaction data generated from actions such as user order flows; and master data from things like product management. We also view both transaction and master data as having to be generated by something. It's been getting increasingly difficult to acquire data in recent years, notably due to calls to protect personal information. There are data that can only be acquired at certain times (user registration data, for example), and if you fail to get them at those times, you won't be able to later on. We of course set up the right analysis tags on websites, and also work on planning systems to generate data via user actions. 2. Data storage We need to be able to store the resulting data in a data lake or data warehouse quickly and accurately before moving on to the next step (analyzing it). The goal here is to make the analysts' job easier. We'd like to lower the workload that data acquisition and processing impose on analysts, so they can focus on the analysis work more. Not being able to find the data they're looking for easily can get interfere with their train of thought. It's important to create a system that, for example, will make it easy for team A to join forces with team B when they want to. I often tell our team members to put themselves in the receivers' shoes before they pass something on to them. 3. Data analysis Analyzing the data consists of splitting them up based on certain perspectives, and comparing them with other kinds. I think the idea is to find differences by doing so. I also see it as a policy of ours to promote widening or narrowing those differences. It's our job to find those differences from right perspectives, inform business team members about them, and also suggest appropriate actions to take. I tell our members that there are 2 skills they need. The first is to have various perspectives. Specifically, we get them to develop the ability to see things as insects, birds, fish, and bats do respectively. The other is performing "why-why analysis." In order to grasp the essence of things, we have to repeatedly ask why, and in so doing, find the root causes among the correct causal relationships. 4. AI and machine learning AI and machine learning are both a part of advanced data analysis. This is the domain of predicting the future using past data. In this field, we work with the Toyota Motor Mirai Creation Fund to create models using advanced algorithms while also gathering academic information. We're also constantly reviewing models for AI and machine learning in business fields. It's also essential that an MLOps environment be created that will make it easier to modify and implement these models, so we're also working on this together with the Platform Group Giving value to data (Roles and Responsibilities) To reiterate, acquiring data at the right time is critical to giving data value, especially in BtoC business. This is because some kinds of data can only be acquired at certain times. The next-most important point is to store the data so it's easy for people performing the next processes to use. To accomplish this, the Analysis Group has the following team member positions: 1. Data analysts Data analysts at KINTO Technologies are more than just analysts. This is because they're involved right from the data acquisition design stage, where the aim is to acquire data in the right places, at the right times, and in the right forms via websites and apps. They're also responsible for setting up the tags. We believe data analysts should think in advance about what kinds of data the analysis is likely to require, then acquire it without missing any out. The resulting data is then stored in a data lake or data warehouse. From there, the data can be freely extracted using BI tools and SQL, and predictors found, countermeasures proposed, and so on by finding the root causes for things while drilling down from the right perspectives. 2. Data engineers We also need highly skilled data engineers. We don't just store data from the backend database in a data warehouse as is, but also create data in ways that also aim to make things easier for members involved in data analysis. (I think this a unique approach only operating companies take.) KINTO is steadily launching new services, and our data engineers are also responsible for creating the development guidelines, common functions, CI/CD systems, and so on needed to develop things efficiently so that data analysis can start right from when the services are launched. 3. BI engineers It's crucial to turn data into information and quickly inform business members about the business situation. Also, managers and staff have different preferences when it comes to the level of detail they prefer in the information they access. A BI engineer's job is to develop, modify, and maintain dashboards for conveying it in the appropriate forms. 4. Data scientists A variety of services are going to be rolled out by KINTO, likely including more and more related to AI and machine learning. A wide range of support is going to be required for things like images as well as numerical data. No matter how accurate a model you construct, there's no guarantee that it'll actually get used in business. First, you need to convince the marketers, etc. that it's worth it. To that end, KINTO Technologies' data scientists need to be able to explain things using conventional statistical methods, etc., understand the businesses, and be interested in consumers. In other words, data scientists in KINTO's business play the role of marketing data scientists. 5. Analysis producers This is a new position that was created in September 2022. The analysis producers' job is to cross-functionally coordinate the roles of each of the positions 1 to 4 above. They require the business abilities needed to get people to tell them what business issues they're facing, then replace those issues with appropriate analytical problems. Of course, they also need to be highly experienced and knowledgeable about the data involved in 1 to 4. They'll also play an extremely important role in enabling the KINTO Technologies Analysis Group to make its presence felt even more in the future. Team dynamics and workflow approaches in the Analysis Group 1. Team members and atmosphere In the Analysis Group, we confront very difficult challenges daily, with each of our members bringing a unique set of experiences to the table. However, what we all have in common is our high level of motivation and curiosity toward further broadening our understanding of data-related domains, while staying true to each core roles as data analysts, data engineers, data scientists, etc. Whenever a new team member joins, we always take the time to introduce ourselves, as each and every one of us has extremely unique hobbies and things we're into. 2. How we work The Analysis Group is split between 3 locations: Tokyo, Nagoya, and Osaka. Each has teams consisting of data analysts, data engineers, BI engineers, data scientists, and analysis producers, and all the teams go about their work while deftly using online and offline approaches to freely consult with each other. Future challenges we want to tackle We'd like to take on the challenge of connecting and analyzing users across multiple KINTO services we're involved in, and other services besides. In order to do that, the functions and roles of the Analysis Group will need to be linked together organically. It's going to be very challenging, but we hope it'll give us an even deeper understanding of users. KINTO aims to be a top runner in the mobility platform world, and can use GPS and other mobility data to understand the "where" and financial data to understand the "how much." By recording the "who" and "when" in logs of these, we want to gain a deeper understanding of users' preferences and lifestyles. Then, the challenge will be to create systems, predictive models, and so on that will enable us to anticipate their behavior. In conclusion In the Analysis Group, we aim to stay updated with the latest information, and keep working diligently every day, so that we can make our presence felt by contributing to the development of new KINTO services and supporting Toyota Financial Services Corporation.
アバター
こんにちは 👋 KINTOテクノロジーズ、グローバルグループ のRuoyang Wenです。* Global KINTO App * チームのフルスタックエンジニアとして勤務しています。当グローバルグループについての詳細は こちら でご確認いただけます 目的 スタートアップ企業ではよくあることですが、初期のプロダクトにはいくつかの妥協点があります。ベネフィットを増やすためには、サービス改善の他に、ワークフローも改善する必要があります。 本記事では、Git-flowからGithub-flowへ変更した理由とその方法をお伝えします。 試み トヨタグループの企業として、当社では日々の作業の中で日本語の「 カイゼン(改善) 」という言葉を使って 改善 を実施しています。お客様からのフィードバックと分析データによって、さらに良いサービスを提供するためのカイゼンはたくさんあります。自動化により、 カイゼン プロセスを加速させることが可能です。 エンジニアにとって、ソースコードをコミットすることは日々の業務であり、単に成果物を提出するだけでなく、サーバー環境でコードをテストすることも含みます。我々は継続的インテグレーションと継続的デプロイ(CI/CD)をチームに導入することで、反復作業の削減を図りました。 CI/CDは、自動化を導入することにより、高頻度でアプリをリリースできる手法です。 ^1 我々はこれまで、Git-flowに従って、ソースコードと開発の進捗を管理してきました。しかし、CI/CDを導入してすぐに、作業プロセスがスピードアップしないことに気がつきました。 推論 git flow ダイアグラム CI/CDスクリプトを利用したり、管理する作業は煩雑です。様々なブランチが多くあるため、ブランチを一つのカテゴリからもう一つの他のカテゴリにマージさせる際、別のスクリプトが必要になります。:_featureからdevelopへ_、_developからreleaseへ_、_releaseからmainへ_、_hotfixからmainへ_、_mainからdevelopへ_、_developからfeatureへ_等… いたるところにコンフリクトが生じます。コンフリクトは自動化スクリプトでは解決できません。自動化により日々の作業量が増大しました。なぜでしょうか。 リサーチを実施して判明したこと: Git-flowは元来、手動かつラグのあるワークフローです。 そこで、新しいワークフローが必要になります。 再試行 先のリサーチで見つけたいくつかのワークフローの中で、私たちはもともとソースコードをGitHubに保存していることもあり、GitHub-flowを試してみることにしました。 github flow ダイアグラム 今回はブランチが二種類のみです: main と 「change-of-anything」 。そして main へ直接コミットすることをブロックします。 main をアップデートする唯一の方法は、 main に対して プルリクエスト を実施することです。その結果、必要なのは1つのCI/CDスクリプトのみとなり、どんなコンフリクトも プルリクエスト の中で発見・解決することが可能です。 新しい課題 我々はブランチ戦略を簡素化し、CI/CDを実施するスクリプトは1つだけで、コンフリクトを管理するためにプルリクエストを利用しました。これですべて解決でしょうか。プラス面もありますが、マイナス面もあります。 プラス面を見てみましょう。反復作業を自動化スクリプトに移管したことで、全員がより生産性の高い作業を行うことができます。リリースとデプロイのプロセスはスクリプトによって処理されるため、ローカルで加えた変更は2分以内にサーバーにデプロイできます。 では、マイナス面を見てみましょう。システムの機能/バージョンの管理ができなくなりました。機能は各ブランチには保存されず、 プルリクエスト 後に直接 main に保存されるため、次のバージョンでどの機能を保留し、どの機能をリリースするかが決められません。また、 main ブランチは最新のソースコードでアップデートされ続けるので、バージョンの番号は1日で数百も増加する可能性があります。 ネクストステップ 幸いなことに我々のみが直面している課題ではありません。すでにこれらの課題に直面している人々がおり、我々は彼らの歩みをたどり、彼らの解決策を見ることができます。 フィーチャートグルを使って、いつでも機能を有効/無効にできます。パラメータの中に保存できるので、新規のリリース/デプロイは不要です。異なる機能の組み合わせを備えた異なるバージョンをリリースするGit-flowよりも優れています。 バージョン番号は、特定のコミットを本番環境にデプロイした後にのみ追加されます。残りの環境には、バージョン番号としてコミットハッシュを使用します。これは、そのコミットのバグや欠陥を即座に見つけるのに役立ちます。 まとめ 疑う余地もなく、すべての問題を解決する完璧なソリューションはありません。GitHub flowには欠点もあり、私はチームと一緒に我々のプロダクトだけではなく、作業方法についても カイゼン するべく動いています。 個人的には、Git-flowはウェブポータルのようなもので、定義されたルールですべてを分類します。誰もミスしなければ、問題なく動作します。一方、GitHub-flowはサーチエンジンのようなもので、本番環境にリリース/デプロイするコミットにバージョン番号をタグ付けし、他の環境ではコミットハッシュがバージョン番号として利用されます。そのため、何か問題があれば、検索でそれらのバージョンを簡単に見つけることができます。 参考 トヨタ生産方式 What is CI/CD? What is Continuous Integration? Git flow for agile teams is a no no Please stop recommending Git Flow! Git Flow vs GitHub Flow GitHub flow - GitHub Docs Continuous Integration Contradicts Features Branches! How to Achieve Continuous Deployment with Feature Flags
アバター
はじめに ホアンです。バックエンドエンジニアとして、Global KINTO IDプラットフォーム(GKIDP)チームに所属し、ヨーロッパと南米を担当しています。チームはKINTOテクノロジーズ(KTC)のグローバルグループに属しています。私たちは世界中のユーザーを認証するため、グローバルな課題に取り組んでいます。GKIDPにとって、速くて信頼性が高く、かつ可用性の高いID管理、認証、認可システム(Identity and Access Management (IAM) システム) は必要不可欠です。この記事では、どんな種類のクロスボーダーシステム上でも実行できるロードバランシング/トラフィックルーティングについて、私の考えをシェアします。 HTTP (UDP, DNSも)はステートレス・プロトコルです。つまり、クライアントからサーバーへの各リクエストにおいて、以前のリクエスト情報を一切利用できません。では、なぜステートレスでなければいけないのでしょうか?目的は何なのでしょうか? これには深い理由があります。ステートレス・プロトコルであれば、どんなリクエストが来ても各リクエストの状態を気にすることなく、任意のウェブサーバーにルーティングすることができます。その結果、スケールアーキテクチャに合わせたロードバランシングが可能になるのです。これにより、ウェブサーバーをグローバルにヨコテンできるようになり、システムレジリエンスの強化とパフォーマンスの向上に繋がります。 この記事では、2種類の主要なロードバランシングについて紹介します。DNSルーティングとハードウェアロードバランサー (別名ロードバランサー)です。これら2つの方法は異なったものですが、組み合わせることで、KINTOの車両サブスクリプションサービスのようなグローバルシステムの強化に利用することができます。 DNSルーティング ユーザーがブラウザにURLを入力すると、ブラウザはDNSサーバーにDNSクエリを送信して、Webサイトのホスト名に対応するIPアドレスを取得します。ブラウザは、Webサイトのドメインではなく、そのIPアドレスを使用してサーバーにアクセスします。以下がシンプルなフローです。 ![](/assets/blog/authors/pham.hoang/figure-2.png =600x) 図2.DNSルーティング システムのサーバーが複数の地域に分散しているこのようなケースでは、DNSサーバーは、クライアントを最も適切なサーバーにルーティングし、パフォーマンスと可用性を改善します。DNSがリクエストの転送先を決定する方法を DNSルーティングと呼びます。 DNSルーティングは、ユーザーのリクエストにタッチしないため、簡単な構成でありながらスケーラビリティの高い方法です。多くの場合、データセンターや地域間でユーザーをルーティングするために使用されます。一般的なDNSルーティング方法には、単純なラウンドロビン方式や、位置情報ベース、レイテンシーベース、ヘルスベースのようなダイナミック方式があります。 DNSルーティングには、古いDNSキャッシュの問題といった欠点もあります。DNSは、サーバーがダウンしている場合でも、TTL(生存期間)経過前であれば、あるドメインに対して常に同じIPアドレスを返します。 混乱するかもしれないのでここで説明しておきます。DNSはトラフィックをルーティングしません。DNSクエリに対して、IPアドレスを返すだけです。このIPアドレスでユーザーがトラフィックを送るべき場所が分かります。図2における、ステップ3以降を説明します。サーバーのIPアドレスを取得後、クライアントは実際にトラフィック(HTTPリクエスト等)をターゲットサーバーに送信します。そうするとハードウェアロードバランサーがその背後にある複数のバックエンドサーバーにトラフィックを分散してくれます。ハードウェアロードバランサーについては次のパートで詳しく説明します。 ハードウェアロードバランサー ドメインに対応する変換後のIPアドレスを受信した後、クライアントはトラフィックをターゲットサーバーに送信します。ここで、複数あるバックエンドサーバーの前に設置されたハードウェアロードバランサーが起動し、これらのウェブサーバーにトラフィックを分散させます。実は、ハードウェアロードバランサーはリバースプロキシ、つまりシステムのコーディネーターとしての役割を果たす物理的なデバイスにすぎません。 ![](/assets/blog/authors/pham.hoang/figure-3.png =600x) 図3.リバースプロキシとしてのロードバランサー ハードウェアロードバランシングには、レイヤー4ロードバランシングとレイヤー7ロードバランシングの2種類があり、それぞれOSIモデルで言うところのトランスポートレイヤーとアプリケーションレイヤーで動作します。OSIモデルを簡単に説明すると、リクエストはマトリョーシカのように7つのレイヤーに集約されます。より深いレイヤー (レイヤー1からレイヤー7まで)のデータであるほど、より多くの情報を得ることができます。つまり、レイヤー7ロードバランサーは、レイヤー4ロードバランサーと比較して、受信リクエストに関し、より多くの情報を持っているということです。 OSIモデル各レイヤーのデータは以下のようになっています。 ![](/assets/blog/authors/pham.hoang/figure-4.png =600x) 図4.OSIモデル各レイヤーのデータ レイヤー4とレイヤー7のロードバランサーは、受信リクエストへの干渉の程度が異なります。 レイヤー4ロードバランサー レイヤー4ロードバランサーは受信リクエストに関する情報をほとんど認識せず、クライアントのIPアドレスとポートのみを認識します。データは暗号化されているので、ロードバランサーはリクエストデータの内容について何も理解できません。そのため、レイヤー4ロードバランシングは、レイヤー7ロードバランシングほどスマートではありません。 メリット: シンプルなロードバランシング データの解読/ルックアップが不要 => 迅速、効率的、安全 デメリット: ロードバランシングの方法が少ない (スマートなロードバランシングができない) キャッシュなし(データにアクセスできないので)。 レイヤー7ロードバランシング レイヤー7ロードバランサーは受信リクエストに関する読み取り可能なデータ (HTTPリクエストヘッダー、URL、Cookie等) に実際にアクセスできます。レイヤー7ロードバランサーは、レイヤー4ロードバランサーよりもはるかにスマートにトラフィックを分散できます。たとえば、非常に便利な戦略としてパスベースのルーティングがあります。 メリット: スマートなロードバランシング キャッシュあり デメリット: 途中でデータを解読する(TLSターミネーション)=> ロードバランサーはデータを確認することになるので、速度が落ち、安全性が低下。 ルーティング/ロードバランシング方法のリスト ルーティング/ロードバランシング方法は目的に応じて複数あります。 ラウンドロビンアルゴリズム:実装する最も簡単な方法:サーバーアドレスが、ランダムまたは順番に返されます。 加重ベースアルゴリズム:各サーバーに送信されるリクエストの割合を制御します。たとえば、少人数のユーザーを対象にカナリアリリースを導入する場合、ユーザーからのフィードバックを得るためにカナリアリリース用に小規模なサーバーを1台設置し、ユーザーの内5%だけをそのサーバーにルーティングします。そして、残りの95%のユーザーには、今までと同じアプリケーションのバージョンを使用してもらいます。 レイテンシールーティングポリシー (通常はDNSルーティング):クライアントに近く、レイテンシーが最も低いサーバーにルーティングします。低レイテンシーを優先する場合には、このポリシーが適しています。 最小コネクション数 (通常はロードバランサー):トラフィックが最も少ないサーバーにトラフィックが送られます。このアルゴリズムは、大きなリクエストが特定のサーバーに集中するのを防ぐことで、ピーク時のパフォーマンス向上に役立ちます。 ヘルスチェック (ハートビート): フェイルオーバー。ライブセッションを行い、各サーバーの状態を監視します。ロードバランサーは、登録されている各サーバーのハートビートをチェックし、あるサーバーの状態が良くない場合はリクエストのルーティングを停止し、別の正常なサーバーに転送します。 IPハッシュ(通常はロードバランサー):最適なパフォーマンス (キャッシュ等) が得られるように、クライアントのIPアドレスを固定サーバーに割り当てる 位置情報ルーティング (通常はDNSルーティング):大陸、国といったユーザーの各場所に基づいて、適切なサーバーへ案内します。 マルチバリュー (DNSルーティングのみ):1つではなく複数のIPアドレスを返します。 パスベースのルーティング (レイヤー7ロードバランサーのみ): リクエストのパスに応じて、処理を担当するサーバーを決定します。例:/processingの場合、リクエストは処理サーバーへ、/imageの場合、イメージサーバーへ転送されます。 最後に ハードウェアロードバランサーとDNSルーティングは混同されがちです。これらは互いに代替手段というわけではなく、通常は組み合わせて使われています。重要なのは、データセンター間や地域間などの広い地域にまたがる場合にはDNSルーティングが使用されているということです。ハードウェアロードバランサーよりもはるかに安価で高速であるためです。その後の段階で使用されるのがハードウェアロードバランサーで、多くの場合データセンターや地域内においてトラフィックを分散させます。DNSルーティングはDNSクエリを、ハードウェアロードバランサーはトラフィックをそれぞれ処理します。 これら2つの定義の理解は、高いパフォーマンスと可用性を持ったグローバルシステムの構築に不可欠なものです。 参考 https://medium.com/@phamduchoang.eee/but-what-is-osi-model-29578b795f0c https://iq.opengenus.org/layer-4-layer-7-load-balancing https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/routing-policy-edns0.html
アバター
自己紹介 KINTOテクノロジーズ(KTC)でアプリケーションエンジニアをしているJLと言います。現在はグローバル開発グループのフロントエンドチームに所属しています。 日本に来る前はフィリピンで3~4年働いていました。最初は水産業界でテクニカルサポートとして、その後は金融セクターでソフトウェアエンジニア(アソシエイト)として働いていました。 フロントエンドとビジネスサイドの両面からプロジェクトに携わることで、Webやバッチ処理、ビジネスプロセスにおける開発経験を積むことができました。使用していたのは主にJava、JSP、JavaScript、CSSです。また、ソフトウェアエンジニアの専門的スキルも学ぶことができました。当社の従業員として会社に貢献しようとすれば必ず必要になるスキルです。 勤めていた会社は日本に本社があり、それで日本に行ってみたいと思うようになりました。日本のエンジニアチームが高い技術力を持っていることは知っていましたし、日本は科学技術分野で高い評価を受けている国ですから、そういった環境で直接学びたいと思いました。 KINTOテクノロジーズに入社 日本に来てからは、東京の派遣会社で契約社員として働いていました。派遣社員という立場だったので、担当するプロジェクトの範囲や責任は限られていました。 リクルーターからKINTOテクノロジーズを紹介された際、以下のような理由で魅力的に感じました。まず、人々が旅行や出張で世界中を飛び回っている中、KINTOテクノロジーズのモビリティサービスのグローバル展開には成長の可能性があると感じました。 次に、KINTOテクノロジーズはまだ新しい会社ですが、長年にわたってより良い車とサービスを世界に提供してきたトヨタグループの一員であることにも魅力を感じました。そして技術的なスキルだけでなく、コミュニケーションやビジネス分析のスキルも学べると思いました。 また、グローバルグループのアプリケーションエンジニアとして、さまざまな国の視点からプロジェクトに取り組む機会も多くあることでしょう。 KINTOテクノロジーズは明確なビジョンを持った企業であり、今現在もさらなる成長の可能性を秘めています。価値ある未来の製品を開発できるポテンシャルがありますし、その開発に携われることを願っています。 KINTOテクノロジーズでの生活と経験 グローバルグループは国籍豊かな多文化グループで、日本、中国、ベトナム、インド、フィリピンなどから人が集まっています。 チームでは英語を使うので、コミュニケーションが取りやすく、言葉の壁をあまり感じません。 日本にいながら、グローバルグループはタイやカタールといった海外のKINTOサービスの支援をしています。 また、グローバルグループでは毎月アンケートを実施しており、その中で改善のためのフィードバックや提案をすることができます。さらに月に1度、上司との1on1ミーティングがあり、キャリアプランを立てたり、社内において今後取り組んでいきたいことを決めるのに役立っています。 入社後、フロントエンド・エンジニアリングチームに配属されました。チームの仕事は、グローバルグループ製品のフロントエンドの開発と、そのためのWebレイアウトを強化し、サイト訪問者の目を惹くような仕上がりにすることでした。 そのチームで私はテクノロジーポータルの開発を任されました。テクノロジーポータルはゼロから開発されたもので、どのようなソリューションが使えるのかを知るために世界中のKINTOパートナーに利用いただいています。 テクノロジーポータルはGatsby.js、CSS、AWS S3を利用して開発されています。使われているプログラミング言語やアーキテクチャが馴染みの薄いものだったので大変でしたが、やりがいはありました。ありがたいことに、このおかげで短期間で大きく成長することができました。 現在、 Global KINTOアプリのランディングページ の開発に取り組んでいます。このページでは、Global KINTOアプリに関する情報と各国のKINTOサービスに関する情報をご覧いただけます。 このプロジェクトでは、既存のウェブサイトをKINTOのデザインシステムに沿ったデザインに改良しました。このデザインシステムでKINTOのシステムとプロダクトデザインの統一を図ります。詳しくは こちら をご覧ください。プロジェクトでは主にVue.js、Vuetifyを使っています。今回のプロジェクトを通して、デザインのセオリーとベストプラクティスについてより深く学べました。とりわけ、レスポンシブデザインの方法や、SASS、CSSの効率的な使い方に関する分析スキルやクリエイティブさが鍛えられています。 終わりに まとめると、日本に行くことを決めたときに思い描いていた通りのキャリアを、KINTOテクノロジーズで歩むことが出来ています。応募したのはアプリケーション開発とモビリティアプリだったので、最初はこれらだけを担当するものだと思っていました。しかし、そのようなポジションに限定されず、フレキシブルに色んな業務を任されていることで、システムデザインやWeb開発にも取り組み、自身のスキルアップ、キャリアアップに繋がっていると思います。 短期にスキルを身に着け、課題に挑戦し、そしてその課題を見事に解決する。解決した後の爽快感を味わうと、この会社で働けて良かったという気持ちになります。自分のスキルが大きく向上したことに気付きます。 これから出会う、よりエキサイティングな課題への挑戦が楽しみです。社員を特定のポジションに限定しないので、KINTOテクノロジーズには多くの可能性があります。将来的には私でもアプリケーションサービスの開発に携わったり、テックリードとして働く可能性もあり得ます。 会社として競争力があり、スキルアップの機会も多く、責任ある仕事を任され、キャリアアップを図ることができる、そんな職場をご希望なら、ぜひKINTOテクノロジーズで働いてみませんか? 特にグローバルグループには、多様で優秀な人材が揃っています。各自が業界のトレンドに合わせて常にスキルを向上させています。KINTOテクノロジーズには確固たる基盤があり、ここで働くことになれば、きっとエキサイティングなプロジェクトに取り組むことができるでしょう。 https://www.kinto-technologies.com/recruit/globalkinto
アバター
はじめに こんにちは。KINTO Technologiesのグローバル開発部でフロントエンド開発をしているクリスです。 この間 Vue Fes Japan 2023 に参加してきましたので、当日参加した講演の要約と私の所感について紹介したいと思います。 エンジニアになって最初の頃にVueに触り始めて、以降もずっとこれを利用してきたのにも関わらず、2018年に初開催されたこのイベントに今年初めて参加するのは少し恥ずかしい話ですが、初参加というのと、VueとViteのクリエイターであるEvan You氏に会えるのもあって、当日朝からずっとワクワクの状態でした! そしてようやく会場に着いたら、入り口に書き込みボードがあったのでさっそく弊社の存在をアピールしてみました! Evan You氏による基調講演 主にVue3の振り返りで、良かったことと反省すべきことについて述べられました。 まず反省点ですが、Vue2からVue3へのマイグレイションが大変だったことについて話されました。具体的には以下のポイントです。 破壊的な変更箇所が多かったこと Vueのエコシステム内になるライブラリーに与えるインパクトを過小評価したこと すべての変更箇所を一気にリリースしなかったこと 一方、バージョンアップそのものに関しては、良かったこともありました。 よりTypeScriptフレンドリーになったこと Composition APIを導入したこと 開発者体験(DX)に投資したこと Vue3が確実に成長していること これらを元に、今後の展望としては、「安定」を重視し、これからのバージョンアップの際に今回みたいに破壊的な変更を抑えていきながら、 スムーズに改善点や新機能を取り入れられるようにし、開発者にとってよい開発体験を与えたいとのことでした。 その一つの例として、VueのエコシステムCIを導入し、今後Vueのバージョンを上げる際にVueをdependencyとしているライブラリーに破壊的な影響を与えていないかを確認するCIツールが立ち上がりましたが、全てのテストが通ったとは言え、例えば本公演の前にリリースされたv3.4 alphaに関しては、リファクタリングが多かったので、やはりコミュニティーのみなさんにいろいろテストしてもらい、フィードバックを求めていました。ちなみにこのリリースには、メモリ制御の改善や、より正確かつ効率的なre-computeの実現などが含まれています。 基調講演を聞いて、自分もVue2からVue3へのマイグレイションで挫折したことがありましたので、それをEvanさんが反省点として取り上げてくださって良かったと思います。 ちなみに余談ですが、講演中HDMIの接続がどうやら悪く、何度も切断されましたが、なんとか無事に講演を終えました(笑) Nuxtに関するアップデート 続いてはNuxtLab CEOのSebastian Chopin氏とNuxt CoreチームメンバーのDaniel Roe氏がそれぞれNuxtについてお話しをしてくれました。 まずはSebastianさんがNuxtの最近のアップデートを紹介し、中にはHybrid Rendering(ページごとレンダリング方法を設定すること)のデモやCloudflareを使ってNuxtアプリをEdgeにデプロイするデモを見せてくれ、最後はNuxtのエコシステムをより大きくするための仲間を募集していました。 一方、DanielさんはNuxtのエコシステムを紹介し、それぞれのツールやライブラリーの最新情報を話してくれました。例えば Nuxt2でもComposition APIなど3系の機能が使えるようにする nuxt/bridge Nuxtのテストに様々な機能を提供する nuxt/test-utils マークダウンファイル(.md)だけでページ作成できる nuxt/content などがありました。 そして最後はNuxt4の方針について、よりDXをフォーカスし、オプトイン方式でたくさんのツールを提供し、そしてよりオープンな開発プロセスを実現するために多くの人に参加してもらいたいと話しました。 お二人の講演を聞いて、Vue/Nuxtのユーザーとして、開発に携わっているみなさんの活躍を見て、将来的には一人のコントリュビュターとしてこのコミュニティーに貢献したいなと思いました。 OSS活動における考え方 本セッションはAnthony Fu氏が、ご自身のOSS活動についてお話されました。 まずはご自身がOSS活動に参加されるきっかけとなった vscode-vue-i18n-ally というエクステンションについて例を挙げましたが、名前通りVSCodeというIDE、Vueというフレームワーク、i18nという機能を利用する人のためのアクセサビリティーツールであり、そう考えると実はターゲットユーザーはかなり限定されてしまうことになります。それではより多くの人に使ってもらえるにはどうしたらいいでしょうか。 Anthonyさんは対象となる二つのアプローチを紹介しました、一つはツールそのものの利用制限をなくすことです。例えば、 vscode-vue-i18n-ally に戻ると、Vue以外のフレームワークも使えるようにすれば、利用できるユーザーが自然に増えることになります。その結果が i18n-ally です(私の知っている限りこちらもVSCode用のエクステンションですが)。そしてもう一つはの方法は複数のツールでツールのユニオンを結成することです。例えば、Nuxtの周辺には多くのサポートツールがあり、それぞれを必要とするユーザーが一定数います。このようなユニオンがあれば、ユーザーは必要なツールを使い、それだけで大きい開発コミュニティを作ることができます。この二つのアプローチは利用ユーザーを増やすだけでなく、利用ユーザーが増えたことによって、最終的にコントリュビュターも増えるという効果が期待されます。 彼の話を聞いて、まだOSS活動に携わったことがない自分から見ると、OSSにも普通のプロダクトと同じく、ユーザー獲得という概念があって、より多くのユーザーに使ってもらえるようにはどうしたらいいか常に考えないといけないなと思いました。 質問コーナー 最後のセッションはEvanさん、SebastianさんとDanielさんに色々質問を答えてもらうセッションでした。1時間なのに、事前にみなさんから投げた質問が多すぎて結局一部しか答えられなかったが、その中には特に印象に残ったのが以下4つでした。 Q: サーバーサイドコンポーネントについてどう思うか A: 結局StaticはStaticのよさがあるので、自分のニーズに合うのを使えばいいが、サーバーサイドコンポーネントを利用した方がいい例を一つ挙げるとしたら、ECサイトでしょう。 Q: Vue, Nuxt, Vuetifyをまとめて2から3にマイグレイションするのが大変で、やり方を教えて欲しい。 A: まずこんなに大変になるのは完全に私のせいですね、そこは申し訳ない(笑) 一つのやり方としては、スクラッチから別のプロジェクトを立ち上げて、少しずつ移行していくのがよいでしょう。 Q: Nuxtにとってのいいコンポーネントのデザインパターンはあるのか? A: 特にない、Vueを作った時はAngularがプレゼンターパターンを利用してたが、パターンに縛られたくないという思いがあったからこそ今のVueなので、こちらに関してもまずは小さく開発し、ちょっとずつどんなパターンがいいか決めていけばいいでしょう。 Q: VueのNativeアプリ対応についての予定を教えてください。 A: 今のチームだと対応する余裕がまったくない、たぶん数倍くらいのリソースが必要。IonicやNativeScriptを利用するといいのではないでしょうか。 アフターパーティー アフターパーティーは立食形式で、協賛によって洋食と寿司が提供されました。 自分はかなり内向的な性格でいつもパーティーなどでこちらから声をかけることを躊躇っていますが、勇気を持って最後の最後に来日してくださったEvanさんをはじめとするコアチームメンバーに声をかけてみて、色々話せてよかったです!中にはAnthonyさんに初めてオープンソース開発で色々不安を持っている方へのアドバイスについて聞いてみたところ、「1. まずは小さいツールからスタートしてみること、2. とにかく勇気を持つこと」を語ってくれましたが、これを聞いた私からするとすぐ納得いきましたね。 感想&まとめ 正直に言うと、初めてVueとNuxtのコミュニティイベントに参加し、とても楽しかったですが、それより一番思ったことはやはりOSSコントリュビュターはやはりすごいということでした。VueとNuxtのコアチームはさほど大きくないからこそ、しっかりやることを制限(質疑応答にあったVue Nativeをやらないと決めることなど)したことと、作ったツールをVueだけではなく、他のフレームワークにも恩恵受けるにはどうしたらいいかを考えるマインドセットを持たれていて尊敬しかありません。 来年はどんな形であろうが、またVue Fes Japanに参加したいと思います!
アバター
Introduction Hello! I’m Kin-chan from the Development Support Division at KINTO Technologies. It might seem sudden, but on Thursday, August 3rd, we’ll be hosting our very first KINTO Technologies MeetUp! - 4 Case Studies by and for Corporate IT Teams . This event will focus on the corporate IT domain and take the form of case study presentations followed by a roundtable discussion. In this article, I’d like to share the behind-the-scenes story of how we got this study session off the ground. If you’re someone who’s thinking, "I want to start and lead a study session from scratch," I hope this will be helpful and encouraging. What Comes to Mind When You Hear "Study Session"? It might be a bit sudden, but what comes to mind when you hear the term "study session"? Listening to experts in your field and gaining new insights Getting together with others who share your interests to learn and grow together Joining as part of your job, perhaps even being expected to attend, in the name of team skill-building (Though less common during COVID) Participating mainly for the conversations and food at the social gatherings As you can see, study sessions can serve many different purposes and come in many forms. When I think about what a study session means to me, I’m reminded of a community I once helped organize called " DevLOVE ." The concept behind it was to create a "learning space" where knowledge and experience could flow between the workplace and the outside world—to help move our teams and organizations forward. Having always seen value in study groups and communities like this, I joined KINTO Technologies hoping to get involved in opportunities to share and learn within the company. *I’ll go into more detail about how this mindset turned into concrete action in a future post in our "Agile Series." Okay, Then Let's Have a Study Session When I joined KINTO Technologies, I found that the company already had a strong culture of holding regular study sessions and offering many opportunities for learning. For example: Optional company-wide study sessions for engineers Business-focused sessions hosted by business divisions to deepen understanding of our operations Small group study sessions and book clubs organized within each product team What impressed me most was the supportive culture that encouraged participation in study sessions and book clubs—both inside and outside the company—as part of regular work. There’s even a system in place that covers the cost of books needed for learning. It was clear to me that KINTO Technologies genuinely supports its employees' development. In this environment, I started holding small study sessions and group reading sessions for my own team, spending my days learning alongside my colleagues. And somewhere in the back of my mind, I kept thinking, "I wish more people knew about this culture." One day, a discussion came up in our team about how we could better recruit corporate engineers. As we explored various ideas, one thought kept coming back to me: "How can we showcase our strengths and this great culture to more people?" The challenge naturally connected with my earlier desire to share our company’s learning culture more widely. As a result, we came up with the idea: "Why not hold a study session that includes people from outside the company?" I Can't Do This Alone! Help Me Everyone! With that spark, I quickly drafted a "Proposal for a Corporate IT Study Session" and shared it with our team leaders as a formal idea. Each leader gave it a "thumbs-up." That gave us a green light to move forward, but saying "let's do it" was only the beginning. There was still a lot to figure out: What would the theme be? Who would present? Where would it take place? When should we hold it? What would the timetable look like? It was clear that I couldn’t handle all this alone. So the following week, during the full team meeting, I made an announcement: " We’re going to hold a study session! We’re looking for members to join as organizers and speakers! " In the end, a team of six was formed, including volunteers and recommended members! We immediately created a dedicated channel on Slack and began communicating with all the relevant members. To make sure everyone was aligned on the goal, we kicked things off with an internal meeting for the organizing team. Since not everyone had been involved from the initial planning stage, we focused on a few key areas: What is the purpose of the event, and what will it look like? What is the goal state we’re aiming to reach? What has already been decided, and what still needs to be figured out to reach our goal? How will we resolve the unsolved issues? We began by clearly aligning our "goals" and "understanding of the current status." Then, we set the "next milestone to aim for," and each member began preparations. We Can't Do All by Ourselves!! We Need More Help! The next step was for the organizing team to discuss the unresolved topics. We broke it down into five major areas: Content Format and equipment needs Date and timetable Promotion and outreach How to measure success We held in-depth discussions to clarify each point. As we worked through these, we identified what we could manage ourselves and what would require cooperation from other departments. Based on this, we began setting up meetings that included key stakeholders from other teams. *Once the decision to hold the study session was made, our manager had already started reaching out to divisions that might be needed for support. Thanks to that early communication, we were able to smoothly hold a company-wide kickoff meeting. Thankfully, many of the people who participated in the joint kickoff were very positive. In response to our ideas, they offered suggestions like, "How about trying this?" and "We’ve done something similar before, so it can be handled quickly." Their input helped us get closer to our goal. We Need Flexible Yet Transparent Project Management! As our plans became clearer, a number of concrete action items began to emerge in parallel. We realized that if we simply relied on "whoever could handle it" to take things on as they came, it would be difficult to keep track of progress. In other words, transparency would suffer. To avoid that, we decided to define a certain level of visualizing, progress tracking, and role assignments. Visualizing We used Jira to make tasks visible and prioritize them in a structured way using the "Epic > Task" hierarchy Progress Management We set weekly goals at the Epic level. Each week, we reviewed our progress and adjusted course as needed. If all went well, we would then set the next goal. Division of Roles We assigned responsibility for each Epic. If someone was falling behind or having trouble reaching their weekly goal, others would step in to help. This approach helped us achieve iterative, incremental progress in managing the organizing team. [Side Note] How We Applied "Agile Kata" to Run the Organizing Team As a quick aside, the way we approached organizing this event was inspired by the concept of the " Agile Kata ," particularly its idea of "Improvement Kata." According to "Agile Kata," you can apply agile thinking in your daily work by repeating the following steps: Understand the direction and challenges Grasp the current situation Set the next target state Experiment toward that target in an iterative way Even without relying on a fixed framework like Scrum, you can create an agile way of working simply by applying the Kata mindset. And Right Now... Even as I write this article, the discussions and actions needed for the study session are moving forward every single day, bringing us closer to the goal. We’re holding synchronous web meetings by topic, and Slack is buzzing with asynchronous conversations—some threads get more than 50 replies in a single day. It really feels like things are progressing quickly. Everyone’s doing an incredible job!! With preparations steadily taking shape, the " KINTO Technologies MeetUp! - 4 Case Studies by and for Corporate IT Teams " is finally within reach. Please look forward to the event!!
アバター
Introduction My name is Zume and I am the Quality Assurance (QA) Group Manager at KINTO Technologies. I'm a lifelong cat lover. Cats truly make everything better. There’s something special about their pure, honest nature. In this article, I’d like to introduce the role and responsibilities of our QA Group. About the QA Group The QA Group is primarily responsible for pre-release verification of various services at KINTO Technologies. Our mission is to improve the overall quality of the products we provide and take the lead in QA initiatives. Current Activities Until recently, our QA efforts mainly focused on domestic products like KINTO ONE (New Vehicle and Used Vehicle). However, KINTO Technologies also has a global development group, so we are now also involved in overseas product projects. We also offer an app called Warikan KINTO, which is developed in collaboration with a Swedish company. As a result, our QA communication for this project is conducted in English. In terms of the development process, our QA work corresponds to the system testing phase, which is paired with requirements definition in the V-shaped model. Our QA testing generally includes the following: Verifying that functions and performance meet requirements. Identifying and eliminating bugs and risks. Confirming that the product meets the release quality standards. At KINTO Technologies, User Acceptance Testing (UAT) is carried out by the business side, QA work involves a series of processes, from test planning, design, implementation, reporting defects, to checking modifications, and reporting release decisions after testing is complete. Depending on the project, QA does not only participate after development is complete, but rather participates from the requirements definition phase. The aim is to understand both business and system requirements at an early stage. The QA Group is also the only division that has the unique ability to perform cross-functional testing across different systems. Therefore, from our QA perspective, we aim to raise any concerns in advance so that we can resolve them at the earliest stage. After each release, we hold internal retrospectives, and participate in overall project reviews where we identify trends in defects and propose improvements. These efforts help ensure smoother progress in future projects. If you’re interested in an overview of our QA work, our team member okapi has written another article , so I’d appreciate it if you took a look as well! Group Members, Atmosphere, and Work Style There are currently nine members in the QA Group, including myself. As our team becomes more international, English proficiency is becoming increasingly valuable. While not strictly required, the ability to communicate in English can open up additional opportunities. Each of us has our own areas of expertise, such as apps or web, but we structure the team so that everyone can handle any product as needed. Our members vary in age, and the gender ratio is roughly 50/50. We don’t rely solely on Slack or Zoom. In fact, we often come to the office and talk face-to-face more than online. We also keep a dedicated Zoom room open all day, so whether you’re working remotely or in the office, there’s always someone you can talk to. Since we work with external partner companies, we frequently use Zoom breakout rooms for meetings. To keep the communication flowing smoothly within the team, we hold morning meetings at the start of the day, an evening wrap-up meeting, and a weekly team meeting. While some members work from home depending on the day, most meetings are held via Zoom. However, our weekly team meetings are usually held in person. We also hold study sessions from time to time to help improve team members’ skills. These are not regular events, but we organize them as needed. For now, the main focus is on deepening our understanding of services and system specifications. In the field of testing, the Foundation Level certification defined by ISTQB is generally considered a basic qualification. However, we do not require it as long as you have equivalent experience and skills. In fact, having a certificate alone is not enough. What really matters is how well you can apply that knowledge in practice. That’s why we place greater value on hands-on QA experience, such as the types of projects you’ve worked on and the specific roles you played, as well as your practical skills. This applies to engineers in general, and QA engineers are no exception. Strong problem-solving skills are essential. Future Outlook In our QA Group, we have been working on test automation for some time now. Currently, automation is limited to certain areas and only covers the web, but in the future we would like to expand it to the mobile (native apps) area as well. We hope to share more about the tools we use, and those we’re considering, in a future post. Since everything we work on is car-related, if you enjoy cars, whether driving or just looking at them you'll likely find it even more enjoyable. Personally, I’ve been driving and owning cars for quite a long time, and I still enjoy learning new things and making unexpected discoveries every day. The QA Group also plays the role of the final checkpoint to ensure product quality before it reaches our customers. At the same time, we’re also the team that gets to experience new services before anyone else. If you find that rewarding and interesting, we would love for you to join us.
アバター