KINTOテクノロジーズのブログ - TECH PLAY

TECH PLAY

KINTOテクノロジーズ

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

1113

この記事は KINTOテクノロジーズアドベントカレンダー2024 の3日目の記事です🎅🎄 学びの道の駅の始まりから早くも一年が経過しようとしています!「学びの道の駅」がKINTOテクノロジーズの学びカルチャーを活性化する起爆剤となると信じている技術広報グループの中西です。弊社の技術広報グループでは社員のインプットからアウトプットまで人の成長における様々な点を繋げて組織カルチャーを改革すべく日々走り続けています。 エンジニアカルチャーを後押しする大きな変化 今年、弊社のエンジニアカルチャーに大きな変化が起こりました。それは、技術広報グループの立ち上げです。今までは、プロジェクトとして活動していたに過ぎませんでしたが、今年の春より正式に組織としてグループ化し、現在は兼任ではなく専任で技術広報Gの活動を本務としている方々もいます。これは、会社として社員の発信力を後押しするという大きな決断の一つになります。 そして今年は技術広報グループの立ち上げに留まらずテックブログ立ち上げ当初から計画していたインプットの領域も組織として認めて頂く事になりました。 技術広報グループの活動を自動車に例えてみる 自動車で例えるとしたら今まで力を入れてきた「アウトプット領域」はマフラーを交換したり、排気効率が良いようにエンジンの構造変更を行っていたという活動です。 「インプット領域」である「学びの道の駅」は燃料をガソリンからハイオクやニトロに変えたり、キャブレターからインジェクションになりターボにしていくようなパワーを秘めています。つまり、燃料の変革とそれをどのように効率的に噴射してエンジンにインプットするかを担う重要なチームです。 エンジンを人やチームとして考えた場合、それぞれのエンジンに必要なインプットの内容や量が異なります。ディーゼルエンジンやガソリンエンジンでは燃料のインプット方式も異なりますし、エンジンの排気量によっても効率性が異なってきます。 社内の学びを集約 前置きが長くなりましたが「学びの道の駅」ではインプットを各人に最適化して社員同士の強みを活かすような学びの仕組みを検討しております。今までの活動として勉強会の見える化、勉強会情報のシェアや拡散、勉強会開催のサポート、社内の勉強会情報の集約など、とにかく散らばっていた学びを一箇所に集約する活動に力を入れてきた一年でした。これからは「K to K」に軸足を置き、人と人とを繋げていく活動をしていきます。 「K to K」とは? 「K to K」とは「KINTOテクノロジーズ to KINTOテクノロジーズ」の略です。社員同士で学びを得たりスキルを伝え合ったりするということを目的としています。 例えば 「分析力足りないな」→分析グループに相談。 「コーチングを学びたい」→〇〇さんがコーチングが上手。 「プロジェクトマネージメント」→PdMのあの人達に聞いてみよう のような学びたいというエネルギーと 「自分の〇〇スキルをもっと活かしたい」 あの人こんなスキルもあるけど業務で活かすきっかけを探している などの教えたい、伝えたいエネルギーを繋げていくことで、社員の魅力をより活かした形で業務を活性化させることが出来ます。 「学びの道の駅」のネクストアクションは、我々が得意とする「人やチームの魅力を引き出すこと」「点と点を結びつけること」です。どのような壁が立ちはだかるのか来年以降の活動が今から楽しみです。 他の活動 インプット領域の活動として、現在展開しているPodcastの拡張版も検討しています。今は勉強会を主催している皆様にインタビューを行うという活動が中心になっていますが、K to K同様に、社内に伝達していきたいことがたくさんあります。これらをPodcast形式にして社員の学びに繋げたり、社内に限らないアウトプットの場にもしていきたいと考えています。 またUdemy Businessも取り組みとして行っているので、動画コンテンツでどのように効果的に学習を行うのかなども企画していきたいと考えています。 まとめ テックブログやイベント開催、登壇などと並ぶ、次の大きな柱として今後の「学びの道の駅」の活動の幅を様々に展開していきたいと思います。インプット領域をどんどん拡張していく事で、最終的には事業領域での幅を広げ、社員一人ひとりがより成長でき、個性を活かして活躍できるような環境を目指していきたいと思います。 来年の活動も積極的に発信していきますので、皆様お楽しみに!
Introduction Hello, I am ahomu, a new member who joined the company in June. In this article, I asked everyone who joined the company in June and July 2024 to share their thoughts and experiences since joining. I hope this will be helpful for anyone interested in KINTO Technologies and serve as a meaningful reflection for those who contributed to it when they look back someday! hosoya ![Photo of a houseplant](/assets/blog/authors/ahomu/20241007/hosoya.jpg =300x) Self-introduction I am hosoya. I am part of the IT/IS Division, where I handle help desk support for in-house systems. How is your team structured? The team consists of five people including myself. In addition to my team, there are several other teams, each with distinct roles, and we collaborate with them based on the nature of the inquiries we receive. What was your first impression of KTC when you joined it? Were there any surprises? I was impressed by how the info sys staff is organized into dedicated teams for each role, with seamless and thorough collaboration among them Having only worked in info sys departments with one or two people before, I was amazed by how well-structured and robust the team here is. What is the atmosphere like on-site? It is a quiet environment where you can focus on your own work. However, it’s easy to talk to the people around you, and whether it’s about work or just casual chatting, the mood instantly brightens. It’s a very cheerful and lively atmosphere. How did you feel about writing a blog post? I imagine that unless you work directly with others, you might not have the opportunity to learn about what they typically do. I hope this blog provides a chance for people to gain that insight A question from someone else: Please tell me about your daily work schedule. Answer: Basically, I get to work at 9:00 a.m., and handle help desk inquiries until I leave at 6:00 p.m. In the mornings and evenings, we hold meetings to share information across the teams The tasks vary depending on the inquiries, but for the most part, I handle routine work each day. my ![Photo of a blue ocean and sky with white clouds](/assets/blog/authors/ahomu/20241007/my.jpg =300x) Self-introduction I am my, and I am in the Data Analysis Division. I am currently working as a data scientist. As a data scientist and machine learning engineer, I have been involved in a variety of work related to data. How is your team structured? It consists of four people including the manager. What was your first impression of KTC when you joined it? Were there any surprises? I was pleasantly surprised by the excellent onboarding process, the comprehensive in-house documentation, and the vibrant communication on Slack. These aspects left a strong impression on me. What is the atmosphere like on-site? The environment has a calm atmosphere, making it easy to engage in discussions about technology. How did you feel about writing a blog post? I'm glad to have had the opportunity to share some information. **A question from someone else: Please tell me something you were really glad you bought while working from home! **Answer: A Herman Miller chair. It is comfortable to sit in even for a long time, and I am very satisfied with it. yi ![Photo of two cacti in a flower pot](/assets/blog/authors/ahomu/20241007/yi.jpg =300x) Self-introduction I am yi from the Platform Development Division’s QA Group, where I do QA. How is your team structured? The team is composed of 10 members and is broadly divided into three groups: front-end, back-office, and apps, each managing their respective projects. What was your first impression of KTC when you joined it? Were there any surprises? Although it’s a newly established company, I was impressed by how well-structured its internal organization is. Before joining, I imagined things might be a bit more chaotic, but everything felt much more organized and calm than I expected. What is the atmosphere like on-site? Even when they’re busy, the team and project members are always willing to answer my questions, and the overall atmosphere is relaxed. This makes it an environment that’s easy to settle into. How did you feel about writing a blog post? I had never written for a blog like this before, so honestly, I wasn’t sure what to write. A question from someone else: What is the atmosphere in the team like? Please tell me about something you felt was good about your team recently. Answer: As I mentioned earlier, the overall atmosphere is calm. As a member of KTC’s QA staff, it feels like we each work on our assigned project tests collaboratively with our partners. Many people are handling multiple projects and everyone is busy, but despite that, it’s an environment where not only newcomers but everyone feels comfortable asking each other questions. I think that’s one of its great qualities. ahomu ![Illustration of a seabird holding an axe](/assets/blog/authors/ahomu/ahomu.png =300x) Self-introduction I am ahomu. I belong to the IT/IS Division. In terms of work experience, I have quite a bit of experience in web front-end development, but currently, I’m involved in various inter-organizational tasks. How is your team structured? When I joined the company, I planned to figure out the specifics after getting hired. At the time of writing this article, I’m working solo, attached to a division as an in-house freelancer. (。•̀ᴗ-)✧ What was your first impression of KTC when you joined it? Were there any surprises? During my casual interview and the selection process, the head of my current division and the vice president openly shared insights about the business situation and the organization's atmosphere, so nothing has come as a surprise. If I had to mention something along those lines, being part of a large company means that internal controls are stricter compared to my previous experiences with mega-ventures and startups. I find this refreshing and quite positive. What is the atmosphere like on-site? While I mentioned working solo, I still get the chance to engage with managers and team members from various departments. I can clearly sense the weight of responsibility they carry in managing the business, yet they are always willing to engage in conversation, even with a newcomer like me reaching out unexpectedly. It’s incredibly helpful. How did you feel about writing a blog post? Now that I think about it, I was truly amazed by how actively people contribute to the Tech Blog within the company. What’s particularly impressive is that information is consistently shared without anyone needing to push for it. I feel this proactive approach holds tremendous potential for growth. A question from someone else: Please tell me about any differences you found between the Nagoya and Tokyo companies in terms of culture, atmosphere, and the like. Answer: Nagoya has a small, close-knit setup with around 20 people, many of whom are actively involved in a wide range of fields. It gives the place a unique and distinctive vibe. It feels quite connected to KINTO’s business, and there seem to be many people there who interact with the parent company as well. Recently, occasional drinking parties have started taking place at the Nagoya office. Tsuzura ![Photo of a sunset showing a river running through a foreign city and the townscape on both banks](/assets/blog/authors/ahomu/20241007/tsuzura.jpg =300x) Self-introduction I am a designer in the Marketing Planning Division’s Organization Group! How is your team structured? It consists of nine directors and four designers. What was your first impression of KTC when you joined it? Were there any surprises? Since the departments and teams are divided, I initially thought there might be limited interaction between employees. However, I’ve been able to connect with designers from other departments during lunch and at informal gatherings like private drinking parties. This has allowed me to exchange information and ideas effectively, which has been incredibly helpful. What is the atmosphere like on-site? In our team, we each focus on our own projects, so there isn’t much direct involvement with one another’s work. However, when we gather at the office, we take time to chat and connect while staying productive. Overall, it feels like a well-balanced dynamic. How did you feel about writing a blog post? Extremely excited. A question from someone else: Please tell me about any delicious lunches there are near your office! Answer: I belong to the Muromachi office, and I recommend Dedesuke Saigon Kitchen ! I always opt for their Half & Half option and get pho and curry. They offer about four flavors for each, and every single one is absolutely delicious. I highly recommend trying it! Naoki Uehara ![Profile photo of a cat with its eyes closed](/assets/blog/authors/ahomu/20241007/uehara.png =300x) Self-introduction My name is Uehara. I am part of the Project Promotion Division’s KINTO FACTORY Development Group. I work as a backend engineer. In my previous job, I did news media development at long-established ISP. My favorite programming language is Rust, and my favorite editor is NeoVim. How is your team structured? Back-end development is done by six engineers. If you include the front-end engineers as well, there are around 20 people. What was your first impression of KTC when you joined it? Were there any surprises? I initially thought I might be thrown straight into the deep end with little onboarding in place, but to my surprise, the onboarding process, one-on-one support, and other resources were incredibly well-organized. This made it much easier for me to transition smoothly into the work. The company has an atmosphere that encourages trying new things, which I find incredibly stimulating and inspiring What is the atmosphere like on-site? I think it is a very friendly atmosphere. I’m the kind of person who gets bothered when there’s something I don’t understand, but the other team members always answer my questions without hesitation or frustration, and I’m incredibly grateful for that. I now have more time to focus on development work and can think more deeply about the products from an engineer’s perspective. I feel it’s a great environment for that. **How did you feel about writing a blog post? ** Actually, before I joined KINTO Technologies, I was helped out by an article on its Tech Blog. So, it is a great honor to be joining the ranks of its bloggers myself now. Personally, I make a conscious effort to share my ideas through platforms like Slack and blogs. Moving forward, I aim to contribute more useful information to the Tech Blog. **A question from someone else: Please tell me what your best-ever vacation was! And why, if you do not mind! ** Answer: I guess that has to be my honeymoon in Ise-Shima! The Mawaryanse tickets offered by Meitetsu are incredibly convenient. They are hard to get if you live in Tokyo, but I recommend buying one on Jalan with no limited express ticket attached. Jinrong Liang ![Photo of a curry, fries, and a can of Sui (gin soda)](/assets/blog/authors/ahomu/20241007/jin.jpg =300x) Self-introduction I am Jinrong Liang, and I come from Taiwan. I belong to the Mobile Development Group, which mainly develops Android apps. How is your team structured? In the development team for the products I work on, there are six Android engineers, including me. What was your first impression of KTC when you joined it? Were there any surprises? The team I’m part of is full of energy and includes many Android engineers. Engaging with others on technical topics through study sessions and similar activities has been a highly stimulating and rewarding experience for me. What is the atmosphere like on-site? Depending on the development period, it is often busy, so it felt like quite a fast-paced development team. Even so, everyone on the team wants to make good products, so we spare no effort when it comes to communicating in detail. How did you feel about writing a blog post? Writing my first entry about joining the company gave me an opportunity to reflect on how I felt when I first started and consider how I want to grow and contribute at KTC moving forward. **A question from someone else: Are there any smartphone apps that you have been interested in lately? ** Answer: The PayPay app. I have been using it for many years since the service launched, and I am deeply interested in how it functions as a super app, continuously evolving while maintaining quality as new features are introduced. Dara Lim ![Photo of a car exhibited indoors](/assets/blog/authors/ahomu/20241007/daralim.jpg =300x) Toyota FJ25 Land Cruiser - Toyota Dealership in Bogota, Colombia Self-introduction My name is Dara Lim. I belong to the KINTO Global Development Group in the Business Development Department. My title is Business Development Manager, but the work I do relates closely to working as a business analyst. In my previous job, I worked as a financial analyst and business analyst in the insurance industry. How is your team structured? My team consists of three members, and we collaborate closely with the engineering team to develop software solutions for global full-service lease businesses. What was your first impression of KTC when you joined it? Were there any surprises? I really appreciate the orientation/onboarding process and the 1-on-1 meetings. They helped me to smoothly transition into work. My team was also very supportive. What is the atmosphere like on-site? I really enjoy the Jimbocho office space and its surroundings. My team sits close to each other so we are able to have discussions readily. How did you feel about writing a blog post? Actually, before I joined the company, I was helped by many articles on KINTO Technologies' Tech Blog, so I’m glad to write my initial experience on joining the company. A question from someone else: What is the best thing you have noticed since joining KTC? Answer: I have had the experience of traveling to Latin America to visit KINTO businesses in Peru, Brazil, and Colombia. These were very valuable experiences for me to understand the car leasing business, its profitability and best of all, to meet others fellow KINTO members. I think this is the best thing I’ve experienced since joining KTC. Ikuya Tani ![Illustration of a fluffy cat](/assets/blog/authors/ahomu/20241007/tani.jpg =300x) Self-introduction I am Tani from the KINTO ONE Development Division’s New Car Subscription Development Group, where I am a front-end engineer in the Osaka Tech Lab. I have done a wide variety of front-end development work ranging from production-related stuff to service development. How is your team structured? The team consists of four people. We are developing tools for dealers and in-house use with a small number of people. What was your first impression of KTC when you joined it? Were there any surprises? Before joining the company, I imagined it might be a chaotic environment, blending the atmosphere of a large corporation with that of a startup, and lacking a fully established work structure. However, once I started, I was pleasantly surprised to find a thorough onboarding process, flexible workload adjustments, a fully flextime system, properly reflected overtime pay, a generous welfare package, and a welcoming, friendly team. It turned out to be a collection of wonderful surprises. What is the atmosphere like on-site? I believe it’s an environment with a strong sense of psychological safety, where you feel comfortable actively asking questions about anything you don’t understand. Another attractive feature is that taking part in study sessions is recommended, and in addition, in the case of my team, there is a high degree of freedom in terms of selecting technologies, and rearchitecting and refactoring are also recommended. So all in all, it feels like an environment where it will be easy to level up my skills. How did you feel about writing a blog post? I wanted to convey a detailed and vivid picture of what KINTO Technologies is like, so I dedicated myself to typing away at the keyboard with all my effort. **A question from someone else: What is your favorite possession, and why? **Answer: My Sony noise-cancelling headphones (WH-1000XM5)! Thanks to these, even someone as sensitive to sounds as me can quickly get into the zone, so I really treasure them. Closing words Thank you everyone for sharing your thoughts on our company after joining it! There are more and more new members at KINTO Technologies every day! Stay tuned for more blog entries about joining the company, featuring perspectives from people across various departments in the future! KINTO Technologies is looking for people to work with us! For more information, please see the recruitment information . https://www.kinto-technologies.com/recruit/
この記事は KINTOテクノロジーズアドベントカレンダー2024 の2日目の記事です🎅🎄 はじめに こんにちは!KTCでAndroidエンジニアをしている 長谷川 です! 本記事ではAndroid開発において、Applicationクラスでやりがちなミスとその対処法の一例を紹介します。 Applicationクラスとは Androidにおける Applicationクラス とは公式ドキュメントを参考に、以下の説明ができそうです。 「Base class for maintaining global application state. It is instantiated before any other class for your application/package is created.」 つまりグローバルで状態を管理できること、他のどのクラスよりも先にインスタンス化されるということです。 プロジェクトによって色々な実装をしているケースがあると思いますが、一般的には以下のようにアプリ内で使用するライブラリの初期化をしたり、DIの設定を行うことが多いと思います。 class MyApplication: Application() { override fun onCreate() { super.onCreate() // ライブラリ初期化 // DIの設定 } } もしここでアプリケーション起動時にサーバーからデータが欲しくて、API通信をした場合どうなるでしょうか? class MyApplication: Application() { override fun onCreate() { super.onCreate() // ライブラリ初期化 // DIの設定 // APIコール } } 少なくとも私はこのようなコードを何回か見たことがあります。 このコードはすぐには問題にならないですが、将来的に問題を引き起こす可能性があります。 本記事ではどのような場合に、このコードが問題になりうるか、説明します。 4つのアプリコンポーネントとApplicationクラスの関係 ApplicationクラスでAPIコールを行うと何が問題になるかを説明するためには、 Androidの4つのアプリコンポーネント についての理解が必要です。 下記の画像は4つのアプリコンポーネントと、それぞれのコンポーネントでよく使用される機能を表しています。 Activityは主にアプリの画面の責務を持ち、最も使用されると思います。 また通知の機能を持つアプリはServiceを使用することが多いと思います。加えてWidgetの機能を持つアプリではBroadcast Receiverを利用することになると思います。Content Providerを使ったことがある方は少ないかもしれないですが、自アプリのデータを他アプリに公開したい場合などに使用できます。 注意して欲しいことは、これらのコンポーネントのどれかが動いている場合、Applicationクラスがインスタンス化されているということです。特にActivity以外のコンポーネントはユーザーが明示的にアプリを開いていないことがあります。 例えば、Widgetを持つアプリの場合、端末の再起動などでウィジェットが作成されますが、この時にApplicationクラスはインスタンス化される可能性があります。 もしApplicationクラスにAPIコールが記述されている場合、このタイミングでユーザーはアプリを開いていなくても(そしてほとんどの場合、開発者も意図しないタイミングで)APIコールが行われてしまいます。 通知の機能を持つアプリの場合、push通知が届くタイミングでApplicationクラスがインスタンス化される可能性があります。 もし複数のユーザーにまとめてpush通知を送信した場合、ほぼ同タイミングでAPIコールを行ってしまい、ある意味DDoS攻撃のような状態になるリスクがあります。 特にこの問題はユーザー数の増加など後になって発覚することもあり、知識として知っておくことが大切です。 最初にAPIコールしたい場合どうする? 対処方法はたくさんあると思うので、正解はありませんが一例を紹介します。 データは必要な時に必要な分だけ取得するべきなので、4つのコンポーネント内でそれぞれ取得しましょう。 その際に取得したデータを4つのコンポーネント内で使いまわしたい場合は、永続化をしたり、データをApplicationに保持させたり、DIでライフサイクルスコープをSingletonに設定したクラスに保持させておくことが可能です。 おわりに お疲れ様でした。短い記事ですが、今回はApplicationクラスのライフサイクルと気をつけたい実装について解説しました。 本記事ではApplicationクラスに記述されたAPIコールを例に説明しましたが、例えばアプリ起動のイベントなどをApplicationクラスで送信することもよくある間違いの1つかなと思います。 上記で説明した通り、Applicationクラスのインスタンス化は必ずしもユーザーが明示的にアプリを起動したタイミングとは一致しないためです。 ユーザーがアプリを起動したイベントであれば、Activityに記述するべきです。もしマルチアクティビティを採用しているアプリだとしても、アプリの入り口の導線を正しく把握しましょう。 本記事がどなたかの助けになれば幸いです。 ※Android ロボットは、Google が作成および提供している作品から複製または変更したものであり、 クリエイティブ・コモンズ 表示 3.0 ライセンスに記載された条件に従って使用しています。
こんにちは。 DBRE チーム所属の @hoshino です DBRE(Database Reliability Engineering)チームでは、横断組織としてデータベースに関する課題解決や、組織のアジリティとガバナンスのバランスを取るためのプラットフォーム開発などを行なっております。DBRE は比較的新しい概念で、DBRE という組織がある会社も少なく、あったとしても取り組んでいる内容や考え方が異なるような、発展途上の非常に面白い領域です。 弊社における DBRE チーム発足の背景やチームの役割については「 KTC における DBRE の必要性 」というテックブログをご覧ください。 この記事では、Amazon Aurora MySQL 2からAmazon Aurora MySQL 3への移行の際に mysqldump コマンドで発生したエラーメッセージなしで処理が終了する現象についてご紹介します。少しでも参考になれば幸いです。 エラーの原因 まずはエラーの原因について説明します。今回のエラーメッセージなしで処理が終了する現象は、Amazon Aurora MySQL 2のデータベースのトリガーに設定された照合順序が、MySQL 5系では未対応の utf8mb4_0900_ai_ci だったため、mysqldump がそれを認識できなかったことが原因で発生しました。 原因が判明するまでの調査過程と解決方法を、以下で詳しく説明させていただきます。 発生した現象 Aurora MySQL 2からデータをエクスポートするために、mysqldump コマンドを直接実行しました際に、エラーメッセージが表示されないまま終了してしまう現象が発生しました。 コマンドの実行後に exit code を確認したところ、2( Internal Error )が返されました。エラーが発生していることはわかりましたが、具体的な原因を特定できない状況です。 $ mysqldump --defaults-extra-file=/tmp/sample.cnf > sample.sql $ echo $? 2 原因調査 問題の原因を特定するために、以下を実施しました。 まず、別バージョンの mysqldump コマンドを実行した場合の挙動を確認しました。 今回、Aurora MySQL 2に対してMySQL 5.7系の mysqldump コマンドを使用しています。 $ mysqldump --version mysqldump Ver 10.13 Distrib 5.7.40, for linux-glibc2.12 (x86_64) 試しにMySQL 8系の mysqldump コマンドでエクスポートを試みました。 $ mysqldump80 --version mysqldump Ver 8.0.31 for Linux on x86_64 (MySQL Community Server - GPL) $ mysqldump80 --defaults-extra-file=/tmp/sample.cnf > sample.sql $ echo $? 0 結果は成功でした。このことから、MySQL のバージョン差異がエラーの原因である可能性が浮上しました。 さらに、mysqldump コマンド自体が内部でエラーを起こしている可能性を考え、さまざまなオプションを試してエラーメッセージの有無を確認しました。その結果、 --skip-triggers オプションを付与するとエラーが発生しないことが判明しました。 $ mysqldump --defaults-extra-file=/tmp/sample.cnf --skip-triggers > sample.sql $ echo $? 0 この結果から、トリガーに関連する部分でエラーが起きていると推測されます。そこで、トリガーの設定を確認しました。 mysql> SHOW TRIGGERS FROM sample_database \G *************************** 1. row *************************** Trigger: sample_trigger Event: UPDATE Table: sample_table Statement: BEGIN SET NEW.`lock_version` = OLD.`lock_version` + 1; END Timing: BEFORE Created: 2024-10-04 01:06:38.17 sql_mode: STRICT_TRANS_TABLES Definer: sample-user@% character_set_client: utf8mb4 collation_connection: utf8mb4_general_ci Database Collation: utf8mb4_0900_ai_ci *************************** 2. row *************************** (以下省略) ここで、データベースの照合順序(Collation)が utf8mb4_0900_ai_ci になっていることに気付きました。これは MySQL 5系では認識されない照合順序です。 エラーが発生していたテーブルのトリガー定義を utf8mb4_general_ci に修正し、再度 mysqldump コマンドを実行しました。 mysql> SHOW TRIGGERS FROM kinto_terms_tool \G *************************** 1. row *************************** Trigger: sample_trigger Event: UPDATE Table: sample_table Statement: BEGIN SET NEW.`lock_version` = OLD.`lock_version` + 1; END Timing: BEFORE Created: 2024-10-04 01:06:38.17 sql_mode: STRICT_TRANS_TABLES Definer: sample-user@% character_set_client: utf8mb4 collation_connection: utf8mb4_general_ci Database Collation: utf8mb4_general_ci *************************** 2. row *************************** (以下省略) $ mysqldump --defaults-extra-file=/tmp/sample.cnf > sample.sql $ echo $? 0 mysqldump が成功しました。MySQL 8系のコマンドで成功していた理由も、この照合順序の違いで説明できます。 この調査により、トリガーに設定されていたデータベースの照合順序が MySQL 5系では存在しない utf8mb4_0900_ai_ci だったため、mysqldump が失敗していたことが判明しました。 Amazon Aurora MySQL 2 と MySQL 5.7 の関係について Amazon Aurora MySQL 2は MySQL 5.7 をベースに構築されていますが、完全に同一というわけではありません。AWS は Aurora に独自の拡張機能を実装しており、その中には MySQL 8.0 の一部機能(今回問題となった utf8mb4_0900_ai_ci 照合順序など)も含まれています。 MySQL 5.7 にて utf8mb4_0900_ai_ci を照合順序に指定しようとすると、以下のエラーが発生します。 mysql> ALTER DATABASE sample_database CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; ERROR 1273 (HY000): Unknown collation: 'utf8mb4_0900_ai_ci' 一方、Aurora MySQL 2では同じコマンドが正常に実行されます。 mysql> ALTER DATABASE sample_database CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; Query OK, 1 row affected (0.03 sec) mysql> SHOW CREATE DATABASE sample_database; +------------------+---------------------------------------------------------------------------------------------------------+ | Database | Create Database | +------------------+---------------------------------------------------------------------------------------------------------+ | sample_database | CREATE DATABASE `sample_database` /*!40100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci */ | +------------------+---------------------------------------------------------------------------------------------------------+ 1 row in set (0.00 sec) 追加の調査 エラーの原因がトリガーのみなのか検証するために、他の MySQL のオブジェクトも調査します。 Aurora MySQL 2の環境でビュー(VIEW)の照合順序(Collation)を utf8mb4_0900_ai_ci で作成し、そのダンプ時の挙動を確認しました。 CREATE VIEW customer_view AS SELECT customer_name COLLATE utf8mb4_0900_ai_ci AS sorted_name, address FROM customers; mysqldump コマンドを実行すると、エラーなく成功します。 $ mysqldump --defaults-extra-file=/tmp/sample.cnf > sample.sql $ echo $? 0 次に、Aurora MySQL 2の環境でストアドプロシージャ(PROCEDURE)の場合も同様に確認しました。 DELIMITER // CREATE PROCEDURE sample_procedure() BEGIN DECLARE customer_name VARCHAR(255); -- 照合順序を指定した文字列操作 SET customer_name = (SELECT name COLLATE utf8mb4_0900_ai_ci FROM customers WHERE id = 1); -- 照合順序を使用した比較 IF customer_name COLLATE utf8mb4_0900_ai_ci = 'sample' THEN SELECT 'Match found!'; ELSE SELECT 'No match.'; END IF; END // DELIMITER ; こちらも問題なくダンプが成功します。 $ mysqldump --defaults-extra-file=/tmp/sample.cnf > sample.sql $ echo $? 0 Aurora MySQL 2では、MySQL 5系には存在しない照合順序(Collation) utf8mb4_0900_ai_ci を使用できます。 しかし、mysqldump コマンドが MySQL 5系 ベースの場合、この照合順序を認識できず、特にトリガーに関連する部分でエラーが発生することがわかりました。 ビューやストアドプロシージャでは問題が生じないことから、トリガーにおける照合順序の扱いが原因であると推測されます。 解決方法 今回の問題は、トリガーに設定されていたデータベースの照合順序がMySQL 5系では未対応の utf8mb4_0900_ai_ci であったために発生していました。 エラーへの対応としてはデータベースの照合順序(Collation)を utf8mb4_general_ci に変更し、トリガーを再設定しました。これにより、MySQL 5.7系の mysqldump コマンドでも照合順序を正しく認識できるようになり、エクスポートが正常に行えるようになりました。 ALTER DATABASE sample_database CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; -- 必要に応じてトリガーを再作成 別の解決策として、MySQL 8.0系の mysqldump コマンドを使用する方法もあります。MySQL 8.0 系のクライアントは utf8mb4_0900_ai_ci を認識できるため、データベースの照合順序を変更せずにエクスポートが可能です。 $ mysqldump80 --defaults-extra-file=/tmp/sample.cnf > sample.sql $ echo $? 0 環境や他の依存関係の制約からクライアントのバージョンを簡単に変更できない場合もあります。 おわりに 今回の事例では、mysqldump コマンドがエラーメッセージを表示しないまま終了し、exit code を確認することで初めてエラーが発生していることに気付きました。このようなエラーメッセージなしで処理が終了する現象は、気付かないまま不完全なデータをエクスポート・インポートしてしまうリスクがあります。そのため、データベースのバックアップや移行をする際には、exit code を確認するなど、処理結果をチェックすることが重要です。 Aurora MySQL 2系はすでに EOL(End of Life)を迎えており、サポートが終了しています。まだ稼働している環境があり、移行を検討していることがあればお気をつけください。
この記事は KINTOテクノロジーズアドベントカレンダー2024 の2日目の記事です🎅🎄 はじめに(活動の背景紹介) こんにちは。学びの道の駅チームの「きんちゃん」です。普段はコーポレートエンジニアとして「全社で利用するITシステムの維持管理」を行っています。 最近は「生成AI活用プロジェクト」や、「技術広報グループ」という組織にも所属し、色々と活動しています。 さて、 以前のテックブログ で「学びの道の駅」の成り立ちについてご紹介しました。 その中に「社内Podcast」の活動について記載していました。今回の記事では、このPodcastへの取り組みについて詳しくご紹介します。 このPodcastの取り組みは、学びの道の駅メンバーであるHOKAさんのこんな想いから始まりました。 社内の色々な取り組みを、Podcastで発信していきたい! とてもシンプルなモチベーションだったことに加えて、基本的に「Noを言わないメンバー」が集まっていたこともあり、学びの道の駅チームの活動として取り組んでみることになりました。 もちろん、学びの道の駅チームの取り組みなので、「学びのための情報発信」とうまく組み合わせる必要があります。 あれこれ議論の上で、以下の形にまとまりました。 社内で勉強会を主催・運営・参加している人にインタビューをする。その内容を配信する ねらいは、以下の通りです。 「社内に多くの勉強会があるけど、どんな人がどんな想いで開催しているんだろう?」という疑問に答えられる 実際に参加した人のナマの声を聞いて、価値を感じてもらえる もし興味を持ってくれる人がいたら、勉強会参加のきっかけにできる 色々な勉強会の存在を見える化(聴こえる化)していくと、社内に「学びの文化」が根付いていく 「Podcastって何だっけ?」 Podcastをやろう!と決まったものの、「何をどうやるとPodcastになるんだっけ?」という疑問が出てきます。 インターネットラジオの形で公開するもの?(社外の人も聴くのかな?) 専用のアプリで配信しなければならないもの?(配信する側も聴く側も準備の手間が…) 設備とか、どういうものが必要なんだっけ? 考えれば考えるほど色々な壁が出てきます。 とは言え、我々はシンプルに「社内の情報を社内に届ける」ことを価値としたいので、「まずは出来る仕組みでプロトタイプを作って手触り感を試す」「その後のフィードバックを受け、カイゼンしていく」という「アジャイルなマインド」で進めることとしました。 さぁ、最初のインタビューだ! そうと決まれば、インタビューすべき勉強会の選定です! ちょうど良いタイミングで、社内で「合同勉強会」という大型の勉強会が開催される予定があったため、初回はその勉強会へと参加し、運営チームの皆さんへインタビューを実施する事となりました。 しかし、参加当日にバタバタしてしまい、当日の録音はできず…。 結果、代替案として「後日、運営メンバーの皆さまに集まってもらい、インタビューする形式」を取る事としました。(実は、この代替案が今後のPodcastのカタになっていくのでした) 実際のインタビューの場はとても盛り上がり、無事に収録が完了できました。 しかし、そこで新たな難関が! 実は、「どのようにPodcastコンテンツを作るか?」「どうやって配信するか?」をまったく決めないまま、ここまで来ていたのです。 チーム内で議論しながら試行錯誤の結果、以下のような形に落ち着きました 録音したデータの加工 Clipchamp(Microsoft365ファミリー製品)を利用し、音声メインの動画ファイルとして仕上げる 配信方法 社内のSharepointにファイルを置いて、それを皆さんにPCやスマホ上で開いて再生してもらう 最終的なPodcast公開フローは、 コンテンツを完成させる 学びの道の駅チーム内レビューを実施 インタビュイーのレビューを実施 上長レビューを実施(初回実施した上で、大きな懸念がなければ2回目以降は我々に判断を移譲していただく) 社内アナウンス(=公開) となりました。あくまでも社内向けコンテンツという事を前提に、シンプルなフローを整備しました。 その後、すべてのチェックが通った上で、晴れて社内に公開できました! ![](/assets/blog/authors/ktc-taku-yajima/2024-12-02/started-a-podcast001.png =700x) その後のPodcast 初回のPodcast公開で自信を付けた我々は、2回目、3回目の企画を進めていきます。 インタビューを繰り返すたびに、我々の中に知見が貯まっていき、一定のカタ化ができるようになりました。 企画のカタ 社内の勉強会や、情報発信の活動の情報を集める 学びの道の駅チームから運営メンバーへアプローチをして、インタビューを実施する インタビューした結果をPodcast化する 運営チーム、インタビュイーのレビューを経た後に、公開(社内アナウンス)する 録音・編集・配信のカタ インタビューはZoomで行う(メンバーがそもそも多拠点に散らばっているため) マイクは各人のPCマイク、会議室のマイクを利用する。音質はいったん妥協する Zoomの録画データを音源とする 音源データはClipchampを利用して編集・加工する 音源データを編集後、社内のストレージ(MS SharePoint)に保存する 視聴者はMS Stream経由で配信を聴く このカタ化ができたことで、スムーズに仕組みが回るようになり、現在までで11本のコンテンツを配信できました。 今後について このPodcast配信活動を続ける中で、我々の中に「もっとこういう拡がりを作っていきたい」という気持ちが湧いてくるようになりました。 勉強会以外にも、色々な形で社内活動をされている方々にインタビューしたい マネジメント層の方々にインタビューして、普段考えている事を社内の皆さんに聞いてもらいたい 社内に限らないアウトプットをしていきたい 来年以降も、学びの道の駅チームは積極的に情報発信していきますので、皆様お楽しみに!
この記事は KINTOテクノロジーズアドベントカレンダー2024 の2日目の記事です🎅🎄 はじめに こんにちは! 新車サブスク開発G、Osaka Tech Lab 所属の high-g( @high_g_engineer )です。 最近は、type-challenges という TypeScript の型パズル問題集を業務前に取り組むことを日課にしています。 本記事では、TypeScript の型システムの中でも少し癖のある infer の Tips をいくつか紹介します。 まずは、infer の解説の前に、infer を利用する上で必須の Conditional Types について説明します。 Conditional Types とは Conditional Types は、条件型、型の条件分岐とも言われ、型レベルで条件分岐を可能にする型システムの機能です。 以下のように記述します。 type ConditionalTest<A> = A extends 'a' ? true : false 上記の右辺は、以下のような意味になります。 型 A が リテラル型 'a' に代入可能な場合、true 型となる 型 A が上記以外の場合、false 型となる 一般的なプログラミング言語の三項演算子と同じような挙動ですね。 ちなみに、ここでの extends キーワードは、一般的なオブジェクト指向プログラミングにおける継承とは異なる意味を持ちます。 この場合の extends は型の互換性(assignability)を確認するキーワードになります。 infer とは inferとは、「推論する」を意味し、Conditional Types 内でのみ利用できるキーワードで、TypeScript 2.8 から導入されました。 以下のように記述します。 type InferTest<T> = T extends (infer U)[] ? U : never 右辺では、Conditional Types を利用し、extends の右側で取得したい型を infer ◯ で記述します。 上記の型の場合、型 T が「任意の要素型の配列」であれば、その要素の型を返すという意味になります。 (※ここでの never は、条件に合致しない場合に返される型です) (infer U)[] は、任意の要素型の配列を表す型なので、string[]、number[]、boolean[] などあらゆる配列型が該当します。 なので、型 T が number[] だった場合、型の解決は以下のようになります。 type Result = InferTest<number[]> // number あくまでここに示したのは一例で、これ以外にも infer の利用方法は多数存在します。 infer を利用した関数型の操作 戻り値の型を取得する場合 const foo = (): string => 'Hello, TS!!' type MyReturnType<T> = T extends (...args: any[]) => infer R ? R : never type FunctionReturn = MyReturnType<typeof foo> // string 先程と同じ要領で Conditional Types を記述し、extends の右側で、取得したい型を記述します。 今回は、戻り値の型を取得したいので、extends の右側に関数の型を記述し、戻り値の部分に infer を記述して完了です。 これは TypeScript の組み込みユーティリティ型 ReturnType<T> と同じ挙動になります。 引数の型を取得する場合 const foo = (arg1: string, arg2: number): void => {} type MyParameters<T> = T extends (...args: infer Arg) => any ? Arg : never type FunctionParamsType = MyParameters<typeof foo> // [arg1: string, arg2: number] 引数はタプル型になるため、残余引数(スプレッド構文)を利用することで、引数が任意の数の場合でも対応できるようになります。 Conditional Types + 取得したい型 + infer を記述することで型を抽出できます。 これは TypeScript の組み込みユーティリティ型 Parameters<T> と同じ挙動になります。 infer を利用した配列型(タプル型)の操作 末尾の要素を取得したい場合 タプル型の先頭要素の型を取得する場合、以下のような型定義で解決します。 type Tuple = [number, '1', 100] type GetType = Tuple[0] // number しかし、タプル型の末尾の要素の型を取得したい場合、 Tuple[length-1] の様な記述は TypeScript では出来ません。 この解決方法として、ベストなのが infer です。以下のような型定義になります。 type ArrayLast<T> = T extends [...infer _, infer Last] ? Last : never [...infer _, infer Last] で、型 T が配列型またはタプル型の場合に、末尾の要素の型を Last として抽出します。 type Test1 = ArrayLast<[1, 2, 3]> // 3 type Test2 = ArrayLast<[string, number, boolean]> // boolean type Test3 = ArrayLast<[]> // never infer を利用したリテラル型の操作 リテラル型の先頭の文字の型を取得する場合 type LiteralFirst<T extends string> = T extends `${infer First}${string}` ? First : never ${infer First}${string} は、文字列の先頭の文字を First として抽出し、残りの部分を string として扱います。 リテラル型の先頭を大文字にして取得する場合 type FirstToUpper<T extends string> = T extends `${infer First}${infer Rest}` ? `${Uppercase<First>}${Rest}` : never 上記は先程と同じ様に、先頭とそれ以外に分けて文字列を処理し、 Uppercase<First> でユーティリティ型を使用して、先頭の文字を大文字に変換し、残りの文字列と結合します。文字列が空の場合は never 型を返します。 リテラル型の先頭と末尾から空白文字、改行文字などを取り除く場合 type Space = ' ' | '\n' | '\t' type Trim<S extends string> = S extends `${Space}${infer T}` | `${infer T}${Space}` ? Trim<T> : S; Space という空白文字、改行文字を格納した型を作成し、Conditional Types の条件に当てはまる場合、型 Trim を再帰的に適用することで、文字列の先頭と末尾の空白文字を取り除くことができます。 まとめ これらの例のように、infer を利用することで、ある型から欲しい部分を抜き出せるため、型を表現する際の自由度が格段に上がります。 少し癖があるため、慣れるまでに時間がかかりますが、非常に便利な機能です。 型を利用することで堅牢な開発が実現できますが、型を正しく表現しきれないと以下のようなリスクが生じます。 不要な型定義によるコード可読性の低下 不必要に複雑な型定義によるメンテナンスコストの増大 型安全性の低下 infer をはじめとした TypeScript の型システムを適切に活用することで、簡潔かつ明確な型表現を実現し、開発生産性と品質向上を常に心がけられるようになっていきましょう。
Introduction Hello, I am Ueyama, a new member who joined the company in April. In this article, I've compiled the reflections and thoughts of everyone who started with me in April 2024. I hope that it will be useful for everyone who is interested in KINTO Technologies, and for everyone who took part in this article if they look back at it someday. 🌸 Matsuno ![Golf](/assets/blog/authors/K.Ueyama/Newcomers/golf.jpg =250x) Self-introduction Nice to meet you, everyone! I am Matsuno, a new member who joined the company in April 2024! I belong to the MSP Team in the Platform Development Division’s Platform Group. In my previous job, I was responsible for maintaining and operating systems built on AWS. How is your team structured? The MSP Team I belong to consists of four people. I am mainly responsible for routine work we have taken over from other teams. What was your first impression of KTC when you joined it? Were there any surprises? I got the impression that there are lots people who seem to be outstanding, including the people who joined when I did. Also, finding there were lots of frank people here was a surprise in a good sense. What is the atmosphere like on-site? Basically, the atmosphere makes it easy to ask questions and consult people anytime. In addition, when people are working, they concentrate silently on what they are doing, and when they are chatting, they are very friendly. So in that sense, it is nice and balanced. How did you feel about writing a blog post? I already knew about the Tech Blog and was interested in it as well, so I thought it was a perfect opportunity! m ![Sea](/assets/blog/authors/K.Ueyama/Newcomers/sea.jpg =250x) Self-introduction I am m from the Creative Office. In my previous job, I was a UI/UX designer at an SES IT company. How is your team structured? There are 10 directors and designers. What was your first impression of KTC when you joined it? Were there any surprises? I was struck by how clean the office was, and how comfortable, too, since it even has a free drink server. What is the atmosphere like on-site? The age group is mostly 30s to 40s, and everyone is extremely knowledgeable and experienced. The office is often surprisingly bustling. How did you feel about writing a blog post? I think it is good that there is a forum where we can share our own thoughts and knowledge! Rassel ![Castle](/assets/blog/authors/K.Ueyama/Newcomers/castle.png =250x) Self-introduction I am Rassel from Bangladesh, and I joined the company in April 2024. I am an iOS developer in the Prism Team in the Platform Development Division’s Mobile App Development Group. How is your team structured? The team consists of around 14 people, and includes engineers, designers, and POs. What was your first impression of KTC when you joined it? Were there any surprises? I am interested in mobility services. I was very impressed by KTC’s mission of leading Toyota’s mobility services. There was nothing in particular that was a surprise for me. What is the atmosphere like on-site? The people are kind and helpful. There are no barriers to using the latest technology. It is also easy to talk about technical problems. How did you feel about writing a blog post? This is the first time I have written a blog post in this context, but I think it is a really cool and fun idea. Ueyama ![Pasta](/assets/blog/authors/K.Ueyama/Newcomers/pasta.jpg =250x) Self-introduction I am Ueyama from the Work System Group. In my previous job, I did system development at an SIer. How is your team structured? Our team includes seven engineers. What was your first impression of KTC when you joined it? Were there any surprises? I was able to talk to the same team members as in my consultation and interview, so I did not feel all that surprised by anything. What is the atmosphere like on-site? It is an environment where everyone is really kind and easy to talk to. How did you feel about writing a blog post? The format of managing self-introduction articles on GitHub and publishing them with pull requests was a surprise. R ![Catandfish](/assets/blog/authors/K.Ueyama/Newcomers/catfish.jpg =250x) Self-introduction I am R, and I belong to the Member Platform Team in the Development Division’s Common Services Development Group. The ratio of front-end to back-end development is around 6:4. How is your team structured? There is one product manager (PdM) and four engineers. What was your first impression of KTC when you joined it? Were there any surprises? Seeing up close outstanding people who are involved in multiple projects, events, and so on both inside and outside the company, I got the impression that it is a very free environment. Before I joined KTC, I had attended a study group held by it, and although most of the people taking part were young, I got to know a bit about its atmosphere beforehand. So, There was nothing in particular that was a surprise for me. What is the atmosphere like on-site? The back-end work is done in silence, and with the front-end work, things sometimes get very lively, with everyone talking about their views, impressions, and so on about a screen with it being implemented on. How did you feel about writing a blog post? I didn't have any worries when reading it, but when it came to writing it myself, I was troubled because I didn't know what to say. It looks like I need to hone my verbalization and communication skills. kasai ![Chick illustration](/assets/blog/authors/K.Ueyama/Newcomers/chickicon.png =250x) Self-introduction I am kasai from the SRE Team in the Platform Development Division’s Platform Group. I was an SRE in my previous job as well. How is your team structured? There are lots of people in the group, but the SRE Team consists of just two! An article about the team is going to be posted on the blog at a later date, so stay tuned for that! What was your first impression of KTC when you joined it? Were there any surprises? I got to thoroughly talk things over and get on the same page as the company in my consultation, interview, and so on, so I did not get any surprises! What is the atmosphere like on-site? Very friendly indeed! How did you feel about writing a blog post? At last...my time...has come! https://blog.kinto-technologies.com/posts/2022-12-03-ktc_club_introduction/ Closing words Thank you everyone for sharing your thoughts on our company after joining it! There are more and more new members at KINTO Technologies every day! Please look forward to more company-joining blog entries by various people from various departments in the future, too. 🍻 KINTO Technologies is looking for people to work with us! For more information, please see the recruitment information . https://www.kinto-technologies.com/recruit/
Introducing the Team and Its Work Our team is the Woven Payment Solution Development Group. However, before I tell you about our team, I need you to know a bit about Woven City . Woven City is both a test course for mobility and a city for conducting demonstration experiments, where Toyota Motor Corporation is developing technologies with the aim of “mass-producing happiness.” The development of Woven City is led by Woven Planet Holdings, a member of the Toyota Group. Our team is working with members of Woven Planet’s Payment Solution team and other teams to develop payment services to be used in Woven City. KINTO Technologies and Woven Planet are separate companies, but when it comes to the development work, we are working as one team without particularly being conscious of that. Woven City is a mobility test course for inventing the commonplace things of the future, and we are also required to develop features that will contribute to that. In Woven City, all the residents and other people involved are either inventors who create new value in some form or other, or people who create inventions together with them. That means those inventors must be provided with the features and data that they need to create better products, services, and the like. This is a big difference between general payment services and the ones we make. For example, for how a given service accepts payments from users, we are thinking about UX aspects such as making it easier to consider things like whether the best option is prepaid, postpaid, recurring payment, or some other method. We are also looking at the data provision aspects, and thinking about providing not just the payment information but that combined with other Woven City data, in a form that will enable things to be considered from multiple angles. Of course, these kinds of data are not only provided to inventors, but also used to kaizen the system that is Woven City itself. (Of course, we never obtain people’s personal information without their consent.) How We Work The Payment Solution team, including the members from Woven Planet, is doing the development work remotely from home. However, we come to the office once a week on Wednesday, giving us an opportunity to communicate directly with each other. We use Google Meet and Slack for our communication tools. In Woven Planet, the basic premise is that communication is in English, so we converse in that, especially if there are non-Japanese speakers present. Also, on Slack and in documents, communication is done in English even when it is between Japanese people. We also use English for postings that are like talking to ourselves, so if we write about something we are concerned about, it sometimes leads to getting advice from other teams or sparks a discussion. Besides that, other teams also sometimes consult us, and sometimes, that can lead straight into starting up an oral chat via Huddle, for example. So, there is lively communication even though we are working remotely. When we come to the office, we enjoy the stimulation you can get from actually meeting people in person, such as discussing things face-to-face and eating the company food together if our lunchtimes coincide. Our development work also gives us opportunities to visit the planned site for building Woven City. Woven City itself is currently under construction, so it looks different depending on when we visit, and although there are still many vacant areas, it is a useful experience for expanding our mental image of it actually being used. Technologies We Are Using Programming language Kotlin Our team’s scope is server-side applications, and we use Kotlin to develop them. A major reason why we chose Kotlin is its high interoperability with Java. There are already a great many Java engineers in KINTO Technologies and the group, and we are hoping this will induce them to join our team quickly. I myself have experience of using Java in the past, and had a go at building simple web applications with Kotlin several times as part of introducing it. Through this, I became able to code in it about as smoothly as in Java and with no real problems just by referring to the official documentation a bit. Some of the members have experience of server-side development in other languages like Go, C#, and Ruby but none in terms of Java itself, but they can all use Kotlin with no real problems now, too. Initially, we used Kotlin as a better version of Java, but now, we are starting to forget about Java and consciously use Kotlin for its own merits. Additionally, we use Gradle for project management and write the configuration files in Kotlin Script. The main libraries we are using Ktor, Koin, Exposed, Kotest, MockK, etc. Our services consist of several application services, many of which are web services with a REST API. You can use Spring MVC and Spring Boot with Kotlin as well, but we chose Ktor instead. We chose not to use Spring because we wanted to avoid code that is heavily annotated and feels like magic. However, we still wanted to use a dependency injection mechanism for testing and dependency management, so we adopted Koin as our DI library. Koin also has an annotation-based configuration method like Spring, but it has a DSL-based configuration method as well, and we are using that one. Although it requires knowledge of Koin and of DSL itself, DSL can be incorporated into normal Kotlin code more naturally than annotations, so I feel you can express your intentions clearly in it. In addition, we use various FOSS libraries, such as Exposed for ORM and Kotest and MockK for testing. Ktor official website Koin official website Exposed GitHub repository Kotest official website MockK official website This is more of a personal interest than a team one, but since Ktor also works in GraalVM , I would like to try doing a native build with GraalVM if I get the chance. Reference: https://ktor.io/docs/graalvm.html#prepare-for-graalvm Application Infrastructure Kubernetes The applications we are developing are deployed on Kubernetes, and for the services we are developing ourselves, we write the configuration files ourselves in YAML. Another team is in charge of the development and operation of the Kubernetes environment as a whole, but this team has also prepared a CD mechanism. Basically, if we turn the new configuration data into PRs then review and merge them on GitHub, everything right up to deploying the apps gets done automatically. The team also responds to requests from us about configuration, architecture, and so on, so they are always a tremendous help. How We Do Development Because of the project’s unprecedented nature of creating a city that functions as a test course, there are many unknowns not just in the parts that our team is working on but in the whole thing, too. So, using as a foothold the features that will be required as a matter of course, we are coming up with rough hypotheses and development plans, and are learning about our own services ourselves as we create them, and about Woven City, too. Specifically, the process we are following is to set a big theme for each quarter, add or update features as needed for that, then check them by running small demos in the office. For this reason, we adopt an agile development method, we do the development iteratively with two weeks as the timeboxes, while managing the backlog with Jira. We are not following strict Scrum practices, but are seeking to adapt to the facts that come to light and changes in requirements that arise along the way as we develop things. Conclusion What is difficult about this project is that, at this point in time, the real test course that is Woven City still does not exist, and of course, the users are only potential ones, too. This goes for all teams and not just ours. It often happens that another team wants to use a feature that is still in development, but it is still unstable. On the other hand, you could also say that since there are no general users, it does not matter (at least now) if things are unstable to a certain extent. While being in this unstable state can be taken as grounds for not using each other’s creations, it can also be relished as an opportunity to improve them by respectfully but frankly pointing out their defects, all in the spirit of forging and honing them together through use. We definitely want people who think in the latter way to take part in our project.
こんにちは、ヒロヤ (@___TRAsh) です🎅 今年のモバイルアプリ開発グループはアウトプットに力を入れた年でした。 iOSDCやDroidKaigiでのスポンサードや外部登壇、このテックブログの執筆など、様々な形でアウトプットを行っていて、みなさんにも知っていただける機会が増えたかなと感じています。 今年の最後の締めくくりとして、 KINTO TechnologiesのAdvent CalendarでAndroid/Flutter/iOSで1シリーズ投稿します🎉 https://qiita.com/advent-calendar/2024/kinto-technologies 弊社のAndroid/Flutter/iOSエンジニアが頑張って1シリーズ書き切るので、ぜひチェックしてみてください🎅 本日はそんなAndroid/Flutter/iOSのアプリ開発をしているモバイルアプリ開発グループのことを紹介させていただきます。 モバイルアプリ開発グループとは? 弊社KINTOテクノロジーズのモバイルアプリを、iOS、Android、Flutterで横断的に開発しているグループです。 主には以下のプロダクトの開発をしています。 https://kinto-jp.com/entry_app/ https://kinto-jp.com/unlimited/app/ https://top.myroute.fun/ https://ppap.kinto-jp.com/prismjapan 上記以外にもPoCの要望などを受けた開発も行っています。 また、業務以外にも横軸組織という利点を活かし、社内勉強会も頻繁に行っており、新しい技術や知識の共有を積極的に行っています。 メンバーのみんなにアンケートを取りました 今回は弊社のエンジニアにアンケートを取ってみました。 普段の業務ではなかなか知ることができない情報を取得できたので、ここで共有させていただきます。 1. あなたの開発環境は?(最大2つまで) ![開発環境円グラフ](/assets/blog/authors/HiroyaHinomori/mobile_advent_calendar_2024_12_01_01.png =450x) Androidの割合が多いのは、弊社はAndroidエンジニアが多いことが要因ですね。 国内では結構珍しいんじゃないかと思います。 また、今年からFlutterチームができました!少しづづFlutterに関するアウトプットも出していければと思います。 2. 開発年数を教えてください ![開発年数円グラフ](/assets/blog/authors/HiroyaHinomori/mobile_advent_calendar_2024_12_01_02.png =450x) 弊社は中途採用がメインなので、開発年数が長い方が多いですね。 10年以上の方がこんなにいるのは今回初めて知りました。 これからも経験豊富なエンジニアの方々から学び続けていきたいです。 3. 出身地域を教えてください ![出身地域円グラフ](/assets/blog/authors/HiroyaHinomori/mobile_advent_calendar_2024_12_01_03.png =450x) 薄々気づいていたんですが、日本人が半分も居ないのはかなり珍しい現場じゃないかと思います。 現場では基本的にみなさん日本語で話していますが、ところにより英語や、中国語、韓国語が話されるグローバルな環境です。 4. モバイルアプリ開発グループの良いところがあれば教えてください 頂いたコメントを元にワードクラウドを作成しました。 雰囲気と技術が目立ちますね。 日々の業務だけでは疎かになりがちなプロダクト間のコミュニケーションを大切にして技術共有を行なっている成果かなと感じます。 この調子で来年も頑張っていきます💪 :::details Summary 技術と学習環境 最新技術への挑戦:自由度が高く、新しい技術の導入や利用に対してオープンな環境。 スキル向上の支援:学びやすい環境で、勉強会や知識共有が積極的に行われている。 スキルレベルの高さ:メンバー全体の技術レベルが高く、成長意欲が強い。 アウトプットを重視:成果を出す努力を惜しまない姿勢がある。 コミュニケーションと雰囲気 親しみやすい雰囲気:メンバー同士が親切で、質問や相談がしやすい。 協力的なチーム:プロジェクトチーム内外での連携がスムーズで、協力し合える風土。 多様性とユーモア:多国籍で個性豊かなメンバーが集まり、文化の違いも楽しめる環境。 上下関係の壁が少ない:年齢やバックグラウンドに関わらずフラットなコミュニケーション。 働きやすさ 柔軟で自由な働き方:自由奔放で、それぞれのスタイルを尊重。 良好なチームの雰囲気:みんな仲が良く、協力し合う文化が根付いている。 優しい雰囲気:親しみやすく、安心して働ける環境が整っている。 これらの特徴から、学びながら成長し、多様性と協調性を楽しめる理想的なチーム環境と言えます。 ::: 5. 今最も関心のある技術を教えてください iOS Android こちらもいただいたコメントを元にワードクラウドを作成しました。 Android、iOSともにKMPが注目されていますね。FlutterやCompose Multiplatformなどのワードも見えるので、クロスプラットフォームに関心がある方が多いようです。 僕の体感としても今年はクロスプラットフォームが躍進してきたなと感じます。 あとはそれぞれ、言語の技術的な進化にも関心があるようです。 また、AI周りの技術も注目されているのは昨今のトレンドを反映していて、メンバーの技術関心が高いことがわかります。 :::details Summary iOS トップ3技術 Swift / SwiftUI Appleプラットフォームの主要技術で、特にUI構築への関心が高い。 KMP(Kotlin Multiplatform) マルチプラットフォーム開発でiOS側にも活用。 AI(MLC LLM、Apple Intelligence) 機械学習やAppleのAI技術への注目。 Android トップ3技術 Jetpack Compose UI構築技術の中心。効率的なコード記述や熟練に向けた研究が活発。 KMP(Kotlin Multiplatform) Androidアプリ開発での活用やCompose Multiplatformとの連携が注目される。 Flutter クロスプラットフォーム開発の選択肢として人気。 ::: まとめ モバイルアプリ開発グループは技術と学習環境、コミュニケーションと雰囲気、働きやすさ、これらの観点から学びながら成長し、多様性と協調性を楽しめる環境を作っていけているかなと感じます。 モバイルの技術は日々トレンドが変化する業界でもあるので、これからもトレンドのキャッチアップを行いながら、グループでの成長を目指していきたいです。 最後に、この記事を読んでいただいた方々にも、モバイルアプリ開発グループの魅力を感じていただけたら幸いです🎅 それでは、明日からの弊社のAdvent Calendarもお楽しみに🎄
この記事は KINTOテクノロジーズアドベントカレンダー2024 の 1 日目の記事です🎅🎄 はじめに こんにちは、KINTO テクノロジーズ ( 以下、KTC ) の SCoE グループの多田です。SCoE は「Security Center of Excellence」の略で、少し耳慣れない方もいらっしゃるかもしれません。KTC では、今年の 4 月に CCoE チームを SCoE グループとして再編しました。再編の経緯については こちらのブログ にまとめてありますので、ぜひご覧ください。また、私は大阪オフィスである Osaka Tech Lab に勤務しており、Osaka Tech Lab についても こちらのブログ でご紹介していますので、ぜひ覗いてみてください。 KTC では、多くのプロダクション環境を Amazon Web Services ( 以下、AWS ) 上で運用していますが、最近では OpenAI の活用に伴い、Microsoft Azure ( 以下、Azure ) の利用も増えてきました。 SCoE のタスクのひとつに、グループポリシーに基づくセキュリティ設定を事前に実施した上で環境を提供することがあります。本ブログでは、Azure サブスクリプションを提供する際に行っているセキュリティ設定について、いくつかご紹介したいと思います。 Azure 特有の用語が登場しますので、詳細については公式サイトなども合わせてご確認ください。 Azure ランディングゾーンと管理グループの設計 セキュリティ設定を考える上で、まずはランディングゾーンと管理グループについて理解することが重要です。KTC のサブスクリプション環境は Azure ランディングゾーンの設計原則に基づいて設計・構築しています。ただし、 Microsoft の公式ランディングゾーン をそのまま使用するのではなく、ベストプラクティスを参考にしつつ、KTC の環境に合わせてライトに設計しています。 ランディングゾーン内では、サブスクリプションを論理的にまとめて効率的に管理するために、いくつかの管理グループを設計しています。以下の図はその概要です。これらの管理グループを使用し、各サブスクリプションに適切なポリシーを適用しています。 管理グループ 概要 KTC 管理グループの root、各管理グループの共通となるポリシーを適用 Management 全サブスクリプションのActivity Log の集約用のサブスクリプションなど、セキュリティ系で利用するサブスクリプションを管理 Workload ワークロード用のサブスクリプションを管理 Sandbox Sandbox用のサブスクリプションを管理 PolicyStaging Azure ポリシーのテストを行うための管理グループ、サブスクリプションを管理 ポイントとして、ワークロード用の管理グループは 1 つに統一しています。この管理グループにはプロダクト用のサブスクリプションが含まれ、1 つのサブスクリプション内で 本番・開発・ステージング環境をリソースグループ単位で分離しています。 環境分離の設計には様々なアプローチがありますが、KTC ではワークロードが多くならないこと、特定の Azure サービスに限定していること、サブスクリプション単位の費用管理が容易であることから、この形でスタートしました。将来的に Azure の利用が増えれば、再検討も視野に入れています。 Management 管理グループの役割 Management 管理グループは、全サブスクリプション共通の運用管理やセキュリティツール展開用のサブスクリプションを集約するための管理グループです。運用監視を担当するメンバーのみがアクセスできるようにしており、例えば、全サブスクリプションの Activity Log を集約・監視するサブスクリプションをここで管理しています。 Azure ポリシーを利用したセキュリティ設定 Azure ポリシー を利用することで、セキュリティやガバナンスに沿ったリソースの作成が可能で、違反があれば検出・修復もできます。KTC でも Azure ポリシーを使用し、サブスクリプション作成時に自動でセキュリティ設定を適用しています。現在はビルトインのポリシーのみを使用しており、カスタムポリシーの作成までは実施していません。今後、ワークロードが増えるなど環境が変われば検討していきたいと思います。 以下は代表的な Azure ポリシーを活用した設定例です。 Activity Log の監視と保管 Defender for CSPM の設定と利用 Azure ポリシーは、予防的ガードレールとして、KTC では過度に適用する方針を取っていません。これは、KTC のワークロードが比較的少ないことや、エンジニアのスキルレベル、運用コスト等を考慮した結果です。厳格な予防的ガードレールで制約を増やすよりも、一定の自由度をエンジニアに委ね、発見的ガードレールで検出された内容をカイゼンするアプローチをとっています。これにより、エンジニアが問題解決を通じてスキルを磨き、興味を持って成長できるよう意図しています。 Activity Log の監視と保管 各サブスクリプションの Activity Log は、Management サブスクリプションの Log Analytics ワークスペースに集約しています。サブスクリプションが新規で追加された場合でも、Azure ポリシーによって自動的に Audit Log が集約されるよう設定しています。 利用している Azure ポリシーは以下となります。 指定された Log Analytics ワークスペースにストリーミングするように Azure アクティビティログを構成します Log Analytics の保管期間はデフォルトで 90 日なので、ストレージアカウントにバックアップを保管していますが、こちらは Azure ポリシーには設定がなく、手動で行っています。カスタムポリシーを作成することで、自動設定できることは確認しているのですが、そこまでは実施していません。 Defender for CSPM の設定と利用 発見的ガードレールと呼ばれますが、Azure 環境にリスクのある設定や操作が行われた場合に、Cloud Security Posutre Management ( CSPM ) のソリューションを利用して、これらのリスクを検知します。Azure の場合、 Microsoft Defender for Cloud が CSPM として利用できます。Microsoft Defender for Cloud は、CNAPPと呼ばれるクラウドネイティブアプリケーション保護プラットフォーム ( Cloud Native Application Platfrom ) のためのソリューションであり、CSPM や Cloud Workload Protection Platform ( CWPP ) 等をカバーするセキュリティソリューションです。 Microsoft Defender for Cloud の CSPM 機能は、無料の Foundational CSPM と サーバ、データベース、ストレージなどのリソースに対して費用が発生する Defender CSPM があります。KTC の場合、より詳細な CSPM のチェックが可能な Defender CSPM を利用しています。 Defender CSPM は、以下の Azure ポリシーを利用して、サブスクリプション発行時に自動設定しています。 Microsoft Defender CSPM を有効にするように構成する 設定後は、定期的に Microsoft Defender for Cloud からアラート状況を監視し、リスクのある設定があれば、サブスクリプションを利用しているプロダクト側と連携し、リスクのカイゼンを行います。 クラウドワークロード保護については、今の時点では実施しておらず、今後、リソースが増えるなどに応じて検討していきたいと思います。 脅威検知 Azure 環境のセキュリティインシデントや不正アクセスを早期に発見するために、脅威検知の仕組みを導入しています。KTC でもそうですが、多くの AWS 導入会社であれば、Amazon GuardDuty で実現している仕組みだと思います。Azure の場合、 Microsoft Sentinel を使うことが鉄板のようですが、KTC の環境の場合、導入の手間や費用面を考慮し、サードパーティ製品の sysdig の CDR ( Cloud Detection Response ) 機能を使って実現しています。 CDR の実態は、OSS の Falco です。Falco は、ホスト、コンテナ、Kubernetes、クラウド環境全体に対して、異常な振る舞いや潜在的なセキュリティ脅威等の違反を検出し通知します。脅威検知ルールは、一般的なものが提供されており、カスタマイズやチューニングも可能で使い勝手がよいです。 KTC では、sysdig を Google Cloud 環境の CSPM や脅威検知として既に利用していたので、そのノウハウを Azure にも適用しています。 まとめ KTC では、Azure サブスクリプションを提供する際に行っているセキュリティ設定について、いくつかをご紹介しました。セキュリティを強化するために、Azure ポリシー、Microsoft Defender for Cloud や sysdig の CDR 機能を活用しています。 Azure ポリシーを利用したセキュリティ設定 に記載しましたが、予防的ガードレールをどこまで厳格にするかは、各社の状況によるものが大きいと思いますので、自社の状況にあわせて最適な設計・運用をしていただくのが良いと思います。 この内容が、Azure を利用するさいの、セキュリティ設定の参考になれば幸いです。 最後まで、読んでいただきありがとうございました。 さいごに SCoE グループでは、一緒に働いてくれる仲間を募集しています。クラウドセキュリティの実務経験がある方も、経験はないけれど興味がある方も大歓迎です。お気軽にお問い合わせください。 詳しくは、 こちらをご確認ください。
この記事は KINTOテクノロジーズアドベントカレンダー2024 の1日目の記事です🎅🎄 こんにちは!リナ( @chimrindayo )です。 KINTOテクノロジーズで、エンジニアとして モビリティマーケット の開発運用と技術広報を兼務しています。 今回は株式会社Luupの t-kurimuraさん と一緒に結成した"Mobility Night"という勉強会をご紹介します🙌 Mobility Nightとは 引用元: t-kurimuraさんの作成資料 モビリティに関連する企業や団体がソフトウェアの技術や知見について共有し、業界を盛り上げていくための勉強会です🚀 例えばGPS・IoT・品質保証・プロダクトデザインなど、モビリティを取り扱うソフトウェアの技術的な課題には共通点があると考えています。こうしたモビリティ業界ならではの知見を共有することで、モビリティ業界全体のソフトウェア技術の発展やプロダクトの全体的な向上を願って結成されました。 "Mobility Night"という命名の由来は、みんなが集まってカジュアルに情報発信と交流ができる場になって欲しいという思いを込めています。 クローズドイベントの開催 初回は、 Mobility Night#0 (第0回)と称してクローズドの勉強会を開催しました。 登壇企業である株式会社Luup、チャリチャリ株式会社、GO株式会社、newmo株式会社をはじめとしたモビリティ企業に所属のみなさまにお声がけし、それぞれの事業・プロダクトの紹介を中心にモビリティにまつわる情報を互いに共有しました。 まずは今後オープンな勉強会を開催するにあたって、モビリティ業界の技術勉強会を開催すること自体に共感いただけるのかどうか、そして今後どんなテーマで勉強会を開催すれば有意義な時間になるか共通点を探りたいという考えから、クローズドイベントの開催に踏み切りました。 クローズドイベントの開催結果 ありがたいことに、クローズドイベントは大変好評かつ盛況だったと思います。 どれくらい盛り上がったかというと...そのまま2次会に行く人がいたぐらいです🍻 同じモビリティ業界の企業で働く者同士、モビリティ業界の動向や近未来のモビリティについて熱く語り合うことができたのではないかと感じています🔥 ここで勉強会のアンケートにご記入いただいた感想の中から一部をご紹介します。 モビリティ業界の情報収集が有益でした 業界特化した勉強会もいいものですね モビリティを軸に集まっているので全ての話が興味深く聞けました! 共有される情報が近しく、今後も繋がりたいと思った なんかホーム感がある 今後のMobility Night まずは継続開催を目指して、隔月(偶数月)で勉強会を開催予定です。 そして、現時点ではイベントの開催初期ということもあり、運営メンバーがお声がけさせていただいた企業のみなさまにご登壇いただいておりますが、今後は運営メンバーからのお声がけの有無に関わらず、登壇しやすい雰囲気を作っていきたいです! ぜひ、登壇したい方がいればお気軽にconnpassからのエントリーお待ちしております🙌 (登壇人数が多い場合は、抽選させていただく場合がございます。) また、Mobility Nightの最新情報は、Discordで公開しております。 「Mobility Nightに参加したい」「登壇したいけど事前に相談したい」「運営メンバーとして参加したい」など、Mobility Nightに少しでもご興味があるみなさまは、ぜひDiscordにご参加ください! 私個人としては、Mobility Nightの情報共有だけでなく、モビリティに関する勉強会の共催募集などもできる場になるといいなと考えています。 https://discord.gg/nn7QW5pn8B 次回開催のお知らせ Mobility Night #1 を以下の日程で開催いたします! https://mobility-night.connpass.com/event/334400/ 日時 2024年12月5日(木) 18:30~ テーマ GPS・位置情報 会場 KINTOテクノロジーズ 室町オフィス 現在モビリティ業界の企業・団体に属しているか否かに関わらず、 モビリティ業界に興味があるみなさまのご参加いただきたいと思っております! ご興味がある方は、残席わずかのためお早めにお申し込みくださいませ。 当日のみなさまのご参加を心よりお待ちしております。
Introduction Hello! This is Iki from the Cloud Infrastructure Team of the Platform Group (in the Osaka Tech Lab) at KINTO Technologies. I heard that a skilled young team member from Osaka will be writing about troubleshooting CloudFront Functions, so I’ll cover CloudFront edge functions as some foundational knowledge in advance! Let's start with an overview of CloudFront CloudFront is a content delivery network (CDN), designed to bring content closer to users by strategically placing CDN points worldwide and caching content at these locations. Users can enjoy low-latency access by connecting to the nearest CDN point. There are two types of edge locations: one closer to the user and a regional edge cache (regional edge location) situated closer to the origin server. What is an Edge Function? An edge function is a function that runs on an edge server, processing traffic at the location where it is received. By using edge functions, you can execute operations at the time of request or response, and in our company we mainly implement and operate the following functions: Redirect the URL of the response Add header Resize the image according to the request parameters When to Run an Edge Function An edge function can be run at the following four times. Viewer request Viewer response Origin request Origin response With CloudFront, the viewer manages all communication, so if there’s no cached content, the origin handles common processing. This setup is useful for controlling information sent to the origin, resizing cached data, and other optimizations. Types of Edge Functions There are two types of edge functions: CloudFront Function and Lambda@Edge. CloudFront Function An edge function that runs at an edge location close to the user. It performs large-scale latency-sensitive CDN customizations. CloudFront Functions are ideal for simple tasks like header manipulation and redirection, and they cost less than one-sixth of Lambda@Edge. Since it runs at the edge location closest to the user, it responds to viewer requests and responses, but not origin requests and responses. Lambda@Edge Edge functions that run in the region edge cache close to the origin. For processing tasks that CloudFront Functions cannot handle, you can use other AWS services, including the AWS SDK, and access file systems by leveraging Lambda@Edge or similar AWS services. Lambda@Edge is an extension of AWS Lambda, and although it appears the same on the console, it has some functional limitations—such as not allowing user-defined environment variables. Please keep these restrictions in mind. While the Lambda@Edge function itself is stored in the Virginia region, it operates by creating replicas in various edge locations, allowing it to run within each regional edge cache. As a result, the concurrent Lambda execution limit and access limits for each service apply in the specific region where the function runs (such as the Tokyo region in Japan). Be mindful of these limits to ensure smooth operation. It supports viewer requests, viewer responses, origin requests, and origin responses. The Differences between CloudFront Function and Lambda@Edge CloudFront Functions Lambda@Edge Programming language JavaScript Node.js / Python Event source Viewer request Viewer response Viewer request Viewer response Origin request Origin response Scale Number of requests: More than 10,000,000 per second Number of requests: Up to 10,000 per second per region The duration of the function Less than 1ms Viewer: 5 seconds Origin: 30 seconds Maximum memory 2 MB 128 ~ 3,008 MB Maximum size of the function code and included libraries 10KB Viewer: 1MB Origin: 5MB Network access No Yes Access to file systems No Yes Access to the request body No Yes Access to location and device data Yes Viewer request: No Viewer response: Yes Origin request: Yes Origin response: Yes Quote: CloudFront Functions and Lambda@Edge selection Usage of Edge Functions in KINTO Technologies KINTO Technologies, though not yet fully utilizing it, recommends CloudFront Functions. They can help manage costs and reduce concurrent Lambda executions for viewer requests and responses, especially in high-traffic environments. Besides, CloudFront Functions have more limitations compared to Lambda@Edge. Therefore, we prioritize reducing development and operational costs by using Lambda@Edge, rather than focusing solely on minimizing daily AWS expenses. Conclusion In this post, I discussed CloudFront edge functions (CloudFront Function and Lambda@Edge). By understanding and leveraging edge functions, you can enhance your system’s capabilities and improve the user experience. However, given their strict limitations, any mistakes can result in errors and unexpected outcomes. I hope this article has provided valuable insights and will be beneficial for your development work. Stay tuned for an upcoming post on troubleshooting CloudFront Function! We're also seeking new team members to join us at the Platform Group (Osaka Tech Lab), so don't hesitate to reach out! KINTO Technologies Corporation Platform G Recruitment Top   wantedly
Kotlin / Ktorで作るクラウドネイティブなマイクロサービス(オブザーバビリティ編) こんにちは。Woven Payment Solution開発グループの楢崎と申します。 我々は、 Woven by Toyota で Toyota Woven City で利用される決済基盤のバックエンド開発に携わっており、 Ktor というKotlin製のWebフレームワークを用いて開発しています。 これらのバックエンドアプリケーションは、Woven Cityで利用される、KubernetesをベースにしたCity Platformという基盤で動作し、マイクロサービスを構成しています。 今回は、マイクロサービスアーキテクチャを構成する上でマイクロサービスのペインポイントと、それらを解消する上で必要不可欠となる、オブザーバビリティ(Observability)を向上させるためのtipsを、 我々が利用しているKtorというWebフレームワークと、マイクロサービスをホストするプラットフォームとしてKubernetesを例にいくつかご紹介したいと思います。 またKubernatesと合わせて、いわゆる「クラウドネイティブ」な技術スタックも合わせてご紹介したいと思います。ログ収集ツールとして Loki , メトリクス収集ツールとして Prometheus 、可視化ツールとして Grafana を今回は用いています。 実際にJavaやKotlinを使ってマイクロサービスを開発している方々はもちろん、プログラミング言語を問わず、マイクロサービスやKubernetesをこれから導入しようとしている開発者の皆さんの参考になれば幸いです。 この手順を再現する方法とサンプルコードはまとめて 記事の最後 に記載していますので、お時間ある方は是非手を動かしてみてください! 最初に: マイクロサービスのつらみ 一般的に、マイクロサービス化することによって、モノリシックなアプリケーションの諸問題は解消することができますが、一方でアプリケーションの複雑性が増して、問題が発生した際の切り分けが非常に難しくなってしまいます。今回は以下の3つの問題を考えてみます。 ペインポイントその1: エラーがどのタイミングでどのサービスが起因となって起こったのかよくわからない ペインポイントその2: 依存性のあるサービスの稼働状況を常に考慮しないといけない ペインポイントその3: リソース起因のパフォーマンス低下切り分けが難しい オブザーバビリティを向上させることによって、それらの問題をどのように解消できるのか、今回はKtorを例に、 わずか3つのプラグインの導入と、数行のコードの追加 でペインポイントごとに解決策を実装してみてみたいと思います。 今回導入する3つのKtorプラグイン 施策1. CallIdの導入 今回以下のような、マイクロサービスでよくあるクラスタ内でAPIを呼び出すような2つのサービスを作成し、どのようにログが見えるか見てみたいと思います。 sequenceDiagram participant User as ユーザー(クラスタ外) participant A as Frontendサービス participant B as Backendサービス User->>A: /call リクエスト Note over User,A: クラスタ外からのリクエスト A->>B: / リクエスト Note over A,B: Frontendでの結果をBackendに渡す B-->>A: / レスポンス Note over B,A: Backendで処理した結果を返す A->>User: /call レスポンス ログは標準出力へ出力し、Kubernetes上に別にデプロイしたログ収集ツール(今回はLoki)で収集することを前提とします。 サービスをそれぞれ、呼び出し元(frontend)と呼び出し先(backend)とします。 監視する時にそれぞれのサーバで起こっていることは、ロギングプラットフォームなどでポッド名などを指定して見ることができるかもしれませんが、サーバをまたいだリクエストは、お互い関連させて見ることはできません。 特にリクエスト数が増大した場合、時系列でログを表示するだけでは、どのアプリケーションログ同士が関連しているか切り分けるのは非常に難しくなってしまいます。 大量にリクエストが来ると、どのリクエストとレスポンスが関連があるかわからない... 別のサーバ上で起こった因果関係のあるイベントをネットワーク越しに関連させる仕組みを分散トレーシング(distributed tracing)と言います。 一般的には、Istio等サービスメッシュを利用すればZipkinやJaegerで関連しているリクエストの可視化は可能で、直感的にどこでエラーが発生したか理解することはできます。 一方で、ログからキーワードで検索するなどアプリケーションログを中心としたトラブルシュートの際の使い勝手はあまりいいとはいえません。 そこで、Ktorの CallId という機能を利用します。これでロギングプラットフォームで特定のCallIdのログを、キーワードとして検索して見ることができます。 またネットワークレイヤーの設定が不要なので、サービスメッシュなどを導入しなくてもアプリケーションエンジニア側で完結し融通が効きます。 実際にアプリケーションを動かしてGrafanaでログを確認してみましょう。 今回はフロントエンド、バックエンド共に同じコンテナイメージを用意するので、生成するプロジェクトは一つでOKです。 こちらの手順 にそってソースコードをテンプレートから生成します。 dependencies { implementation("io.ktor:ktor-server-call-logging-jvm:$ktor_version") implementation("io.ktor:ktor-server-call-id-jvm:$ktor_version") implementation("io.ktor:ktor-server-core-jvm:$ktor_version") 上記のような必要なライブラリが参照されています。 生成されたコードのうち、ログに関する部分を以下のように修正します。 (以下に各行が何を表すか、コメントとして付記しています、修正する必要はありません。) fun Application.configureMonitoring() { install(CallLogging) { level = Level.INFO filter { call -> call.request.path().startsWith("/") } // ログを出力する条件を指定できる callIdMdc("call-id") // これを設定しておくことで、logback.xmlの %X{call-id} の部分に値を埋め込む事が可能 } install(CallId) { header(HttpHeaders.XRequestId) //どのヘッダーにIDの値を格納するか verify { callId: String -> callId.isNotEmpty() //値が存在するか検証する } + generate { + UUID.randomUUID().toString() // なかったら値を生成して埋め込む + } } HTTPクライアントの実装では、リクエストのヘッダーに値を入れて同じCallIdが伝搬するように設定しておくと良いでしょう。 以下のコードをそれぞれ追加して、実際にCallIdがサーバ間の通信で伝搬するか確認してみます。 dependencies { ... + implementation("io.ktor:ktor-client-core:$ktor_version") + implementation("io.ktor:ktor-client-cio:$ktor_version") ... } routing { + get("/call") { + application.log.info("Application is called") + val client = HttpClient(CIO) { +   defaultRequest { + header(HttpHeaders.XRequestId, MDC.get("call-id")) + } + } + val response: HttpResponse = client.get("http://backend:8000/") + call.respond(HttpStatusCode.OK, response.bodyAsText()) + } サンプルコードを 以下 を参考にビルド、デプロイできる様になったら実際に以下のコマンドを実行してAPIを呼んでみてください。 curl -v localhost:8000/ curl -v -H "X-Request-Id: $(uuidgen)" localhost:8000/call サーバー間でCallIdが伝播して検索キーワードとして検索できる様になった ヘッダーに値を入れなくても、ログ上で、CallIdの値が追加されたかと思います。 またこちらのコマンドで生成されたUUIDの値を検索すると、一連の複数のサーバ上でのイベントを関連付けることができている事がわかります。。 施策2. Liveness Probe、Readiness Probeの設定 Kubernetesのコントロールプレーンにアプリケーションの死活状況を伝える仕組みとして、liveness probeとreadiness probeという仕組みがあります。 それぞれ何を表すのかは、 こちらのGoogleの記事 が参考になりますが Liveness Probe: コンテナアプリケーション単体での死活状態 Readiness Probe: 依存関係のあるサービスへを含めたアプリケーションが稼働可能な状態 をそれぞれAPI経由で取得できるようにしたものを言います。 これらを設定することによって、起動に失敗したコンテナを効率的にリサイクルできたり、起動に失敗したコンテナにアクセスしないよう、トラフィックを制御できます。 Ktorでこれらを実装してみます。ここでは、特にライブラリは使用しません。 実装の方針としては、liveness probeは自分自身の死活状況をKubernetesに伝えるためなので、リクエストに対してOKを返すだけで大丈夫です。 Readiness probeの方に、依存しているサービスや接続しているデータベースなどにpingを送ります。 また期待できる時間までにレスポンスが得られなかった自体に備えて、リクエストタイムアウトもここで設定しておきましょう。 routing { ... get("/livez") { call.respond("OK") // Web serverが起動しているかどうかだけ伝えられればいいので、200をかえせばOK } get("/readyz") { // DBやその他の依存サービスに応じてpingを送る実装をアプリケーションの用途に応じて記述  // SQL ClientやHTTP Clientにはリクエストタイムアウトが設定できるので、期待した時間内に接続できるか記述する call.respond("OK") } } これらのAPIのエンドポイントが存在することをKubernetesのコントロールプレーンに伝える必要があります。 Deploymentの定義に以下を追記します。 これらには、リクエストを処理可能になるまでの時間も設定できるので、初回起動に時間がかかる場合でも想定する経過時間を入れておけば誤検知しない様にできます。 ... livenessProbe: httpGet: path: /livez port: 8080 readinessProbe: httpGet: path: /readyz port: 8080 initialDelaySeconds: 15 # コンテナが起動して15秒後にreadiness probeに聞きに来る、defaultは0 periodSeconds: 20 # 20秒に一回 timeoutSeconds: 5 # 5秒以内に結果が帰ってくることを期待 successThreshold: 1 # 1回成功すれば起動成功と判定 failureThreshold: 3 # 3回連続失敗すればpodが再起動される ... 以上で設定は完了です。エンドポイント内にsleepなどを入れたり、各種パラメータを変えて振る舞いを確認してみてください。 また、今回は言及までにとどめますが、異常を検知した場合、Prometheusの Alertmanager などを利用して、通知する仕組みを構築しておきましょう。 施策3. Micrometerの設定 上記の2つを導入する事によって、かなりオブザーバビリティは向上したかと思います。またKubernetesではPod, Nodeレベルで監視もできると思いますが、アプリケーションのランタイムレベルの監視が不十分です。 一般的にKotlinのアプリケーションはJVM上で動作していて、JVM上のCPUやメモリ等の使用量やガベージコレクションの挙動を監視することによって、ランタイムを外形監視することができます。 それによって、意図しないランタイム起因のパフォーマンスの低下などを捉える事ができます。 では、マイクロサービスアーキテクチャではどのように導入するのが良いでしょうか? モノリスであれば、動作させるサーバにエージェントを入れることで比較的シンプルに導入できるはずです。一方でコンテナが生成、消滅を繰り返すKubernetesでエージェントの導入はあまり現実的ではありません。 Ktorは Micrometer というJava界隈ではデファクトなメトリクス取得の仕組みを、 Prometheusで収集できるプラグイン があります。 冒頭で説明した、プロジェクトをテンプレートから作成する際に以下のパッケージとソースコードがプロジェクトに追加されます。 implementation("io.ktor:ktor-server-metrics-micrometer-jvm:$ktor_version") implementation("io.micrometer:micrometer-registry-prometheus:$prometeus_version") val appMicrometerRegistry = PrometheusMeterRegistry(PrometheusConfig.DEFAULT) install(MicrometerMetrics) { registry = appMicrometerRegistry } routing { get("/metrics-micrometer") { call.respond(appMicrometerRegistry.scrape()) } } これらをKubernetesの設定ファイル上で提示すれば、勝手にPrometheusがエンドポイントを叩いてデータを集積してくれます。 kind: Service metadata: name: backend namespace: sample + annotations: + prometheus.io/scrape: 'true' + prometheus.io/path: '/metrics-micrometer' + prometheus.io/port: '8080' 更に マーケットプレイスに公開されているGrafanaにダッシュボード を追加する事によって非常に簡単にJVMのパフォーマンスを可視化することができ、アプリケーションの透明性を上げることができます。 マーケットプレイスからIDをコピペして持ってくるだけで登録可能 メモリ、CPU、ガベコレなどがpod単位で表示する事が可能 またこれらのメトリクスからアプリケーションが常時どれくらいのCPUやメモリを利用するのかを監視して、コンテナのCPUリソースを設定することによってKubernetesクラスタ全体のリソース使用の効率化にも繋がります。 (これらのリソースの設定は、アプリケーションを正しくスケールアウトさせるためにも必要となってきます) resources: requests: memory: "512Mi" cpu: "500m" limits: memory: "512Mi" cpu: "750m" 最後に Ktorというwebフレームワークには、プラグインベースで、既存のアプリケーションの動作を大きく変更することなく、非機能要件を向上させる事がおわかりいただけたと思います。 複雑性が増したシステムでは、一箇所でも死角を作ってしまうと、バグの原因を検証するために立てた仮説が、検証できず迷宮入りしてしまいます。 どんなアーキテクチャであっても不具合が起こった時に備えて常に死角を減らす努力をすることが大事です。 今回取り上げた内容で、マイクロサービスアプリケーションのWebフレームワークのオブザーバビリティの機能に関してご紹介できたかと思います。 もし今後マイクロサービスを採用したいとお考えの方で、フレームワークの選定に迷われている方は、これらの機能があるかどうかも技術選定のポイントとして加えたいですね。 他にもマイクロサービスを構成し、円滑に運用する上でのベストプラクティスとしてGitOpsの実践, サービス間の認証認可、負荷分散などが必要になってきますが、それはまた別の機会にご紹介できたらと思います。 最後に当社では 様々なポジション で採用していますので、ご興味あればまずはカジュアル面談からどうぞ。 (参考)環境設定とサンプルコード 上記の解説をお手元で再現するにあたって、Javaの実行環境と、Kubernetesを有効化したDocker Desktop、 Helm が動作することを前提としています。 これらはMac / Linuxで動作を確認しております。(Windowsをお使いの方はWSL2をご利用ください。) Kubernetesがお手元の端末の場合を想定しています。クラウド上にある場合は適宜読み替えてください。 この記事では、ログ収集ツールとして Loki , メトリクス収集ツールとして Prometheus 、可視化ツールとして Grafana を利用しています。 ソースはテンプレートからゼロから作成し、 Jib というツールを用いてビルドタスクを実行することでDockerイメージを作成することとします。 以下の例では、Kotlin Script(.kts)のGradleでビルドタスクを実行するものとします。 またコンテナをクラスタにデプロイするための Skaffold というツールもインストールしておくと、自動でDocker tagの設定からKubernetesへのデプロイを実行できます。 helm repo add grafana https://grafana.github.io/helm-charts helm repo add prometheus-community https://prometheus-community.github.io/helm-charts helm repo update helm install prometheus prometheus-community/prometheus -n prometheus --create-namespace helm install loki grafana/loki-stack -n grafana --create-namespace helm install grafana grafana/grafana -n grafana export POD_NAME=$(kubectl get pods --namespace grafana -l "app.kubernetes.io/name=grafana,app.kubernetes.io/instance=grafana" -o jsonpath="{.items[0].metadata.name}") kubectl --namespace grafana port-forward $POD_NAME 3000 # 上記コマンドは実行後閉じないように別のターミナルを開いて kubectl get secret --namespace grafana grafana -o jsonpath="{.data.admin-password}" | base64 --decode #| pbcopy # Macをお使いの方はこちらをコメントアウトするとクリップボードにパスワードがコピーされます これでブラウザから http://localhost:3000 のGrafanaにアクセスして、ユーザID: admin , パスワードは最後のコマンドの結果を入力してログインし、 データソースをそれぞれ設定します。 Loki: http://loki:3100 Prometheus: http://prometheus-server.prometheus.svc.cluster.local これで監視ツールの方は完了です。 コードの方はInetelliJで新規のKtorのアプリケーションをテンプレートから新規作成します。IntelliJから以下を選びます。 VS Codeなどをお使いの方は こちらのサイト からダウンロード可能です。 今回はフロントエンド、バックエンド共に同じコンテナイメージを用意するので、生成するプロジェクトは一つでOKです。 以下のDockerでビルドするためのJibの設定をいれてJibのGradleタスク ./gradlew jibDockerBuild でビルドできることを確認してください。 plugins { application kotlin("jvm") version "1.8.21" id("io.ktor.plugin") version "2.3.1" + id("com.google.cloud.tools.jib") version "3.3.1" } ... + jib { + from { + platforms { + platform { + architecture = "amd64" + os = "linux" + } + } + } + to { + image = "sample-jib-image" + tags = setOf("alpha") + } + container { + jvmFlags = listOf("-Xms512m", "-Xmx512m") + mainClass = "com.example.ApplicationKt" + ports = listOf("80", "8080") + } +} 今回追加したログを注視できるよう、Logbackのログレベルを変更しておきましょう。また監視用に追加したエンドポイントはノイズになってしまうので、表示されないようにしてしまいます。 - <root level="trace"> + <root level="info"> install(CallLogging) { level = Level.INFO - filter { call -> call.request.path().startsWith("/") } + filter { call -> !arrayOf("/livez", "/readyz", "/metrics-micrometer") + .any { it.equals(call.request.path(), ignoreCase = true) }} callIdMdc("call-id") } ここまでソースに追記したら、以下のコマンドでコンテナイメージがKubernetes上にデプロイされてアプリケーションを実行されます。Grafana上にログやメトリクスが流れてくるか確認します。 services.yaml ファイルは少々長いので一番最後に記載しております。 ./gradlew jibDockerBuild && kubectl apply -f services.yaml # Buildするたびにdocker tagを修正する # Skaffoldをインストールしている方は以下のコマンドで skaffold init # yamlファイルが生成される skaffold run # 一回だけビルドデプロイ作業を実行 skaffold dev # ソースコード修正するたびに継続的にビルド、デプロイ作業が走る SkaffoldファイルにportForwardを記述しておくと自動でlocalhost:8000にアクセスできるようになって便利です apiVersion: skaffold/v4beta5 kind: Config metadata: name: observability build: artifacts: - image: sample-jib-image - buildpacks: # ビルドが遅いので消す - builder: gcr.io/buildpacks/builder:v1 + jib: {} # JAVA_HOMEに正しいPATHが入っていないと実行エラーになる可能性あり manifests: rawYaml: - service.yaml +portForward: + - resourceType: service + resourceName: frontend + namespace: sample + port: 8000 + localPort: 8000 apiVersion: v1 kind: Namespace metadata: name: sample --- apiVersion: apps/v1 kind: Deployment metadata: name: backend-deployment namespace: sample spec: replicas: 2 selector: matchLabels: app: backend template: metadata: labels: app: backend spec: containers: - name: backend image: sample-jib-image:alpha imagePullPolicy: IfNotPresent ports: - containerPort: 8080 # Liveness probe, readiness probeを実装するまでコメントアウトしておいてください # livenessProbe: # httpGet: # path: /livez # port: 8080 # initialDelaySeconds: 15 # periodSeconds: 20 # timeoutSeconds: 5 # successThreshold: 1 # failureThreshold: 3 # readinessProbe: # httpGet: # path: /readyz # port: 8080 resources: requests: memory: "512Mi" cpu: "500m" limits: memory: "512Mi" cpu: "750m" --- apiVersion: v1 kind: Service metadata: name: backend namespace: sample annotations: prometheus.io/scrape: 'true' prometheus.io/path: '/metrics-micrometer' prometheus.io/port: '8080' spec: selector: app: backend ports: - protocol: TCP port: 8000 targetPort: 8080 type: ClusterIP --- apiVersion: apps/v1 kind: Deployment metadata: name: frontend-deployment namespace: sample spec: replicas: 2 selector: matchLabels: app: frontend template: metadata: labels: app: frontend spec: containers: - name: frontend image: sample-jib-image:alpha imagePullPolicy: IfNotPresent ports: - containerPort: 8080 # Liveness probe, readiness probeを実装するまでコメントアウトしておいてください # livenessProbe: # httpGet: # path: /livez # port: 8080 # initialDelaySeconds: 15 # periodSeconds: 20 # timeoutSeconds: 5 # successThreshold: 1 # failureThreshold: 3 # readinessProbe: # httpGet: # path: /readyz # port: 8080 resources: requests: memory: "512Mi" cpu: "500m" limits: memory: "512Mi" cpu: "750m" --- apiVersion: v1 kind: Service metadata: name: frontend namespace: sample annotations: prometheus.io/scrape: 'true' prometheus.io/path: '/metrics-micrometer' prometheus.io/port: '8080' spec: selector: app: frontend ports: - protocol: TCP port: 8000 targetPort: 8080 type: LoadBalancer ここまでご覧いただきありがとうございました。以下のコマンドで今回作成したリソースを消しておきましょう。 skaffold delete docker rmi $(docker images | grep 'sample-jib-image') # kubectl delete all --all -n sample # skaffoldを実行していない場合 helm uninstall grafana -n grafana helm uninstall loki -n grafana helm uninstall prometheus -n prometheus
Introduction Hello. I am Koike, a data engineer in the Analytics Group. Slack is incredibly convenient, isn’t it? You might be wondering, “What’s this guy talking about all of a sudden?” But don't worry. It’s undeniable that Slack is a very useful tool. Among its features, I believe that emoji reactions are the most useful by far. But first, do you know what an emoji reaction is? Let me explain it in a bit more detail. Take a look at this image: ![Slack emoji reaction](/assets/blog/authors/charlie/slack_emoji_reaction.png =660x) This is something I posted in my personal times-channel. If you look at the area circled in white, you’ll notice small bubble-like icons. These are what we call emoji reactions. Next to the "🥲" emoji, there’s a "1," which meaning that one person reacted with that "🥲" emoji. The more reactions there are, the happier it makes me! There is also another emoji called donmai (meaning “don’t worry!”) This isn’t a default Slack emoji, but rather a custom emoji ^1 added by a user. This time, I’ll be adding custom emojis with the help of ChatGPT. Background Let me explain why I wanted to add custom emojis in the first place. Our Analysis Group is split across three locations: Tokyo, Nagoya, and Osaka. This means some of us are working fully remotely at all times. Since offline communication does happen occasionally in each location, information gaps may be even more pronounced compared to a fully remote setup. To help bridge this gap, I thought we should focus more on using the text communication tool that is Slack. And that’s how I came up with the idea of adding custom emojis as a solution. Creating custom Slack emojis Now let's dive into adding custom emojis. Here’s how I plan to proceed: Count the number of times each emoji has been used in reactions Group the emojis collected in step 1 by the emotions they represent Use ChatGPT to generate words based on the groups created in step 2. Select the words generated in step 3, create images using an emoji creation tool, and register them on Slack. Counting the number of times emojis are used When it comes to adding custom emojis, it’s hard to know what exactly to add right away. So, I decided to start by researching what kinds of emojis are already being used regularly. Specifically, I’ll narrow down the channels and time period, and then examine the emoji reactions and how often they were used in posts to identify trends. First, I created a Slack app, set the permissions as shown below (the permissions might be a bit excessive since I added whatever seemed necessary), and issued a token. Next, I called the app from Python and performed the aggregation as follows: 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() Let me briefly explain the source code. In main() , functions for each process are called. get_save_and_messages() is a function that retrieves messages from Slack and saves them to a file. Note that SLACK_API_TOKEN and target_channel_id_to_name are hidden. If there are too many messages, they may not be retrieved all at once. To handle this, get_unix_times() divides the period into smaller parts and returns a list, allowing the messages to be retrieved in smaller batches. Once the messages are retrieved, the function get_reaction_counts() counts the number of emoji reactions. After that, the results are sorted by frequency and displayed. Here is an example of the execution results: For example, ('man-gesturing-ok', 248) means that the man-gesturing-ok emoji was used 248 times as a reaction. The results include not only default emojis, but also custom ones and even emojis created by external users [^2]. ('man-gesturing-ok', 248), ('eyes', 248), ('arigatougozai', 199), ('understood', 64), ('+1', 49), ('thinking_face', 43), ('yorosikuonegaisimasu', 26), ('arigatougozaimasu', 17), ('tada', 15), ('nice', 14), ('arigato', 13), ('do_ne', 13), ('man-gesturing-no', 11), ('scream', 10), ('kakuninshimasu', 10), ('acknowledged2', 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), ('uoooo', 5), Emoji grouping The resolution of the collected data is still low, so I will group the emojis by type of emotional expression to better understand their characteristics. While, I came across emotion classifications developed by psychologists, I found them too detailed and not well suited for this purpose. So I came up with my own method of grouping. I propose that the structure could be divided into two main categories: actions for reporting, communicating, and consulting, and actions for expressing emotions. These can then be further subdivided for clarity. Here is the breakdown: - Reporting, Communicating, and Consulting - Reporting - Checking - Requesting - Emotional Expressions - Empathy - Praise - Gratitude - Support - Comfort - Others Let's apply this classification to the results from the earlier aggregation. ![Emoji grouping](/assets/blog/authors/charlie/grouping.png =170x) If you've looked closely, you may have noticed that for most of the emojis that didn’t clearly fit into any category, I sorted them based on intuition... 🤫 However, I believe the groupings I've come up with feel reasonably appropriate. **Emoji generation using ChatGPT ** After grouping the emojis, the next step is to generate custom emojis for each emotional category. As a method, I’ll be using the popular ChatGPT. I used the GPT-4 model without plugins or web browsing features. Here’s the prompt I used: I want to create custom emojis for Slack. Please provide 10 suggestions for each of the following categories: Also, please try to keep them between 4 to 6 characters, using either hiragana, katakana, or kanji. - Show empathy for the person who sent the message (e.g. :indeed:, :That's true:) - Something to compliment the person who sent you the message (e.g. :Amazing:, :God:, :Great:) - Something to show appreciation to the person who sent you a message (e.g. :Thanks:,:Appreciated:) - Something to encourage the person who sent you the message (e.g.: :Fight:,:Woohoo:) - Something to comfort the person who sent you the message (e.g. :Oh:,:Wow:) Here are the results: Custom emoji suggestions. I tried to keep each one within 4-6 characters using hiragana, katakana, or kanji. Something that shows empathy for the person who sent you the message: :I know: (wakaru yo) :I agree: (doukan) :Makes sense: (nattoku) :Yep: (un un) :That's it: (sore sore) :I want to say: (iitai) :Empathy: (kyoukan) :I feel you: (kanjiru) :You're right: (gomottomo) :Thoughtfulness: (omoiyari) Something to compliment the person who sent you the message: :Amazing: (sugoi ne) :Genius: (tensai) :Incredible: (sugoi) :Cool: (kakkoii) :Lovely: (suteki) :Beautiful: (utsukushii) :Perfect: (kanpeki) :I respect you: (sonkei) :Praise: (shousan) :You’re shining: (kagayaiteru) Something to show appreciation to the person who sent you the message: :Thank you: (arigatou) :Gratitude: (kansha) :I’m grateful: (kansha shiteru) :Thanks: (arigatou ne) :Thanks a lot: (arigatou-) :You saved me: (tasukatta) :Grateful feelings: (kansha no kimochi) :Repay your kindness: (ongaeshi) :Appreciation: (orei) :I’m moved: (kangeki) Something to encourage the person who sent you the message: :Do your best: (ganbatte) :Fight: (faito) :Hang in there: (ganbare) :I’m cheering for you: (ouen suru) :I’ll be your strength: (chikara ni naru) :Let's do our best together: (issho ni ganbarou) :Let’s keep going: (ganbarimashou) :Sending you cheers: (eeru wo okuru) :Wishing you success: (seikou wo Inoru) :Be confident: (jishin wo motte) Something to comfort the person who sent you the message: :It’s okay: (daijoubu) :Hang in there: (ganbare) :Don’t worry about it: (ki ni shinaide) :You did your best: (ganbatta ne) :It's tough: (tsurai ne) :That's tough: (tsurai yo ne) :Tomorrow is a new day: (ashita wa atarashii hi) :Don't feel down: (ochikomanaide) :Stay positive: (maemuki ni) :You’re doing great: (ganbatteru ne) Some of these may feel unnatural for use on Slack, but overall, they seem pretty good. Emoji selection and Slack registration Next, I'll select the most useful ones from the emojis generated by ChatGPT, while removing any that are already registered in the Slack workspace. - Empathy - :I understand: - :I agree: - :You have a point: - Praise - :That’s amazing: - Gratitude - :I am touched: - Support - Comfort - :Don’t worry: This is what it turned out like. Finally, I’ll use an emoji creation tool [^3] to turn these into images and register them on Slack. Ah, what a great view! Conclusion What do you think? While this may not instantly enhance communication on Slack, taking the initiative to actively use the new emojis and encourage their adoption across the organization will be key. This time, we’ve added custom emojis spanning various emotional expression categories, but focusing on creating emojis to fill specific gaps could also be effective. I encourage you to try them out in your organization’s Slack space! [^2]: ¥0 – SmartHR Store [^3]: Emoji Generator
Introduction Konnichiwa! I am Felix, and I develop iOS applications at KINTO Technologies. This time, I would like to share my experience at iOSDC, held from August 22nd to 24th (Thursday to Saturday). Continuing from my previous post about trySwift , this was my second time attending an iOS conference. This time, we participated as a sponsor and hosted our own booth! KINTO Booth Our booth was decked out in KINTO blue, and as you can see, even the happi coats (traditional Japanese straight-sleeved coats) matched the theme! We hosted a coding challenge where participants had to read through some real project code and stamp the corresponding question numbers onto the code. For our booth giveaways, we distributed KINTO mascot stickers and cardboard cutouts shaped like iOS devices, which participants could decorate with stickers. Those who took part in the coding challenge received either an eco-bag or a multi-chain as a reward. It was a great opportunity to engage with attendees, hear their thoughts on KINTO Technologies, and gather valuable feedback on our projects. The conversations provided fresh insights into how our products are perceived and offered helpful guidance for future improvements. Other Booths There were many interesting and educational booths from other companies that caught my attention, and I'd like to highlight a few that stood out: Sansan Sansan’s booth was intriguing because it showcased their technology stack, allowing guests to react to various tools and frameworks. DeNA DeNA’s booth had a particularly fun activity where participants solved a crossword puzzle by both reading code and looking at a map. Bitkey Although you needed a test device and a MacBook to implement their beacon app, it was fun trying to develop the test app and find the person carrying the beacon. Glassfiber Glassfiber had a quiz that attracted a lot of people, offering both fun and educational content. Presentations I attended several sessions and would like to highlight the two that impressed me the most: Modern In-App Purchasing with StoreKit 2 First, I would like to talk about StoreKit 2 and its modern usage. As I haven't worked with StoreKit yet, I found the session insightful. It focused on the introduction, implementation, and testing of StoreKit 2, with a detailed comparison to StoreKit 1. The presentation covered key areas like simplifying asynchronous processing with async/await, streamlining receipt validation, and testing using sandbox environments, TestFlight, and StoreKitTest. This was highly informative for anyone looking to integrate in-app purchases into their apps. One interesting point I learned was that StoreKit doesn't directly support scenarios where a customer makes a payment but doesn't receive their purchased item, which was surprising to me. StoreKit 2によるモダンなアプリ内課金 This session explains StoreKit 2 implementation and testing, focusing on improvements and simplifying in-app purchases. How does GPS find your location? Another session I found interesting was about how mobile devices receive GPS signals and compute their location. It explained how Core Location uses a combination of GPS, Wi-Fi, and cellular signals for accurate positioning. While the basic GPS principle is triangulation with satellites, the talk also highlighted the complex engineering behind receiving weak signals over vast distances and how smartphones leverage network data for quick, precise results. I did not really know much related to this topic so I found it enlightening. GPSでどのようにして現在地が分かるのか The video explores how GPS and network data enable smartphones to quickly and accurately determine location. Conclusion Overall, iOSDC 2024 was a fantastic experience. It was not only an opportunity to learn from insightful sessions but also a chance to engage with the broader iOS developer community. Hosting the KINTO booth allowed me to interact with many talented individuals, hear their feedback, and showcase our work in a meaningful way. The presentations I attended, particularly those on StoreKit 2 and GPS technology, provided me with actionable insights that I can directly apply to KINTO’s ongoing projects. For example, the async/await improvements in StoreKit 2 will greatly streamline our in-app purchase implementation, making the process more efficient and user-friendly. Similarly, the advanced use of GPS and network triangulation will help us enhance location-based services in our apps, leading to more accurate and faster results for users. I’m excited to integrate these learnings into our development process and continue growing as a developer. Thank you for reading!
Introduction Hello. My name is Chris and I work in the Global Development Department at KINTO Technologies, where I work on front-end development. Today, I'd like to write about automation of business tasks, instead of front-end development. According to Slack's Productivity Report released last month, 77% of workers said that being able to automate routine tasks would greatly improve their productivity, saving them about 3.6 hours per week. So, it's important to automate your daily work as much as possible, so you can focus on what you really need to do and get more done. Sorry for the sudden change of subject, but I'd like to talk about our company's attendance rules, which were revised from July this year. Following the rules, we have a fixed maximum number of remote workdays every month. When to take those days is generally up to the individuals, as long as it's coordinated within the team and shared with other members of the division. In order to know the attendance schedule of the members, we have to report the schedule for the following week in advance. So in the Global Development Department, each member writes down their schedule for the following week on a monthly Excel spreadsheet stored on a cloud service called Box, and the leader then compiles their team's data and shares it with their manager on Slack. What's the Problem? Due to various departmental circumstances, the most efficient way to manage information is to use Excel to manage it all at once, but the problem is the flow of sharing information with managers. The Global Development Department has many members, and as a result, there are a fair number of leaders. It takes some time for all the leaders to take screenshots of Excel sheets and share them every week, and switching between tasks is a mental burden. In addition, some team members don't have assigned leaders, so their schedules aren't shared. The only way to check their availability is by looking directly at the Excel sheet. I thought it would be great to eliminate these two difficulties through leveraging my engineering skills with minimal time and efforts. So, I used the SDK provided by Box and Slack to automate the process of extracting information from Excel and uploading the schedule details to Slack! Development Environment This automation was achieved using Node.js with the following libraries. The actual code was created using Typescript, but this article will show the Javascript code. The following are also used for the implementation. dotenv When using the Box SDK or Slack SDK, I need to enter sensitive information such as tokens, so I want to make them environment variables using dotenv. https://github.com/motdotla/dotenv box-node-sdk SDK for Node.js provided by Box. https://github.com/box/box-node-sdk node-slack-sdk SDK for Node.js provided by Slack. https://github.com/slackapi/node-slack-sdk node-xlsx A library that converts information from Excel files to JSON. https://github.com/mgcrea/node-xlsx canvas-table A library that turns tables into images. https://github.com/el/canvas-table node-canvas A library on which canvas-table is based. https://github.com/Automattic/node-canvas/ Implementation Now I would like to explain the implementation step by step. Step1: Retrieve files from Box First, an application needs to be created from the Box admin console to enable the use of the Box SDKs. You can create a new one from the Box Developer Console (xxx.app.box.com/developers/console). After creation, a client ID will be issued for the app, but an access token must be issued separately. If your workspace is managed by your company, you will generally need to get approval from the company administrator on the administrator's screen. Once you have obtained the token, you should have been issued a service account ID from the app details screen. If you do not share the folder or file you want to access with this service account, you will get a 404 error when you try to get it from the SDK. Next, I'd like to move on to the code. First, install the Box SDK. yarn add box-node-sdk After that, you can write code like this to download the file to the specified location. A description of the download process can also be found in the official documentation . import BoxSDK from "box-node-sdk"; // Put the issued token here. const boxClient = BoxSDK.getBasicClient("token information"); // After that, use async/await for the process to retrieve information from the file. await downloadFile(); async function downloadFile() { return new Promise((resolve, reject) => { boxClient.files.getReadStream( // File ID "1234567", // Query parameter, for example, use if you want to get an older version of a file // https://developer.box.com/reference/get-files-id-content/ null, // Callback function function (error, stream) { if (error) { reject(error); } const output = fs.createWriteStream("output path of the file"); // Resolve Promise when finished writing output.on("finish", resolve); stream.pipe(output); } ); }) } Run the above code, and if the file exists and access permissions are correctly granted, the file should be exported to the specified path. Step2: Retrieve necessary information from a file Next, I want to retrieve the necessary information from the file downloaded from Box. Since it is an Excel file, I'll use node-xlsx to parse the Excel information. yarn add node-xlsx import xlsx from "node-xlsx"; const workSheets = xlsx.parse("path of the downloaded file"); console.log(workSheets) // [ // { // name: "sheet name", // data: [ // [], // [], // [] // ] // } // ] This will allow you to extract the information for each Excel sheet as a nested array, allowing you to process the data or delete any unnecessary data. Step3: Convert the information into an image Frankly, many of you may be wondering, "Why do we need this?" In fact, even when I first tried the automation, I had no idea. However, after obtaining the necessary information, I tried several ways to post table information to Slack in an easy-to-read format. For example, I tried creating a table using Markdown, but Slack does not support it, so when I actually tried, the layout was quite messed up. As a result, when I turned the table information into an image, the members' schedule information was neatly arranged. To this end, I used canvas-table for creating the table image. import { CanvasTable } from "canvas-table"; import { createCanvas } from "canvas"; // First, create a blank image (Canvas) const canvas = createCanvas(image width, image height); // Define information about the table const tableConfig = { // Column information columns: [ { title: "title" } ], // Information for each cell data: [ [ { value: "text", } ] ], // Optional information options: { borders: { column: { width: 1, color: "#555" }, row: { width: 1, color: "#555" }, table: { width: 1, color: "#555" }, }, title: { text: "title", }, } }; } const ct = new CanvasTable(canvas, tableConfig); await ct.generateTable(); await ct.renderToFile(fileName); This will generate the table image shown below. Step4: Post to Slack The next step is to post the image to Slack. Use @slack/web-api 's files.upload provided by Slack. yarn add @slack/web-api import fs from "fs"; import { WebClient } from "@slack/web-api"; // Set Slack OAuth Tokens const slackClient = new WebClient("token information"); const result = await slackClient.files.upload({ channels: "channel ID", initial_comment: "accompanying comment text", file: fs.createReadStream("file path") }); Upload is now complete! Step5: Autorun with GitHub Action With the steps above, the script is complete, but it still needs to be run locally. Now it would be perfect if this script could run automatically, wouldn't it? We use GitHub Actions a lot in our company, regardless of department, so we will use it again this time. First, create a yml file. name: Name of the workflow # Runs every Wednesday at 1:00 p.m. JST (listed at 4:00 a.m. UTC) on: schedule: - cron: '0 4 * * 3' jobs: build: runs-on: ubuntu-latest steps: # Checkout a repository - name: Checkout uses: actions/checkout@v3 # Set up the Node environment - name: Setup Node.js environment uses: actions/setup-node@v3 with: # Specify the appropriate Node version node-version: '18' # Install the library with Yarn - name: yarn install run: yarn install # Run the script (if the js file you want to run is index.js, as follows) - name: Run index file run: node index Now it will be executed automatically at the time specified by cron (although it may be slightly delayed). Step Extra: Change the font While this step is not necessary, I tried it as an extra step. As a group company of Toyota, we use Toyota's own font. I would like to apply it to the schedule table. I used the library called cavnas to create the image, but you can actually set the font as well. Since the Toyota Font is proprietary, a font file must be provided so that it can be referenced by the project. // Import registerFont import { registerFont, createCanvas } from "canvas"; // Always place before createCanvas registerFont('Font file path', { family: 'Font name' }); const canvas = createCanvas(canvasWidth, canvasHeight); // Specify the font to be used for the image const config = { columns: [ // ... ], data: [ [ // Define the cell information { value: "text", fontFamily: "font name", } ] ] options: { // Define the title title: { fontFamily: 'font name', text: "title", }, } }; } const ct = new CanvasTable(canvas, config); // ... If all goes well, you will have an image with the font applied like the one below. Conclusion There are still many areas to improve on what I created this time, so when I have time I would like to refactor it and add some nice features. If your company is also considering automating some business tasks, I hope this will be helpful!
Hi, my name is Ryomm and I’m developing the iOS version of an app called my route at KINTO Technologies (KTC). This year, KTC is proud to sponsor iOSDC Japan 2024 for the first time! The event will run for three days, from August 22 to 24, 2024. ▼ I also recommend you to check following blogs on this topic ▼ ✨ KINTO Technologies is a Gold Sponsor of iOSDC Japan 2024 ✨ We’re even setting up a booth✨ A lot of people, including from the Tech PR Group, the Creative Office, and the Mobile App Development Group, have come together to prepare, and I believe it’s shaping up to be a fantastic experience for everyone. Please come visit the KTC booth! We would be happy if you could remember the name KTC (KINTO Technologies)! We’ve put a lot of thought into this sponsorship and are excited to showcase the many swags we’ve created on this blog! Kumobii Paper Clip This item is included in the novelty box! It was an idea from Chimrin, combining practicality and style! Kumobii is the official mascot of KINTO. https://corp.kinto-jp.com/mascot/profile/ You can use the clip to mark your favorite page in a pamphlet or as a bookmark for technical books. Despite being made of paper, the clip is quite durable and easy to use! When you open the paper base...a token appears! Brochure manuscript There is also an advertisement for KTC in the brochure included in the novelty box! We designed it to reflect the essence of KTC, a company that provides technological support for Toyota's mobility services. Sticker & Sticker Mount Set This novelty item will be given to everyone who stops by our booth! I’m happy to say that my idea, Ryomm, was selected for this 🙌 At events like this, you often receive a lot of stickers at each booth, but what do you usually do with them? At try! Swift Tokyo 2024, I saw someone decorating their name tag with stickers like a collage, and I thought that was such a great idea! So i decided to copy it. For iOSDC, since the clear name tag holders don’t enable the use of folded paper, we prepared a backing paper specifically designed so you can create your own sticker collage! We also designed it to resemble an iPhone and made it about the same size as the 15 Pro, so it fits perfectly into your name tag case. It would be great if you could place it in your name tag case as a memento of the event. We are also distributing icon-style stickers based on the apps provided by KTC, so feel free to stick them on the backing paper and use them as well. Multi-card tool This is the first commemorative novelty item from the booth event! The iOS team held an ideathon, and K. Kane's idea was selected. When stored I'm sure many iOS engineers have, at some point, used a ruler to check if their view matches the design when implementing it...or maybe not. But in cases like those, this business card-sized tool has you covered! You can measure both length and angles anytime, anywhere. Tote Bag This tote bag features a cute print of Kumobii. It’s the second commemorative novelty item from the booth project. You can choose between the multi-card tool or the tote bag, so feel free to stop by the booth as many times as you like. Since you’ll collect lots of items at iOSDC, wouldn't it be handy to have a bag to carry everything? This idea came from uka! This bag is made of a durable material, and I highly recommend it! Leaflets distributed at the booth We’re also handing out leaflets introducing KTC at the booth. We want people to learn about the products that KTC develops, and that’s the message we’ve put into our leaflets. Booth Activities At the booth, we’ve prepared a game called "I Found the Code!" where you search for the part of the code that performs a given task. Each KTC product team has prepared its own set of questions, and the questions will change throughout the day, so be sure not to miss any! While we’re particular about the content of the questions, we’ve also paid close attention to small details to create a cohesive booth atmosphere. We borrowed wooden frame for displaying our posters and customized them in black using DIY stickers, designed the background to make the double-column code easier to read, and even tailored the question text to match the overall booth theme! We also took the opportunity to create a roll-up banner. So, why not try your hand at our booth activities while surrounded by KINTO Blue? Conclusion A huge thanks go to Sugimoto Aya san and Awano san from the Creative Team, who took on this massive deliverables and delivered the coolest designs! When we were creating the novelties, they brought handmade prototypes and made sure to communicate closely with us to help visualize the final product. Thanks to their efforts, we are ready to confidently welcome everyone to our booth. And now, it's finally happening--starting August 22nd! We’ll be waiting for you at our sponsor booth at ROHM Square! Please stop by and visit us!
Building Cloud-Native Microservices with Kotlin/Ktor (Observability Edition) Hello. My name is Narazaki from the Woven Payment Solution development group. At Woven by Toyota , we are involved in the backend development of the payment infrastructure used in Toyota Woven City , and we are developing it using Ktor , a web framework made with Kotlin. These backend applications run on City Platform, a Kubernetes-based infrastructure used in Woven City, forming the foundation of our microservices. In this article, I would like to introduce some pain points of microservices when configuring a microservices architecture and some tips for improving observability, which is essential to resolving those pain points, using as examples Ktor, a web framework that we use, and Kubernetes as a platform for hosting microservices. In addition to Kubernetes, I would like to introduce a so-called "cloud native" technology stack. This time, I will use Loki as a log collection tool , Prometheus as a metrics collection tool, and Grafana as a visualization tool . I hope this will be useful not only for those who are actually developing microservices using Java or Kotlin, but also for developers who are planning to introduce microservices and Kubernetes, regardless of the programming language they use. Instructions on how to replicate these steps, along with sample code, are provided at the end of this post . If you have time, please give it a try! First: The Challenges of Microservices Generally speaking, by adopting microservices, various problems of monolithic applications can be resolved, but on the other hand, the complexity of the application increases, making it very difficult to isolate problems when they occur. Here, we will consider three specific pain points. Pain Point 1: It is not clear when and which service caused the error. Pain Point 2: The operation status of dependent services must always be taken into consideration. Pain point 3: It is difficult to isolate resource-related performance degradation. By improving observability, we can tackle these challenges. In this post, I’ll show how we can implement solutions for each pain point using Ktor as an example. The approach involves introducing just three plugins and adding a few lines of code . The three Ktor plugins that we are introducing Solution 1. Introducing CallId In this solution, I will create two services that frequently call APIs within the same cluster, as is common in microservices. Let's see how the logs are captured in this environment. sequenceDiagram participant User as External user (outside the cluster) participant A as Frontend Service participant B as Backend Service User->>A: /Call request Note over User,A: Requests from outside the cluster A->>B: / Request Note over A,B: Pass the result from Frontend to Backend B-->>A: / Response Note over B,A: Return the result processed by Backend A->>User: /call response Logs will be output to standard output and collected by a log collection tool (Loki, in this case) deployed separately on Kubernetes. The services will be referred to as the caller (frontend) and the callee (backend). When monitoring, you may be able to see what is happening on each server by specifying the pod name, etc., using a logging platform, but requests across servers cannot be viewed in relation to each other. Especially as the number of requests increases, it becomes very difficult to isolate which application logs are related simply by displaying logs in chronological order. When a large number of requests come in, it becomes unclear which requests and responses are related... The mechanism that associates causally related events across servers over the network is called distributed tracing. In general, if you use a service mesh like Istio, you can visualize related requests with tools like Zipkin and Jaeger, making it intuitive to understand where errors occured. On the other hand, it is not very convenient to use when troubleshooting application logs, such as searching for keywords in the logs. This is where Ktor's CallId comes into play. With this feature, you can search and view specific logs by using CallId as a keyword on the logging platform. Also, since there is no need to configure the network layer, it is flexible and can be completed by the application engineer without having to introduce a service mesh or similar. Let's actually run the application and check the logs in Grafana. In this example, we will prepare the same container image for both the frontend and backend, so we only need to generate one project. Follow these steps to generate the source code from the template. dependencies { implementation("io.ktor:ktor-server-call-logging-jvm:$ktor_version") implementation("io.ktor:ktor-server-call-id-jvm:$ktor_version") implementation("io.ktor:ktor-server-core-jvm:$ktor_version") } The necessary libraries are referenced as shown above. The logging-related part of the generated code should be modified as follows. (Comments are included to explain each line; no further modifications are necessary.) fun Application.configureMonitoring() { install(CallLogging) { level = Level.INFO filter { call -> call.request.path().startsWith("/") } // Specify the conditions under which logs will be output callIdMdc("call-id") // By setting this, the value can be embedded in the %X{call-id} part of logback.xml } install(CallId) { header(HttpHeaders.XRequestId) // Specify which header will store the ID value verify { callId: String -> callId.isNotEmpty() // Verify if a value exists } + generate { + UUID.randomUUID().toString() // If not, generate and embed a new value + } } In the HTTP client implementation, it’s recommended to set the header with this value so that the same CallId propagates across requests. Add the following dependencies to verify that the CallId propagates correctly between servers. dependencies { ... + implementation("io.ktor:ktor-client-core:$ktor_version") + implementation("io.ktor:ktor-client-cio:$ktor_version") ... } routing { + get("/call") { + application.log.info("Application is called") + val client = HttpClient(CIO) { +   defaultRequest { + header(HttpHeaders.XRequestId, MDC.get("call-id")) + } + } + val response: HttpResponse = client.get("http://backend:8000/") + call.respond(HttpStatusCode.OK, response.bodyAsText()) + } Once you’re able to build and deploy using the sample code below , try running the following commands to make API calls: curl -v localhost:8000/ curl -v -H "X-Request-Id: $(uuidgen)" localhost:8000/call With this setup, the CallIds now propagates between servers, allowing it to be used as a searchable keyword Even if you don't enter a value in the header, the CallId value will be added to the log. Also, if you search for the UUID value generated by this command, you will be able to correlate events on multiple servers. Solution 2. Setting Up Liveness and Readiness Probes In Kubernetes, liveness and readiness probes are mechanisms that communicate the application’s health status to the control plane. You can refer to this Google article for more information on each. Liveness Probe: Reports the container’s own health status. Readiness Probe: Reports whether the application, including to dependent services, is ready for operation, accessible through APIs. By setting these, you can efficiently recycle containers that have failed to start, or control traffic so that containers that have failed to start are not accessed. Let's implement these with Ktor. Here, no libraries are needed. The implementation policy is that the liveness probe is to inform Kubernetes of its own aliveness status, so it's fine to just return OK to the request. The readiness probe will send pings to dependent services and connected databases. To handle cases where responses aren’t received in time, set a request timeout. routing { ... get("/livez") { call.respond("OK") // Simply returns a 200 status to indicate the web server is running } get("/readyz") { // Implement pings to the DB or other dependent services based on the application’s requirements  // You can set request timeouts for SQL Client or HTTP Client to ensure connection are made within the expected time call.respond("OK") } } You need to tell the Kubernetes control plane that these API endpoints exist. Add the following to the Deployment definition. This configuration also allows you to set the time needed for the application to be ready to process requests, which will prevent false detections even if the initial startup takes longer. ... livenessProbe: httpGet: path: /livez port: 8080 readinessProbe: httpGet: path: /readyz port: 8080 initialDelaySeconds: 15 # The readiness probe will start checking 15 seconds after the container starts; default is 0 periodSeconds: 20 # Runs every 20 seconds timeoutSeconds: 5 # Expected to return results within 5 seconds successThreshold: 1 # Considered successful after one success failureThreshold: 3 # If it fails three consecutive times, the pod will restart ... With this, the setup is complete. You can test the behavior by adding a sleep command within the endpoint or by adjusting these parameters. Also, although this is only a reference this time, we recommend building a system to notify you using Prometheus's Alertmanager or similar if an abnormality is detected. Solution 3. Configuring Micrometer By implementing the first two solutions, observability should be significantly improved. While Kubernetes allows monitoring at the Pod and Node levels, runtime-level monitoring within the application is still limited. Generally, Kotlin applications run on the JVM, allowing you to monitor runtime performance by tracking CPU and memory usage, as well as garbage collection behavior on the JMV. This helps detect unintended runtime-related performance degradation. So, how should we approach this in a microservices architecture? In a monolith, it should be relatively simple to implement by installing an agent on the server where it will run. On the other hand, in Kubernetes, where containers are repeatedly created and destroyed, installing an agent is not very practical. Ktor provides a plugin for Micrometer , the de facto standard in the Java ecosystem for collecting metrics, which can be integrated with Prometheus for monitoring. When creating a project from the template described above, the following packages and source code will be added to the project. implementation("io.ktor:ktor-server-metrics-micrometer-jvm:$ktor_version") implementation("io.micrometer:micrometer-registry-prometheus:$prometeus_version") val appMicrometerRegistry = PrometheusMeterRegistry(PrometheusConfig.DEFAULT) install(MicrometerMetrics) { registry = appMicrometerRegistry } routing { get("/metrics-micrometer") { call.respond(appMicrometerRegistry.scrape()) } } By specifying these in Kubernetes configuration files, Prometheus will automatically scrape the endpoints and collect the data. kind: Service metadata: name: backend namespace: sample + annotations: + prometheus.io/scrape: 'true' + prometheus.io/path: '/metrics-micrometer' + prometheus.io/port: '8080' Additionally, by adding a Grafana dashboard from the marketplace , you can easily visualize JVM performance metrics, improving the transparency of your application. You can simply copy and paste the dashboard ID from the marketplace to register it This setup allows you to display memory, CPU, garbage collection, and other metrics on a per-pod basis In addition, by monitoring how much CPU and memory an application is using at any given time from these metrics and setting the CPU resources for containers, you can improve the efficiency of resource usage across the Kubernetes cluster. (Setting these resources is also necessary to ensure proper scaling of the application.) resources: requests: memory: "512Mi" cpu: "500m" limits: memory: "512Mi" cpu: "750m" lastly I hope you have seen that Ktor is a plugin-based web framework that can improve non-functional requirements without significantly changing the behavior of existing applications. In complex systems, a single oversight can lead to untraceable issues, where hypotheses about bugs can’t be verified and debugging turns into a maze. Regardless of the architecture, it’s important to continuously reduce blind spots to prepare for potential issues. I hope that this article has provided you with an introduction to the observability features of web frameworks for microservice applications. If you are considering adopting microservices in the future and are unsure of which framework to choose, you should also consider whether these features are available when selecting a technology. There are also other best practices for building and smoothly operating microservices, such as implementing GitOps, managing inter-service authentication and authorization, and load balancing, which I hope to cover in a future post. Finally, we are hiring for a variety of positions . If you’re interested, feel free to start with a casual chat. (Reference) Environment Setup and Sample Code To replicate this setup in your own, you’ll need a Java runtime environment, Docker Desktop with Kubernetes enabled, and Helm . These have been tested on Mac/Linux. (Windows users, please use WSL2.) This article assumes Kubernetes is running locally. If it’s in the cloud, adjust accordingly. In this article, we used Loki for log collection, Prometheus for metrics collection, and Grafana for visualization. The source code is created from scratch using a template, and the Docker image is built using Jib as a Gradle build task. In the following example, we will run the build task in Gradle using Kotlin Script(.kts). We also recommend installing a tool called Skaffold to automate Docker tagging and Kubernetes deployment for your container cluster. helm repo add grafana https://grafana.github.io/helm-charts helm repo add prometheus-community https://prometheus-community.github.io/helm-charts helm repo update helm install prometheus prometheus-community/prometheus -n prometheus --create-namespace helm install loki grafana/loki-stack -n grafana --create-namespace helm install grafana grafana/grafana -n grafana export POD_NAME=$(kubectl get pods --namespace grafana -l "app.kubernetes.io/name=grafana,app.kubernetes.io/instance=grafana" -o jsonpath="{.items[0].metadata.name}") kubectl --namespace grafana port-forward $POD_NAME 3000 # Open another terminal to keep this command running after execution. Kubectl get secret --namespace grafana grafana -o jsonpath="{.data.admin-password}" | base64 --decode # | pbcopy # For Mac users, uncomment this line to copy the password to the clipboard. Now, access Grafana in your browser at http://localhost:3000 . Use the user ID: admin and enter the password output from the last command to login. Configure each data source as follows: Loki: http://loki:3100 Prometheus: http://prometheus-server.prometheus.svc.cluster.local This completes the monitoring setup. For the code, create a new Ktor application from a template in InetelliJ. Select the following from IntelliJ. If you’re using VS Code, you can download it from this site . In this example, we will prepare the same container image for both the frontend and backend, so we only need to generate one project. Add the following Jib configuration for building with Docker. Then, confirm that you can build by running the Jib Gradle task ./gradlew jibDockerBuild . plugins { application kotlin("jvm") version "1.8.21" id("io.ktor.plugin") version "2.3.1" + id("com.google.cloud.tools.jib") version "3.3.1" } ... + jib { + from { + platforms { + platform { + architecture = "amd64" + os = "linux" + } + } + } + to { + image = "sample-jib-image" + tags = setOf("alpha") + } + container { + jvmFlags = listOf("-Xms512m", "-Xmx512m") + mainClass = "com.example.ApplicationKt" + ports = listOf("80", "8080") + } +} Let's change the log level of Logback so that we can keep an eye on the logs we added this time. Also, to avoid noise, we’ll hide the monitoring endpoints. - <root level="trace"> + <root level="info"> install(CallLogging) { level = Level.INFO - filter { call -> call.request.path().startsWith("/") } + filter { call -> !arrayOf("/livez", "/readyz", "/metrics-micrometer") + .any { it.equals(call.request.path(), ignoreCase = true) }} callIdMdc("call-id") } Once you have added this to the source, the container image will be deployed to Kubernetes with the following command and the application will be executed. Check Grafana to see if logs and metrics are being streamed correctly. Since the services.yaml file is a bit lengthy, it’s provided at the very end. ./gradlew jibDockerBuild && kubectl apply -f services.yaml # Update the Docker tag with each build # If you have Skaffold installed, you can use the following commands: skaffold init # Generates yaml files skaffold run # Builds and deploys the application once skaffold dev # Continuously builds and deploys each time you update the source code Including portForward in the Skaffold file makes it convenient to access the application at localhost:8000 automatically. apiVersion: skaffold/v4beta5 kind: Config metadata: name: observability build: artifacts: - image: sample-jib-image - buildpacks: # Remove this as it slows down the build - builder: gcr.io/buildpacks/builder:v1 + jib: {} # Make sure JAVA_HOME is set to the correct PATH to avoid execution errors. manifests: rawYaml: - service.yaml +portForward: + - resourceType: service + resourceName: frontend + namespace: sample + port: 8000 + localPort: 8000 apiVersion: v1 kind: Namespace metadata: name: sample --- apiVersion: apps/v1 kind: Deployment metadata: name: backend-deployment namespace: sample spec: replicas: 2 selector: matchLabels: app: backend template: metadata: labels: app: backend spec: containers: - name: backend image: sample-jib-image:alpha imagePullPolicy: IfNotPresent ports: - containerPort: 8080 # Comment out until implementing the liveness and readiness probes # livenessProbe: # httpGet: # path: /livez # port: 8080 # initialDelaySeconds: 15 # periodSeconds: 20 # timeoutSeconds: 5 # successThreshold: 1 # failureThreshold: 3 # readinessProbe: # httpGet: # path: /readyz # port: 8080 resources: requests: memory: "512Mi" cpu: "500m" limits: memory: "512Mi" cpu: "750m" --- apiVersion: v1 kind: Service metadata: name: backend namespace: sample annotations: prometheus.io/scrape: 'true' prometheus.io/path: '/metrics-micrometer' prometheus.io/port: '8080' spec: selector: app: backend ports: - protocol: TCP port: 8000 targetPort: 8080 type: ClusterIP --- apiVersion: apps/v1 kind: Deployment metadata: name: frontend-deployment namespace: sample spec: replicas: 2 selector: matchLabels: app: frontend template: metadata: labels: app: frontend spec: containers: - name: frontend image: sample-jib-image:alpha imagePullPolicy: IfNotPresent ports: - containerPort: 8080 # Comment out until implementing the liveness and readiness probes # livenessProbe: # httpGet: # path: /livez # port: 8080 # initialDelaySeconds: 15 # periodSeconds: 20 # timeoutSeconds: 5 # successThreshold: 1 # failureThreshold: 3 # readinessProbe: # httpGet: # path: /readyz # port: 8080 resources: requests: memory: "512Mi" cpu: "500m" limits: memory: "512Mi" cpu: "750m" --- apiVersion: v1 kind: Service metadata: name: frontend namespace: sample annotations: prometheus.io/scrape: 'true' prometheus.io/path: '/metrics-micrometer' prometheus.io/port: '8080' spec: selector: app: frontend ports: - protocol: TCP port: 8000 targetPort: 8080 type: LoadBalancer Thank you for following along this far. Let’s delete the resources created in this blog using the following commands. skaffold delete docker rmi $(docker images | grep 'sample-jib-image') # Kubectl delete all --all -n sample # If you didn’t use skaffold helm uninstall grafana -n grafana helm uninstall loki -n grafana helm uninstall prometheus -n prometheus
Introduction Hi, we're Yao, Bahng, and Lai from the Global Development Division. We're mobile app engineers, usually developing Global KINTO App . A few months ago, we investigated Kotlin Mltiplatform Mobile (KMM) as a preliminary activity for the future of Global KINTO App. See our previous article on this. The results of our previous investigation indicate that KMM is an excellent solution for rapid product development. Now that KMM has been revealed as a new approach compatible with Compose UI, we decided to investigate it further. Based on that investigation, this article discusses app development using Kotlin Multiplatform Mobile (KMM) and Compose Multiplatform. Before getting into the main topic, let's first clarify three points: What is KMP? What is KMM? What is the relationship between KMP and KMM? The answers are as follows: KMP: Kotlin Multiplatform, referring to the technology used to develop applications across multiple platforms using Kotlin, along with its entire ecosystem. KMM: Kotlin Multiplatform for Mobile. One of the primary use cases for KMP is code sharing between mobile platforms. In addition to KMP, several technologies specifically for mobile app development are collectively referred to as KMM. The graph below illustrates the relationship between KMP and KMM. Reference: -- 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 Cross-Platform You may wonder about the advantages of cross-platform development, particularly when considering Kotlin Multiplatform Mobile (KMM) as cross-platform solutions. Here are the benefits: Cost-effective: Cross-platform development allows the use of a single codebase across multiple platforms, eliminating the need for separate platform development teams and reducing the cost of app development. Faster deployment: By leveraging a single codebase, developers can create and launch applications on multiple platforms simultaneously, significantly reducing development time and accelerating time to release. Simplified maintenance and updates: By using a single codebase, apps can be easily maintained and updated, allowing changes to be made once and propagated across all platforms. This streamlines the maintenance process and ensures that all users have access to the latest features. Consistent user experience: By using cross-platform development tools and frameworks, a consistent look and feel can be maintained across different platforms, providing a unified user experience. This can lead to improved user satisfaction and user retention. Shared resources and skills: Developers familiar with cross-platform tools and languages can create apps for multiple platforms. This allows for more efficient use of resources and maximizes the return on investment in developer skills and training. History of Cross-Platform Development Tools for Mobile In 2009, PhoneGap was created and later renamed Apache Cordova. In 2011, Xamarin was created by Mono and later acquired by Microsoft. In 2015, React Native was created by Facebook (Meta). In the mid-2010s, designer Frances Berriman and Google Chrome engineer Alex Russell coined the term "progressive web app (PWA)," and Google made several efforts to popularize it. In 2017, Flutter was created by Google. In 2021, KMM was created by JetBrains. This means that KMM is currently the most recent cross-platform solution available. Logo source: -- 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 Why is KMM Different? Shared business logic: KMM reduces code duplication and maintains consistency between Android and iOS by allowing code related to business logic, networking, and data storage to be shared across platforms. True native UI: KMM allows the use of platform-specific tools and languages (e.g. XML for Android and SwiftUI or UIKit for iOS) for UI development, resulting in a more native look and feel compared to other cross-platform solutions. Performance: Kotlin code is compiled into native binaries for each platform, resulting in high-performance applications that are comparable to native development. Seamless integration: KMM can be integrated into existing projects, developers can adopt it incrementally and migrate sharing logic to Kotlin without having to completely rewrite their apps. Interoperability with native libraries: KMM seamlessly interoperates with both Android and iOS native libraries, facilitating the use of existing libraries and frameworks. Benefits of the Kotlin language: Kotlin is a modern and concise language that provides similar functionality to existing alternatives while reducing redundant code, with tool support from JetBrains. The above points are explained in detail below. (1) Shared Business Logic KMM is used when implementing the data, business, and presentation layers in new projects. Flexibility: KMM allows developers to determine the scope of code they want to share, offering a flexible implement balanced with platform-specific code as needed. Consistency assurance: While differences in UI can be easily detected in QA testing, inconsistencies between Android and iOS are difficult to detect in logic. By using KMM, the same code can be used, thus ensuring consistency. (2) Truly Native UI KMM supports native UI, uses native UI components, and follows platform-specific design patterns. Android: xml, Jetpack Compose, etc. iOS: UIKit, SwiftUI, etc. UI performance: KMM uses native UI components, and since the Kotlin code is compiled into native binaries for each platform, its performance is generally comparable to native apps. Easy platform updates: KMM makes it easy for developers to update new platform features and designs. Because it uses the native UI framework for each platform. (3) Performance No JavaScript bridge is required; no reliance on third-party libraries. Uses the system's default rendering engine, reducing resource consumption compared to other cross-platform solutions. Native code compilation: KMM compiles Kotlin code into native binaries for each platform. This native code compilation enhances app efficiency and overall performance. Android: Standard Kotlin/JVM iOS: Kotlin/Native Compiler (Objective-C) (4) Seamless Integration No need to bridge native modules or rewrite existing code. Phased adoption: KMM can be gradually introduced into existing native Android and iOS projects. This allows teams to share business logic, network, and data storage code across platforms in phases, reducing the risks associated with a complete technology switch. Multiple approaches to using KMM modules in iOS CocoaPods Gradle plugin and git submodules Framework Swift Package Manager (SPM): Starting with Kotlin 1.5.30, KMM modules are available in iOS projects using the Swift Package Manager. (5) Interoperability with Native Libraries Access to native APIs and libraries: KMM provides direct access to native APIs and libraries, facilitating easy integration with platform-specific functions and hardware components such as sensors and Bluetooth. Seamless integration with platform-specific code: KMM allows for writing platform-specific code as needed, which is useful when dealing with complex native libraries or accessing features not available through shared Kotlin code. Kotlin/Native: KMM uses Kotlin/Native for iOS. This allows seamless interoperability with Objective-C and Swift code. This means that existing iOS libraries and frameworks can be used without additional bridging or wrapping code. (6) Kotlin Language Benefits Language features: Modern, static typing, null safety, extension functions, data classes, SmartCast, interoperability with Java Tools and support: Kotlin provides exceptional support and first-class integration in Android Studio and IntelliJ IDEA. Industry adoption: Kotlin has seen rapid adoption since becoming the official programming language for Android development. Many backend developers also use Kotlin. What Kind of People are Using KMM? In fact, several companies have adopted Kotlin Multiplatform Mobile (KMM) for mobile app development. Here are some notable examples: Netflix: Netflix uses KMM in some of its internal tools to share code between Android and iOS apps. VMware: VMware uses KMM for cross-platform development of Workspace ONE Intelligent Hub app (employee management tool for Android and iOS). Yandex: Yandex, a Russian multinational technology company, has adopted KMM in several of its mobile apps, including Yandex Maps and Yandex Disk. Quizlet: Quizlet, an online learning platform, uses KMM to share code between Android and iOS apps, improving development efficiency. These companies represent diverse industries, and their adoption of KMM demonstrates the flexibility and usefulness of technology in different contexts. As KMM becomes more popular, it's likely that even more companies will adopt KMM to meet their cross-platform mobile development needs. Reference: -- JetBrains "Case studies. (n.d.). Kotlin Multiplatform." Accessed June 1, 2023 How to Easily Create a KMM Project Given these benefits, would you like to create a KMM project and give it a try? The following is a guide on how to do this. Download the latest Android Studio. In Android Studio, select File > New > New Project. Select Kotlin Multiplatform App in the list of project templates, and click Next. Specify the Name of the new project and click Next. In the iOS framework distribution, select the Regular framework. Keep the default names for Applications and Shared folders. Click Finish. -- JetBrains "Create your first cross-platform app | Kotlin. (n.d.). Kotlin Help." Accessed June 1, 2023 Mobile App Architecture Using KMM The following graph is an example of one of common KMM patterns. This architecture takes full advantage of KMM's characteristic code sharing. Data persistence, including cache, database, network, use cases, and view model are all implemented in KMM. For UI, both Android and iOS use native UI components. Support is provided for both older frameworks such as XML and UIKit, and newer frameworks such as Jetpack Compose and SwiftUI. This architecture allows business logic modules written in Kotlin to be imported into iOS as SDKs. This allows iOS developers to focus on UI development for efficient development. Here's some iOS code for a simple screen with an FAQ list. Except for the common UI Utility Class, this is all that needs to be implemented. #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()) } ) } } That's not all about KMM. KMM has even more potential! Architecture That Shares UI Code In addition to business logic code, KMM can also share UI code using Compose Multiplatform. As we discussed earlier, Kotlin Multiplatform Mobile (KMM) is primarily used for implementing shared business logic, but it also supports shared UI development. Compose Multiplatform is a declarative framework for sharing UI across multiple platforms using Kotlin. Based on Jetpack Compose, it was developed by JetBrains and open source contributors. Combining KMM with Compose Multiplatform allows for the building of both logic code and UI using the Kotlin language. Reference: -- 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 Comparison of Different Patterns of KMM Architecture Assuming a mobile project is being developed, the estimated workloads for each client are as follows: UI: 2 people, Presentation: 1 person, Business/Domain: 1 person, Data/Core: 1 person The workloads saved from this point is based on the percentage of code written by KMM. Pattern 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 Total 9 8 7 5 workload cost -10% -20% -30% -50% KMM can reduce workloads by up to 50%. The biggest advantage of KMM compared to other cross-platform solutions is its flexibility in code sharing. How much code to share with KMM is entirely up to us. Other cross-platform solutions do not offer this level of flexibility. Summary Cons of KMM Of course, every tool has its drawbacks. KMM is no exception. Limited platform support: Kotlin Multiplatform Mobile can target multiple platforms, but not all platforms are supported. For example, it does not currently support web or desktop applications. Learning cost: If you are not familiar with Kotlin, there is a learning cost to effectively use it for multi-platform development. Framework compatibility: Kotlin Multiplatform Mobile can be used with various frameworks, but is not compatible with all of them. This limits your options and may require you to work within certain constraints. Maintenance overhead: Maintaining a multiplatform codebase can be more complex than maintaining a separate codebase for each platform. This added complexity can lead to increased overhead in testing, debugging, and maintenance. Tool limitations: Some tools and libraries may not be compatible with Kotlin Multiplatform Mobile, making development more complicated or requiring the search for alternative solutions. Applications As mentioned above, integrating KMM's architecture into a project can be considered in various situations, each with it’s pros and cons. Pattern A B C D General existing project ✓ ✓ ✓ ? Simple existing project ✓ ✓ ✓ ✓ Complex existing project ✓ ✓ ✓ ✗ New project ✓ ✓ ✓ ✓ Prototype ✓ ✓ ✓ ✓ With the technical benefits covered, let's get back to the actual development process. Like most mobile development teams, ours is small. Given our limited engineering resources, when faced with a significant change, such as upgrading from version 1.0 to 2.0, we need to collaborate with other divisions and both onsite and offshore outsourcing teams to ensure a quick release. However, there are several problems in this process: Seamless collaboration between different teams is challenging. With more developers and different teams in different offices, communication costs increase. It becomes difficult to maintain consistency across different teams. Working with external teams makes it difficult to manage the security of sensitive information. KMM can address almost all of these problems by developing core modules, defining protocols, and adopting a separate approach for UI and logic development: Allows each team to focus on their part. Can greatly facilitate collaboration. Reduces the time and cost required for communication. By having the core modules developed by the KMM team on a consistent basis, most inconsistencies are eliminated in advance. Although KMM supports a single codebase, the separation of the UI and logic layers allows for the use of multiple repositories. The core modules are developed by the KMM team and the SDK is provided to external teams. This eliminates the need for the source code to be disclosed to external teams and reduces the risk of leaking confidential information. This is difficult to achieve with other cross-platform technology solutions. In conclusion, it can be said that KMM brings significant benefits not only in terms of technical advantages but also in fostering cooperation across divisions and companies. Conclusion Given the importance of KMM in new projects and its potential for significant workload savings, we have already integrated KMM into new projects for the next major release. We will continue to monitor new technologies and tools related to KMM and seek opportunities to further enhance efficiency.
Introduction Hello. My name is Nakaguchi and I am the team leader of the iOS team in the Mobile Development Group. In my day-to-day job, I'm involved in iOS development for: KINTO Kantan Moushikomi App (KINTO Easy Application App) Prism Japan ( Smartphone app version / Recently released web version ) As a retrospective of iOSDC Japan 2024, held from Thursday, August 22nd to Saturday, August 24th, we hosted the [iOSDC JAPAN 2024 AFTER PARTY] on Monday, September 9th, 2024. I'd like to reflect on why I held the event, the preparations leading up to it, and how it went. In particular, regarding the part about "why it was held," I will present my own thoughts, and I would be happy if many people can relate to them. This blog is for: Those who participated in this event Those who attended iOSDC Those who often participate or would like to participate in events Those who organize or would like to organize events I'm also writing this as a Tech Blog post to share my experience with as many people as possible, because my own motivation has exploded by hosting this event. Why I Held the Event This event has been planned in my mind since around April. If you ask me why I planned it, I honestly don't think I'd be able to express it in words. Since I took on the role of team leader in October of last year, I have made an effort to attend many events that interest me, not only those related to iOS, but also those related to development productivity, organizational management, engineering managers, and so on. In the midst of this, I noticed the following feelings arising. Participating in an event really boosts your motivation. The people who speak at events and organize them are so cool! If I had to put my feelings into words, it would be: "It's kind of cool! I want to host an event myself!" That's how I felt back in April. However, the purpose of an event that involves investing a lot of resources, such as money, time, and people, cannot be explained simply by "because it's cool." After that, I begin to struggle within myself about the significance of hosting an event. Even now that the event has ended, I don't think I've yet reached a clear answer. (I'm just grateful that we were able to hold the event under such ambiguous circumstances.) When hosting an event as part of an organization, certain expectations are inevitably placed upon you. Commonly mentioned benefits include "increasing the organization's presence," "spreading the word about services," "leading to recruitment," etc. I think these are all great benefits of holding an event properly, and if these results appear, I think the event can be called a great success. However, there are some aspects that I personally don't feel quite right about. I believe that most participants in IT industry events attend for the purpose of self-improvement, such as "I want to acquire new knowledge," "I want to expand my network," or "I enjoy participating in the event itself," and I think it is very rare for people to attend events because they want to know what kind of organization the organizer is, what services they offer, or want to change jobs to that company. In the midst of this, after struggling with the significance of holding events, I came to my own conclusion. "I want my motivation to be contagious to as many people as possible." As I mentioned above, when I participate in an event, I feel a huge boost in motivation, and I think many others feel the same way. I believe that if there is even one more person who wants to work harder tomorrow, the accumulation of those efforts will lead to the betterment of the world. Also, as motivation increases, some people may want to host events like I did, or speak at one. In turn, others will see this and want to do the same. I believe that good motivation like this is surely contagious! So, at this stage, I decided to hold this event with the thought that "I want my motivation to be contagious to as many people as possible" as the significance of the event (although I hadn’t organized my thoughts to this extent back in April when I first came up with the idea). (And, from an organizational perspective, the mere fact that it increases motivation does not mean that we should start holding events one after another, so it looks like the days of struggle will continue for a while.) Next, I would like to give you an overview of this event. Event Overview Event name: iOSDC JAPAN 2024 AFTER PARTY Date and time: Monday, September 9, 2024 from 19:00 Participants: Around 20 people Three companies: WealthNavi, TimeTree, and us held a joint meeting as the iOSDC Retrospective. There were three LT presentations, one from each company, plus a panel discussion with three people, one from each company. Now, let me introduce the process leading up to this event. Until the Event In April, I came up with the idea to hold a mobile development-related event, but I was unsure how to proceed. We have a Developer Relations Group (DevRel) that provides support for event management, so I thought that if I reached out to them, I could run the event smoothly without any issues. On the other hand, Attracting attendees Calling for speakers Deciding on the theme of the event are challenging even with the support of our Developer Relations Group. Therefore, we've determined that organizing a mobile-related event on our own would be difficult. Under this circumstance, we wanted to ask Findy for their help, as they put a lot of effort into hosting events and have extensive know-how in attracting attendees and recruiting speakers. So, we attended this event which was held in May . I have also posted a blog Event Participation Report , so please take a look. This event gave us the opportunity to exchange information with the person in charge at Findy. After much discussion about what kind of event to hold, we were introduced to WealthNavi and TimeTree, and decided to hold an iOSDC retrospective event. I want to extend my thanks to Findy for their advice and help in organizing the event, and to WealthNavi and TimeTree for co-hosting the event. After the three companies decided to hold an iOSDC Retrospective, many things were decided smoothly, including: How to structure the event Speakers and panelists for the panel discussion Date and time of the event Now that the event recruitment page on Connpass has been successfully completed, the next step is to recruit participants. This time, all three companies shared the desire to place emphasis on communication with event participants, so the event was offline only. Since the event was held in our company's event space, we aimed to recruit around 30 people, given the capacity. We opened the Connpass page on Thursday, August 8th, 2024, and within a few days we had about 10 people register to attend, which we thought was a good number of participants. However, the actual event promotion would take place from August 22nd to 24th, when the iOSDC would be held, so I thought it would be up to us to see how much we could increase participation during that period. This year, we displayed our first sponsor booth, which allowed us to promote the event there and carry out PR by posting on our official X page during the iOSDC period. As a result, the number of registrations that increased during the iOSDC period was **"0"** ...! *To be honest, I was lazy about the event's call for participants.* Looking back, I think there was a need to improve the way we promoted the event at the sponsor booth. Rather than just handing out flyers, we should have put more thought into creating a flow of people to register on the spot (for example, handing out novelties to people who registered). Here is a reminder for next time. In fact, when we checked the statistics on the event page on Connpass, we could see that there were absolutely no registrations between August 22nd (Thu) and 24th (Sat), and that there was no increase in page views at all. ![](/assets/blog/authors/nakaguchi/2024-09-12-after-iosdc/connpass.png)*Statistics confirmed by Connpass* After that, up until Monday, September 9th, participants gradually registered at the pace shown in the image above. I also had the opportunity to take the time to announce the event when I attended another company's event, so we were able to have 24 participants registered as of the day of the event. I felt that the theme of "iOSDC Retrospective Event" was effective in drawing in a certain number of people. Although we did not reach our initial goal of 30 registrations, I personally felt that the number of registrants was more than sufficient for the first organized event. Now all that was left was to wait for the day. On the Day of Event These kinds of events are often subject to cancellation on the day of the event for a variety of reasons. In fact, several participants unfortunately canceled on the day of this event as well. However, with the day arriving, I didn't have the time to be overly excited or upset about the increase or decrease in the number of participants. We focused on making this an event that was worth attending for co-hosts WealthNavi and TimeTree, as well as for all participants who joined us on the day. Here's a quick look back at what happened on the day. We waited nervously for everyone to arrive. The venue seemed to be set up. Venue set-up completed It was 7pm, and with WealthNavi, TimeTree, and all the participants present, the first LT session was about to begin. "DX: Digital transformation starting with Package.swift" presented by Muta-san from WealthNavi. Muta-san's presentation I learned a lot from his explanation of the basics of Swift Package Manager, including aspects that I thought I knew but actually did not. I believe it was a valuable opportunity to hear about the initiatives of WealthNavi and what they envision for the future. I also learned a lot from the explanation of Swift 6, which is coming up soon. Next is the second LT. "Morphological Analysis of iSODC Proposals to Explore Trend Transitions" presented by Sakaguchi-san from TimeTree. Sakaguchi-san's presentation I was very interested in this presentation from the moment I saw the title. I have attended iOSDC several times in the past, and I feel that there are certain trends in the sessions, which was interesting to see reflected in the proposals. In addition, this analysis tool was created using Xcode, and it was fun to see it being demonstrated on a simulator during the presentation. Next is the third LT. "I want to share what we did before our first exhibit at iOSDC" presented by Hinomori-san from KINTO Technologies. Hinomori-san's presentation Since this was our first time exhibiting at a sponsor booth, he shared the challenges we faced during the preparation period. I was also involved in preparing some of the exhibits, and it was quite difficult to figure out through trial and error what kind of content would resonate with visitors and how to make it more visually appealing. Please take a look at what we produced as a sponsor, which is introduced in more detail on the Tech Blog here . Next, there was a panel discussion, followed by a break and a toast. The panelists are: Cho-san from WealthNavi Masaichi-san from TimeTree Hinomori-san from KINTO Technologies And I was the moderator of the session. Panel Discussion Members These topics were prepared in advance as we looked back on the iOSDC. The topics were decided after interviewing the panelists in advance to find out what kind of content they would be interested in. Panel Discussion Topics Due to time constraints, we were unable to discuss all the topics, but we made a conscious effort to proceed by observing the topic at hand and picking out topics that fit the flow of the moment. They talked about the status of iOS development at each company, their efforts towards iOSDC, and the changes this year compared to previous years. Panelists Finally, a group photo was taken with all participants. Group photo Thoughts After the Event As I mentioned at the beginning, I started planning for this event around April and was able to hold it. I was constantly anxious about whether the event could be held smoothly, whether the participants would show up, and whether my moderation on the day would go well. I personally feel that we were able to hold a very successful event, thanks to the cooperation of WealthNavi and TimeTree, our co-hosts, as well as the support of the Developer Relations Group and the organizing staff on the day of the event. Of course, everyone who participated on the day made the event a great success. I would like to express my sincere gratitude to everyone who was involved in this event. ● What I liked It was invaluable to be able to connect with other companies such as WealthNavi, TimeTree, and Findy when hosting the event. Additionally, this was my first time organizing an event, I gained confidence from being able to successfully complete it. ● What I'd like to improve in the future As I mentioned earlier, I find it quite challenging to attract participants. Since I haven't found a good solution to this yet, I'd like to carefully consider it with everyone involved the next time we organize the event. I also wish more team members from our iOS team could have participated this event. At this event, Assistant Manager Hinomori-san took the stage as a LT speaker and panelist. While he usually has many opportunities to speak at events, I wanted to encourage team members who don't often get the chance to take on that challenge. However, when we reached out for speakers within the company, there were no volunteers from the team members, so we decided to have Hinomori-san take the stage. I personally feel that there are major areas for improvement going forward, such as making efforts to lower the hurdles to speaking at the internal recruitment stage and establishing a support system for preparing for speaking sessions. Conclusion In October, we are planning to hold a review event for Doroidkaigi 2024 together with WealthNavi and TimeTree, and we hope to continue holding such events on an irregular basis in the future. As I said at the beginning, "I want to spread motivation to as many people as possible," and I feel that the person who was most motivated by this event was none other than myself. If there were participants who felt that their motivation had increased, then I would consider this event a great success. I'd like to continue to motivate everyone involved through various activities, including holding events like this one.