TECH PLAY

KINTOテクノロジーズ

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

969

​ こんにちは( º∀º )/ ​ テックブログチーム、予算統括Gの村山です! 今回はKINTOテクノロジーズ初の外部向けイベント『KTC Meet Up!』を開催するにあたっての 運営スタッフ目線での記事を書いていきたいと思います😗 ​ 勉強会の企画~事務局立ち上げに関する記事は企画者の きんちゃん が書いてくれてますこちら✍️ ​ はじめての「KINTOテクノロジーズ MeetUp!」が開催されるまで ![](/assets/blog/authors/uka/ktc-meet-up/kinchan.png =500x) ​ テックブログメンバーはイベント系のフォローを積極的に行っております! テックブログの運営に携わるだけでなく、 みんなを巻き込んで会社を盛り上げていこー!ってチームなのです( ‘-‘ )ง ​ 遡ること6月後半、8月に外部向けイベントやっちゃうよ~!でJoinしました。 メンバー全員が兼務で、みんな別のグループに所属しています。 わたしは通常業務でお金回りの担当をしているため、 必要備品の購入、当日のサポートを行いました。 KINTOテクノロジーズ初の外部向けイベント開催ということで 必要な機材は一式そろえさせてもらいました。 ​ 今回はオフライン開催でしたが、撮影機材も購入させていただきました! これでオンラインやハイブリット開催もできちゃう😳 ​ 新しい挑戦に前向きな弊社、すてき! ! ​ ここからは写真でお送りします ​ テストまで我慢できず届いたその場で手出しちゃう人たち ​ みんなで試行錯誤 ​ あっという間に当日・・! ​ テステス ​ 今回はお試しで社内向けに配信してみることに ​ おおー! ​ Tシャツもつくったよ〜!おそろ~ ​ わくわく ​ はじまりはじまりー! ​ ​ 社外向けイベントの誕生日ということでバースデーメガネを着用 ​ ​ さすが我らのマネージャー! ​ ​ 個性が活きてる! ​ ​ テックブログチームも登壇しました! ​ ​ 登壇おつかれさま~ ​ ​ 座談会 各テーブル賑わっていました! ​ ​ 喋るのに夢中で食べる暇なかった・・By.登壇者 ​ ​ わいわい ​ ​ たのしそうだあ ​ ​ メモ:ビールとハイボールが人気 ​ ぴーす ​ ぐっ ​ ​ ​ ​ そんなこんなであっという間の2時間でしたー! ​ 事務局、運営メンバーによる今回のイベントで「最高だった瞬間」はこちら ​ 協力者が集まったとき 片付けが終わった瞬間。皆が自発的に動いた! 退場時に「最高でした!」「次いつやるんですか!?」ときかれた! 企画で協力者を募った直後、皆さんから色んなアイデアが続々と出てきたこと 当日イベント会場に着いた時のインパクト 当日の座談会。皆めっちゃ夢中でしゃべってる! 相談をもらえた事。そういうのに関わって盛り上がるのが好き!大成功に終わった! カメラの説明等、みんなでOneTeamでなんとかしたこと! Twitter、始まる前からめっちゃ盛り上がってた。一体感あった! 座談会で、参加者の方から色々と質問有り「ウチでもやってみます」と アンケート結果を見た時。運営が楽しんでいる状況が良い! 現地で機材がバッチリ準備できていたこと 受け付けで名札がほとんどなくなった時! 普段専門性も、部署も違う仲間が、協力してイベント成功に繋がったところ。当日運営もスムーズだったし、みんな自分の出来ることを考えて動いていたことが素晴らしかった! ​ ​ 振り返りも意見が飛び交い充実したものでした。 みんなの協力が目に見える素敵なイベントになりました! ​ おわりに ​ 「事務局スタッフ視点でのテックブログ記事」や「登壇者による事例発表の紹介テックブログ記事」も上がる予定なので細かい内容はそちらで😗 てことでわたしからは、『いい写真がたくさん撮れたので載せたいだけの記事』をお送りしました😳 ​ 最後まで読んでくださりありがとうございました!
アバター
Introduction Hello, my name is Rina and I’m involved in Mobility Market development and operation at KINTO Technologies. Usually I work as a front-end engineer implementing websites using Next.js, but I am also one of the members of our Tech Blog team, where I mainly help manage the publishing schedule of all articles. Speaking of our techblog, one year has passed since our first article was released!🎉 This time, in order to celebrate our memorable 1st anniversary, I want to talk about its development after our launch. If you are interested, you can also read our other article to find out how we built this techblog with Next.js.🙌 Adapting InnerSource At KINTO Technologies, we adapted InnerSource when designing and developing our tech blog. What is InnerSource? According to InnerSource Commons, one of the biggest InnerSource communities, InnerSource software remains proprietary to the company, but within it is open for anyone to use it and contribute to it. Reference: InnerSource Commons That means, it is a style of development where any developer within the company could contribute to the project. Practicing The repository of our Tech Blog is open to all developers in the company, where any of us can contribute by implementing new features, fixing bugs, or reviewing new pull requests. In our GitHub Issues, not only all planned new features will be raised there, but anyone who has new ideas that they could not develop on their own can also submit new issues there. Based on what was written inside the issue, those who are willing to contribute can create GitHub Pull requests (PR), or conduct code reviews on other's PR. However, most of the urgent issues would be handled by the techblog team members. Why InnerSource? There are two reasons why we applied InnerSource methodology to our techblog: To utilize this blog as a space for experimenting with new technology To promote collaboration across different teams To utilize this blog as a space for experimenting new technology We wanted our Tech Blog to be a platform for our developers to try out new technologies so that they could grow. This was also cited by the leader of our Tech Blog team, Nakanishi-san in his interview article (conducted in Japanese). We could have used some external CMS provider, yet we decided to build the blog by ourselves . Sometimes, our blog became a platform for us to showcase what we learnt recently, some others, a space to try out features that could not be considered in other products, or also just to simply perform A/B tests. By creating the Tech Blog from scratch, we tried to create a place to encourage our developers to test new things. We hope that any insights, skills and experiences gained by contributing to the Tech Blog could be applied to future products and projects. To promote collaboration across different teams We also wanted the Blog to help break down barriers between teams within our company. Based on our company structure , the vertical communication is strong, but we believe that there are benefits by promoting horizontal communication as well. So how about making new discoveries by sharing what you know with someone from other teams or divisions? It could lead you to having a new business idea or creating a new long-lasting friendship. By strengthening and diversifying our communication methods, we believe developers could grow further, and non-developers may become more interested in development. These are the reasons why we believe InnerSource could help creating a culture where developers learn actively and non-developers to enjoy development. Features we have added until now Among all the features we developed with this practice, I would like to list 5 of them; all of them developed and reviewed by internal developers across different teams. ① RSS ② Social media share button ③ Latest/Related articles ④ Table of contents ⑤ Pagination Summary In this article, I have introduced how we use InnerSource to develop this Tech Blog until now. Soon we are going to fully renew our design and ship our article category feature! Not only we are publishing new articles every week, but we are planning to improve the blog to make it easier for you to enjoy our content. Please look forward to it!🙌 Last but not least, I would like to take this opportunity to express my gratitude to all our readers, and all the members who have been involved in this Tech Blog project. Thank you!!✨
アバター
Hello. My name is Koyama from KINTO Technologies. I work on mobile app development and maintenance. I am an iOS engineer. Last time, I wrote an article focusing on iOS development, titled( Using Combine to Achieve MVVM ). This time, I’ll be focusing on the Agile development practices we use at our development site. In the team I’ve been part of for a while, we follow Agile development with the Scrum framework. I was asked whether I wish to try being a Scrum Master, so I would like to share my experience. *This post is part of an ongoing series exploring Agile. We have faced various challenges and difficulties in our quest to become Agile as an organization. Although there have been failures at times, we have continued to grow steadily. In this series of articles, I would like to introduce some of our actual efforts. Why Did I Take the Scrum Master Training? I had worked with Scrum in previous projects as well, but always in a developer role. I had never acted as a Scrum Master before, so I felt anxious about taking on the role. Then, I learned that there was a Scrum Master training course available, so I decided to take it. This time, I took the Certified Scrum Master training course hosted by Attractor Inc. My Development Team Let me introduce the development team I belong to. Structure and Roles Product Owner Team Leader Backend Development Team Frontend Development Team iOS Development Team ← I am here now Android Development Team QA Designer In mobile app development, it is not uncommon for the frontend development team to split into two teams like this. This time I was going to be a Scrum Master, and the structure was expected to be as follows: Product Owner Backend Development Team Scrum Master (Team Leader) Frontend Development Team Scrum Master ← I was supposed to be 50% responsible iOS Development Team ← I was supposed to be 50% responsible Android Development Team QA Designer Since the team leader had a background in backend development, the plan was for him to act as the Scrum Master for the backend, while I would handle the frontend. In hindsight, it was a rather unusual arrangement. But at the time, I didn’t recognize that—and even if I had, I wasn’t in a position to voice it confidently. Challenges in the Existing Team Development Of course, since we had been proceeding with Agile development up until then, we had many questions and challenges when using Scrum. What should we do if our development team exceeds 10 people? We are carrying out various sprint events by separating them into backend and frontend, but is it OK? The level of understanding of Scrum varies among the team members. And so on… I attended the training hoping to resolve these issues. Taking on the Training! Training Overview The Scrum Master training consisted of a three-day curriculum and was held online every day from 1:00 pm to 6:00 pm. After that, you can take the exam at any time. Upon passing, you can obtain the Certified Scrum Master qualification issued by the Scrum Alliance. The breakdown of the three-day training was as follows: Day 1: Lectures on Agile and Scrum Day 2: Hands-on Scrum practice Day 3: Lecture and hands-on practice on the Scrum Master You can read about Agile and Scrum in books or online, but the lectures explained the concepts in a much clearer and more understandable way. I was able to relearn the principles and mindsets that I had almost forgotten, and I was able to ask the instructors any questions I had, allowing me to solidify my knowledge in a practical format. It was a very meaningful experience. What Changed after the Training Anyway, I gained confidence That’s what it all comes down to. One absolute requirement for a Scrum Master is to provide guidance on Scrum. With what I had learned up to that point, I couldn’t completely clear up my doubts and often found myself thinking, “This is what the book says, but it doesn’t quite match reality.” Even though I understood the principles, I didn’t feel confident enough to share or promote them within my team. Taking the training cleared up all the questions I had, and the reassurance of being taught by a renowned instructor, who has even published books on Scrum, gave me confidence. I was also able to obtain the Certified Scrum Master qualification, which further reinforced my confidence. What I Tried after the Training Gaining the Product Owner’s Understanding During the training, I discovered that my team was using a number of techniques known as anti-patterns. So I began by explaining to the Product Owner what anti-patterns are and why certain practices we were following fit into that category. Since changing everything at once would be difficult, we decided to start with the more straightforward areas. The team agreed to begin by reevaluating how we manage the backlog. Sharing the Training with Team Members Agile development requires the entire team to be on the same page. I think this is a very important aspect of moving forward with Agile. Even if we were to start by changing the way we manage the backlog, we first needed to unify our understanding. To achieve this, I created new documents for internal sharing and held group training sessions the week after the training. I was able to do this just because I gained confidence from the training. From the team members, I received feedback such as "I realized we had been unknowingly doing anti-patterns" and "I'm glad you shared this immediately after the training!" To be honest, creating the documents and conducting the group training was quite a challenge, but I'm glad I did it. Implementing Improvements To improve our approach to backlog management, I outlined the current issues and the desired state, then began applying those changes starting with the next backlog refinement session. All of this was accomplished in just one week after completing the training. The reason we were able to get this far, even though I was still fresh from the training, was thanks to the product owner’s and development team’s understanding. What Happened to Our System? Sharing the training content with the team went smoothly, but that’s when a new issue came up. It turned out that our team leader had to go on parental leave suddenly. Our company has a parental leave system (for men as well, of course), and the whole team wanted to support child-rearing, so we were happy to send him off. However, it seems that I need to support this big project (in terms of number of members) as the Scrum Master . Oh no! So, the restructured system is as follows: Product Owner Scrum Master ← I was supposed to be 50% responsible Backend Development Team Frontend Development Team iOS Development Team ← I was supposed to be 50% responsible Android Development Team QA Designer The reason for the earlier statement, "Looking back now, I feel that we had come up with a strange structure," is that a single Scrum Master is generally sufficient for a team in the first place. Having separate Scrum Masters—one focused on the backend and the other on the frontend—doesn’t seem to align with the original purpose of the Scrum Master role. There’s a concern that this kind of division could result in Scrum Masters being Scrum Masters in name only, functioning more like team leads. Additionally, the initial team split between backend and frontend naturally required cross-team communication, leading to the challenge of needing someone to serve as a bridge. Furthermore, there was a likelihood that the burden on the bridge member may become very heavy, so I believe that consolidating them into one team at this time was actually a good move, considering future developments (see below). What We Want to Do in the Future Our immediate goal is to continue updating our development team, starting with the improvements mentioned above. Once we are able to run Scrum smoothly, we would like to try splitting our team into feature teams. This will solve all the team challenges we originally faced. In the long term, I would also like to work with other Scrum Masters within the company to spread Agile development using Scrum. Once we've helped them reach a point where they can operate smoothly, we can shift our support to other teams. As Scrum adoption grows, we’ll be able to contribute more effectively to building better products. Conclusion This concludes my experience with the Scrum Master training. Anyway, there are many benefits, so if you are reading this and struggling to make Scrum work well, I encourage you to consider taking the training. The training fee is relatively high, which makes it hard for individuals to take it on their own. However, I believe it includes enough value to justify asking your company or manager to cover the cost. Personally, I think the biggest point of Agile development using Scrum is that each team has different practices. Other companies' practices may not necessarily be applicable to your company. Exploring different ways of doing things can be difficult, but it may actually be the most fun part.
アバター
KINTOテクノロジーズの分析グループでデータエンジニアリングチームのチームリーダーを担当している中川です。 最近はゴルフに目覚め球単価を気にする生活になりました。今年の目標はコースデビューすることです! さて本記事ではKINTOの分析基盤をどのように効率よく開発し、サービスローンチに合わせて分析に必要となるデータを提供しているのかというデータエンジニアリングチームの取り組みについてご紹介させて頂きます。 データエンジニアリングチームの目標 データエンジニアリングチームは分析基盤の開発・運用を行っています。 分析基盤とは、社内外各システムのデータを収集・蓄積し、ビジネスに活かせるようデータを提供する役割を担っている縁の下の力持ち的な役割です。そしてサービスイン直後からデータの利活用ができるように下記を目標として掲げています。 「各種のサービス開始にあわせ、分析基盤にデータを集約し即時提供する!」 課題 ただ上記のような役割・目標を掲げる中でKINTO事業の拡大に伴い以下のような課題がでてきました。 限られた開発リソース(数名体制の少数精鋭チームのため) 事業拡大に伴う連携対象システムの増加 連携対象システムの増加に比例し改修の増加 ※ 改修の増加に関しては、「小さく始めて大きく育てる」というアジャイル的な当社ビジネススタイルも影響しています。 解決方法 上記課題を解決するために、ETLとして当社ではAWS Glueを利用していますが、工数削減の観点として、運用面・開発面の2点を中心に改善案を検討し以下のような手段でアプローチしました。 ノーコードを目指した共通化 カラム自動拡張でより速く柔軟性の高い分析基盤へ 当社のAWS分析基盤環境 2点の改善案の前に、当社の分析基盤環境について説明します。 当社の分析基盤は、ETLにAWS Glue、DBにAmazon Athenaを利用しており、最もシンプルなパターンでは下図のような構成です。データをソーステーブルからロードし、生データを時系列でデータレイクに蓄積、利活用のためにデータウェアハウスに格納するという構成になっています。 AWS Glueでデータ連携のためのワークフロー・ジョブを開発するにあたり、当社ではCloudFormationを利用してワークフロー・トリガー・ジョブ・データカタログ・Python・PySpark・SQLなど一連の資材のデプロイを行っています。 そしてデプロイに必要な資材は主に以下となります。 YAMLファイル (ワークフロー・ジョブ・トリガーなどの設定情報) Pythonシェル (ジョブ実行用) SQLファイル (ジョブ実行用) これらの開発作業工数が課題にあげた通りサービスの増加やテーブル数・カラム数に比例して増加し、開発リソースが逼迫し始めました。 そこで先の解決方法に記載した通り主に2点の改善を行うことで問題を解消したため、その手法に関してご紹介させていただきます。 ノーコードを目指した共通化 「ノーコードを目指した共通化」は、進め方としては下記ステップをとりました。 2022年 第1弾: Pythonプログラムの共通化 2023年 第2弾: YAML、SQLファイルの自動生成 第1弾のPythonシェル部分の改善では、これまでは各サービス単位でワークフロー開発を行い、Pythonシェルについてもワークフロー単位で開発・テスト・レビューを行っていたため工数が膨らんでた点に着目しました。微修正しながら各ワークフロー内で再利用していたプログラム部分を共通化することや、データソースの違いなどを吸収し汎用的な作りにすることでプログラムの共通化を進めました。 これにより現在、共通化コードは重点開発・レビューを集中的に行っていますが、各ワークフロー単位でのソースコード開発は不要となり、データソースがAmazon RDSやBigQueryであれば、Amazon Athenaへのデータ型変換なども含めて全て共通化部分で処理が実行できます。 そのため、各サービスのデータ連携を開始したい場合には、設定ファイルに設定のみを記載するだけというノーコードでのデータ連携を可能としています。 第2弾のYAML、SQLファイルの自動生成は、第1弾では必要な部分として残ってしまった設定ファイルやソース側との連携に必要なView定義に関しての改善となります。 こちらはGAS(Google Apps Script)を利用して、設定ファイルであるYAMLやView用のSQLなどを自動生成するように改修しました。このことによりGoogleスプレットシート上に必要なワークフローIDや連携が必要なテーブル名などの最小限の定義を設定するだけで、設定のためのYAMLファイルやView用のSQLファイルが自動で生成されるようにして開発作業の極小化を図っています。 カラム自動拡張でより速く柔軟性の高い分析基盤へ 「カラム自動拡張でより速く柔軟性の高い分析基盤へ」では、改善前にはデータ連携元で定義しているテーブル定義・項目定義を分析基盤側でもYAML上で定義を行っていました。[^1] そのため、初期構築時はデータ連携元での開発側と同じ数だけ項目定義が必要となり、平均して各サービス『20・30テーブル × 20項目 × lakeとdwh』の800〜1,200程度の項目定義が必要な状況でした。 また当社では小さく始めて大きく育てるという理念の基サービスの拡張を常時行っているため、それに伴うバックエンド側のDB更新も頻繁に発生します。この更新作業も以前設定した800〜1,200項目定義の中から注意深く修正箇所を特定し修正していくことが必要となるため、この点も非常に開発工数を圧迫することとなってきました。 そこで考えたのが、データ連携のためにデータ連携元にアクセスしているので、その際に項目の定義情報も一緒に連携し、分析基盤側の項目定義は自動更新を行うという方式です。 せっかくきちんと開発された情報がソース側にあるので、それを活かさない手はない!という考えです。 具体的な実装方法としては下記のような手順でカラム自動拡張を行っています。 glue_client.get_table AWS Glueのデータカタログからテーブル情報を取得 table['Table']['StorageDescriptor']['Columns'] を連携元から取得した項目リスト col_list で置換え glue_client.update_tablet でAWS Glueのデータカタログを更新 def update_schema_in_data_catalog(glue_client: boto3.client, database_name: str, table_name: str, col_list: list) -> None: """ Args: glue_client (boto3.client): Glue client database_name (str): Databse naem table_name (str): Table name col_list (list): Column list of dictionary """ #AWS Glueのデータカタログからテーブル情報を取得 table = glue_client.get_table( DatabaseName = database_name, Name = table_name ) #col_listでColumnsを置換え data = table['Table'] data['StorageDescriptor']['Columns'] = col_list tableInput = { 'Name': table_name, 'Description': data.get('Description', ''), 'Retention': data.get('Retention', None), 'StorageDescriptor': data.get('StorageDescriptor', None), 'PartitionKeys': data.get('PartitionKeys', []), 'TableType': data.get('TableType', ''), 'Parameters': data.get('Parameters', None) } #AWS Glueのデータカタログを更新 glue_client.update_table( DatabaseName = database_name, TableInput = tableInput ) これらに加えて連携元から取得した項目リストを作成する際には、裏でDB毎に異なるデータ型のマッピングなども実施しておりますが、このようにすることでソース側のスキーマ情報で分析基盤の項目定義を生成することができます。 また、このように分析基盤側の項目定義を自動更新となることで気をつけた点としては、知らない間に我々の管理下にある分析基盤のテーブル構造が勝手に変わって行ってしまうという点があげられます。 この点に関しては変更が発生したら"notification"としてSlackに通知が届くような仕組みとしています。こうすることで知らない間にテーブルの構成が変わってしまっていたということを防ぎ、変更を検知し変更内容をソースシステム側に確認した後に必要に応じて後続のシステムにも変更点の連携ができるようにしています。 [^1]:詳細は割愛させていただきますが、AWS Glueの中にはデータカタログを更新してくれるCrawlerもありますがサンプルデータでの更新やエラー解析ができないなどの問題があり当社では利用を見送っています。 最後に いかがでしたでしょうか? 今回は当社分析基盤でのAWS Glueでの『ノーコードを目指した共通化』、『カラム自動拡張でより速く柔軟性の高い分析基盤へ』の2つの手法をご紹介させていただきました。 これら2点を改善することで開発工数を削減をすることに成功し、現在では40テーブルのデータ連携ジョブでも開発工数は1人日程度に抑えられるようになり、目標である「各種のサービス開始にあわせ、分析基盤にデータを集約し即時提供する!」が実現できています。 同じように開発工数を削減されたい方に是非参考にしていただけますと幸いです!!
アバター
こんにちは(こんばんは)、Svelte不定期連載その6です。 過去の記事はこちら SvelteKit + Svelte を1年間くらい使ってみた知見など※SvelteKit メジャーリリース対応済み Svelteと他JSフレームワークとの比較 SvelteでStorybookを使ってみる AstroでSvelte使ってみた SvelteTips 今回はSvelteKitのSSRデプロイについて書いていこうと思います。 モジュールはこちらです。 @sveltejs/adapter-node SSRのデプロイにはadapterが必要で、今回はNodeのadapterを利用します。 https://www.npmjs.com/package/@sveltejs/adapter-node このadapterは公式のGitHubにも載っています。 https://github.com/sveltejs/kit Express Node.jsのウェブフレームワークです。他にはfastifyなどもありますが、どれを使うかは自由です。 https://www.npmjs.com/package/express 環境設定 まずはSvelte内の設定を行います。SvelteKitはデフォルトでSSRになっているので、そこは特に設定する必要がありません。一方デプロイの際にbuildするためにadapterというのを利用する必要があります。 公式サイト にも記載してありますが、対象のsvelteプロジェクトから yarn add -D @sveltejs/adapter-node でインストールし、 svelte.config.js に以下のコードを追加してください。 import adapter from '@sveltejs/adapter-node'; const config = { kit: { adapter: adapter() } }; export default config; これでプロジェクトを yarn build でビルドすると、ビルドしたファイルがデフォルトの出力先である /build に入り、その中に index.js と handler.js というファイルが作成されますが、 もしビルドしたファイルでそのままサーバーを利用したい場合は、 build/index.js を動作させるための、 node build を実行すれば、サーバーが始動されて、動作確認ができます。( node xxxx の xxxx はビルドしたファイルの出力先なので、デフォルトが build です) 次はルートディレクトリにExpressの設定ファイルを入れます。(事前にexpressをインストールしてください) import { handler } from './build/handler.js'; import express from 'express'; const app = express(); // 例えば今回作ったSvelteKitアプリと関係ない、aws用のヘルスチェックパスを作る app.get('/health', (_, res) => { var param = { value: 'success' }; res.header('Content-Type', 'application/json; charset=utf-8'); res.send(param); }); // ビルドで作ったSvelteはこちらでハンドリングされます app.use(handler); app.listen(3000, () => { console.log('listening on port 3000'); }); 上記の設定を完了したら、 node server.js でExpressサーバーを立ち上げ、 http://localhost:3000 でSvelteKitのアプリを確認できます。 アプリをAWSにデプロイしてみる ここからはAWSにデプロイしていきます。AWSでは要件に応じて様々な構成で実現できます。この記事ではEC2のみを利用し最低限ネットからアプリにアクセスできるて手順を紹介します。 実際はセキュリティやパフォーマンスの理由で、Cloudfront/ALB/VPCなどの組み合わせも検討してください。 AWSサービスを利用すると、料金がかかってしまうので、なるべくコストなどを確認し、利用しない時は該当サービスを停止したほうをお勧めします。 EC2 今回作ったSvelteKitアプリをホストするためのクラウドサーバーサービスです。 https://aws.amazon.com/jp/ec2/ EC2インスタンスの作成 まずはEC2のセットアップから始めたいと思います。 EC2のインスタンスを作成するために、EC2の管理画面に入り、右上にある「インスタンスを起動」をクリックします。 そしたら上記のような画面に遷移されますが、下記の項目の設定を行い、「インスタンスを起動」をクリックします。 名前:自分にとってわかりやすい名前をつけます OSイメージ:自分の好みに合わせればいいですが、この記事ではAmazon Linuxを利用し、後ほど利用するコマンドもAmazon Linuxをベースにします インスタントタイプ:t2.micro (それ以外だと有料になります) キーペア:この記事はSSHクライアントでEC2にアクセスするので設定します ネットワーク設定:この記事はSSHクライアントによる接続と最低限ウェブで確認したいのでHTTPアクセスを許可します EC2インスタンスに接続 EC2を作成すると、一覧画面に戻りますが、そこには今回作成されたEC2インスタンスが出てくるはずなので、次はインスタンスに接続し、中で残りの設定を行います。 一覧画面からインスタンスを選択し、「接続」ボタンをクリックします。そうすると、4つの接続方法が選べますが、今回はSSHを利用します。 先ほどインスタンス起動の画面で、キーペアを作成され、ローカルにダウンロードされたと思いますが、画面に書いてある指示通りにそのキーを利用し、接続します。接続が成功したら様々なものをインストールします。 Node.jsのセットアップ まずはNode.jsをインストールしますが、Node Version Manager(nvm)からインストールしたほうが様々なnodejsバージョンをインストールでき、切り替えも便利です。 https://github.com/nvm-sh/nvm curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.3/install.sh | bash インストール後はターミナルからメッセージが来ると思いますが、 nvm コマンドが実行できるようにパスを通す必要があるので、 以下のコードをコピーして、コマンドラインに貼り付けて実行します。 export NVM_DIR="$HOME/.nvm" [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" [ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion" // nvmコマンドが動くかどうかの確認 nvm --version -> 0.34.0 そしてnvmでnodejsをインストール // 18系のnode.jsをインストール nvm install 18 // インストールしたnodeとnpmバージョンを確認 node -v -> 18.16.1 npm -v -> 9.5.1 // yarnをインストール(npmを利用したい方はこれをスキップ) npm install -g yarn yarn -v -> 1.22.19 これでNode.js周りのセットアップは完了しました。 SvelteKitアプリの配置 次は作ったアプリをインスタンスの中に入れたいと思います。自分のローカルからEC2に移す方法もありますが、今回はGitHubに上げているリポジトリからcloneします。 まずはcloneできるようにGitHub CLIをインストールします。Linuxでのインストール方法は 公式ドキュメント にも記載されています。 // 公式ドキュメントに記載されたコマンド(変わる可能性があるため、必ず公式ドキュメントも確認してください) type -p yum-config-manager >/dev/null || sudo yum install yum-utils sudo yum-config-manager --add-repo https://cli.github.com/packages/rpm/gh-cli.repo sudo yum install gh // バージョン確認 gh --version -> 2.31.0 次は自分のアカウントでログインし、リポジトリをcloneします。 // GitHubへのログイン gh auth login // cloneしたいリポジトリのurlを入れてください gh repo clone https://github.com/xxxxxx/yyyyyyy これでアプリは無事インスタンスにcloneできました。 Nginxのセットアップ 続いてははNginxサーバーをインストールし、configファイルを修正します。 // インストール sudo yum install nginx // nginxフォルダーに移動 cd /etc/nginx // vimでnginxのconfigファイルを開く sudo vim nginx.conf configファイルに server という枠がありますが、 以下のようにプロキシパスを設定します。 この構文はnginxに対して / にアクセスした場合、EC2内で立ち上げたsveltekitサーバーにアクセスするように指示するものです。 server { location / { proxy_pass http://localhost:3000; } } Nodeサーバーの立ち上げとWebからのアクセス 最後はローカルでの確認と同じように、ビルドして、nodeサーバーを立ち上げます。 yarn install yarn build node server.js そして作ったEC2インスタンスが提供しているDNS名からアクセスしてみます。(情報はEC2の一覧ページから確認できます) 以下のように表示ができました! しかし、これでEC2インスタンスとの接続を中止すると、Nodeサーバーも停止になってしまいます。 そこで pm2 というライブラリーを使ってNodeサーバーの永続化にします。 https://pm2.io/docs/runtime/guide/installation/ yarn global add pm2 pm2 -v -> 5.3.0 pm2 start server.js // 今pm2で立ち上げているnodeサーバーの状況と停止したいサーバーidを確認 pm2 status // 今pm2で立ち上げているnodeサーバーを停止します pm2 stop [id] これでEC2との接続を止めても、引き続きウェブから閲覧できます! 以上、SvelteKitのSSRアプリをAWSにデプロイする方法でした。
アバター
Introduction Handling variables like URLs, dates, or times in translatable sentences is a common challenge in localization. The position or format of these can vary significantly depending on the language, making it tricky to manage strings across languages, especially if you are managing multiple languages at the same time. Hello! I’m Maya and I lead localization at KINTO Technologies. This article is for anyone managing multilingual content in-house in several languages without a coding background but would like to create a lean mechanism alongside their development colleagues. Don't worry if you're not familiar with coding; while having direct access to colleagues who can assist with implementation would be ideal, there are many resources available online to support you in managing your source language and translation data cohesively with your engineering team. In our case, we use Lokalise as our TMS and source of truth, integrated with GitHub to keep translation data and development in sync. Why This Matters For localization specialists, understanding how to use dynamic placeholders like URLs or dates can streamline your workflow and close the gap in communication between dev and localization teams. Shoutout to our teammate Wen-san for explaining this method in a beginner-friendly way! In my case, since we implemented this approach, we’ve significantly reduced manual string adjustments, especially for placeholder-heavy sentences like terms and conditions and date/time formats. Internal rework due to these issues dropped from about 5 reports per month to nearly zero. We not only saved time but also minimized QA back-and-forth and improved trust between localization and development teams. The Problem Let’s take a sample sentence in English with two URLs: "By using our service, you agree to our Terms and Conditions and Privacy Policy ." In Japanese, the URLs will be placed in a different position relative to the English one: "本サービスを利用することにより、 利用規約 および プライバシーポリシー に同意したものとみなされます。 If we had a third language, especially the more it differs from the above two, the position may again be different. If you’re handling multiple languages, managing these differences manually can be very time-consuming and error-prone. The Solution: formatString() Here’s where this function can be a lifesaver. It allows us to create a flexible template where the URLs can be placed dynamically, depending on the language. You can adjust the placement of the URLs in a sentence without having to rewrite the entire sentence for each language. The tedious alternative many teams still face is hardcoding the URLs directly into each localized string, which leads to manually having to recreate near-identical sentence variants with cluttered string files, a higher risk of typos or broken links, and painful maintenance when URLs change. This article introduces one solution to this issue using JavaScript as an example, but it can also be expressed in other programming languages such as Phython, C# or PHP. Step 1: Identify the Dynamic Elements in Your Sentence Let’s start with the previous sample sentence in English, our base language in this example: "By using our service, you agree to our {Terms and Conditions} and {Privacy Policy}." The placeholders {Terms and Conditions} and {Privacy Policy} represent URLs. While their content and placement will vary by language, they must remain clickable. Because sentence structures can differ across languages, these placeholders should be handled dynamically to fit each language's syntax. Step 2: Create the Translatable String with Placeholders In your i18n file in key-value format, you’ll define the placeholders that will be replaced by URLs. Using names ones instead of symbols ensures clarity and will prevent errors: { "terms_and_privacy": "By using our service, you agree to our {terms} and {privacy}." } Here I have renamed them simplified as {terms} and {privacy} to avoid issues in code. Best practice is to use simple, descriptive keys in snake_case or camelCase. Step 3: Define the URLs and Anchor Texts Per Language Now, define not just the URLs, but also the translated anchor texts. You’ll need to handle these separately for each language. For English: const en = { terms: ' Terms and Conditions ', privacy: ' Privacy Policy ' }; For Japanese: const ja = { terms: ' 利用規約 ', privacy: ' プライバシーポリシー ', }; This gives flexibility, ensuring that both the anchor text and links are localized and reusable. Step 4: Use the format() Function for Dynamic Replacement Now comes the key step: using the format() method to replace the placeholders with the actual URLs. While JavaScript doesn’t have a built-in method like some other languages such as Python, we can define our own formatting function to replace placeholders dynamically like this: String.prototype.format = function (replacements) { return this.replace(/{(\w+)}/g, (match, key) => typeof replacements[key] !== 'undefined' ? replacements[key] : match ); }; // Example of localized string from i18n file const localizedString = i18n.t('terms_and_privacy'); // Example for English const formattedStringEn = localizedString.format(en); console.log(formattedStringEn); // Output: By using our service, you agree to our Terms and Conditions and Privacy Policy . // Example for Japanese const formattedStringJa = localizedString.format(ja); console.log(formattedStringJa); // Output: 本サービスを利用することにより 利用規約 および プライバシーポリシー に同意したものとみなされます。 The format() function searches for placeholders in curly braces, like {terms} or {privacy}, and replaces them with the corresponding values from the urls object. Note for developers: you will see that I have inserted raw HTML into the strings, just for visual aid and demonstration purposes. In a production app, always sanitize them to prevent injection. Final Thoughts Combining placeholder-based translation strings with dynamic formatting gives you flexibility and control. It keeps your source clean and helps avoid many technical issues as well as human errors. There are different placeholders, depending on the language, framework, or the type of argument they represent. While the Microsoft internationalization guidelines suggest giving translators control over placeholder naming, the key I think here is ensuring that developers and translators share a clear understanding of what each placeholder signifies. To quote an example from their guidelines, to express Available Monday to Friday, from 8 AM to 5 PM Central Time, the first version will be easier to understand than the second one: Available {firstWeekDay} to {lastWeekDay}, from {startTime} to {endTime} {timeZone}. vs. Available %s to %s, from %s to %s %s. To wrap up, here’s a quote I like from Microsoft: When working on any software project, it's important to think about internationalization from the very beginning of its design. Your first iteration or release may not be translated into another language, but the decisions that you make early in the project can determine your ability to deliver to other markets later. […] It's important to ensure all our features are world-ready.
アバター
翻訳が必要な文章に含まれるURLや日付、時刻などの変数の取り扱いは、ローカライゼーションにおいてよくある課題です。これらの位置やフォーマットは言語によって大きく異なるため、多言語対応を同時に進めるような場面では、文字列の管理が非常に複雑になる場合もあります。 こんにちは、KINTOテクノロジーズでローカライゼーションを担当しているMayaです。本記事は、社内で多言語コンテンツを扱いつつも開発バックグラウンドはなく、それでもエンジニアと連携しながら効率的な仕組みをつくりたい!と思い悩んでいる方にぜひお役に立てば良いなという思いで書きました。 コーディング経験がなくても大丈夫です。実装のサポートをいただける同僚がいると理想的ですが、最近はソース言語と翻訳データをエンジニアチームと一緒に整えるための情報もオンラインに多く公開されています。 ちなみに私たちのチームでは、TMS はLokalise を翻訳データのSingle Source of Truthとして使用しており、GitHub に連携しています。 なぜこの仕組みの理解が重要か ローカライゼーション担当者がURLや日付のような動的プレースホルダーの扱い方を理解することで、作業効率が上がるだけでなく、開発チームとの認識ギャップも縮まる期待が見込めます。わかりやすく説明してくれた開発チームのWenさんに感謝です! このアプローチを導入してから(特に利用規約や日付・時間などプレースホルダーを多く含む文の)翻訳対応箇所の修正工数が月に5件ほどからほぼゼロに削減されたのです。時間の節約だけでなく、QAとのやり取りも減り、ローカライゼーションと開発の間の信頼関係も向上しました。 課題:言語ごとに異なるURLの位置 では本題の課題を見てみましょう。以下のような文章があるとします。 "本サービスを利用することにより、利用規約 および プライバシーポリシー に同意したものとみなされます。" 表現は一旦目を瞑って頂きたいのですが、英語版が以下の形だったとして、URLの位置は英語とは異なります。 "By using our service, you agree to our Terms and Conditions and Privacy Policy." さらに3つ目の言語が加われば、その配置はまた異なるかもしれません。多言語を同時に扱う場合、これらの構文差を手動で管理するのはとても手間がかかり、ミスも起こりやすくなります。 解決策:formatString() を使おう ここで登場するのが、この関数です。言語ごとにURLの配置を柔軟に変えられるテンプレートを作成できるため、非常に役立ちます。文全体を各言語ごとに書き換える必要がなく、URLの位置だけを調整できます。 多くのチームが直面している面倒な代替手段は、各言語の文字列にURLを直接ハードコーディングしてしまうことです。これにより、ほとんど同じ文のバリエーションを手作業で複数作成することになり、文字列ファイルが煩雑になり、タイプミスやリンク切れのリスクが高まり、URL変更時のメンテナンスが非常に大変になります。 この記事では、JavaScriptを例にこの課題への解決策を紹介しますが、同様の考え方はPython、C#、PHPなど他の言語でも応用可能です。 ステップ 1:文中の動的要素を特定する まず、英語(ベース言語)の例文を見てみましょう: “By using our service, you agree to our {terms and conditions} and {privacy policy}.” ここで {terms and conditions} と {privacy policy} はURLに置き換えるプレースホルダです。これらの内容と配置は言語ごとに異なりますが、リンクとして機能する必要があります。言語ごとに文構造が異なるため、プレースホルダは各言語の構文に合わせて動的に扱う必要があります。 ステップ 2:プレースホルダ付きの翻訳対象文字列を作成する i18nファイル内のキーと値の形式で、プレースホルダ付きの文を定義します。記号ではなく名前付きプレースホルダを使うことで、より明確でミスを防ぎやすくなります。 json CopyEdit { "terms_and_privacy": "By using our service, you agree to our {terms} and {privacy}." } ここでは、コード上の混乱を避けるため、{terms} と {privacy} に簡略化しています。ベストプラクティスとしては、snake_case や camelCase の簡潔で説明的なキーを使うのが良いでしょう。 ステップ3:言語ごとにURLとリンクテキストを定義する 次に、URLだけでなくリンクテキストも翻訳して定義します。言語ごとに個別に処理する必要があります。 英語の場合: js CopyEdit const en = { terms: ' Terms and Conditions ', privacy: ' Privacy Policy ' }; 日本語の場合: js CopyEdit const ja = { terms: ' 利用規約 ', privacy: ' プライバシーポリシー ' }; この方法により、リンクテキストとURLの両方を翻訳・再利用可能な形で柔軟に扱えます。 ステップ4:format()関数で動的にプレースホルダを置換する ここがポイントです。format() メソッドを使って、プレースホルダを実際のURLに置き換えます。 JavaScriptにはPythonのような組み込みの format() メソッドはありませんが、以下のように自作することで同様の機能が実現できます: js CopyEdit String.prototype.format = function (replacements) { return this.replace(/{(\w+)}/g, (match, key) => typeof replacements[key] !== 'undefined' ? replacements[key] : match ); }; 例: js CopyEdit // i18nファイルから取得した翻訳済み文字列 const localizedString = i18n.t('terms_and_privacy'); // 英語版 const formattedStringEn = localizedString.format(en); console.log(formattedStringEn); // 出力:By using our service, you agree to our Terms and Conditions and Privacy Policy . // 日本語版 const formattedStringJa = localizedString.format(ja); console.log(formattedStringJa); // 出力:本サービスを利用することにより 利用規約 および プライバシーポリシー に同意したものとみなされます。 この format() 関数は {terms} や {privacy} といった中括弧付きのプレースホルダを検索し、指定されたオブジェクトの値で置換します。 開発者向けメモ:ここでは視覚的にわかりやすくするために生のHTMLを挿入していますが、本番環境では必ずサニタイズ処理を行い、インジェクション対策をしてください。 最後に プレースホルダベースの翻訳文字列と動的フォーマットを組み合わせることで、柔軟性と制御性を確保できます。ソースコードをクリーンに保てるだけでなく、技術的な問題や人的ミスの回避にもつながります。 プレースホルダの形式は言語やフレームワーク、引数の種類によって異なります。Microsoftの国際化ガイドラインでは、翻訳者がプレースホルダ名を制御できるようにすべきとされていますが、最も重要なのは「開発者と翻訳者が、それぞれのプレースホルダが何を意味するのかを正しく理解し合うこと」だと思います。 例として、以下の2つの書き方を比べると、最初のものの方が理解しやすいです: css CopyEdit Available {firstWeekDay} to {lastWeekDay}, from {startTime} to {endTime} {timeZone}. よりも: perl CopyEdit Available %s to %s, from %s to %s %s. Microsoftの引用より ソフトウェアプロジェクトを進める際は、最初の段階から国際化を意識することが重要です。最初のバージョンやリリースでは翻訳が不要かもしれませんが、初期の設計での判断が将来的に他市場向けに展開できるかどうかを左右します。…すべての機能を「ワールド・レディ(世界対応)」にしておくことが大切です。 参考リンク https://learn.microsoft.com/en-us/globalization/methodology/international-design
アバター
はじめに こんにちは、グローバル開発部のYao, Bahng, Laiです。 普段は、 Global KINTO App のモバイルアプリエンジニアとしてアプリ開発を行っています。 数ヶ月前、Global KINTO Appの今後に関する事前アクティビティとして、Kotlin Mltiplatform Mobile (KMM)について調査しました。これについては私たちの 前回の記事 をご覧ください。 前回の調査の結果、KMMはプロダクトを迅速に開発するための非常に優れた解決策であることを示していました。 今回、Compose UIと一緒に機能する新しいアプローチのようなKMMが明らかになったので、私たちはKMMについてさらに調査することにしました。 この記事では、その調査に基づいて、Kotlin Multiplatform Mobile(KMM)とCompose Multiplatformを利用したアプリ開発についてお話します。 記事の本題に入る前に、まず以下の3つを説明します。 KMPとは? KMMとは? KMPとKMMの関係は? 回答は以下の通りです: KMP = Kotlin Multiplatform、Kotlinを利用して、複数のプラットフォームに対応したアプリケーションを開発するための技術や、それに関連するエコシステム全体を指しております。 KMM = Kotlin Multiplatform for mobile、KMPの主な利用ケースの一つは、モバイルプラットフォーム間でのコード共有です。KMPに加えて、いくつかのモバイルアプリケーション開発に特化した技術を総称して、「KMM」という名称で呼ばれております。 KMPとKMMの関係を説明するためにグラフを描きました。 参考: -- JetBrains "Kotlin brand assets | Kotlin. (n.d.-c). Kotlin Help." , "Get started with Kotlin Multiplatform for mobile | Kotlin. (n.d.). Kotlin Help." Accessed June 1, 2023 クロスプラットフォーム KMMがクロスプラットフォームのソリューションの一つだという前提で、クロスプラットフォーム開発のメリットについて疑問に思われるかもしれません。以下にその利点を記載します。 コスト効率が良い:クロスプラットフォーム開発により、複数のプラットフォームで単一のコードベースを使用できるため、プラットフォーム別の開発チームが必要なくなり、アプリ開発のコストが削減されます。 より速いデプロイメント:単一のコードベースを活用することで、開発者は複数のプラットフォームで同時にアプリケーションを作成およびローンチできるため、開発時間が大幅に短縮され、リリースまでの時間が短縮されます。 メンテナンスとアップデートの簡素化:単一のコードベースを使用することで、アプリケーションのメンテナンスとアップデートが容易になり、変更を一度行うだけですべてのプラットフォームに反映されます。これにより、メンテナンスプロセスが合理化され、すべてのユーザーが最新の機能を利用できるようになります。 一貫したユーザーエクスペリエンス:クロスプラットフォーム開発ツールとフレームワークを使用することで、異なるプラットフォーム間で一貫したルックアンドフィールを維持でき、統一されたユーザーエクスペリエンスが提供されます。これにより、ユーザー満足度やユーザー維持率の向上に繋がる可能性があります。 共有リソースとスキル:クロスプラットフォームツールと言語に精通した開発者は、複数のプラットフォーム向けのアプリケーションを作成することができます。これにより、リソースの効率的な使用が可能となり、開発者のスキルやトレーニングへの投資のリターンを最大限に引き出すことができます。 モバイル向けクロスプラットフォーム開発ツールの歴史 2009年にPhoneGapが作成され、その後Apache Cordovaに改名されました。 2011年には、MonoによってXamarinが作成され、後にMicrosoftに買収されました。 2015年には、Facebook(Meta)によってReact Nativeが作成されました。 2010年代半ば、デザイナーのFrances BerrimanとGoogle ChromeエンジニアのAlex Russellが「プログレッシブ ウェブ アプリ」という用語を作り出し、Googleはそれを普及するためにいくつかの努力を行いました。2017年には、GoogleによってFlutterが作成されました。 2021年には、JetBrainsによってKMMが作成されました。 つまり、現時点ではKMMが最新のクロスプラットフォームのソリューションです。 ロゴ出典: -- Apache "Artwork - Apache Cordova. (n.d.)." Accessed June 1, 2023 -- Microsoft "Conceptdev. (n.d.). Xamarin documentation - Xamarin. Microsoft Learn" Accessed June 1, 2023 -- Meta "Introduction · React native." Accessed June 1, 2023 -- Google "Progressive web apps. (n.d.). web.dev." Accessed June 1, 2023 -- Google "Flutter documentation. (n.d.)." Accessed June 1, 2023 -- JetBrains "Kotlin Multiplatform for Cross-Platform development" Accessed June 1, 2023 なぜKMMが他と違うのか? 共有ビジネスロジック:KMMは、ビジネスロジック、ネットワーク、データストレージに関連するコードをプラットフォーム間で共有できるようにすることで、コードの重複を減らし、AndroidとiOS間の一貫性を維持します。 真のネイティブUI:KMMは、UI開発にプラットフォーム固有のツールや言語(例:Android用のXML、iOS用のSwiftUIやUIKit)を使用し、他のクロスプラットフォームソリューションと比較してよりネイティブなルックアンドフィールを実現します。 パフォーマンス:Kotlinコードは各プラットフォーム用のネイティブバイナリにコンパイルされるため、ネイティブ開発と同等の高性能なアプリケーションが実現されます。 シームレスな統合:KMMは既存のプロジェクトに統合できるため、開発者は段階的に採用し、アプリを完全に書き換えることなくKotlinへの共有ロジックを移行できます。 ネイティブライブラリとの相互運用性:KMMは、AndroidとiOSの両方のネイティブライブラリとシームレスに相互運用できるため、開発者は既存のライブラリやフレームワークを簡単に活用できます。 Kotlin言語の利点:Kotlinは、モダンで簡潔な言語であり、冗長なコードを減らしながら、既存同等の機能を提供でき、JetBrainsからツールサポートも提供されています。 上記のポイントについて、以下に詳しく説明いたします。 ① Shared Business Logic 共有ビジネスロジックについて 新しいプロジェクトでデータ、ビジネス、プレゼンテーション層を実装する際、KMMを使用します。 柔軟性:KMMは、共有したいコードの範囲を開発者が決定できるため、必要に応じてプラットフォーム固有のコードとバランスの取れた柔軟な実装が可能です。 整合性の担保:UIの違いはQAテストで容易に発見できますが、ロジック部分でAndroid/iOSの不整合を発見することは難しいです。 KMMを使用することで同じコードを用いることができるので、整合性を担保することができます。 ② Truly Native UI 真のネイティブUIについて KMMはネイティブなUIをサポートし、ネイティブなUIコンポーネントを使用し、プラットフォーム固有のデザインパターンに従います。 Android:xml、Jetpack Composeなど iOS:UIKit、SwiftUIなど UIパフォーマンス:KMMはネイティブUIコンポーネントを使用し、Kotlinコードが各プラットフォーム用のネイティブバイナリにコンパイルされるため、パフォーマンスは一般的にネイティブアプリケーションと同等です。 容易なプラットフォームの更新:KMMを使用すると、開発者は新しいプラットフォーム機能やデザインの更新を簡単に行うことができます。各プラットフォームのネイティブUIフレームワークを使用しているためです。 ③ Performance パフォーマンスについて JavaScriptブリッジは不要で、サードパーティーのライブラリに依存しません。 独立したレンダリングエンジンを使用する必要はなく、システムデフォルトのレンダリングエンジンを使用するため、他のクロスプラットフォームソリューションと比較してリソース消費が少なくなります。 ネイティブコードのコンパイル:KMMは、Kotlinコードを各プラットフォームのネイティブバイナリにコンパイルします。このネイティブコードのコンパイルにより、アプリはより効率的に実行され、パフォーマンスが向上します。 Android:標準のKotlin/JVM iOS:Kotlin/Nativeコンパイラ(Objective-C) ④ Seamless Integration シームレスな統合について ネイティブモジュールをブリッジする必要はなく、既存のコードを書き換える必要もありません。 段階的な採用:KMMは、既存のネイティブAndroidおよびiOSプロジェクトに徐々に導入できます。これにより、チームはビジネスロジック、ネットワーク、データストレージコードを段階的にプラットフォーム間で共有でき、完全な技術切り替えに関連するリスクを軽減できます。 iOSでKMMモジュールを利用する複数のアプローチ CocoaPods Gradleプラグインとgitサブモジュール Framework Swift Package Manager(SPM):Kotlin 1.5.30から、Swift Package Managerを使用してiOSプロジェクトでKMMモジュールを利用できます ⑤ Interoperability with Native Libraries ネイティブライブラリとの相互運用性について ネイティブAPIおよびライブラリへのアクセス:KMMはネイティブAPIとライブラリへ直接アクセスできるので、センサーやBluetoothなどのプラットフォーム固有の機能やハードウェアコンポーネントと簡単に統合することができます。 プラットフォーム固有のコードとのシームレスな統合:KMMは、必要に応じてプラットフォーム固有のコードを記述できるようになっています。これは複雑なネイティブライブラリを扱う場合や、共有Kotlinコードを通して利用できない機能にアクセスが必要な場合に有効です。 Kotlin/Native:KMMは、iOS向けにKotlin/Nativeを使用します。これにより、Objective-CおよびSwiftコードとのシームレスな相互運用性が実現されます。これは、既存のiOSライブラリやフレームワークを追加のブリッジングやラッピングコードなしで使用できることを意味します。 ⑥ Kotlin Language Benefits Kotlin言語の利点について 言語機能:モダンで静的型付け、null安全、拡張関数、データクラス、スマートキャスト Javaとの相互運用性 ツールおよびサポート:Kotlinは、優れたツールサポートを提供し、Android StudioおよびIntelliJ IDEAでの第一級の統合が行われています。 業界による採用:Kotlinは、Android開発の公式プログラミング言語になって以来、急速に採用が進んでいます。多くのバックエンド開発者もKotlinを使用しています。 KMMを利用しているのは、どのような方々でしょうか 実際に複数の企業がKotlin Multiplatform Mobile(KMM)をモバイルアプリ開発に採用しています。いくつかの注目すべき例を挙げます。 Netflix:Netflixは、AndroidアプリとiOSアプリ間でコードを共有するために、一部の内部ツールでKMMを使用しています。 VMware:VMwareは、Workspace ONE Intelligent Hubアプリ(AndroidおよびiOS向けの従業員管理ツール)のクロスプラットフォーム開発にKMMを使用しています。 Yandex:ロシアの多国籍技術企業であるYandexは、Yandex MapsやYandex Diskを含むいくつかのモバイルアプリでKMMを採用しています。 Quizlet:オンライン学習プラットフォームであるQuizletは、AndroidアプリとiOSアプリ間でコードを共有するためにKMMを使用し、開発効率を向上させています。 これらの企業は、多様な業界を代表しており、KMMの採用は、異なるコンテキストでの技術の柔軟性と有用性を示しています。KMMがさらに普及するにつれて、より多くの企業がクロスプラットフォームのモバイル開発のニーズに対応するためにKMMを採用することが予想されます。 参考: -- JetBrains "Case studies. (n.d.). Kotlin Multiplatform." Accessed June 1, 2023 KMMプロジェクトを簡単に作成する方法 上記のメリットを考慮した上で、KMMプロジェクトを作成して試してみたいと思われますか?以下に方法をご案内いたします。 最新のAndroid Studioをダウンロードします。 Android Studioで、ファイル|新規|新規プロジェクトを選択します。 プロジェクトテンプレートのリストでKotlin Multiplatform Appを選択し、Nextをクリックします。 最初のアプリケーションに名前を付け、Nextをクリックします。 iOSフレームワーク配布リストで、Regular frameworkオプションを選択します。 アプリケーションと共有フォルダのデフォルトの名前を維持します。Finishをクリックします。 -- JetBrains "Create your first cross-platform app | Kotlin. (n.d.). Kotlin Help." Accessed June 1, 2023 KMMを使用したモバイルアプリのアーキテクチャ 以下のグラフは、KMMの標準的なパターンの一つの例です。 このアーキテクチャは、KMMの特性であるコード共有を最大限に活用しています。キャッシュ、データベース、ネットワーク、ユースケース、ビューモデルを含むデータ永続化は、すべてKMMで実装されています。 UIについては、AndroidとiOSの両方でネイティブUIコンポーネントを使用しています。XMLやUIKitなどの古いフレームワークから、Jetpack ComposeやSwiftUIを含めた新しいフレームワークもサポートされています。 このアーキテクチャではKotlinで書かれたビジネスロジックモジュールをSDKとしてiOSにインポートすることができます。これにより、iOS開発者はUI開発に集中でき、効率的な開発が実現できます。 下記はFAQリストを表示する簡単な画面のiOS側の実装になります。共通のUI Utility Classを除くと実装が必要なものはこれで終わりです。 #FaqView.swift struct FaqView: View { private let viewModel = FaqViewModel() @State var state: FaqContractState init() { state = viewModel.createInitialState() } var body: some View { NavigationView { listView() } .onAppear { viewModel.uiState.collect(collector: Collector<FaqContractState> { self.state = $0 } ) { possibleError in print("finished with possible error") } } } private func listView() -> AnyView { manageResourceState( resourceState: state.uiState, successView: { data in guard let list = data as? [Faq] else { return AnyView(Text("error")) } return AnyView( List { ForEach(list, id: \.self) { item in Text(item.description) } } ) }, onTryAgain: { viewModel.setEvent(event: FaqContractEvent.Retry()) }, onCheckAgain: { viewModel.setEvent(event: FaqContractEvent.Retry()) } ) } } それがKMMの全てではありません。KMMにはさらなる可能性があります! UIコードを共有するアーキテクチャ ビジネスロジックコードだけでなく、KMMはCompose Multiplatformを使用してUIコードも共有することができます。 先述のように、KMMは主に共有ビジネスロジックコードの実装に使用されますが、共有UIにも対応しています。Compose Multiplatformは、Kotlinで複数のプラットフォーム間でUIを共有するための宣言的フレームワークです。Jetpack Composeをベースに、JetBrainsとオープンソースの貢献者によって開発されました。KMMとCompose Multiplatformを組み合わせることで、Kotlin言語を使用してロジックコードとUIを両方構築できます。 参考: -- JetBrains "Kotlin brand assets | Kotlin. (n.d.-c). Kotlin Help." , "Compose multiplatform UI framework | JetBrains. (n.d.). JetBrains: Developer Tools for Professionals and Teams." Accessed June 1, 2023 KMMのアーキテクチャの異なるパターンの比較 モバイルプロジェクトを開発すると仮定し、各クライアントの工数は次のとおりです: UI:2人、Presentation:1人、Business/Domain:1人、Data/Core:1人 ここから削減できる工数はKMMによって書かれたコードの割合に基づきます。 パターン A B C D UI 2*2 2*2 2*2 2 Presentation 1*2 1*2 1 1 Business/Domain 1*2 1 1 1 Data/Core 1 1 1 1 合計 9 8 7 5 工数コスト -10% -20% -30% -50% KMMを用いることで最大で50%もの工数を削減することができます。 他のクロスプラットフォームソリューションと比較して、KMMの最大の利点は、KMMが柔軟にコード共有できることです。 KMMでどれだけのコードを共有するかは、完全に私たち自身で決定できます。 他のクロスプラットフォームソリューションではここまでの柔軟性がないため、こうはいきません。 まとめ KMMの欠点 もちろん、すべてのツールにはデメリットがあります。KMMにもデメリットがあります。 限られたプラットフォームのサポート: Kotlin Multiplatform Mobileは複数のプラットフォームを対象とできますが、すべてのプラットフォームをサポートしているわけではありません。たとえば、現時点ではWebやデスクトップアプリケーションをサポートしていません。 学習コスト: Kotlinに慣れていない場合、マルチプラットフォーム開発に効果的に使用するための学習コストが必要です。 フレームワークの互換性: Kotlin Multiplatform Mobileはさまざまなフレームワークと共に使用できますが、すべてのフレームワークと互換性があるわけではありません。これにより、オプションが制限され、特定の制約内で作業する必要があるかもしれません。 メンテナンスのオーバーヘッド: マルチプラットフォームのコードベースを維持することは、各プラットフォームごとに別々のコードベースを維持するよりも複雑になる可能性があります。これにより、テスト、デバッグ、メンテナンスにおいて追加のオーバーヘッドが生じる可能性があります。 ツールの制限: 一部のツールやライブラリがKotlin Multiplatform Mobileと互換性がない場合があり、開発がより困難になるか、代替のソリューションを探す必要があるかもしれません。 応用 上記の通り、メリットやデメリットを含めて、さまざまな状況に応じてKMMのアーキテクチャをプロジェクトに統合することが検討できます。 パターン A B C D 一般的な既存プロジェクト ✓ ✓ ✓ ? シンプルな既存のプロジェクト ✓ ✓ ✓ ✓ 複雑な既存プロジェクト ✓ ✓ ✓ ✗ 新しいプロジェクト ✓ ✓ ✓ ✓ プロトタイプ ✓ ✓ ✓ ✓ 技術的な利点を一旦ここまでとして、実開発プロセスの話題に戻ります。多くモバイル開発チームと同様、私たちのチームも規模が小さいです。限られたエンジニアリソースの中で、バージョン1.0から2.0へのアップグレードといった比較的大規模な変更に直面した場合、我々は他の部署やオンサイトまたオフショアのアウトソーシングチームと連携し、リリースの迅速化を確実にする必要があります。 しかし、この過程に置いて、以下のような複数の問題が存在します: 異なるチーム間のシームレスな連携が難しい。 開発者が増え、異なるオフィスに所属する異なるチームとなると、コミュニケーションコストが上昇します。 異なるチーム間での一貫性を維持することが難しくなります。 外部チームとの連携において、機密情報のセキュリティ管理が難しくなります。 KMMはコアモジュールの開発を行い、プロトコルを定義し、UIとロジックを分離した開発というアプローチを取ることで、これらの問題をほぼ全て解決することができます: 各チームが自分のパートに専念することが可能になります。協力作業を大幅に容易にすることができます。 コミュニケーションに必要な時間やコストを削減することができます。 コアモジュールはKMMチームが開発することで、同じ基盤で開発を行うことで、ほとんどの不整合が事前に解消されます。 KMMは一つのコードベースをサポートしているものの、UI層とロジック層が分離しているため、複数のリポジトリを利用することも可能です。コアモジュールはKMMチームが開発し、SDKは外部チームに提供します。これにより、ソースコードが外部チーム公開しする必要がなく、機密情報の漏洩リスクを減らすことができます。これは他のクロスプラットフォーム技術のソリューションでは達成が難しいです。 結論として、KMMは技術的な利点だけでなく、部門や会社を超えた協力作業にも多大な利点をもたらしてくれると言えます。 最後に 新プロジェクトにおけるKMMの重要性と、大幅な工数削減の可能性を考慮し、すでに次のメジャーリリース向けの新プロジェクトにKMMを統合しました。私たちは引き続きKMMに関連する新たな技術やツールに留意し、さらなる効率向上を図るための可能性を探し続けます。
アバター
はじめに こんにちは、KINTOテクノロジーズ(以下、KTC)プロジェクト推進GのRisako.Nです。 KTCには2023年3月に入社して、PjMという役割を担っています。キャリアとしてはずっとSIerで、新卒当初は主にWebベースの業務システムをゴリゴリ開発、その後いわゆる上流工程へどんどんスライドし、ここ数年はほぼPMを担ってきました。 今回の記事では、SIerでのPM経験を踏まえつつ、KTCのPjMの仕事をご紹介したいと思います! プロジェクトとは KTCでは基本的にプロダクト(以下、PD)ごとに開発チームやPdMがいて、開発チームはKINTOの各事業部門とコミュニケーションをとりながら、日々PDの機能強化や改善案件に対応しています。また案件は事業部門から起案されるものだけではなく、法令対応やアーキテクチャの更改など開発チームから起案するものもあり、常に複数の案件が並行して動いています。 そして案件によっては、実現したい1つのことに対して複数のPD・複数の事業部門を跨って対応が必要なものもあります。こういった部門横断的に進めていく必要があるもの、ある一定期間以上(あくまで目安ですが4〜5ヶ月以上)の長さがあり、かつ有期的な案件を「プロジェクト」と呼び、その「プロジェクト」に対して実行責務をもったPjMを配置して、プロジェクトを実行・推進していきます。 プロジェクトはどう発足されるのか 次にプロジェクトがどう発足されるのか、その流れについてご紹介します。KINTO/KTCでは、プロジェクトは以下のステップを踏んでいくことになります。 企画の起案 企画としての成立性(売れる商品か、収益は担保できるかなど)に対する社内合意・承認 企画を実現するために関係各部よりメンバーがアサインされて、プロジェクトが組成・発足 この3ステップ目のプロジェクト組成のタイミングで、事業側・システム側それぞれの推進役であるビジネス統括・システムPM(=PjM)がアサインされ、このビジネス統括とシステムPMを中心にプロジェクトを立ち上げ、実行・完遂させていくことになります。 このようにPjMがアサインされるのは基本的にプロジェクトを立ち上げる(企画がほぼ形作られた)タイミングとなるわけですが、KTCには企画段階からシステム開発視点をもって企画やプロジェクトの立ち上げを支援する「プロデュースG」もあります。プロデュースGはどの案件にも常に参画しているわけではないのですが、もしアサインされるプロジェクトにプロデュースGが関係している場合はPjMはプロデュースGからこのプロジェクトの背景や向かう方向性、現在の課題など引継ぎながら、プロジェクトを立ち上げ、実行フェーズへと突入させていきます。 …と、基本的には上述の流れなのですが、そのプロジェクトごとに個別の状況があり、また携わるメンバーも様々であるため、プロジェクトごとに立ち上がり方は様々です。 例えば、 要件定義までは昨年度終わっていて満を持して今年度再開したプロジェクト 昨年度には全く企画されていなかったけれど急遽企画があがり走り出したプロジェクト などなど またいったん企画が走り出したものの、検討するにつれて見えてきたものや環境の変化により、推進が凍結されたプロジェクトもあります。こういった立ち上げや意思決定のスピード感はKINTO/KTCならではかなと思います。 プロジェクトの進め方とPjMとしてやること プロジェクトには色々なものがありますが、例えばKINTO ONEの新しいプランを打ち出そう!というKINTO ONE商品開発プロジェクトを例にすると、プロジェクト全体という大きな視点で見ると、シンプルにウォーターフォールで進めることが多いです。 <ウォーターフォール型プロセス> 「プロジェクト全体」という表現をしたのは、そのプロジェクトに参画しているPDは、 ユーザーストーリー単位で、設計・開発・テスト工程を繰り返すPD 全機能設計して、開発・テストと進めていくPD といったように、PDチームごとに開発の進め方が異なるためです。 このプロセスの中でPjMは何をするのかというと、 プロジェクトを立ち上げる段階では、今回のプロジェクトの目的やビジネス要求をよく理解し、事業部門と要件の検討、調整を行いながらシステム要件定義を推進 要件定義後は基本的にPDごとに設計開発を進めることになりますが、各PDの開発規模はそのプロジェクトによって大小様々、各PDは他の案件も並行して進めている状況なので、各PDどのような開発スケジュールで進めていくのかを把握し、定期的に進捗状況を確認しながら案件の進捗管理 各PDが開発を進めている間に、PD合流後のテスト準備、QAチームとのテスト調整 要件定義後に挙がってくる追加/変更要望、リリース日の決定など、事業部門との調整全般 などです。 PjMを実際やり始めてみて PjMの役割は「プロジェクトのゴールに対して実行計画をたて実行、推進、完遂までやりとげること」であり、役割自体は他の会社でも多く同じなのではと思います。ただわたしのこれまで経験してきたSIerプロジェクトと比べてみると、よりビジネスに近い立場でプロジェクトの推進役を担っているように思います。これまでシステムコンサル、PjMどちらの参画としてもやはり事業側とは受発注の関係であったため、入り込めない(また、入り込むべきではない)壁がありましたが、KINTOとKTCは会社そのものは別としても基は同じなので、良い意味で分け隔てなく意見を飛び交わせ、協力して進めていきます。 PjMとしてはシステム開発を計画・主導していくことはもちろんですが、ビジネスをよく理解したうえでシステム観点からKINTOの事業拡大に寄与できるように、またビジネス+システムというプロジェクト全体のマネジメントを視野に入れて経験を積んでいきたいと思っています。 そしてKTCでPjMをやり始めて難しいと思ったことは、KTCにはデザイナーから商用サイト、業務システム開発と様々な分野に携わるメンバーがいて、それぞれ様々な経験を持って集まってきているため、今まで常識と思っていたことが常識ではない、やり方も個々それぞれのやり方があってそれを尊重しつつ、でもリードもしていかないと!というところです。 またビジネス面としても、実際わたしは入社して2ヶ月目で新しいプランを打ち出すプロジェクトにPjMとして携わったのですが、KINTOではどうやって新プランを作り上げていくのか、リース業として検討・ケアするべきことは何か、自動車業界ではどういったことが起こりうるのか(どういったリスクが潜んでいるのか)など、わからないことだらけでした。ただわからないこと1つずつに対して、過去プロジェクトの情報を収集する、まわりの人に聞くなどしてわかることに変えていきながら、プロジェクトとしての立ち上げや推進はこれまでの経験を活かして前進!でした。 プロジェクトに参画して推進するためには、関係者の方々との関係性も築く必要があるので、基本的にはオンラインで会話しつつも、直接会って話をするために積極的に名古屋出張にも行きました。 ちなみにKINTO名古屋オフィスは「来た人が楽しくなるオフィスに」という想いのあるデザイン設計がされていまして、ちょっとオシャレなのです!( 初公開!KINTO名古屋オフィスツアー でご紹介しています!)加えて、オフィスの近くには柳橋中央市場(名古屋中心街に急に現れる本格的な市場!)があったり、有名パン屋さんがあったり。。出張っていいですよね! ![](/assets/blog/authors/risako.n/nagoya-morning.png =400x) 名古屋といえばモーニング!コーヒーはデフォルト2杯でした ![](/assets/blog/authors/risako.n/nagoya-food.png =400x) 名古屋セットです! ![](/assets/blog/authors/risako.n/tebasaki.png =400x) 色んな食べ方があるみたいです。一緒に食べていた人はどれでもないって言ってたような… このようにわからない環境に飛び込むことやその中で物事を前進させることは簡単ではないですが、新たな知識や経験、気づきを得ることは面白く、これからも色んな人と出会って自分自身の幅を広げていきたいな〜と思っています。 さいごに KTCはまだ若い会社、かつ取り巻く環境もどんどん変化しているため、プロジェクトの立ち上げ方や推進手法も様々です。その分新しい発見も多く、また自分の意思を持って進めることができる環境にあると思います。そういった環境、またPjMという職種に興味を持っていただけた方、ぜひジョインいただいて一緒に働くことができたらうれしいです!
アバター
自己紹介 こんにちは。KINTOテクノロジーズ株式会社(以降、KINTOテクノロジーズ)のPlatform Group/SRE Teamに所属している渡辺宇と申します。WEBサービスのアプリケーション開発・インフラ構築・CI/CD構築・保守運用をしてきた経験を活かして、自社サービスの信頼性向上をサポートしています。 はじめに どんな優れたサービスでも、全く問題が発生しないという状態は現実的には存在しません。そのため、予めどれくらいの問題が発生しても許容範囲内であるかという目標を設定し、場合によってはこれをユーザーと共有し、合意を形成することが現代のサービス提供において重要な考え方となっています。 具体的には、Service Level Indicator(以降、SLI)を用いてサービスレベルの指標を定義し、Service Level Objective(以降、SLO)でその目標値を設定します。そして、Service Level Agreement(以降、SLA)を通じて、これらの目標値についてユーザーと合意を取ります。 SLOを設定したら次のステップは、目標違反が発生していないか監視することです。違反が発生した場合にはアラートを発信する必要があります。しかし、アラートを発信するルールは煩雑で、管理が困難になりがちです。 その問題を解決するために、今回の記事ではSlothというアラートルールのジェネレータを用いたアラートルールの生成と管理の効率化について紹介します。 背景 私たちKINTOテクノロジーズでは、以前に紹介した通り、「Prometheus + Grafana + X-Ray」のスタックを用いてテレメトリデータの取得を実現し、リクエスト・レスポンス型のWEBサービスの可観測性(Observability)を向上させています。 https://blog.kinto-technologies.com/posts/2022-12-09-AWS_Prometehus_Grafana_o11y/ この取り組みのおかげで、特に、Spring Bootのアプリケーションメトリクスについては、アプリケーションコード上で特別に計装することなく、多様なメトリクスをPrometheusに格納できています。 格納されたメトリクスの中には、リクエスト単位の成功・失敗ステータスや応答速度のデータも含まれています。これにより、PromQLを用いて、可用性やレイテンシのSLIを表現することが可能になりました。 課題 一般的に、WEBサービスのCUJ(Critical User Journey)に対してSLI/SLOの定義をした後、本番サービスのエラーバジェットの消費状況を監視します。その際、SLO/SLA違反が発生しそうな場合は適切なタイミングでそれを検知する必要があります。そのためには、開発担当者が異変を検知できるように、アラートを設定しておく必要があります。 サイトリライアビリティワークブックでは、SLO違反の検知には「複数ウィンドウ、複数バーンレートのアラート(Multiwindow, Multi-Burn-Rate Alerts)」の手法が、最も現実的でおすすめできると書かれています。適合率・再現率・検出時間・リセット時間をうまくコントロールできるメリットがある、とのことなので積極的に使いたいです。 ウィンドウとバーンレートについて簡単に説明します: ウィンドウ 計測期間のことを指します。どの時点で計測を開始し、どの時点で計測を終了するかの期間です。SLOは比率で表現されるため、計測期間が終わると次の計測期間開始時にサービスレベルは100%に回復します。一般的に、ウィンドウサイズが大きいほど、アラートは発しにくく、かつ、終了しにくくなります。 バーンレート エラーバジェットの消費速度のことを指します。エラーバジェットを全て消費してからアラートが発されると遅いので、エラーバジェットがある程度消費された時点でアラートを発することが理想的です。“サービスのSLOに紐付くエラーバジェットがウィンドウの終わりにちょうど0になるようなバーンレート”を1(基準)とし、それと比較して何倍の速さでエラーバジェットが消費されているのかを計算し、その結果(基準と比べて何倍か)をバーンレートとして数値で保持します。バーンレートが事前に定められた値を超えた時にアラートが発されるように設定します。 「複数ウィンドウ、複数バーンレートのアラート」について詳しく知りたい方は、サイトリライアビリティワークブックの5章「SLOに基づくアラート」をご覧ください。 https://www.oreilly.co.jp/books/9784873119137/ 英語版はWebで公開されています: https://sre.google/workbook/alerting-on-slos/#6-multiwindow-multi-burn-rate-alerts 「複数ウィンドウ、複数バーンレートのアラート」の手法を利用するには、1つのSLI/SLO定義に対して複数のウィンドウとバーンレート、つまり、複数の異なるパラメータを指定したアラートルールを設定する必要があります。 よって、アラートルールの数が増えるため、アラートルールの管理が難しくなるという課題がありました。 やること 今回の記事では、Slothというオープンソースツールを活用して、この課題を解決していきます。 https://sloth.dev/ Slothを利用すると、シンプルな記述でSLI/SLOの仕様を入力し、それをもとに、複雑でエラーになりやすいPrometheusの記録ルールやアラートルール定義ファイルを生成することが可能となります。 私たちKINTOテクノロジーズでは、下図のような構成を採用しています。 Slothには、デフォルトで複数ウィンドウ、複数バーンレートのアラートルールを生成する機能が備わっています。そのため、この記事ではSlothを使って「複数ウィンドウ、複数バーンレートのアラート」の設定をする方法を紹介します。 アラートルール生成 :::message Slothは、OpenSLOの規格に従ったSLI/SLO仕様ファイルを入力とすることが可能です。しかし、現状ではPrometheusのアラートルール生成に対応していないようなので、今回はSloth独自のSLI/SLO仕様記法を採用しています。 ::: 次の簡単なSLI/SLO仕様を、Slothの規格に基づいたYAMLファイルで表現してみます。 Category SLI SLO Availability 30日のうち、アプリケーションで測定される成功したリクエストの割合。500~599と429以外のHTTPステータスは成功とみなします。actuator以外の全てのリクエストパスを統合して計測します。 99.5% version: "prometheus/v1" service: "KINTO" labels: owner: "KINTO Technologies Corporation" repo: "slo-maintenance" tier: "2" slos: # We allow failing (5xx and 429) 5 request every 1000 requests (99.5%). - name: "kinto-requests-availability" objective: 99.5 description: "Common SLO based on availability for HTTP request responses." sli: events: error_query: sum(rate(http_server_requests_seconds_count{application="kinto",status=~"(5..|429)",uri!~".*actuator.*"}[{{.window}}])) total_query: sum(rate(http_server_requests_seconds_count{application="kinto",uri!~".*actuator.*"}[{{.window}}])) alerting: name: KINTOHighErrorRate labels: category: "availability" annotations: # Overwrite default Sloth SLO alert summmary on ticket and page alerts. summary: "High error rate on 'KINTO SERVICE' requests responses" page_alert: labels: severity: "critical" ticket_alert: labels: severity: "warning" http_server_requests_seconds_count はSpring Bootを使った場合のメトリクスです。 このファイルを./source/ディレクトリに保存した状態で、次のコマンドを実行します。 docker pull ghcr.io/slok/sloth docker run -v /$(pwd):/home ghcr.io/slok/sloth generate -i /home/source/slo_spec.yml > slo_generated_rules.yml 上記のコマンドを実行すると、カレントディレクトリに下記のファイルが生成されます。生成されたファイルはそのままPrometheusにアップロードできます。 :::details slo_generate_rules.yml --- # Code generated by Sloth (a9d9dc42fb66372fb1bd2c69ca354da4ace51b65): https://github.com/slok/sloth. # DO NOT EDIT. groups: - name: sloth-slo-sli-recordings-KINTO-kinto-requests-availability rules: - record: slo:sli_error:ratio_rate5m expr: | (sum(rate(http_server_requests_seconds_count{application="kinto",status=~"(5..|429)",uri!~".*actuator.*"}[5m]))) / (sum(rate(http_server_requests_seconds_count{application="kinto",uri!~".*actuator.*"}[5m]))) labels: owner: KINTO Technologies Corporation repo: slo-maintenance sloth_id: KINTO-kinto-requests-availability sloth_service: KINTO sloth_slo: kinto-requests-availability sloth_window: 5m tier: "2" - record: slo:sli_error:ratio_rate30m expr: | (sum(rate(http_server_requests_seconds_count{application="kinto",status=~"(5..|429)",uri!~".*actuator.*"}[30m]))) / (sum(rate(http_server_requests_seconds_count{application="kinto",uri!~".*actuator.*"}[30m]))) labels: owner: KINTO Technologies Corporation repo: slo-maintenance sloth_id: KINTO-kinto-requests-availability sloth_service: KINTO sloth_slo: kinto-requests-availability sloth_window: 30m tier: "2" - record: slo:sli_error:ratio_rate1h expr: | (sum(rate(http_server_requests_seconds_count{application="kinto",status=~"(5..|429)",uri!~".*actuator.*"}[1h]))) / (sum(rate(http_server_requests_seconds_count{application="kinto",uri!~".*actuator.*"}[1h]))) labels: owner: KINTO Technologies Corporation repo: slo-maintenance sloth_id: KINTO-kinto-requests-availability sloth_service: KINTO sloth_slo: kinto-requests-availability sloth_window: 1h tier: "2" - record: slo:sli_error:ratio_rate2h expr: | (sum(rate(http_server_requests_seconds_count{application="kinto",status=~"(5..|429)",uri!~".*actuator.*"}[2h]))) / (sum(rate(http_server_requests_seconds_count{application="kinto",uri!~".*actuator.*"}[2h]))) labels: owner: KINTO Technologies Corporation repo: slo-maintenance sloth_id: KINTO-kinto-requests-availability sloth_service: KINTO sloth_slo: kinto-requests-availability sloth_window: 2h tier: "2" - record: slo:sli_error:ratio_rate6h expr: | (sum(rate(http_server_requests_seconds_count{application="kinto",status=~"(5..|429)",uri!~".*actuator.*"}[6h]))) / (sum(rate(http_server_requests_seconds_count{application="kinto",uri!~".*actuator.*"}[6h]))) labels: owner: KINTO Technologies Corporation repo: slo-maintenance sloth_id: KINTO-kinto-requests-availability sloth_service: KINTO sloth_slo: kinto-requests-availability sloth_window: 6h tier: "2" - record: slo:sli_error:ratio_rate1d expr: | (sum(rate(http_server_requests_seconds_count{application="kinto",status=~"(5..|429)",uri!~".*actuator.*"}[1d]))) / (sum(rate(http_server_requests_seconds_count{application="kinto",uri!~".*actuator.*"}[1d]))) labels: owner: KINTO Technologies Corporation repo: slo-maintenance sloth_id: KINTO-kinto-requests-availability sloth_service: KINTO sloth_slo: kinto-requests-availability sloth_window: 1d tier: "2" - record: slo:sli_error:ratio_rate3d expr: | (sum(rate(http_server_requests_seconds_count{application="kinto",status=~"(5..|429)",uri!~".*actuator.*"}[3d]))) / (sum(rate(http_server_requests_seconds_count{application="kinto",uri!~".*actuator.*"}[3d]))) labels: owner: KINTO Technologies Corporation repo: slo-maintenance sloth_id: KINTO-kinto-requests-availability sloth_service: KINTO sloth_slo: kinto-requests-availability sloth_window: 3d tier: "2" - record: slo:sli_error:ratio_rate30d expr: | sum_over_time(slo:sli_error:ratio_rate5m{sloth_id="KINTO-kinto-requests-availability", sloth_service="KINTO", sloth_slo="kinto-requests-availability"}[30d]) / ignoring (sloth_window) count_over_time(slo:sli_error:ratio_rate5m{sloth_id="KINTO-kinto-requests-availability", sloth_service="KINTO", sloth_slo="kinto-requests-availability"}[30d]) labels: owner: KINTO Technologies Corporation repo: slo-maintenance sloth_id: KINTO-kinto-requests-availability sloth_service: KINTO sloth_slo: kinto-requests-availability sloth_window: 30d tier: "2" - name: sloth-slo-meta-recordings-KINTO-kinto-requests-availability rules: - record: slo:objective:ratio expr: vector(0.995) labels: owner: KINTO Technologies Corporation repo: slo-maintenance sloth_id: KINTO-kinto-requests-availability sloth_service: KINTO sloth_slo: kinto-requests-availability tier: "2" - record: slo:error_budget:ratio expr: vector(1-0.995) labels: owner: KINTO Technologies Corporation repo: slo-maintenance sloth_id: KINTO-kinto-requests-availability sloth_service: KINTO sloth_slo: kinto-requests-availability tier: "2" - record: slo:time_period:days expr: vector(30) labels: owner: KINTO Technologies Corporation repo: slo-maintenance sloth_id: KINTO-kinto-requests-availability sloth_service: KINTO sloth_slo: kinto-requests-availability tier: "2" - record: slo:current_burn_rate:ratio expr: | slo:sli_error:ratio_rate5m{sloth_id="KINTO-kinto-requests-availability", sloth_service="KINTO", sloth_slo="kinto-requests-availability"} / on(sloth_id, sloth_slo, sloth_service) group_left slo:error_budget:ratio{sloth_id="KINTO-kinto-requests-availability", sloth_service="KINTO", sloth_slo="kinto-requests-availability"} labels: owner: KINTO Technologies Corporation repo: slo-maintenance sloth_id: KINTO-kinto-requests-availability sloth_service: KINTO sloth_slo: kinto-requests-availability tier: "2" - record: slo:period_burn_rate:ratio expr: | slo:sli_error:ratio_rate30d{sloth_id="KINTO-kinto-requests-availability", sloth_service="KINTO", sloth_slo="kinto-requests-availability"} / on(sloth_id, sloth_slo, sloth_service) group_left slo:error_budget:ratio{sloth_id="KINTO-kinto-requests-availability", sloth_service="KINTO", sloth_slo="kinto-requests-availability"} labels: owner: KINTO Technologies Corporation repo: slo-maintenance sloth_id: KINTO-kinto-requests-availability sloth_service: KINTO sloth_slo: kinto-requests-availability tier: "2" - record: slo:period_error_budget_remaining:ratio expr: 1 - slo:period_burn_rate:ratio{sloth_id="KINTO-kinto-requests-availability", sloth_service="KINTO", sloth_slo="kinto-requests-availability"} labels: owner: KINTO Technologies Corporation repo: slo-maintenance sloth_id: KINTO-kinto-requests-availability sloth_service: KINTO sloth_slo: kinto-requests-availability tier: "2" - record: sloth_slo_info expr: vector(1) labels: owner: KINTO Technologies Corporation repo: slo-maintenance sloth_id: KINTO-kinto-requests-availability sloth_mode: cli-gen-prom sloth_objective: "99.5" sloth_service: KINTO sloth_slo: kinto-requests-availability sloth_spec: prometheus/v1 sloth_version: a9d9dc42fb66372fb1bd2c69ca354da4ace51b65 tier: "2" - name: sloth-slo-alerts-KINTO-kinto-requests-availability rules: - alert: KINTOHighErrorRate expr: | ( max(slo:sli_error:ratio_rate5m{sloth_id="KINTO-kinto-requests-availability", sloth_service="KINTO", sloth_slo="kinto-requests-availability"} > (14.4 * 0.005)) without (sloth_window) and max(slo:sli_error:ratio_rate1h{sloth_id="KINTO-kinto-requests-availability", sloth_service="KINTO", sloth_slo="kinto-requests-availability"} > (14.4 * 0.005)) without (sloth_window) ) or ( max(slo:sli_error:ratio_rate30m{sloth_id="KINTO-kinto-requests-availability", sloth_service="KINTO", sloth_slo="kinto-requests-availability"} > (6 * 0.005)) without (sloth_window) and max(slo:sli_error:ratio_rate6h{sloth_id="KINTO-kinto-requests-availability", sloth_service="KINTO", sloth_slo="kinto-requests-availability"} > (6 * 0.005)) without (sloth_window) ) labels: category: availability severity: critical sloth_severity: page annotations: summary: High error rate on 'KINTO SERVICE' requests responses title: (page) {{$labels.sloth_service}} {{$labels.sloth_slo}} SLO error budget burn rate is too fast. - alert: KINTOHighErrorRate expr: | ( max(slo:sli_error:ratio_rate2h{sloth_id="KINTO-kinto-requests-availability", sloth_service="KINTO", sloth_slo="kinto-requests-availability"} > (3 * 0.005)) without (sloth_window) and max(slo:sli_error:ratio_rate1d{sloth_id="KINTO-kinto-requests-availability", sloth_service="KINTO", sloth_slo="kinto-requests-availability"} > (3 * 0.005)) without (sloth_window) ) or ( max(slo:sli_error:ratio_rate6h{sloth_id="KINTO-kinto-requests-availability", sloth_service="KINTO", sloth_slo="kinto-requests-availability"} > (1 * 0.005)) without (sloth_window) and max(slo:sli_error:ratio_rate3d{sloth_id="KINTO-kinto-requests-availability", sloth_service="KINTO", sloth_slo="kinto-requests-availability"} > (1 * 0.005)) without (sloth_window) ) labels: category: availability severity: warning sloth_severity: ticket annotations: summary: High error rate on 'KINTO SERVICE' requests responses title: (ticket) {{$labels.sloth_service}} {{$labels.sloth_slo}} SLO error budget burn rate is too fast. ::: 今回は簡単な例を生成してみましたが、実際にはより複雑なSLI/SLO仕様を複数定義することになるでしょう。Slothを使用しない場合、生成されたような長いコードを直接管理する必要がありますが、Slothを活用すればその手間が大幅に軽減されます。 設定手順 私たちKINTOテクノロジーズでは、「Amazon Managed Service for Prometheus」を活用しています。そのため、AWSのマネージドコンソールを通じて生成ファイルのアップロードが可能です。 「Amazon Managed Service for Prometheus」の詳細な利用方法については公式ドキュメンテーションをご参照ください: https://docs.aws.amazon.com/ja_jp/prometheus/latest/userguide/AMP-rules-upload.html もしくは、AWS CLIをワークフローから実行することもできます。ここでは、GitHub Actionsを利用した例を示します。 name: SLO set up on: workflow_dispatch: jobs: setup-slo: name: Set up SLOs runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Set AWS Credentials to EnvParam(Common) uses: aws-actions/configure-aws-credentials@v2 with: aws-access-key-id: ${{ 利用するAWSのアクセスキー }} aws-secret-access-key: ${{ 利用するAWSのシークレットアクセスキー }} aws-region: ${{ 利用するAWSのリージョン }} ## 定義ファイルから設定ファイルを生成 - name: download and setup generator binary run: | ## 適宜、最新のリリース状況を確認してください。 wget https://github.com/slok/sloth/releases/download/vX.XX.X/sloth-linux-amd64 chmod +x sloth-linux-amd64 ./sloth-linux-amd64 validate -i ./services/kinto/source/slo_spec.yml ./sloth-linux-amd64 generate -i ./services/kinto/source/slo_spec.yml -o ./services/kinto/configuration.yml ## Prometheusに設定ファイルをアップロード - name: upload configuration file to APM run: | base64 ./services/kinto/configuration.yml > ./services/kinto/configuration_base_64.yml aws amp create-rule-groups-namespace \ --data file://./services/kinto/configuration_base_64.yml \ --name slo-rules \ --workspace-id ${{ 利用するAMPワークスペースのID }} \ --region ${{ 利用するAWSのリージョン }} 可視化 Prometheusへルールファイルのアップロードが完了したら、次に行うのはデータの可視化です。私たちは、Grafanaを使用しています。Slothには生成したルールを可視化するためのダッシュボードのテンプレートがGrafana Labsにあるため、これをインポートすることで可視化することができます。 https://sloth.dev/introduction/dashboards/ アラート設定の手順 「複数ウィンドウ、複数バーンレートのアラート」は、Prometheusから送信されます。アラートマネージャーの設定ファイルを作成し、それをPrometheusにアップロードします。 https://docs.aws.amazon.com/ja_jp/prometheus/latest/userguide/AMP-alertmanager-config.html :::message 「Amazon Managed Service for Prometheus」では、現時点ではAmazon SNSへの通知のみサポートされています(今後の改善に期待しています!)。そのため、事前にSNSトピックを作成し、そのトピックのARNを設定ファイル内で指定しています。 ::: 私たちKINTOテクノロジーズでは、CriticalアラートとWarningアラートのルーティングを分けるため、次のような設定ファイルを作成しています。送信されるSNSアトリビュートには、アラートの種類情報を含めています。 alertmanager_config: | # The root route on which each incoming alert enters. route: # A default receiver receiver: warning_alert routes: - receiver: critical_alert matchers: - severity="critical" - receiver: warning_alert matchers: - severity="warning" # Amazon Managed Service for Prometheus, # The only receiver currently supported is Amazon Simple Notification Service (Amazon SNS). # If you have other types of receivers listed in the configuration, it will be rejected. # Expect future revisions. https://docs.aws.amazon.com/ja_jp/prometheus/latest/userguide/AMP-alertmanager-config.html receivers: - name: critical_alert sns_configs: - topic_arn: arn:aws:sns:{AWS region}:{AWS account}:prometheus-alertmanager sigv4: region: {AWS region} attributes: severity: critical slack_api_url: '<your slack api url>' slack_channel: '#<your channel name>' - name: warning_alert sns_configs: - topic_arn: arn:aws:sns:{AWS region}:{AWS account}:prometheus-alertmanager sigv4: region: {AWS region} attributes: severity: warning slack_api_url: '<your slack api url>' slack_channel: '#<your channel name>' また、SNSトピックはAWS Lambdaで購読しています。Lambdaでは、トリガーとなったアトリビュートを利用し、Slackの通知先チャネルを動的に変更しています。実際には、Criticalアラートが発された場合にPagerDutyのAPIを叩くようにするなど、もっとカスタマイズします。 # # this script based on https://aws.amazon.com/jp/premiumsupport/knowledge-center/sns-lambda-webhooks-chime-slack-teams/ # import urllib3 import json http = urllib3.PoolManager() def lambda_handler(event, context): print({"severity": event["Records"][0]["Sns"]["MessageAttributes"]["severity"]["Value"]}) url = event["Records"][0]["Sns"]["MessageAttributes"]["slack_api_url"]["Value"] msg = { "channel": event["Records"][0]["Sns"]["MessageAttributes"]["slack_channel"]["Value"], "username": "PROMETHEUS_ALERTMANAGER", "text": event["Records"][0]["Sns"]["Message"], "icon_emoji": "", } encoded_msg = json.dumps(msg).encode("utf-8") resp = http.request("POST", url, body=encoded_msg) print( { "message": event["Records"][0]["Sns"]["Message"], "status_code": resp.status, "response": resp.data, } ) 工夫した点 SLOs as Code Slothを活用すれば、SLI/SLO仕様をYAMLファイル形式でコード化できます。これはコードなので、Gitなどを用いてバージョン管理することが可能です。また、GitHubなどのホスティングツールを利用することで、レビューしやすくなります。SLI/SLO仕様がPrometheusで対応可能なもの(PromQLで表現可能なもの)であれば、アプリケーションだけでなく、ロードバランサーや外形監視サービスなどのメトリクス監視にも適用できます。そのため、Slothの応用範囲は広いと言えます。 私たちKINTOテクノロジーズのSREチームでは、YAML形式のSLI/SLO仕様を一つのGitHubリポジトリに集約しています。SREチームはリポジトリにテンプレートを設け、開発チームはそのテンプレートに基づいてSLI/SLO仕様を定義し、それをコミットしプルリクエストを作成します。そしてSREチームはそのプルリクエストをレビューします。この流れで、SLI/SLO仕様の理解と監視への反映作業をスムーズに行うことができます。結果として管理コストが削減でき、KINTOテクノロジーズの開発組織全体であらゆるプロダクトのSLOが参照しやすくなります。 依存関係のサービスレベルは、自サービスのサービスレベルに大きく影響を与えます。KINTOテクノロジーズで開発されているサービスは、他のKINTOテクノロジーズ開発サービスとの依存関係にあるため、組織の垣根を越えてサービスレベルを共有することで、自サービスのサービスレベルを担保するのに役立ててもらいます。 LatencyのSLI 「遅さは新たなダウン」とも言われるため、5xx系エラーの有無だけでなく、レスポンスタイムについても監視が必要となります。 次の簡単なSLI/SLO仕様をSloth規格のYAMLファイルで表現してみます。 Category SLI SLO Latency アプリケーションで測定される成功したリクエストの内、actuator以外の全てのリクエストパスを統合して計測します。30日のリクエストのうち、3000ミリ秒以内にレスポンスを返す比率。 99% version: "prometheus/v1" service: "KINTO" labels: owner: "KINTO Technologies Corporation" repo: "slo-maintenance" tier: "2" slos: ... # We allow failing (less than 3000ms) and (5xx and 429) 990 request every 1000 requests (99%). - name: "kinto-requests-latency-99percent-3000ms" objective: 99 description: "Common SLO based on latency for HTTP request responses." sli: raw: # Get the average satisfaction ratio and rest 1 (max good) to get the error ratio. error_ratio_query: | 1 - ( sum(rate(http_server_requests_seconds_bucket{le="3",application="kinto",status!~"(5..|429)",uri!~".*actuator.*"}[{{.window}}])) / sum(rate(http_server_requests_seconds_count{application="kinto",status!~"(5..|429)",uri!~".*actuator.*"}[{{.window}}])) ) alerting: name: KINTOHighErrorRate labels: category: "latency" annotations: summary: "High error rate on 'kinto service' requests responses" page_alert: labels: severity: "critical" ticket_alert: labels: severity: "warning" その際、ヒストグラムでデータを取得するため、application.ymlに次の設定を追加します。 management: ... metrics: tags: application: ${spring.application.name} distribution: percentiles-histogram: http: server: requests: true slo: http: server: requests: 100ms, 500ms, 3000ms management.metrics.distribution 以下の設定を追加することにより、summary型の percentiles ではなく、histogram型の percentiles-histogram でメトリクスを取得するように設定しています。この理由として、 percentiles は特定のパーセンタイルのレスポンスタイムをタスク単位で直近1分間のみ集計するので複数タスクでの集計ができず、30日間といった任意の範囲での集計もできないためです。これに対し、 percentiles-histogram は閾値以内のレスポンスタイムだったリクエスト件数を値として保持するため、PromQLを用いて複数タスクを横断した任意の範囲での集計が可能です。 そうすることで、リクエスト件数と総件数との比率を用いて、LatencyのSLI仕様を表現しています。 議論 設定可能なSLOは94%以上推奨 サイトリライアビリティワークブックでは、エラーバジェットの消費率を検知したい場合に推奨されるウィンドウとバーンレートの閾値が定められています。 https://sre.google/workbook/alerting-on-slos/#6-multiwindow-multi-burn-rate-alerts Slothは、デフォルトでサイトリライアビリティワークブックに記載の複数のバーンレートに対応しています。よって、アラートが発生する最大バーンレートの閾値は14.4となります。 その場合、例えば、SLOが93%の時のエラーバジェットは7%になります。ここで試しに、バーンレートが14.4のときのエラーレートを計算してみると、14.4 * 7 = 100.8 になります。基本的にエラーレートは、「エラーリクエスト/全てのリクエスト」によって算出されるため、エラーレートが100を超えることはありません。従って、SLOを93%と設定したときにバーンレートが14.4を超える事態を報告するアラートが発砲される可能性は0となります。 そのため、設定するSLOは94%以上を推奨しています。 おわりに ここまで、私たちKINTOテクノロジーズのSREチームの取り組みについて紹介してきました。いかがでしたでしょうか。現状、組織全体として、非常に厳格なサービスレベル管理が求められているわけではありませんが、今回紹介した手法を用いて、手軽に有益なアラートを試せることに満足しています。 Platform Groupでは一緒に働ける仲間を募集しています。少しでも興味を持ったり話を聞いてみたいと思った方は、お気軽にご連絡ください! @ card
アバター
こんにちは KINTOテクノロジーズの小山です。モバイルアプリの開発・運用に携わっています。担当はiOSです。 前回はiOS開発に特化した記事( Combineを使ってMVVMを実現した話 )を書かせてもらいましたが、今回はその開発現場で取り入れているアジャイル開発の内容です。 以前から私の携わっているモバイルアプリ開発ではアジャイル開発を取り入れており、スクラムを使って運用しています。今回、スクラムマスターをやってみないかとの話をいただきましたので、その経過を紹介したいと思います。 ※この記事は「アジャイル」をテーマにした一連の連載記事となります。 私たちは「組織としてアジャイルな状態」になっていくために、 さまざまな課題や困難に立ち向かってきました。 時には失敗もありましたが、それでも着実に成長を続けています。 この連載記事では、そんな私たちの実際の取り組みをご紹介していきたいと思います。 なぜスクラムマスター研修を受けたのか 以前からスクラムを使った開発には携わっていましたが、いずれも開発者の立場での参画でした。スクラムマスターとしての立ち回りはやったことがなかったため不安に感じていたところ、スクラムマスター研修なるものが存在するとの情報を得て受講に至りました。 今回は株式会社アトラクタさんが主催している認定スクラムマスター研修というものを受講しました。 それまでの開発チーム 私の所属している開発チームについて紹介したいと思います。 体制・役割 プロダクトオーナー チームリーダー バックエンド開発チーム フロントエンド開発チーム iOS開発チーム ←今ここ Android開発チーム QA デザイナー モバイルアプリの開発現場ではこのようにフロントエンドの開発チームが2チームに分かれることがままあります。 今回私がスクラムマスターを実施するということで、以下のようになる想定でした。 プロダクトオーナー バックエンド開発チーム スクラムマスター(チームリーダー) フロントエンド開発チーム スクラムマスター ←ここ半分 iOS開発チーム ←ここ半分 Android開発チーム QA デザイナー 元々チームリーダーはバックエンド開発寄りの立場にいたため、そちらのスクラムマスターを担当し、フロントエンド側を私が担当する想定でした。 …今思えば奇妙な構成を考えていたと感じますが、当時の私は気づけず、気づいていたとしても自信を持って意見できる状態ではありませんでした。 既存のチーム開発における課題 もちろんそれまでもアジャイル開発を進めていたため、スクラムを利用する上での疑問点や課題が多くありました。 開発メンバーが10人を超えてしまっているがどうすれば良いか 各種スプリントイベントをバックエンドとフロントエンドで分けて実施しているが問題ないのか スクラムへの理解度がメンバーによってばらつきがある 等々… これらが解決できればと思い研修に臨みました。 いざ、受講! 研修概要 スクラムマスター研修は3日間のカリキュラムとなっており、毎日13時〜18時の間オンラインで実施されました。その後任意のタイミングで試験を受けることができ、合格するとScrum Allianceが発行するCertified ScrumMaster(認定スクラムマスター)の資格を取得することができます。 3日間の内訳はざっくり以下の通りでした。 1日目:アジャイルとスクラムについての座学 2日目:スクラムの実践 3日目:スクラムマスターの座学と実践 アジャイルやスクラムについては書籍やWebなどから学ぶことができますが、それらを遥かに凌ぐわかりやすさで座学が進められました。忘れかけていた原則やマインドを学び直した上で、疑問点は常に講師の方に聞くことができ、実践形式で知識を定着させることができました。とても有意義な時間でした。 研修を受けて変わったこと とにかく、 自信がついた これに尽きます。 スクラムマスターをやる上で絶対に必要なのがスクラムについて指導をすることです。 それまでの学びでは「こういうことが本には書いてあるけど実際こうだしな」と自分でも疑問を解消しきれずにいました。原理原則は知っていても自信を持ってチームメンバーに広めることができずにいました。 研修を受けることで少なくとも自分が思っている疑問点は全て解消でき、スクラムの書籍も出しているような著名な講師の方に教えてもらったという絶対的な安心感が自信に繋がりました。また認定スクラムマスターの資格も手に入れることができたためこれも自信の裏付けとなりました。 受講後、やってみた プロダクトオーナーの理解を得る 私のチームでは研修の中でアンチパターンと呼ばれる手法をいくつも取っていることが判明しました。そこでまずプロダクトオーナーにアンチパターンがあること、なぜアンチパターンなのか、を説明しました。 一気に全てを変えることは難しいのでわかりやすいところから変えていこうとなり、私のチームでは「バックログの運用方法」から変えることを検討していくことになりました。 チームメンバーに研修の展開 アジャイル開発ではチーム全体で同じ意識を持つ必要があります。これがアジャイルを進める上でのとても重要なことだと私は思います。「バックログの運用方法」から変えるにしても、まずは意識を統一させることが必要でした。 そのために、受講後の週で内部展開用の資料を新たに作成し集合研修を行いました。まさに自信がついたからこそできた行動でした。 チームメンバーからは「アンチパターンを実施してしまっていたことがわかった」という声や、「研修受講後すぐに展開してくれるのが嬉しい!」といった反応をいただけました。正直なところ資料の作成と研修展開はかなり大変でしたが、やってよかったです。 改善内容の実践 「バックログの運用方法」の改善にあたり、現状の課題点とあるべき姿を説明し、次のバックログリファインメントから取り入れていきました。ここまでを研修受講後から1週間というスピードで実施しました。研修受講したてのホットな状態でここまでやり切れたのは、理解のあるプロダクトオーナーと開発メンバーのおかげでした。 体制はどうなった? 研修内容のチームへのフィードバックは滞りなく完了したのですが、ここで問題が発生してしまいました。 なんとチームリーダーが急遽育休に入るということでした。 当社は育休制度(もちろん男性も)がありますし、育児を応援したい気持ちはチーム全体としてありましたので、気持ちよく送り出すことにはなったのですが、 どうやらこのビッグ(メンバー数が)プロジェクトを私がスクラムマスターとしてサポートする必要 がありそうです。ひええ。 ということで再構成された体制が以下の通りです。 プロダクトオーナー スクラムマスター ←ここ半分 バックエンド開発チーム フロントエンド開発チーム iOS開発チーム ←ここ半分 Android開発チーム QA デザイナー 前述の …今思えば奇妙な構成を考えていたと感じますが、 の記載については、そもそもチーム内にスクラムマスターは1名で十分なためです。主にバックエンドをフォローするスクラムマスター、主にフロントエンドをフォローするスクラムマスター、といった分け方は本来の役割を期待されているようには見えません。名ばかりのスクラムマスターで、チームリーダーのような役割になってしまいそうな懸念があります。 また、元々実施していたチーム分け(バックエンド/フロントエンド)ではどうしてもチーム間でのやり取りが発生し、結局ブリッジとなる役割のメンバーが必要になってくる課題がありました。かつそのブリッジメンバーの負担が非常に大きくなってしまう傾向があったので、このタイミングで1チームにまとめられたことは今後のこと(後述)を考えるとむしろ好手であったと私は考えています。 これからやっていきたいこと 当面の目標としては、上記の改善を皮切りにどんどん開発チームをアップデートしていくことです。上手にスクラムが回るようになってきたら、今度はフィーチャーチームに分割するといったことにもトライをしていきたいと考えています。こうすることで当初考えていたチームの課題を全て解決することができます。 また長期的には社内にいるスクラムマスターと協力して、スクラムを使ったアジャイル開発を広めていけたらと考えています。うまく走り出すところまでサポートしたら、またさらに別のチームをサポートし、そうしてスクラムがもっと広まればより良いプロダクトの開発に貢献できるはずです。 終わりに 以上、スクラムマスター研修の体験記でした。とにかくメリットだらけですので、もし読んでいる方の中にスクラムがなかなかうまくいかないと悩んでいる方がいれば、受講を検討してみてはいかがでしょうか。受講料が少々お高めなので個人では受けづらいですが、会社に費用を負担してもらうために会社・上長を説得できる要素は十二分にあると思います。 個人的にスクラムを使ったアジャイル開発の最大のポイントは「チームごとにプラクティスが違う」というところだと思います。他社のプラクティスが必ずしも自社に適用できるとは限りません。いろいろなやり方を模索していくのは難しいけれど実は一番楽しいポイントかもしれませんね。
アバター
はじめに KINTOテクノロジーズでモビリティマーケットの開発・運用を担当しているリナです! 普段はフロントエンドエンジニアとして、Next.jsを用いて実装しています。 またテックブログの運営チームとして、記事のリリース管理なども行っています。 最初の記事 をリリースしてから早いもので、1年が経ちました🎉 今回はテックブログ公開1周年を記念して、テックブログ「ローンチ後の開発」にまつわるお話を記事にしました。 なお、ローンチまでの開発は「 Next.jsでテックブログを作った話 」という記事で紹介しています。 あわせてご覧ください🙌 インナーソースを目指して KINTOテクノロジーズのテックブログをローンチしてから現在に至るまで、デザインや機能開発は「インナーソース」で進めてきました。 インナーソースとは インナーソースの世界最大のコミュニティであるInnerSource Commonsによると以下のように定義されています。 企業内のソフトウェア開発にオープンソースの実践と原則を適用したものです。 参考: InnerSource Commons つまりインナーソースとは、オープンソースのようにソースコードを開発者が自由に利用・改修できる開発スタイルを社内で実践することを指しています。 実際の取り組み KINTOテクノロジーズでは、テックブログのリポジトリを社内の全エンジニアに公開し、新規機能の開発や機能改修および開発された機能に対するレビューができるようにしています。 また、開発予定の機能はすべてGitHub Issuesに列挙しており、「リソースに空きがなく、自分は開発できないけど、誰かにやってほしい・・・!」といった改善要望もGitHub Issuesに書いてもらっています。 GitHub Issuesの内容をもとに、有志のメンバーが機能修正してPRを出したり、レビューをしたり、すぐに対応したい事項はテックブログ運営チームで対処することもあります。 なぜインナーソースか? テックブログをインナソースにした理由は、主に2つあります。 新しい技術の実験場所として使ってほしい 組織を超えたコラボレーションを促進したい 新しい技術の実験場所として使ってほしい まず1つ目は、エンジニアが新しい技術や自身で学んでいる技術を検証する場として使ってもらいたいと考えたためです。これは、テックブログ運営チームのリーダーである 中西さん が繰返し言っていることでもあります。 KINTOテクノロジーズのテックブログは、外部のブログサービスを利用することもできましたが、あえて 自前 でテックブログを創っています。 ときには、自己学習によって習得したプログラミング言語やフレームワークを検証する場として、各プロジェクト・プロダクトに要件などの都合で導入できなかった仕組みを実行する場として、またはABテストの場として・・・など、テックブログを自前で創ることで「エンジニアが新しい技術にチャレンジできる場づくり」に努めました。 また最終的には、テックブログの開発でチャレンジ・実験して得られた知見を各プロダクト・プロジェクトに還元することを目指しています。 組織を超えたコミュニケーションを促進したい そして2つ目は、組織を超えたコミュニケーションをより強化したいと考えたためです。 組織の構造 上、部門ごとの縦のコミュニケーションは盛んに行われていますが、部門を超えた横のつながりはもっと強化できると考えています。 他部門のメンバーとコミュニケーションをとることで、新たな発見や知見が得られるかもしれません。 もしくは、新しいアイディアが生まれたり、気の合う自己研鑽仲間ができたりするかもしれません。 組織を超えたコミュニケーションが今よりもっと盛んになることで、エンジニアの成長および非エンジニアがエンジニアリングを楽しむきっかけに繋がることが理想です。 上記の2つの理由から「インナーソース」によって、エンジニアの学びを推進し、非エンジニアもエンジニアリングを楽しめる文化を創っていくことができると考えています。 これまでに追加した機能 インナーソースによってこれまでに開発された機能のうち、特に目立つ機能を5つご紹介します。 これらの機能は、異なる部門の開発者とレビュアーで構成された有志のチームで実装されています。 ① RSS ② SNSシェアボタン ③ 最新/関連記事 ④ 目次 ⑤ ページネーション おわりに 今回はテックブログのローンチ後から現在に至る「インナーソース」の取り組みをご紹介しました。 今後は、テックブログの全体的なデザイン修正やカテゴリ機能のリリースを予定しております! 毎週の記事リリースだけでなく、より見やすいテックブログになるよう機能面も充実していきたいと思っているため、今後もご期待ください🙌 またこの場を借りて、テックブログ読者のみなさま、そして日頃からテックブログの執筆・開発・運用にご協力くださっているみなさまにお礼を申し上げます。 Thank you!!✨
アバター
Hello, My name is Ashi Singh. I joined KINTO Technologies in Feb 2022, and am part of the Global Development Group. I am an application developer and currently working as an engineer on the back office system being developed by Global Development G. Overview My team is in charge of developing and maintaining microservices as a base package for use of other global teams. Authentication and authorization are the main features that we want to include in this base package. We want an authentication solution that does not depend much on cloud technology so we decided to investigate Keycloak as a possible choice. What is Keycloak? Keycloak is an open-source identity access and management (IAM) solution that adds authentication to applications and secure services with minimum effort. Keycloak provides user federation, strong authentication, user management and more. Keycloak supports mainly three types of protocols, which include OpenID Connect, OAuth 2.0, and SAML 2.0. It supports both Single Sign-On and Single Sign-Out fully. It is fast and flexible and has the capability to manage the accounts of users seamlessly and maintain data as well as sessions. Keycloak Official Page Keycloak was interesting to us because in the future we wanted to use it with different cloud architectures and we did not want to restrict ourselves to a single one. Problem In order to start our investigation of Keycloak, the first step is to test it out on our local environment. In our team, we have equal number of developers using Windows and MacOS. (KINTO Technologies lets us use whichever OS we are most comfortable with!) During this initial set-up, we found that Windows users as well as MacOS with Intel chip users have no problem with the Keycloak docker image. However, those with newer MacOS machines - mainly the M1 chip - ran into an error whenever you tried to start it up. Settings used before errors are given below : Keycloak Docker Image Version : jboss/keycloak:16.1.1 [This image was used because image jboss/keycloak:16.1.1 was the latest one used in the project . Also easy to do configuration with that] My Local PC Version : MacBook Air (M1, 2020) Apple M1 [macOS Monterey] Solution As when I was working with M1 machine in the team, I got assigned to the task. During my deep dive into the problem, I found that the JBoss image that Keycloak is using is not compatible with the M1 chip. The one solution I found was changing the docker image in the Mac M1 machine. So in Mac M1 we are using wizzn/keycloak:14 as the Keycloak docker image instead of the usual jboss/keycloak:16.1.1 image. Changes in the configuration files are needed as well in order to make it work. Changes needed docker-compose.yml The first step to run Keycloak on local is to change the docker image to wizzn/keycloak:14. Change the order in the volumes section like this: volumes: - ./custom-scripts/:/opt/jboss/startup-scripts/ - ./import/backoffice-realm.json:/tmp/backoffice-realm.json.orig #move to after startup-scripts - ./keycloak/themes/your-default-theme:/opt/jboss/keycloak/themes/your-default-theme - ./keycloak/configuration/mysql/change-database.cli:/opt/jboss/tools/cli/databases/mysql/change-database.cli #move to the last All the other scripts that are required to be added in the volume section can be added in the middle of ./import/backoffice-realm.json and ./keycloak/configuration/mysql/change-database.cli change-database.cli Then we remove the explicitly defined database settings that are already defined in database (change-database.cli) file. In this file, we make the changes as shown in green Changes in the Dockerfile In the Dockerfile change the docker image as wizzn/keycloak:14 Run the Keycloak Run the Keycloak using docker compose up -d Keycloak: Good or Bad? After this initial hiccup, we manage to run Keycloak efficiently on both Windows and MacOS systems. We were able to confirm Keycloak features like out-of-the-box user and role management, MFA authentication, session management, among others. Using Keycloak would save us time compared to us developing those features by ourselves. There is also an option to link various social networks like facebook, twitter, etc., so that you can easily implement social login if needed. But still, there are some areas that need to be addressed. In Keycloak the customization is quite difficult and it needs more time for the developers to do the changes. Keycloak is still an open-source project so you do not have any guarantee provided by its developer about the road map, and things like bugfixes are taken care of via GitHub issues with no hard deadlines for response time. But as said if there is a solution then it will have some cons, but the ability to easily manage and run this solution outweighs other criteria as it can be used as a major solution going forward.
アバター
はじめに こんにちは!KINTOテクノロジーズの開発支援部に所属する「 きんちゃん 」です。 普段はコーポレートエンジニアとして「全社利用するITシステムの維持管理」を行っています。 今回、私が社内で実施している「アジャイルな活動を組織横断的に推進する」取り組みについて、ご紹介します。「自身に熱い想いがあり、ゼロから新しい取り組みを推進していきたい!」と考えている方のご参考になれば幸いです。 ※この記事は「アジャイル」をテーマにした一連の連載記事となります。 私たちは「組織としてアジャイルな状態」になっていくために、 さまざまな課題や困難に立ち向かってきました。 時には失敗もありましたが、それでも着実に成長を続けています。 この連載記事では、そんな私たちの実際の取り組みをご紹介していきたいと思います。 背景 私は2023年1月にKINTOテクノロジーズへ入社しました。 これまで、社内外を問わず様々な「アジャイルについての活動」に関わってきた経験から、ここKINTOテクノロジーズにおいても、入社当初より「社内にいるアジャイル有識者の方々と組織横断的に交流していきたい」という想いを持っていました。 これまでの経験としては 社内でスクラムマスターやスクラムコーチとしてソフトウェア開発チームへ関与 管理部門において、アジャイルを軸にした業務改善の取り組みを推進 社内の実践者と定期的に活動内容を共有する実践コミュニティ活動の推進 社外のアジャイルコミュニティやカンファレンスへの定期的な参加 等になります。 そんな想いを持っていた私ですが、実際にコーポレートIT部門として業務を開始した時に持った印象としては「思った以上にプロダクト開発のチームが遠いな」という感覚でした。 「プロダクト開発部門とコーポレート部門」という「組織上の距離」ももちろんですが、「物理的な距離」も大きな要因としてありました。私が勤務するのは名古屋オフィスなのですが、プロダクト開発のエンジニアの多くは東京のオフィスに在籍していた、というのがその理由です。 この距離感がなかなかのハードルとなり、元来コミュ障である自分の性格も相まって、入社後しばらくは他部署の方々とも積極的に関われず、「(アジャイルなマインドを心に秘めた)真面目なコーポレートエンジニア」として過ごす日々が続いていました。 きっかけ 私の所属する開発支援部では、定期的に上長との「1on1」が実施されています。 入社し2ヶ月が経った頃、当時のマネージャー(現在の部長)に「社内のアジャイル有識者と交流したい」旨を相談しました。 その際に、色々な方の名前を挙げてもらったのですが、そこで一番マッチしたのが「少し前にスクラムマスター研修を受けた木下さん」でした。木下さんはエンジニアでもあり、社内のテックブログチームにも所属し、「 認定スクラムマスターになった記事 」も書かれている方でした。私もKINTOテクノロジーズに入社する前に木下さんの記事を読んでいたため、「ぜひ木下さんとコミュニケーションを取りたい」と要望を出し、結果としてテックブログチームと交流の機会をいただく事となりました。 テックブログチームと出会って 初めてテックブログチームの方々と交流した時に感じたのは「何だか個性豊かで楽しそうな人たちだな」といった感想です。皆さんそれぞれ異なる本業を持ちながら「テックブログ」という一つのプロダクトを育てる活動に積極的に関わり、結果として「組織内の色々な方々とコミュニケーションを取り、横断的にひとつのうねりを作り上げる」事ができている。僕としては「理想的な組織内コミュニティのひとつの形」が、そこにありました。 また、このテックブログチームは「 中西さんという、一人の熱い想いを持った人の行動 」から始まった活動であるという事。私は何回かテックブログチームの方々と交流を続ける中で、「一緒になって良い文化を社内に広めていきたい」と思うようになりました。 そして、ひとつの転機がやって来ました。 テックブログチームの方々から「木下さんの記事が安定したPVを得られており、アジャイルについての記事は人気が出るのではないか?」という話が挙がった事と、もともと私が「アジャイルについての熱量が大きい人間」であった事が掛け合わさり、「テックブログでアジャイルについての連載記事を作る」という企画が立ち上がったのです。 さあ、何からはじめよう? 「アジャイル連載」の企画はとても良さそうに見えましたが、いざ考えてみると「KINTOテクノロジーズにおけるアジャイルな活動」についてまったく私の知見がない状況であったため、コンテンツがほぼ何もない状態です。 そのため、まずは「有識者を紹介」してもらうことから始めました。 テックブログチームの方々の人脈を軸に 過去、社内で認定スクラムマスターを取得した方 前職での経験を元に、KINTOテクノロジーズでこれからScrumに取り組もうとしている方 これから認定スクラムマスター研修を受講しようとしている方 と交流する場をセッティングしていただき、「KINTOテクノロジーズのアジャイル」について色々とインタビューする流れが出来ていきました。 最初は「名古屋と東京」という物理的な距離に対して、勝手に心の距離を感じてしまっていた私ですが、この頃には「特に距離は関係なく、ちょっと離れたところにいる仲間」という感覚で皆さんに接する事ができるようになっていました。 最初の一歩を踏み出すまでは足が重いものですが、ほんのちょっと一歩を踏み出すきっかけさえあれば、後は自然と身体がついていってくれるものですね、という気付きでした。 次回予告 今回の内容はここまでとなります。 次回は、ここから更に話が進んだ後の 社内の有識者と交流し、皆さんのアジャイルに対する想いを直接感じられたこと 実際のスクラムイベントに参加させてもらい、熱い現場を間近に見られたこと 「とは言え、うまく行くことばかりではないよね」という「アジャイルあるある」の話ができたこと インタビューをきっかけに「アジャイルの有識者が集まる社内交流の場」を始められたこと といった内容をご紹介する予定です。 そして、この「アジャイル連載」は「KINTOテクノロジーズのアジャイル」が中心となりますので、「社内で活躍されている有識者」や「そのチームの方々」、また「ソフトウェア開発以外の場面で目にしたアジャイルな活動」についてもどんどん順を追ってご紹介できればと思います。 楽しみにお待ちください!
アバター
はじめに こんにちは!KINTOテクノロジーズの開発支援部に所属する「 きんちゃん 」です。 突然ですが、8/3(木)に「 KINTOテクノロジーズ MeetUp!~情シスによる情シスのための事例シェア4選~ 」というタイトルで「コーポレートIT領域に特化した、事例発表+座談会形式の勉強会」を開催します。 今回、この勉強会を開催するまでに至るあれこれを、ご紹介します。 「自社でゼロから勉強会を立ち上げ、推進していきたい!」と考えている方のご参考になれば幸いです。 「勉強会」ってどんなイメージ? 突然ですが、「勉強会」というと皆さんどのようなイメージを持たれるでしょうか? 業界・領域の有識者の話を聴き、新しい知識を身に付ける 同じテーマに興味を持つ人たちが集まり、相互に研鑽し合う チームのスキルアップを目的に、半ば強制的に業務として開催・参加させられる (コロナ禍では下火だったが)懇親会での会話や食事を目的に参加する このように、様々な目的やスタイルを持った勉強会があると思っています。 私自身の「勉強会観」をお伝えすると、過去に私が運営にも関わっていた「 DevLOVE 」という勉強会コミュニティのコンセプトに立ち戻るのですが、 自分たちの現場を前進させるために、現場の中と外で知識や経験を循環させる「学びの場」 という考えを持っています。 もともと「勉強会」や「コミュニティ」に一定の価値を持っていた私であったため、KINTOテクノロジーズに入社する際にも「できれば社内で交流・研鑽する場に関わっていきたい」と考えていました。 ※この考えに基づく行動ついては、後日公開予定の「アジャイル連載」で具体的にお伝えする予定です。 よろしい、ならば勉強会だ 私がKINTOテクノロジーズに入社した時、既に社内には「勉強会を定期的に開催する文化」があり、多くの「学びの場」が存在していました。例として以下のようなものです。 定期的に開催される「任意参加の全社エンジニア向け勉強会」 自社の事業やビジネスについて学ぶ「事業部門主催のビジネス勉強会」 各プロダクトチームで個別に開催される「個別勉強会・読書会」 何よりも素晴らしいと感じたのは、「社内外の勉強会や読書会への参加」を「業務として取り組んでも良い文化」や、「学びに必要な書籍を、会社負担で購入可能な制度」でした。 KINTOテクノロジーズという会社自体が、「社員の学び」を積極的に支援している姿勢が感じ取れます。 そのような文化の中で、私も「自チーム向けの勉強会や輪読会」を小さく実施し、仲間と共に学習する日々を過ごしていました。心のどこかで「もっとこの文化を多くの人に知ってもらいたい」と思いながら…。 そんなある日、所属チームの中で「コーポレートエンジニア採用への取り組み」についての議論が起きました。色々な施策の話が議論される中、私の頭の中で「どうすれば、我々の強みや良い文化を多くの方々に知ってもらえるか?」という課題と、前述の「学びに対する自社の文化を多くの人に知ってもらいたい」という想いが繋がりました。 その結果「社外の人を交えた勉強会をやってみませんか?」という提案が出てくる事となったのです。 一人じゃ無理だ!みんな助けて! その後、勢いで「コーポレートIT勉強会の企画書」を作成し、チームリーダー陣に正式な案として提案しました。各リーダーからは「いいね!」という反応を得られたので、前向きに検討する事となりました。 いざ「やりましょう」となったところまでは良いのですが、考えるべき事はたくさんあります。 何をテーマに開催するのか?誰が話すのか? 場所は?時期は?タイムテーブルは? さすがにこれらを一人で進めるのは限界があるため、翌週のチームメンバーが全員集まる場で「 勉強会を開催します!事務局メンバーと登壇者を募集します! 」と同志を募りました。 結果として、有志や推薦を含め、全員で6名のチームが結成されたのです! さっそくSlack上に専用のチャンネルを作成し、関係者を集めたコミュニケーションを開始します。さしあたっては確実にゴール達成への目線を合わせるための事務局キックオフの実施です。最初の企画構想時点で話に加わっていないメンバーもいるため、主に そもそもの主旨は何で、どのような開催概要であるか? 到達すべきゴール状態は何か? ゴール到達に向けた、「検討済み事項/未検討事項」は何か? 未検討事項をどのようにして解決していくのか? といった、「目指すべきゴール」と「現状把握」を揃えるところから丁寧に実施し、その上で「次に目指すべき到達点」を定め、それぞれが準備を進めていく事としました。 俺たちだけじゃ無理!!もっとみんなを頼りたい! 次に事務局メンバーで実施したのは「未検討事項のディスカッション」です。大きく コンテンツ 開催方式と利用設備 開催日時・タイムスケジュール 集客・広報 効果測定 といった点について、より解像度を上げていく議論を実施します。 その結果、「自分たちだけでも進められるもの」と「他部署の協力を得たいもの」が見えてきました。それらを踏まえ、他部署の関係者も踏まえた会議体をセッティングする流れが作られました。 ※「勉強会の実施」が決まった段階で、我らが部長からヘルプが必要になりそうな他部署への声がけを進めてもらっていた事もあり、全体でのキックオフMTGも、スムーズに実施が可能となりました。 ありがたい事に、合同キックオフに参加してくださった方々はとても前向きな方が多く、我々の「やりたい」に対して「こういう事をやるのはどうか?」「こういうものは過去に前例があるので、すぐに対応が可能」といった、「ゴールにより近づくための意見」をたくさん出していただきました。 フワッとしたプロジェクト管理が必要だ! 「何をやるべきか?」の解像度が高まったところで、「具体的なアクションプラン」が並列でいくつも見えてきました。 さすがにこれらを「できる人が良い感じにやっていく」と「現在地の把握」つまり「プロジェクトの透明性」が下がる懸念があったので、一定の「見える化」と「進行管理」と「役割分担」を定める事にしました。 見える化 Jiraを利用し、「Epic>Task」の階層をもったタスクの見える化と優先度設定 進行管理 Epicの粒度で、週単位で「ここまでやる」ゴールを設定 週次で「達成度を確認」し、必要があれば軌道修正。問題無ければ次ゴールの設定 役割分担 Epic単位で担当者を決める 誰かの進行が滞ったり、週のゴール達成が難しそうな場合は、他メンバーがフォロー このような形で、イテレーティブかつインクリメンタルな事務局運営を実現しています。 【余談】事務局運営に見る「Agile Kata」の実践 余談ですが、今回のこのような仕事の進め方は「 Agile Kata 」に書かれた「改善のカタ」を参考にしています。「Agile Kata」には、以下のステップを繰り返す事で、日々の行動をアジャイルに実践する事が可能である、と書かれています。 方向性やチャレンジを理解する 現状を把握する 次のターゲット状態を設定する ターゲット状態に向かって実験する(これを反復的なアプローチで実行する) Scrumのような決まったフレームワークを利用せずとも、カタを理解する事で「アジャイルな状態」はいくらでも作る事が可能です。 そして現在 まさにこの記事を書いている現在も、毎日毎日「勉強会に必要な議論やアクション」が進められ、ゴールに近づいています。 テーマ毎に個別に同期型のWeb会議が実施され、Slack上では非同期で会話が進み(1日で50件を超える返信が付くようなスレッドもあり)、ガンガン前に進んでいる感じがあります。みんなすごい!! このような形で準備が進められ、いよいよ開催が見えてきた「 KINTOテクノロジーズ MeetUp!~情シスによる情シスのための事例シェア4選~ 」、皆さまぜひ楽しみにお待ちください!!
アバター
はじめに はじめまして。 KINTOテクノロジーズ 開発支援部所属で、グローバル開発部のアシスタントを担当している三浦です。 普段はオフィス回り、グローバル開発部のツール手配などを含めて、メンバーが仕事をしやすいような環境を整えるお手伝いや、問い合わせ等の対応をしています。 最近のマイブームは、推し活!1年限定で活動中の某バンドをあちこちへ追いかけ応援しています! さて、事務回りの細々とした作業が多いなか、だからこそ日々少しでも業務改善をしていくぞと考えています。本記事ではKINTOテクノロジーズで実践してきた業務改善について紹介します! これまでの「カイゼン」について トヨタグループであるKINTOテクノロジーズ社内では改善ではなく「カイゼン/Kaizen」というワードがよく使われています。その定義は以下です🔻 カイゼンとは、作業や業務の中にあるムダを排除し、より価値が高いものだけを行えるように、作業や業務のやり方を変える活動を行うことを指します。 ^1 入社してからこれまでに、以下のようなカイゼン活動を行ってきました。 [1] メーリングリストの棚卸しと管理方法を変更 [2] セキュリティカードの貸出管理台帳と承認ルートの変更 [3] テスト端末の管理 [4] 置き傘用の名札作成 ここからは、きっかけ・背景、実施した内容と効果についてです。 [1] メーリングリストの棚卸しと管理方法変更について 📧 入社1か月目の事、ミーティングのメンバー招集をする際に、メーリングリストの登録者が分からない…というのが始まりでした。 私自身、開発支援部に所属していますが、開発支援部では部内で使用するメーリングリストの登録者一覧があったのに、グローバル開発部にはない!! ならば同じように作ってみようと考えました。 対象のメーリングリスト自体がわからないので、メーリングリストを抽出することからはじめましたが、なんと使用中のメーリングリストが94個も!?ホントに使っているの??という疑問から、棚卸を実施することに。 まずは見様見真似、開発支援部で使っているメーリングリスト一覧のExcelをもとに 縦(Y):登録メンバー、横(X):メーリングリスト、登録者:●で表にしました。 メーリングリスト一覧(目隠しだらけで失礼します🤣) この表を各チームリーダーに確認してもらい、管理者の設置・用途の明記・登録メンバー確認という形で棚卸を実施しました。 メーリングリスト一覧は誰でも閲覧することが出来るように、クラウドストレージのBOXで共有し、内容変更依頼はJIRAチケット起票として、リストが陳腐化しないようにデータ更新の権限は私だけに付与しました。 一覧にしているので登録者や種類などの確認が容易になるとともに、グローバル開発部のメンバー全員がメーリングリストは自動更新ではない、という認識を持つことができたと思います。 また、全メーリングリストを見える化したことで、似たような用途で重複作成になっていないか確認できるという効果もありました。開発支援部に所属しながらグローバル開発部のサポートをしていたからこそできたヨコテン(横展開)でした。 [2] セキュリティカードの貸出管理台帳と承認ルートの変更 について 神保町オフィスに週2日以上出社する外部ベンダーの方には、オフィスのセキュリティカードをお渡しすることになっています。こちらも単純なことですが、これまで履歴が残らない仕様だったExcelを履歴管理できるように変更し、さらに、未使用のセキュリティカードがわかるように変更しました。 条件付き書式と関数を使って、未使用カードしか選択が出来ないようにしています。 これにより、使用者情報の誤削除もなく、棚卸もしやすくなりました。 自動で枚数や空きカード番号を見えるようにしました 承認ルート変更については、私が開発支援部の所属のため、これまではセキュリティカードの交付依頼を受けても、グローバル開発部のメンバーに代理で申請をしてもらわないと、正しい承認ルートで起票できませんでした。 この変わった習慣が全く実務に沿っていなかったため、「このおかしなフローは変えた方が良いのでは?」と考え、主管する部署へ相談しました。その後、二部署の兼務という役割・体系の整理を行い、私の申請の際には開発支援部とグローバル開発部の承認ルートを選択できるように変更しました。 これにより、以前まで代理で対応してもらっていた方のリソースや個別調整の必要がなくなりました ✨ [3] テスト端末の管理 について 📱 これまでシステム開発時に使用するスマートフォン等のテスト・検証端末は、Confluenceの表で管理していたのですが、誰が使用しているか所在が一目でわかりづらく、その表自体が更新されないまま、ある特定の端末などは個人管理となっているケースもありました。 そもそも、テスト端末の使用状況がわからないので、確認せずに追加購入しようとする未遂事件が発生したのですが、そのころ、会社で購入した書籍をJIRAで一括管理することになった事を知り、書籍管理と同じようなことが出来ないかと考えました。 ➡️ 書籍の管理方法をラクにした話 こちらもJIRA化するタイミングで棚卸を行ったのですが、紛失していないか、壊れてはいないか、本当に使っているのかがよく見えました。(実際にパスワード不明でロックかかったままなどもありました🔒) テスト端末は日常的に使用しているため、棚卸と同時進行で現物を確認し、パスワード設定や、端末自体の写真をJIRAに掲載することで、機種名だけではわかりにくかった点も改善されたと思います。 管理をJIRA化したことで、メンバー全員が貸出状況を一目で確認できるとともに、貸出期限を設定したので、使用状況を追えるようになりました。 貸出状況の可視化、チケット内には端末の詳細情報を記載しています また、貸出・返却時にコンフルの更新漏れや、都度Slackで連絡するなどの手間がなくなりました。 何より、担当者と使用端末の紐づけに加えて、返却期限を設けたので、メンバー全員に 「借りている」 という認識が強まったと感じられます。 期限が近づくとJIRA管理者に通知が来るように設定しましたが、このあたりは書籍管理をJIRA化されたリナさんにお助けいただきました。ご協力いただき本当にありがとうございました!!! [4] 置き傘用の名札作成 について☔ エントランスの傘立てに「置きっ放しの傘があるので片づけてほしい」という依頼から、他の傘立てを含めて確認し、数日置きっ放しの傘はオフィス内でアナウンスして処分しました。 そのアナウンス内で、貸出傘についてのコメントをもらったことをきっかけに、処分する傘の中でもきれいなものはオフィス貸出用としてリサイクルできるようにテプラを貼って、誰でも使用できる共用傘とすることにしました。 オフィス内の傘立ては、意外とビニール傘や無地の似たような傘が多く、今後も放置されてしまう傘は増えるのかな、きっと取り間違いも出るだろうな、と考えたとき、過去に自分の傘に養生テープで名前を書いて輪ゴムで名札を作っていたことを思い出しました。笑 自分一人の場合はそれだけでいいですが、できれば皆さんにも名札があれば良いかなと思いキーホルダを用意しました。 名前入りのキーホルダーで置き傘を確保します👍 この改善はまだまだ浸透していませんが、傘に限らず、冷蔵庫保管の私物につける名札としても使用できるので、どんどん利用して欲しいなと思っています。 カイゼンマインドはどこからくる? 私のカイゼンマインドの起源についてお話しします。 子供のころから想像することが好きでした。 通学の時「道が勝手に動いたらいいな ✨」「雨の日勝手にシールド張ってくれたらいいな✨」(ドラえもんの世界に近いですよね、、😅) のように、「いいな✨」の想像を昔からよくやっていました。カイゼンはこの延長にある気がします。 偉人達はこの発想から研究者や開発者に繋げていると思いますが、人並みの勉強をしてこなかった私の場合は、目の前の課題をなんとかクリアさせたいので「こうなったら楽なのに🤔」という発想になるのだと思います。 仕事に関しても 「楽に仕事をしたい」=「仕事は楽しく」 が根本にあります。 少しでも自分の仕事を楽できたらうれしいですよね。いずれその楽は当たり前になる日も来るのですが。。 「自分が楽になる」がきっかけですが、それにプラスして相手や使う人のことを考えて進めていく感じです。 常に自分がやっている仕事(特に定期的にやらないといけない業務)は「こうなったらいいな」を考えています。 「こうなったら楽なのに」を最終的に自分で実現するのは難しいかもしれないけど、 今の「楽になったこと」が業務上当たり前になって、引き継いだ人がそこからさらに楽にしてくれたらいいなというのが理想です。 自分がやった改善が最終形ではなく、自分の手を離れ、その後の状態がさらに進化してると嬉しく思います。なんかそういう想像はワクワクしますよね。 Next Kaizen~次に取り組みたい課題~ Excelを使用している定期業務をマクロ化等でもう少し簡単にしたいと考え、先日社内で公開されたSherpa ^2 やChatGPTを使って試行錯誤で開始しております!これからもカイゼンマインドを胸に、楽しく仕事に励みます ✨
アバター
Svelte Tips こんにちは(こんばんは)、Svelte不定期連載その5です。 過去の記事はこちら SvelteKit + Svelte を1年間くらい使ってみた知見など※SvelteKit メジャーリリース対応済み Svelteと他JSフレームワークとの比較 - Svelte不定期連載-01 Svelteでユニットテスト - Svelte不定期連載-02 SvelteでStorybookを使ってみる - Svelte不定期連載-03 SvelteでStorybookを使ってみる - Svelte不定期連載-04 なかなかに書きましたね…。 今回は実際に今までプロジェクトで使ってきて、 「ここ詰まった!」「??」 となった点をわかりやすく書いていこうの回です。 目次は以下の4つです。 SSGの際の設定 .page.tsと .server.tsの違い metaと使い方(プラグイン紹介) それぞれのライフサイクルのつかいどころ SSGの設定 SvelteKitではAdapterというモジュールを使用することで簡単にデプロイ先を設定できます。 デフォルトでは adapter-auto というSSR用のAdapterになっているため、adapter-staticというモジュールを導入する必要があります。 筆者は最初ここで躓いてしまい、autoって命名されているくらいだし、よしなにしてくれるんじゃないか・・・?とこねくりまわしていた記憶があります。 だがしかし、そうではありませんでした。 adapter-staticを導入してドキュメント上のコードを書くだけで、すぐさまに静的ホスティングに最適化されたビルドファイルが出来上がりました。 (ちゃんとドキュメントを読もう…。) Svelteの公式サイトは和訳PJもあるので日本語ドキュメントもありとても助かります :) // これがなければSSGとしてビルドできない import adapter from '@sveltejs/adapter-static'; /** @type {import('@sveltejs/kit').Config} */ const config = { // 割愛 kit: { adapter: adapter({ pages: 'build', assets: 'build', fallback: null, precompress: false, strict: true }) } }; export default config; 詳細: https://kit.svelte.jp/docs/adapter-static .page.tsと .server.tsの違い これもSvelteKit v1がリリースされ大きく変わった際に躓いた点です。破壊的な変更だったのでみなさま覚えているのではないでしょうか。 v1がリリースされてから、SvelteKitではページ内でデータfetchなどをする場合、以下の2ファイルを置くことがデフォルトになっています。 *.svelte ⇒ UIなどのファイル *.page.server.ts || *.page.ts ⇒ fetchなどデータを定義するファイル そしてデータを定義するファイルは page.tsと page.server.tsというものに分類がされます。 *.page.tsと *.page.server.tsの違いを最初わからず作ってしまい、 SSGにしたつもりが 遷移時にデータをAPIなどに取りに行く挙動 になっていました…。ナンダッテー。 *.page.tsの場合は クライアントサイド・サーバーサイド の両方で実行 *.page.server.tsの場合は サーバーサイド のみ実行 なのでSSGでJAMSTACKしたい場合は*page.server.tsが正解なのでした。 https://kit.svelte.jp/docs/load#universal-vs-server これもドキュメントを読めば…!といったところですね。ドキュメントは最高。 サーバーサイドのみで実行したい場合の正解例 export async function load({ params, fetch }) { const pagesReq = await fetch(`APIURL`); let data = await pagesReq.json(); return { data }; } meta管理方法 どのフレームワークも、もっといえば、どのウェブサイトでもそうですが、 meta情報をどのように管理するかという悩ましい問題があります。 フレームワークを知る前はpugとjsonとgulpもしくはwebpackの三種の神器で頑張っていたりしたのですが、 Svelteではこんな形で簡単に行なえました。 <script lang="ts"> import { siteTitle, siteDescription } from '$lib/config'; interface META { title: string; description?: string; } export let metadata: META; </script> <title>{`${metadata.title}|${siteTitle}`}</title> {#if metadata.description} <meta name="description" content={metadata.description} /> {:else} <meta name="description" content={siteDescription} /> {/if} <script lang="ts"> import Meta from '$lib/components/Meta.svelte'; let metadata = { title: 'title, title, title', description: 'description, description, description, description' }; </script> <Meta {metadata} /> こんな感じでmetaコンポーネントを作って読み込むことができます。 またわざわざ作らなくても、こんな素敵なプラグインもあります。 https://github.com/oekazuma/svelte-meta-tags 感謝…!!!!! それぞれのライフサイクルのつかいどころ 最後に避けては通れぬライフサイクル関数について Svelteには onMount , onDestroy , beforeUpdate , afterUpdate , tick と全部で5つのライフサイクル関数があります。 onMount その名の通りコンポーネントが初めてDOMにレンダリングされたあとに実行されます。 Vueのmountedとほぼ同義がタイミングです。 onDestroy これも名前の通りでコンポーネントが破棄されたタイミングで実行されます。 処理が不必要になったタイミングでコンポーネントを破棄することでメモリリークなどを防ぐことができます。 またサーバーサイドコンポーネントの場合、このライフサイクル関数のみが使えます。 beforeUpdate コンポーネントがDOMがレンダリングされる前のライフサイクル関数です。 また状態の変更を先に、反映したい場合などもbeforeUpdateを使う場合が多いです。 DOMレンダリング前のライフサイクル関数になるので、DOMにまつわる処理を書く場合は注意が必要です。 afterUpdate コンポーネントがDOMにレンダリングされ、データが反映されたあとに実行される関数です。 Svelte上では最後に実行されるライフサイクル関数ですね。 tick 状態が更新されたあと、その状態がDOMにレンダリングされるまでのタイミングを扱うことができます。 DOMの更新を待ってから何か処理をする、ということが可能になります。 ライフサイクル関数も他のフレームワークと比べ少ないので比較的わかりやすいのではないでしょうか。 以上で今回のSvelte Tipsを終わります。 最後に Software Design 2023年7月号にて Svelte特集「はじめよう Svelte」 を執筆致しましたのでさらに詳しく知りたい方は書籍で :) (SSGでJAMSTACKするチュートリアルも書いてあるのでぜひお試しください) https://twitter.com/gihyosd/status/1669533941483864072?s=20
アバター
はじめに こんにちは。KINTO Technologiesのグローバル開発部でフロントエンド開発をしているクリスです。以前は Storybook と Vue.js について執筆させていただきましたが、今日は技術と少し離れ、マネジメントについて話したいと思います。 実は私は去年の7月からフロントエンドチームのリーダーになり、もうすぐ1年が経ちます。初めてのリーダー経験で、たくさんの出来事がありましたが、次の1年に向けてよりよいリーダーになるために何をすればいいか考え、この1年間リーダーとしてやってきたことの振り返りをテキストに起こしたいと思い、本記事を執筆しました。 チームについて まず去年リーダーに就任した当時に遡ると、チームそのものが、部署の組織変更でフロントエンドに特化した部隊が欲しく作られたチームです。以前の記事でも少し触れましたが、多国籍なチームで、日本語がそこまで堪能ではないメンバーもいるので、チーム内では基本英語でコミュニケーションを取っています。 このチームの主な仕事はグローバル開発部が持つ各プロジェクトのフロントエンド開発を行うことですが、複数のプロダクトに対してメンバーの数も多くないので、一人が複数のプロダクトを担当するのは珍しくありません。また、保守期間になったプロダクトにおいてはずっとフロントエンドのタスクがあるわけでもないので、リファクタリングでコードを改善すると同時にローテーションでメンバーを他のプロダクトに回す時もあります。 チームリーダーとしての役割 私はチームリーダーになる前も、一人のメンバーとしてこのチームでフロントエンドの開発作業に専念し、デザインシステムの開発を行なっていましたが、リーダーになってからは主に以下の仕事に専念し、自ら開発するよりはメンバーに委ねることが多くなりました。 技術と実装方法の選定 一つ目の仕事はチーム全体の技術と実装方法を選定することです。フロントエンド開発においてはたくさんのフレームワークやライブラリーが選べます。実際、選択肢が多すぎて、何がいいか悩む方が多いと思いますが、私ももちろんその一人です。フレームワークの話だと、メンバーが慣れているか、機能面でプロダクトの要件を満たしているか、コミュニティーサポートが十分か、などの面で考慮しないといけないですが、なるべく話題になって、流行ってるフレームワークも使ってみたいという開発者のありがちの気持ちとどうバランスを取るかも重要です。 今グローバル開発部のフロントエンドチームは基本Vue.js/Nuxt.jsで統一していますが、かと言って今後もこのセットでやっていかないといけないというわけでもありません。チーム内には常に新しいことを試す雰囲気があるので、最近では社内の技術交流会で他のフロントエンドチームの影響を受けて SvelteKit やAstroなども試しながら評価しています。そして評価の結果を基に、何を利用するかを決めていきます。 PMとメンバーのコミュニケーションフォローとプロダクトのコードレビュー 二つ目の仕事はPdM/PjMとメンバーとのコミュニケーションが円滑に行えるようにすることです。フロントエンドチームは複数のプロダクトの開発タスクを行うことがあるため、それぞれのPdM/PjMとコミュニケーションを取って作業を進んでもらっています。仕様確認、提案やフィードバックなどをすることによって、プロダクト開発がスムーズに行えることを手伝っています。また、現行各プロダクトにアサインしているメンバーが一人の場合が多いので、リーダーとして品質と情報把握のために全プロダクトのレビューを行っています。 シニアマネージャーとメンバーの架け橋 三つ目はシニアマネージャーとメンバーの架け橋を担うことです。弊社では、マネージャー層になると、所属メンバーが10人以上増えることもあります。例えば私の場合ですと、自分の直上長は他のチームも管理していて、合計20人近くのメンバーを取りまとめています。そのため、多くの場合はリーダー経由でメンバーの管理を行います。 現在私がやっているのは、主に以下のことです: メンバーの能力や志向を踏まえて、それぞれの役割とミッションを設定すること(もちろん最終的にマネージャーにもすり合わせること) 決めたミッションを基づいて、定期的に1on1をセットアップし、フィードバックをしたり、彼らの相談や悩みを聞いたりして、必要に応じて現場の声をマネージャーにもフィードバックすること 動機づけとメンターリング 最後はもちろんメンバーの管理です。前述したように、1on1でのコミュニケーションはもちろん、それぞれの強みを発見し、なるべくそれを活かすというのも重要な役割です。私のチームメンバーは国籍だけでなく、スキルレベルもバラバラですが、共通点としては向上心が高いのが挙げられます。中には細部に気を配って、気づきにくいところまで発見くれるメンバーもいれば、プライベートで勉強して、取得したスキルや知識を業務で活かすメンバーもいます。あとはやる気を維持するのも必要で、なるべくメンバーのやりたいことを実現させてあげたいですが、そうならない時もモチベーションを持たせるコミュニケーションは必要です。 また、メンバーがまだ使ったことがない技術を使う必要がある場合は私がレクチャーしたり、もし私も使ったことがないなら一緒に勉強したりします。 リーダーとして気をつけていきたいこと 適切なコミュニケーションとフォロー コロナ禍以降、リモートワークが増える傾向にありますが、弊社もフルフレックス制度やリモートワーク制度を導入しています。そうするとオンラインでのミーティングが多くなります。対面と比べて相手の表情やジェスチャーからの情報が察知しづらくなり、コミュニケーションミスが発生するリスクがあるので、特に自分がリーダーとして主に話したい時と、メンバーから相談がある時の1on1はなるべくお互いオフィスにいる時に行っています(もちろんミーティング自体は最小限にしています)。 また、私自身は去年部署内で開催された ハッカソン で優勝し、作品を製品化に向けて別のチームで進めているので、兼務の中で素早くメンバーのフォローに回せないという反省もありますので、今は両立できるように手探りしています。 うまく任せて育てること 私のチームは複数のプロダクトを担当しているので、プロダクトへのアサインはメンバーのスキルレベル、好みなどとプロダクトの難易度、課題などを掛け合わせて考慮するようにしています。 そして、アサインした後も定期的に状況をヒアリングしたり、サポートしたりします。たまにメンバーのタスクを見て、自分もやりたい気持ちが強くなる時もありますが、そこは頑張って自分の気持ちを抑えてメンバーにやってもらっています(笑)。 一方、任せた後もどんなことをしたらメンバーが成長するかも考えなくてはいけません。私の一つの答えとしては、なるべく自分で考えてもらうことです。例えばジュニアレベルのメンバーだと、0から何もかも教えるのでなく、レビューで指摘したい時はまず質問して、「何で」という部分を意識してもらって考えさせています。 終わりに 1年リーダーをやってみて、たくさんのことを経験し、今年度のグローバルGフロントエンドチームの展望について述べてこの記事の終わりに迎えたいと思います。 FE分野のスキルアップデート 前述したように、チームにはまだジュニアレベルのメンバーがいるということに加え、技術も日々変わってきます。フロントエンドだけでもここ数年たくさんの新しい話題となるフレームワークが生まれました。例えば他の部署で使っているSvelteや、AstroやQwikなどがあります。 新しいフレームワークやライブラリーを導入するには技術以外の要素も考えないといけませんが、勉強しておくと将来役に立つし、視野が広がるので、メンバーに対して影の年間目標として設定し、チームとしてスキルのアップデートをしていきたいと思います。 BE分野への理解 「フロントエンドだけ特化して、バックエンドへの理解が少ないと、API連携の話で噛み合わない部分がある」というのは自分の経験上でよくある話なので、自社では職種的にフロントエンドとバックエンドで分かれていても、なるべく担当でない領域の基礎知識も身についてもらいたいと考えます。例えば、今の一つやっている施策としては、メンバーをプロジェクトにアサインした後、バックエンドにも迷惑かけないレベルで簡単なタスクを経験させてもらえるようにプロジェクトリーダーと調整したりしています。
アバター
はじめに 先週末にOpenAIの発表があり、ChatGPT(GPT-4)のCode Interpreterという機能が公開されました。内部でPythonが動いており日本語や英語で指示するだけでノーコードでデータの分析などを行うことができる便利なサービスです。 そのままではグラフ描画などで日本語の表示ができないのですが、「 日本語フォント表示する方法を見つけ出した 」のでこちらの記事でご紹介します。 この記事で学べること Code Interpreterを使用して日本語フォントでグラフや画像、PDFファイルを出力する手順 Code InterpreterでTwitterのログ分析の解析方法 前置き不要という方は[Code Interpreterで日本語フォントを使ってグラフや画像、PDFを出力する手順だけを知りたい方はこちらからどうぞ]。(#chatgpt(gpt-4)のcode-interpreterで日本語フォントをつかう方法のまとめ) 自己紹介 自己紹介が遅れましたが、 KINTO FACTORY のリードエンジニアをしている 中西 葵 です。本業の開発とは別にテックブログやイベント運営、Twitterアカウントの運用も行っており、いわゆる技術広報的な取り組みも一部行っております。 前提条件 ChatGPTに課金していること(ChatGPT Plus) Code Interpreterを有効にしていること 以上の2点です。 Code Interpreterの有効化 Code Interpreterの有効化は Settings -> Beta Features -> Code Interpreter から以下の画像の通りに有効化してください。 日本語化までの道のり 指示なしでは自動的に日本語での描画が行われないことがわかったので、ChatGPTに探りを入れていきましょう。 〇〇を日本語表示して! まずは「日本語表示して」と指示してみましたが、現在の環境では日本語フォントを埋め込むことができないと返されてしまいました。 日本語表示できない理由を探る 「 現在の環境では 」という一文に注目し、グラフ画像をどのように生成しているのかを尋ねてみます。 日本語表示のやり方を聞いてみる PythonとMatplotlibを使っていることがわかったので日本語表示する方法を聞いてみます。 例として出てきたコードがこちらになります。 インターネットアクセスが無効化されているため、フォントのダウンロードやシステムの設定変更はできないと言われてしまいました。ファイルのアップロードは可能ですが、ダウンロード等はできないようです。また別で確認したところ、システム関数なども使えないようになっているようです。 日本語フォントファイルのアップロード 「 ダウンロードはできない 」との事なので逆にアップロードはできるのではないか?との疑問から早速聞いてみます。 ファイルのアップロードなら可能とのことなので早速試してみます。 日本語フォントのダウンロード ChatGPTから教えてもらったサンプルコードにもNoto Sansのフォントが書かれていたので今回はNoto Sansを使うことにします。 https://fonts.google.com/noto/specimen/Noto+Sans+JP からフォントをダウンロードします(試していませんが他のフォントでも利用可能) 日本語フォントのをアップロードしてmatplotlibの日本語化 早速日本語でグラフを生成してみましょう 日本語フォントのアップロード 画面の下にあるプラスボタンからファイルをアップロードします。合わせて「 グラフの描画を行う際にはこちらの日本語フォントを利用してください 」のようにグラフ描画時にアップロードした日本語フォントを使用するように指示してください。 ↓ ↓ ↓ ChatGPT(GPT-4)のCode Interpreterで日本語フォントをつかう方法のまとめ 以下の手順で日本語のグラフを表示することができるようになりました。 日本語フォントをローカルにダウンロード ChatGPTに日本語フォントをアップロード ChatGPTにグラフ描画時のフォントを指定 必要なデータをアップロードしてグラフを描画 また、グラフ描画時の凡例が日本語化されない場合は、 凡例も日本語化してください と伝えて再生成することで凡例部分も日本語化することができます。ポイントはChatGPTにアップロードした日本語フォントファイルを使って日本語を描画するように指示するところです。PDF等も同じ手順で日本語を正しく表示可能なファイルを出力することができます。 Twitterログの分析 日本語で分析結果を出力できる準備が整ったので早速Twitterログの分析を行っていきましょう。 https://twitter.com/KintoTech_Dev こちらのTwitterアカウントのデータ分析を行います。Twitterアナリティクスから細かなデータをダウンロードして様々な分析を行うことができますが今回は既にパプリックに公開されているデータを元に分析を行います。利用するデータは以下の通りです。 ツイート本文 ツイート日時 リツイート数 リプライ数 いいね数 これらのデータはアカウントオーナー以外でもアクセスできる一般に公開されているデータとなります。 CSVファイルのアップロード フォントをアップロードしたとき同様にアップロードボタンからファイルを選択し分析対象のデータをアップロードします。今回はCSVファイルをアップロードしましたが、Pythonで扱えるデータであれば基本的に何でも読み取ることができるようです。またアップロード可能なファイルサイズを確認したところ正確な数字は教えて頂けませんでしたが、数百MBのサイズが受け入れ可能とのことです。 ChatGPTにファイルをアップロードし分析を依頼します。いくつかのChatGPTからの質問に答えたあと出てきた分析結果は以下のとおりです。インプレッション数、リツート数、いいね数、本文の分析それぞれの解説もしてもらった結果は以下の通りです。 インプレッション数の分析 ChatGPT: 特定の時間帯や曜日に明確な傾向は見られません。しかし、火曜日の特定の時間帯では他の時間帯よりもインプレッション数がやや高いようです。 KINTOテクノロジーズでは火曜日に記事出すことが多く、公開と合わせてツイートをしているためにこのような分析結果が出ているようです。 リツイート数の分析 ChatGPT: こちらも特定の時間帯や曜日に明確な傾向は見られません。しかし、火曜日の特定の時間帯では他の時間帯よりもリツイート数がやや高いようです。 インプレッション数と比例してリツート数も多いようです。 いいね数の分析 ChatGPT: いいね数も特定の時間帯や曜日に明確な傾向は見られません。しかし、火曜日の特定の時間帯では他の時間帯よりもいいね数がやや高いようです。 インプレッション数、リツイート数、いいね数はそれぞれ相関があることが見て取れますが、 ChatGPT: 今回の分析ではツイートの数が比較的少ないため、結果はあくまで参考程度に留めていただくことをお勧めします。 と言及されているため分析結果のヒートマップを見てもわかりにくい部分もありますのでまずはツイートやフォロワーを増やして行く必要がありそうですね。 ツイート本文の分析 インプレッション数が高いトップ10のツイートをChatGPTを使ってトピックモデリングで分析した結果を以下に示します。意図した内容が分析結果に反映されていることが分かりました。 項目 説明 注目を引く視覚的な要素 ユーザーの注意を引き、エンゲージメントを向上させる ハッシュタグの使用 ツイートが見つけやすくなる 記事の主題の明確な紹介 記事の主題が明確に紹介されている 具体的な行動の呼びかけ 「開発環境の構築や管理にお困りの方は是非ご覧ください!」という具体的な行動の呼びかけが含まれている 今後の記事の予告 新しい記事の公開を通知することで、ユーザーの関心を引き、エンゲージメントを向上させる 特定のテーマの強調 テーマを強調するとイベントや企業の内部の活動に関心があるユーザーの関心を引く ツイート分析結果のまとめ ここまでの分析結果から投稿時間、ツイート内容、URLの活用がツイートの最適化におけるポイントとなっていることが分かりました。 項目 説明 投稿時間の最適化 顕著に出ているわけではないが木曜日と金曜日の夜にエンゲージメントが高まる傾向がある。週末に向けてフリータイムが増えより多くのコンテンツを探求している可能性がある ツイート内容の最適化 イベントや新しい記事のトピックが含まれるツイートがエンゲージメントが高い URLを含むツイート テックブログURLを含むツイートのエンゲージメントが高い 全体のまとめ 本記事ではChatGPTのCode Interpreterを使ってデータ分析を行い日本語でグラフ等の作成を行う方法を解説しました。皆さんの分析ライフにお役に立てましたら幸いです。 これからも続々と役立つ記事を公開していきます。最新情報が気になる方はぜひこちらのアカウントをフォローしてください。 https://twitter.com/KintoTech_Dev KINTOテクノロジーズでは一緒にモビリティの未来を創る仲間を募集しています。カジュアル面談なども行っておりますのでご興味をお持ち頂けましたらぜひお気軽にご連絡ください。 https://www.kinto-technologies.com/recruit/
アバター
はじめに こんにちは。分析グループでデータエンジニアをしている小池です。Slackって便利ですよね。こいつは突然何を言い出すのか。そう思ったかたも多いかもしれません。しかし、安心してください。Slackが便利であることはまぎれもない事実です。そして、その中でも絵文字リアクションは群を抜いて便利だとわたしは思います。そもそも絵文字リアクションが何者かわかりますか。少していねいに説明してみますね。次の画像をご覧ください。 ![Slack絵文字リアクション](/assets/blog/authors/charlie/slack_emoji_reaction.png =660x) これはわたしが自分のtimesチャンネルでぼやいたものです。白く囲んだ部分を見ると、ぽこぽこと泡のようなものがあることがわかると思います。これがまさしく絵文字リアクションです。「🥲」という絵文字の横に「1」とありますが、これは1人が「🥲」絵文字のリアクションをしたことを表しています。この数字が多ければ多いほどうれしいですね。また、ほかに「どんまい」という絵文字があります。これはSlackに標準搭載されているわけでなく、ユーザが追加した「カスタム絵文字 ^1 」というものです。今回は、ChatGPTの力を借りてカスタム絵文字の追加を行ってみます。 背景 そもそも、なぜカスタム絵文字を追加したくなったのかを説明しましょう。わたしの所属している分析グループは、東京、名古屋、大阪の3拠点に分かれて業務を行っています。これは、常時フルリモート状態になっているメンバーがいるということです。東京は東京、名古屋は名古屋、大阪は大阪でオフラインのコミュニケーションが行われることもあるため、全員が自宅からリモートワークをしている状態よりも情報の格差が生じてしまっているかもしれません。こういった事象をなくすべく、テキストコミュニケーションツールであるSlackにもっと力を入れるべきなのではないかと考えました。そして、その手段としてカスタム絵文字の追加を思いついたというわけです。 Slackカスタム絵文字の作成 それでは、さっそくカスタム絵文字を追加していきましょう。以下のような手順で進めていこうと思います。 リアクションに使われた絵文字の利用回数の集計 1で集計した絵文字を感情別にグループ分け 2で作成したグループをもとにChatGPTで単語を生成 3で生成した単語を選定し、絵文字作成ツールで画像にしてSlackへ登録 絵文字の利用回数の集計 カスタム絵文字を追加するといっても、どんなものを追加すればよいのかいきなりはわかりませんね。そこで、まずは日ごろからどのような絵文字が使われているのかをリサーチするところから始めてみます。具体的には、チャンネルと期間をしぼり、それらの投稿の中でなされた絵文字リアクションとその回数を調べて傾向をつかんでみようと思います。 はじめに、SlackのAppを作成して、以下のように権限を設定し(必要そうなものをなんとなく追加したため、過剰な権限になっているかもしれません)、トークンを発行します。 作成したAppをPythonから呼び出して次のように集計作業を行います。 import datetime import pprint import time import yaml from slack_sdk.web import WebClient def main(): get_and_save_messages() reaction_counts = get_reaction_counts() pprint.pprint(sorted(reaction_counts.items(), key=lambda x: x[1], reverse=True)) def get_and_save_messages(): SLACK_API_TOKEN = "SLACK_API_TOKEN" client = WebClient(token=SLACK_API_TOKEN) target_channel_id_to_name = { "id0": "#name0", "id1": "#name1", "id2": "#name2", "id3": "#name3", "id4": "#name4", } unix_times = get_unix_times() messages = get_messages_in_channels(client, list(target_channel_id_to_name.keys()), unix_times) with open("messages.yaml", "w") as f: yaml.dump(messages, f, allow_unicode=True) def get_messages_in_channels(client, channel_ids, unix_times): merged_messages = [] for channel_id in channel_ids: for unix_time_pair in unix_times: merged_messages += client.conversations_history( channel=channel_id, include_all_metadata=False, latest=str(unix_time_pair[0]), limit=100000, oldest=str(unix_time_pair[1]) )["messages"] return merged_messages def get_unix_times(): unix_times = [] today = datetime.date.today() start = 1 end = 15 for _ in range(24): start_date = today - datetime.timedelta(days=start) end_date = today - datetime.timedelta(days=end) start_unixtime = int(time.mktime(start_date.timetuple())) end_unixtime = int(time.mktime(end_date.timetuple())) unix_times.append((start_unixtime, end_unixtime)) start = end + 1 end = start + 14 return unix_times def get_reaction_counts(): with open("messages.yaml", "r") as f: messages = yaml.safe_load(f) reaction_counts = dict() for message in messages: if "reactions" in message: for reaction in message["reactions"]: reaction_counts[reaction["name"]] = reaction_counts.get(reaction["name"], 0) + reaction["count"] return reaction_counts if __name__ == "__main__": main() ソースコードについてかんたんに説明しましょう。 main() では各工程ごとの関数を呼び出すようになっています。 get_save_and_messages() はSlackからメッセージを取得してファイルに保存する関数です。なお、 SLACK_API_TOKEN と target_channel_id_to_name は伏せてあります。メッセージが多すぎると一度に取得できない場合があるため get_unix_times() で期間を分割したものをリストで返し、メッセージを小分けに取得するようにしています。メッセージ取得が終わったら get_reaction_counts() で絵文字リアクションの回数を集計します。その後、回数が多い順にソートして表示して終了です。 実行結果を次に示します。たとえば、 ('man-gesturing-ok', 248) は、 man-gesturing-ok という絵文字が248回リアクションされたことを表しています。また、この実行結果にはデフォルトの絵文字はもちろん、手作りのものや社外のかた[^2]が作成したものも含まれています。 ('man-gesturing-ok', 248), ('eyes', 248), ('arigatougozai', 199), ('承知しました', 64), ('+1', 49), ('thinking_face', 43), ('yorosikuonegaisimasu', 26), ('arigatougozaimasu', 17), ('tada', 15), ('素敵', 14), ('arigato', 13), ('do_ne', 13), ('man-gesturing-no', 11), ('scream', 10), ('kakuninshimasu', 10), ('承知2', 9), ('woman-bowing', 9), ('sob', 8), ('ok', 8), ('faito', 8), ('kami_bl', 7), ('done_hiragana', 7), ('desune', 7), ('naruhodo', 7), ('ok_hand', 7), ('sugoi', 7), ('tasukaru', 7), ('pray_mochimochi', 7), ('done-payment', 6), ('hai-', 6), ('nanto', 6), ('yokata', 6), ('mumondai', 6), ('tashi-crab', 5), ('muscle', 5), ('oh', 5), ('sasuga2', 5), ('uooo', 5), 絵文字のグループ分け 先ほど集計した状態のままだとまだ解像度が低いため、絵文字を感情表現の種類ごとにグループ分けして特徴をつかんでみます。なお、グループ分け方法は独自で考えたものにしました(調べてみると心理学者が考案した感情の区分けがあるようでしたが、仰々しすぎて今回にはそぐわないと判断しました)。 報連相のために行われるものと感情表現のために行われるものに二分され、そこからさらに分かれていくといった構造になっていると考えました。それを次に示します。 - 報連相 - 報告 - チェック - お願い - 感情表現 - 共感 - 褒め - 感謝 - 応援 - 慰め - その他 この区分けを先ほどの集計結果に適用してみます。 ![絵文字グルーピング](/assets/blog/authors/charlie/grouping.png =170x) まじまじとご覧になったかたはわかると思いますが、どこにあてはまるかわからないものはほとんどフィーリングで適当に分けました...🤫 ですが、まずまず納得感のあるグループ分けができたのではないかと思います。 ChatGPTを用いた絵文字生成 絵文字のグループ分けに続いて、それぞれの感情表現の種類ごとに絵文字を生成してみようと思います。また、手段として今はやりのChatGPTを使ってみます。使用したモデルはGPT-4で、プラグインやウェブブラウジング機能は使いませんでした。使用したプロンプトは次のとおりです。 Slackのカスタム絵文字を作りたいです。下記についてそれぞれ10こずつあげてください。 また、ひらがな、カタカナ、漢字のいずれかで4~6文字に収めるよう努力してください。 - メッセージを送ってきた相手に共感を示せるもの(例 : :たしかに:、:ですね:) - メッセージを送ってきた相手を褒められるもの(例 : :すてき:、:神:、:すごい:) - メッセージを送ってきた相手に感謝を示せるもの(例 : :ありがと:、:たすかる:) - メッセージを送ってきた相手を応援できるもの(例 : :ファイト:、:ウオオオ:) - メッセージを送ってきた相手を慰められるもの(例 : :oh:、:なんと:) 実行結果は次のとおりです。 カスタム絵文字の提案です。ひらがな、カタカナ、漢字のいずれかで4~6文字に収めるよう努力しました。 メッセージを送ってきた相手に共感を示せるもの: :わかるよ: (wakaru yo) :同感: (doukan) :納得: (nattoku) :うんうん: (un un) :それそれ: (sore sore) :言いたい: (iitai) :共感: (kyoukan) :感じる: (kanjiru) :ごもっとも: (gomottomo) :思いやり: (omoiyari) メッセージを送ってきた相手を褒められるもの: :すごいね: (sugoi ne) :天才: (tensai) :凄い: (sugoi) :かっこいい: (kakkoii) :素敵: (suteki) :美しい: (utsukushii) :完璧: (kanpeki) :尊敬: (sonkei) :称賛: (shousan) :輝いてる: (kagayaiteru) メッセージを送ってきた相手に感謝を示せるもの: :ありがとう: (arigatou) :感謝: (kansha) :感謝してる: (kansha shiteru) :ありがとうね: (arigatou ne) :ありがとー: (arigatou-) :助かった: (tasukatta) :感謝の気持ち: (kansha no kimochi) :恩返し: (ongaesha) :お礼: (orei) :感激: (kangeki) メッセージを送ってきた相手を応援できるもの: :がんばって: (ganbatte) :ファイト: (faito) :頑張れ: (ganbare) :応援する: (ouen suru) :力になる: (chikara ni naru) :一緒にがんばろう: (issho ni ganbarou) :がんばりましょう: (ganbarimashou) :エールを送る: (eeru wo okuru) :成功を祈る: (seikou wo inoru) :自信を持って: (jishin wo motte) メッセージを送ってきた相手を慰められるもの: :大丈夫: (daijoubu) :頑張れ: (ganbare) :気にしないで: (ki ni shinaide) :がんばったね: (ganbatta ne) :辛いね: (tsurai ne) :つらいよね: (tsurai yo ne) :明日は新しい日: (ashita wa atarashii hi) :落ち込まないで: (ochikomanaide) :前向きに: (zenmuki ni) :頑張ってるね: (ganbatteru ne) Slackで使うには明らかに不自然なものもありますが、なかなかよさそうです。 絵文字の選定とSlackへの登録 先ほどChatGPTに生成させた絵文字から使い勝手のよさそうなものを選んでみるとともに、すでにSlackワークスペースに登録されている絵文字を省いてみます。 - 共感 - :wakaruyo: - :doukan: - :gomottomo: - 褒め - :sugoine: - 感謝 - :kangeki: - 応援 - 慰め - :kinishinaide: こんな感じになりました。 最後にこれらを絵文字作成ツール[^3]で画像にし、Slackに登録して完了です。 うーん、いい眺め! おわりに いかがでしたか。これでSlackでのコミュニケーションが活発になること間違いなし...とはいかず、追加した絵文字を率先して使って組織に浸透させていく作業が重要になってくると思います。また、今回は感情表現の種類ごとにまんべんなくカスタム絵文字を追加しましたが、足りていないところを集中的に作るというアプローチもできそうです。あなたの組織のSlackワークスペースでもやってみてくださいね! [^2]: ¥0 – SmartHR Store [^3]: 絵文字ジェネレーター
アバター