TECH PLAY

KINTOテクノロジーズ

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

969

はじめに こんにちは、プロジェクト推進グループに所属している沼田と申します。KINTO FACTORY のバックエンドエンジニアとして日々開発に勤しんでいます。 今回は KINTO FACTORY のサービス紹介と今年の夏に行った DX 化についてお話しします。 KINTO FACTORY とは KINTO と聞くとサブスクの印象を抱く方が多いと思いますが、 KINTO FACTORY (以下 FACTORY)は毛色が違い愛車に長く乗れるようさまざまなアップグレードサービスを提供しています。 具体的にはシート張り替えやホイールキャップ交換など内装/外装を変えたり、ドアの開閉速度を向上させたり、ソフトウェアの書き換えたりするなど愛車をカスタマイズすることができます。 トヨタグループの車種(トヨタ、レクサスや GR)が対象で取り扱う車種や商品、エリアは順次拡大中です。 私はこれまで車といえば一度買うと次の車に乗り換えるまで特に手を加えない印象があったので、FACTORY の取り組みはチャレンジングで面白いと感じています。 施工の流れ FACTORY は KINTO 単体ではなく、トヨタや販売店と連携しながらサービスを提供しています。 ※ 画像内の番号は厳密な流れではなく、あくまでイメージです。 まずお客さまが FACTORY のサイトで商品を申し込むと FACTORY からトヨタに必要な部品を発注すると共に、お客さまが選択した販売店に申し込み内容の共有をします。 販売店は申し込みの内容を確認した後、お客さまと入庫日程の調整を行います。入庫日を確定した後は入庫日にお客さまが販売店に車を持ち込み、販売店は納品された部品を元に施工を行います。 施工が完了次第、販売店からお客さまに連絡を行いお客さまに納車することで施工完了となります。 商品や施工内容によって詳細は異なりますが、大まかな流れは上記のようになります。 施工証明書の DX 化 施工証明書は初めて聞く方もいらっしゃるかもしれません(私は入社してから知りました)。施工完了後にお客さまにお渡しするもので、施工内容や施工日時、施工した販売店などが記載されています。 これまではお客さまに納車する販売店で施工証明書を手作業で作成していましたが、今年の 8 月から FACTORY 上で発行できるようになりました。 施工証明書発行の流れ 購入した商品のステータスが 施工完了 に変わると、マイページ(FACTORY ではマイページのことをマイガレージと表現しています 😃)の購入履歴から発行できるようになります。 画面イメージ: 施工証明書ボタンを押すと下図のように別タブで施工証明書が表示されダウンロードできます。 現在、施工証明書の発行は GR 関連の商品とイベント商品を除くすべての商品に対応しています。 また、2023 年 10月 から始まったインボイス制度にも対応しており、支払い明細書も FACTORY から発行できます。 アーキテクチャや技術スタック アーキテクチャ周りも軽く紹介します。 FACTORY は AWS 上で稼働しており、ざっくりとした概要図ですが上図のような構成でマイクロサービスアーキテクチャを採用しています。 施工証明書を発行するマイクロサービスは Go 言語で開発しており、PDF 発行のライブラリとして gopdf を使用しています。発行された施工証明書は S3 に保存されます。 PDF のテンプレート 弊社は Office365 を導入しているため、PDF の雛形管理にエクセルを用いています。変更があればエクセルの雛型を修正し PDF としてエクスポートします。 トヨタの施工証明書 LEXUS の施工証明書 車種によって施工証明書のデザインが変わることに加え、商品名の文字数もさまざまなためどちらにも影響がないようにレイアウトを決めるのに苦労しました。 手作業で作成していた施工証明書と同じ A5 サイズにしているのは小さなこだわりです。 余談: 今回の対応で初めて PDF をプログラムで扱ったのですが、正直こんなに愚直な作業とは思いませんでした。 施工証明書の DX 化を経て これまで販売店で手作業で行なっていた施工証明書の発行を FACTORY 上で行えるようにすることで、お客さま、販売店ともにより良い体験を提供できるようになったと思います。 施工証明書以外にも手作業で行なっている作業がまだまだあり、自動車業界の DX 化の余地はたくさんあると感じています。 今後も FACTORY はより良い体験を提供できるように、さまざまな取り組みを行なっていきます。 さいごに FACTORY では一緒に FACTORY を盛り上げていく新たな仲間を募集しています。カジュアル面談のご応募お待ちしています! @ card
アバター
Introduction Hello. I am Koike, a data engineer in the Analytics Group. At my previous job, which I started as a new graduate, I mainly performed analyses for service growth, but in my current position, I am developing a data analysis platform. To put it simply, I changed my career from a data analyst to a data engineer. In this article, I would like to talk about my journey from being a data analyst to a data engineer. Data Analysts and Data Engineers As there might be some who are not familiar with the roles of each, I'd like to begin by outlining the responsibilities of a data analyst and a data engineer. The responsibilities of each role are shown in the diagram below. Data engineers are responsible for preparing data for data analysts to aggregate and analyze. Their specific tasks are as follows. Acquiring data from other systems and data sources. Processing data obtained in 1 into a form that is easy for data analysts to use. Delivering data processed in 2 to allow data analysts to access it. In contrast, a data analyst is responsible for aggregating and analyzing the data and suggesting how to improve the business. Their specific tasks are as follows. Aggregating data prepared by data engineers using SQL or other tools. Analyzing the data aggregated in 1. Suggesting how to improve the business based on the results of the analysis in 2 Or, they may do the following tasks. Aggregating data prepared by data engineers using SQL, or other tools. Summarizing the data aggregated in 1 in a dashboard and creating an environment where data can be observed at a fixed point I hope this gives you a general idea. With this in mind, in this article, I would like to show you how I changed my career from a data analyst to a data engineer and started working as the latter. I hope this information will serve as a reference for those considering a career transition to become data engineers. The Data Architecture of KINTO Technologies Before talking about data platform development, I will explain the company's data architecture first. It is mainly composed of AWS services, and the general flow of data is as follows. Using a service called Glue, data obtained from external sources is converted, processed, and stored in S3 An SQL query is performed on S3 data using a service called Athena Our data engineers mainly do part 1, creating an environment in which various data can be aggregated and analyzed in Athena. Glue Workflow Development Now that you understand the data architecture, I will talk about the Glue workflow I developed as my first step from data analyst to engineer. Glue has three main features. Job: Function that does preprocessing of analyses (data extraction, conversion, loading) Crawler: Function that creates metadata in the Data Catalog. Trigger: Function that runs jobs and crawlers manually or automatically A Job is the function that performs preprocessing for an analysis. For example, you can define a series of processes such as reading, processing, and outputting CSV data as a single job. The Crawler has the function of creating metadata in the Data Catalog. Basically, you can define data types such as table input/output formats and column names, and put them in a box called a Data Catalog. A Trigger is the function that runs a Job or Crawler manually or automatically. It can be executed at a fixed time every day, or when the previous job is successfully completed. In addition, a workflow combines these three processes into a series of processes to make them easier to manage. By developing a workflow that allows data from external sources to be aggregated in Athena, I got a general understanding of the data flow and took my first step as a data engineer. By the way, when I was a data analyst in my previous job, I was in an environment where data engineers aggregated and analyzed formatted data, so I did not pay much attention to how the data was created. However, during development, I was able to better understand how triggers and jobs are combined and other parts of data preprocessing. It was a great experience. Comparing the Skill Set of Analysts and Engineers So far, I have talked about the job of a data engineer, but I would like to outline the skill sets that are required for data analysts and data engineers. The Data Analyst Skill Set Analysis and design Aggregation Analysis Explaining results of analyses The first skill required by a data analyst is the ability to analyze and design. For example, if a marketer says, “I want this data.” You can just output data as you are told, but doing so could lead to rework. Therefore, it is necessary to clarify the original purpose by asking why they want you to output that data, and determine what kind of data you can output and analyze to achieve that purpose. That is analysis and design. Next is the ability to aggregate. This refers to extracting data that you want using SQL or other tools. It is surprisingly difficult to learn how to check figures to check there are no mistakes in the extracted data written with SQL, or how to write SQL with few mistakes. Next is the ability to analyze. Basically, it is the ability to think logically without subjectivity. To do this, you may need to know about statistics, machine learning, and so on. The last skill is the ability to explain the results of analyses. No matter how sophisticated your analysis is, it has no value unless it can be applied to the business. It only has value when you explain it properly to the decision makers and get them to understand it. The Data Engineer Skill Set Data pipeline Design Code design Data processing The first is the ability to design data pipelines. To compare with what I have explained in this article, you can think of it as the ability to determine how to configure the data processing workflow to produce the data you are looking for. Next is the ability to design code. I think this true for all engineers, not just data engineers, but coding does not end with writing and may need to be modified later. Therefore, it is important to write code that is easy to maintain. The last skill is the ability to process data. We mainly use SQL and Python, it is necessary to be able to handle them sufficiently. We have now compared the skill sets necessary for a data analyst and a data engineer. Future Outlook So far I've told you about half a year of my experience since I changed my career from a data analyst to a data engineer. Looking back, I have been able to grow my skills as a data engineer little by little, but now I feel like I am losing my perspective as a data analyst because I'm concentrating too much on development. Therefore, in the future, I would like to remember to create a platform that is easy for users to use. No matter how beautiful the data platform is for developers, it has no value to the business unless the data analyst effectively communicates its output to the business side. In order to avoid this, I will use my experience as a data analyst and as a data engineer and work hard every day to be able to connect data to value from start to finish!
アバター
はじめに こんにちは。ご覧いただきありがとうございます! KINTO FACTORY (以下 FACTORY)というサービスで、フロントエンド開発をしている中本です。 この度、アドベントカレンダーに参加することになりましたので、今年リリースした「車検証の2次元コード読み取りの実装」についてお話させて頂こうと思います。 導入の動機 FACTORY では、現在お客様がお乗りのクルマを「アップグレード」や「リフォーム」、「パーソナライズ」といった観点から長くお乗り頂けるサービスを展開しております。 FACTORY のフロントエンドは EC サイトのような役割となっており、お客様のクルマ情報から、取り付けが可能な商品を検索でき、実際にお申し込みまで WEB 上で完結することができます。 そこで重要となってくるのが、車種や年式・グレードといった、現在お客様がお乗りのクルマの情報となってきます。 車種・年式・グレードなどの情報から、どの商品を取り付けることができるか、や商品の組み合わせ可否、などを判断しております。 FACTORY では、それらの情報を正確に判定するために「車台番号」とよばれるものを使用しております。ご存知の方も居られるかもしれませんが、実は車検証を眺めてみると「車台番号」といったシリアル番号のようなものを見つけることができます。(下図の中の左上から3つ目の欄) ※出所:国土交通省「自動車検査証(車検証見本)」( https://wwwtb.mlit.go.jp/hokkaido/content/000176963.pdf) FACTORY では、こちらの「車台番号」を入力することで、そのクルマに取り付け可能な商品を簡単に探すことができます。 商品一覧ページ(車台番号で商品検索済み) ここで、下図の車台番号を入力するフォーム部分を見ていただくと、リリース当初は図のようにユーザーによる手入力フォームとなっていました。 車台番号入力フォーム 車台番号入力による検索機能は、今年の6月にリリースしたのですが、リリース当初よりこちらの入力欄に直接慣れない英数字を入力することは困難ではないか、と思っていました。 特に、昨今の EC サイトの例に漏れず、FACTORY へのアクセスはスマートフォンからがほとんどとなっております。 画面の小さいスマートフォンで、何桁もの英数字を入力頂くのは手間であり、また入力ミスも起こりやすく、入力ミスしてしまうと車両自体が FACTORY の非対応車両となってしまい、適合する商品が見つからないといった機会損失に繋がっているのではと考えました。 そこで目を付けたのが、車検証の右下の方にひっそりと印刷されている2次元コードでした。 車検証の2次元コードから読み取れる情報 国土交通省の 電子車検証 特設サイト にある通り、車検証の2次元コードから上記の検索で使用する「車台番号」を読み取ることが可能でした。 また、FACTORY で取り付けたい商品が見つかり、実際に購入へ進もうとした場合、まずお客様のクルマ情報を登録する必要があります。この登録には、上記の「車台番号」以外に、ナンバープレートの情報も必要となっております。車検証の2次元コードには、基本的に車検証に書かれている情報が入っており、これらを読み取ることで、上記の商品の検索に加えそのまま購入に進んで頂いたときの車両登録も簡単に行えるのでは、と考えました。 実際に車検証を見られた方だとお分かりかと思いますが、下図にあるように2次元コードには、3つ並んでいるものと2つ並んでいるものとがあり、過不足なく読み取ってもらう必要があります。また、それぞれの2次元コード単体のみでは情報が分割されて格納されており、すべてのコードを読み取った後に順番通りに並べて結合する必要があります。 そこで、それぞれのグループのどの位置のコードを読み取っているか、をわかりやすく画面へフィードバックしたいと考えました。 ※出所:国土交通省「2次元コードについて」( https://www.denshishakensho-portal.mlit.go.jp/assets/files/Two-dimensional_code_item_definition.pdf ) 読み取り UI の実装 スマートフォンのカメラを使って読み取ったものを画像処理し、2次元コードをデコードしてくれる Java Script ライブラリにて解析するといった流れとなります。「カメラで読み取る」部分や、「2次元コードのデコード」は初めての試みだったので WEB で情報を集めながらテストしていきました。 カメラの読み取りには、 getUserMedia() の Web API を、2次元コードの解析には qr-scanner という js library を選定しました。 流れでいうと、 getUsetMedia で読み取ったものを canvas 要素へコピーしつつ、 qr-scanner へ転送しコードを解析、データが読み取れたらどの位置のものかを判断する、といったものとなります。 実装のテストをしている段階で、少し難しかったのが、新車検証の場合2次元コードの読み取り率が少し悪いということでした(なかなか読み取れないなど)。 というのも、実は車検証ですが、今年の1月に電子車検証というタイプに変更がされており、最近クルマを購入された方や車検をした方だとお分かりかと思いますが、以前のもの(A4 サイズ)よりかなり小型化(B5 サイズ)されております。 これに伴い、2次元コード自体も少し密度が高くなったような印象があり、なかなか読み取れない状況でした。 そこで、2次元コードをデコードする qr-scanner のソースを眺めながら、Grayscale の設定値を調整していくことで、新・旧どちらのタイプの車検証もある程度の確率で読み取れるようになりました。Canvas へ画像データをコピーした後、上記 Grayscale の設定値を使って、白黒画像へコンバートしそれを2次元コードの解析ライブラリへ送信するといった動きでしたが、値によっては車検証自体の模様などがゴミとして映り込んでしまい、それにより読み込み率の減少を招いていたようでした。 https://github.com/nimiq/qr-scanner/blob/master/src/worker.ts#L73-L79 UI の実装自体は、リアルタイムにカメラで写している画像を見ながら、読み取れた2次元コードの位置をグループ別に示していきたかったので、上部に読み取りの必要な数だけ BOX を並べ、読み取れた2次元コードの枠にチェックアイコンを表示するようにしてみました。 実際の読み取り画面が下図のようになります。 コードの読み取る順番は関係なく、読み取ったコードの位置が正しく認識されていることが分かるかと思います。すべての情報を読み取ると、手入力にて車台番号検索した時のように画面が遷移します。 リリースしてみて こちらの2次元コードによる検索機能は 10 月にリリースすることができました。このリリースからしばらくは、上記説明の中での画面例のように、商品一覧ページのみへの適応だったので、劇的にこちらの機能が利用されたとは言いづらいですが、現在はトップ画面へも適応されております。トップページから、2次元コードを読み取ることで、対応車両のチェックや商品適合のチェックまで一度で行えることができるようになっており、利用率向上を見込んでおります。 また、「読み取れる情報」の部分でも少し触れましたが、2次元コードには車両登録に必要な情報も含まれているので、今後は車両登録時にもこちらの情報を使っていき、できるだけ手入力部分を減らすことで利便性や入力ミスの削減を狙っていきたいと思います。 最後に スマートフォンの台頭や、Web コンテンツのリッチ化、各種 Web API の拡充によって Web サイトの可能性が広がっているように思います。 例えば、リアルタイムによる OCR や NFC タグの読み取りなど、非接触技術なども含めてユーザーの利便性を考えながら新しい技術にも積極的にチャレンジしていきたいと思います。 より簡単に、楽しく KINTO FACTORY を使っていただけるように、色々なアイデアを実現していく方法を模索していきたいです! 最後に、私の所属する KINTO FACTORY では一緒に働く仲間を募集しています。 ご興味があればぜひ下記の求人をチェックしてみてください! @ card @ card
アバター
こんにちは KINTOテクノロジーズ 人事採用グループのHOKAです。 組織人事と採用広報を担当しています。 X(旧Twitter) も担当しております。 さて、年に1度のアドベントカレンダーの機会に、「人事採用グループがどう立ち上がったか」をTechblog につづります。 どうか最後までお付き合いいただければ幸いです。 開発組織、KINTOテクノロジーズ設立 KINTOテクノロジーズ株式会社(以下、KINTOテクノロジーズ)は2021年4月に設立し、現在3年目です。2023年12月時点で従業員数は320人を越えました。文字通り「急成長」している企業です。 そもそもKINTOテクノロジーズは、株式会社KINTO(以下、KINTO)のITエンジニアを中心とした開発組織「IT開発編成グループ」でした。KINTOテクノロジーズ設立前は、KINTOでITエンジニア採用を担当していましたが、ITエンジニアはどこの企業も人手不足の売り手市場。優秀なITエンジニアを採用するには、採用手法をIT業界に合わせる必要があるのではないか、という仮説が浮上しました。そこで、IT業界でのエンジニア採用経験がある岩本さんがKINTOテクノロジーズで、ITエンジニアの採用をスタートしました。いわゆる「一人目人事」です。 「エンジニア採用」だけがミッションだった 初めは「エンジニア採用」だけが岩本のミッションでした。 岩本さんが参画した2021年11月時点では、KINTOテクノロジーズの社員数は約130人。人事部門を立ち上げる話はなく、採用担当は岩本のみ。秘書の志田さんと共に始動します。 製造業のやり方ではITエンジニアは採用できない まず、岩本さんが着手したのが、IT業界の採用スタイルに変えていくことでした。 PDFだった求人票を、採用管理システム(ATS)導入へ 求人票がPDFだったため、応募する際は転職エージェントへの登録が必須でした。KINTOテクノロジーズに興味を持っていただいたタイミングで応募できるよう、コーポレートサイトの改修を行い、採用管理システム(ATS)を導入しました。 面接は「見極める」のスタンスから「対話」へ 候補者1名:面接官6名の面接スタイルは圧迫感があり、不評でした。 面接官トレーニングを実施し、求職者の方のキャリアをじっくり伺う対話スタイルへ面接を変えました。 候補者に寄り添い、柔軟性のある選考フローに切り替え 候補者の要望をヒアリングし、現場エンジニアとのカジュアル面談や、オフラインでの面談を実施したり、面接スケジュールも候補者に寄り添うことを目指しました。柔軟性のある選考フローへと切り替えました。 内定通知までにかかる日数を5営業日短縮 最終面接後、面接官のコメントを添えたオファーレター(紙)を名古屋に郵送し、採用条件が決裁されるまでに5営業日を要していました。フローを見直し、電子承認システムを導入。最終面接の翌日には承認が終わるようになりました。 転職エージェント向け企業説明会を開催 今後の展望やポジションをより詳しく知っていただくために、KINTOテクノロジーズの取締役副社長である景山さんと、マネージャー数名が登壇する、3時間ほどの説明会をオンラインで開催しました。約15社の転職エージェントが参加し、今もKINTOテクノロジーズの採用を支援いただいております。 そして、最後は力技!!岩本は9:00~21:00まで面接し、並行して上記のカイゼンを行っていました。2022年1月に有能な採用アシスタントの竹野さんが入社し、採用スピードはますますアップしていきました。(現在は18:30頃に退社していますのでご安心ください・笑) 人事採用グループの発足へ 岩本さんの参画後、採用実績は1年間で140人に(2022年1月1日~2022年12月31日)。 一方、組織の拡大に伴い、組織課題が表面化してきました。そこで、2022年4月に労務のエキスパートであるつんつんが入社します。私たちは新入社員を対象とした「入社後面談」や、「退職者面談」など、採用以外の人事業務に少しずつ着手できるようになっていきます。社員の声を経営層に届ける役割を果たすことができるようになってきたのです。同時に オフィスのカイゼン もつんつんが推進し始めます。 そしてついに、景山さんから「人事採用を組織化しよう」という声がかかりました。 2022年8月、社長から「階段の踊り場に立って冷静に」 2022年7月、KINTOテクノロジーズとしては初めての「全社員面談」を実施します。当然、社員にとっても初めての機会なので、会社へのリクエストや課題がたくさん寄せられ、どう解決していくか、人事採用グループは思案に暮れていました。その状況を見て、KINTOおよびKINTOテクノロジーズの代表取締役社長である小寺さんから「階段の踊り場に立って冷静に。組織規模に見合ったマネジメント機能を構築してから、人を採用しなさい。」というアドバイスがありました。2022年8月のことです。 そこからは、あえて採用スピードを落とし、マネジメント研修等を実施し、組織力の向上に取り組みました。そして、徐々にマネージャー同士が連携し始めました。 2022年10月、採用の要件を見直す KINTOテクノロジーズはKINTO以外のサービスも開発し、会社としてできることもエンジニアの役割も大きくなっていました。思い切って採用の要件を見直し、より組織にフィットするよう、自社サービスの開発経験がある人をターゲットとしました。 同時期にメガベンチャーでHRBPの経験がある村中さんが入社します。先に記載したマネジメント機能の構築や、採用スピードを落とし、採用要件を見直したことなど、人事採用グループの舵取りがその後の組織力強化につながっていくのですが、村中さん自身はKINTOテクノロジーズの成長スピードに圧倒され「入社直後の2022年10月から2023年3月までの記憶がない」と苦笑いしております。 人事採用グループは第2フェーズへ 2023年12月、KINTOテクノロジーズの社員数は320人になりました。組織の拡大に伴い、部署の階層も増え、役割も増えてきました。現場主導でどういう人に入社していただきたいかを考え、人事採用グループにリクエストが来るようになり、私たちはマネージャーや社員と、より近い距離でヒアリングを行っています。また、新たに面接に関わる方には面接官トレーニングを実施しており、採用に関わる社員も常に増加しています。 岩本一人で始まった採用活動は、今や部門を横断した活動になりました。 現在、人事採用グループは10人が所属しており、採用チーム、労務総務チーム、組織人事チームの3チーム体制になりました。社員の皆様がよりチャレンジし、成長できる会社でいられるよう、人事採用グループ一丸となって2024年も邁進していく所存です。 さて… 年末に人生を振り返ったり、ライフプランを見直したり、転職を検討される方が多いかと存じます。KINTOテクノロジーズでは、様々な職種で新しい仲間を募集していますので、興味のある募集がありましたら、ぜひお気軽にご応募ください。カジュアル面談も承っています。 KINTOテクノロジーズの募集職種は こちら 以上で、「人事採用グループをつくろう」を終わります。 アドベントカレンダーなので、最後はこの言葉で。 メリークリスマス!!
アバター
私は Tim です。KINTOテクノロジーズ(KTC)のグローバルプロダクト開発グループで UI/UX デザイナーをしています。私たちの UI/UX チームには、建築からビジネス、地質学まで様々な文化的、学術的背景をもつ計 4 人のデザイナーおよびリサーチャーが在籍しています。このような多様性を強みとして、複雑なデザイン課題に対しあらゆる方法論で対処でき、全方位的かつ実効性の高いソリューションを提示してきました。 課題 KINTO テクノロジーズにおける重要なデザイン課題のひとつは、 モバイルアプリケーション から マーケティングウェブサイト に至るすべてのKINTO テクノロジーズプロダクトにおいて、デザインプラクティスを標準化させ一貫したブランドランゲージを確立することです。この課題に対処していくため、書体、カラーパレット、キービジュアルなどの正しい使用法を含むデザインガイドラインをまとめたブランドポータルをリリースしました。 ブランドポータルの全容 このガイドラインを KINTO テクノロジーズプロダクトに適用するにあたり、ブランドポータルだけでは不十分だということが複数の理由により明らかになっています。 非デザイナーによる理解の必要性 :デザイナーではない人にとっては、それぞれのデザインにおける判断の背景にある意図を直接理解していないと、ブランドポータルを自分のプロジェクトに適用するのは難しいことです。 コラボレーションの必要性 :デザインアセットライブラリがないと、デザイナーは自分の作業をエンジニアと直接共有することができません。その結果、複数のエンジニアが仕事を引き継ぐことでそれぞれのプロジェクトの外観に相違が生じてしまい、それらが一人のデザイナーによる制作であったとしても本来のコーディングと矛盾してしまいます。 開発コストの増加 :デザインの更新や変更が行われると、エンジニアはコードベースを手動で書き直す必要があるため、一貫性が失われるリスクが生じますし、時間とコストの浪費にもつながります。 こういった問題が続く中、一貫性と汎用性のあるソリューションを確立すべきと全員が考え、 全社的、部門横断的なデザインシステム を構築していくことがチームの指針となりました。 デザインシステムとは Invision 社 では、デザインシステムの定義がわかりやすくまとめられています。 A collection of reusable components, guided by clear standards, that can be assembled together to build any number of applications. 明確な基準に基づいた再利用しやすいコンポーネントを集めたもので、それらを組み合わせることでいくらでもアプリケーションを構築できるもの(筆者翻訳) デザインシステムは、相互に関連する 3 つの要素によって成り立っています。 ユーザーインターフェース (ボタン、テキストフィールド、アイコンなど)が、 ビジュアルスタイル (色、グリッド、活字など)によって形成され、 ウェブパターン (ヘッダー・フッター、コンタクトフォーム、コールトゥアクション)で表示される。 デザインシステムの定義を図示 プロダクトデザインの文脈では、ブランドポータルとデザインシステムには少し似ていますが、前者はプロダクトのブランドと見た目にフォーカスしている一方、後者はビジュアル以上にインタラクションデザインや UI コンポーネント、デザインパターンやユーザビリティガイドラインの範囲にまで及びます。 デザインシステムを綿密に整理しておくことにより、全員が同じデザイン原理に従うことが可能となり、それぞれのプロダクトやプラットフォーム間でのビジュアル上の一貫性および機能上の一貫性を維持することができます。 KINTO Design System はどのように作られたか? 無からは何も生まれません私たちのチームでは、ゼロからデザインシステムを構築するのではなく、既存のオープンソースフレームワークを利用することにしました。最終的には、 Vuetify をフロントエンドフレームワークとして採用し、組織のデザインランゲージとして Material Design を選択しました。同時に、アクセシビリティやインクルーシビティなどの見過ごされがちですが大切なデザイン要素については Human Interface Guidelines を参照しました。 KINTO Design System の方法論とツール この判断が、デザインチームとエンジニアリングチーム双方にとって大変有益な結果をもたらすことになりました。というのも、全プロジェクトメンバーがデザインシステムの共通部分と異なる部分に対して、初めて同時に作業できるようになったからです。 Figma などのデザインツールを使用し、コンポーネントを構築するのと同時に、エンジニアが設計をレビュー、プロトタイプ作成、テストを行いコード化していきました。このような手法のおかげで、開かれたコミュニケーションを続けるきっかけができ、部門間の協業がこれまでにないレベルで浸透していきました。 KINTO Design System モバイルビュー画面の実装 私たちのエンジニアチームでは、デザインシステムのドキュメント化について Storybook で幅広く紹介しています。 結果と次のステップ KINTO Design System は、デザインとコードでシームレスに運用できるため、エンジニアおよびステークホルダーの皆様にとっては、私たちの成果を単なるデザイン提案という以上に KINTO テクノロジーズプロダクトに必要不可欠な要素とみなしていただけるようになっています。結果的に、エンジニア側では、トラブルシューティングの必要がなかったとしても、デザインとコーディング両方の仕様を満たす目的でデザインシステムを学習、採用しようという意志をみせてくれています。 たとえば、ユーザーのサインアップフローでよく使用されるステッパーのセットを最初からコーディングするには 3 時間かかります。しかし、KINTO Design System と Vuetify を使えば、 完了するまでに10分もかかりません 。 ステッパー作成時に要するコード行数 95行 vs 20行 KINTO Design System はプロダクトの共存関係にじかに影響を及ぼすことができますが、いまだ発展途上のものであることには誰もが納得していただけると思います。新しいユーザーインターフェースやウェブパターンが様々検討されている昨今、デザインアプローチに関する試行錯誤を続けながら、多様なパートナーたちと部門横断的かつ実効性ある対話をしています。私たちの長期目標は、内部向けプロダクトおよび外部向けプロダクトの双方におけるユーザビリティの向上です。KINTO Design System は、その最初の一歩となるものです。 当記事は、KINTO Design System に関する 2 部構成のシリーズの第 1 回目です。第 2 回目の記事では、実際の KINTO テクノロジーズプロダクトにおける適用事例を詳しく説明していきます。どうぞご期待ください! 参考 デザインシステムハンドブック - DesignBetter ヒューマンインタフェースガイドライン | Apple Developer Documentation Material Design Vuetify — Vue.js 向けマテリアルデザインフレームワーク Figma:コラボレーティブ・インターフェース・デザイン・ツール Storybook:UI 開発用フロントエンドワークショップ
アバター
Introduction Hi, I'm Daichi, Front End Engineer from the Global Development Division at KINTO Technologies(KTC). Currently I'm developing the e-commerce website for KINTO Factory . KINTO Factory is a vehicle upgrading service for Toyota and Lexus car owners. It offers the opportunity to integrate the latest hardware and software technologies into their vehicles through the introduction of three primary services: Reforms, Upgrades, and Personalization. As a growing e-commerce website, SEO and page load time take a fundamental role in reaching more users and delivering a better user experience. I'd like to share how we improved SEO and page load time in KINTO Factory by optimizing the Core Web Vitals score. Let's take a closer look. What is Core Web Vitals? Core Web Vitals is Google's initiative to measure real-world user experience for loading performance, interactivity, and visual stability of the page. In May 2021, Google officially announced that Core Web Vitals would become a ranking factor for search results affecting SEO. These are the current(2023) 3 main Core Web Vitals metrics: Largest Contentful Paint (LCP) - measures how long it takes to load the largest image or block of text in the viewport(your PC/phone screen). First Input Delay (FID) - measures how long it takes for the browser to respond when a user engages with the page (button click, tap, input, etc). Theres another pending metric called Interaction to Next Paint (INP) similar but focuses on the responsiveness of the page after it has initially loaded. Google announced that INP will replace FID in March 2024. Cumulative Layout Shift (CLS) - measures the visual stability of a webpage during loading. Before optimization There are many tools out there to measure your website, but I recommend Google PageSpeed Insights , which gives you a detailed report pointing areas to improve(even gives you tips of how to improve the issues) and you can see how your page has performed in the real world(based on Chrome browser data). Before the optimization, KINTO Factory's score was as below for mobile and desktop. As you can see in the image above(Core Web Vitals thresholds) and comparing it with the results below the red color is not a good sign. Before optimization MOBILE DESKTOP After analyzing the reports, some of the main issues for the slow page load were related to the images: The assets(especially images) loaded on the first page load were too large. Around 13MB in mobile and 14MB in desktop. The image size was too large(most of them having 300+ KB). Improper image size according to the screen size. Using the same image for mobile and desktop. Largest Contentful Paint image was lazily loaded. Image elements without an explicit width and height causing layout shifts through the website. Loading mobile and desktop specific images everytime because of how the markup and css were implemented. Mobile assets size(before) Desktop assets size(before) After optimization So now that we measured the website performance and know some of the issues slowing down the page, we started working on it. What we did to optimized KINTO Factory: Check all the images, optimized them according to the screen size. Use a proper format for each image. Using webp images as well, especially for the Largest Contentful Paint images in the first view. Lazy load images that are not shown in the first view and load them when needed(when showing in the screen). But preventing lazy loading images in the first view. Set explicit height and width for images to prevent layout shifts(especially for the Largest Contentful Paint images in the first view). Establish early connections with rel=preconnect resource hint for faster font load(Google fonts). Prevent layouts with mobile and desktop markup, which was causing to load unnecessary images in every page load, since the image elements were rendering but showing only by styling(css). Like the code below. <!-- Before --> <img src="pc-image.png" class="show-on-desktop-size" /> <img src="sp-image.png" class="show-on-mobile-size" /> <!-- After --> <picture> <source media="(min-width: 600px)" srcset="sp-image.png" /> <img src="pc-image.png" alt="🙂" /> </picture> After implementing the above optimizations, we managed to: Cut the assets size over 60%. Improve page load time. Reduce the cumulative layout shift(CLS) almost to 0. After optimization(mobile/desktop) BEFORE AFTER Mobile assets size(after) Desktop assets size(after) Conclusion Core Web Vitals is a great way to measure your website overall performance. As you saw in each report, just with simple asset(images, fonts) optimizations, you can deliver a better user experience and rank higher in search results improving SEO. As the first step for KINTO Factory we optimized the top page, and I believe it was a great step. However, the scores are still not yet optimal, so I think there is more work to do, to keep improving so that we can give all users the best experience.
アバター
はじめに KINTOテクノロジーズでmy routeのAndroidを開発しているHand-Tomiと申します。 最近、私たちのプロジェクトでは、RecyclerView内のRecyclerViewアイテムが完全に表示された際のイベントトリガーが必要になりました。これを実現する方法はいくつかありますが、RecyclerViewのAdapterに少しコードを追加することで解決できる方法を見つけました。この方法とその実装について、皆さんと共有し、意見交換をするためにこの記事を執筆しました。 :::details RecyclerViewとは RecyclerViewは、Androidアプリケーションで効率的な動的データセットの表示と管理を行うための拡張可能で柔軟なUIコンポーネントです。 ::: この記事の目的 複数RecyclerViewのアイテムが完全に表示された際にイベントをトリガーする方法について説明しましょう。 例えば、左の画像(←)が表示されたときに、 Title1 の Card1 と Card2 、そして Title2 の Card1 と Card2 がトリガーされることを想定します。そして、 Title2 をスクロールして右の画像(→)のように表示を変えた際に Title2 の Card3 をトリガーする方法について解説します。 左の画像(←) 右の画像(→) 単語 親RecyclerView : 縦スクロールが可能な全体のレイアウトに配置されたRecyclerViewです。 子RecyclerView : 「親RecyclerView」のアイテムとして横スクロールが可能なRecyclerViewです。 親RecyclerView 子RecyclerView スクロールイベントの受け取り まず、アイテムの表示に変化があった場合、その表示状況を確認するために以下の二つイベントをトラッキングする必要があります。 親RecyclerViewが縦スクロールされる時 子RecyclerViewが横スクロールされる時 これらのイベントをトラッキングするためには「子RecyclerView」に、 viewTreeObserver.addOnScrollChangedListener を設定します。 recyclerView.viewTreeObserver.addOnScrollChangedListener { // TODO: 表示状況の確認 } viewTreeObserver では全体のレイアウト変更や描画開始、タッチモードの変更など、ViewTreeのグローバルな変更を監視するリスナーを登録するために使用されます。 viewTreeObserver の addOnScrollChangedListener を登録することで画面に含まれているスクロール変更イベントを取得することができるようになります。 「子RecyclerView」がレイアウトに配置された際にイベントを取得するため、「子RecyclerView」のAdapter内で onAttachedToRecyclerView() メソッドに設定し、 onDetachedFromRecyclerView() メソッドで解除するコードを記述します。 private val globalOnScrollChangedListener = ViewTreeObserver.OnScrollChangedListener { checkImpression() } override fun onAttachedToRecyclerView(recyclerView: RecyclerView) { super.onAttachedToRecyclerView(recyclerView) this.recyclerView = recyclerView recyclerView.viewTreeObserver.addOnScrollChangedListener(globalOnScrollChangedListener) } override fun onDetachedFromRecyclerView(recyclerView: RecyclerView) { super.onDetachedFromRecyclerView(recyclerView) recyclerView.viewTreeObserver.removeOnScrollChangedListener(globalOnScrollChangedListener) } private fun checkImpression() { // TODO Check } 上記のコードを実装することで、親または子のRecyclerViewがスクロールされた際、または初めて表示された際に、イベントが checkImpression() 関数に渡されます。 「子RecyclerView」の完全表示をチェック 「子RecyclerView」自体が完全に表示されていなければ、その中のアイテムも完全に表示されていないと見なされます。したがって、まず「子RecyclerView」が完全に表示されているかを確認する必要があります。この確認のために以下の関数を作成しました。 private fun RecyclerView.isRecyclerViewFullyVisible(): Boolean { if (!this.isAttachedToWindow) return false val rect = Rect() val isVisibleRecyclerView = getGlobalVisibleRect(rect) if (!isVisibleRecyclerView) return false return (rect.bottom - rect.top) >= height } View.getGlobalVisibleRect(rect: Rect): Boolean このメソッドは、Viewが画面上に一部でも表示されている場合に true を返し、全て表示されていない場合には false を返します。 rect には、画面の左上を原点とするViewの位置とサイズが格納されます。 if (!this.isAttachedToWindow) return false RecyclerView がレイアウト階層に含まれていない場合、 getGlobalVisibleRect メソッドは true を返すことがあります。そのため、レイアウト階層に含まれていない場合はチェック処理をスキップし、 false を返します。 (rect.bottom - rect.top) >= height 表示されているViewの高さを確認し、Viewの高さを比べてViewが完全に表示されているか確認します。 この関数を checkImpression() メソッドに組み込むことで、「子RecyclerView」が完全に表示されていない場合、処理をスキップします。 private fun checkImpression() { if (recyclerView?.isRecyclerViewFullyVisible() == false) { return } // TODO 「子RecyclerView」のアイテムが完全に表示しているか確認する } 「子RecyclerView」で完全表示アイテムのPositionを取得 LinearLayoutManager は、RecyclerViewのアイテムが画面に表示されているかどうかを確認できる関数を提供しています。 findFirstCompletelyVisibleItemPosition() 画面に完全に表示されている最初のPositionを返します。 findLastCompletelyVisibleItemPosition() 画面に完全に表示されている最初のPositionを返します。 findFirstVisibleItemPosition() 画面に部分的にでも表示されている最初のPositionを返します。 findLastVisibleItemPosition() 画面に部分的にでも表示されている最初のPositionを返します。 この記事では、アイテムが 完全に表示 しているかどうかを確認することです。そのため findFirstCompletelyVisibleItemPosition() と findLastCompletelyVisibleItemPosition() を使いました。 private fun checkImpression() { if (recyclerView?.isRecyclerViewFullyVisible() == false) { return } val layoutManager = layoutManager as? LinearLayoutManager ?: return null val first = layoutManager.findFirstCompletelyVisibleItemPosition() val last = layoutManager.findLastCompletelyVisibleItemPosition() // TODO 新しく表示されたアイテムがある場合、イベントをトリガーする } アイテムが新しく完全に表示された際のイベントトリガー 上のセッションで取得したPositionでそのままイベントをトリガーすると、現在完全に表示されているアイテム全てに対して何回もイベントをトリガーしてしまいます。 重複した情報を取得したいわけではないので「アイテムが新しく完全に表示された時」にイベントをトリガーする実装してみましょう。 private var oldRange = IntRange.EMPTY private fun checkImpression() { if (recyclerView?.isRecyclerViewFullyVisible() != true) { oldRange = IntRange.EMPTY return } val layoutManager = recyclerView?.layoutManager as? LinearLayoutManager ?: return val newFirst = layoutManager.findFirstCompletelyVisibleItemPosition() val newLast = layoutManager.findLastCompletelyVisibleItemPosition() val newRange = newFirst..newLast for (position in newRange.minus(oldRange)) { // 新しく完全に表示されたアイテムのPositionが届きます。 onImpression(position) } oldRange = newRange } fun onImpression(position: Int) { // ここでインプレッションイベントを送信します。 } newRange には、現在画面上で完全に表示されているアイテムの位置(Position)が格納されます。重複したイベントのトリガーを避けるために、以前にトリガーした oldRange を除外した後、新たなイベントをトリガーします。 このようにして、新しく完全に表示されたアイテムのPositionは onImpression() 関数に渡されます。そして、この関数内でイベント送信のコードを実装することで、インプレッションイベントの送信処理が完了します。 まとめ 上記のコードを利用することで、アダプター側でインプレッションイベントの監視が可能になると思います 本PJでは便利性を向上させるためにインプレッション機能を備えた ImpressionTrackableAdapter を作成し、必要なAdapterが ImpressionTrackableAdapter を継承することにしました。 この ImpressionTrackableAdapter のコードを下記のトグルに添付しておりますので、必要な方はぜひコピー&ペーストしてご利用ください。 :::details 全体コード abstract class ImpressionTrackableAdapter<VH : RecyclerView.ViewHolder> : RecyclerView.Adapter<VH>() { private val globalOnScrollChangedListener = ViewTreeObserver.OnScrollChangedListener { checkImpression() } private var recyclerView: RecyclerView? = null private var oldRange = IntRange.EMPTY abstract fun onImpressionItem(position: Int) override fun onAttachedToRecyclerView(recyclerView: RecyclerView) { super.onAttachedToRecyclerView(recyclerView) this.recyclerView = recyclerView recyclerView.viewTreeObserver.addOnScrollChangedListener(globalOnScrollChangedListener) } override fun onDetachedFromRecyclerView(recyclerView: RecyclerView) { super.onDetachedFromRecyclerView(recyclerView) this.recyclerView = null recyclerView.viewTreeObserver.removeOnScrollChangedListener(globalOnScrollChangedListener) } private fun checkImpression() { if (recyclerView?.isRecyclerViewFullyVisible() != true) { oldRange = IntRange.EMPTY return } val layoutManager = recyclerView?.layoutManager as? LinearLayoutManager ?: return val newFirst = layoutManager.findFirstCompletelyVisibleItemPosition() val newLast = layoutManager.findLastCompletelyVisibleItemPosition() val newRange = newFirst..newLast for (position in newRange.minus(oldRange)) { onImpressionItem(position) } oldRange = newRange } private fun RecyclerView.isRecyclerViewFullyVisible(): Boolean { if (!this.isAttachedToWindow) return false val rect = Rect() val isVisibleRecyclerView = getGlobalVisibleRect(rect) if (!isVisibleRecyclerView) return false return (rect.bottom - rect.top) >= height } } ::: 終わり この記事が、複数のRecyclerViewを扱う際のインプレッションイベントのトリガーに役立つことを願っています。ご質問やフィードバックがありましたら、お気軽にお寄せください。お読みいただき、ありがとうございました!
アバター
Self Introduction I am Morino, leader of the CIO Office Security Team at KINTO Technologies. My hobby is supporting Omiya Ardija, the soccer team from my childhood hometown Omiya (now part of Saitama City), in Saitama Prefecture. Lately I’m into watching Mobile Suit Gundam: The Witch from Mercury, and eagerly look forward to catching it every Sunday at 5 p.m. The other day I went to the Information Security Workshop in Echigo Yuzawa 2022 for the first time, so in this article, I'm going to talk about some of the talks that left an impression on me. What Is the Information Security Workshop in Echigo Yuzawa? The Information Security Workshop in Echigo Yuzawa has a long history, having been held annually since 1998. The 2022 iteration was held on October 7 and 8 (Fri & Sat). On both days, the Yuzawa Community Center was the venue for the daytime session, and the Yuzawa Grand Hotel was the venue for the live broadcast and for the nighttime session itself. The Night Session — The Highlight of Echigo Yuzawa Echigo Yuzawa is famous for its delicious sake and hot springs... I meant, its night session. The battle for tickets is fierce every year and I'd given up on getting one, but then someone gave me theirs at the last minute; I was so lucky! Before COVID-19, the speakers and participants would sit in a circle for the discussions. However this time, they were placed further apart from each other, since the pandemic was still a major concern. Nevertheless, the Q&A exchanges were still very lively. They're Back! “That Security Thing” in Echigo Yuzawa I always look forward to seeing the members of That Security Thing do their podcast in the night session. Nobuhiro Tsuji, Principal Security Researcher at SB Technology Corp. Masafumi Negishi, General Manager at the Security Headquarters Security Information Office at Internet Initiative Japan (IIJ) piyokango, the security-researching parrot Given how cyberattacks can strike out of the blue, piyokango said that he wants to do something like a weather forecast that predicts them, so that people won't be at their mercy quite so much. A general-purpose one might be tricky, but with phishing attacks for example, domains and certificates for the phishing sites are obtained before they're launched. As I listened, I was thinking that if we could detect those activities, then maybe we could to do something like a phishing weather forecast. Mr. Negishi called our attention to an important problem—namely, that we shouldn't judge the urgency of responding to vulnerabilities based solely on their score in the CVSS (Common Vulnerability Scoring System), but should also take into account whether they're actually being used by cyberattacks. As its name suggests, CVSS is a system for quantifying the severity of vulnerabilities in information systems. Reference: IPA Common Vulnerability Scoring System CVSS Overview As someone who collects information on vulnerabilities and evaluates their impact on my own company's systems on a daily basis, I find it extremely handy to be able to decide how urgently they should be addressed based on their calculated scores. However, even ones with a low score can be used by cyberattacks, so caution is required. The Cybersecurity & Infrastructure Security Agency (CISA) in the United States publishes information on vulnerabilities used by cyberattacks and updates it as needed, so I think adding information like that to our criteria for prioritizing them will help us provide systems that are even safer and more secure. What about Mr. Tsuji? Well, I missed your session because I went to another one. I'm very sorry, Mr. Tsuji. Phishing Hunters Sometimes Need to Relax in Hot Springs Too This was a night session event by people who battle with phishing sites on a daily basis. - Self-proclaimed handsome phishing scam hunter Nyan☆Taku ozuma5119 KesagataMe Cyber Samurai Kazumi To prevent as many people as possible from falling victim to phishing scams, they asked us to share some slides they'd made about them on social media and so on. So I've added them to this blog article too. It's extremely hard to tell whether links on social media and in emails are phishing attempts, so don't click on them at all. Instead, go to the target site via your browser's bookmarks or a search engine. How Young People Are Using the Internet, and What Can We Do About It Maiko Shichijo, ICT Usage Environment Awareness Support Office, Cyber Grid Japan, LAC Co., Ltd. I have a child in junior high school, so I listened to this lecture with tremendous interest. Apparently, 30% of children have a smartphone of their own when they're 9, and 90% do when they're 13. These percentages are much higher than I'd imagined, so it was a real eye-opener. I've listed below the things that were news to me about how young people tend to use the Internet, but I was utterly shocked to hear that they use smartphone apps that not only share their location, but even their phone's battery levels. - They don't like wordy chat messages. They use images for long passages of text. (Notepad screenshots.) They use different accounts for different things. About half of all high school students use their real names on social media (because their friends won't be able to find them if they use aliases). They share information and video in real time. As I listened, I was struck by how much we don’t understand about how children are using the Internet, and how dangerous it can be for them. But we mustn't tell them that we don't understand, because they'll stop listening as soon as we do, mentioned the presenter. The message was that we should accept the reality that children use the Internet in ways we don’t understand. After the presentation, I got to discuss children's use of social media, and the takeaway was that they should be allowed to use them under parental supervision, while being shown how to do so safely instead of banning it completely. They could use it behind your back anyway, which will make things even worse. Vulnerability Measures and Information Sharing: What We Learned From Establishing an In-house Bug Bounty System Ikuya Hayashi and Sakura Tsukakoshi, NTT Communications Corporation The idea with a bug bounty system is to pay people rewards for uncovering software vulnerabilities. When someone outside the company submits a software vulnerability report, it's hard to decide whether to trust them or not. So NTT Communications Corporation decided to provide a bug bounty system to mobilize its employees. That's because if you get a bug report from a fellow employee, you can be sure of their credentials. The system really did help to strengthen their security, they said, including the disclosure of a serious vulnerability that allowed common users to have high-level privileges. As a result, voluntary study sessions were held, non-engineers got rewards, and it created the opportunity to discover new talent among employees. I thought it might a good idea for us to establish a bug bounty system, too. In Conclusion Taking a timeout from my daily work to meet and hear talks by people from a variety of backgrounds was a very stimulating experience. I'd like to start using the vulnerability prioritization criteria mentioned above in our company as soon as possible. I think it'd be great to have a bug bounty system, too, although it might be difficult to get one up and running straight away. Besides the hot spring workshop in Echigo Yuzawa, there are other sister ones (so to speak) all over Japan. So, how about trying those out, too? Battlefield Symposium Atami Shirahama Cyber Crime Symposium Cyber Security Symposium in Dogo Kyushu Cyber Security Symposium
アバター
​ はじめに こんにちは! KINTO テクノロジーズ、プロジェクト推進Gの青嶋と申します。 Figmaに新たに追加されたバリアブル機能ですが、とても便利なのに日本語で書かれたチュートリアルが少ないかなという思いから、僭越ながら初心者の方にもご参考になればということで記事を書かせていただきました。 ​ Figmaのバリアブル機能を使ってショッピングカートのモックアップを作ってみよう! ​ ![](/assets/blog/authors/aoshima/figma/image.webp =400x) ショッピングカート 完成図 Figmaのバリアブルの機能を使ってインタラクティブに動作するショッピングカートの機能を作ってみようという内容で2回に分けて説明していきたいと思います。 ​ パート1はバリアブルの説明に始まり、商品数を増やしたり減らしたりできるカウントアップ機能と商品の個数に応じた小計を計算する機能を説明したいと思います。 ​ パート2はカート内の商品数を2つに増やした上で小計の計算、送料無料設定、合計金額の設定、送料無料になったときに文言を変更する設定について書いていきたいと思います。 ​ 【パート1】 バリアブルとは パーツ作成 まずはカウントアップ機能 変数の作り方、割当方 カウントアップ機能の作成 小計設定 ​ 【パート2】 商品を2つに増やしてみる​ 小計の設定 送料無料設定 合計の設定 送料無料の文言に変更 完成 ​ 【パート1】 ​ バリアブルとは ​ バリアブルとは2023年6月にFigmaに追加された新機能の一つで、オプジェクトに割当てることのできる変数の事です。バリアブルとは英語のvariableの事で、日本語では変数と呼ばれます。 変数とは”色々な情報や値を一時的に持っておく「箱」のようなもの”などと説明される事が多いです。 ​ Figmaで用意されているバリアブルの種類は4つ(数字、色、テキスト、ブーリアン値)で、この4種類の情報や値をバリアブルに代入できることになります。しかし種類をまたがることはできないようですので、「数値」の変数を途中で「テキスト」に変更することはできないようです。 ​ パーツ作成 では早速制作に入っていきたいのですが、今回作りたい機能は以下の2つの機能になります。 ​ カウントアップ機能:プラスボタンを押したら商品の個数が増え、マイナスボタンを押したら個数が減る 小計設定:個数に合わせて、合計金額が変わる まずこれらの機能を作るために必要なパーツを作成し、バリアブルの作り方と割当て方をお伝えしたいと思います。 ​ ベースパーツ作成 カウントアップ機能を作るに当たって、ジョッピングカートのベースとしてコーヒーショップをイメージしたデザインを作成し、コーヒー豆がカートに入っている状態を用意しました。 ​ ![](/assets/blog/authors/aoshima/figma/base.png =400x) ベースパーツ 商品が1つカートに入っている状態 ​ ボタンパーツ作成 続いてボタンにマウスオーバーした時にボタンの色が変わるアクションを入れ込みたいので、ボタンのコンポーネントを作成しバリアントを作成します。サンプルではデフォルト状態は薄いグレーでマウスオーバー状態は濃いグレーに設定しました。 ​ ![](/assets/blog/authors/aoshima/figma/button.png =400x) ボタンのバリアントを作成 ​ バリアブルの作り方、割当方 必要なパーツが揃いましたのでここからはバリアブルの作り方と割当て方を説明していきます。 ​ バリアブルとは主に(クリックなどのアクションによって)変化させたいオブジェクトに対して割当てるもの になります。例えばプラスボタンやマイナスボタンを押した時に商品の個数を変化させたいので、個数の数字に対してバリアブルを割当てることになります。 ​ ![](/assets/blog/authors/aoshima/figma/variable.png =400x) 赤枠の個数部分に対してバリアブルを割当てます ​ バリアブルを作成するには右カラムから「ローカルバリアブル」をクリックしウィンドウを開き、ウィンドウ左下にある「バリアブルを作成」ボタンをクリックします。 ​ ![](/assets/blog/authors/aoshima/figma/local_variable.png =400x) ![](/assets/blog/authors/aoshima/figma/all_variable.png =400x) 今回は個数なので種類は「数値」を選択し名前は「個数」にちなんで”Kosu1”としました。そしてすでにカートに商品が1つ入っている状態を想定しているのでMode1欄に入れる数字は1にします。これでバリアブルが1つ作成されました。 ​ 次にこのバリアブルをオブジェクトに割当てていきます。 デザイン上の数字の1を選択し、右カラム内のテキスト情報欄にある八角形のアイコン(三点リーダーの上)をクリックします。 すると作成したバリアブルの一覧リストが出てきますので、そこからKosu1を選択します。 これでバリアブルの割当てが完了しました。 ​ ![](/assets/blog/authors/aoshima/figma/kosu1.png =400x) 割当て可能なバリアブルの一覧がリストで出現 ​ プラスボタンとマイナスボタンに挟まれた「1」にはバリアブル「Kosu1」が割当てられ、値として1が代入されています。この値を2に変えるとオブジェクトの方の数字も2に変わります。 ​ カウントアップ機能の作成 パーツも揃いバリアブルの設定もできたので、次にプラスボタンを押したら商品の個数が増えるという機能を作っていきたいと思います。 そのためにはプラスボタンにアクションを割当てる必要がありますので、プラスボタンを選択し右カラムの最上部にある「プロトタイプ」のテキストをクリックしてモード変更をした後、「インタラクション」の右にある+マークをクリックすることでマウスアクションを割当てることが可能になります。 ![](/assets/blog/authors/aoshima/figma/count_up1.png =400x) バリアブルを選択して式を記入します。 アクションの種類としてはプラスボタンをクリックした時に個数を増やしたいので、デフォルト設定の「クリック」のままで大丈夫です。そして「バリアブルを設定」を選択し、以下の手順で設定します。 クリックした時に変化させたいバリアブルを選択 クリックした時にどうなるかの式を記入 記入を終えると図のようになります。 ![](/assets/blog/authors/aoshima/figma/kosu.png =400x) バリアブルを選択して式を記入します。 ​ この式は、 (プラスボタンをクリックした時に)Kosu1を(=上の段)Kosu1+1する(=下の段) ということを表しており、Kosu1が1の時にプラスボタンをクリックしたら、1+1で2になります。これをプレビューすると、プラスボタンをクリックするたびに個数が1つずつ増えていくことが確認できると思います。 これと同じ様にマイナスボタンにもアクションを割当てていくのですが、マイナスボタンの時は1つ注意することがあります。それはプラスボタンと違い、なんの制限(条件)も付けないと個数がマイナスになってしまうことです。 ​ この様な場合は以下の図の様な「条件付きアクション(if文)」を設定する必要があります。 ![](/assets/blog/authors/aoshima/figma/count_up3.png =400x) この式は、 Kosu1が0でない場合、Kosu1をKosu1から-1した状態にする ということを表しています。例えばKosu1が1の時にマイナスボタンをクリックしたら、1-1で0になります。ですがKosu1が0の時にマイナスボタンをクリックしても何もアクションは起きないので0のままになります。 ​ これをプレビューするとプラスボタンクリックで数が増えていき、マイナスボタンクリックで数が減り且つ0以下にはならないことが確認できると思います。 ​ ![](/assets/blog/authors/aoshima/figma/count_up.gif =400x) カウントアップ機能の動き ​ 以上でカウントアップ機能は完成です。 ​ 小計設定 最後に小計を設定していきたいと思います。パート1では商品が1つしか無いのでその合計金額を小計として扱います。 ​ バリアブル作成と割当て 小計も個数に応じて変化するオブジェクトですので、バリアブルを設定する必要があります。先程と同じ手順でバリアブルを作成し名前はShoukeiとしました。そしてショッピングカートには元々商品が1つ入っている状態を想定していますので、小計は¥100となり代入する値は100となります。 数字へのバリアブル割当ても先程と同じ手順で行います。 ​ ![](/assets/blog/authors/aoshima/figma/sum1.png =400x) 赤枠の金額部分に対してバリアブルを割当てます ボタンへのアクション入力 小計金額を導くには個数の場合と同じくプラスボタンやマイナスボタンを押した分だけ商品の値段を増減させるという方法でも実現可能ですが、multiplication(掛け算)の演算子を使用して個数×値段でも可能です。さらにプラスボタンとマイナスボタン両方に同じ式を使用する事ができるので少しだけ楽ができます。 ​ 記入を終えると図のようになります。 ![](/assets/blog/authors/aoshima/figma/sum2.png =400x) この式は、 ShoukeiをKosu1 x 100の状態にする ということを表していますので、Kosu1が1の場合は小計=1x100となり、¥100となります。 これをプレビューするとプラス(マイナス)ボタンクリックで商品数が増減し、それに伴って小計の値段も変化することが確認できると思います。 ​ ![](/assets/blog/authors/aoshima/figma/sum.gif =400x) 小計の動き ​ 以上で小計設定は完成です。 パート1でお伝えした内容だけでも例えば「いいねボタン」や簡単なカート機能のモックを作ることは可能ですので色々と応用が効くと思います。 ​ パート1はここまでとなります。次回パート2は応用編としてカート内の商品を2つに増やし一定の金額を超えたら送料無料にするといった内容をお伝えしたいと思います。 ​ この内容が参考になれば幸いです。
アバター
こんにちは、KINTO テクノロジーズ (以下 KTC) で DBRE をやっていますあわっち( @_awache ) です。 KTC では、モビリティサービスを提供する基盤として Amazon Web Services (以降 AWS と略します) 上で Amazon Aurora 等の Database を多く運用しており、Database Reliability Engineering (以降 DBRE と略します) を組織として設立し、事業のアジリティとガバナンスを両立するための取り組みを実施しています。 本投稿では KTC はなぜ DBRE を必要としたのか、について記載させていただきたいと思います。 Database Reliability Engineering (DBRE) とは DBRE とは Database Reliability Engineering の略で「Software Engineering と Database 管理の Best Practice を組み合わせたアプローチ」を行っていく役割です。主なものとして SLO/SLI を定義、測定し、それに合わせた開発組織へのアプローチ 自動化、自律化推進による生産性の向上 Backup/Restore などサービス稼働率の向上 Database Security の担保とガバナンスコントロールの実現 他分野のスペシャリストとの分野を超えたコラボレーション などが挙げられます。 これらの役割を一言で言うと「Database に対する専門知識と判断を用いて サービスの信頼性を担保する 」こと、と言えると思います。 DBRE は企業にとって必要なのか? 「DBRE」というキーワードは、ある人にとってはとても魅力的に映るかもしれません。しかし、なぜそれが必要なのかを考えなければ、持続的な DBRE 活動を実現することは困難になります。 なぜなら AWS をはじめとした Public Cloud の爆発的な進展、DevOps/SRE 思想の成熟、AI 技術の進歩など Cloud を活用することによって誰でも一定のレベルで課題を解決できる土壌が揃っており、Database Engineer の専門知識の必要性が小さくなってきているからです。 一方で、過去も現在も実は Database そのものに対する基本的なアプローチはこれまでと変わっていないことも事実です。 環境分離 構成管理 パフォーマンス測定 Backup/Restore Security の担保、など 変化しているのは Database を取り巻く周りの環境である と考えることができます。 こういった時代の変化を判断し、その上で DBRE の必要性がある場合には皆様の会社でも DBRE を検討するといいでしょう。 なぜ KTC に DBRE が必要だったのか ではなぜ KTC が DBRE を実践しているのか、を以下で説明します。 Software Engineer と Database Administrator の関係性の変化 Software Engineer と Database Administrator の役割が明確に分かれていた時代 まだ企業においてオンプレミスの構成がメインであった時代、一般的には Software Engineer と Database Administrator はそれぞれの役割が明確に分かれており、それぞれの専門領域の分野で活動を行なっていました。 例えば下記のような分類がされていることがあったと思います。 Software Engineer 領域 サービスの稼働率を守り、その成長を促進するための活動 ユーザーからのリクエストで必要なデータを抽出する クエリの変更など Database の DDL に対する変更を必要としないチューニング Database Administrator からの要望に対するアプリケーションへの適用 Database Administrator 領域 Database そのものの稼働率を守るための活動 両者の大きな違いはそのリクエストやユーザー要望に対する対応速度と専門性であると言えます。 Software Engineer はサービス提供側に近いところにあるためどちらかというとニーズに合わせて可能な限り素早く応える必要があります。 Database Administrator はサービスというよりも Database そのものに重点を置いているため、多少レスポンス速度は遅くとも専門知識を用いて様々な課題を解決していきます。 図に表すと下記のような形になります。 ![関係性1](/assets/blog/authors/_awache/20231211/関係性1.jpg =500x) このような組織では DBA は組織横断的に課題発見と課題解決を行っていました。 Public Cloud の台頭による DBA の役割の縮小と SWE の領域の拡大 AWS をはじめとする Public Cloud が台頭してきて両者の役割は大きく変化してきました。特に Database そのものの稼働率を守るという点に関しては Public Cloud が担ってくれることから Database Administrator の役割は大きく縮小してきたと言えます。 代わりにこれまで Database Administrator が担ってきた役割のうち Database Administrator と Software Engineer の役割が被っている部分、具体的には 環境分離 構成管理 パフォーマンス測定 Backup/Restore Security や Governance Control などを現場の Software Engineer が自分達で担う必要が出てきました。 その結果どうしても Database にまで注意が行き届かないというケースも存在してしまいます。 ![関係性2](/assets/blog/authors/_awache/20231211/関係性2.jpg =500x) KTC ではこの点に課題を持ち始めました。継続的に事業成長を加速させるためには Software Engineer が事業成長に注力できる状態でなくてはいけません。 Cloud 利活用を主軸とした現在において、Software Engineer が事業活動に注力できる状態にするためには DBRE が Public Cloud との間に Hub となって活動する必要があります。 DBRE に求められる主な役割としては Database を軸としてサービスの稼働率を守る 現場のエンジニアの生産性を高める データベースセキュリティへの対応 内的要因、外的要因から Database を守る を通じて企業の継続的成長を促進することが挙げられます。 ![関係性3](/assets/blog/authors/_awache/20231211/関係性3.jpg =500x) 次の章ではそれぞれの役割について補足します。 Cloud 時代の DBRE の役割 Database を軸としてサービスの稼働率を守る 一般的にサービスが正常な状態は Database が正常に機能していることが前提となります。つまり Database のダウンタイムはそのままサービスのダウンタイムにつながってしまうのです。サービスの稼働率を維持するためには正しく適切に迅速に Database を復旧しなければなりません。 そこで DBRE に必要とされるのは Cloud 上で稼働するデータベースに対するスキルと言えるでしょう。 現場のエンジニアの生産性を高める Database を運用していく中で、標準化を推進することは企業に対して大きな貢献ができるポイントです。例えば企業のガイドラインを提供した Platform の適用、ER 図をはじめとした Database ドキュメントの自動生成、定常的なオペレーションの自動化などが挙げられます。 また、日々進化する Cloud 技術の新機能検証と企業への適用、スロークエリなどのアプリケーションのボトルネック対応のサポート、そしてトラブル対応などを DBRE としてサポートすることも重要な役割となります。 これらは DBRE だけができてもスケールしません。DBRE は Software Engineer に比べて非常に限られたリソースの中で活動を行う必要があります。 大切なポイントは DBRE によってアウトプットされた物事は Software Engineer に還元され、そして誰でも扱える状態になることです。 データベースセキュリティへの対応 Database の重要性は事業の成長と共に高まっていきます。そしてその価値が高まった Database は常に狙われる一つのコンポーネントとなります。 万が一データ流出が発生した場合、財務的な損失だけでなくそれに伴う甚大なレピュテーションリスクを抱えることになってしまいます。 企業を継続的に成長させるためにはこういったリスクから Database を守ることが必要不可欠です。そのためにも DBRE は Database に対するセキュリティを企業単位で適用することが重要となるのです。 ![時間と価値の変化](/assets/blog/authors/_awache/20231211/時間と価値の変化.png =500x) 内的要因、外的要因から Database を守る データはその重要性から外部要因の変化による影響も受けやすい一つの要素です。そのため Database 運用に求められることは年々厳しくなっています。 特に政治・法律・世論の変化など、企業や個人ではコントロールできない要因に適切に対応することは企業に求められるものとなります。例えば個人情報保護法など定期的に改定されており、その度に罰則が厳しくなる傾向にあります。 外部要因による影響を各サービスが独自に適用しているのではコスト的なリスクだけでなく、適用漏れなどによるリスクも発生してしまいます。 DBRE に求められることは外的要因によるリスクを適切に把握し、企業内全ての Database に対して最適なガバナンスコントロールを行うこととなります。 KTC DBRE の目指すべき姿 KTC における DBRE は横断組織です。自分達のアウトプットがビジネスに反映されることによって価値提供されます。横断組織はそこにあるだけでは何に使われるかわからない税金と同じです。ビジネスに対してどのように良い影響を与えることができるか、が重要です。 今の時代、ルールやレビューだけで対応できるほど事業の変化のスピードは遅くありません。DBRE の持っている Database の知識を現場のエンジニアに還元して KTC 全体の Reliability を守ることが重要になります。 そのためにも私たちは Database に対するモノゴトを可能な限り Engineering によって解決しようと試みています。 ルールでがんじがらめにするのではなく、現場のエンジニアと協働して解決する、高いアジリティを保持しつつサービスのガバナンスコントロールを実現することでビジネスへの良い影響を提供していきます。 まとめ KTC DBRE の役割 現場のエンジニアが事業成長に注力できる環境を提供すること KTC DBRE に求められる主な役割としては Database を軸としてサービスの稼働率を守る 現場のエンジニアの生産性を高める データベースセキュリティへの対応 内的要因、外的要因から Database を守る を通じて KTC の継続的成長を促進することが挙げられます。 この役割を軸として、ルールに縛られるのではなく、現場のエンジニアと協力して問題を解決し、高いアジリティを保ちつつサービスのガバナンスコントロールを実現することで、ビジネスへの良い影響を提供していくことが求められています。 僕たちと DBRE ディスカッションしませんか? 僕たちの DBRE はまだまだ始まったばかりです。なぜ DBRE があり、何を実現するためにどんなことをしていくのか、試行錯誤をしています。 お互いのやっていること、やりたいことなどをざっくばらんにお話しできると僕たちも視野が広がります。 僕たちからも多くをインプットしていただけるように取り組んでいることをお話しさせていただきたいと思います。 興味がある方は Twitter DM でも構いませんのでご連絡ください。 We are Hiring DBRE の採用に関する課題は非常に大きいなと思っています。僕たちの活動に興味がある方はぜひ 採用ページ からご登録ください。 カジュアルに話を聞きたい、という方も Welcome です。 そのうち DBRE を実践している企業様とコラボレーションして DBRE Meetup 的なイベントもできたら嬉しいと思っていますのでその際はどうぞよろしくお願いします!
アバター
はじめに こんにちは、KINTO FACTORY のバックエンドエンジニアをしている西田です。 KINTO FACTORY をローンチして早いもので半年が経ったので、サービス運用を開始してからリリースやシステム監視を中心に遭遇した課題や、私たちが学んだことをみなさんと共有したいと思います。 KINTO FACTORY について はじめに KINTO FACTORY のサービス概要を少しだけ。 お乗りのクルマに適合するハードウェア・ソフトウェアといった機能やアイテムをアップデートできるサービスとなります。 これまでのクルマのアップデートはディーラーでの作業が必要であったり、いわゆるメーカーオプションのような注文時にしか選択できないアイテムを KINTO FACTORY ではウェブ上から申込みができます。 また、KINTO 契約車両だけでなくトヨタ/レクサス/GRのクルマが対象となっており、適合するクルマであればアップデートを行うことができます。 今年の夏にローンチし、半年が経過しようとしています。 @ card 運用について リリースは2週間に1回のペースで行い、GitHub Actions をメインに CI/CD を構築してデプロイしています。 サービスの監視は、運用チームといった専用の組織を設けずに開発メンバーが中心となり、当番を回して運用をしています。 ローテーションは PagerDuty のスケジュール機能を利用しています。 また、インシデントの検知は次のようなイメージで構成されています。 アプリケーションログやサービス監視の情報を OpenSearch に連携し、ユーザー影響を判断して PagerDuty に通知が行われます。(画像は対応完了後のものです) その後 Slack にも通知がされることで監視当番が対応を行います。 基本的には担当者が対応しますが、必要に応じて有識者を巻き込んで対応をするようにしています。 また、対応者だけでなくチームとして把握ができるように翌日のデイリースクラムで対応内容を共有しています。 課題と対応 運用を始めてから遭遇した課題と、それをどのように対応したかを共有します。 リリース準備の負担が大きい テンプレート化されていないこともありリリースのたびにデプロイのための準備を1から行っていて、粒度も統一されておらず時間がかかっていました。 中でもリリース手順書周りは Confluence ベースで管理していて、手順の確認や修正が煩雑でした。 ⇒ リリース手順書をコード管理へ移行することで、バージョン管理やテンプレート化、差分確認をできるようにすることで、リリース準備の負担を軽減することができました。 インシデント検知が多発 ローンチ後、数日間のインシデントの件数が↓の通りで アプリケーション側のエラーハンドリング設計が甘かったため、ユーザー影響がないものであってもクリティカル扱いとして検知され、運用開始後は毎日のようにインシデントが発生し監視の負荷が大変なことに... ⇒ 対応するには数が多く一括で対応が難しかったため、緊急度が高くないものは出力を見直すように修正、修正がすぐできない場合は通知されないように除外設定を組み込んで最適化を図りました。 インシデント対応の初動に時間がかかる 対応フローの手順などが整っていなかったためメンバーによって認識の差があり、対処に時間がかかってしまう場面がありました。 PagerDuty の操作に慣れていないこともあり確認済みステータスへの更新が漏れてたりも。。。 ⇒ Slack でワークフローを定義して、コマンド入力でインシデント対応を開始して手順に沿って対応が進められるように環境を整えて改善を図りました。 また、インシデントVSチームという構図になるようにまずは全員が集まって対応をモブ作業として行い、認識を擦り合わせながら対応をすることで、アラートが発火してから対応開始するまでの時間を1/10以下に短縮することができました。 まとめ ローンチ直後は色んな課題が起きてバタバタな日々を過ごしていましたが、継続的に改善を行うことで少しずつ改善されてきました。 あらためてふりかえるとメンバーの経験値や知識による対応の差があったため、事前にどういった運用をしていくのかをきちんと整理しながら運用を開始することで、立ち上がりもスムーズにできたのかなと実感しました。 まだまだ改善の余地はありますが、これからもユーザーにとってより良いサービスを提供できるように運用を継続していきます! さいごに 今回はサービス運用で遭遇した課題とその対応を共有しました。 私たちの経験が何かの参考になれば嬉しいです。 また、KINTO FACTORY では新たな仲間を募集していますので、ご興味があればぜひ下記の求人をチェックしてみてください! @ card @ card
アバター
はじめに 初めまして、KINTOテクノロジーズ株式会社(以降、KTC)でプラットフォームグループのCloud Infrastructure Teamでクラウドインフラエンジニアをしている白井です。 普段はAWSで構築されているシステムのインフラ構築、および設計を行っています。趣味は、卓球とゲームです。最近だとスーパーマリオRPGがリメイクされたのを購入し、思い出に浸りながらプレイしています。 今回は、KTCで構築しているCloudFront FunctionsのDeployのプロセスと運用カイゼンをしたお話について、背景も含めてご紹介します! KTCのCloud Infrastructure Teamについて 本題に入る前に、私たちのチームについて補足させていただきます。 KTC ではインフラの構築を Terraform を使用した IaC で管理しています。 歴史的背景など詳しくは同じチームの 島川さん が Terraform を抽象化し環境構築の工数を削減する取り組みについて という資料を公開していますのでぜひそちらをご確認ください。 現状の課題 KTCでは現在、一部のシステムにおけるリダイレクト処理などでCloudFront Fucntions(以降、CF2)を利用しています。 CloudFrontで利用できるエッジ関数 というタイトルでCF2について、同じチームメンバの井木さんが紹介しているので、ぜひご覧になってください。 さて、KTCではCF2を利用する中で、以下3点が課題として上がってきました。 Application TeamとCloud Infrastructure Teamのコミュニケーションコストが大きい Application TeamにCloudWatch Logsに出力されるログの閲覧権限を付与できていない CloudWatch Logsに出力されるログが失効されない状態で残る この三つの課題を解決します。 課題の深掘り 1. コミュニケーションコストが大きくなっている課題 今までのCF2が適用までのプロセスは以下になります。 今までのDeployまでのプロセス DeployがCloud Infrastructure Teamに依存している影響で、CF2のソースコードに問題があった際に、再度上の図の(2)~(4)をCloud Infrastructure Teamにて実施する必要があります。 このフローの問題点としては、 CF2の更新がCloud Infrastructure Teamに依存していること CF2の更新時に、Cloud Infrastructure Teamも影響範囲を確認し、Application Teamとの調整が必要になること 上記二点により、コミュニケーションコストが大きくなっていました。 2. Application Teamがログを閲覧できない課題 KTCでは、Application Teamに引き渡す権限を強く絞っています。その結果、CF2のログの閲覧権限がなく、見えなくなっていました。この状況では、CF2における問題発生時にApplication Teamは調査することができません。 3. CF2のログが恒久的に残る課題 現状、CloudWatchのロググループを設定せず、CF2を構築していました。 CF2の仕様上、CF2のログが出力された際に自動的にus-east-1リージョンのCloudWatchLogsに /aws/cloudfront/function/${FunctionName} というロググループが作成されます。この状況では、ロググループは失効期間が設定されていないため、残り続けコストが嵩んでしまいます。 解決策 問題と解決策を以下にまとめました。 課題 課題 解決策 1 コミュニケーションコストが大きい Application Teamに追加権限を付与し、任意のタイミングでDeployできるようにする 2 Application Teamがログを閲覧できない Application Teamにログ閲覧権限を追加する 3 CF2のログが恒久的に残る 失効期間付きのロググループを先に作成しておく では、それぞれの解決策について深掘りしていこうと思います。 課題1: コミュニケーションコストが大きい 解決策でも述べたように、方針としてはApplication Teamが任意のタイミングでDeployできるようにすることです。 そこで、Deployするまでのプロセスを一新しようと思いました。 まずは、CF2構築前の構成例とプロセスを一新後の構成例をお見せします。 CF2構築前構成例 最終構成例 CF2のDevelopment StageとLIVE Stageについて、補足します。LIVE Stageは実際にCloudFrontに紐づけられて動作しているCF2となります。それとは別にDevelopment Stageでは主に開発用途として使われ、LIVE Stageで流入してくるリクエストの検証が実施できます。 次に、赤文字で記載されているメンテナンスロールとCICDユーザについて少しだけ触れさせていただきます。 メンテナンスロールとCICDユーザ それぞれ以下の役割です。 メンテナンスロールの役割 AWSマネージメントコンソール上での各種AWSサービスの閲覧・更新をすることです。 KTCにおいて、AWSマネージメントコンソールにログインする際には、環境ごとに用意されているアカウントにSSOログインをします。SSOログインをした先で適切に権限を絞られているメンテナンスロールにスイッチロールすることで必要なAWSサービスの閲覧や更新を手動で行うことができます。 同じアカウント内にはさまざまなプロダクトが存在しているため、誤操作防止のため、閲覧・更新権限についても強めの制限をしています。 CICDユーザロールの役割 Github ActionsなどのCICDツールを使用した各種AWSサービスの更新をすることです。 Applicationのデプロイで使用する権限を設定しています。各プロダクトが使用するAWSリソースによって付与する権限を決めています。例えばLambdaとECSをデプロイするプロダクトにはそれをデプロイできる権限を与え、ECSのみをデプロイするプロダクトにはECSのみをデプロイできる権限を与えています。 既存のメンテナンスロールとCICDユーザはCF2の権限が付与されていなかったので、下記権限を追加しました。 { "Action": [ "cloudfront:UpdateFunction", "cloudfront:TestFunction", "cloudfront:PublishFunction", "cloudfront:ListFunctionTestEvent", "cloudfront:GetFunction", "cloudfront:DescribeFunction", "cloudfront:DeleteFunctionTestEvent", "cloudfront:CreateFunctionTestEvent" ], "Effect": "Allow", "Resource": "arn:aws:cloudfront::{AccountID}:function/${aws:PrincipalTag/environment}-${aws:PrincipalTag/sid}-*", "Sid": "" }, { "Action": [ "cloudfront:ListFunctions" ], "Effect": "Allow", "Resource": "*", "Sid": "" } 余談ですが、CF2にはDevelopment StageでLambdaのようにテストリクエストを投げることができます。その中で*TestEventの権限が必要だったのですが、 公式ドキュメント にはそのアクションの記載がされておらず、CloudTrailを頼りに必要な権限を追加していきました。公式ドキュメントが全てではないことに気づくよい例だなと思いました。 次にCloud Infrastructure TeamとApplication Teamの分担について述べていきます。 Cloud Infrastructure TeamとApplicaiton Teamの役割分担 実施作業 Cloud Infrastructure Team Applicaiton Team CF2の権限付与 ○ - サンプルアプリのCF2の作成とCloudFrontへの紐付け ○ - CF2の開発、LIVE Stageへのpublish - ○ CF2の運用・監視 - ○ では、実際にLIVE StageへのDeploy(Publish)までのプロセスを見ていきましょう。 Deployプロセス 1. Application TeamがCloud Infrastructure Teamへ構築を依頼 以下のテンプレートを元に、Jiraベースでチケットを発行していただきます。 CF2の命名: hogehoge e.g.) redirect-cf2 構築する環境一覧: xxx 関連づけるCloudfFrontのARN: arn:aws:cloudfront::{AccoutID}:distribution/{DistributionID} e.g.) arn:aws:cloudfront::111111111111:distribution/EXXXXXXXXXXXXX 関連づけるcache behavior ビューワーリクエスト ビューワーレスポンス hogehoge ○ - 2. Cloud Infrastructure Teamが構築 Cloud Infrastructure Teamで作成したサンプルアプリ(リクエストスルー)のCF2をCloudFrontのビヘイビアに紐付け Application Teamに開発とDeployのための権限をメンテナンスロールとCICDユーザに付与 function handler(event) { var request = event.request; return request; } Cloud Infrastructure Teamが必要なリソースを更新・作成。赤枠が対象。 3. Application TeamがCF2のコードをDevelopment Stageにpublishする。 Development Stageへのソースコード更新方法は下記の通りです。 メンテナンスロールを用いた、AWS マネージドコンソール上からの手動実行 CICDユーザのクレデンシャルを用いた、Github ActionsなどのCI/CDツールを使用した適用 AWSマネージドコンソール上またはCI/CDツールでテストの実施が可能です。 開発とテスト 4. Application TeamがCF2のコードをLIVE Stageにpublishする。 LIVE StageへのpublishもDevelopment Stage への適用と同様にAWS マネージドコンソール、もしくはGithub ActionsなどのCI/CDツールから実行できます。 最終構成 課題2: Application Teamがログを閲覧できない。 ロググループへの閲覧権限を付与します。 { "Action": [ "logs:StartQuery", "logs:GetLogGroupFields", "logs:GetLogEvents" ], "Effect": "Allow", "Resource": "arn:aws:logs:us-east-1:{AccountID}:log-group:/aws/cloudfront/function/${aws:PrincipalTag/environment}-${aws:PrincipalTag/sid}-*-cloudfront-function:log-stream:*", "Sid": "" } メンテナンスロールにおいて、上記のようにロググループの閲覧権限とログのインサイトの閲覧権限を付与しているため、ログが見えるようになりました。 その結果として、問題発生時にApplication Teamが主導で問題発生に取り組めるようになれたと思います。 課題3: CF2のログが失効されない状態で残る。 CF2構築時に併せてCloudWatchLogグループを作成するようにしました。 これは、CF2が作られる過程で参照するモジュールの中に記載することで実現しました。 resource "aws_cloudwatch_log_group" "this" { name = "/aws/cloudfront/function/${local.function_name}" retention_in_days = var.cwlogs_retention_in_days == null ? var.env.log_retention_in_days : var.cwlogs_retention_in_days } まとめと最後に 今回、CF2に対して3つの改善の取り組みを行いました。箇条書きでまとめさせていただきます。 課題1: コミュニケーションコストが大きい 解決策: Application Teamが自分たちでDeploy可能にできる様に権限とプロセスを整理 効果: Application Team が任意のタイミングで実行可能となり必要なコミュニケーションを適切にできる様になった 課題2: Application Teamがログを閲覧できない 解決策: Application Teamにログの閲覧権限を付与 効果: 問題発生時でも自分たちでログを確認し、対応することが可能になった 課題3: CF2のログが恒久的に残る 解決策: 失効期間付き転送先のロググループを先に作成 効果: ログの有効期限を決めたことで、コストの適正化に寄与できた 以上です。ここまで読んでいただき、ありがとうございました!
アバター
3行で概要 スクラムマスターをやっています 会社で利用しているのがJira含めたアトラシアン製品でした Jiraの便利な仕組みをいくつか紹介 はじめに みなさんこんにちは。KTCの小山です。iOS(Swift)エンジニアをしております。最近はスクラムマスターもかじっております。 今回はJiraのプラクティスを紹介したいと思います。 スクラムとJira? スクラムガイド2020 に記載の通り、スクラムマスターには「⾃⼰管理型で機能横断型のチームメンバーをコーチする。」という大きな任務があります。これを満たすために、さまざまな仕掛けを作ることになるかと思いますが、この時にできるだけ人(スクラムマスター)が介さずとも、回る仕組みが重要だと感じております。 ・・・。 ・・・いえ、決してただ楽したいなどとは。 ・・・少し、ほんの僅かばかりしか思っておりませんとも。(みんな楽して仕事したいよね) そもそもJira? Jiraはアトラシアン社の製品で、主にプロジェクト管理に用いられるSaaSになります。詳しくは 公式サイト をご覧ください。タスク管理の機能が非常に多機能で、同じくアトラシアン社が出しているドキュメント管理ツールのConfluenceと親和性が高いです。 なお弊社ではJiraとConfluenceを導入しており、全社員が利用できるようになっています。全社的に採用しているところって多いのかな? ようやく本題 フィールドの作成・必須化 「担当者が課題を詳細に書いてくれない!」 「期限は後で追加しようと思ってたけど忘れてた・・」 こんな声が聞こえてきます。 Jiraを使ったプロジェクト管理では1つ1つの課題にどれだけ情報が残るかで、自分や周りの担当者の素早い理解や、何度も同じ話をすることの防止に繋がります。 Jiraではテンプレートで用意されているフィールドの他に、任意のフィールドを追加してカスタマイズすることができ、さらに必須化も自由に設定できます。これを利用して上記の意見を仕組み化して解決することができると思います。 「担当者が課題を詳細に書いてくれない!」 詳細を書けるようにフィールドを細かく追加してみましょう 「期限は後で追加しようと思ってたけど忘れてた・・」 期限フィールドを必須化しましょう 活用事例 私たちのチームでは「iOS」「Android」「バックエンド」等、どのチームが担当するかを明確にするため、「ラベル」の入力を必須化しています。適当なラベルをつけただけでも必須フィールドとしては満たせてしまいますが、作成中の担当者がつけ忘れてしまう事象の回避には十分でしょう。 「ラベル」の項目を必須化しています 設定方法は「必須」のチェックボックスをポチッとするだけ。とても簡単ですね。ただし、どれもこれも必須にしてしまうと課題の作成が億劫になってしまう懸念もありますので、用法要領はチームで相談の上ご設定くださいね。 自動化 「課題作成するたびに毎回同じ値を入れるのが面倒くさい」 「定期的に課題が正しく運用されているか確認したい」 続いては、こんなフィードバックに対応してみます。 Jiraには自動化という機能があり、Jira使いたての頃は何がなんだか分からなくて避けていました。しかし使ってみると本当に便利。Jiraでできることは大抵これで解決することができます。順番に設定方法を見てみましょう。 「課題作成するたびに毎回同じ値を入れるのが面倒くさい」 トリガーを「課題作成時」にしたアクションを作成してみましょう。 私たちのプロジェクトでは「作成時」に「関連した課題があるかどうか」を判断して自動的にフラグをつけるようにしています。参考にこちらの設定内容を紹介します。 ルールのトリガーには「課題の作成時」を設定 リンクされた課題に「ストーリー」「バグ」があるかを判定 Jiraではただリンクされている課題があるかどうかだけでなく、そのリンクされている課題が何の種類かまで絞り込むことができます。これらを使ってチームのやり方に沿った課題運用をサポートしています。 「定期的に課題が正しく運用されているか確認したい」 こちらはトリガーを「毎日0時」や「毎週月曜日の13時」などに設定してみるのはいかがでしょうか。 私たちのプロジェクトでは、期限付きの課題を忘れないように、定期的に確認できる仕組みを作っています。こちらをご紹介します。 ルールのトリガーに「平日の朝9時」を設定 期限が切れていることをチェック Slack Webhookを使ってメッセージを送信 JiraではSlackへメッセージを飛ばすこともできるため、重要なメッセージは慣れ親しんだSlackに任せて管理することができます。Jira上のコメントだけでは気づけなかった!なんてことを回避することも可能です。 JQL? 自動化の設定の中でちょくちょく出没する「JQL」という単語、聞き馴染みがないですよね。Jiraをタスク管理として使っているだけであれば利用することがないかもしれませんが、管理の観点ではこのJQLのおかげで細やかな設定が可能になっています。 「JQL」とは「Jira Query Language」の略で、Jiraで特定の課題を検索するための独自の言語になります。私自身、Webで必要な情報を検索するのが難しかったため、ここでは自動化で利用しているJQLを紹介したいと思います。 プロジェクトと課題ステータスを絞り込む 定期的な実行ではデフォルトですべてのJiraプロジェクトが適用範囲になってしまうため、何も設定しないまま自動化を設定してしまうと、他のチームの課題をうっかり操作してしまった!なんてこともありえます。 私たちのプロジェクトではスケジュールで実行する自動化については以下の条件を設定しています。 プロジェクトが担当プロジェクトであること ステータスが「Done」もしくは「DROP」であること project = PP20 AND status not in (Done, "DROP") ※PP20の部分にはプロジェクトに設定されたキーを入力 Slackに送信するメッセージに課題の中身を挿入 自動化でも紹介した通り、Slackに連携することは可能ですが、課題の情報がなければSlackで呼びかけられた担当者が困惑してしまいます。 私たちのプロジェクトでは以下の設定でSlackメッセージを作成しています。 *期限の過ぎているチケットがあります。至急内容を確認してください。* @channel > 【<https://hogehoge.atlassian.net/browse/{{issue.key}}|{{issue.key}}>】{{issue.summary}} このようなメッセージに変化します。 実際のSlackメッセージ 終わりに 以上、Jiraの便利設定のご紹介でした。Jiraを使っている方、これから管理ツールをJiraにしようかと選定している方の参考に少しでもなれば幸いです。アジャイルの開発がもっとやりやすく進んでいくために、これからもJiraの便利機能は使い倒していきたいと思います。
アバター
こんにちは、Ashi Singh です 私はグローバル開発グループの一員で、KINTOテクノロジーズには2022年2 月に入社しました。私はアプリケーション開発担当です。現在は、グローバル開発グループが開発するバックオフィスシステムのエンジニアをしています。 概要 私のチームは、他のグローバルチームが使用する基盤パッケージとしてのマイクロサービスの開発と保守を担当しています。認証・認可は、この基盤パッケージに含めるべき主な機能です。認証ソリューションは、クラウド技術にあまり依存しないことが求められていますので、Keycloakを候補として検討することにしました。 Keycloakとは? Keycloakは、アプリケーションとセキュリティサービスに認証機能を最小限の工数で付与できるIDアクセス管理(IAM)ソリューションで、オープンソースとして提供されています。Keycloakにより、ユーザーの認証連携、厳格な認証、ユーザー管理などが可能となります。Keycloakは、OpenID Connect、OAuth 2.0、SAML 2.0 など、主に3種類のプロトコルをサポートしています。シングルサインオンとシングルサインアウトの両方を問題なく実行できます。高速かつ柔軟な動作が可能で、ユーザーアカウントをシームレスに管理でき、データもセッションも保存できます。 Keycloak公式ページ 将来的に、他のクラウドアーキテクチャへの適用も考えられるため、Keycloakはその点において有力な選択肢だと言えます。 課題 Keycloakについての検討を始めるにあたっての最初のステップは、ローカル環境での試行です。 私たちのチームでは、WindowsとMacOSを使用している開発者が同数います。(KINTOテクノロジーズでは、自分が使い慣れたOSを使用できます!) 初期セットアップにおいては、Windowsだけでなく、Intel を搭載したMacOSでも、KeycloakのDockerイメージに問題はありませんでした。ただし、最近のMacOS(主にM1搭載機種)では、スタートアップの際にどうしてもエラーが発生してしまいます。 エラー発生の際に使用していた設定は以下のとおりです。 Keycloak Dockerイメージバージョン:jboss/keycloak:16.1.1 (当プロジェクトで使用されていた最新イメージ。設定も簡単。) 私のローカルPCのバージョン:MacBook Air (M1, 2020) Apple M1 [MacOS Monterey] ソリューション M1搭載機種を使用していた際、このタスクを担当することになりました。調査したところ、Keycloakで使用しているJBossイメージはM1と互換性がないことが判明しました。 解決策は、Mac M1のDockerイメージの変更です。Mac M1 では、通常の jboss/keycloak:16.1.1 イメージの代わりに wizzn/keycloak:14 を KeycloakのDockerイメージとして使用しています。これを機能させるには、設定ファイルの変更も必要です。 変更内容 docker-compose.yml ローカルで Keycloakを実行するための最初のステップとして、Dockerイメージを wizzn/keycloak:14. に変更します。 ボリュームセクションの順序を以下のように変更: volumes: - ./custom-scripts/:/opt/jboss/startup-scripts/ - ./import/backoffice-realm.json:/tmp/backoffice-realm.json.orig #move to after startup-scripts - ./keycloak/themes/your-default-theme:/opt/jboss/keycloak/themes/your-default-theme - ./keycloak/configuration/mysql/change-database.cli:/opt/jboss/tools/cli/databases/mysql/change-database.cli #move to the last このボリュームセクションに追加する必要のある他のすべてのスクリプトは、 ./import/backoffice-realm.json と ./keycloak/configuration/mysql/change-database.cli の間に追加できます。 change-database.cli 次に、データベース (change-database.cli) ファイルにすでに定義されているデータベース設定を削除します。 このファイルでは、緑色で示した変更を実施。 Dockerfile内の変更点 Dockerfileでは、Dockerイメージを wizzn/keycloak:14 へ変更。 Keycloakを実行 Docker compose up-dを使用してKeycloakを実行します。 Keycloak:長所と短所 この最初の不具合を切り抜けると、WindowsとMacOSの両方でKeycloakを効率的に実行できるようになりました。 ユーザー管理とロール管理はインストール後すぐに動作が可能で、MFA認証、セッション管理などのKeycloakの機能がまちがいなく動作することが確認できました。こういった機能の開発は、自身で行うよりもKeycloakを活用することで工数短縮につながります。Facebook、twitterなどのさまざまなソーシャルネットワークをリンクさせるオプションもあり、必要に応じてソーシャルログインを簡単に実装できます。 ただし、対処しておくべきポイントがいくつかあります。Keycloakはカスタマイズが非常に難しく、変更に時間がかかりがちです。Keycloakが依然としてオープンソースプロジェクトであるがゆえですが、ロードマップについての開発側による保証は何もありません。また、バグ修正などは GitHub issues を通して処理されますが、明確な対応期限が設定されていません。 総じて、ソリューションには常にマイナス面が存在するものです。それでも、管理のしやすさや運用性の高さが他の要件を上回っている点からすると、Keycloakは有力なソリューションとして今後活用していけるものだと考えています。
アバター
Spring Bootを2系から3系へバージョンアップしました。 自己紹介 こんにちは。プラットフォーム開発部/共通サービス開発グループ[^1][^2][^3][^4]/決済プラットフォームチームの竹花です。 今回は、決済プラットフォームのAPI・バッチに使用しているSpring Bootのバージョンアップを行った話について書きたいと思います。 解決したい課題・実現したいこと 当時使用していたSpring Bootが2系であったため、サポート期間等を鑑み3系にバージョンアップしたい。 伴って使用しているライブラリのバージョンアップも実施 ライブラリ 移行前(2系) 移行後(3系) Java 17 変更なし MySQL 5.7 8.0 Spring Boot 2.5.12 3.1.0 Spring Boot Security 2.5.12 3.1.0 Spring Boot Data JPA 2.5.12 3.1.0 hibernate Types 2.21.1 3.5.0 MyBatis Spring Boot 2.2.0 3.0.2 Spring Batch 4.3 5.0 Spring Boot Batch 2.5.2 3.0.11 Spring Boot Cloud AWS 2.4.4 3.0.1 試行錯誤した内容・工夫したこと 適用方法 まずは公式のmigrationガイドを参考に、既存コードのまま影響が少ないライブラリのupdate、deprecated解消等を実施。 その後3.1.0に上げて、ひたすら地道に修正 → build、およびテスト → 調整を繰り返しました。 Spring Boot 3.1 Release Notes Spring Boot 3.0 Migration Guide Spring Batch 5.0 Migration Guide javax → Jakarta 多くのファイルに影響を与える javax → Jakarta パッケージへの変更がありました。 パッケージルート以降の名称は変わりなかったので、機械的に置換しました。 DBアクセス周り MySQL-Connector-Java 移行されたとのことで、mysql-connector-jへ変更しました。 Maven Repository MySQLDialect org.hibernate.dialect.MySQLDialect にすることでMySQLのversion違いを吸収してくれるようになりました。 Hibernate-Types バージョンアップに伴い、JPA Entityで使用していたJson型の設定方法が変わりました。 ID generateをIDENTITYに変更 Spring DATA JPAで自動採番の方法が変わったようで、 AUTOのままでは XXX_seq というテーブルを必要とするようになりました。 我々のシステムではMySQLのAuto Incrementを使っていたためJPAで採番機能を利用しないよう対応しました。 Spring Batch Metaテーブルの変更 Spring Batchの管理テーブルの構造が変更されました。 Migration Guidを参考に、 /org/springframework/batch/core/migration/5.0/migration-mysql.sql を使って既存テーブルを更新しました。 ですが、ALTER TABLEを実行しただけでは既存データにより動作時にエラーとなってしまったため、今後の運用に影響しないことを確認してデータを初期化することにしました。 Caused by: java.lang.RuntimeException: java.lang.ClassNotFoundException: LONG at org.springframework.batch.core.repository.dao.JdbcJobExecutionDao$2.processRow(JdbcJobExecutionDao.java:468) ... (BATCH_JOB_EXECUTION_PARAMSのPARAMETER_TYPEに'LONG'という値が入っていました) データの初期化は以下のSQLで行いました。 TRUNCATE TABLE BATCH_STEP_EXECUTION_CONTEXT; TRUNCATE TABLE BATCH_STEP_EXECUTION_SEQ; TRUNCATE TABLE BATCH_JOB_SEQ; TRUNCATE TABLE BATCH_JOB_EXECUTION_SEQ; TRUNCATE TABLE BATCH_JOB_EXECUTION_PARAMS; TRUNCATE TABLE BATCH_JOB_EXECUTION_CONTEXT; SET foreign_key_checks = 0; TRUNCATE TABLE BATCH_JOB_EXECUTION; TRUNCATE TABLE BATCH_JOB_INSTANCE; TRUNCATE TABLE BATCH_STEP_EXECUTION; SET foreign_key_checks = 1; INSERT INTO BATCH_STEP_EXECUTION_SEQ VALUES(0, '0'); INSERT INTO BATCH_JOB_EXECUTION_SEQ VALUES(0, '0'); INSERT INTO BATCH_JOB_SEQ values(0, '0'); BasicBatchConfigurerが使えなくなった DefaultBatchConfiguration を使う方法に変更しました。 StepBuilderFactory、JobBuilderFactoryが非推奨となった new StepBuilder() でJobRepositoryとTransactionManagerを渡すように変更しました。 ItemWriterの引数の型が変わった Listだったものが org.springframework.batch.item.Chunk に変わったので、write処理を修正しました。 修正前 修正後 ItemWriter<Dto> write() { return items -> { ... items.stream() .flatMap(dto -> dto.getDatas().stream()) .forEach(repository::update); ... ItemWriter<Dto> write() { return items -> { ... items.getItems().stream() .flatMap(dto -> dto.getDatas().stream()) .forEach(repository::update); ... @EnableBatchProcessingの挙動が変わった 動作確認時に、chunkモデルのバッチでprocess処理がスキップ?されるような現象が発生しました。 @EnableBatchProcessing の 挙動が変わった ことによる影響で、こちらのアノーテーションを外すことで解消しました。 Spring Cloud AWS ライブラリの変更 本システムではAWSサービスを多く利用しており、連携にSpring Cloud AWSを利用していました。 バージョンアップに伴い、 io.awspring.cloud:spring-cloud-starter-aws を io.awspring.cloud:spring-cloud-aws-starter に変更し(紛らわしい...)、 com.amazonaws:aws-java-sdk を software.amazon.awssdk へ置き換え、動作するよう修正を行いました。 Spring Cloud AWS SES SESに関して AmazonSimpleEmailService が使えなくなったため、JavaMailSenderを使う実装へ変更しました。 使用するJavaMailSenderは、SESのAuto Configurationで構築されたものをDIして利用します。 SQS SQSへの送信等、リクエスト用のオブジェクトをビルダーパターンで構築する形になったので、これに合わせて修正しました。 また、SQSListenerで使用していた@NotificationMessageが無くなったたため、SqsListenerConfigurerを作成し、MessageConverterを用意することで対応しました。 @Bean public SqsListenerConfigurer configurer(ObjectMapper objectMapper) { return registrar -> registrar.manageMessageConverters( list -> list.addAll( 0, List.of( new SQSEventModelMessageConverter( objectMapper, ReciveEventModel.class), ... } @RequiredArgsConstructor private static class SQSEventModelMessageConverter implements MessageConverter { private static final String SQS_EVENT_FILED_MESSAGE = "Message"; private final ObjectMapper objectMapper; private final Class<?> modelClass; @Override public Object fromMessage(Message<?> message, Class<?> targetClass) { if (modelClass != targetClass) { return null; } try { val payload = objectMapper .readTree(message.getPayload().toString()) .get(SQS_EVENT_FILED_MESSAGE) .asText(); return objectMapper.readValue(payload, targetClass); } catch (IOException ex) { throw new MessageConversionException( message, " Could not read JSON: " + ex.getMessage(), ex); } ... } S3 S3へのアップロードについて、 TransferManager を S3TransferManager に変更、署名付きURL発行の実装も修正が必要でした。 SNS SNS送信に関して DefaultTopicArnResolver のままではsns:CreateTopic権限が必須になっていました。 TopicsListingTopicArnResolverを使うようにして、CreateTopic権限不要で動作するよう対応しました。 @ConditionalOnProperty("spring.cloud.aws.sns.enabled") @Configuration public class SNSConfig { @Bean public TopicArnResolver topicArnResolver(SnsClient snsClient) { return new TopicsListingTopicArnResolver(snsClient); } } API周り WebSecurityConfigurerAdapter参照不可 リンクを参考にSecurityFilterChainを使用する方法に変更しました。 spring-security URLパスの厳格化 Trailing Slash(末尾スラッシュ付き)パスが厳格に区別されるようになりました。 本システムは内部の別システムとの連携があったため @RequestMapping にパスを追加し、 対向システムとの調整後追加したパスを除去する流れで対応しました。 追加前 追加後 ... @RequestMapping( method = RequestMethod.GET, value = {"/api/payments/{id}"}, ... ... @RequestMapping( method = RequestMethod.GET, value = {"/api/payments/{id}", "/api/payments/{id}/"}, ... プロパティ名の変更(application.yml) jpa、redis、spring-cloud-aws系などのプロパティ名が変更されました。 こちらは公式情報を参考に適宜調整しました。 デプロイ ECSデプロイで404が発生 なんとかECSへデプロイできるところまで進み、アプリケーションログで起動が確認できたのですが、APIにアクセスすると404に。 確認したところ、ECSのデプロイでHealth Checkが失敗していました。 弊社クラウドプラットフォームエンジニアの方々にも協力を頂き、テレメトリデータ収集に使用していた aws-opentelemetry-agent のversionが古かったことを突き止めました。 1.23.0以降のバージョンのjarに変更することで、正常にデプロイされAPIの疎通が確認できるようになりました。 AWSが提供するOpenTelemetryJava 結果・プラスアルファの知見や応用案、次へのトライなど 様々な要件に対応するためSpring Bootの一般的な実装パターンとなっていない箇所もあるなど、 migration guideだけですんなりと移行できるような構成でなかったところもありますが、 Try & Errorの繰り返しでなんとかリリースすることができました。 地道な積み重ねの作業・レビューに付き合って頂いたチームの皆さんに感謝しています。 今後は下記のような残課題を引き続き対応しながら、Spring Boot3で強化された機能等も活用していきたいと思います。 Swagger-UI こちらはバージョンアップを後回しにしました。 使用していたspring-foxがまだSpring boot3系に対応していないため、springdoc-openapiに変更することも検討しています。 Spring Batch + MySQL 8.0 + DbUnit 一部条件に該当するテストがエラーになってしまいました。 Spring BatchのTransaction管理(metaテーブル操作)が関連していそうなのですが、修正方法について調査中です。 本記事の事例を通して学んだことまとめ Spring Bootのバージョンアップはmigration guideを参考にしながら、地道にbuild・テスト繰り返すことで実施できた。 今回のバージョンアップは影響範囲が大きかったが、テストを用意していたことで修正すべきところが明確になり、効率よく対応できた。 @EnableBatchProcessingの変更のように動かしてみて問題が見つかるケースもあったため、動作確認も必須。 JavaEE関連がJakartaに変わったことで、Spring Bootだけでなく他のライブラリの更新も必要だった。 Security関連がより強固になった(Trailing Slashの厳格化やIgnore PathでもAuthFilterを通るようになった等)。 依存ライブラリ別で更新内容に差があり、特にSpring Cloud AWSの変更は大きく感じた。 もう少しこまめにライブラリ別のバージョンアップを実施していれば、修正範囲を小さくできたかもしれない。 本記事をお読みいただきありがとうございました。 同様にバージョンアップを検討されている方の参考になれば幸いです。 [^1]: 共通サービス開発グループメンバーによる投稿 1 [ グローバル展開も視野に入れた決済プラットフォームにドメイン駆動設計(DDD)を取り入れた ] [^2]: 共通サービス開発グループメンバーによる投稿 2 [ 入社 1 年未満メンバーだけのチームによる新システム開発をリモートモブプログラミングで成功させた話 ] [^3]: 共通サービス開発グループメンバーによる投稿 3 [ JIRA と GitHub Actions を活用した複数環境へのデプロイトレーサビリティ向上の取り組み ] [^4]: 共通サービス開発グループメンバーによる投稿 4 [ VSCode Dev Container を使った開発環境構築 ]
アバター
自己紹介 ご覧いただきありがとうございます! KINTOテクノロジーズ(以下、KTC)のプロジェクト開発部でエンジニアをしている三上裕太郎です 今年の9月入社で普段はフロントエンドエンジニアとして「KINTO FACTORY」の開発業務を行っています 今回、「アジャイル」をテーマに自分のこれまでの経験とKTCに入社してからの取り組みを執筆させていただきます 話すこと タイトルにもある通り、私のチームのアジャイル開発において、バーンダウンチャートが 右肩上がり している(縁起イイネ👍 という状態に対して、正しく進捗管理を機能するために何を取り組んできたかをお話しします 本編 進捗管理のあるべき姿 バーンダウンチャートは残りの作業量が減少していく様子が一目で分かるようになっており、 ステークホルダーへの進捗報告 開発者にとっての視覚的なモチベーション維持 タスクのストッパー早期発見 チームの協力と連携を促進 といった効果やメリットが期待できます 現状の課題定義 上記を踏まえて、私のチームの1スプリントのバーンダウンチャートを取り上げ、感じる課題をまとめてみます 改善前のバーンダウンチャート 作業は進んでいくので当然最終的にグラフは下がっていくのですが 不定期にあるグラフの上昇や終了時点の理想線との開きの大きさが目につきます 結論 ステークホルダーへの進捗報告 意図したグラフの上昇なのかどうかをチームで把握できていないため信用性が低い報告になってしまう 開発者にとっての視覚的なモチベーション維持 グラフに降下傾向がないため、成功体験が得られずモチベーション維持が難しい タスクのストッパー早期発見 デイリーで進捗報告をしているので、タスクの進捗が遅れていることはチームで把握できているが、グラフからはタスクのストッパーが見えずタスクの進捗が滞っていることが分かりづらい チームの協力と連携を促進 日頃のコミュニケーションは十分に取れているが、チャートを通じて協力や連携をとったケースは少ない カイゼンのゴール ゴールしたら終わりではないですが、分かりやすさをとってチームとしてあるべき姿をゴールと表現しました この記事におけるゴールの定義は以下とします チャートを通じてタスクの進捗を把握、コントロールができていること グラフが上昇することを許容しつつ、上がった事由がチームで認識できていること 開発者各々がチャートを通じて達成感を得られること チーム全体の協力と連携が促進できていること カイゼン開始後 今も現在進行形でカイゼン中ですが現時点でチャートは改善傾向にあります 最新のバーンダウンチャート やったこと1 ”意識” カイゼン ” とりあえずスプリントに積んでおこう ” を止める 具体的なアクションは一つでしたが大きく効果があったと思います この意識をチーム全体が持つようになったことでスプリント終了時点での理想線との開きを小さくすることに成功しています 加えてベロシティの精度が上がり、見積りの精度向上も期待できるようになりました やったこと2 ”プランニング” カイゼン 「次スプリントに積みたいチケット置き場」を設ける スプリント中に追加で積まれるチケットの内訳は プランニング時の積み忘れ スプリント中に追加されたチケット 大きく分けるとこの2つでした スプリント中に追加されるチケットには要因がいくつかあり短期的に改善するのは難しかったため、まずはスプリントへの積み忘れを予防するためのアクションを取りました バックログ上に「次スプリントに積みたいタスク置き場チケット」を作成し、以上に並べたタスク(画像赤枠)についてプランニングで議論するようにしたところ以下の効果を実感しています 積み忘れの防止 プランニングまでにタスクをバックログ上部に動かしておくというアクション追加されたことで積み忘れがなった タスク理解度の向上 1チケットに掛けるブレイクダウンの時間が増えたことでメンバー各々のタスク理解度が上がりました 適正な数のタスクをスプリントに積む 意識カイゼンにもつながりますがとりあえず積むのではなく、積むタスクを選別する機会を得られたので、適正な数のタスクをスプリントに積むことができるようになりました やったこと3 ”カイゼン会議” レトロスペクティブとは別に時間を設けてメンバー同士で日頃のスクラムに対する課題や改善点を話し合う時間を取りました 短〜長期的に取り組んでいく課題について話し、ネクストアクションを決めることで、チームの意識が高まりました 結果 ステークホルダーへの進捗報告 意図したグラフの上昇なのかどうかをチームで把握できていないため信用性が低い報告になってしまう グラフの精度・信用性が向上したことで正確な進捗を報告できるようになった 開発者にとっての視覚的なモチベーション維持 グラフに降下傾向がないため、成功体験が得られずモチベーション維持が難しい 明確に降下傾向があり、成功体験が得られるようになった スプリント中に追加されるチケットへのアクションは今後の課題 タスクのストッパー早期発見 デイリーで進捗報告をしているので、タスクの進捗が遅れていることはチームで把握できているが、グラフからはタスクのストッパーが見えずタスクの進捗が滞っていることが分かりづらい 引き続きデイリーで進捗報告を続けるグラフからストッパーを見えるようにする準備は整った印象 チームの協力と連携を促進 日頃のコミュニケーションは十分に取れているが、チャートを通じて協力や連携をとったケースは少ない チャートを通じて協力や連携をとったケースはまだないが、誰がどのタスクを進行中なのかが以前より把握できるようになったので協力や連携が取れる体制づくりはできたと感じている 最後に ご覧いただきありがとうございました 今回はチャートから見えてくるスクラムカイゼンについてお話ししました チームでスクラムを継続していく上でカイゼンを繰り返すのはプロダクトだけでなく、チームやプロセスにも必要だと改めて実感しました レポートやチャートを利用した客観的な改善はデータに基づいていて課題が発見されやすく、改善が目に見えるのでモチベーション維持にもつながります 皆さんのチーム”カイゼン”の一助になれば幸いです 最後に私の所属するKINTO FACTORYでは一緒に働く仲間を募集しています! ご興味があればぜひ下記の求人をチェックしてみてください @ card @ card
アバター
1. Introduction Hello. Torii here, from the team of Common Services Development Group[^1] [^2] [^3] that develops payment platforms used by multiple services. In the previous article ^4 , I shared an example of how Visual Studio Code (from here on, VS Code)'s Dev Container was used to create a comfortable development environment. While Dev Container is very useful, it uses resources on the local machine, so the performance is dependent on the machine's specs. On Mac in particular, interactions between file systems can cause delays. In this article, I will talk about GitHub Codespaces, an upgrade of the Dev Container development environment creation method that our team uses. I will explain how to build a cloud-based development environment easily and efficiently with GitHub Codespaces. In addition, I will show a sample configuration file used in actual development. The sample configuration file is taken from code that is used in actual development and contains methods of using MySQL and LocalStack with Dev Container, methods for local environments to coexist with the Dev Container development environment, and more. 2. Overview of GitHub Codespaces GitHub Codespaces is a service that provides a complete development environment in the cloud and supports VS Code, VS Code Web, IntelliJ, JupyterLab, and other major development tools. These tools can be used on all major platforms— Windows, Mac, and Linux. This allows developers to work in an environment that suits their preferences. Also, if you use VS Code Web, you can access your development environment anywhere you have a browser and work seamlessly between your local machine and the cloud. 3. Specific use cases for GitHub Codespaces GitHub Codespaces can be used in situations like the ones below. 3.1 Cross-Platform Project Development Because it is a cloud-based development environment, GitHub Codespaces does not depend on the developer’s device or OS. This saves you the trouble of building and configuring development environments in various environments from scratch. Developers can use their favorite OS and enjoy the same development environment as everyone else. 3.2 Use in education and workshops Because it is easy to set up a development environment, GitHub Codespaces is particularly useful in educational and workshop settings. Participants can focus on learning and practicing instead of spending time configuring the environment. 3.3 Pull request reviews GitHub Codespaces is deeply integrated with GitHub, so you can open pull requests directly. This allows you to review pull requests smoothly and quickly without having to switch from the branch you are currently working on. 4. GitHub Codespaces setup procedure To build GitHub Codespaces, you need to create a configuration file in the .devcontainer directory, as in Dev Container. These are the steps to setting up GitHub Codespaces. 4.1 Prerequisites GitHub account Target repository 4.2 Procedure for creating a Dev Container Create a .devcontainer directory in the target repository. In the .devcontainer directory, create Dockerfile , Docker-compose.yml , and .devcontainer.json with suitable content in each configuration file (see sample configuration file in the next chapter). Commit the configuration file to the repository and push it. Access your GitHub repository and click the green "Code" button at the top right of the repository page. Select the "Codespaces" tab from the drop-down menu. You can create a new codespace or select an existing codespace to open it. Since this article is for first-time users, we will select "Create new codespace on main". Optionally, you can specify the remote machine's region and specs. Select "New with options..." from the three dots. You will be taken to the Codespaces startup screen. It will take a few minutes to get ready. When it is ready, the Visual Studio Code interface will appear in your browser. You should then be able to edit the code in the repository by launching Codespaces. You can also use a terminal, which allows you to use tools installed in the development environment. 5. Sample configuration file 5.1 Sample devcontainer.json The .devcontainer.json file describes the configurations for Dev Container and Codespaces. This file defines the construction of the development environment, extensions used, configurations, and so on. docker-from-docker is a configuration item for using Docker from the development environment. By adding this configuration, you can use Docker on the host machine from within the dev container. If you do not add this configuration, you will not be able to use Docker from within the dev container. ghcr.io/devcontainers/features/sshd [^5] is a configuration item for JetBrains Gateway Codespaces. JetBrains Gateway Codespaces is a function that allows you to use GitHub Codespaces in the JetBrains IDE. Adding this configuration allows you to access GitHub Codespaces from the JetBrains IDE. { "name": "sample-app", "build": { "dockerfile": "Dockerfile" }, "service": "devcontainer", "workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}", "postCreateCommand": "sh .devcontainer/post-create.sh", "features": { "ghcr.io/devcontainers/features/go:1": { "version": "latest" }, // Configurations using Docker on the host machine "docker-from-docker": { "version": "latest" }, // for Jetbrains Gateway Codespaces "ghcr.io/devcontainers/features/sshd:1": { "version": "latest" } }, "settings": { "editor.guides.bracketPairs": true, "editor.stickyScroll.enabled": true, "editor.stickyScroll.maxLineCount": 5, "workbench.colorCustomizations": { "editorStickyScroll.background": "#00708D", "editorStickyScrollHover.background": "#59A2B5" }, "editor.formatOnSave": true, "[go]": { "editor.formatOnSave": true, "editor.defaultFormatter": "golang.go" }, "go.formatTool": "gofmt" }, "extensions": [ "golang.go", "GitHub.vscode-pull-request-github", "GitHub.copilot" ] } 5.2 Sample Dockerfile This Dockerfile is used when building a Docker container for devcontainer and Codespaces. ARG VARIANT="jammy" FROM mcr.microsoft.com/vscode/devcontainers/base:1-${VARIANT} 5.3 Sample .devcontainer/docker-compose.yml This docker-compose file is used to build and run devcontainer and Codespaces containers. This file sets the services, environment variables, and volumes required for the development environment. If you want to access MySQL or LocalStack from within a container, you need to access it with the hostname mysql or localstack instead of localhost . For that reason, we set the hostname as an environment variable, such as MYSQL_HOST or LOCALSTACK_HOST . version: "3" services: devcontainer: build: context: . dockerfile: .devcontainer/Dockerfile environment: TZ: Asia/Tokyo MYSQL_USER: developer MYSQL_PASSWORD: password MYSQL_HOST: mysql:3306 # localstack LOCALSTACK_HOST: localstack:4566 DEFAULT_REGION: ap-northeast-1 AWS_ACCOUNT_ID: "000000000000" AWS_ACCESS_KEY_ID: dummy-access-key AWS_SECRET_ACCESS_KEY: dummy-secret-key volumes: - ..:/workspaces:cached command: /bin/sh -c "while sleep 1000; do :; done" 5.4 Sample docker-compose.yml Here is a sample docker-compose.yml used in general application development. This file is used to configure the services, environment variables, and volumes required by applications such as MySQL [^6] and localstack ^7 . This makes it possible to build and run the containers that make up the application all at once. version: "3" services: app: build: context: . dockerfile: Dockerfile volumes: - .:/workspace ports: - "3000:3000" mysql: container_name: mysql build: ./docker/mysql environment: MYSQL_DATABASE: sample MYSQL_USER: developer MYSQL_PASSWORD: password MYSQL_ROOT_PASSWORD: password volumes: - ./docker/mysql/sql:/docker-entrypoint-initdb.d ports: - 3320:3306 localstack: image: localstack/localstack:latest environment: - HOSTNAME=localstack - SERVICES=s3 - DEFAULT_REGION=ap-northeast-1 - DATA_DIR=/tmp/localstack/data volumes: - "${LOCALSTACK_VOLUME_DIR:-./volume}:/var/lib/localstack" - "/var/run/docker.sock:/var/run/docker.sock" ports: - 4777:4566 6. Using from a VSCode or JetBrains IDE Codespaces can be used from a web browser, or from the VSCode or JetBrains IDE. In this section, I will explain each method. 6.1 Launch Codespaces with VSCode Install the VSCode GitHub Codespaces extension To use Codespaces with VSCode, you need to install the "VSCode GitHub Codespaces" extension. To open the Extensions panel, click the Extensions icon from the activity bar on the left and type "GitHub CodeSpaces" in the search box. Install the GitHub CodeSpaces extension that appears in the search results. You can also install it here . Log in to GitHub In VSCode, launch the GitHub CodeSpaces extension and sign in with your GitHub account. 6.2 Launch Codespaces in the JetBrains IDE Install JetBrains Gateway JetBrains Gateway is a tool that allows you to use GitHub CodeSpaces with IDEs by JetBrains (e.g., IntelliJ IDEA, WebStorm, PyCharm). [^8]These are the steps to launching CodeSpaces on the JetBrains Gateway. As in the previous example, in order to use CodeSpaces with the JetBrains Gateway, you need to add the following configurations to .devcontainer.json . { "features": { "ghcr.io/devcontainers/features/sshd:1": { "version": "latest" } } } Go to the JetBrains Gateway installation page to download the installer and install it. You can also install it from the JetBrains Toolbox . Install GitHub CLI The JetBrains Gateway logs in to GitHub using GitHub CLI. To install GitHub CLI, follow the instructions here . If you are using Windows, you can also use the installer here . Log in to GitHub Launch JetBrains Gateway and install GitHub Codespace. Next, click GitHub Codespaces > Sign in to GitHub in the menu and log in to GitHub. You will see a one-time password and a link to the authentication page. Click the link and log in to GitHub. Enter the one-time password and click the [Continue] button. Next, click the [Authorize github] button. Launch CodeSpaces Select the codespace you want to launch from Your Recent Codespaces and click the [Open] button. If you have not created any codespaces, click on Click here to open the Codespaces creation screen. 7. Pros and cons after using codespaces 7.1 Pros Development environment accessible from anywhere: You can do development work without being tied to a physical location Easy to create an environment within your team: Sharing environment configurations makes setting up new members fast and easy Can share configurations with devcontainers: Ensures the consistency of the development environment Faster setup on new devices: Hardware changes do not hinder project progress Can be used with multiple development tools: Visual Studio Code, Visual Studio Code for the Web, JetBrains IDE, JupyterLab (previously limited to VS Code with Dev Container) 7.2 Cons May cost money: May cost money depending on usage time and resources[^9][^10] JetBrains Gateway still in beta may be unstable: This function is still in development, and some functions may not work as expected Internet connection required: You cannot work offline Performance and security concerns because of running on the cloud: There may be network latency, data protection, and other issues Files that are not managed in the repository and data that is put into MySQL disappear when Codespaces is deleted: If you need permanent data storage, you need a suitable backup strategy 8. Conclusion What did you think? I hope this was helpful. The sample configuration file is taken from code that is used in actual development, and every member develops seamlessly in their own local environment, dev container, and codespaces. We also use it together with VS Code’s Code Tour extension for onboarding new members and workshop-style study sessions. In workshops, Codespaces is very convenient because it takes out the hassle of creating the environment, so we can start the main work immediately. Google Cloud has also released Cloud Workstations [^11]. If you are interested, please feel free to try it. GitHub Codespaces is a powerful tool that makes it easy to build a cloud-based development environment. With Dev Container, you can streamline the environment creation for your entire team. Using GitHub Codespaces makes it even easier to build a development environment and improves team productivity. Please take advantage of GitHub Codespaces . [^1]: Posted by a member of the Common Services Development Group 1 [ About how we incorporated Domain-Driven Design (DDD) into payment platforms, with a view toward global expansion as well ] [^2]: Posted by a member of the Common Services Development Group 2 [ About how a team of people who'd all been with the company for less than a year successfully developed a new system through remote mob programming ] [^3]: Posted by a member of the Common Services Development Group 3 [ Initiatives to improve deployment traceability in multiple environments using JIRA and GitHub Actions ] [ [Creating a Development Environment Using VS Code's Dev Container](https://blog.kinto-technologies.com/posts/2022-12-10-VSCodeDevContainer/) ] [^5]: About devcontainers/features sshd [ devcontainers/features sshd ] [^6]: How to configure MySQL in docker-compose [ Create multi-container apps with MySQL and Docker Compose ] [ [GitHub localstack](https://github.com/localstack/localstack) ] [^8]: Remote development in the JetBrains IDE [ Remote development in the JetBrains IDE ] [^9]: About GitHub Codespaces Billing [ About GitHub Codespaces Billing ] [^10]: Shortening the Default idle timeout configuration can save money. [ Configuring Codespaces ] [^11]: Google Cloud - Cloud Workstations [ Cloud Workstations ]
アバター
Mobile Development Group Study Group (Huang) I am Huang, an Android app developer from the KINTO Technologies Mobile Development Group. In this article, I want to talk about the study group we starteed in the Mobile Development Group at KINTO Technologies. A source of team culture When it comes to software development, I think the first thing you need is a culture of sharing. There are many advantages to sharing information with lots of people, but the biggest is being able to reflect the knowledge and views of people with various perspectives. This not only reduces project risks, but also helps us gain new information. Do we really need a study group? Given how busy we all are with coding, do we really need a skill-sharing study group? There's a tendency to think this way, but the two decades since the dot-com bubble have seen the software industry grow in scale and sophistication extremely rapidly, and development become much more technically complex as well. So, as the scope of software development skills and the knowledge required increases, it's becoming vital to have a development culture that's conducive to sharing information and skills so that people can make decisions and communicate efficiently. Problems to solve I'd been finding it difficult to learn about other areas since joining the company, because I'd always been concentrating on one project. Then, I remembered how in my previous job, I'd gained a variety of new technical insights through a study group. So, I decided to start one up here. People can only spend a limited amount of time on studying, and studying with other members is more efficient than doing it alone. What we want to achieve As a culture of skill sharing develops, the members of the organization will grow more quickly, enabling them to contribute to multiple technical communities. This in turn will help the company develop into one where good human resources can grow. Forming a culture of skill sharing Our Mobile Team's study group consists of team members from the API Team, the Android Team, and the iOS Team. Basically, every week, we all take turns to give presentations about our own know-how, technical trends we're interested in, and skills we're currently learning up on. At the end of each presentation, we set aside time for everyone to exchange views and ask questions with a positive and open attitude. Also, if there's a particular skill, etc. you want to know more about, you can post a message on the bulletin board so someone with the right know-how can help. All Mobile Team engineers get to learn about having a skill-sharing culture through the study group after they join the company. The form of the study group is like this: Free topics (areas you're interested in). Ideas are shared every Thursday. About 20 participants. The participants take turns to be the facilitator. The details of the presentations are shared on Confluence. Are we heading in the right direction? Over the course of doing the study group for a year, I noticed a few things that should be kept in mind. Problem: The study group is a project with a very large number of participants. We weren't sure if we were heading in the right direction, and we needed a way to boost its activity even further. Try: When a problem arises, we all get together and have a meeting to get a picture of the goals, consolidate our ideas, and see if they'll work. Everyone plays a leading role in creating our study group and development culture, by thinking about whether we're doing things right and whether we're understanding and communicating with each other properly. Investing in information sharing within the company The Mobile Team study group uses Confluence as a document management system for sharing information within the company. The study group members use Confluence to create and organize various documents about the skills they know themselves and things they need for their work. I think an in-house document system like Confluence plays a crucial role when it comes to sharing information within the company smoothly. For this reason, the Mobile Team is steadily creating study group-related content in Confluence so that other team members can use the document system more efficiently when searching for knowledge they need. What we want to try Support for presenters from other companies There aren't many mobility service companies in Japan, so we're striving to give presentations on and share examples of good practices through technology conferences and within the community. We want to make them events where we can share our in-house development culture, skills, know-how, and the like with developers from other companies. In conclusion Most developers want a good development culture. For that, I think a good culture will have the following: Core values: A culture where people understand the core values of the services, share knowledge with each other, and are proactive about developing the core skills. DevOps: A culture where people understand that code review and testing are necessary if you want to achieve short development cycles, fault tolerance, and high-quality code. Professionalism: A culture where everyone has a sense of responsibility when it comes to results, takes pride in their work, and can improve each other's levels of expertise. In fact, it'll take a fair amount of time and effort to steadily continue skill-sharing activities and establish a culture like this, but in a team that has a good development culture, the team members will be able to share problems they encounter during development and use their know-how to come up with solutions. And as a result, they'll be able to cut down on trial and error, and ultimately provide the best possible services. We firmly believe that establishing a culture like this is essential if the company and its members are to keep on growing. The Mobile Team will continue to engage with developers from other companies in a variety of ways. I think it's worthwhile to hold Mobile Team study group meetings, and through them, pursue a development culture where everyone shares their skills and grows together. If you'd like to study with us, feel free to contact us anytime.
アバター
👋自己紹介 こんにちは!KINTOテクノロジーズのプロジェクト推進GでPjMをしている佐々木です。 これまでのキャリアでは、プログラマとして働いたり、PLとして設計したり、メンバを育成したり、PMっぽいこと(要件定義、ステークホルダー管理など)をしたりしていました。 前職では3年ほどチームみんなでアジャイルに取り組んでリアルカイゼンジャーニーをしていました。 アジャイル熱が強めなので、今日はアジャイル開発について記事を書かさせていただきます! 🚗トヨタとアジャイル 皆さんは、チームへどのようにアジャイル開発の流儀を取り込んでいるでしょうか。 新規サービスはスクラム、運用・保守業務はカンバンなど、アジャイル開発には様々な形があると思いますが、アジャイル開発を学んでいく上でリーン開発とその源流と言われているトヨタ生産方式(Toyota Production System)に出会った方は多いのではないでしょうか[*1]。 今回は、そんなトヨタグループの一員であるKINTOテクノロジーズのアジャイルへの取り組みを見える化してみようと思います。 そして、見える化を通じて社内でアジャイルに取り組んでいる皆さんが新たな気づきを得る手助けができればと思います。 [*1] トヨタの話を引用しているアジャイル本達 アジャイルサムライ リーン開発の現場 カンバン仕事術  など ::: message ### この記事はこんな人に役立ちます - チームのアジャイルの状態をしりたい - ふりかえりがマンネリ化している - アジャイルの理想と現実で苦しんでいる - KINTOテクノロジーズのアジャイルへの取り組みを知りたい ::: やりかた スクラムチェックリスト で各チームのスクラム度を定量的に見える化 1の結果を眺めながらディスカッション 今後やりたいことをゆるく表明 まずは スクラムチェックリスト を用いてスクラムの切り口でスクラムの各指標をどれくらいできているかを見える化します。 ![サンプル:スクラムチェックリストの結果](/assets/blog/authors/K.Sasaki/image-20231120-002531.png =400x) 見える化できたら、ディスカッションを行います。 ディスカッションには 4Lふりかえり のフレームワークを使います。 https://www.ryuzee.com/contents/blog/14561 :::details スクラムチェックリストの使用上の注意点 配布されているスクラムチェックリストには注意書きがあります。 他のチームと比較して評価に使ったりはしないでください 他のチームと比較して一喜一憂するものではありませんので、あくまで似たようなコンテクストでしゃべるためのきっかけとして使っています。 本記事と似たような使い方をする際は、管理者や評価者としての使用は避け、節度のある大人なメンバ間でおつかいください。 ::: 🎉参加メンバ 社内でスクラムマスター(もしくは似た立場)でスクラムやアジャイルっぽくチーム運営している方にお声をかけて協力を募り、10チーム(10名)の方々に集まっていただきました! 皆さん、お忙しいところご協力いただき、ありがとうございます! やってみた ✅スクラムチェックリスト いろいろな形のチャートができました。 「ウォーターフォールに近いがスクラムイベントを回してる」、「自分では課題を感じていたが点数は高めに出た」などチームの状況によって多種多様な結果となりました。 また「スクラムマスターを置いていないけど開発者同士でスクラムイベントを持ち回りで回している」、「プロダクトバックログにはおこせていないがオーナーとの関係は良好」といったように、点数が低い指標があっても現状重めの課題がないチームもありました。 お互い得意な部分が違うので、自分が苦手な指標について他チームに教えてもらうような使い方もできそうですね。グループAはバックログを整理しているチームが多く、グループBではバックログに課題を感じていたチームが多かったです。バックログ整理の知見が交換できるかも…👀 📒ふりかえり(4Lふりかえり) 2グループに分かれてふりかえりを実施しました。 これまでのキャリアでは発言を引き出すための工夫を凝らしたりしましたが、KINTOテクノロジーズでは5~8分程度でボードがすぐに埋まり、意見が活発な印象をうけました。 赤い付箋は、ほかの人の付箋を見たうえでの皆さんの感想です。 グループAの結果 グループBの結果 ※今回は きんちゃん の勧めでConfuruenceに新しく追加されたWhiteBoardを使用しました。 付箋をそのままJIRAチケットに変換することができ、アクションアイテムの整理に活用できそうです。 🚩ふりかえり結果 複数声が聞こえたものをピックアップしてご紹介します。 PO(プロダクトオーナー)との関係を強めることでサービスに生かしたい、アジリティを上げたいという意見が多くきかれました。自己組織化が進んでいるチームが多い印象をうけました。 よかったこと 見える化することでチームの良いところ、悪いところを知ることができた 理想的なスクラムとの乖離している部分を知ることができた 開発者が責任をもって自律的に仕事できている(自己組織的) かけていること POが不在だったり、POがいてもスクラムイベントに巻き込めていない SP(ストーリーポイント)の設定、見積がうまくできていない チーム人数が多くなってきたので、チームの分割が必要 学んだこと 社内のいろんなチーム・プロダクトでのアジャイルの取り組みを知ることができた チームの状況に応じてスプリント期間を短く・長くしてもよい みんなが今後やりたいこと(抜粋) アクションアイテム…とまではいきませんが、ゆるく決意表明していただきました。 スプリント期間の見直し チームの分割 POとのコミュニケーションの強化 💭感想 入社2か月目の自分の呼びかけにも関わらず、部署も拠点も違う皆さんに参加していただけた事に本当に驚いています(初対面の方もいました)。ご協力いただいた方、本当にありがとうございました。 社内のアジャイルに取り組んでいる人たちに集まっていただくことで、ファシリーテータとして以下のような発見や学びを得ました。 スクラムチェックリストを使うことで、チームのスクラム度を定量的に見える化することができる 取り組み度合が違うチーム同士の話を聞くことで、改善のきっかけが得られたり自分の活動に勇気をもつことができる 各チームのスクラムマスター同士がつながることで、課題の相談先を見つけることができる 組織としての共通課題が見つかった(POとのコミュニケーション強化やチーム分割の必要性) 当日はファシリテーションに徹していたのでリアクション薄めでしたが、参加者の「めげかけていたが、みんなの活動を知れて励まされた」という声をお聞きした時、涙ぐみそうになりました。 私は、これまでのキャリアでアジャイルの課題にぶつかった時には、解決方法や共感を外部の勉強会や本で補充していました。社内でこうしたもやもやを共有できたり、課題を相談する相手がいるのは素晴らしいことだと思います。 🏔まとめ:僕たちアジャイル何合目? KINTOテクノロジーズでは、大規模プロジェクトはウォーターフォールで進めたり、開発の性質に応じて柔軟に開発スタイルが決まります。今回はスクラムという切り口で社内のアジャイル度を見える化してみましたが、それぞれのチームで色んなアジャイルへの取り組み方や課題があることがわかりました。 なので…アジャイル何合目?という問いに対し「これ!」という答えは出ませんでした!(スミマセン) しかしながら、スクラムマスター同士がつながることで、更にみんなでアジャイルの道を進めた気がします! ✨今後やりたいこと 私はプロジェクト推進Gという横断系のチームにいます。 入社したばかりなのでおこがましいですが、これをきっかけに スクラムオブスクラムズ のような取り組みや、ふりかえりのふりかえり会(チームの改善をスクラムマスター同士で共有する会)など、チームを横断した開発を促進する手助けができればいいなぁと考えています。 さて、アジャイルサムライの最後は「アジャイルであるかなんて気にしない」という言葉で締めくくられています。 これからも自分のできる範囲でカイゼンを続け、アジャイル山をみんなで登り続けたいと思います! Be Agile!記事をお読みいただきありがとうございました。
アバター
はじめに こんにちは、KINTOテクノロジーズの長谷川です。 普段はAndroidエンジニアとして、myrouteというアプリの開発をしています。 この記事ではmyrouteのAndroid開発を通じて経験した、データベースのマイグレーションについて紹介します。 記事の概要 RoomはAndroidにおいて、データをローカルに簡単に永続化することができる公式のライブラリです。データをデバイスに保存することはユーザー視点で、オフラインでアプリを使用できるようになるなど大きなメリットがあります。一方で開発者視点では、いくつか必要な作業があります。その一つがマイグレーションです。Room公式ではデータベースの自動マイグレーションに対応していますが、複雑なデータベースの変更を伴うアップデートなどでは手動で対応するケースもあります。本記事では簡単な自動マイグレーションから複雑な手動マイグレーションまで、いくつかのユースケースと共に紹介します。 正しくマイグレーションしないとどうなる? ところで正しくマイグレーションに対応しないと、どうなるのでしょうか?アプリ内の対応状況にもよりますが、大きく分けて2パターンがあります。 アプリが落ちる データが消える 「アプリが落ちる」の方は、Roomを触ったことがある方なら体験したことがあるかもしれません。それぞれの場合に応じて、以下のようなエラーが発生します。 DBバージョンが更新されたのに、適切なマイグレーションが提供されていない場合 A migration from 1 to 2 was required but not found. Please provide the necessary Migration path スキーマを更新したのにDBバージョンがアップデートされていない場合 Room cannot verify the data integrity. Looks like you've changed schema but forgot to update the version number. 手動マイグレーションが正しく動作していない場合 Migration didn't properly handle: HogeEntity(). 基本的には全て開発環境でも発生するはずなので、そこまで問題になることはないと思います。ただし後述する fallback~ 系の対応により、マイグレーションの失敗をもみ消していたりするととても気付きにくく、場合によっては本番環境だけ発生する可能性もあるので注意が必要です。 「データが消える」の方はどうでしょうか?実はRoomはDBオブジェクトの作成時に fallbackToDestructiveMigration() という関数を呼ぶことができます。これはマイグレーションに失敗した場合データを恒久的に削除して、アプリを正常に起動することができる関数です。上記で紹介したエラーなどの対策のためか、それともDBのマイグレーションは手間がかかるため避けたのかは分かりませんがたまに使われているケースを見ます。これを行うとマイグレーションに失敗した場合データが消えてしまう上に気づきづらいのでなるべくマイグレーションを正常に行うようにしましょう。 マイグレーションの4個のシナリオ ここからはアプリ開発を行う上で発生しそうなスキーマの更新を4つの例とともに紹介します。 1. 新しいテーブルの追加 新しいテーブルを追加する場合は既存のデータに影響しないため、自動マイグレーションすることができます。 例えばDBバージョン1では HogeClass というエンティティがあり、DBバージョン2で HugaClass というエンティティを追加した場合、以下のように autoMigrations に AutoMigration(from = 1, to = 2) のような形で渡すだけで大丈夫です。 @Database( entities = [ HogeClass::class, HugaClass::class, // 追加 ], version = 2, // 1 -> 2 autoMigrations = [ AutoMigration(from = 1, to = 2) ] ) abstract class AppDatabase : RoomDatabase() {} 2. テーブルの削除や名前変更、columnの削除や名前変更 削除と名前の変更に関しては自動マイグレーションが可能ですが、 AutoMigrationSpec というものを定義する必要があります。 一番発生しそうなcolumn名の変更の例として、 User というエンティティの name というcolumnを firstName という名前に変更したとします。 @Entity data class User( @PrimaryKey val id: Int, // val name: String, // old val firstName: String, // new val age: Int, ) まずは AutoMigrationSpec を実装したクラスを定義します。そして、 @RenameColumn というアノテーションを付与し、変更するcolumnに対して必要な情報を引数で渡します。 作成したクラスを AutoMigration の対応するバージョンに渡し、それを autoMigrations に渡します。 @RenameColumn( tableName = "User", fromColumnName = "name", toColumnName = "firstName" ) class NameToFirstnameAutoMigrationSpec : AutoMigrationSpec @Database( entities = [ User::class, Person::class ], version = 2, autoMigrations = [ AutoMigration(from = 1, to = 2, NameToFirstnameAutoMigrationSpec::class), ] ) abstract class AppDatabase : RoomDatabase() {} 他にも @DeleteTable や @RenameTable 、 @DeleteColumn というアノテーションが用意されており、削除、名前の変更はこれにより簡単に対応を行うことができます。 3. columnの追加 columnの追加は個人的には一番発生する可能性が高いと思います。ここでは User というエンティティに対して、 height という身長を表すcolumnを追加したとしましょう。 @Entity data class User( @PrimaryKey val id: Int, val name: String, val age: Int, val height: Int, // new ) columnの追加は手動マイグレーションが必要です。理由はRoomにheightのデフォルト値を教えるためです。以下のようにMigratonを継承したオブジェクトを作成し、Databaseオブジェクト作成時に addMigration() に渡すだけです。 database.execSQL の中で必要なSQLステートメントを記述します。 val MIGRATION_1_2 = object : Migration(1, 2) { override fun migrate(database: SupportSQLiteDatabase) { database.execSQL( "ALTER TABLE User ADD COLUMN height Integer NOT NULL DEFAULT 0" ) } } val db = Room.databaseBuilder( context, AppDatabase::class.java, "database-name" ) .addMigrations(MIGRATION_1_2) .build() 4. 主キーを追加する 筆者が経験したアプリでは、主キーを追加するケースもありました。テーブル作成時に想定していた主キーだけでは一意性が保てず、他のcolumnを主キーに追加する場合です。例えばUserテーブルにて、 id が今まで主キーでしたが、 name も主キーにして複合主キーになったとします。 // DBバージョン 1 @Entity data class User( @PrimaryKey val id: Int, val name: String, val age: Int, ) // DBバージョン 2 @Entity( primaryKeys = ["id", "name"] ) data class User( val id: Int, val name: String, val age: Int, ) この場合はAndroidに限った話ではありませんが、新たにテーブルを作り直す方法が一般的です。下記のSQLステートメントでは UserNew というテーブルを新しい主キーの条件で作成し、 User テーブルの情報をコピーします。その後既存の User テーブルは削除して、 UserNew テーブルの名前を User に変更します。 val migration_1_2 = object : Migration(1, 2) { override fun migrate(database: SupportSQLiteDatabase) { database.execSQL("CREATE TABLE IF NOT EXISTS UserNew (`id` Integer NOT NULL, `name` TEXT NOT NULL, `age` Integer NOT NULL, PRIMARY KEY(`id`, `name`))") database.execSQL("INSERT INTO UserNew (`id`, `name`, `age`) SELECT `id`, `name`, `age` FROM User") database.execSQL("DROP TABLE User") database.execSQL("ALTER TABLE UserNew RENAME TO User") } } マイグレーションが正しく動作するか確かめよう! ここまで紹介したマイグレーションの例以外にも、複雑なケースはたくさんあります。筆者が関わっているアプリでも外部キーが関わり合っているテーブルの変更などがありました。そのような場合SQLステートメントをゴリゴリ書いていくしかないのですが、本当にそのSQLが正しく動いているか確認したくなります。そのためにRoomにはMigrationをテストする方法が提供されています。 以下のテストコードでmigrationが適切にできているかテストすることができます。なおテストを行う場合、予め各DBバージョンのスキーマをエクスポートしておく必要があります。詳しくは スキーマをエクスポートする を参考にしてみてください。昔のDBバージョンのスキーマをエクスポートしていなかった場合も、gitのタグなどから過去のバージョンを特定し、スキーマをエクスポートしておくことをお勧めします。 ポイントとしては manualMigrations というリストで定義された変数のように、プロダクションコードとテストコードで実行するmigrationとして同じ値を参照することです。これにより、プロダクションコードで migration5_6 を追加したとしても、テストコードが自動でそれを検証してくれるため安心です。 // production code val manualMigrations = listOf( migration1To2, migration2To3, // 3->4は自動マイグレーションとする migration4To5, ) // test code @RunWith(AndroidJUnit4::class) class MigrationTest { private val TEST_DB = "migration-test" @get:Rule val helper: MigrationTestHelper = MigrationTestHelper( InstrumentationRegistry.getInstrumentation(), AppDatabase::class.java, ) @Test @Throws(IOException::class) fun migrateAll() { helper.createDatabase(TEST_DB, 1).apply { close() } Room.databaseBuilder( InstrumentationRegistry.getInstrumentation().targetContext, AppDatabase::class.java, TEST_DB ).addMigrations(*ALL_MIGRATIONS).build().apply { openHelper.writableDatabase.close() } } } まとめ RoomのマイグレーションをいくつかのUseCaseで紹介しました。 手動マイグレーションはなるべく避けたいですが、そのためにはチーム全体でテーブル設計をしっかり行うことが鍵だと思います。またDBバージョンごとにスキーマをエクスポートすることも忘れずに行いましょう。そうしないと、gitなどで履歴を遡ってスキーマをエクスポートして、それを検証する未来の開発者がちょっと大変ですからね。 以上になります。 参考 https://developer.android.com/training/data-storage/room/migrating-db-versions?hl=ja
アバター