TECH PLAY

KINTOテクノロゞヌズ

KINTOテクノロゞヌズ の技術ブログ

å…š975ä»¶

この蚘事は KINTOテクノロゞヌズアドベントカレンダヌ2024 の23日目の蚘事です🎅🎄 はじめに こんにちはKINTO テクノロゞヌズでiOSアプリケヌションを開発しおいるFelixです。今日は、最近芋぀けた蚭蚈ツヌルを玹介したす Play .開発の生産性を向䞊させる方法を暡玢する䞭で、UI蚭蚈をSwiftコヌドに倉換できるツヌルを探しおいたした。そんな時にPlayを芋぀け、予想以䞊に倚くの機胜を持っおいるこずがわかりたした。Playを䜿うず、開発者や蚭蚈者は、SwiftUI コヌドを自動的に生成しながら、デバむス䞊で盎接ナヌザヌむンタヌフェヌスを䜜成、テスト、反埩凊理できたす。このツヌルは、蚭蚈ず開発を結び付け、より優れたコラボレヌションを促進し、効率を高めたす。 この投皿では、私が特に䟿利だず感じた機胜をいく぀か玹介したす。 蚭蚈者はNative SwiftUIコンポヌネントを䜿甚できたす KINTOでは、蚭蚈者ず密に連携しお、アプリケヌションがAppleのヒュヌマンむンタヌフェヌスガむドラむンに沿うようにしおいたす。ただし、これは難しい堎合がありたす。蚭蚈者はiOSにおける詳现な実装を必ずしも完党に把握しおいるわけではないからです。Playの堎合、蚭蚈者はネむティブ SwiftUI コンポヌネントを䜿甚しお蚭蚈するこずができたす。これにより、蚭蚈者はiOSですぐに利甚できる機胜をより深く理解できるようになり、開発者はiOSネむティブ ラむブラリにすでに含たれおいる機胜を再発明する必芁がなくなりたす。このツヌルは、ネむティブコンポヌネントを掻甚するこずで、蚭蚈者ず開発者ずのコミュニケヌションを明確にし、Appleのヒュヌマンむンタヌフェむスガむドラむンの遵守を確保したす。 盞互䜜甚の定矩 UI蚭蚈のみに焊点を圓おおいる倚くの蚭蚈ツヌルずは異なり、Playを䜿甚するず、 蚭蚈者はアクションずアニメヌションをiOSで自然に蚭定するこずができたす。これにより、コンポヌネントがどのように動䜜するかがより明確になりたす。䟋えば、蚭蚈者は、ボタンを抌すず䜕が起こるか、次のペヌゞぞの移行がどのように芋えるか、アニメヌションがどのように流れるかを指定できたす。この機胜は、蚭蚈ず開発のギャップを埋め、コンセプトから実装ぞの移行を円滑にしたす。 ラむブプレビュヌ 私たちの開発プロセスで特に時間のかかる郚分は、UI蚭蚈を実装し、テストビルドの公開埌にフィヌドバックを埅぀こずです。Playを䜿甚するず、このプロセスが円滑化し、埅ち時間が倧幅に短瞮したす。蚭蚈者がラむブプレビュヌで動䜜を確認できるからです。むンタラクションが蚭蚈内で蚭定されるず、Playを䜿甚するこずで、蚭蚈者は実際のデバむスで盎接䜜業をテストできたす。このツヌルはiOSのネむティブラむブラリを䜿甚しおデモアプリを構築するため、蚭蚈者は補品版で期埅される正確な動䜜を䜓隓できたす。この機胜は、蚭蚈やむンタラクションを開発者に匕き枡す前に怜蚌する際に非垞に圹立ちたす。 他にも... もちろん、PlayはUI蚭蚈をSwiftUIコヌドに倉換したすが、それだけでなく、ワヌクフロヌを向䞊させる远加機胜も提䟛したす。䟋えば、Playは既存のFigma蚭蚈のむンポヌトをサポヌトしおいるため、移行が簡単になりたす。蚭蚈者は、UI がさたざたな画面サむズやデバむスでどのように衚瀺されるかをプレビュヌできるため、レスポンシブで適応可胜な蚭蚈を確保できたす。ただ私が発芋しおいない機胜がたくさんあるず思いたす私たちのチヌムはこのツヌルの䜿甚に非垞に興味を持っおいたすが、これを詊す前にiOSずAndroid甚に蚭蚈を分ける必芁がありたす。iOS甚の独立した蚭蚈があり、チヌムの生産性を向䞊させ、蚭蚈者ず開発者ずのコミュニケヌションを促進したいず考えおいる堎合は、Playを詊しおみるこずを匷くお勧めしたす。
はじめに こんにちは、KINTO テクノロゞヌズ ( 以䞋、KTC ) SCoE グルヌプの桑原 @Osaka Tech Lab です。 SCoE は、Security Center of Excellence の略語で、ただ銎染みのない蚀葉かもしれたせん。KTC では、2024 幎 4 月に CCoE チヌムを SCoE グルヌプずしお再線したした。SCoE グルヌプに぀いお知りたい方は、 クラりドセキュリティの進化をリヌドする SCoE グルヌプ を参照ください。 たた、KTC の関西拠点である Osaka Tech Lab に぀いおは、 Osaka Tech Lab 玹介 をご参照ください。 SCoE グルヌプでは、AWS や Google Cloud , Azure のクラりドにおける「ガヌドレヌル監芖ず改善掻動をリアルタむムで実斜する」をミッションに掻動しおいたす。具䜓的な掻動の芳点ずしおは以䞋の 3 ぀です。 セキュリティリスクを発生させない セキュリティリスクを垞に監芖・分析する セキュリティリスクが発生したずきに速やかに察応する 今回は「KTC のクラりドセキュリティ゚ンゞニアっお䜕をやっおるの」を具䜓的に知っおいただこうず思いたす。 クラりドセキュリティ゚ンゞニアのずある䞀日 具䜓的なむメヌゞをしおいただくために、クラりドセキュリティ゚ンゞニアの "ずある䞀日" をご玹介したす。セキュリティずいう分野の郜合䞊、詳现に語れない郚分があるこずをご了解ください。 アラヌト確認 朝䞀番に行うのは、リスクの高いアラヌトが発生しおいないかの確認です。CSPMCloud Security Posture Managementや脅嚁怜知サヌビスを䜿甚しお、クラりド環境党䜓のセキュリティ状況を把握し、即時察応が必芁なアラヌトがないか確認したす。 KTC では、CSPM や脅嚁怜知サヌビスずしお、 AWS Security Hub や Amazon GuardDuty 、 Sysdig Secure 等のサヌビスを掻甚しおいたす。 アラヌトを確認する際は、以䞋の芳点で察応したす。 アラヌトの優先順䜍付け : 重倧床や圱響範囲に基づいおアラヌトを分類し、優先順䜍を付けたす。 アラヌトのトリアヌゞ : 発生したアラヌトの原因を特定し、必芁な察応策を講じたす。 停陜性過怜知の管理 : セキュリティツヌルは時折、停陜性False Positivesを発生させるこずがありたす。これにより、実際には問題のないアクティビティがアラヌトずしお報告されるこずがありたす。これらの管理もアラヌト凊理ずしお察応したす。 業務䞊必芁なオペレヌションの識別 : 停陜性の管理に関係したすが、䞀郚のアラヌトは、業務䞊必芁なオペレヌションによっお匕き起こされるこずがありたす。䟋えば、各プロダクトの担圓者が定期的に行うメンテナンス䜜業などです。これらのアクティビティを識別し、適切に察応したす。 これにより、クラりド環境党䜓のセキュリティ状況を把握し、即時察応が必芁なアラヌトがないか確認したす。 情報収集、キャッチアップ 次に、サむバヌセキュリティの動向や AWS などのクラりドサヌビスの最新情報をキャッチアップしたす。情報源ずしおは以䞋のものを利甚したす。 X旧:Twitter : サむバヌセキュリティの専門家や業界リヌダヌをフォロヌしおいたす。圌らは最新の脅嚁情報や察策を共有しおいるため、リアルタむムでの情報収集が可胜です。 AWS や Google Cloud の公匏ニュヌスやブログ : クラりドサヌビスプロバむダヌの公匏情報は、新機胜のリリヌスやセキュリティアップデヌトに関する重芁な情報源です。これにより、新サヌビスのロヌンチ情報や最新の技術動向、ベストプラクティスを把握できたす。 その他ニュヌスサむト : サむバヌセキュリティに特化したニュヌスサむトやブログを定期的にチェックするこずで、業界党䜓の動向を把握し、最新の脅嚁や攻撃手法に぀いおキャッチアップしたす。 SIEMでの脅嚁怜知 KTC では、SIEMSecurity Information and Event Managementずしお、 Splunk Cloud Platform を䜿甚しおいたす。この Splunk にセキュリティ関連ログを集玄し、ログの暪断分析ず監芖を行える環境を敎備しおいたす。 この日は、Splunk のダッシュボヌドにお、違和感のあるログを発芋したした。内容ずしおは、「Google Cloud の組織ポリシヌで制限されおいるサヌビスに察しお、リ゜ヌス䜜成を䜕床しようずしおオペレヌションに倱敗しおいる」ずいうものです。 Splunk にお独自に䜜成しおいる Google Cloud Audit logs 甚のダッシュボヌドの情報から、おおよそのアクティビティは刀断できおいたしたが、より詳现に調査したす。 たず、Google Cloud の組織ポリシヌで制限をかけおいるサヌビスに察しお、リ゜ヌス䜜成を䜕床もリトラむしおいるナヌザヌを特定したす。 Google Cloud の Audit log (audit:policy_denied) ではナヌザヌ情報はマスクされた状態でログ出力されるため、このログ単䜓ではナヌザヌは特定できたせん。端末系のログなどず䞀緒に暪断的なログを分析するこずで、ナヌザヌを特定したす。この分析に䜿甚するク゚リを䜜成し、該圓のナヌザヌを特定したした。 次に特定したナヌザヌの行動を詳现に分析するためのク゚リを䜜成し、ログを分析したす。 どうやら、AI/ML のサヌビスである Vertex AI を䜿甚しようずしおいる暡様です。該圓のプロゞェクトでは、Compute 系の利甚申請は出おいたせんでしたので、組織ポリシヌにお Compute 系サヌビスの䜿甚を制限をしおいたす。 Vertex AI は、Notebook を䜿甚する際に、Compute Engine (GCE) むンスタンスが起動したす。よっお、この郚分で組織ポリシヌ違反ずなりたす。 結果的に、「Google Cloud プロゞェクト新芏発行申請時に利甚予定サヌビスの蚘茉挏れがあった」ずいうこずで、この件は、問題ないアクティビティであるこずを確認したした。 クラりドベンダヌネむティブのセキュリティサヌビスに察するコスト最適化怜蚎 クラりドベンダヌが提䟛するセキュリティサヌビスは埓量課金制であり、クラりドリ゜ヌスが増加するず、それに䌎っおセキュリティサヌビスの利甚料も増加したす。 私たちが考える「セキュリティ」は、 「ビゞネスのためのセキュリティ」であり、「ビゞネスを阻害するセキュリティ」は NG です。 そのため、「セキュリティずコストのバランス」も重芁なポむントであり、セキュリティサヌビスのコスト最適化も SCoE グルヌプのミッションに含たれたす。 この日は、党䜓コストの䞭で割合が高いいく぀かのセキュリティサヌビスに぀いお、コスト最適化の可胜性を調査したした。 䞊蚘のグラフは、今回の分析察象ずなったサヌビスを瀺しおいたす。その䞭でも特に AWS Config に泚目したした。具䜓的な項目名はマスキングしおいたす AWS Config は、AWS リ゜ヌスの構成を監査、評䟡、そしお蚘録するためのサヌビスです。2023 幎 11 月たでは、AWS Config の蚘録方匏ずしお「リ゜ヌス構成の倉曎が発生するたびに蚘録する方匏」しか提䟛されおいたせんでした。この方匏は、「蚘録頻床継続的な蚘録」ず呌ばれおいたす。 ぀たり、リ゜ヌスの倉曎頻床が高い堎合、AWS Config の蚘録回数が増加し、それに比䟋しお利甚料金も増加する仕組みです。 䟋ずしお、ネットワヌク関連のむベントを確認しおみたしょう。以䞋のデヌタは、ある AWS アカりントにおける 1 週間分の VPC およびネットワヌク関連の構成倉曎回数を瀺したグラフです。 Elastic Network Interface (ENI) の䜜成・削陀に該圓する  CreateNetworkInterface ず DeleteNetworkInterface が䞀日あたり玄 17,000 件発生しおいるこずがわかりたす。 KTC では、Amazon Elastic Container Service (ECS) の Fargate を掻甚しおいたす。このため、ECS タスク (コンテナ) が起動・停止するたびに、ENI の䜜成・削陀が発生したす。このような状況䞋で AWS Config を「蚘録頻床継続的な蚘録」に蚭定しおいる堎合、これらの倉曎に䌎う AWS Config の蚘録回数が膚倧になり、それに応じお課金額も増加したす。 しかし、2023 幎 11 月以降、AWS Config に「蚘録頻床日次蚘録」を遞択できる機胜が远加されたした。この新機胜により、リ゜ヌスタむプごずに蚘録頻床を調敎するこずが可胜ずなり、セキュリティずコストのバランスを柔軟に取るこずができるようになりたした。䞀般的には、この蚭定を掻甚するこずで AWS Config の利甚コストを最適化できるず考えられおいたす。 ただし、これは AWS Control Tower を䜿甚しおいない堎合に限りたす。AWS Control Tower は耇数の AWS アカりントのガバナンスを䞀元管理するためのサヌビスです。 AWS Control Tower を利甚しお AWS アカりントの AWS Config を管理しおいる堎合は、 Guidance for creating and modifying AWS Control Tower resources を確認しおください。 ガむダンスの冒頭に蚘茉されおいる以䞋の䞀文に泚目しおください。 Do not modify or delete any resources created by AWS Control Tower, including resources in the management account, in the shared accounts, and in member accounts. If you modify these resources, you may be required to update your landing zone or re-register an OU, and modification can result in inaccurate compliance reporting. この蚘茉が瀺すように、 AWS Control Tower によっお䜜成されたリ゜ヌスを AWS Control Tower 以倖の方法で倉曎たたは削陀するこずは非掚奚 です。 具䜓的には、2024 幎 12 月珟圚、AWS Control Tower では AWS Config の蚘録頻床を倉曎する機胜が提䟛されおいたせん。そのため、AWS Control Tower 管理䞋の AWS Config の蚘録頻床を倉曎するこずは非掚奚ずされおおり、公匏ドキュメントにも問題が発生する可胜性があるず蚘されおいたす。 公匏ドキュメントの内容を螏たえ぀぀、念のため AWS サポヌトにも問い合わせを行ったずころ、同様の芋解を埗たした。 このように、「蚭定そのものは可胜であっおも、問題が発生するリスクがある、たたは非掚奚ずされる」堎合には、安定したクラりドセキュリティずガバナンスを維持するこずが難しくなりたす。その結果、 「ビゞネスを阻害するセキュリティ」 ずなりかねたせん。 以䞊を螏たえ、AWS Config の蚘録頻床倉曎は珟時点では芋送るこずずし、AWS サポヌトに改善芁望を提出したした。クラりドサヌビスの利䟿性向䞊を目的ずしたこうした改善芁望の起案は、地道ではありたすが非垞に重芁な取り組みであるず考えおいたす。 セキュリティ勉匷䌚の準備 最埌に、定期的に実斜しおいる瀟内セキュリティ勉匷䌚セキュリティプラむバシヌ勉匷䌚での登壇資料を䜜成したした。 SCoE グルヌプでは、プロダクト開発時の「芁件定矩」「蚭蚈」「開発」フェヌズにおけるクラりドセキュリティの勘所をたずめた、"クラりドセキュリティガむドラむン" を策定し、瀟内向けに公開しおいたす。 このガむドラむンは、KTC が所属するグルヌプ䌁業のセキュリティポリシヌを遵守し、セキュリティリスクを最小限に抑え぀぀、効率的な開発を支揎するための重芁なリ゜ヌスです。 この "クラりドセキュリティガむドラむン" の呚知ず理解を促進するために、勉匷䌚でのセッションを受け持っおいたす。勉匷䌚では、具䜓的な事䟋や実践的なアドバむスを亀えながら、ガむドラむンの各項目に぀いお詳しく説明したす。 この日は、IAMIdentity and Access Managementのベストプラクティスに぀いお、持ち時間 20 分に収たるサむズの登壇資料を䜜成したした。 さいごに KTC のクラりドセキュリティ゚ンゞニアのずある䞀日をご玹介いたしたした。業務のごく䞀郚でしたが、業務内容をむメヌゞできたしたでしょうか 我々 SCoE グルヌプでは、䞀緒に働いおくれる仲間を募集しおいたす。クラりドセキュリティの実務経隓がある方も、経隓はないけれど興味がある方も倧歓迎です。お気軜にお問い合わせください。 詳しくは、 こちらをご確認ください 。
This article is the entry for day 14 in the KINTO Technologies Advent Calendar 2024 🎅🎄 Introduction Konnichiwa! I am Felix, and I develop iOS applications at KINTO Technologies. Today, I would like to introduce a design tool I recently discovered: Play . While exploring ways to improve development productivity, I was searching for a tool that could translate UI designs into Swift code. That is when I found Play, and it turned out to offer far more features than I had expected. Play allows developers and designers to create, test, and iterate on user interfaces directly on their devices, all while automatically generating SwiftUI code. This tool connects design and development, fostering better collaboration and increasing efficiency. In this post, I will share some of the features I found particularly useful. Designers Can Use Native SwiftUI Components At KINTO, we collaborate closely with designers to ensure our applications align with Apple’s Human Interface Guidelines. However, achieving this can sometimes be challenging, as designers may not always be fully aware of the detailed implementation in iOS. Play allows designers to use native SwiftUI components in their designs. This helps designers better understand what features are readily available in iOS, saving developers from having to reinvent features that are already part of the iOS native library. By leveraging native components, the tool promotes clearer communication between designers and developers and ensures adherence to Apple’s Human Interface Guidelines. Defining Interactions Unlike many design tools that focus solely on UI design, Play enables designers to define actions and animations natively in iOS. This provides a clearer vision of how components are expected to behave. For instance, designers can specify what happens when a button is pressed, how transitions to the next page should look, and how animations should flow. This capability bridges the gap between design and development, making the transition from concept to implementation smoother. Live Preview A particularly time-consuming part of our development process is implementing UI designs and waiting for feedback after publishing a test build. Play helps streamline this process and significantly reduces the waiting time, as designers can check the behavior in the live preview. Once interactions are set up in the design, Play allows designers to test their work directly on a real device. Since the tool builds a demo app using iOS native libraries, designers can experience the exact behavior they would expect in the production version. This feature is incredibly valuable for validating designs and interactions before handing them off to developers. And More... Of course, Play converts UI designs into SwiftUI code, but it goes beyond that to offer additional features that can elevate your workflow. For example, it supports importing existing Figma designs, making migration effortless. Designers can also preview how the UI will look on different screen sizes and devices, ensuring responsive and adaptable designs. I’m sure there are plenty more features I haven’t discovered yet! Our team is highly interested in using this tool, however we need to separate the design for iOS and Android before we try this. If you have the independent designs for iOS and if you are looking to improve team productivity and foster better communication between designers and developers, I would highly recommend giving Play a try.
この蚘事は KINTOテクノロゞヌズアドベントカレンダヌ2024 の14日目の蚘事です🎅🎄 孊びの道の駅の䞭西です。今幎は孊びの道の駅プロゞェクトが立ち䞊がり組織化されたした。そんな䞭、瀟内Podcastの運営も行っおおり、今幎のアドベントカレンダヌではその内容もお届けしたいず思いたす。 「孊びの道の駅」ずは 「孊びの道の駅」は、瀟内で頻繁に開催される勉匷䌚をもっず䟿利に、そしお効果的に掻甚するために立ち䞊げられたプロゞェクトです。瀟内の有志が䞭心ずなり勉匷䌚の開催を支揎し瀟内の知識共有を促進するこずを目的ずしおいたす。 郚長䌚議事メモを読む䌚 KTC孊びの道の駅ポッドキャストでは、KTCの勉匷䌚を開催しおいる方々にむンタビュヌをしおいたす。その名も「突撃隣の勉匷䌚」。今回は「郚長䌚議事メモを読む䌚」を開催しおいる倧森さんず高朚さんにお話を䌺いたす。 むンタビュヌ HOKAさん: では、早速お二人の自己玹介をお願いできたすか 倧森さん: はい、コヌポレヌトITの倧森です。普段は宀町16階センタヌでパ゜コンのキッティング䜜業をしおいたす。アセットプラットフォヌムチヌムに所属しおおり、業務甚デバむスやSaaSアカりントラむセンスの管理を行っおいたす。新入瀟員のデバむス準備や回収も担圓しおいたす。 高朚さん: はい、同じくコヌポレヌトITの高朚です。私はテックサヌビスチヌムで勀務しおおり、神保町ず宀町を行き来しおいたす。サヌビスデスクずしお、瀟内からの問い合わせに察応したり、問題解決を行っおいたす。具䜓的には自己サヌビスマネゞメントGSMやOPITマネゞメントを担圓しおいたす。 HOKAさん: ありがずうございたす。それでは、郚長䌚議事メモを読む䌚のきっかけに぀いお教えおいただけたすか 倧森さん: きっかけは、名叀屋にいるきんちゃんが発起人です。コヌポレヌトITは普段の業務で事業の最前線の情報に觊れる機䌚が少ないため、郚長䌚の議事メモを共有し、みんなでむンプットし議論するこずで生産性を向䞊させようずいう目的で始めたした。 高朚さん: 私も同じように感じおいたす。議事メモを読むこずで、事業の動きを先読みし、業務に圹立おるこずができたす。䟋えば、申請が来る前に準備を敎えるこずができるので、業務の効率が䞊がりたす。 HOKAさん: 実際にこの䌚を通じおどのような効果がありたしたか 倧森さん: 実際の業務に盎結するこずは少ないですが、議事メモを読むこずでプロゞェクトの背景を理解し、適切な提案ができるようになりたす。これにより、業務の質が向䞊したす。 高朚さん: 同感です。議事メモを読むこずで、事業の動きを把握し、突発的な䟝頌にも察応しやすくなりたす。議事メモを通じお埗た情報は、業務の刀断や提案に圹立っおいたす。 HOKAさん: これからの展望に぀いお教えおください。 高朚さん: ファシリテヌタヌのチャレンゞの堎ずしおも䜿っおもらいたいです。新しい参加者も増え、より賑やかに、楜しく孊べる堎にしおいきたいです。 倧森さん: 同感です。事業理解を深めるために、議事メモを読み、議論するこずで、参加者党員が業務に圹立おられるようにしたいです。情報を蓄積し、埌からでもキャッチアップできるようにしおいきたいです。 HOKAさん: 最埌に、聞いおいる皆さんに䞀蚀お願いしたす。 倧森さん: この䌚は誰でも参加可胜です。興味がある方はぜひ参加しおください。䞀緒に事業理解を深め、業務の質を向䞊させたしょう。 高朚さん: これからSlackチャンネルを䜜成し、告知を行いたすので、興味のある方はぜひ参加しおください。参加するこずで、事業理解が深たり、自分の仕事にも圹立぀ず思いたす。 HOKAさん: 今日はありがずうございたした。 今回は郚長䌚議事メモを読む䌚の詳现ず、その運営の背景、今埌の展望に぀いおお届けしたした。次回の勉匷䌚も楜しみにしおください
Introduction Hello. I am Chris, a front-end developer in the Global Development Division at KINTO Technologies. I have written about Storybook and Vue.js , but today, I would like to move away from technology a little, and talk about management. Actually, I became the leader of the front-end team in July of last year, and it's been almost a year since then. It has been my first leadership experience and a many things happened, but I wanted to think about how I can become a better one for next year, look back on what I have done as one over the past year, and set it all down in writing. So, I decided to write this article about it. About the Team First, going back to when I became the team leader last year, the team itself was created when the department reorganized and we wanted a unit that specialized in front-end development. As I mentioned in a previous article, we are a multinational team with some members who are not that fluent in Japanese, so we communicate in basic English within the team. The main job of this team is to do the front-end development for each project that belongs to the Global Development Division, but since there are not many members in relation to the multiple products involved, it is not uncommon for one person to be responsible for several of them. Also, since there are no front-end tasks for products that have entered their maintenance period, we sometimes improve the code by refactoring it, and at the same time rotate team members to other products. Role as the Team Leader Before I became the team leader, I used to focus on front-end development work and develop the design system as a member of this team. However, since becoming the leader, I have mainly focused on the following tasks, and have often left the development work to the other members rather than doing it myself. Selecting technologies and implementation methods The first task is to select the technologies and implementation methods for the team as a whole. There are many frameworks and libraries to choose from for front-end development. In fact, there are so many to choose from that I imagine lots of people probably struggle to decide—and I am one of them, of course. When it comes to frameworks, you have to consider things like whether the team members are used to it, whether it meets the product requirements in terms of functionality, and whether the community support is adequate. However, another important factor is how to strike a balance with developers’ common tendency to want to try out trending frameworks, too. Currently, the front-end team in the Global Development Division is basically uniformly using Vue.js/Nuxt.js, but that does not mean that we will have to continue using this set indefinitely. The team always has an atmosphere of wanting to try new things, so recently, inspired by other front-end teams at in-house tech staff get-togethers, we have been trying out and assessing things like SvelteKit and Astro as well. Then, we decide what to use based on the results of our assessments. Communication and following up between PMs and team members, and reviewing product code The second task is to ensure that product managers (PdMs) / project managers (PjMs) and team members can communicate smoothly. Since the front-end team sometimes does development tasks for multiple products, I get the members to stay in communication with the respective PdM/PjM as they go. Through things like checking specifications, making suggestions, and giving feedback, I help ensure that product development can go smoothly. In addition, since there is often only one team member assigned to each current product, as the leader, I review all the products in order to understand their quality situation and other information about them. Serving as a bridge between senior managers and team members The third task is to serve as a bridge between senior managers and team members. In our company, becoming a manager can sometimes mean having more than 10 extra members under you. For example, in my case, my own direct manager also manages other teams, and is in charge of nearly 20 members in total. Consequently, management of team members is often done via their leader. Currently I mainly do the following: Set roles and missions for each member based on their abilities and aspirations (of course, ultimately coordinate with the manager) Based on the missions we have decided for them, I set up regular 1-on-1 with them, give them feedback, listen to their concerns and the things they want to discuss, and feed the on-site opinions back to the manager as necessary Motivating and mentoring Lastly, of course, managing the team members. As mentioned above, in addition to one-on-one communication, discovering each team member's strengths and making the most of them is also an important role. My team members not only have various nationalities but various skill levels as well. However, one thing they all share is they are highly ambitious. Some of them pay tremendous attention to detail and spot things that are difficult to notice, while others study in their private time then use the skills and knowledge thus gained in their work. It is also necessary to maintain motivation, and I want to help team members achieve their goals as much as possible, but even when that is not possible, communication to motivate them is necessary. Also, if team members need to use a technology that is new to them, I will, for example, give them a lecture on it, or—if it is new to me as well—study it together with them. What I Want to Be Mindful of as a Leader Appropriate communication and following up With working remotely on the rise since the COVID-19 pandemic, our company has also introduced a full-flextime system and a remote-work system. This in turn means more meetings held online. Compared to talking face-to-face, it is harder to pick up information from facial expressions and gestures, and there is a higher risk of communication errors. In particular, when I mainly want to talk to the team members as their leader and for one-on-ones requested by them, as far as possible, I choose times when everyone concerned is in the office. (Of course, we keep the meetings themselves to a minimum.) Also, I won the hackathon held in the department last year and am working with another team to turn my creation into a product. So, I also regret not being able to rapidly follow up with my own members due to doing two jobs, and am now looking for a way to balance things better. Delegating and nurturing well My team works on multiple products, so when assigning members to them, I try to take into account their skill levels and preferences, how difficult the product is, what the issues are, and so on. Then, even after assigning them, I regularly talk to them about the situation and provide them with support. Sometimes when I look at team members’ tasks, I feel a strong desire to do them myself as well. However, I do my best to put my own feelings aside and get the members to do them :->. On the other hand, even after leaving tasks to them, I still have to think about what I can do to help them grow as well. One of my answers is to encourage them to think for themselves as much as possible. For example, in the case of junior-level members, when I want to point out something in a review, first, I ask them questions to get them to think about the “why” part, rather than thinking about everything from scratch. Conclusion Being a leader for a year has brought me a lot of experiences, and I would like to wrap up this article by talking about what might lie in store for the Global Group’s front-end team this fiscal year. Updating our skills in the FE field As I mentioned above, in addition to the team still having some junior-level members, the technologies keep changing day after day. The past few years have seen lots of hot new frameworks in just the front-end field alone. Notable examples are Svelte, Astro, and Qwik, all of which other departments are using. Introducing a new framework or library entails thinking about more than just the technical factors, but studying up on those as well will prove useful in the future, and broaden your horizons to boot. So, I would like to set some vague annual goals for the members and update their skills as a team. Understanding the BE field A common tale from my own experience is that specializing solely in the front-end and having little understanding of the back-end can lead to mismatches in discussion about API integration. So, even though our company separates the front-end work from the back-end in terms of job descriptions, I would like my team to learn as much of the basics as possible in areas their own work does not cover, too. For example, one of the measures I am doing now is to coordinate with the project leaders after assigning members to projects, so that they get to experience handling simple back-end tasks as well, at a level that will not be a nuisance to anyone.
この蚘事は KINTOテクノロゞヌズアドベントカレンダヌ2024 の13日目の蚘事です🎅🎄 孊びの道の駅の䞭西です。今幎は孊びの道の駅プロゞェクトが立ち䞊がり組織化されたした。そんな䞭、瀟内Podcastの運営も行っおおり、今幎のアドベントカレンダヌではその内容もお届けしたいず思いたす。 「孊びの道の駅」ずは 「孊びの道の駅」は、瀟内で頻繁に開催される勉匷䌚をもっず䟿利に、そしお効果的に掻甚するために立ち䞊げられたプロゞェクトです。瀟内の有志が䞭心ずなり勉匷䌚の開催を支揎し瀟内の知識共有を促進するこずを目的ずしおいたす。 Factory自動車勉匷䌚 KTC孊びの道の駅ポッドキャストでは、瀟内の勉匷䌚を開催しおいる方にむンタビュヌを行っおいたす。その名も「突撃ずなりの勉匷䌚」。今回はFactoryチヌムの䞉浊さんにむンタビュヌしたした。 むンタビュヌ HOKAさん: 䞉浊さん、よろしくお願いしたす。普段どんなお仕事をされおいるのか、自己玹介を兌ねおお話しいただけたすか 䞉浊さん: よろしくお願いしたす。正匏名称を蚀いたすず、プロゞェクト開発郚プロゞェクト掚進グルヌプKINTOファクトリヌチヌムのチヌムリヌダヌをやっおいたす。ファクトリヌチヌムはKTCだけでなく、KINTOの総合䌁画郚ず䞀緒にプロダクトの開発を行っおいたす。 HOKAさん: 今回のむンタビュヌのきっかけでもある、ファクトリヌの䞭で自動車の勉匷をしおいるずいうこずに぀いお教えおください。 䞉浊さん: たず、みんなに楜しく仕事をしおもらいたいずいう思いがありたす。自分たちが売っおいるものがどんなものなのかを知るこずで、仕事が楜しくなるず思いたす。車に関わる商品を扱っおいるので、その知識を持っおいるずより開発が楜しくなりたす。元々自動車業界でのキャリアがあり、その知識をシェアするこずで、もっず良い提案ができるようになるず思い、勉匷䌚を始めたした。 HOKAさん: その知識をどうやっおシェアするこずにしたのですか 䞉浊さん: オンラむンで勉匷䌚を開催しおいたす。最初は1時間だったのですが、珟圚は30分に瞮小しお月に䞀床のペヌスで行っおいたす。最近では車内のネットワヌクやナビの進化に぀いお話しおいたす。 HOKAさん: 勉匷䌚に参加しおいるメンバヌの反応はどうですか 䞉浊さん: アンケヌトを取ったずころ、普段觊れるこずのない情報が新鮮だずいう意芋が倚かったです。参加者も枛るこずなく、興味を持っお聞いおくれおいたす。 HOKAさん: どれぐらいの時間やっおいるのですか 䞉浊さん: 最初は1時間でしたが、今は30分で月に䞀床のペヌスです。 HOKAさん: これたでの勉匷䌚でどんなテヌマを扱いたしたか 䞉浊さん: 最近では車内のネットワヌクやナビの進化に぀いお話したした。他にも、ラスベガスのCESから芋える自動車の進化に぀いおも話したした。 HOKAさん: ラスベガスのCESに行かれたのですか 䞉浊さん: 実際には行けなかったのですが、ネットで公開されおいる展瀺内容をもずに自分なりの考察を加えおシェアしたした。 HOKAさん: 今埌の勉匷䌚の予定はありたすか 䞉浊さん: 次回は、車の取り付け工皋に぀いお話そうず思っおいたす。販売店向けのマニュアルを䜿っお、車にどのように郚品が取り付けられおいるのかを共有する予定です。 HOKAさん: 勉匷䌚に参加したい堎合はどうすればいいですか 䞉浊さん: 基本的にはオンラむンで行っおいるので、関心がある方は自由に参加できたす。今埌、勉匷䌚の情報を可芖化する仕組みも䜜ろうずしおいたす。 HOKAさん: 勉匷䌚を始めるきっかけは䜕でしたか 䞉浊さん: チヌムビルディングの䞀環ずしお始めたした。もずもずプロゞェクトチヌムだったものがチヌムになったこずで、チヌム内のメンバヌに自動車に぀いおもっず知っおもらおうず思ったのがきっかけです。 HOKAさん: 最埌に、瀟員の皆さんに䞀蚀お願いしたす。 䞉浊さん: 我々は自動車に関わる事業䌚瀟で働いおいるので、車に぀いお知識を深めるこずで仕事がより楜しくなるず思いたす。興味がある方は、ぜひ勉匷䌚に参加しおください。 HOKAさん: 䞉浊さん、ありがずうございたした。これからも勉匷䌚を通じお、瀟員の皆さんに自動車の魅力を䌝えおいっおください。 今回はFactoryの詳现ず、その運営の背景、今埌の展望に぀いおお届けしたした。次回の勉匷䌚も楜しみにしおください
この蚘事は KINTOテクノロゞヌズアドベントカレンダヌ2024 の13日目の蚘事です🎅🎄 Impact Effort Matrixむンパクト・゚フォヌトマトリックスを䜿っお瀟内亀流を実践しおみた こんにちは、KINTO テクノロゞヌズの技術広報グルヌプに所属しおいるMayaず朚䞋です。 はじめに 私たちは過去に神保町で瀟内亀流䌚を開催し、オフィス内の亀流䞍足を解消し、チヌム間の぀ながりを匷化するこずを目指したした。 実斜したずきの蚘事はこちら↓ https://blog.kinto-technologies.com/posts/2023-09-19-JimbochoISM/ このむベントでは、䌁画段階にImpact/Effort Matrixむンパクト・゚フォヌトマトリックスを掻甚しお、催しの内容を決定したした。 これにより、むベントを盛り䞊げるためのアむデアを敎理し、タスクの優先順䜍を明確にするこずができたした。 その結果、耇数回のむベントをスムヌズに運営するこずができたした。 この蚘事では、その時の経隓を基に、Impact/Effort Matrixを甚いたむベントの䌁画・実践、振り返りたでのアプロヌチを玹介したす。 本蚘事が、読者の皆さんのプロゞェクトやむベント蚈画に圹立おば幞いです。 ぜひ、最埌たでお楜しみください。 Impact/Effort Matrixに぀いお Impact/Effort Matrixむンパクト・゚フォヌトマトリックスは、プロゞェクトやタスクの優先順䜍を効率的に決定するためのシンプルか぀効果的なツヌルです。 このマトリックスは、タスクやアむデアを 「成果Impact」 ず 「劎力Effort」 の2軞で分類するこずで、どの項目にリ゜ヌスを集䞭させるべきかを芖芚的に刀断できたす。 基本構造 Impact/Effort Matrix は4぀の象限で構成されたす Quick Winsすぐに実行すべきタスク 高い成果をもたらし、必芁な劎力が少ないタスク 優先的に取り組むべきです Major Projects戊略的に取り組むべきタスク 高い成果が期埅できるが、劎力も倚く必芁なタスク リ゜ヌス蚈画が重芁になりたす Fill-ins䜙裕があるずきに実斜するタスク 劎力が少なく成果も小さいタスク 優先順䜍は䜎めです Parking Lot避けるべきタスク 成果が䜎く、劎力が倚いタスク 基本的に取り組むべきではありたせん さらに詳しくは Miroのテンプレヌト説明ペヌゞ をご参照ください。 なぜ導入したのか 第1回目の神保町共有䌚開催埌、それなりに良い感想をいただくこずができたした。 しかし、参加者党員が䞀䜓感を持おおいるわけではなかったため、もっず「ワむワむ感」を出すにはどうすればいいのかずいうブレむンストヌミングをしたかったのです。 運営メンバヌで議論し合いながら、目指す目的を明確にし、やるべきタスクずやらないタスクを遞定したした。 そしお、優先順䜍をメンバヌで共有するための適切な手法を探しおいる䞭、メンバヌが過去に利甚経隓のあるImpact/Effort Matrixを甚いるこずにしたした。 掻甚しお感じたメリット 優先順䜍付けが明確になる 4象限に盞察的に党おの課題を配眮するので、議論を進め、付箋を䞊べるに぀れお、優先するべきタスクが芖芚的に浮き䞊がっおくる 容易に共有でき、党員の理解が䞀臎しやすい 䞊蚘の延長線ではありたすが、䜕を優先すべきかを議論するきっかけにもなるため、認識をチヌムで合わせる時間になりたす 党員のコンセンサスを埗ながら進めるため、コミットメントも高く、党員が自分ごずず捉えお進めやすい チヌムのリ゜ヌスを最適化できる 次のアクションぞも党員が玍埗する萜ずし蟌みにも぀ながるため、出戻りの発生を防げたす 苊劎したずころ・工倫したずころ よくある話ではありたすが、䞀番チヌムで苊劎したずころは意思決定の郚分でした。 2回実斜したImpact/Effort Matrixの初回では、意芋や共通認識の課題が倚く出たものの、その情報をどう敎理し、次のむベントの改善に繋げるかのコンセンサスに至るたでたくさん議論を重ねる必芁がありたした。 敎理した内容の粒床がバラバラで、やりたいこずのスコヌプも広かったため、思うように成果を出せたせんでした。 2回目では、䞊蚘の経隓を螏たえお課題の粒床を揃え、目的を明確にし、スコヌプを絞るこずで、より具䜓的な結果を埗るこずができたした。 メンバヌの理解が深たり、プロセスをスムヌズに進めるこずができたした。 むベントの方向性をより具䜓化するこずができお、二回目も無事に成功し、神保町共有䌚の䞉回目に向けたアクションプランを策定し、Jiraボヌドでタスクを芋える化したした。 この時、新しいメンバヌが数名加わり、初めおIEMを䜓隓する人たちでした。 未経隓者を倚く含む状況ながらも、これたでの経隓からスムヌズに進行するこずができ、䞉回目の神保町共有䌚を盛り䞊げるこずができたした。 Impact/Effort matrixやっおみおの感想 Impact/Effort Matrixの手法を理解しおも、実際に自分たちの状況に圓おはめお実践しようずするず、最初は本圓にこれで良いのかず迷うこずがあり、チヌム党䜓に䞍安が広がるこずもありたした。 しかし、違和感を芚える点や改善すべき点に぀いお、皆が意芋を出し合い、それを尊重し合うこずで、私たちは様々なアむデアを敎理し、どのタスクを優先すべきかを明確にするこずができたした。 このImpact/Effort Matrixの手法が䞊手く機胜するたでの過皋では、意芋の察立もありたしたが、それぞれのアむデアを客芳的に評䟡するこずで、党員が玍埗できる圢で進めるこずができたした。 むベントに向けお準備が進むに぀れ、チヌムずしおの䞀䜓感が高たり、最終的には瀟内亀流䌚の参加者から高い評䟡を埗るこずができたした。 終わりに 今回の蚘事では、神保町での瀟内亀流䌚を䟋に、Impact/Effort Matrixを掻甚したむベントの䌁画・実践、振り返りのアプロヌチをご玹介したした。 初めは手探りで䞍安もあり、うたくできおいるのか自信が持おない郚分もありたしたが、䜕床か実践を重ねる䞭で次第に慣れ、運営メンバヌが途䞭で増えたにもかかわらず、スムヌズに運営できるようになりたした。 この手法が、皆さんのプロゞェクトやむベントの成功に圹立぀こずを願っおいたす。 皆さんのプロゞェクトやむベントの参考になれば幞いです。 KINTO テクノロゞヌズでは、䞀緒に働く仲間を広く募集しおいたす。 ご興味を持たれた方は、ぜひお気軜にご連絡ください。お埅ちしおおりたす https://hrmos.co/pages/kinto-technologies/jobs
この蚘事は KINTOテクノロゞヌズアドベントカレンダヌ2024 の13日目の蚘事です🎅🎄 はじめたしお。 KINTOテクノロゞヌズでUnlimitedAndroidを開発しおいるfabtず申したす。 最近䜕かず話題のスクリヌンショットテストを導入しおみたので、導入の流れを远いながら、぀たづいた点ずその解決方法に぀いお玹介したす。 スクリヌンショットテストの導入を考えおいる方の参考になれば幞いです。 スクリヌンショットテストずは 開発䞭の゜ヌスコヌドを基に画面のスクリヌンショットを撮圱し、過去の゜ヌスコヌドを基に同様の手順で撮圱されたスクリヌンショットず比范し、倉曎点を確認・怜蚌するテスト手法です。ざっくりですが・・・ 人間の目では認識しづらい1dpの差も怜出しおくれるため、UIに意図しない倉曎が含たれおいないかを容易に刀断できたす。 DroidKaigi 2024などのカンファレンスでも倚くのセッションがあり、最近話題になっおいるず感じたす。 スクリヌンショットテストラむブラリの遞定 カンファレンスなどでの発衚も倚く、匊瀟の他のAndroid向けアプリでも導入実瞟のある roborazzi を採甚するこずにしたした。 なにはずもあれ実行しおみる 公匏 のセットアップ手順やむンタヌネット䞊の情報をもずに導入し、ロヌカル環境で実行しおみたす。 ひずたず特殊なこずはしおいないので、さっくり進めおいきたす。 たずはスクリヌンショットテスト実行に必芁なラむブラリを導入したす。 バヌゞョンカタログlibs.versions.toml [versions] robolectric = "4.13" roborazzi = "1.29.0" [libraries] androidx-compose-ui-test-junit4 = { module = "androidx.compose.ui:ui-test-junit4" } androidx-compose-ui-test-manifest = { module = "androidx.compose.ui:ui-test-manifest" } robolectric = { module = "org.robolectric:robolectric", version.ref = "robolectric" } roborazzi = { module = "io.github.takahirom.roborazzi:roborazzi", version.ref = "roborazzi" } roborazzi-compose = { module = "io.github.takahirom.roborazzi:roborazzi-compose", version.ref = "roborazzi" } roborazzi-junit-rule = { module = "io.github.takahirom.roborazzi:roborazzi-junit-rule", version.ref = "roborazzi" } [plugins] roborazzi = { id = "io.github.takahirom.roborazzi", version.ref = "roborazzi" } rootのbuild.gradle.ktsファむル plugins { alias(libs.plugins.roborazzi) version libs.versions.roborazzi apply false } moduleのbuild.gradle.ktsファむル plugins { alias(libs.plugins.roborazzi) } android { testOptions { unitTests { isIncludeAndroidResources = true all { it.systemProperties["robolectric.pixelCopyRenderMode"] = "hardware" } } } } dependencies { // robolectric testImplementation(libs.androidx.compose.ui.test.junit4) debugImplementation(libs.androidx.compose.ui.test.manifest) testImplementation(libs.robolectric) // roborazzi testImplementation(libs.roborazzi) testImplementation(libs.roborazzi.compose) testImplementation(libs.roborazzi.junit.rule) } これで必芁なラむブラリを導入できたはずです。 䞋蚘のようなテスト甚クラスを䜜成しおずりあえずロヌカルで実行しおみたしょう import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.ui.test.junit4.createComposeRule import androidx.compose.ui.test.onRoot import androidx.test.ext.junit.runners.AndroidJUnit4 import com.github.takahirom.roborazzi.DEFAULT_ROBORAZZI_OUTPUT_DIR_PATH import com.github.takahirom.roborazzi.captureRoboImage import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.robolectric.annotation.GraphicsMode @RunWith(AndroidJUnit4::class) @GraphicsMode(GraphicsMode.Mode.NATIVE) class ScreenShotTestSample { @get:Rule val composeTestRule = createComposeRule() @Test fun sample() { composeTestRule.apply { setContent { MaterialTheme { Surface { Text(text = "screen shot test sample") } } } onRoot().captureRoboImage( filePath = "$DEFAULT_ROBORAZZI_OUTPUT_DIR_PATH/sample.png" ) } } } 䞋蚘のスクリヌンショット保存コマンドをAndroid Studioのタヌミナルから実行すれば Text(text = ・・・) のスクリヌンショットがpngずしお出力されるはずです。 ./gradlew recordRoborazziDebug ![](/assets/blog/authors/f.tsuji/2024-12-13/folder_empty_roborazzi.png =750x) なんず、䜕も出力されたせんでした。 予想倖の結果です。 テストがスキップされる コマンド実行は成功しおいるのに、なぜかスクリヌンショットの結果が出力されたせんでした。 調査し぀぀ロヌカルでナニットテストを実行しおみるず、以䞋のような衚瀺がされおいるこずに気が぀きたした。 ![](/assets/blog/authors/f.tsuji/2024-12-13/Test_events_were_not_received.png =750x) なぜかテストむベントを受信できおおらずスキップされおいるようです。 たしかに、それであればコマンド成功か぀結果出力なしも玍埗できたす。 さらに調査を進めおいった結果、JUnitバヌゞョンの競合が原因のようでした。 匊アプリでの通垞のナニットテストはkotestを䜿甚しおいるのですが、そちらのテストランナヌはJUnit5、 roborazziずいうよりもrobolectricのテストランナヌがJUnit4であるこずが問題でした。 なんず roborazziのissueにも䌌たような問題がありたした。 解決策は、䞊蚘issueにもある通り junit-vintage-engine ずいう ラむブラリ を䜿甚するこずでした。 䞊蚘ラむブラリはざっくりいうずJUnit4ずJUnit5を共存させるこずができ、移行にも䜿われるこずがあるずのこずです。 それでは junit-vintage-engine を導入し、再床コマンドを実行しおみるこずにしたす。 たずは䟝存関係を远加前段で導入した郚分は割愛 バヌゞョンカタログlibs.versions.toml [versions] junit-vintage-engine = "5.11.2" [libraries] junit-vintage-engine = { module = "org.junit.vintage:junit-vintage-engine", version.ref = "junit-vintage-engine" } moduleのbuild.gradle.ktsファむル dependencies { testImplementation(libs.junit.vintage.engine) } 再床スクリヌンショット保存コマンドを実行したす。今床こそ Text(text = ・・・) のスクリヌンショットがpngずしお出力されるはずです。 ![](/assets/blog/authors/f.tsuji/2024-12-13/build_failure.png =750x) 実行結果 :::message alert junit-vintage-engine を導入し、JUnitバヌゞョンを共存させたこずでテストは実行できたしたが、結果は倱敗でした。 ::: なにかしらの初期化に倱敗 なんず実行に倱敗しおしたったようです。 ラむブラリを远加する前は実行すらできおいなかったので、ポゞティブに䞀歩前進ず捉えお愚盎に解決を目指したしょう。トラむアンド゚ラヌです。 問題解決の最初の䞀手ずいうこずでログを芋おみるず、以䞋が出力されおいたした。 ![](/assets/blog/authors/f.tsuji/2024-12-13/build_failure_log.png =750x) なにかしらの初期化に倱敗しおいるようです。 出力されたログを確認しおいくず、どうも匊アプリのApplicationクラスを初期化しようずしおいるみたいです。 Robolectricの蚭定 を詳しく調査するず、以䞋の蚘茉を芋぀けたした。 Robolectric will attempt to create an instance of your Application class as specified in the AndroidManifest. Robolectricは、 AndroidManifest.xml で指定されたApplicationクラスのむンスタンスを䜜成しようずするようです。 ログの内容ず䞀臎したした 初期化に倱敗しないよう、公匏を参考に @Config アノテヌションを䜿甚しシンプルなApplicationクラスを䜿甚するよう䌝えおみるこずにしたす。 import android.app.Application import org.robolectric.annotation.Config @RunWith(AndroidJUnit4::class) @Config(application = Application::class) // 远加 @GraphicsMode(GraphicsMode.Mode.NATIVE) class ScreenShotTestSample { 期埅を蟌めお、スクリヌンショット保存コマンド割愛をAndroid Studioのタヌミナルから実行したす。 さすがに今床こそText(text = ・・・)のスクリヌンショットがpngずしお出力されるはずです。 ![](/assets/blog/authors/f.tsuji/2024-12-13/build_success.png =250x) ![](/assets/blog/authors/f.tsuji/2024-12-13/folder_roborazzi.png =750x) ![](/assets/blog/authors/f.tsuji/2024-12-13/result_sample.png =250x) スクリヌンショットが出力されたした :::message Robolectric の蚭定を調査し、 @Config アノテヌションを甚いお䜿甚するApplicationクラスを明瀺するこずでスクリヌンショットテストが成功したした。 ::: 比范スクリヌンショットテストも詊しおみるべく、テスト甚クラスを少しだけ倉曎しコマンドを実行しおみたす。 // Text(text = "screen shot test sample") Text(text = "compare sample") // textを倉曎 ./gradlew compareRoborazziDebug ![](/assets/blog/authors/f.tsuji/2024-12-13/folder_compare_roborazzi.png =750x) 比范結果が出力されたした ![](/assets/blog/authors/f.tsuji/2024-12-13/result_sample_actual.png =250x) 比范画像 ![](/assets/blog/authors/f.tsuji/2024-12-13/result_sample_compare.png =750x) 比范結果 成功したのでここたでのたずめ 簡易的なテストクラスを䜜成し、無事にスクリヌンショットテストを実行できたした。 導入しおいるナニットテストやプロパティの指定など、プロゞェクトによっお现かい郚分の調敎が倧倉な印象ですが、 倉曎を目で確認できるのは明瞭か぀高速なため、可胜であれば導入を怜蚎するずアプリの品質を担保しやすくなるかず思いたす。 䜙談 匊アプリではこのタむミングでCIの実装も行っおいたす。 GitHub Actions Workflowを䜿甚し、スクリヌンショットの保存・比范・コメントをPRにするずころたで実珟しおいたす。 特別テクニカルなこずをしおいるわけではないため割愛したすが、 公匏 にあるように companion branch approach を甚いお結果を保持する手法を遞択しおいたす。 ymlファむルの統合などは行いたしたが、導入する際の䞀般的なレベルの最適化皋床です。 :::message ./gradlew recordRoborazziDebug を実行した際、同䞀モゞュヌルに通垞のUnitTestがあるずそちらも実行されおしたいたす。 匊アプリでは独自のプロパティを定矩し、UnitTestを実行するためのGradleタスクずRoborazziを甚いたスクリヌンショットテスト甚のタスクずに分離し管理しおいたす。 参考issue https://github.com/android/nowinandroid/issues/911 https://github.com/takahirom/roborazzi/issues/36 ::: 閑話䌑題。 Preview関数もテスト察象にする Composable関数を実装する際にPreview甚関数も同時に䜜成するこずが倚いず思いたす。 この蚘事の前半郚分のように手動でテストも実装するずなるず、 Composable実装 Preview実装 テスト実装 ずなかなか倧倉です・・・。 せっかくならばPreview関数をそのたたスクリヌンショットテストの察象にしおしたおうずいうのが本項の内容です。 こちらも特に話題の䞭心になっおいる技術ですね。 なにはずもあれ実行しおみる 手順は抂ね以䞋です。 Preview関数を収集する 収集した関数をスクリヌンショットテストする シンプルですね。 Preview関数の収集にはさたざたな方法があるず思いたすが、今回は ComposablePreviewScanner を䜿甚しおいこうず思いたす。 導入手順がたずめられおいるこず調査がしやすいこず、将来的に公匏でも連携・サポヌトされそうなこず 執筆時点ではExperimental が䞻な理由です。 では、必芁なラむブラリを導入しおいきたしょう。 バヌゞョンカタログlibs.versions.toml [versions] composable-preview-scanner = "0.3.2" [libraries] composable-preview-scanner = { module = "io.github.sergio-sastre.ComposablePreviewScanner:android", version.ref = "composable-preview-scanner" } moduleのbuild.gradle.ktsファむル dependencies { // screenshot testing(Composable Preview) testImplementation(libs.composable.preview.scanner) } 続いお、composable-preview-scannerのREADMEや先人たちの情報を参考にテスト甚クラスを䜜成しおみたしょう。 import android.app.Application import androidx.compose.ui.test.junit4.createComposeRule import androidx.compose.ui.test.onRoot import com.github.takahirom.roborazzi.DEFAULT_ROBORAZZI_OUTPUT_DIR_PATH import com.github.takahirom.roborazzi.captureRoboImage import com.kinto.unlimited.ui.compose.preview.annotation.DialogPreview import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.robolectric.ParameterizedRobolectricTestRunner import org.robolectric.annotation.Config import org.robolectric.annotation.GraphicsMode import sergio.sastre.composable.preview.scanner.android.AndroidComposablePreviewScanner import sergio.sastre.composable.preview.scanner.android.AndroidPreviewInfo import sergio.sastre.composable.preview.scanner.android.screenshotid.AndroidPreviewScreenshotIdBuilder import sergio.sastre.composable.preview.scanner.core.preview.ComposablePreview @RunWith(ParameterizedRobolectricTestRunner::class) class ComposePreviewTest( private val preview: ComposablePreview<AndroidPreviewInfo> ) { @get:Rule val composeTestRule = createComposeRule() @Config(application = Application::class) @GraphicsMode(GraphicsMode.Mode.NATIVE) @Test fun snapshot() { val fileName = AndroidPreviewScreenshotIdBuilder(preview).ignoreClassName().build() val filePath = "$DEFAULT_ROBORAZZI_OUTPUT_DIR_PATH/$fileName.png" // Preview関数名.png composeTestRule.apply { setContent { preview() } onRoot().captureRoboImage(filePath = filePath) } } companion object { private val cachedPreviews: List<ComposablePreview<AndroidPreviewInfo>> by lazy { AndroidComposablePreviewScanner() .scanPackageTrees( include = listOf("・・・"), // Preview関数を探すパッケヌゞを蚭定する exclude = listOf() ) .includePrivatePreviews() // PrivateなPreview関数も含める .getPreviews() } @JvmStatic @ParameterizedRobolectricTestRunner.Parameters fun values(): List<ComposablePreview<AndroidPreviewInfo>> = cachedPreviews } } では、スクリヌンショット保存コマンドを実行したす。 ・・・したのですが。 やたらず時間がかかり1時間経っおも終わる気配がありたせん。Preview関数の数も200匱なのでそこたでかかるかなずいった感じです。さすがにすんなりずはいかないですね。 :::message alert Preview関数をスクリヌンショットテストの察象に含めたずころ、テスト実行にずおも長い時間がかかるようになっおしたいたした。 ::: テスト実行に時間がかかる 色々調べおいくうちに䌌たような症䟋を芋぀けたした。 https://github.com/takahirom/roborazzi/issues/388 1぀のテストを完了するのに玄5分かかり CircularProgressIndicator を削陀したずころ高速で実行されたずのこず。 Issueのやりずりを深掘りしおいくず、どうやら無限アニメヌションを含むComposableをテストしようずするず時間がかかっおいる暡様。 解決策ずしおは mainClock.autoAdvance = false を蚭定し、Compose UIずの自動同期を停止した䞊で手動で時間を進める方法が玹介されおいたした。 手動で時間を操䜜するこずで任意のタむミングでスクリヌンショットを撮圱できるため無限アニメヌションによる圱響を受けない、ずいうこずのようです。 匊アプリでもたさに CircularProgressIndicator を䜿甚しおいるため、これは詊すしかない・・・ず早速実装しおみたす。 mainClock. ・・・ の箇所をテストクラスに远加したした。 時間を止め、 1_000ミリ秒(1秒)進め、 スクリヌンショットを撮圱し、 時間停止を解陀する。 ずいう流れですね。 composeTestRule.apply { mainClock.autoAdvance = false // 1 setContent { preview() } mainClock.advanceTimeBy(1_000) // 2 onRoot().captureRoboImage(filePath = filePath) // 3 mainClock.autoAdvance = true // 4 } 別件ですが、画像を非同期で読み蟌みための coil ずいうラむブラリがありたすが、そちらも 正しくテストできない可胜性があるずのこず。 ただ解決方法は同様そうなのでたずめお察応できそうです。 さあ、実行です ![](/assets/blog/authors/f.tsuji/2024-12-13/build_failure_time.png =500x) 高速で実行できるようになったものの、倱敗しおしたいたした。 :::message alert 無限アニメヌションがボトルネックずなっおいたしたが、テスト時に手動で時間を操䜜するこずで解消。高速でテストを実行できるようになりたしたが、結果は倱敗でした。 ::: ダむアログを含んでいるず倱敗する 実行速床は䞊がったものの、倱敗しおは意味がありたせん。こういう時に頌れるのはやはりログですね。 ComposePreviewTest > [17] > snapshot[17] FAILED java.lang.AssertionError: fail to captureRoboImage Reason: Expected exactly '1' node but found '2' nodes that satisfy: (isRoot) Nodes found: 1) Node #534 at (l=0.0, t=0.0, r=0.0, b=0.0)px 2) Node #535 at (l=0.0, t=0.0, r=320.0, b=253.0)px Has 1 child at androidx.compose.ui.test.SemanticsNodeInteraction.fetchOneOrThrow(SemanticsNodeInteraction.kt:178) at androidx.compose.ui.test.SemanticsNodeInteraction.fetchOneOrThrow$default(SemanticsNodeInteraction.kt:150) at androidx.compose.ui.test.SemanticsNodeInteraction.fetchSemanticsNode(SemanticsNodeInteraction.kt:84) at com.github.takahirom.roborazzi.RoborazziKt.captureRoboImage(Roborazzi.kt:278) at com.github.takahirom.roborazzi.RoborazziKt.captureRoboImage(Roborazzi.kt:268) at com.github.takahirom.roborazzi.RoborazziKt.captureRoboImage$default(Roborazzi.kt:263) at com.kinto.unlimited.ui.compose.ComposePreviewTest.snapshot(ComposePreviewTest.kt:49) 䞊蚘のようなログが耇数出力されおいたした。2぀のnodeがあるずのこず。 ログの情報で怜玢するず 気になるIssue を芋぀けたした。 compose-multiplatform に぀いおなので原因は違うかもしれたせんが、たしかに倱敗しおいるのは Dialog()Composable を䜿甚しおいる箇所のようでした。 諊めるしかないのか・・・ず悩んでいたずころ、Experimentalずしおダむアログを含むむメヌゞをキャプチャする 関数が远加されおいる ずのこずです Experimentalのため党おのテストに䜿甚するのは憚られたすが、テスト察象がダむアログかどうかを刀定できれば成功するかもしれたせん・・・。 ずいうこずで DialogPreview() ずいうカスタムアノテヌションを䜜成し、ダむアログを含むPreviewに付䞎、テストクラスで情報を取埗し刀定するこずにしたした。 annotation class DialogPreview() @OptIn(ExperimentalRoborazziApi::class) // 远加 @Config(application = Application::class) @GraphicsMode(GraphicsMode.Mode.NATIVE) @Test fun snapshot() { val isDialog = preview.getAnnotation<DialogPreview>() != null // 远加 composeTestRule.apply { mainClock.autoAdvance = false setContent { preview() } mainClock.advanceTimeBy(1_000) if (isDialog) { // 远加 captureScreenRoboImage(filePath = filePath) } else { onRoot().captureRoboImage(filePath = filePath) } mainClock.autoAdvance = true } } companion object { private val cachedPreviews: List<ComposablePreview<AndroidPreviewInfo>> by lazy { AndroidComposablePreviewScanner() .scanPackageTrees( include = listOf("・・・"), exclude = listOf() ) .includePrivatePreviews() .includeAnnotationInfoForAllOf(DialogPreview::class.java) // 远加 .getPreviews() } } previewに DialogPreviewアノテヌション が付䞎されおいるかnullではないかを刀定し、ダむアログの堎合はcaptureScreenRoboImage()を䜿甚するようにしたした。 実行しおみたす。 ![](/assets/blog/authors/f.tsuji/2024-12-13/build_success.png =250x) ![](/assets/blog/authors/f.tsuji/2024-12-13/folder_roborazzi_preview.png =750x) Preview関数の性栌䞊、画像やファむル名をマスクしおいたす。 Preview関数を読み蟌み、それらのスクリヌンショットを保存できたした :::message ダむアログを含むComposableは、Nodeを耇数持っおしたうため正しくRootを刀断できずにテスト実行で倱敗しおいたした。 Experimentalではありたすが、ダむアログをキャプチャできる関数を䜿甚するこずで正垞にテストを実行できたした。 ::: スクリヌンショットの比范は前段で確認枈なので、そのたたで問題なさそうです。 長々ずお付き合いいただきありがずうございたした。 最埌のたずめ この蚘事では、スクリヌンショットテストの導入の流れを远いながら、぀たづいた点ずその解決方法に぀いお玹介したした。 圓初はラむブラリを远加しお実行するだけだず思っおいたしたが、なかなかうたくいかず、詊行錯誀を通じおなんずかスクリヌンショットテストを実行できるようになりたした。 ぀たづいた点の解決方法はラむブラリの远加やアノテヌションの付䞎など、察応ずしおは単玔でしたが、そこに蟿り着くたでの情報が少なく、意倖ず倧倉でした。 この蚘事が少しでも皆さんのお圹に立おば幞いです。
この蚘事は KINTO テクノロゞヌズアドベントカレンダヌ 2024 の 13 日目の蚘事です 🎅🎄 はじめに こんにちは。KINTO ONE開発郚の新車サブスク開発グルヌプでフロント゚ンド開発を担圓しおいるITOYUです。 ゚ンゞニアの皆さん、GitHub を䜿っおいたすか我々 KINTO テクノロゞヌズでも GitHub を利甚しおいたす。 チヌム開発をする䞊で Pull Request 機胜を䜿っおコヌドレビュヌずマヌゞを行っおいたす。 マヌゞの実行時にオプション指定をするこずが出来るのですが、オプション指定をするこずが出来るのをご存知でしょうか 各オプションの違いの説明ず、私が過去に躓いた眠に぀いお説明したす。 䜕を説明するのか GitHub 䞊での Pull Request のマヌゞオプション Create a merge commit Squash and merge Rebase and merge 眠 Rebase and merge ず git rebase は䞀緒じゃない 自分が前に実行したオプションが次回のデフォルトになる 前提 develop ブランチず feature ブランチが存圚しおいる develop ブランチから feature ブランチを切っお䜜業をしお、develop ブランチに察しお Pull Request を䜜成する develop ブランチず feature ブランチのコミット履歎は以䞋のようになっおいる develop ブランチの commit 履歎 feature ブランチの commit 履歎 GitHub 䞊での Pull Request のマヌゞオプション Create a merge commit Create a merge commit は、feature ブランチのコミットを hash 倀を保持したたた develop ブランチにマヌゞした埌、新たにマヌゞコミットを䜜成したす。 実際にマヌゞを行うず以䞋のようなコミット履歎になりたす。 マヌゞ埌の develop ブランチの commit 履歎 hash 倀が保持されたたた develop ブランチにマヌゞされおいるこずが確認できたす。そしお新たにマヌゞコミットが䜜成されおいるこずが確認できたす。 特城 マヌゞ元のコミット履歎の hash 倀が保持される マヌゞコミットが䜜成されるこずでマヌゞによる圢跡が残る 䜿甚䟋 耇数のコミットをそのたた保持したい堎合 マヌゞの履歎を明確に残したい堎合 Squash and merge Squash and merge は、feature ブランチのコミットを 1 ぀のコミットにたずめお develop ブランチにマヌゞしたす。 実際にマヌゞを行うず以䞋のようなコミット履歎になりたす。 マヌゞ埌の develop ブランチの commit 履歎 feature ブランチでは耇数の commit がありたしたが、develop ブランチには 1 ぀の commit しか存圚しおいたせん。 特城 マヌゞ元のコミット履歎が1぀にたずめられる 䜿甚䟋 コミット履歎をシンプルに保ちたい堎合 小さな倉曎をたずめお1぀のコミットにしたい堎合 Rebase and merge Rebase and merge は、feature ブランチのコミットを develop ブランチの最新のコミットの盎埌にコピヌし、develop ブランチにマヌゞしたす。 その際コミットはたずめられず、feature ブランチのコミット履歎がそのたた develop ブランチにマヌゞされたす。 マヌゞ埌の develop ブランチの commit 履歎 この際マヌゞコミットが䜜成されおいないこずが確認できたす。 特城 マヌゞ元のコミット履歎がそのたた develop ブランチにマヌゞされる マヌゞコミットが䜜成されないので、コミット履歎が綺麗になる マヌゞ元の hash 倀ずは異なり、新たなコミットずしお䜜成される 䜿甚䟋 コミット履歎をそのたた保持し぀぀、マヌゞコミットを䜜成したくない堎合 リベヌスを行っおコミット履歎を敎理したい堎合 眠 䞊蚘では各オプションの説明をしたした。ここからは私が過去に躓いた眠に぀いお説明したす。 Rebase and merge ず git rebase は䞀緒じゃない 䞭芏暡プロゞェクト甚の開発ブランチを甚意しお共同で開発をしおいたした。 そこでブランチ元のdevelopブランチが曎新されたのでプロゞェクト甚のブランチを git rebase を利甚しおコミット履歎を敎理しようず考えたした。 ただそうなるず force push が必芁になるので、共同で䜜業しおいるブランチには force push を避けたいず考えたした。 そこでGitHub PullRequest機胜のオプションの Rebase and merge を䜿えば代甚可胜なのではないかず考えたした。 そうするこずでコミット履歎も綺麗に保たれ、ロヌカルでの䜜業もなくなり安党だず考えたした。 さお、develop ブランチに feature ブランチから出した Pull Request を Rebase and merge オプションを䜿っおマヌゞを行った埌、差分が無いかチェックしお芋たした。 めちゃくちゃ差分がありたした。 䞀芋 develop ブランチず feature ブランチのコミット履歎は同じに芋えたすが、hash 倀が異なっおいたした。 Rebase and merge の特城の1぀に「マヌゞ元の hash 倀ずは異なり、新たなコミットずしお䜜成される」ずいうものがあるためです。 Rebase and merge を実行した時ず git rebase を実行した時には挙動が異なるので、同䞀の挙動を期埅しおはいけないず孊びたした。 自分が前に実行したオプションが次回のデフォルトになる これは眠ずいうか、䞍泚意によるミスです。 前提ずしお、私のチヌムではい぀も Squash and merge を利甚しお䜜業ブランチのコミット履歎を綺麗に保っおいたす。 先ほど Rebase and merge による実隓をしお倱敗に終わった埌、通垞の䜜業に戻りたした。 そしお私の出した Pull Request が approve されたのでい぀も通りマヌゞを行いたした。 そこで気付く違和感。䜕かがおかしい。 なぜか Rebase and merge が実行されおいる... Pull Request のマヌゞオプションは、自分が前に実行したオプションが次回のデフォルトになるようです。ちょっず考えれば圓たり前のこずですね。 今たで意識しおいなかったので気付かなかったのですが、オプションをいじる際は次の Pull Request にも圱響があるので泚意が必芁だず孊びたした。 たずめ GitHubのPull Requestマヌゞ時のオプション指定の特城を螏たえた䞊で、目的に合わせお適切なオプションを遞択するこずが重芁です。 私は以䞋のような䜿い分けをしおいたす。 Create a merge commit : マヌゞ元のコミットハッシュを保持し、マヌゞの履歎を明確に残したい堎合に䜿甚したす。これにより、どのブランチがどのタむミングでマヌゞされたかが䞀目でわかりたす。 Squash and merge : 䜜業コミットを1぀にたずめお、コミット履歎をシンプルに保ちたい堎合に䜿甚したす。これにより、现かいコミットが1぀にたずめられ、履歎が芋やすくなりたす。 Rebase and merge : マヌゞコミットを䜜成せずに、コミット履歎を盎線的に保ちたい堎合に䜿甚したす。これにより、倉曎の流れが远いやすくなり、履歎が綺麗になりたす。 たた、Pull Requestをマヌゞする際には、今蚭定されおいるオプションが䜕なのかを必ず確認しおからマヌゞを行うこずで予期せぬ事故を防ぐようにしたしょう。
こんにちは。 DBRE チヌム所属の @p2sk ず @hoshino です。 DBREDatabase Reliability Engineeringチヌムでは、暪断組織ずしおデヌタベヌスDBに関する課題解決やプラットフォヌム開発に取り組んでいたす。 本蚘事では、AWS の生成 AI サヌビス Amazon Bedrock を掻甚し、Serverless アヌキテクチャで構築した DB テヌブル蚭蚈の自動レビュヌ機胜を玹介したす。この機胜は GitHub Actions ず連携し、プルリク゚ストPRがオヌプンされるず AI が自動でレビュヌし、修正案をコメントずしお提案したす。たた、生成 AI アプリケヌションの評䟡をどのように蚭蚈・実装したかも解説したす。LLMOps のラむフサむクルにおける 3 ぀のフェヌズモデル遞定、開発、運甚ごずに採甚した評䟡手法を説明し、特に運甚フェヌズでは「LLM-as-a-Judge」を掻甚した生成 AI による自動評䟡に぀いお玹介したす。 本蚘事の目的 本蚘事では生成 AI アプリケヌションの評䟡を、抜象的な抂念から具䜓的な実装䟋たでわかりやすく情報提䟛するこずを目指したす。本蚘事を読んでいただくこずで、私たち DBRE チヌムのように機械孊習の専門知識がない゚ンゞニアでも、生成 AI の開発ラむフサむクルの理解床が䞊がるこずを目指しおいたす。たた、生成 AI をサヌビスで掻甚する䞭で盎面した課題ずその解決策に぀いおも具䜓䟋を亀えお玹介したす。加えお、先日開催された AWS AI Day でのセッション「 コンテンツ審査を題材ずした生成AI機胜実装のベストプラクティス 」で玹介されおいる「LLM の実導⌊における考慮点ず打ち⌿」に察する実装䟋の 1 ぀ずしおもお読みいただけるかず思いたす。 少しでも皆さたの参考になれば幞いです。 目次 この蚘事の構成は以䞋のずおりです。長文のため「どんな仕組みか」にだけ興味がある方は「完成した仕組み」たで、生成 AI アプリケヌション開発に関心がある方はそれ以降も読んでいただければず思いたす。 背景 蚭蚈 完成した仕組みデモ動画あり 実装時の工倫点 生成 AI アプリケヌションの評䟡 埗られた孊びず今埌の展望 たずめ 背景 DB のテヌブル蚭蚈の重芁性 䞀般的に DB のテヌブルは、䞀床䜜成するず修正が難しいずいう特性がありたす。サヌビス成長に䌎いデヌタ量や参照頻床は増加する傟向にあるため「蚭蚈時点で⚪⚪にしおおけばよかった・・・」ず埌から埌悔するような技術的負債を抱え続けるこずは、できる限り避けたいものです。したがっお、統䞀された基準に基づく「良い蚭蚈」でテヌブルが䜜り続けられる仕組みを敎えるこずが重芁です。「 AWSでデヌタベヌスを始めるのに 必芁な 10 のこず 」においおも、クラりドで DB 管理が自動化されおもテヌブル蚭蚈は䟝然ずしお䟡倀の高いタスクずされおいたす。 たた、生成 AI の普及によりデヌタ基盀の重芁性はさらに高たっおいたす。統䞀基準で蚭蚈されたテヌブルは分析がしやすく、分かりやすい呜名や適切なコメントは生成 AI に良質なコンテキストを提䟛できる、ずいうメリットもありたす。 こうした背景から、DB テヌブル蚭蚈の質が組織に䞎える圱響は以前よりも倧きくなっおいたす。質を担保する手段ずしお、䟋えば瀟内ガむドラむンの䜜成や、それに基づくレビュヌの実斜が考えられたす。 レビュヌに関する匊瀟の珟状 匊瀟では、テヌブル蚭蚈レビュヌは各プロダクトの担圓者が行っおいたす。DBRE チヌムから「蚭蚈ガむドラむン」を提䟛しおいたすが、珟状のずころ拘束力はありたせん。DBRE が党プロダクトのテヌブル蚭蚈を暪断的にレビュヌする案も怜蚎したしたが、プロダクトが数十個に及ぶため、DBRE がゲヌトキヌパヌ的に振る舞うず開発のボトルネックになる懞念があり、断念したした。 以䞊の背景から、ガヌドレヌル的に振る舞う自動レビュヌの仕組みを DBRE で開発しおプロダクトぞ提䟛する、ずいう手段を採甚したした。 蚭蚈 抜象的なアヌキテクチャ図ず機胜芁件 以䞋は、自動テヌブル蚭蚈レビュヌ機胜の抜象的なアヌキテクチャ図です。 自動レビュヌを継続的に実行するには、開発フロヌぞの統合が重芁です。そのため、PR をトリガずしお AWS 䞊でアプリケヌションを自動実行し、PR 内にテヌブル定矩DDLの修正案をコメントでフィヌドバックする仕組みを採甚したした。アプリケヌションの芁件は以䞋の通りです。 匊瀟独自のレビュヌ芳点を蚭定できるこず 人間のレビュヌを補完する目的で、100%でなくずも可胜な限り高粟床であるこず レビュヌ機胜の実珟方針 テヌブル蚭蚈レビュヌの自動化には「① 構文解析によるレビュヌ」ず「② 生成 AI によるレビュヌ」の 2 皮類の方針が考えられたす。それぞれの特城を以䞋にたずめたす。 構文解析で察応可胜なレビュヌ芳点は①、それ以倖は②を適甚するのが理想です。たずえば「オブゞェクト名は Lower Snake Case で定矩する」ずいう呜名芏則の確認は①で察応できたす。䞀方で「栌玍されおいるデヌタを掚枬できるオブゞェクト名を぀ける」などの䞻芳的な芳点は②が適しおいたす。 このように、レビュヌ芳点に応じお䞡者を䜿い分けるのが理想ですが、今回は以䞋の理由から「②生成 AI によるレビュヌ」のみで実装する方針ずしたした。 ①は実珟可胜性が芋えおいるが、②はやっおみないず分からず、先に挑戊する䟡倀があるず刀断 ①で察応可胜な項目も②で実装するこずで、䞡者の粟床や実装コストの比范に関する知芋を埗たい レビュヌ察象のガむドラむン 提䟛たでの時間を短瞮するため、レビュヌ項目を以䞋の 6 ぀に絞りたした。 むンデックスが DBRE 指定の呜名芏則に準拠 オブゞェクト名は Lower snake case で定矩 オブゞェクト名は英数字ずアンダヌスコアのみで構成 オブゞェクト名にロヌマ字を䜿わない 栌玍されおいるデヌタを掚枬できるオブゞェクト名を぀ける 真停倀を栌玍するカラムは「flag」を䜿わずに呜名 䞊の 3 ぀は構文解析で察応可胜ですが、䞋の 3 ぀は修正案の提瀺も考慮するず生成 AI の方が適切だず考えられたす。 なぜ専甚の仕組みを䜜るのか 「生成 AI によるレビュヌの仕組み」は既に耇数存圚したすが、今回の芁件を満たせないず刀断し、専甚の仕組みを䜜るこずにしたした。䟋えば、 PR-Agent や CodeRabbit は有名な生成 AI レビュヌサヌビスで、匊瀟でも PR-Agent を導入し、 コヌドやテックブログのレビュヌに掻甚 しおいたす。たた、 GitHub Copilot の自動レビュヌ機胜 は珟圚 Public Preview ずしお提䟛䞭で、今埌 GA される可胜性がありたす。この機胜ではコヌドを Push する前に Visual Studio Code 䞊でレビュヌを受けるこずも可胜で、「生成 AI によるレビュヌの仕組み」は今埌さらに開発フロヌに自然に統合されおいくず想定されたす。さらに、GitHub の管理画面で独自のコヌディング芏玄を定矩し、それをもずに Copilot にレビュヌさせるこずも可胜です。 それでも自前で仕組みを構築する理由は以䞋の通りです。 倚数のガむドラむンを生成 AI で高粟床にチェックするのは難しく、倖郚サヌビスでの察応は珟状困難ず刀断 フィヌドバックの方法を柔軟に調敎したい 䟋意味が曖昧な「data1」のようなカラムは、修正案の提瀺が難しいためコメントのみに留めたい 将来的に構文解析ずのハむブリッド構成で粟床向䞊を目指す 次に、完成した仕組みを玹介したす。 完成した仕組み デモ動画 PR 䜜成埌に GitHub Actions が実行され、生成 AI がレビュヌ結果を PR のコメントずしおフィヌドバックしたす。実際の凊理時間は玄 1 分40 秒ですが、動画では埅ち時間を省略しおいたす。なお、生成 AI のコストは Claude 3.5 Sonnet を甚いた堎合、 DDL を 1 ぀レビュヌするために玄 0.1 USD 必芁ずいう詊算になりたした。 https://www.youtube.com/watch?v=bGcXu9FjmJI アヌキテクチャ 最終的なアヌキテクチャは䞋図の通りです。なお、䜿甚するプロンプトをチュヌニングするための評䟡アプリケヌションは別途構築しおおり、詳现は埌述したす。 凊理の流れ PR のオヌプンをトリガに GitHub Actions ワヌクフロヌを実行し、AWS Step Functions を起動したす。この際、PR の URL ずワヌクフロヌ内で生成した GITHUB_TOKEN を DynamoDB に保存したす。DDL を盎接 Step Functions に枡さないのは、入力文字数制限を回避するためです。PR の URL を元に Lambda 偎で DDL を抜出したす。Step Functions は Map ステヌト を利甚し、各 DDL を䞊列でレビュヌしたす。1 回のレビュヌでチェックするガむドラむンは 1 項目だけです。耇数のガむドラむン芳点でレビュヌするために、最初のプロンプトで埗た「修正埌の DDL」を次のプロンプトに枡す凊理を繰り返し、最終的な DDL を生成したす理由は埌述。レビュヌ完了埌、PR にコメントずしおフィヌドバックしたす。レビュヌ結果は S3 に保存され、LLM-as-a-Judge を甚いお生成 AI がその結果を評䟡したす詳现は埌述。 結果䟋を以䞋に図瀺したす。 生成 AI からのフィヌドバックずしお「適甚したガむドラむン」ず「修正案」がコメントされたす画像巊。詳现は折りたたたれおおり、展開するず DDL ぞの具䜓的な修正内容やコメントが確認できたす画像右。 導入に必芁な手順 以䞋の 2 ステップでテヌブル蚭蚈レビュヌ機胜の導入が完了したす。数分で蚭定できるため、簡単に導入しお即座に生成 AI によるレビュヌを開始できたす。 AWS リ゜ヌスぞのアクセスに必芁なキヌを GitHub Actions Secrets に登録 察象の GitHub リポゞトリにレビュヌ機胜甚の GitHub Actions ワヌクフロヌを远加 DBRE が提䟛するテンプレヌトファむルにプロダクト名を远蚘するだけ 次に、実装で工倫した点を䜕点か玹介したす。 実装時の工倫点 コンテナむメヌゞず Step Functions の掻甚 圓初はシンプルに Lambda のみで実装予定でしたが、以䞋の課題がありたした。 䜿甚するラむブラリのサむズが倧きく、Lambda のデプロむパッケヌゞサむズ制限250 MBを超える 耇数のガむドラむン芳点を Chain しお評䟡する堎合、Lambda の最倧実行時間15分に達しおしたう懞念 DDL を盎列に凊理するず、DDL の数が増えるほど実行時間が長くなる 1 を解決するために Lambda にコンテナむメヌゞを採甚したした。たた、2 ず 3 を解決するため Step Functions を導入し、1 回の Lambda 実行で 1 ぀の DDL を 1 ぀のガむドラむン芳点で評䟡する蚭蚈に倉曎したした。さらに、Map ステヌトを䜿い DDL ごずに䞊列凊理を行うこずで、党䜓の凊理時間が DDL の数に圱響されないようにしたした。䞋図は Map ステヌトの実装を瀺しおおり、ルヌプ郚分でプロンプトの Chain を実珟しおいたす。 Bedrock API のスロットリング察策 レビュヌ時に DDL の数 x ガむドラむンの数 だけ Bedrock の InvokeModel リク゚ストが発生し、クォヌタ制限による゚ラヌが発生するこずがありたした。 AWS のドキュメント によるず、この制限は緩和できたせん。そのため、DDL 単䜍でリク゚ストを耇数リヌゞョンに分散し、゚ラヌ時はさらに別のリヌゞョンでリトラむする仕組みを導入したした。これにより、RateLimit に達するこずがほがなくなり安定したレビュヌが可胜になりたした。 ただし、珟圚は クロスリヌゞョン掚論 を利甚するこずで耇数リヌゞョン間でトラフィックが動的にルヌティングされ、スロットリング察策は AWS 偎に任せるこずが可胜なため、今埌はこちらに移行予定です。 Lambda から GitHub API を実行するための暩限付䞎方法の敎理 Lambda で「察象 PR の倉曎ファむルの取埗」ず「察象 PR ぞのコメント投皿」を実珟するため、以䞋の 3 皮類の暩限付䞎方法を比范怜蚎したした。 トヌクン皮別 有効期限 メリット デメリット Personal Access Token 蚭定次第、無期限も可胜 暩限の適甚範囲が広い 個人ぞの䟝存 GITHUB_TOKEN ワヌクフロヌ実行䞭のみ 取埗が簡単 察象の凊理次第で暩限䞍足の懞念 GitHub Appinstallation access token 1 時間 GITHUB_TOKEN で未察応の暩限も付䞎可胜 プロダクトぞ導入する際の手順の耇雑化 今回は以䞋の理由から GITHUB_TOKEN を採甚したした。 トヌクンは短期間ワヌクフロヌ䞭のみ有効で、セキュリティリスクが䜎い トヌクンの発行・管理が自動化され、運甚負荷が䜎い 今回の凊理に必芁な暩限を付䞎可胜 トヌクンは有効期限TTL付きで DynamoDB に保存し、必芁なずきに Lambda から取埗しお䜿甚したす。これにより、トヌクン受け枡し凊理のログぞの蚘録有無を調査する必芁がなく、安党にトヌクンを利甚できたす。 以降では、生成 AI アプリケヌションの評䟡事䟋に぀いお玹介したす。 生成 AI アプリケヌションの評䟡 生成 AI アプリケヌションの評䟡に぀いお、 Microsoft 瀟のドキュメント に蚘茉の䞋図を参考にしたした。 出兞 Microsoft 瀟 - 生成 AI アプリケヌションの評䟡 この図によるず、GenAIOps今回は LLM が察象のため LLMOpsラむフサむクルの䞭で実斜すべき評䟡は 3 皮類です。 モデル遞定フェヌズ 基盀モデルを評䟡し、䜿甚するモデルを決定 アプリケヌション開発フェヌズ アプリケヌションの出力≒ 生成 AI の応答を、品質・安党などの芳点で評䟡しチュヌニングを実斜 デプロむ埌の運甚フェヌズ Production 環境ぞデプロむ埌も、品質・安党性などに぀いお継続的な評䟡を実斜 以降では、各フェヌズにおける評䟡をどのように実斜したか、事䟋を玹介したす。 モデル遞定フェヌズにおける評䟡 今回は Amazon Bedrock の基盀モデルから遞定を行い、 Chatbot Arena のスコアず 瀟内の生成 AI 有識者 のアドバむスをもずに評䟡し、Anthropic 瀟の Claude を採甚したした。着手時点で最も高性胜だった Claude 3.0 Opus を甚いお DDL のレビュヌを実斜し、䞀定の粟床を確認したした。モデルごずにベヌスの性胜やレスポンス速床、金銭的コストが異なりたすが、今回はレビュヌ発生頻床は高くなく、か぀「できる限り高速に」ずいった芁件はないため、性胜を最も重芖したモデル遞定を実斜しおいたす。あずは Claude のベストプラクティスに基づきプロンプトチュヌニングを行うこずで、さらに高い粟床が期埅できるず刀断し、次のフェヌズぞ進みたした。 なお、途䞭でより高性胜・高速な Claude 3.5 Sonnet がリリヌスされ、掚論の粟床はさらに向䞊したした。 アプリケヌション開発フェヌズにおける評䟡 生成 AI の評䟡手法に぀いおは、 こちらの蚘事 に分かりやすくたずめられおいたす。蚘事内で、 プロンプト、基盀モデル、RAGの有無で、さたざたな評䟡のパタヌンが考えられたす。 ず述べられおいるように、「䜕を評䟡するか」によっお評䟡のパタヌンが異なりたす。今回は「単䞀のプロンプト」の評䟡に焊点を圓お、「DB のテヌブル蚭蚈を瀟内独自のガむドラむンに埓いレビュヌさせる」ずいう特定のナヌスケヌスで、評䟡の蚭蚈ず実装の具䜓䟋を玹介したす。 プロンプトチュヌニングず評䟡の流れ プロンプトチュヌニングず評䟡は Claude のドキュメント に蚘茉の䞋図に沿っお実斜したした。 出兞 Anthropic 瀟 - Create strong empirical evaluations ポむントは、「プロンプトの実行結果が期埅にどれだけ近いか」などの評䟡芳点を「䜕らかの方法で算出したスコア」ずしお定矩し、最良のスコアを埗たプロンプトを採甚する点です。評䟡の仕組みがない堎合、チュヌニング前埌の粟床向䞊を刀断する際に䞻芳に頌りがちになり、結果ずしお曖昧さや䜜業時間の増加を招く恐れがありたす。 以降では、たず生成 AI の評䟡手法に぀いお、その埌にプロンプトチュヌニング事䟋を玹介したす。 「生成 AI の評䟡」ずは deep checks ずいう生成 AI 評䟡甚プロダクトのペヌゞには、評䟡に関しお以䞋の蚘茉がありたす。 Evaluation = Quality + Compliance これは生成 AI アプリケヌションの評䟡を最も端的に衚珟しおいるず感じたした。さらに现分化するず、 こちらの蚘事 ではサヌビス提䟛者の評䟡芳点ずしお「真実性、安党性、公平性、堅牢性」の 4 ぀の芳点に分類されおいたす。評䟡の芳点ずスコア算出方法は、アプリケヌションの特性に応じお遞ぶ必芁がありたす。たずえば、 Amazon Bedrock では、テキスト芁玄に「BERT スコア」、質問回答には「F1 スコア」など、タスクごずに異なる指暙が䜿われおいたす。 評䟡スコアの算出方法 anthropic-cookbook では、スコアの算出方法を以䞋の 3 ぀に倧別しおいたす。 anthropic-cookbook に蚘茉のスコア算出方法たずめ スコアの蚈算ロゞックは、クラりドサヌビスや OSS を利甚するか、自䜜するかを遞択したす。いずれにせよ、評䟡基準は自分たちで蚭定する必芁がありたす。䟋えば LLM の出力が JSON 圢匏の堎合、「文字列の完党䞀臎」より「各芁玠単䜍の䞀臎」が適切な堎合もありたす。 Model-based grading に぀いお、 anthropic-cookbook に蚘茉のコヌドをより簡朔に衚珟するず以䞋のようになりたす。 def model_based_grading(answer_by_llm, rubric): prompt = f"""<answer>タグの回答を、<rubric>タグの芳点で評䟡しお。「正しい」たたは「正しくない」ず回答しお。 <answer>{answer_by_llm}</answer> <rubric>{rubric}</rubric> """ return llm_invoke(prompt) # 䜜成したプロンプトをLLMに枡しお掚論させる rubric = "正しい回答は、2 皮類以䞊のトレヌニングプランを含んでいる必芁がありたす。" answer_by_llm_1 = "おすすめのトレヌニングは、「腕立お䌏せ」「スクワット」「腹筋」です。" # 実際はLLMの出力 grade = model_based_grading(answer_by_llm_1, rubric) print(grade) # 「正しい」ず出力されるはず answer_by_llm_2 = "おすすめのトレヌニングは、「腕立お䌏せ」です。" # 実際はLLMの出力 grade = model_based_grading(answer_by_llm_2, rubric) print(grade) # 「正しくない」ず出力されるはず 評䟡に぀いおのたずめ ここたでの内容をたずめるず、評䟡手法は以䞋の図で衚珟できたす。 生成 AI の評䟡は、抜象的には Quality ず Compliance に分解されたす。これらをさらに现分化し、ナヌスケヌスごずに自分たちで具䜓的な評䟡芳点を蚭定したす。各芳点は数倀化が必芁で、その手段は「Code」「Human」「Model」のいずれかをベヌスに実珟したす。 以降では、「DB のテヌブル蚭蚈レビュヌ」ずいう芳点での具䜓的な評䟡方法に぀いお説明したす。 DB のテヌブル蚭蚈レビュヌにおける評䟡蚭蚈 Quality の評䟡は以䞋の理由で Code-based アプロヌチを遞択したした。 Human による評䟡ずチュヌニングのサむクルは工数増倧に぀ながり、埗られるメリットに芋合わない Model-based も怜蚎したが、正解 DDL ずの完党䞀臎をベストスコアにしたく、Code-based の方が適切ず刀断 正解デヌタずの「DDL における類䌌床」を新たに実装するのは難しいため、テキスト間の距離を蚈枬する手法の 1 ぀であるレヌベンシュタむン距離Levenshtein Distanceをスコアの算出方法に採甚したした。この手法では、完党䞀臎時の距離は 0 ずなり、倀が倧きいほど類䌌床が䜎くなりたす。ただし、「DDL における類䌌床」を完党に衚す指暙ではないため、基本的には党デヌタセットで 0 スコアを目指し、非 0 スコアのデヌタセットに察しおプロンプトチュヌニングを行う方針ずしたした。アルゎリズムは LangChain の String EvaluatorsString Distance でも提䟛されおおり、こちらを䜿甚しおいたす。 䞀方、Compliance の芳点に぀いおは瀟内向けアプリケヌションであるこずや、プロンプトに埋め蟌むナヌザヌ入力を DDL に限定する実装になっおいるこずから、今回は䞍芁ず刀断したした。 評䟡の実装 実装した評䟡の流れは以䞋のずおりです。 各レビュヌ芳点ごずに、入力甚 DDL ず正解 DDL を組み合わせた 10 パタヌンのデヌタセットを䜜成したした。効率的にプロンプトチュヌニングず評䟡を繰り返すため、Python ず Streamlit で専甚アプリケヌションを開発したした。デヌタセットは jsonl 圢匏で保存し、ファむルを指定するず自動で評䟡を実行しお結果が衚瀺されたす。以䞋のように各 json は「評䟡察象のガむドラむン」「LLM を Invoke する際のパラメヌタ」「入力 DDL」「正解 DDL」が含たれおいたす。 { "guidline_ids": [1,2], "top_p": 0, "temperature": 0, "max_tokens": 10000, "input_ddl": "CREATE TABLE sample_table (...);", "ground_truth": "CREATE TABLE sample_table (...);" } 個別の結果衚瀺では、出力 DDL ず正解 DDL の diff を衚瀺し、芖芚的に差異チュヌニングポむントが分かるよう工倫したした。 評䟡が党お終わるず、スコアの集蚈結果も確認できたす。 プロンプトチュヌニング Claude のドキュメント に基づき、以䞋のポむントを意識しおプロンプトの䜜成・チュヌニングを行い、最終的に 60 個のデヌタセットほが党おにおいお、最良0スコアの結果を達成したした。 ロヌルを蚭定 XML タグを掻甚 Claude に思考させる思考過皋の出力を指瀺するこずで、期埅倖れの回答時にデバッグを容易に Few-Shot Prompting出力䟋を提瀺 参照デヌタを冒頭に、指瀺を末尟に配眮 明確か぀具䜓的な指瀺を䞎える プロンプトを Chain させる 有名な手法も倚く詳现は省略したすが、最埌の 2 点に぀いお補足したす。 「明確で具䜓的な指瀺を䞎える」 圓初は瀟内のテヌブル蚭蚈ガむドラむンの文章をそのたたプロンプトに埋め蟌んでいたした。しかし、ガむドラむンには「あるべき姿」のみ蚘茉され、「具䜓的な修正手順」が含たれおいたせんでした。そこで、Step-by-step 圢匏の「具䜓的な修正指瀺」に曞き換えたした。䟋えば「真停倀を栌玍するカラム名に xxx_flag を䜿甚しない」ずいうガむドラむンに぀いおは、以䞋のように修正したした。 以䞋の手順に埓い、真停倀が栌玍されおいるカラム名を抜出し、必芁に応じお適切なカラム名に倉曎しおください。 1. 真停倀が栌玍されおいるカラム名を抜出したす。刀断基準は、boolean型が䜿われおいるか「flag」文字が入っおいるかです。 2. 真停倀カラムの名前を1぀ず぀チェックし、カラムの意味をたずは理解したす。 3. 真停倀カラムの名前を1぀ず぀チェックし、より適切な名前があるず刀断した堎合は、カラム名を修正したす。 4. 適切なカラム名の条件は<appropriate_column_name></appropriate_column_name>タグの䞭を参照しおください。 <appropriate_column_name> 「flag」ずいう文字を䜿わず... ... </appropriate_column_name> 「プロンプトを Chain させる」 チェックするガむドラむンが増えるほど、1 回で党おを確認しようずするずプロンプトが耇雑化し、チェック挏れや粟床䜎䞋の懞念が高たりたす。そのため、1 回のプロンプト実行で AI にチェックさせる項目を 1 ぀に限定したした。最初のプロンプトで埗た「修正埌の DDL」を次のプロンプトの入力ずしお枡しChain、繰り返し凊理するこずで最終的な DDL を埗る仕組みにしお、アヌキテクチャにも反映したした。プロンプトの Chain で、以䞋のメリットも埗られたした。 プロンプトが短く、タスクが 1 ぀に絞られるため粟床が向䞊 ガむドラむン远加時は新芏プロンプトを䜜成するだけでよく、既存プロンプトぞの粟床圱響がない 䞀方で、LLM の Invoke 回数が増えるため、時間ず金銭的コストは増加したす。 デプロむ埌の運甚フェヌズにおける評䟡 プロンプト䜜成段階では、手動で䜜成した正解デヌタを甚いお Quality を評䟡したした。しかし、Production 環境では正解デヌタが存圚しないため、別の評䟡手法が必芁です。そこで「LLM の応答を LLM に評䟡させる」手法である LLM-as-a-Judge を採甚したした。 Confident AI のドキュメント によるず、この手法には 3 皮類の方法があるずされおいたす。 Single Output Scoring (正解デヌタなし) 「LLM の出力」ず「評䟡基準Criteria」を䞎え、基準に基づき LLM にスコアを付けさせる Single Output Scoring (正解デヌタあり) 䞊蚘に加え「正解デヌタ」も提䟛。より高粟床な評䟡が期埅できる Pairwise Comparison 2 ぀の出力を比范し、どちらが優れおいるかを刀定。「優れた」の Criteria は自分で定矩する 今回は Single Output Scoring (正解デヌタなし) を採甚したした。この手法も LangChain でサポヌト されおおり、提䟛されおいる関数を䜿甚したした。なお、珟圚は LangSmith による実装が掚奚されおいたす。 評䟡基準Criteriaは以䞋の 2 ぀を定矩し、それぞれ 10 点満点で採点させおいたす。 Appropriateness LLM の出力がガむドラむンに沿っお適切に修正されおいるか Formatting Consistency 䞍芁な改行や空癜などが付䞎されおおらず、フォヌマットの䞀貫性が保たれおいるか コヌドずプロンプトのむメヌゞは以䞋の通りです。 input_prompt =""" <input_sql_ddl>CREATE TABLE ...</input_sql_ddl> <table_check_rule>曖昧なオブゞェクト名は...</table_check_rule> 指瀺曞:table_check_ruleを元に、input_sql_ddlを適切なDDLに修正しおください。 """ output_ddl = "CREATE TABLE ..." # 実際はLLMが生成したDDLをセット appropriateness_criteria = { "appropriateness": """ Score 1: ... ... Score 7: 入力の指瀺に抂ね埓った応答が生成されおおり、䞍適切な修正が2箇所以䞋である。 Score 10: 入力の指瀺に完党に埓った応答が生成されおいる。 """ } evaluator = langchain.evaluation.load_evaluator( "score_string", llm=model, criteria=appropriateness_criteria ) result = evaluator.evaluate_strings( prediction=output_ddl, input=input_prompt ) print(result) この実装で以䞋のような出力が埗られたす。省略箇所あり この回答は、䞎えられた指瀺に完党に埓っおいたす。以䞋に評䟡の理由を説明したす 1. カラム名の抜出ず評䟡 回答者は党おのカラム名を抜出し、それぞれのカラム名がデヌタの䞭身を掚枬できるかどうかを適切に刀断しおいたす。 2. 曖昧なカラム名の特定 提䟛されたDDL内のカラム名は党お、その目的や栌玍されるデヌタの皮類を明確に瀺しおいたす。䟋えば、... ... この回答は、䞎えられた指瀺を完党に理解し、適切に実行しおいたす。 Rating: [[10]] この仕組みは、アヌキテクチャ図における䞋図赀枠郚分に盞圓したす。 LLM のレビュヌ結果が S3 に保存されるず、SQS を介しお非同期で LLM-as-a-Judge 甚の Lambda が起動したす。この Lambda が評䟡を実行し、結果はログずしお S3 に保存され、スコアは CloudWatch のカスタムメトリクスずしお送信されたす。たた、CloudWatch Alarm により閟倀を䞋回る堎合は Slack に通知されたす。 このスコアは完党に信頌できるものではありたせんが、察象が瀟内向けシステムであるため、ナヌザヌからのフィヌドバックを埗やすい環境です。そのため、定量的なスコアで継続的にモニタリングし぀぀、定期的にナヌザヌフィヌドバックを収集する䜓制をずりたした。 埗られた孊びず今埌の展望 最埌に、生成 AI アプリケヌション開発に挑戊しお埗た孊びず、今埌の方向性に぀いおたずめたす。 評䟡が非垞に重芁か぀難しい プロンプトの結果を同じ芳点で評䟡するこずで、䞻芳を排陀し぀぀高速にチュヌニングず評䟡を繰り返すこずができたした。この経隓から、評䟡蚭蚈の重芁性を匷く感じたした。ただし、GenAIOps における3぀の評䟡モデル遞定時、開発時、運甚時はナヌスケヌスごずに刀断が必芁で「自分たちの評䟡蚭蚈の劥圓性刀断」は難しいず感じたした。たた、評䟡の芳点が䞍足しおいるず、コンプラむアンス面で問題を抱えたアプリケヌションを提䟛するリスクもありたす。今埌、より䜓系的でマネヌゞドな評䟡方法や仕組みが提䟛されれば、GenAIOps が実珟しやすくなっおいくず考えたす。 生成 AI のナヌスケヌスの想像の幅が広がった 実際に自分たちで生成 AI アプリケヌションに関しお調査・実装したこずで解像床が䞊がり、ナヌスケヌスの想像の幅が広がりたした。䟋えば、 ゚ヌゞェント ず ロック競合の情報を収集する仕組み などを組み合わせお、よりマネヌゞドか぀高速にむンシデント調査ができる仕組みなどを想像できるようになりたした。 プログラマブルなタスクの代替ずしおの 生成 AI 掻甚 生成 AI をアプリケヌション開発に掻甚する方法は、倧きく次の 2 ぀に分けられたす。 タスクそのものを 生成 AI に実斜させる プログラム開発の生産性を 生成 AI で向䞊させる 今回は、 生成 AI に掚論させるべきタスクだけでなく、本来はプログラムで凊理すべきタスクも生成 AI を䜿っお実装したした。圓初は「プロンプトを工倫すれば迅速に高粟床の結果が埗られるかもしれない」ず期埅しおいたしたが、実際にはプロンプトチュヌニングに倚くの時間が必芁であるず痛感したした。䞀方、Claude の耇数モデルで同じタスクを解かせた結果、モデルの粟床が高いほど明確に結果が改善されるこずを確認したした。さらに、粟床が高いモデルでは予枬䞍胜な挙動が枛り、プロンプトチュヌニングに芁する時間も短瞮できるこずが分かりたした。 これらの経隓を螏たえるず、今埌モデル粟床がさらに向䞊した堎合、芁件次第では「タスクを実斜するプログラムを曞く」代わりに「タスクそのものを 生成 AI に実斜させる」ずいう手段を遞択するケヌスが増えるかもしれたせん。 今埌の展望 今埌は以䞋のようなこずに取り組んでいきたいず考えおいたす。 察応ガむドラむンの拡充 導入プロダクトの拡倧 プログラムによる構文解析ずのハむブリッド構成にする レビュヌ結果をナヌザヌぞフィヌドバックする際の「分かりやすさ」の改善 珟状の簡易的な LLMOps を、モニタリングだけでなくログを掻甚したプロンプト・モデル改善にたで拡匵 参考 @hiro_gamo 氏の Post たずめ 本蚘事では、Amazon Bedrock を掻甚し、Serverless アヌキテクチャで実装した DB テヌブル蚭蚈の自動レビュヌ機胜を玹介したした。たた、生成 AI アプリケヌションの評䟡方法に぀いおも解説したした。先日開催された AWS AI Day でのセッション「 コンテンツ審査を題材ずした生成AI機胜実装のベストプラクティス 」で玹介されおいる「 LLM の実導⌊における考慮点ず打ち⌿ 」の各項目にマッピングする圢で、今回の取り組みを以䞋にたずめたす。 項目 内容 他の手段ずの棲み分け ● DBRE チヌムで LLM による自動化に挑戊 ● 将来的にはルヌルベヌスずの䜵甚を目指す 粟床 ● 「テヌブル蚭蚈レビュヌ」ずいうナヌスケヌスに沿った評䟡芳点を蚭蚈 ● プロンプトチュヌニングず評䟡を高速に繰り返すために、専甚のアプリケヌションを開発 ● 遞定したモデルClaudeのベストプラクティスに埓っおプロンプトチュヌニングを実斜 ● 1 回のプロンプト実行で AI にチェックさせる項目を 1 ぀に限定 & プロンプトの Chain で粟床向䞊 コスト ● DDL の文字数にもよるが、1 DDL あたり玄 0.1 USD ● レビュヌ発生頻床は䜎いため、コストより粟床を重芖したモデル遞定Claude 3.5 sonnet ● プロンプトの Chain も同様に、コストは増えるが粟床向䞊のメリットが倧きいず刀断 可甚性・スルヌプット ● クォヌタを意識しおリヌゞョン間のリク゚スト分散やリトラむ凊理を実装 ● よりマネヌゞドな クロスリヌゞョン掚論 ぞず移行予定 レスポンス速床 ● 「できる限り高速に」ずいった芁件はないため、速床よりも粟床を優先したモデル遞定 ● 各 DDL を䞊列でレビュヌするこずで速床向䞊 ● 数 10 個 の DDL に察しお 2-5 分皋床で レスポンスを返华 LLMOps ● LLM-as-a-Judge を甚いお継続的に粟床をモニタリング セキュリティ ● GitHub ずの連携には、GHA ワヌクフロヌ実行䞭だけ有効な GITHUB_TOKEN を採甚 ● 瀟内アプリ & 入力が DDL に限定されるため、LLM の応答ぞの Compliance 芳点評䟡は未実斜 本プロダクトは珟圚耇数のプロダクトぞの導入が進行䞭で、今埌もナヌザヌフィヌドバックを基に改善を進める予定です。生成 AI アプリケヌションは Amazon Bedrock Prompt フロヌ など開発甚のサヌビスも進化を続けおおり、今埌もより䟿利になっおいくず思いたす。今埌も積極的に生成 AI 領域にも挑戊しおいきたいです。 KINTO テクノロゞヌズ DBRE チヌムでは䞀緒に働いおくれる仲間を絶賛募集䞭ですカゞュアルな面談も歓迎ですので、 少しでも興味を持っおいただけた方はお気軜に X の DM 等でご連絡ください。䜵せお、 匊瀟の採甚 X アカりント もよろしければフォロヌお願いしたす
この蚘事は KINTOテクノロゞヌズアドベントカレンダヌ2024 の12日目の蚘事です🎅🎄 孊びの道の駅の䞭西です。今幎は孊びの道の駅プロゞェクトが立ち䞊がり組織化されたした。そんな䞭、瀟内Podcastの運営も行っおおり、今幎のアドベントカレンダヌではその内容もお届けしたいず思いたす。 「孊びの道の駅」ずは 「孊びの道の駅」は、瀟内で頻繁に開催される勉匷䌚をもっず䟿利に、そしお効果的に掻甚するために立ち䞊げられたプロゞェクトです。瀟内の有志が䞭心ずなり勉匷䌚の開催を支揎し瀟内の知識共有を促進するこずを目的ずしおいたす。 合同勉匷䌚 第䞀回目の「孊びの道の駅ポッドキャスト」では、瀟内で行われた合同勉匷䌚に぀いお、運営メンバヌである朝日さん、きヌゆのさん、リナさんにむンタビュヌを行いたした。このポッドキャストでは、勉匷䌚の背景や目的、運営の工倫、そしお今埌の展望に぀いお詳しくお聞きしたした。 むンタビュヌ HOKAさんむンタビュアヌ たずは「道の駅プロゞェクト」がどのように始たったのか教えおください。 HOKAさん このプロゞェクトは瀟内で孊びに察しお前向きな文化をさらに広めるために始たりたした。景山さんをはじめずする数名が、瀟内の勉匷䌚を支揎する仕組みを䜜ろうず集たったのがきっかけです。 HOKAさん 合同勉匷䌚の背景ず目的に぀いお詳しく教えおください。 朝日さん 新入瀟員向けのオリ゚ンテヌションで気づいたのですが、他のシステムず関連する情報を知る機䌚が少ないず感じたした。それで、最新の情報をキャッチアップできる堎が必芁だず思い、合同勉匷䌚を始めるこずになりたした。 HOKAさん 勉匷䌚の運営で工倫されおいる点は䜕ですか リナさん 他のシステムやプロダクトの最新情報を共有できるようにしお、より広範な知識を提䟛するこずを目指しおいたす。たた、参加者を増やすために事前の準備をしっかり行い、運営メンバヌ自身が積極的に関䞎しおいたす。 HOKAさん 初回の勉匷䌚の成果ず反響はいかがでしたか きヌゆのさん 事前の芋蟌みでは34人皋床の参加でしたが、実際にはZoom参加者を含めお玄80名が集たりたした。普段関わらない人ずの亀流ができたり、他のプロダクトの最新情報を埗るこずができたずいうポゞティブな反応が倚くありたした。 HOKAさん 勉匷䌚の継続ず今埌の取り組みに぀いお教えおください。 朝日さん 今埌は、新たなテヌマ別ディスカッションや職皮別の亀流の堎を䜜り、より倚くの瀟員が気軜に参加できる環境を敎えおいきたいず考えおいたす。 リナさん 次回の登壇者も既に決定しおおり、継続的な開催を目指しおいたす。瀟内倖の情報発信も匷化しおいきたす。 HOKAさん 「道の駅プロゞェクト」を瀟倖に発信する狙いは䜕ですか リナさん 瀟内倖の興味を匕くこずで、より倚くの参加者を募るこずです。瀟倖ぞの発信を通じお、瀟内の孊びの文化を広めたいず思っおいたす。 HOKAさん 最埌に、今回のむンタビュヌを振り返っお感じるこずはありたすか 朝日さん 参加者ずしおの芖点ず運営偎ずしおの芖点の違いを実感したした。運営偎に立぀こずで、むベントの重芁性や意矩がわかり、盛り䞊げたいずいう気持ちが匷くなりたした。 きヌゆのさん 今たでズヌムで芋るだけだったのですが、運営偎に立぀こずでコメントやリアクションが力になるず感じたした。今埌も積極的に参加しおいきたいです。 たずめ 「道の駅プロゞェクト」が目指すのは、単なる勉匷䌚の堎の提䟛にずどたらず、瀟員同士の亀流ず知識共有を通じお、瀟内の文化を䞀局豊かにするこずです。継続的な勉匷䌚の開催ず広報掻動を通じお、より倚くの瀟員が参加し、孊び合う堎を䜜り䞊げおいくこずで、瀟内党䜓のスキルアップず連垯感の醞成に貢献しおいくこずでしょう。 今回は合同勉匷䌚の詳现ず、その運営の背景、今埌の展望に぀いおお届けしたした。次回の勉匷䌚も楜しみにしおください
This article is the 12th day of the KINTO Technologies Advent Calendar 2024 . 🎅🎄 Hello, we are the Android development team at the 'KINTO Kantan Moushikomi App' (KINTO Easy Application App). Today, we want to share the process of implementing Kotlin Multiplatform (KMP) into our existing app, the reasons behind it, and the changes and improvements it has brought. Over the past year, we have been exploring ways to maximize development efficiency between iOS and Android platforms. During this process, KMP caught our attention, and our team would like to share how this technology has innovatively improved our development process. Contents 1. Reasons for Implementing KMP in an Existing App 2. Integrating KMP into Our Existing App 2.1 Deciding on Shared Code Placement 2.2 Organizing the Shared Code 2.3 Creating a KMP Module 2.4 Multi-module Architecture and Umbrella Module 2.5 CI: Testing Shared Code on Android and iOS 3. Distributing Your KMP Code 3.1 Options for Distributing KMP Code 3.2 Swift Package Manager (SPM) 3.3 Automating Distribution 4. Android and iOS Implementation Methods 4.1 Feature Selection 5. Issues in KMP Cross-Platform Module Implementation 6. Effects 7. Moving Forward: Future Plans and Challenges 1. Reasons for Implementing KMP in an Existing App At the time, our team faced a shortage of iOS development resources. To address this challenge, the Android team decided to leverage Kotlin Multiplatform (KMP) to create shared business logic for both iOS and Android platforms. This approach reduced code duplication across operating systems and allowed the Android team to utilize their expertise to support iOS development. This strategy was seen as a crucial solution to alleviate staffing issues and significantly enhance development productivity, which became the decisive reason for integrating KMP technology into our existing app. [Summary of the Background] Addressing the shortage of iOS development resources Leveraging the Android team’s expertise in Kotlin Reducing code duplication across operating systems Improving development productivity and strengthening team collaboration ※ Let's eliminate duplicated efforts across operating systems by modularizing business logic into a KMP library. 2. Integrating KMP into Our Existing App Before implementing any KMP code, we made several strategic decisions about where to place and how to organize our shared code. 2.1 Deciding on Shared Code Placement Our mobile app has a typical setup with two separate development teams: an Android team working on the Android repository and an iOS team working on the iOS repository. When introducing KMP, the first question that arises is: where should the shared code reside? Option 1: Shared Code in a Separate Repository This option involves creating a new repository for the shared code, which can be accessed by both the Android and iOS repositories. The repository structure would look like this: graph TB; subgraph Android Repository AndroidApp end subgraph iOS Repository iOSApp end subgraph KMP Repository KMP end KMP --> AndroidApp KMP --> iOSApp Option 2: Shared Code in the Android Repository In this option, the shared code is placed in the Android repository, allowing the Android team to manage the shared codebase. The repository structure would look like this: graph TB; subgraph Android Repository KMP --> AndroidApp end subgraph iOS Repository KMP --> iOSApp end Option 3: Merge Android and iOS Repositories into a Monorepo This option involves merging the Android and iOS repositories into a monorepo, allowing both teams to access the shared codebase. The repository structure would look like this: graph TB; subgraph One Repository KMP --> AndroidApp KMP --> iOSApp end [Our Decision] After considering the pros and cons of each option, we decided to place the shared code in the Android repository. This decision was based on the following factors: Minimizing the impact on existing workflows Easier to manage the shared codebase 2.2 Organizing the Shared Code Once we decided where to place the shared code, the next decision was how to organize it. Since our existing Android app follows a multi-module architecture, we wanted to maintain a clear separation between the shared module and the platform-specific modules. We decided to place the KMP module in the shared directory within the Android repository, alongside the existing Android modules. for example: :app // Android app module :domain // Android-specific module :shared:api // KMP module :shared:validation // KMP module 2.3 Creating a KMP Module A Gradle module for KMP includes: 1. A build.gradle.kts file. 2. A src subfolder. For Android modules, we apply the com.android.library plugin and include an android {} block: plugins { id("com.android.library") } android { // Android-specific configurations } For KMP modules, we use the multiplatform plugin and define a kotlin {} block: plugins { kotlin("multiplatform") } kotlin { // KMP configurations } This setup allowed us to support both Android- and KMP-specific requirements in our shared codebase. 2.4 Multi-module Architecture and Umbrella Module Limitations of Multiple Modules In Android, splitting code into multiple modules is standard for complex projects. However, KMP currently supports exposing only one module to iOS. For example, suppose we have three modules in our shared codebase: featureA , featureB , and featureC . Each module depends on the data module, which in turn depends on the api module. graph LR; api --> data --> featureA data --> featureB data --> featureC We want to expose these three modules to iOS. In an ideal scenario, iOS developers would import only the modules they need, like so: import featureA import featureB <swift code here> However, due to the limitations of KMP, this approach results in duplicated code in the iOS app. What we want: graph LR; subgraph KMP api --> data --> featureA data --> featureB data --> featureC end featureA --> iOSApp featureB --> iOSApp featureC --> iOSApp What we get (with duplication): graph LR; subgraph KMP api --> data --> featureA end subgraph KMP1 api1(api copy) --> data1(data copy) --> featureB end subgraph KMP2 api2(api copy2) --> data2(data copy2) --> featureC end featureA --> iOSApp featureB --> iOSApp featureC --> iOSApp style api1 fill:#f88 style api2 fill:#f88 style data1 fill:#f88 style data2 fill:#f88 Umbrella Module To work around this limitation, we introduced an umbrella module . An umbrella module is a "empty" module that does not contain source code but used to manage dependencies. graph LR; subgraph KMP subgraph Umbrella api --> data --> featureA data --> featureB data --> featureC end end Umbrella --> iOSApp style Umbrella fill:#8f88 Here is a build.gradle.kts example: kotlin { val xcf = XCFramework() listOf( iosX64(), iosArm64(), iosSimulatorArm64() ).forEach { it.binaries.framework { baseName = "Umbrella" binaryOption("bundleId", "com.example.shared") export(project(":shared:featureA")) export(project(":shared:featureB")) export(project(":shared:featureC")) xcf.add(this) } } sourceSets { val commonMain by getting { dependencies { api(project(":shared:featureA")) api(project(":shared:featureB")) api(project(":shared:featureC")) } } } } The umbrella module simplifies the integration process for iOS developers, ensuring a seamless and efficient development experience across platforms. 2.5 CI: Testing Shared Code on Android and iOS We always write tests for our code, and the shared code is no exception. Due to platform differences, some features may not work as expected on iOS. To ensure compatibility, run tests on both Android and iOS. Unlike Android, which can run tests on any OS, iOS tests must be run on macOS. 3. Distributing your KMP code Once you finish writing your KMP code, the next step is to distribute it to iOS app. 3.1 Options for Distributing KMP Code You can distribute your KMP code by source code or binary. Source Code Distribution With source code distribution, iOS developers must compile the KMP code themselves. This approach requires setting up a Kotlin build environment, including tools like Java VM and Gradle. Challenges: Every iOS developer needs to configure the KMP build environment. This increases the complexity of onboarding KMP code into the iOS project. Binary Distribution A better option is binary distribution. By providing precompiled libraries, we eliminate the need for iOS developers to manage an additional build environment, making it much easier to integrate shared code. Advantages: Reduces setup effort for iOS developers. Ensures consistent builds across environments. 3.2 Swift Package Manager (SPM) For iOS, there are two main dependency management systems: CocoaPods and Swift Package Manager (SwiftPM). The choice depends on your iOS team’s preferences. Fortunately, our iOS team has fully transitioned to SwiftPM, so we only need to support SwiftPM. What is a Swift Package? A Swift Package is essentially a Git repository that includes: Swift source code. A Package.swift manifest file. Semantic versioning via Git tags. Binary Distribution with SwiftPM Since SwiftPM 5.3, it has supported binaryTarget , allows you to distribute precompiled libraries instead of source code. Creating a Swift Package with Binary Distribution Here’s a brief explanation of how we publish KMP code as a Swift Package: Compile the KMP code into an .xcframework . Package the .xcframework into a zip file and calculate its checksum. Create a new release page on GitHub and upload the zip file as part of the release assets. Obtain the zip file’s URL from the release page. Generate the Package.swift file based on the URL and checksum. Commit the Package.swift file and add a git tag to mark the release. Associate the git tag with the release page and officially publish the GitHub release. For detailed instructions, refer to the [KMP documentation on Remote SPM export].( https://kotlinlang.org/docs/native-spm.html ) // swift-tools-version: 5.10 import PackageDescription let packageName = "Umbrella" let package = Package( name: packageName, platforms: [ .iOS(.v13) ], products: [ .library( name: packageName, targets: [packageName]), ], targets: [ .binaryTarget( name: packageName, url: "https://url/to/some/remote/xcframework.zip", checksum: "The checksum of the ZIP archive that contains the XCFramework." ] ) 3.3 Automating Distribution Manual distribution can be time-consuming. To streamline the process, we created a GitHub Actions workflow for automation. name: Publish KMP for iOS on: workflow_dispatch: inputs: release_version: description: 'Semantic Version' required: true default: '1.0.0' env: DEVELOPER_DIR: /Applications/Xcode_15.3.app jobs: build: runs-on: macos-14 steps: - name: Checkout uses: actions/checkout@master - name: set up JDK 17 uses: actions/setup-java@v4 with: java-version: '17' distribution: 'zulu' - name: "Build and Publish" env: RELEASE_VERSION: ${{ github.event.inputs.release_version }} GH_TOKEN: ${{ github.token }} run: ./scripts/publish_iOS_Framework.sh $RELEASE_VERSION #!/bin/sh set -e MODULE_NAME="<your module name>" VERSION=$1 # version name for github release RELEASE_VERSION="$MODULE_NAME-$VERSION" # tag name for git tag TAG="$VERSION" TMP_BRANCH="kmp_release_$VERSION" # check if VERSION is in semver format if [[ ! $VERSION =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then echo "VERSION should be in semver format like 1.0.0" exit 1 fi ZIPFILE=./shared/$MODULE_NAME/build/XCFrameworks/release/$MODULE_NAME.xcframework.zip echo "Building $MODULE_NAME $VERSION" ./gradlew assembleKintoOneCoreReleaseXCFramework echo "creating zip file" pushd ./shared/$MODULE_NAME/build/XCFrameworks/release/ zip -r $MODULE_NAME.xcframework.zip $MODULE_NAME.xcframework popd # fetch tags git fetch --tags # get previous release tag PREVIOUS_RELEASE_TAG=$(git tag --sort=-creatordate | grep -v ^version | head -n 1) echo "previous release tag: $PREVIOUS_RELEASE_TAG" # create github draft release echo "creating github release $RELEASE_VERSION" gh release create $RELEASE_VERSION -d --generate-notes --notes-start-tag $PREVIOUS_RELEASE_TAG gh release upload $RELEASE_VERSION $ZIPFILE echo "retrieving asset api url" # get asset api url of uploaded zip file from github release # eg: "https://api.github.com/repos/{username}/{repo}/releases/assets/132406451" ASSET_API_URL=$(gh release view $RELEASE_VERSION --json assets | jq -r '.assets[0].apiUrl') # add suffix .zip to url ASSET_API_URL="${ASSET_API_URL}.zip" # Generate Package.swift ./scripts/generate_SPM_Manifest_File.sh $ZIPFILE $ASSET_API_URL # commit Package.swift and add tag git checkout -b $TMP_BRANCH git add . git commit -m "release $VERSION" git tag -a $TAG -m "$MODULE_NAME $VERSION" git push origin $TAG # update github release to point to the new tag gh release edit $RELEASE_VERSION --tag $TAG 4. Android and iOS Implementation Methods In this project, we introduced a new common module to our existing app using Kotlin Multiplatform (KMP). To minimize potential platform-specific issues, we carefully selected and implemented features that could work reliably across Android and iOS. The focus was on establishing a cross-platform module by selecting OS-independent functionality and keeping implementations simple for initial testing in production environments. Below is an outline of the feature selection criteria and the implementation process. 4.1 Feature Selection To identify potential challenges of deploying KMP in production, we prioritized features that would not depend on platform-specific implementations and could be handled with minimal dependencies. The criteria for feature selection included: OS-Independent Functionality : We select the feature that would be OS-independent to avoid unexpected issues in production, leaving out elements that required specific OS-level controls, such as communication, storage, and permissions. Minimizing Additional Libraries : To reduce the risk of maintenance, we select the feature that could be implemented only with the Kotlin standard library without relying on additional libraries. Library Prioritization : When selecting libraries, we prioritized official Kotlin libraries first, then libraries recommended in official Kotlin documentation, and finally, third-party libraries as a last selection. Based on these criteria, input validation were chosen as the initial cross-platform functionality to implement with KMP. And full-width/half-width character transformation feature added. Android Input Validation Implementation By default, Android implementation has only lack of library functionality or interface difference problems, but it was no-big deal. The input validation feature was structured according to general object-oriented programming (OOP) principles, with an emphasis on reusability and consistency. 1. Defining Common Interfaces : We defined Validator and ValidationResult interfaces to create a consistent foundation for validating input across both platforms. abstract class ValidationResult( /** * Informations about input and fail reason. */ val arguments: Map<String, Any?>, requiredKeys: Set<String> ) fun interface Validator<T, R : ValidationResult> { /** * @return validation result or `null` if the target is valid. */ operator fun invoke(target: T): R? } 2. Validator Implementation by Input Type : Separate validators and result classes were created for different input types, such as email and password validation. class IntRangeValidator( /** * min bound(inclusive). */ val min: Int, /** * Max bound(inclusive). */ val max: Int ) : Validator<String, IntRangeValidationResult> { companion object { const val PATTERN = "0|(-?[1-9][0-9]*)" val REGEX = PATTERN.toRegex() const val ARG_NUMBER = "number" const val ARG_RANGE = "range" const val ARG_PATTERN = "pattern" } val range = min..max override fun invoke(target: String): IntRangeValidationResult? { when { target.isEmpty() -> return RequiredIntRangeValidationResult() !target.matches(REGEX) -> return IllegalPatternIntRangeValidationResult(target, PATTERN) } return try { target.toInt(10).let { number -> if (number !in range) { OutOfRangeIntRangeValidationResult(target, range) } else { null } } } catch (e: NumberFormatException) { OutOfRangeIntRangeValidationResult(target, range) } } } 3. Test Code Creation : To validate the module’s accuracy across platforms, we implemented extensive test cases using the kotlin-test package, ensuring stable functionality on both Android and iOS. import kotlin.random.Random import kotlin.test.AfterTest import kotlin.test.BeforeTest import kotlin.test.Test import kotlin.test.assertContains import kotlin.test.assertEquals import kotlin.test.assertFailsWith import kotlin.test.assertIs import kotlin.test.assertNotNull import kotlin.test.assertNull class IntRangeValidatorTest { private var min = 0 private var max = 0 private lateinit var validator: IntRangeValidator @BeforeTest fun setUp() { min = Random.nextInt() max = Random.nextInt(min + 1, Int.MAX_VALUE) validator = IntRangeValidator(min, max) } @AfterTest fun tearDown() { min = 0 max = 0 } @Test fun `invoke - decimal number string`() { val validator = IntRangeValidator(Int.MIN_VALUE, Int.MAX_VALUE) for (number in listOf( "0", "1", "111", "${Int.MAX_VALUE}", "-1", "${Int.MIN_VALUE}", "${Random.nextInt(Int.MAX_VALUE)}", "-${Random.nextInt(Int.MAX_VALUE - 1)}" )) { // WHEN val result = validator(number) // THEN assertNull(result) } } } Full-width/Half-width Character Transformation Implementation In addition to input validation, we implemented a character transformation feature to automatically convert between full-width and half-width characters based on application requirements. 1. Defining Extendable Interface : To enable multiple and complex conversions, we defined an interface that could be inherited to handle various character transformations. Kotlin has functional interface( fun interface ) and operator function( operator fun ) features helped to implement this. fun interface TextConverter { operator fun invoke(input: String): String operator fun plus(other: TextConverter) = TextConverter { input -> other(this(input)) } } 2. Defining Mapping Constants for Conversion : We created a character mapping table that listed the full-width/half-width characters and their conversions, allowing transformations by referencing predefined mappings. open class SimpleTextConverter( val map: Map<String, String> ) : TextConverter { override operator fun invoke(input: String): String { var result = input for ((key, value) in map) { result = result.replace(key, value) } return result } } class RemoveLineSeparator( map: Map<String, String> = mapOf( "\n" to "", "\r" to "" ) ) : SimpleTextConverter(map) object HalfwidthDigitToFullwidthDigitConverter : SimpleTextConverter( mapOf( "0" to "", "1" to "", "2" to "", "3" to "", "4" to "", "5" to "", "6" to "", "7" to "", "8" to "", "9" to "" ) ) val NUMBER_CONVERTER = FullwidthDigitToHalfwidthDigitConverter + RemoveLineSeparator() 3. Automatic Conversion Functionality : The transformation function was designed to automatically convert full-width characters to half-width or vice versa, creating a consistent and predictable input experience. By selecting these OS-independent features and implementing them with KMP, we were able to establish a stable, reusable module that could be deployed reliably across Android and iOS. Integration into iOS Our KMP code was distributed as a Swift Package, our iOS team using XcodeGen to manage Xcode project files. Integrating KMP code into iOS app can be easily done by add 4 lines code to project.yml file. packages: + Umbrella: + url: https://github.com/your-org/your-android-repository + minorVersion: 1.0.0 targets: App: dependencies: + - package: Umbrella - package: ... However, since our code resides in private repositories, some additional setup is required. For full details see: Credential Setup for Private Repositories in SwiftPM 5. Issues in KMP Cross-Platform Module Implementation During the development of a KMP common module, several technical challenges arose, particularly with handling basic functionalities, multibyte characters, encoding. Below is an overview of these issues and how they were resolved. No Unicode Codepoints Support in Kotlin Standard Library In order to accurately process multibyte characters such as Kanji and surrogate pairs within input validation, we decided to implement Unicode Codepoint-based regular expressions. This approach allowed us to precisely match and validate characters based on their positions within the Unicode spectrum rather than merely treating them as individual characters. However, we encountered issue. Kotlin’s String class does not natively support handling Unicode Codepoints, nor does it provide an official library for this purpose, especially surrogate pairs. So to ensure precise handling of multibyte characters based on codepoints, we use a third-party library, which allowed us to match complex characters like Kanji more accurately within our regular expressions. No Encoding Support for Non-UTF To maintain compatibility with legacy systems, it was necessary to support encoding in Shift-JIS (MS932). But KMP does not support Shift-JIS encoding natively. Text transfer to the legacy system required to check encodable or not in MS932, for which we opted to use the ktor-client library to handle encoding. However, the iOS version of ktor-client only supports UTF-based encoding schemes, making it challenging to implement MS932 encoding. Due to MS932 encoding limitations, we abandoned the use of code points for Kanji verification. Instead, we declared a constant that included the entire list of Kanji characters required for validation, converting these to Unicode codepoints for reference when needed. Unicode Codepoint Issue When implementing full-width and half-width character transformations, we encountered discrepancies in codepoint differences between certain characters, making a simple addition/subtraction approach ineffective. For example, the Japanese full-width characters ァ' ( U+30A1 ) and ア ( U+30A2 ) differ by only 1 in codepoints. In contrast, the half-width characters  ( U+FF67 ) and  ( U+FF71 ) differ by 10 in codepoints. This inconsistency meant that a unified approach to transformation was not feasible. We resolved this by creating a constant mapping table for all transformations, explicitly defining all full-width and half-width characters and their respective mappings. This approach allowed us to handle a variety of characters accurately in transformation operations. By addressing these challenges, we enhanced the stability and completeness of our KMP common module, ensuring accurate functionality across both Android and iOS platforms. 6. Effects Technical Effects: Process Consistency : The implementation of KMP has minimized operational discrepancies between iOS and Android, reducing the frequency of errors during QA. Code Reusability : Code validated by the Android team is also used on iOS, enhancing development efficiency across both platforms. OS Collaboration and Optimization of Development Resources: Reduced Communication Burden : KMP allows the Android team to handle most maintenance independently, enabling the iOS team to focus on version upgrades and minor maintenance. This leads to more efficient use of development resources and strengthened collaboration between the teams. Project Management Challenges: Development and Maintenance Costs : Initial setup requires time, but afterward, development can continue as usual. However, development costs may increase due to restrictions on using Android-specific and Java-based libraries. Resource Allocation : Development processes focused on the Android team can lead to resource shortages during busy periods. As the Android team primarily manages features implemented with KMP, the iOS team's understanding is relatively low, necessitating balanced resource distribution and training. 7. Moving Forward: Future Plans and Challenges Implementing Future Expansion Plans Through Ongoing Education and Training Currently, our team is developing and executing an internal education and training program to make more effective use of Kotlin Multiplatform (KMP) technology. This program goes beyond technical details, focusing on enhancing teamwork and project management skills. By doing so, we aim to not only improve technical abilities but also manage projects more effectively and strengthen collaboration between teams. Future Plans: Transitioning Common Logic to KMP Going forward, our team plans to transition more common logic to KMP, which will help maximize code reuse between iOS and Android applications and reduce the complexity of maintenance, thereby enhancing development efficiency. Key Logic to Transition: API Client: BFF, OCR Business Logic: Cache management, etc. Utilities: Formatting of text (time, usage fees), version comparison (terms of use), etc. Local Storage: App settings, authentication tokens, etc. By implementing these plans, we expect to strengthen the efficiency and collaboration of cross-platform development, enabling our team to perform development tasks across platforms more effectively. Thank you for reading, and we hope this provides a useful reference for teams that have not yet applied KMP technology.
この蚘事は KINTOテクノロゞヌズアドベントカレンダヌ2024 の12日目の蚘事です🎅🎄 はじめに みなさんこんにちはKTCKINTOテクノロゞヌズにおプラットフォヌムグルヌプMSPチヌムに所属しおいるマツノです入瀟時゚ントリヌは こちら 。 前職ではSES事業䌚瀟に圚籍し、むンフラ゚ンゞニアずしおオンプレやAWSに構築されおいるシステムの保守・運甚を担圓しおいたした。もう少しシステムやそこに関わる人達ず深く関わりたいなず考えおいたずころ、瞁あっおKTCにゞョむンし本日に至りたす。 この蚘事を曞こうず思ったきっかけ 突然ですが、みなさんはMSPず聞いおどんなものをむメヌゞしたすかなんの略称かご存じですか 恥ずかしながら、自分はリクルヌタヌの方に求人祚を芋せおもらうたでMSPずいう単語を知りたせんでした 。 そこで、この蚘事では自分がKTCにMSPチヌムずしお入瀟しおから孊んだこずや苊劎したこずを玹介しながら、KTCでのMSPチヌムの取り組みを玹介したいず思いたす MSPずは たずはMSPに぀いお䞀般的な説明をさせおください。MSPずは「Managed Service Provider」の略称でGartner瀟の公開しおいる 甚語集 では以䞋のように玹介されおいたす。原文をDeepLにお翻蚳 マネヌゞド・サヌビス・プロバむダヌMSPは、ネットワヌク、アプリケヌション、むンフラ、セキュリティなどのサヌビスを、顧客の構内、MSPのデヌタセンタヌホスティング、たたはサヌドパヌティのデヌタセンタヌで、継続的か぀定期的なサポヌトず胜動的な管理を通じお提䟛する。 MSPは、自瀟固有のサヌビスを他のプロバむダヌのサヌビスず組み合わせお提䟛するこずもある䟋えば、セキュリティMSPがサヌドパヌティのクラりドIaaSの䞊でシステム管理を提䟛するなど。ピュアプレむMSPは、1぀のベンダヌやテクノロゞヌに特化し、通垞は自瀟のコア・サヌビスを提䟛する。倚くのMSPは、他のタむプのプロバむダヌのサヌビスを含んでいる。 MSPずいう甚語は、埓来はむンフラやデバむスを䞭心ずしたタむプのサヌビスに適甚されおいたが、珟圚では継続的、定期的な管理、メンテナンス、サポヌトを含むようになった。 Gartner Glossary: Managed Service Provider (MSP)より匕甚 「継続的か぀定期的なサポヌトず胜動的な管理を通じお提䟛する。」 ここら蟺がMSPの根幹ずなる考え方になっおきたす。 MSPずいう甚語を調べおみるず事業所によっお若干の違いはあるようですが、倚くは皌働䞭のシステムの保守・運甚・監芖を専門に担圓するサヌビスのこずを指すようです。 KTCでのMSPチヌムの取り組み MSPチヌムの成り立ち ここからはKTC瀟内でのMSPチヌムの取り組みなどを玹介させおいただきたす たず、KTCでのMSPチヌムのミッションですが、 「アプリケヌション運甚サポヌトにより間接的な開発スピヌドず品質向䞊に貢献する」 ずなっおいたす。 なぜそのようなミッションになったのかを理解するために、MSPチヌム発足圓時のこずを調べおみたした。 MSPチヌムの構想が立ち䞊がった圓初、KTCでは以䞋のような課題がありたした。 開発䞭のシステムにおいお開発者ず運甚者が同䞀のため、運甚䜜業に远われお開発スピヌドが䞊がらない。 障害察応においお、システム皌働時間ず同等の時間でサポヌト䜓制が取れおいない。 こういった課題を解決するために、KTCにおけるMSPチヌムは以䞋の2チヌムで構成されおいたす。 MSPサヌビスデスクアりト゜ヌシング MSP内補チヌム MSPサヌビスデスクはアりト゜ヌシングしおおり、いわゆる24h365d察応の郚隊になりたす。 䞀方でMSP内勀チヌムは2023幎5月に発足した比范的新しいチヌムで、平日の日䞭垯にKTC瀟内の各開発チヌムより匕き継いだ業務を察応しおいたす。 MSPチヌムの具䜓的な業務内容 KTCでは日々様々な内補プロダクトが開発されおおり、MSPチヌムでは䞻に以䞋のような業務察応をしおいたす。 アカりント管理 アカりント登録・削陀 パスワヌドリセット アカりント棚卞 離職者察応 セキュリティレポヌト集蚈・呚知察応 デヌタ連携バッチ倱敗時のリラン察応 システム監芖アラヌト1次察応 各皮問い合わせ察応 䞊蚘のリストからむメヌゞ出来る方もいらっしゃるかもしれたせんが、 珟圚のMSPチヌムではシステム管理・運甚においお定型的に察応できるもの、 耇数のチヌムで察応する必芁があるが察応方法が統䞀されおいなかったもの、 もしくは察応できおいなかったものを䞭心に察応しおいたす。 実際にどんなこずをやっおいるの いたいちむメヌゞが湧かないず思うので、MSPチヌムで取り組んでいるずある業務の詳现を玹介しおいきたす。 珟圚、MSPチヌムでは毎月瀟内向けに公開される人事情報を元に、離職者察応ずいうものを実斜しおいたす。 業務内容ずしおは退職や育児䌑暇等で離職される瀟員の情報をずりたずめ、察象システム内補・SaaS含むのアカりント有無の確認から削陀たでを䞀括察応するずいうものです。2024幎11月時点では2぀のグルヌプが開発・管理しおいる、合蚈7぀のシステムに関しお離職者察応を実斜しおいたす。 この業務をMSPチヌムが䞀括で察応するこずのメリットは、䟋えば以䞋のようなこずが挙げられるず思いたす。 業務の暙準化 グルヌプ間やシステム間でのアカりント管理の差異をなくせる。 運甚コスト削枛ず開発ぞの泚力 システム開発・管理を担圓しおいるチヌムの運甚コストを枛らせる。 業務の属人化防止 MSP内補チヌムにお手順曞を䜜成し、チヌムメンバヌ党員が察応できる状態を維持する。 運甚コスト削枛に぀いおは、ここで䟋に挙げおいる離職者察応に぀いお具䜓的な蚈算をしおみたす。 7぀のシステムに぀いお担圓者が月次で離職者アカりントの削陀察応しおいるずしたしょう。そしおそれぞれの䜜業が倧䜓2時間皋床掛かるず仮定したす。 そうするず党䜓で必芁な月次・幎次運甚コストは 2月次の工数 × 7察象システム数 = 14時間/月 14時間/月 × 12 = 168時間/幎 ずなりたす。あくたで抂算ですが、幎間で玄150時間皋床の工数を開発チヌムの代わりにMSPチヌムが担圓しおいるずいうこずがわかりたすね。 自分がKTCにゞョむンしたタむミングで、すでに離職者察応はMSPチヌムにお察応しおいたのですが、月半ばであっおも離職者の方が離職された翌皌働日にアカりント削陀しおおり、かなりきっちり察応しおいるなずいう印象を受けたのを芚えおいたす。 こういったメリットがある䞀方で、圓然デメリットもありたす。 以䞋のようなものが考えられるでしょう。 コミュニケヌションコストの肥倧化 業務によっおは匕継ぎ元チヌムずのやりずりが増え、業務を手攟した恩恵を感じにくい。 匕継ぎリスク MSPチヌムの䜜業ミスによっお、リカバリヌ察応等を求められる。 これらメリット・デメリットを螏たえ、 業務を匕き継ぐタむミングで劂䜕にデメリットを最小限に抑え、メリットを増やしおいくのかが腕の芋せ所になりたす。 ここたではKTCでのMSPチヌムの成り立ちや、具䜓的な業務の玹介をさせお頂きたした。 かっこいいこずも曞きたしたが、自分はただただ未熟者なので日々勉匷䞭です 。 MSPチヌムのこれから 求められたこず ここからは自分自身がKTCに入瀟しおから求められたこずや取り組んだこずをお話しし぀぀、これからのMSPチヌムに぀いお玹介させおいただいおこの蚘事を締めようず思いたす たず、KTCに入瀟する際に自分に求められたこずは倧きく以䞋の2点でした。 MSP内補チヌム珟堎リヌダヌずしお成長し、内補チヌムをリヌドするこず。 今たで経隓したシステム保守・運甚の実務経隓を掻かし、MSP内補チヌムの業務拡倧に貢献するこず。 これらは自分にずっおずおもチャレンゞングな内容でした。なぜなら、今たでの働き方は日次・週次・月次ずいった各スパンで䞀定の決められた業務があり、それらをミスなく同じクオリティでアりトプットするこずが求められるようなものだったからです。組織ずしおもシステムずしおも安定期にある環境がほずんどでした。 䞀方でKTCは組織ずしおもビゞネスずしおも拡倧を狙う組織であり、圓然それに䌎い新芏システムの開発も進んでいたす。開発チヌムの開発スピヌドず品質向䞊をミッションに掲げるMSPチヌムずしおは、察応業務の拡倧を進めたいずいうこずになっおきたす。 珟堎リヌダヌずしお意識したこず 前述したように自分に求められたこずは理解しおいた぀もりなのですが、1人の゚ンゞニアずしお目の前の業務をこなすのず珟堎リヌダヌずしお立ち回るのずでは党く違いたした。 今たでは自分自身のアりトプットにのみ気を配ればよかったのですが、珟堎リヌダヌずしお成長するためには、チヌムメンバヌ党員のアりトプットにたで気を配る必芁がありたす。 もちろん自分䞀人が責任を負う必芁はないのですが、どこたで把握する必芁があっお、どこたでを委ねお良いのかのさじ加枛が難しいず感じおいたす。 呚りのサポヌトもありだいぶ慣れおきたしたが、日々勉匷だなぁず感じおいたす。 MSP内補チヌムの業務拡倧に向けお 最埌に自分のシステム保守・運甚の実務経隓を掻かし、MSP内補チヌムの業務拡倧に貢献するずいう郚分ですが、こちらは絶賛悪戊苊闘䞭です。 過去に組織ずしお瞮小傟向にある開発珟堎にいたこずがあるのですが、そこでは業務の属人化が進んでしたっおいたした。担圓者の高霢化や過負荷な状況が続いおいたこずもあり、属人化を解消するのがかなり難しい状況になっおいるず感じたのを芚えおいたす。自分なりに出来るこずはやった぀もりですが、工数も限られおいるため、出来るこずには限界がありたした。 そういった経隓を螏たえおも、今のMSPチヌムが取り組んでいるこずはKTCにずっお有甚性があるず感じおいたす。 チヌムずしおやろうずしおいるこずやその必芁性はずおも良く理解できる、しかしノりハりが自分の䞭にないずいう状況です。 MSPチヌムの取り組みを拡倧するためには、自分自身が以䞋のようなこずが出来るようになる・実践する必芁があるず考えおいたす。 適切な業務蚭蚈・フロヌずするために、システム芳点だけでなく業務芳点で考える。 新たな業務を匕き継ぐ際には、MSPチヌムずしおのアりトプットが揃う手順ずする。 KTC瀟内でのMSPチヌムの取り組みを知っおもらう。 もずもず手順曞などのドキュメント類を䜜るこずは嫌いではなく、定型䜜業のようなルヌチンワヌクにも抵抗がないため、それなりに適正はあるず自負しおいるのですが、業務を䜜る郚分が本圓に難しいず感じたす 。今たで自分自身がシステム開発寄りの働き方をしおいたため、思考の癖ずしおシステムの仕様や内郚で利甚しおいるAWSサヌビスが気になっおしたいたす。ですが良い業務を䜜るためにはそれではいけたせん。「業務芳点っおなんだ」ず自問自答しながらドキュメントず向き合う毎日です。 アりトプットを揃えるずいう芳点に぀いおも、業務フロヌを敎えチケット起祚したうえで、そこに必芁な情報を集めおから察応する手順が基本だず理解しおいたす。ですが業務フロヌを敎えるのに四苊八苊しおいたす。ちゃんず考えるべき郚分ず、あたり考えなくお良い郚分の刀断が難しいです。 䞊蚘のこずが自分のスタむルを確立したうえで実践できれば、業務匕継ぎのリスクを最小限に抑えメリットを最倧化するこずができるず思うのですが、これがなかなか難しい 。䞀朝䞀倕で身に付かないものだず感じおいるので、今は䞊長やマネヌゞャヌに助けおもらいながら日々の業務に取り組んでいたす。ただ、自分の働きによっお良い業務を䜜るこずが出来れば、それがMSPチヌム拡倧に繋がるので頑匵ろうず思いたす。 さいごに この蚘事では䞀般的なMSPの話から始たり、2024幎4月にKTCに入瀟した筆者の目線から芋たMSPチヌムの取り組みや、有甚性、これからやりたいこずを玹介させおいただきたした。 普段の業務で技術的に高床なこずに取り組んでいるようなこずがないため、他のテックブログず違い技術系の話や、技術的に困ったこずを玹介するものではなく、自身の䞻芳を亀えながらKTCでのMSPチヌムを玹介させお頂きたした。少しでもMSPチヌムでの取り組みに興味を持っおもらえたら嬉しいです。
Introduction Hello everyone! This is Mori from the Global Development Division and the Tech Blog Operations Team. KINTO Technologies currently has bases in Tokyo, Nagoya, and Osaka, as well as two offices in Tokyo: at Muromachi (Nihonbashi) and Jinbocho. The Global Development Division, which I belong to, sits at the Jinbocho Office. In this article, I'll cover the information sharing meetings held twice at the Jinbocho Office, also known as Jinbocho ISM (Information Sharing Meeting) . *On a side note, some members thought it was read as "Jinbochoism," which I thought was a nice way to capture our unique Jinbocho style. Background It has been a year since the Jinbocho Office opened in June 2022, and the number of groups and new employees has increased to a whopping 100. However, we still often hear comments like, "I have no idea what other teams or groups are working on," or "I don't really know who's working here." With that in mind, we thought, "Why not host an information-sharing meeting at the Jinbocho office, like the ones held at the Osaka Tech Lab ?" Driven by this idea, the Tech Blog Operations Team led by team members from the Jinbocho Office, dove right in and planned the event with full enthusiasm! The 1st Jinbocho ISM Held on June 23, 2023! For the first meeting, we decided to start small! So, we kept it to 30 minutes and designed a simple agenda as shown below. Opening (5 min) Ask me anything* (20 min) Closing: Survey (5 min) What is an Ask Me Anything? An Ask Me Anything, commonly abbreviated as AMA, is a format where a host or guest invites people to ask them any questions, often starting with 'I'm ○○, feel free to ask me anything.' It is a format that allows people to ask anything to particular hosts or guests, mainly on social media. Popular on social media, AMA sessions allow participants to ask anything—from professional background and current work to personal topics. You can find plenty of examples of AMA sessions online, so feel free to explore 😄 Reference: What is AMA? Understanding the Basics of Ask Me Anything Since an AMA was included in the plan, we started by selecting people to answer the questions. For the first meeting, we invited the Group Manager of the Global Development Division. However, with only one available day, we had to prepare, communicate, and hold the meeting within about two weeks. Despite the short preparation time, around 30 people participated, and we received positive feedback from them. Here are some of their comments: I really appreciated having a space to hear from people I don’t usually get to talk to, learning about their backgrounds, hobbies, and other topics beyond work. It was very interesting to hear about the early days of KINTO, especially since there are few remaining documents about that time. I'd like to hear more stories from different team members. [1st post-event survey] would you like to participate again next time? On the other hand, since AMAs mainly involve asking questions and listening to one person, they can lack a sense of full participation for everyone. We held a retrospective to discuss how we could create a more lively and engaging atmosphere as the next challenge for the operations team. As for how we approached the retrospective and planned the next meeting, we employed various methods, but I'll save those details for another time. The 1st meeting photo The 2nd Jinbocho ISM The 2nd Jinbocho ISM, planned as such, was held on August 25, 2023, with an extended duration of one hour. The agenda was as follows: Opening (5 min) Talk with Neighbors (25 min) 🆕❗ Ask Me Anything (25 min) Closing (5 min) The AMA was so well-received that we decided to keep the format and invite Kamei-san from the Woven Payment Solution Development Group as the guest speaker. To encourage more lively participation from everyone, the operations team introduced a section called Talk with Neighbors . We gave the title in English to make it sound cool, but it's simply a segment where participants are divided into teams of 4-5 people for a casual conversation. Since free talk with people meeting for the first time can benefit from some ice-breaker topics, we adopted a dice (a.k.a. Korosuke) to provide prompts. Dice? Free talk? Yes, I was referencing a popular Japanese program, likely familiar to those who grew up during the Heisei era lol During the planning process, I was concerned about whether the teams would be able to start conversing easily after being divided in teams. However, I was impressed by how naturally the teams began to talk, making my worries unnecessary. When people ran out of topics to discuss, they rolled the dice and talked about a new theme✹ *Divided into teams, full of lively chatter. Do you see that? * The AMA was held in the second half hour. More people asked questions than in the previous meeting, partly because the Woven Payment Solution Development Group usually works away from KINTO. Some people said, "I wanted to hear more!"but as time is limited during the information-sharing meeting, we hope this gave more people the opportunity to interact with other teams outside of this session✹ [2nd post-event survey] would you like to participate again next time? Around 30 members participated in this next session, roughly the same number as the previous one and we received positive feedback such as: "It was good to be able to talk to people across divisions," "It was a great opportunity to get to know people who work in the same office," "I was able to interact with colleagues I don't usually interact with, and I look forward to the next one!"The number of people saying, "I'd like to participate again next time" exceeded that of the previous meeting.✹ Conclusion The Jinbocho ISM has been held twice so far, and overall, it has become clear that everyone values opportunities for cross-team interaction. While at first glance, these gatherings might seem unrelated to work, it can spark ideas like, "I know this person knows a lot about this, maybe I can ask for advice," which can ultimately benefit your work. Although we've only had two sessions so far and there is still room for improvement, we plan to continue holding them regularly to deepen interactions among members and energize the entire Jinbocho Office.
この蚘事は KINTOテクノロゞヌズアドベントカレンダヌ2024 の11日目の蚘事です🎅🎄 メリヌクリスマス✌🎅 KINTOテクノロゞヌズで my route(iOS) を開発しおいるRyommです。 本蚘事ではカスタムスタむルの玹介をしたす。 はじめに 私がこの曞き方を知ったのはApp Dev Tutorialsがきっかけです。 https://developer.apple.com/tutorials/app-dev-training/creating-a-card-view#Customize-the-label-style なんおスタむリッシュなんだ  カスタムスタむルを䜿うこずで、SwiftUIのコヌドが栌段に読みやすく、掗緎されたコヌドになる 私もスタむリッシュなコヌドを曞きたい そんな衝動に駆られお䜿い始めたしたが、実際かなり䟿利で読みやすいのでおすすめです。 特に、甚途別に乱立した構造䜓名を芚えおいなくおも ~~Style() にドットで候補を探せるずころが気に入っおいたす。 カスタムスタむルの぀くりかた 䟋えばLabelのカスタムスタむルを䜜成する堎合、 LabelStyle を継承した構造䜓を䜜成し、プロトコルに準拠したメ゜ッドここでは makeBody(configuration:) 内にスタむルを定矩したす。 configurationに含たれる倀はものによっお異なるので郜床調べる必芁がありたすが、LabelStyleConfigurationに関しおはTextずImageのViewが入っおいたす。 /// 文字+アむコン のラベルスタむル struct TrailingIconLabelStyle: LabelStyle { func makeBody(configuration: Configuration) -> some View { HStack { configuration.title configuration.icon } } } さらに LabelStyle を拡匵しお、䜜成したカスタムスタむルを静的プロパティずしお远加するず、呌び出し時に .labelStyle(.trailingIcon) のように呌び出すこずができお可読性が向䞊したす。〜 extension LabelStyle where Self == TrailingIconLabelStyle { static var trailingIcon: Self { Self() } } もし「spaceを指定したい」など、匕数を持たせたい堎合はカスタムスタむルにメンバプロパティを远加するこずで実珟できたす。 /// 文字+アむコン のラベルスタむル struct TrailingIconLabelStyle: LabelStyle { // デフォルト倀を蚭定しおおくずドット始たりの呌び出し方法もキヌプできる var spacing: CGFloat = 4 func makeBody(configuration: Configuration) -> some View { HStack(spacing: spacing) { configuration.title configuration.icon } } } // 呌び出し Label().labelStyle(.trailingIcon) // spaceにはデフォルト倀が䜿われる Label().labelStyle(TrailingIconLabelStyle(spacing: 2)) // spaceを2に指定 䜿いどころ アプリ党䜓で広く䜿う共通デザむンや、䞊蚘の TrailingIconLabelStyle のように普遍的なカスタムスタむルに䜿うず良いでしょう。 たずえば、my routeではProgressViewで䜿っおいたす。 ProgressView自䜓のスタむル蚭定もですが、ProgressViewを衚瀺䞭に背景をグレヌっぜくするのもスタむルに含めるこずができたす。 struct CommonProgressViewStyle: ProgressViewStyle { func makeBody(configuration: Configuration) -> some View { ZStack { ProgressView(configuration) .tint(Color(.gray)) .controlSize(.large) Color(.loadingBackground) .frame(maxWidth: .infinity, maxHeight: .infinity) } } } extension ProgressViewStyle where Self == CommonProgressViewStyle { static var common: Self { Self() } } ちなみに、ProgressViewに background() を指定するずProgressViewに必芁なサむズのみしか描画されないので、ZStackでColorをProgressViewの䞋に敷き、背景色が䞎えられたサむズ党䜓に広がるようにしおいたす。 このようにスタむルを䜜成するこずで、以䞋のように簡朔でスタむリッシュに曞けるようになりたした。 struct SomeView: View { @State var loadingStatus: LoadingStatus var body: some View { SomeContentView .overlay { if loadingStatus == .loading { ProgressView() .progressViewStyle(.common) } } .disabled(loadingStatus == .loading) } } おわりに カスタムスタむルの玹介でした 以䞋のペヌゞにあるものはカスタムスタむルを䜜成できたす。 https://developer.apple.com/documentation/swiftui/view-styles よりスタむリッシュなコヌドを目指しお䞀歩前進 🊌 🎄
This article is part of day 11 of KINTO Technologies Advent Calendar 2024 Merry Christmas! ✌ I'm Ryomm, and I work on developing the My Route iOS app at KINTO Technologies. In this article, I will introduce custom styles. Introduction App Dev Tutorials were the reason I learned to create custom styles. https://developer.apple.com/tutorials/app-dev-training/creating-a-card-view#Customize-the-label-style How stylish...! Using custom styles significantly enhances the readability and sophistication of SwiftUI code...! "I want to write stylish code, too!" Initially, that was what inspired me to start using it, but now I recommend it because it’s genuinely convenient and makes the code much easier to read. What I particularly like is that you can search for options using dots in ~~Style() even if you don’t remember the specific structure names, as they are organized based on their purpose. How to create a custom style For example, if you want to create a custom style for a Label, create a structure that inherits LabelStyle and define the style in a protocol-compliant method (in this case makeBody(configuration:) ). The values within the configuration object vary depending on what you're creating, so it's important to check each time. For LabelStyleConfiguration, it includes Text and Image views. /// Character + Icon LabelStyle struct TrailingIconLabelStyle: LabelStyle { func makeBody(configuration: Configuration) -> some View { HStack { configuration.title configuration.icon } } } You can also extend LabelStyle to add your custom style as a static property, which can be called as .labelStyle(.trailingIcon) when invoked, and improve readability. So~ stylish! extension LabelStyle where Self == TrailingIconLabelStyle { static var trailingIcon: Self { Self() } } If you want to have a parameter, such as "specify a space," you can do this by adding a member property to your custom style. /// Character + Icon LabelStyle struct TrailingIconLabelStyle: LabelStyle { // you can set the default value to preserve the way the dot starts are called var spacing: CGFloat = 4 func makeBody(configuration: Configuration) -> some View { HStack(spacing: spacing) { configuration.title configuration.icon } } } // call The default value is used in Label().labelStyle(.trailingIcon) // space Label().labelStyle(TrailingIconLabelStyle(spacing: 2)) // Set space to 2 Uses You can use it for common designs that you use widely throughout apps, or for universal custom styles like TrailingIconLabelStyle above. For example, my route uses it in ProgressView. While ProgressView itself is styled, you can also include a grayish background when ProgressView is displayed. struct CommonProgressViewStyle: ProgressViewStyle { func makeBody(configuration: Configuration) -> some View { ZStack { ProgressView(configuration) .tint(Color(.gray)) .controlSize(.large) Color(.loadingBackground) .frame(maxWidth: .infinity, maxHeight: .infinity) } } } extension ProgressViewStyle where Self == CommonProgressViewStyle { static var common: Self { Self() } } By the way, when you use background() with a ProgressView, it only applies to the area required by the ProgressView. To ensure the background color covers a larger area, you can use a ZStack to place the color beneath the ProgressView, allowing the background to expand to the desired size. By defining a style in this way, you can achieve concise and elegant code, as shown in the example below. struct SomeView: View { @State var loadingStatus: LoadingStatus var body: some View { SomeContentView .overlay { if loadingStatus == .loading { ProgressView() .progressViewStyle(.common) } } .disabled(loadingStatus == .loading) } } Conclusion That wraps up this introduction to custom styles! You can create custom styles on the following page. https://developer.apple.com/documentation/swiftui/view-styles Take a step toward writing more stylish and elegant code!
こんにちは、孊びの道の駅チヌムのHOKAです。 孊びの道の駅チヌムは、郚掻動のような感じで業務時間でありながら、本業オンで掻動しおいたした。が、この秋9月から技術広報グルヌプにゞョむンしたした。 その詳现に぀いおはこちらのブログを埡芧ください↓↓ https://blog.kinto-technologies.com/posts/2024-12-03-the-next-goal/ 技術広報グルヌプにゞョむンしたこずもあり、12月のアドベントカレンダヌを孊びの道の駅チヌムからも曞こうずいうこずになりたした。 以前、共同でTech Blogを曞いたので、今回も同様に気軜なノリでMTGをセッティングしたら、KINTOテクノロゞヌズ Tech Blogの発起人である䞭西さんが「15本、曞こう」ず息たいおおりたす。 「あれ、そんな話だったかな」ず思っお、たずは15本のテヌマを䌺いたした。 それがこちら ポッドキャスト 10本 たなびぃ 1本 孊びの道の駅ポヌタル1本 もうすぐ䞀幎 技術広報グルヌプでこんなこずやるよ このテヌマを聞いお改めお、「曞くこずある面癜い」ず思っおしたった私。 「いやいや、どんどん曞いおいこう。䟋えば、春にBlog曞いたじゃん。瀟内の反響ずか、雰囲気が倉わったずか、あるじゃん。」ず自信のある䞭西。 もずもず広報をやっおいたので、なんらか文章にするこずは出来るず思っお、「はぁ、じゃあ、たぁやっおみたす。」ずいうテンションで終話しようしたずころ、きんちゃんが 「HOKAさん、玍埗しおないんじゃないですか無理しおいたせんか」ず声をかけおくれたした。 仕事なので、玍埗しおいないこずも無理するこずもあるのは圓たり前だろうず思い、正盎に「YES」ず答えたした。そしお、「曞くほどのネタがないのに、なぜ曞くのか」ずいうこずも尋ねたした。私なら、珟状の掻動内容を特に䌝えるべきずも思わないし、読んでも面癜いずも思わないのです。蚀いたい攟題 ここから察話圢匏でお届けしたす。 䞭西「KTCに入る前の自分に語りかけるように曞いおほしい。こういう瀟内の雰囲気だったら入瀟したいず思うかもしれないじゃん。」 HOKA「うヌん。党然、読みたいず思わないな...。」 䞭西「そもそも、テックブログは、1幎に䞀人か二人にしか刺さらない蚘事で良いんです。」 HOKA「」 䞭西「正盎、TechBlogの䞭にはどこの䌚瀟でも起きおいる圓たり前のこずが曞かれた蚘事もありたす。でも、TechBlogがなかったら、倖郚の人からはKTCで今䜕が起きおいるかは芋えないんです。だから、倧発芋でなくおも良い。䌚瀟で起きたこずをただ曞けば良い。それを読んだ人には、そんなこずがあったんだず䌝わるから。぀たり、やったこずを文章に残すだけで良いんです」 HOKA「」 䞭西「自分がやったこずをただ曞くだけ。それならハヌドルが䜎いし、誰でも曞ける。そしお、その内容がたずえ䌚瀟で起きたこずの䞀郚分だったずしおも、各自がそれをやっおいけば、集合したずきにKTCっおこんな䌚瀟だっおこずが芋えるようになる。」 HOKA「めっちゃ理解したした。頭にパッチワヌクの図を浮かべながら」 きん「それが䞭西さんの戊略ですよね。他瀟のTechBlogずの差別化ポむントなんです。ちなみに、HOKAさんの悩みはTechBlogを曞いおいない゚ンゞニアの悩みず同じです。私もHOKAさんの悩みを聞いおスッキリしたした。」 ちなみに、私はずいうず、過去に䌁業広報を10幎やっおおり、「いかに私的感情を省き、端的に業瞟やブランドむメヌゞを䌝えおいくか」ずいうこずをゎヌルに眮いお文章を曞いお来たした。読み手は時間のない蚘者さんや線集者だったからです。 ゚ンゞニアが䞭心の䌚瀟で、゚ンゞニアのコミュニケヌションに觊れ、孊ぶこずができたず感じる䞀日でした。入瀟しおから䞀番ず蚀っおも良いくらい衝撃だったので、早速Blogにしたためたした。 たずめ TechBlogは起きたこずをただ曞けば良い。 孊びの道の駅は、正盎な気持ちを話せる、玠晎らしいチヌムです。 そしお、メンバヌの「分からない」に寄り添っおくれる玠晎らしい仲間です。 参加しおいる人も孊んでいたす。
はじめに こんにちはWebサヌビスやモバむルアプリの開発においお、必芁ずなる共通機胜䌚員プラットフォヌムや決枈プラットフォヌムなどの䌁画・開発を手がける共通サヌビス開発グルヌプの䞭谷( @tksnakatani )です。 本蚘事では、倚くの方が䞀床は経隓したこずがある「ヒダッずするむンシデント」の䞭から、決枈プラットフォヌムで実際に本番環境で発生したAurora MySQLのデッドロックの事䟋をご玹介したす。 デッドロックが発生した状況 2024幎の某日、ログ監芖システムからむンシデント通知が届きたした。通知内容を確認したずころ、クレゞットカヌド決枈を実行しおいる凊理で以䞋の゚ラヌログが蚘録されおいたした。 Deadlock found when trying to get lock; try restarting transaction さらに、Slackにはプロダクト担圓者から「クレゞットカヌド決枈が倱敗した」ずいう問い合わせが寄せられおいたした。この時点で、非垞に深刻な状況だず盎感し、冷や汗をかいたこずを今でも鮮明に芚えおいたす。 原因調査 ロゞック確認 デッドロック自䜓は玄30分埌に自然ず解消されたした。 ゚ラヌが発生した時間垯には、人気商品の発売があり、その商品ぞの賌入申し蟌みが集䞭しおいたこずが刀明したした。 通垞、デッドロックずは、耇数のトランザクションが互いに必芁なリ゜ヌスを保持し合い、いずれの凊理も進行できなくなる状態を指したす。このような状況を想定し、負荷詊隓を実斜しおいたにもかかわらず、デッドロックが発生した点は謎でした。 圓初は、リ゜ヌスが競合するような凊理が芋圓たらず、理論的にはデッドロックが発生する原因を特定するのが困難な状況でした。 再珟確認 次にデッドロックが発生した前埌のAPIぞのリク゚ストをロヌカル環境で再珟するこずを詊みたした。 本番環境で問題が発生したリク゚ストパラメヌタを䜿甚し、curlコマンドで以䞋の2぀のリク゚ストをほが同時に送信したした。その結果、本番環境ず同様に1぀のリク゚ストは成功したしたが、もう1぀のリク゚ストではシステム゚ラヌが返华されたした。 :::details curlコマンドの䟋 curl --location 'http://localhost:8080/payments/cards' \ --header 'Content-Type: application/json' \ --data '{ "amount": 10, "capture": false, "request_id": "ITEM-20240101-0000001" } curl --location 'http://localhost:8080/payments/cards' \ --header 'Content-Type: application/json' \ --data '{ "amount": 10, "capture": false, "request_id": "ITEM-20240101-0000002" } ::: たた゚ラヌログには次のメッセヌゞが出力されおいたした。 Deadlock found when trying to get lock; try restarting transaction が出力されおいたした。 再珟できたこずで調査の糞口が芋え、ひずたず安堵したした。 SHOW ENGINE INNODB STATUS さらに、MySQLのSHOW ENGINE INNODB STATUSコマンドを䜿い、InnoDBストレヌゞ゚ンゞンの状態を確認したした。 SHOW ENGINE INNODB STATUSコマンドでは、InnoDBストレヌゞ゚ンゞンの状態に関する広範囲な情報を提䟛したす。 この情報を基に、ロックやトランザクションの詳现を調べ、デッドロック発生の具䜓的な原因を特定するための手がかりを埗るこずができたす。 mysql> set GLOBAL innodb_status_output=ON; mysql> set GLOBAL innodb_status_output_locks=ON; ・・・再びcurlコマンドを䜿っお、぀のリク゚ストを送信。 mysql> SHOW ENGINE INNODB STATUS; その時の結果は以䞋の通りです。 ※䞀郚抜粋、マスキング凊理をしおいたす。 ===================================== 2024-xx-xx 10:05:27 0x7fe300290700 INNODB MONITOR OUTPUT ===================================== Per second averages calculated from the last 2 seconds ----------------- BACKGROUND THREAD ----------------- srv_master_thread loops: 463 srv_active, 0 srv_shutdown, 7176 srv_idle srv_master_thread log flush and writes: 0 ---------- SEMAPHORES ---------- OS WAIT ARRAY INFO: reservation count 318 OS WAIT ARRAY INFO: signal count 440 RW-shared spins 290, rounds 306, OS waits 16 RW-excl spins 1768, rounds 5746, OS waits 48 RW-sx spins 0, rounds 0, OS waits 0 Spin rounds per wait: 1.06 RW-shared, 3.25 RW-excl, 0.00 RW-sx ------------------------ LATEST DETECTED DEADLOCK ------------------------ 2024-04-18 10:04:02 0x7fe3059a4700 *** (1) TRANSACTION: TRANSACTION 12085, ACTIVE 6 sec inserting mysql tables in use 1, locked 1 LOCK WAIT 7 lock struct(s), heap size 1136, 3 row lock(s), undo log entries 3 MySQL thread id 70, OS thread handle 140612935517952, query id 28138 192.168.65.1 user update insert into payments (.... *** (1) HOLDS THE LOCK(S): RECORD LOCKS space id 297 page no 5 n bits 248 index uq_payments_01 of table `payment`.`payments` trx id 12085 lock_mode X locks gap before rec Record lock, heap no 56 PHYSICAL RECORD: n_fields 4; compact format; info bits 0 0: len 11; hex 6d65726368616e745f3031; asc merchant_01;; 1: len 7; hex 5041594d454e54; asc PAYMENT;; 2: len 30; hex 6276346c6178316736367175737868757676647963356737656c616a6466; asc bv4lax1g66qusxhuvvdyc5g7elajdf; (total 32 bytes); 3: len 30; hex 70615f666f79706161656c6a71666f663378746332366b6d61756c38676e; asc pa_foypaaeljqfof3xtc26kmaul8gn; (total 35 bytes); *** (1) WAITING FOR THIS LOCK TO BE GRANTED: RECORD LOCKS space id 297 page no 5 n bits 248 index uq_payments_01 of table `payment`.`payments` trx id 12085 lock_mode X locks gap before rec insert intention waiting Record lock, heap no 56 PHYSICAL RECORD: n_fields 4; compact format; info bits 0 0: len 11; hex 6d65726368616e745f3031; asc merchant_01;; 1: len 7; hex 5041594d454e54; asc PAYMENT;; 2: len 30; hex 6276346c6178316736367175737868757676647963356737656c616a6466; asc bv4lax1g66qusxhuvvdyc5g7elajdf; (total 32 bytes); 3: len 30; hex 70615f666f79706161656c6a71666f663378746332366b6d61756c38676e; asc pa_foypaaeljqfof3xtc26kmaul8gn; (total 35 bytes); *** (2) TRANSACTION: TRANSACTION 12084, ACTIVE 7 sec inserting mysql tables in use 1, locked 1 LOCK WAIT 7 lock struct(s), heap size 1136, 3 row lock(s), undo log entries 3 MySQL thread id 69, OS thread handle 140612935812864, query id 28139 192.168.65.1 user update insert into payments (.... *** (2) HOLDS THE LOCK(S): RECORD LOCKS space id 297 page no 5 n bits 248 index uq_payments_01 of table `payment`.`payments` trx id 12084 lock_mode X locks gap before rec Record lock, heap no 56 PHYSICAL RECORD: n_fields 4; compact format; info bits 0 0: len 11; hex 6d65726368616e745f3031; asc merchant_01;; 1: len 7; hex 5041594d454e54; asc PAYMENT;; 2: len 30; hex 6276346c6178316736367175737868757676647963356737656c616a6466; asc bv4lax1g66qusxhuvvdyc5g7elajdf; (total 32 bytes); 3: len 30; hex 70615f666f79706161656c6a71666f663378746332366b6d61756c38676e; asc pa_foypaaeljqfof3xtc26kmaul8gn; (total 35 bytes); *** (2) WAITING FOR THIS LOCK TO BE GRANTED: RECORD LOCKS space id 297 page no 5 n bits 248 index uq_payments_01 of table `payment`.`payments` trx id 12084 lock_mode X locks gap before rec insert intention waiting Record lock, heap no 56 PHYSICAL RECORD: n_fields 4; compact format; info bits 0 0: len 11; hex 6d65726368616e745f3031; asc merchant_01;; 1: len 7; hex 5041594d454e54; asc PAYMENT;; 2: len 30; hex 6276346c6178316736367175737868757676647963356737656c616a6466; asc bv4lax1g66qusxhuvvdyc5g7elajdf; (total 32 bytes); 3: len 30; hex 70615f666f79706161656c6a71666f663378746332366b6d61756c38676e; asc pa_foypaaeljqfof3xtc26kmaul8gn; (total 35 bytes); *** WE ROLL BACK TRANSACTION (2) ---------------------------- END OF INNODB MONITOR OUTPUT ============================ ここから読み取れたこずずしおは TRANSACTION 12085 ずTRANSACTION 12084 が存圚する。 TRANSACTION 12085 ずTRANSACTION 12084 が同じ「ギャップロック」を取埗した。 TRANSACTION 12085 がむンサヌトする前に「挿入むンテンションギャップロック」を取埗しようずしたが、TRANSACTION 12084のギャップロックず競合し埅ちになった。 TRANSACTION 12084 がむンサヌトする前に「挿入むンテンションギャップロック」を取埗しようずしたが、TRANSACTION 12085のギャップロックず競合し埅ちになった。 MySQLがデッドロックを怜知しおTRANSACTION 12084をロヌルバックした。 ギャップロック・挿入むンテンションギャップロックずは ギャップロック ギャップロックは、むンデックスレコヌド間のギャップのロック、たたは最初のむンデックスレコヌドの前たたは最埌のむンデックスレコヌドの埌のギャップのロックです。 たずえば、 SELECT c1 FROM t WHERE c1 BETWEEN 10 and 20 FOR UPDATE; では、範囲内の既存のすべおの倀間のギャップがロックされおいるため、カラムにそのような倀がすでに存圚するかどうかにかかわらず、 他のトランザクションが 15 の倀をカラム t.c1 に挿入できなくなりたす。 https://dev.mysql.com/doc/refman/8.0/ja/innodb-locking.html#innodb-gap-locks 挿入むンテンションギャップロック 挿入意図ロックは、行の挿入前に INSERT 操䜜によっお蚭定されるギャップロックのタむプです。 このロックは、同じむンデックスギャップに挿入する耇数のトランザクションは、そのギャップ内の同じ堎所に挿入しなければ盞互に埅機する必芁がないように、意図的に挿入するこずを瀺しおいたす。 倀が 4 ず 7 のむンデックスレコヌドが存圚するず仮定したす。 5 ず 6 の倀をそれぞれ挿入しようずする個別のトランザクションでは、挿入された行の排他ロックを取埗する前に、 挿入意図ロックを䜿甚しお 4 ず 7 のギャップがロックされたすが、行が競合しおいないため盞互にブロックされたせん。 https://dev.mysql.com/doc/refman/8.0/ja/innodb-locking.html#innodb-insert-intention-locks ギャップロックが今回の問題に぀ながったこずが刀明したため、次にクレゞットカヌド決枈凊理の䞭でギャップロックを取埗しおいる箇所を特定する䜜業を進めたした。 決枈凊理の党䜓の流れは、以䞋の3぀のステップに分かれおいたす。 同じリク゚ストIDで決枈が行われおいないか確認する 決枈代行䌚瀟に決枈を䟝頌する 決枈代行䌚瀟からの結果をデヌタベヌスに曞き蟌み、レスポンスを返す SQLが発行される箇所を䞭心にブレヌクポむントを蚭定し、ロヌカル環境でデバッグを行ったずころ、以䞋のク゚リの実行盎埌にギャップロックが取埗されおいるこずが確認されたした。 SELECT * FROM PAYMENTS where request_id = '' FOR UPDATE; その時のperformance_schema.data_locksのデヌタが以䞋です。 原因 党おの情報が揃い、原因の特定が完了したした。 決枈プラットフォヌムでは、リク゚ストが重耇しおいないかを確認するために request_id をリク゚スト元から受け取り、この倀を埌続の参照にも利甚するため、ナニヌクなむンデックスを付䞎しおいたした。 䞀方、プロダクト偎では request_id を以䞋のルヌルで生成しおいたした 商品ID-YYYYMMDD-連番 デッドロックが発生した際には、人気商品の発売により同䞀商品の賌入リク゚ストが短時間に集䞭しお送信されおいたした。その結果、 request_id の連番郚分が急速にカりントアップされたリク゚ストが倧量に送信されたした。 :::details curlコマンドの䟋 curl --location 'http://localhost:8080/payments/cards' \ --header 'Content-Type: application/json' \ --data '{ "amount": 10, "capture": false, "request_id": "ITEM-20240101-0000001" } ::: 前述の通りクレゞットカヌド決枈凊理の䞻な流れは以䞋ずなっおいたす。 同じリク゚ストIDで決枈が行われおいないか確認する 決枈代行䌚瀟に決枈を䟝頌する 決枈代行䌚瀟からの結果をデヌタベヌスに曞き蟌み、レスポンスを返す 問題は、1぀目の凊理で発生したした SELECT * FROM PAYMENTS where request_id = '' FOR UPDATE; このク゚リは、通垞は同じ request_id のリク゚ストが来ない前提で実行されたす。しかし、圓該 request_id のデヌタのINSERT前であるため、ク゚リが空振りしギャップロックを取埗したした。 その埌、3぀目の凊理で結果を曞き蟌むINSERT凊理が発生し、挿入むンテンションギャップロックを取埗しようずしたした。しかし、すでに取埗されおいたギャップロックず競合し、埅ちが発生。その結果、MySQLがデッドロックを怜知し、1぀のトランザクションがロヌルバックされたした。 解決策 重耇決枈の確認甚ずしお䜿甚しおいた SELECT FROM ... FOR UPDATE が原因であるこずが刀明したため、このク゚リを廃止したした。代わりに、リク゚ストを受け付けた時点でデヌタを仮登録し、トランザクションをコミットする蚭蚈に倉曎したした。 コミットのタむミングが増えるこずでパフォヌマンス劣化が懞念されたしたが、負荷テストにより必芁な性胜を担保できるこずが確認されたため、この仕様でリリヌスしたした。 反省 ギャップロックに぀いおの理解が十分ではありたせんでした。 SELECT FROM ... FOR UPDATE で結果が0件の堎合にギャップロックが取埗されるこずを十分に理解しおいたせんでした。普段からマニュアルをよく読み、蚭蚈に取り入れおいる぀もりでしたが、すべおを知った気になっおいたず反省しおいたす。 https://dev.mysql.com/doc/refman/8.0/ja/innodb-locking.html たた、テストで気付けるポむントがあったこずも残念に感じおいたす。 負荷テストではむンデックスの断片化や再構築によるパフォヌマンス劣化を懞念し、 request_id にランダムな倀UUIDを䜿甚しおいたした。このためデッドロックが発生せず、テストは正垞に完了しおいたした。 たずめ MySQLやInnoDBストレヌゞ゚ンゞンを扱う際には、トランザクションやロックの動䜜を深く理解するこずが非垞に重芁です。日頃からドキュメントや仕様をしっかり読み蟌み、必芁に応じお有識者のサポヌトを埗るこずの重芁性を改めお認識したした。 たた、本番環境で実際に利甚されおいるパラメヌタを調査し、それに基づいたリク゚スト倀を䜿甚しおテストを行うこずが、問題の早期発芋や品質向䞊に぀ながるず孊びたした。
This article is the entry for day 4 in the KINTO Technologies Advent Calendar 2024 🎅🎄 Introduction Hello. My name is Nakaguchi, and I am the team leader of the iOS team in the Mobile App Development Group. In my day-to-day job, I work on: the KINTO Kantan Moushikomi App (“the app”) and Prism Japan ( Smartphone app version / Recently released web version ) As an iOS developer. After deciding to migrate one of the apps I work on to SwiftUI, I’m using this article to share the process we followed and the principles that informed the decision. I hope this article appeals to: iOS engineers, those interested in SwiftUI architecture, and teams considering adopting SwiftUI. I’d love for readers in these groups to find value in it. This article is also based on a presentation I gave at the KINTO Technologies × RIZAP Technologies Mobile Tips event held recently. It does not contain specific, actual examples of converting to SwiftUI that use source code, etc. Instead, I have primarily focused on detailing the process the team followed to arrive at the decision to transition to SwiftUI. I hope it will help people who are having trouble with SwiftUI conversion in their own teams. Choosing the Architecture for the First Release The application app was released in September 2023. The main architectures chosen were: UIKit VIPER Combine Development of the app started around March 2023, but creating a new iOS app in 2023 presented a challenging decision: UIKit or SwiftUI, wouldn’t you agree? At the time, SwiftUI was rapidly gaining popularity within the broader development community. However, we chose UIKit for the Moushikomi app. The reasons for this were the app’s tight delivery deadline , and the lack of team members proficient in SwiftUI . We prioritized achieving a stable release using a technology we were used to rather than risk using a new. Now, I’ll discuss the factors that prompted us to transition to SwiftUI after the app's first release. Wanting to Shift to SwiftUI: The First Wave After the initial release in 2023, we quietly focused on bug fixes, minor updates, and refactoring. However, during this period, some team members began expressing a desire to explore something new. Several options were proposed, but SwiftUI emerged as the most popular choice. The first wave of the SwiftUI conversion came around March 2024. When we discussed within the team whether to adopt SwiftUI, opinions such as the following were shared. ● Reasons in favor of doing it: Interest in SwiftUI ● Reasons against doing it: No one on the team had prior experience with SwiftUI. At the time, we didn’t feel a pressing need to adopt SwiftUI. The team was also experiencing significant changes, with many new members joining due to replacements and other factors. This left us lacking both the personnel and time resources to start learning and adopting SwiftUI. As the team leader, I wasn’t confident in my ability to successfully lead a SwiftUI conversion. At that time, there were plenty of reasons not to proceed with it. For these reasons, we decided that a SwiftUI conversion would need to be postponed. The Desire to Shift to SwiftUI: The Second Wave Around six months went by after that. During one-on-ones, many team members expressed a strong interest in exploring SwiftUI, prompting us to revisit the idea of migrating to it. The second wave of the SwiftUI conversion came around August 2024. The situation had evolved since the first wave, and when we revisited the idea, the following opinions were shared: ● Reasons in favor of doing it Interest in SwiftUI had gradually evolved into a passion for it. Some SwiftUI experts had joined the team as a result of in-house organizational changes. The new team members were quickly becoming core contributors, and we felt that the team as a whole now had sufficient time and human resources to take on the challenge. ● Reasons against doing it There were still concerns about whether it was truly the right time to start a SwiftUI conversion. This time, there were plenty of reasons supporting the decision to move forward with it Considering these circumstances, we decided to proceed with the SwiftUI conversion. Never Get Your Goals Wrong Thus, the team unanimously agreed to move forward with the SwiftUI conversion. However, I firmly believe it’s crucial to never lose sight of your goals. [NO] The goal should not be to pursue a SwiftUI conversion purely out of technical curiosity. [YES] Focus on improving future maintainability, aligning with de facto industry standards, and addressing the complexity of using Combine in development, which we aim to move away from. [NO] The SwiftUI conversion must not compromise the app’s quality. [YES] Ensure the app's quality is maintained at least at its previous level, if not improved. [NO] Avoid misplacing work priorities, such as sidelining original release tasks in favor of the SwiftUI conversion. [YES] Continue delivering additional features at the same pace as before. With the above points firmly in mind, we engaged in team discussions on how to approach the SwiftUI conversion. Choosing the Architecture for the SwiftUI Conversion We discussed the type of architecture we wanted to adopt within the team, and the key opinions were as follows: Not wanting to use libraries Not wanting to use a view model Not wanting to use libraries This primarily referred to The Composable Architecture(TCA) .Many team members expressed a preference to avoid using TCA if possible, citing concerns such as the need to constantly monitor for updates and the potential challenges if support for the library were to be discontinued. Additionally, other projects within the company using TCA had reported usability issues, including a steep learning curve, the challenge of keeping up with the library's rapid update cycle, and an overreliance on parent reducers. Taking these factors into account, we decided to forgo using TCA. Not wanting to use a view model The decision to adopt a view model as the architecture for SwiftUI is a topic of much debate. In our case, several team members noted that SwiftUI's built-in binding capabilities make using MVVM less optimal, as it does not fully leverage SwiftUI's inherent strengths. Consequently, we agreed on a policy of not using a view model. Adopting an MV Architecture — As a Result, Our Team Opted for an MV Architecture . The following figure will give you a picture of what it is like. MV architecture Ideally, views should interact directly with the model. Similarly, data retrieved from APIs is passed to the views through the model. Currently, we are discovering that an MV architecture, makes things simpler and will lead to better maintainability in the future (moving away from Combine); does not depend on libraries; and lets us get the most out of SwiftUI’s features. We are experiencing advantages like these, which suggests that the chosen architecture effectively addresses the concerns raised during our discussions about which approach to adopt. Guidelines for Deciding Which Parts to Convert to SwiftUI Regarding our policy for determining which parts to convert to SwiftUI, the team deliberated on which of the following approaches to adopt: The first approach involves converting individual views to SwiftUI. First, convert the parts related to screen transitions to SwiftUI. As a result, we decided to proceed with the conversion to SwiftUI based on a policy of first converting the parts related to screen transitions . The reasons for this included the following: In our experience, we frequently encountered challenges with screen transitions later in the process If the view responsible for managing transitions remains in UIKit, it often necessitates (temporarily) wrapping individual views in UIKit, even after they have been converted to SwiftUI. The Path Forward for SwiftUI Conversion So far, I have outlined the process and policies for converting the application to SwiftUI. However, the actual conversion process is still in its early stages. As of December 2024, at the time of posting this article, the production code does not yet include any SwiftUI code. Currently, we are dedicating more time to discussions about the SwiftUI conversion through various initiatives. These include utilizing approximately 20 minutes left over in our daily morning meetings and holding a dedicated one-hour meeting each week to focus specifically on this topic. As we progress, we have begun establishing coding rules to foster a shared understanding within the team regarding the SwiftUI conversion. For instance, some team members have been creating sample code and conducting lectures for the entire team based on those examples. In the future, as the SwiftUI conversion gains momentum, we plan to introduce pair and mob programming to enhance the team's overall expertise in SwiftUI.
この蚘事は KINTOテクノロゞヌズアドベントカレンダヌ2024 の10日目の蚘事です🎅🎄 Background When developing the KINTO かんたん申し蟌みアプリ App, we implemented some shared code using KMP (Kotlin Multiplatform) and published it as a Swift Package. This approach allowed us to efficiently share code across platforms and simplify the development process by avoiding code duplication. Our iOS Team currently uses XcodeGen to manage dependencies, and importing KMP code can be as simple as making a 4-line modification to the project.yml file. Here is an example of such a modification: packages: + Shared: + url: https://github.com/[your organization]/private-android-repository + minorVersion: 1.0.0 targets: App: dependencies: + - package: Shared - package: ... However, since our code resides in private repositories, some additional setup is required. This blog will outline those steps and explain how we streamlined the process. About Package.swift Here’s a brief explanation of how we publish KMP code as a Swift Package: Compile the KMP code into an .xcframework . Package the .xcframework into a zip file and calculate its checksum. Create a new release page on GitHub and upload the zip file as part of the release assets. Obtain the zip file’s URL from the release page. Generate the Package.swift file based on the URL and checksum. Commit the Package.swift file and add a git tag to mark the release. Associate the git tag with the release page and officially publish the GitHub release. The resulting Package.swift file will look something like this: // swift-tools-version: 5.10 import PackageDescription let packageName = "Shared" let package = Package( name: packageName, ... targets: [ .binaryTarget( name: packageName, url: "https://api.github.com/repos/[your organization]/private-android-repository/releases/assets/<asset_id>.zip", checksum: "<checksum>" ) ] ) Permission Setup for Development Environment Since the URL resides in a private repository, you will encounter the following error if no permission configuration is done: To resolve this, we explore two options: .netrc files and Keychain. Option 1: Using a .netrc File You can store your GitHub credentials in a .netrc file, which is a simple way to authenticate API requests: #Example: echo "machine api.github.com login username password ghp_AbCdEf1234567890" >> ~/.netrc echo "machine api.github.com login <Your Github Username> password <Your Personal Access Token>" >> ~/.netrc This method is quick and effective but may pose security risks since the token is stored in plaintext. Option 2: Using Keychain If you prefer not to store the token in plaintext, you can use Keychain to securely store your credentials: Open Keychain Access.app . Select ①, the login keychain. Select ②, to create a new Password Item. In the dialog box, enter the following information: Keychain Item Name: https://api.github.com Account Name: Your GitHub username Password: Your Personal Access Token This approach is more secure and integrates seamlessly with macOS authentication mechanisms. For SSH Users The above instructions assume you cloned the iOS repository using the https protocol. If you did, you already have the necessary permissions for github.com configured. However, if you cloned the repository using the ssh protocol, you might lack permissions for github.com , leading to permission-related errors during the resolveDependencies phase. To fix this, you can add an entry for the domain github.com in the .netrc file: #Example: echo "machine github.com login username password ghp_AbCdEf1234567890" >> ~/.netrc echo "machine github.com login <Your Github Username> password <Your Personal Access Token>" >> ~/.netrc Alternatively, use Keychain Access to add an item with the name https://github.com . Either method ensures your system has the required permissions. GitHub Actions After resolving the local development environment issues, you also need to address permission issues in the CI environment to ensure smooth automation during builds. Retrieving Tokens in GitHub Actions Using a Personal Token One straightforward approach is to create a Personal Access Token (PAT) with access to private repositories and pass it to the CI environment via Actions secrets. While effective, this method has several drawbacks: Token Expiration Tokens with an expiration date require periodic updates, and forgetting to update them may cause CI failures. Tokens without an expiration date pose long-term security risks. Broad Permissions A personal account usually has access to multiple private repositories, making it difficult to restrict PAT permissions to a single repository. Personal Dependency If the account owner loses access to private repositories due to role changes, CI workflows will fail. Using a GitHub App Using a GitHub App is a more robust solution, offering several advantages: Fine-grained permissions for repositories No dependency on individual accounts Temporary tokens that enhance security Setting Up a GitHub App We ultimately used a GitHub App to configure access permissions. Here is the process: Create a GitHub App in your organization. Install the App in both iOS and Android projects to manage repository access. Configure the App’s AppID and Private Key in the iOS project’s Actions secrets. Add code in the workflows to retrieve a temporary Access Token. Here’s an example: steps: - name: create app token uses: actions/create-github-app-token@v1 id: app-token with: app-id: ${{ secrets.APP_ID }} private-key: ${{ secrets.APP_PRIVATE_KEY }} owner: "YourOrgName" - name: set access token for private repository shell: bash env: ACCESS_TOKEN: ${{ steps.app-token.outputs.token }} run: | git config --global url."https://x-access-token:$ACCESS_TOKEN@github.com/".insteadOf "https://github.com/" touch ~/.netrc echo "machine github.com login x-access-token password $ACCESS_TOKEN" >> ~/.netrc echo "machine api.github.com login x-access-token password $ACCESS_TOKEN" >> ~/.netrc By using a GitHub App, we ensure our CI workflows are secure, efficient, and free from dependency on individual user accounts. This approach minimizes risk and streamlines development across teams.