TECH PLAY

KINTOテクノロジーズ

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

975

Introduction Hello, my name is Numata, and I am part of the Project Promotion Group. I work hard every day as a backend engineer for KINTO FACTORY. In this article, I will talk about KINTO FACTORY services and the DX initiatives we did this summer. What is KINTO FACTORY? Many people in Japan think of KINTO as a subscription service, but KINTO FACTORY (hereinafter, FACTORY) offers various upgrade services so that you can ride your favorite vehicle over a longer period of time. To be more specific, you can customize your vehicle by updating its software, changing its interior and exterior such as seat replacement and wheel cap replacement; or improving the opening and closing speed of your doors. The range of available Toyota Group vehicle models (Toyota, Lexus, and GR), products, and areas where customizations are possible, is gradually expanding too. I always thought that once you buy a vehicle you do not really change any features until you switch to another vehicle, so I feel that the efforts of FACTORY and its approach to modularity are challenging and interesting. Modification Process FACTORY provides services not just through KINTO, but also in cooperation with Toyota and its dealer network throughout Japan. *The numbers in the image are for illustrative purposes and are not fixed. First, when a customer applies for a product on the FACTORY site, FACTORY orders the necessary parts from Toyota and shares the application details with the dealer of the customer's choice. After confirming the information in the application, the dealer sets up the arrival date with the customer. Once the delivery date is confirmed, the customer takes their vehicle to the dealer on the day of receipt, and the dealer carries out the modification with the delivered parts. As soon as the modification is completed, the dealer contacts the customer and delivers the vehicle to them. Specifics may vary depending on product and the desired modification, but that is the general process. DX of Modification Certificates Some of you may have never heard of modification certificates before (I learned about them after I joined the company). It is given in Japan to customers after a vehicle modification is completed, and written on it appears the details of what was modified, its date and time, the dealer where it was performed, along with other information. Before, the modification certificate was manually created by the dealer that delivered the vehicle to the customer, but since August, it has been possible to issue it on FACTORY. Process of Issuing a Modification Certificate Once the status of a purchased item changes to modification complete , you can issue a modification certificate from your purchase history on My Page (FACTORY refers to My Page as My Garage😃). When you click the modification certificate button, the certificate is displayed in a separate tab and can be downloaded as shown in the figure below. Currently, modification certificates can be issued for any products except for GR-related ones and event products. It also supports the invoice system that we implemented in October 2023, and payment statements can be issued from FACTORY. Architecture and Technology Stack FACTORY runs on AWS, and we use a microservice architecture like the one in the rough diagram above. The microservice that issues the modification certificate was developed using Go as programming language and uses the gopdf library to publish PDFs. The issued modification certificate is stored in S3. PDF Templates Since our company uses Office 365, we use Excel to manage PDF templates. We modify the Excel template and export it as a PDF whenever there is a change. Toyota Modification Certificate Lexus Modification Certificate We struggled with the fact that, not only the design of the modification certificate varies depending on the model, but the number of characters in the product name also varies, so it was difficult to decide on one layout that would not affect either of them. For convenience, we made it A5 just like the proofs of modification that were made manually. Side note: This was my first time handling PDFs with a program. I honestly did not think it was so straightforward. Beyond the Modification Certificate DX We can now provide a better experience for both customers and dealers by making it possible to easily generate modification certificates on FACTORY instead of having them made manually by dealers. Aside from modification certificates, there is still a lot of work that is done manually, and I think there is a lot of potential for DX in the automotive industry. FACTORY will keep working on various initiatives, to offer even better experiences in the future. Conclusion FACTORY is looking for new partners to liven up the company together. We do casual interviews, so feel free to apply! @ card
Introduction Hello, I am Aritome from the Development Support Division at KINTO Technologies Corporation. I am currently working mainly in the areas of organizational development, and education and training in order to address what is necessary for the company in a quick and agile manner. KTC holds a monthly Development and Organization Headquarters Meeting in which all employees (employees, contract employees, and temporary employees) participate. Today, I will talk about the twists and turns of the meeting. Some of you may be thinking, 'Hey, this is a Tech Blog, but this content isn't technical at all!' However, I want to assure you that I'm writing this article from our Osaka Tech Lab. So, even if the content might not seem technical, the place where I'm writing it is definitely tech-related! Also, I talk with various companies because of my work, and I feel that every company is worried about people and team building, including topics like building an organizational culture and retaining employees. I believe that making products means making people! I hope this will be a reference for those who are also struggling to build their organization. <What is a Development and Organization Headquarters Meeting?> KINTO Technologies Corporation's Development and Organization Headquarters Meetings started in July 2021, before I joined the company, with three departments announcing their respective initiatives in something similar to an in-house LT meeting. According to the minutes of the meeting at the time, there were about 160 participants... (We have grown considerably as a company and now have more than 330 employees.) The meeting has been held once a month since then. Each month, Kageyama, our Executive Vice President, gives a presentation, and two or three divisions talk about their activities in a LT (Lightning Talk) format. After that, one of the divisions gets a Kageyama Award. Kageyama gives a complimentary prize. <Why is the Development and Organization Headquarters Meeting Held in the First Place?> At first, we had a rigid culture in which the departments had a vertical structure and did not share information much, As we were in the business launch phase, we were very busy with practical work that we had to prioritize, and we put building a corporate culture on the back burner As the number of employees increased, so did the distance from Vice President Kageyama and the rest of employees. Due to the launch of the business during the COVID-19 pandemic, there were few chances to meet offline, and the company wanted everyone to communicate with each other at least once a month. Originally, the manager (Currently Mr. K, General Manager of the Development Support Division) was in charge of planning and managing, but when I joined the company, we started reviewing the meetings. We took over the operations of the Headquarters Meeting around April 2022, and have been improving it through trial and error for a year and a half. To prepare for the renovation, we identified the following three challenges and worked on them. (1) Improving communication We started entirely online using an LT meeting format, so at first, employees just listened to presentations one-sidedly instead of actively communicating with each other. I remember being surprised when a lot of employees attended halfheartedly, and I thought it felt like a management meeting. (2) Improving the Kageyama Award Also, I felt that it was a burden for employees in the workplace to make a 5-minute LT presentation even though they were busy. As the organization expanded, the number of managers other than Kageyama increased, so we thought, "We need to create a system that lets the board show the efforts of the members to more people." (3) Understanding other departments' work and sharing activities We received comments in the past such as “I don’t know what other departments are doing” in our questionnaires, so we wanted to use the Headquarters Meeting and other events to better understand what work and activities other departments were doing. (1) Improving communication We implemented a tool called Comment Screen , that displays emojis and comments on a screen in real time, and it encouraged more two-way communication. There were some who liked it because it was in real time, while some thought that it felt cheap, or that the presentations were hard to read. We kept experimenting, and now we communicate through Slack. All employees join the Headquarters Meeting Communication Channel when they join the company and use Slack to communicate in real time during the meeting every month. On each session, we added a friendly poll with questions such as, "What did you eat today for lunch?" By the way, there are a lot of restaurants with delicious food near our office, and a lot of members report going to lunch, but a there is also a handful who sadly say "Red Bull" or "I didn't eat..." lol But, just to be clear, our company is a place where everyone can take breaks easily! (Honestly, our company culture makes it easy to take breaks, and I think that is one of our company's good points) (2) Improving the Kageyama Award The purpose of the program was redefined as "managers should not miss the daily efforts of their members," and the style was changed so that managers recommend candidates, give the reason for their recommendation, and write an award speech. Before, Vice President Kageyama decided the winners, but they are now decided in a meeting with the managers in advance. With this system, team members no longer have to make tedious materials for the sake of being nominated and getting awards, and the managers were more motivated to pay attention to the members' daily efforts. I hope that the corporate culture changes so that people's daily efforts are assessed naturally. (3) Understanding other departments' work and sharing activities We made changes so that every month, the managers of each group take turns sharing information on projects that are being focused on and their activities. Kageyama also gives additional comments when necessary. Thanks to managers sharing information on activities and Kageyama's additional notes, the company's activities felt more concrete to employees, and they were more motivated to be informed. We also shake things up so that it’s not always the same announcements, and during the New Year period for example we introduce things such as the kanji of the year or the goals for next year. In addition, we post information on events from Twitter, announced activities related to the Tech Blog, and try to make the Headquarters Meeting an event where people can understand the company's activities. In addition to the above, the results of the previous month's questionnaire are announced at the start of each meeting, and we try to make communication during the meeting as open as possible. We are still making improvements, but the attendance rate of the Headquarters Meeting is gradually increasing. We have almost every employee (close to 300) attend every month, even as more employees join the company. The number of people participating online with their webcam on and making comments on Slack is also increasing gradually! We also get more happy comments through our questionnaires with each meeting. [Some Questionnaire Responses from our Employees (Examples)] It is really interesting to learn what each department is doing. It is also helpful to hear Mr. Kageyama's thoughts on each department! I like how the meeting is packed with information. Generally speaking, this kind of meeting tends to be light on content, or you can just read the materials afterward to get the information, but I think KTC's company-wide meetings have been different recently (in a good way). I can find out the status of departments that I normally don't have any connection with. I find that helpful. Conclusion What do you think? We will start taking on challenges such as announcing the 2024 kanji of the year and holding in-person events (holding the Headquarters Meeting offline). I think one of the good things about our company is that we are open to new initiatives and challenges. We can implement initiatives that are difficult for conventional large organizations and start them in a relatively quick and agile manner. Even if they fail, for better or for worse, they serve as precedents (and we don't get too mad?), and we can use the experience for our next actions. And of course, if you succeed, people will praise you (lol) Also, we have many employees who say, "I want to do it!" and lend us their strength. Please let us know about your company's initiatives when we meet at events! We will challenge ourselves so we won't lose!
はじめに KINTOテクノロジーズでmy routeのAndroid側を開発しているHand-Tomiと申します。 Android 14が2023年4月12日にリリースされてそろそろ1年になります。 しかし、新しく追加された「地域別の設定」についてまだ十分理解されていない方も多いと感じ、この記事を書くことにしました。 多言語対応のアプリケーション開発において「地域別の設定」を理解せずに進めると予期せぬバグが発生するリスクがあります。この記事を読んで、そうしたバグを予防できれば幸いです。 この記事で解説するもの Locale.getDefault() == Locale.JAPAN :::details コード解説 Locale : 言語、国、地域に基づく特定の文化的、地理的設定を表すクラス Locale.getDefault() : 現在のアプリケーションのデフォルトの Locale を返す Locale.JAPAN : 日本の言語( ja )と国( JP )の設定を表すLocaleのインスタンス ::: 上記のコードでは端末に「日本語(日本)」が設定されている場合、 true が出力されますか?それとも false が出力されますか? 正解は、Android 13以下では true であり、 Android 14以上の場合、これだけの情報では不明 です。 この記事では、 Android 14以上の場合、なぜ不明なのか を解説します! Androidでの Locale とは Locale は、言語、国、地域に基づいた文化的、地理的設定を表すクラスです。この情報を利用して、Androidアプリケーションは多様なユーザーに適応したアプリケーションを構成することができます。 Locale は主に言語や国を扱いますが、 LocalePreferences を使用することで、より多くのデータを抽出することが可能です。 val locale = Locale.getDefault() println("calendarType = ${LocalePreferences.getCalendarType(locale)}") println("firstDayOfWeek = ${LocalePreferences.getFirstDayOfWeek(locale)}") println("hourCycle = ${LocalePreferences.getHourCycle(locale)}") println("temperatureUnit = ${LocalePreferences.getTemperatureUnit(locale)}") 「日本語(日本)」の設定を持つ端末で上記のコードを実行すると、以下のようになります。 calendarType = gregorian : 暦法 = グレゴリオ暦 firstDayOfWeek = sun : 週最初の曜日 = 日曜日 hourCycle = h23 : 時間周期 = 0~23 temperatureUnit = celsius : 温度 = 摂氏 「地域別の設定」とは Android 14から導入される「地域別の設定」は、Locale(言語、国)で設定された「温度」や「週の最初の曜日」をカスタマイズできる機能です。 温度 デフォルトを使用 摂氏(℃) 華氏(°F) 週最初の曜日 デフォルトを使用 月曜日 ~ 日曜日 温度設定画面 週最初の曜日画面 :::details 設定画面に入る方法 「設定アプリ」内の「システム」→「言語」セクションから「地域別の設定」画面にアクセスできます。 ![setting](/assets/blog/authors/semyeong/2024-02-28-regional-preferences/setting.png =300x) ::: 「地域別の設定」がなぜ必要か? 「アメリカ🇺🇸」と「オランダ🇳🇱」では、共に英語を使用できますが、使用される「温度」の単位や「週の最初の曜日」が異なります。 アメリカ🇺🇸 オランダ🇳🇱 温度 華氏 摂氏 週最初の曜日 日曜日 月曜日 「アメリカ🇺🇸」に住んでいる「オランダ人🇳🇱」が摂氏に慣れており、 温度のみ を摂氏に変更したい場合は、「地域別の設定」を使用して、 温度のみ を変更することが可能です。 「地域別の設定」を設定すると、どのような変化があるのか Locale.getDefault().toString() 設定値を確認するために、上記のコードを使用しながら各設定を変更してみましょう。 言語 温度 週最初の曜日 結果 日本語(日本) デフォルト デフォルト ja_JP 日本語(日本) 華氏 デフォルト ja_JP_#u-mu-fahrenhe 日本語(日本) デフォルト 月曜日 ja_JP_#u-fw-sun 日本語(日本) 華氏 月曜日 ja_JP_#u-fw-sun-mu-fahrenhe 「温度」や「週の最初の曜日」を設定した結果、 #u や mu-fahrenhe 、 fw-sun など理解しにくいテキストが出力されましたが、これらは Locale のメンバー変数であり、 localeExtensions の値です。このように localeExtensions に値が設定された場合、 Locale の hashCode や equals() の結果も変わり、 Locale.JAPAN と比較しても true にはなりません。 では、どのようにして言語を確認するか? Locale.getDefault() == Locale.JAPAN // X Locale.getDefault().language == Locale.JAPANESE.language // O 言語を確認したい場合、 Locale に含まれる language プロパティで比較してください。 この方法を用いれば、「地域別の設定」を変更しても影響を受けず、求めている結果を得られると思います。 最後に Android 14からこっそり追加された「地域別の設定」機能によって、突然以前動いていたコードが動かなくなっても、この変更を検知することはかなり難しいですね。 ほとんどの方は問題ないと思いますが、もしLocaleインスタンスで言語を比較している場合は、確認してみてください。 一人でも多くの方がこのようなバグを早く発見し、解決できれば、この記事は大成功と言えるでしょう! また、myrouteのメンバーが執筆した他の記事もぜひご覧ください! Structured Concurrency with Kotlin coroutines myroute Android AppでのJetpack Compose Compose超初心者のPreview感動体験 ここまで読んでいただき、ありがとうございました。 ※Android ロボットは、Google が作成および提供している作品から複製または変更したものであり、 クリエイティブ・コモンズ 表示 3.0 ライセンスに記載された条件に従って使用しています。
Hey there! 👋 We're Az, Moji, and Ai—part of the UI/UX team in the Global Product Management Group at KINTO. Today, let's dive into the fascinating world of diversity and inclusivity in UI/UX design! What Does It Mean to Design for ‘Diversity and Inclusivity’? According to Nielson Norman Group , inclusive design describes methodologies to create products that understand and enable people of all backgrounds and abilities. Okay, let’s dive into some examples to get a better understanding of inclusive design. Clear examples can be seen in architecture – ramps for wheelchairs, tactile paving, even bicycle lanes, it’s all about making spaces welcoming for everyone. Examples of inclusive design in everyday life Examples in UI/UX Design Now that you (hopefully) have a clearer understanding of inclusive design, let's delve into specific examples within UI/UX design: (from left to right) Alt text, keyboard navigation, Apple's accessibility For those familiar with HTML/CSS, ever wondered the purpose of for images? It's not just a fallback for when images don't load; it's also the key to providing image descriptions for those visually impaired navigating websites with screen readers. Ensuring that your website is navigable using only a keyboard is crucial. Keyboard accessibility not only enhances the experience for keyboard-only users but also benefits other groups, including screen reader users, individuals with low vision, those with motor impairments or cognitive disabilities, and users who prefer keyboard shortcuts or alternative keyboards. Apple has excelled in incorporating accessibility into its design, with a user interface and experience that cater to individuals with diverse impairments. Notable features include the ability to easily adjust font sizes for improved legibility, the option to tap words for spoken feedback, voice control tailored for those with physical impairments, and a range of other inclusive design elements. You can explore all these features here . These examples represent just a few instances of inclusive design within UI/UX. The Web Content Accessibility Guidelines (WCAG) outline the principles and techniques for enhancing the accessibility of web content, ensuring it is more accommodating to individuals with disabilities. Now that we've explored the fundamentals of inclusive design, let's take a deeper dive into its application in mobility platforms, a core focus of our company. Moji, over to you. Ai has set the stage with the ins and outs of inclusivity, and now it's my turn to zoom in on how we make that happen in the world of moving around. Hi, I'm Moji , and and I'm all about making sure our designs help everyone get where they're going, no matter who they are. I believe as designers, we have the capability, and responsibility, to ensure the products and solutions we develop cater to a wide spectrum of users - in terms of ethnicity, language, age, gender, physical ability, and cultural background. Looking into how the big players nail this isn't just interesting, it helps us do our job better, and it helps us figure out better ways to design more inclusive tech products. Take Google Maps as an example. This app is a daily go-to for many of us for getting around. How Google Maps Sets the Standard for Accessible App Design Not only does Google Maps offer real-time GPS and transit info, it's also designed to cater to a wide-reaching audience. For those with sight impairments , Google Maps has a voice-guided navigation system . It'll tell you when you're about to make a turn, if there's heavy traffic ahead and any disruptions that might mess up your journey. This spoken assist feature makes life easier for anyone who needs audio cues. Google Maps' latest update brings some new features specifically aimed at making the app more accessible and inclusive. The Immersive View for Routes gives a detailed look at the route you’ll be travelling, whether that’s via car, foot or on a bike. Awesome for those with mobility challenges. And, because Google Maps is used all over the world, they’ve made sure it’s available in over 40 languages. This not only makes the app more accessible globally, but also boosts user satisfaction, since people prefer info in their native tongue. Challenges of Creating Globally Accessible App with RTL Language Support Transitioning to a more focused exploration of language and locale considerations in app design, I'll hand the reins over to our teammate Az. She's been at the forefront of tackling the ground-level challenges of designing a globally-accessible app, particularly when it comes to ensuring seamless compatibility for RTL (Right-to-Left) language speakers like those who speak Arabic. Over to you Az. Hi, now it’s my turn; I’m Az from the UI/UX team. While working on RTL (Right To Left) design for a project, I noticed something. And that is: To Promote Diversity, It's Crucial to Remain Mindful of Aspects One May Not Be Familiar With It is important to pick up on details of what one would ‘take for granted' rather than focusing on creating the unprecedented. I discovered many points that were previously unknown to me or learned about them for the first time in this project. The term RTL stands for “Right To Left”, referring to languages that are read from right to left. As the text direction is different from what we are used to in Japanese or even English, the way the eyes move also differs. The layout will also be adjusted to align from right to left. Nested components such as the position of icons and texts, action buttons’ direction, the badge position are also inverted. However, the icons for "the headset" and "the location" are not inverted, as they are physical items and a universal design concept. Not only the display but also the layout itself needs to be inverted. The order of the indicators changes according to the direction. There are some exceptions even in form-related elements. When a numerical sequence has a specific meaning, such as a phone number or a credit card number, it should not be inverted. A proper noun such as “KINTO” will not be inverted We have introduced you to examples in KTC, but you can find additional concrete examples in the Apple Human Interface Guideline and the Google Material Design . I think you noticed from this article that the members in our UIUX team come from diverse backgrounds. That’s why I can improve my knowledge, perspectives, and insights that one may not notice alone. Ai mentioned the necessity of “diversity” through comparison with real-world spaces based on her architectural knowledge. Moji, with her strong research skills and extensive knowledge, provided insights into what is happening in familiar services. Tim, who makes an appearance on another article, is also a reliable member capable of handling a wide range of tasks from project execution to frontend development. By building upon the "taken for granted" knowledge that each of us possess, we will continue to develop services that everyone can comfortably use in the future.
Flutter Webで単体テストしてますか? こんにちは。Woven Payment Solution開発グループの大杉です。 私たちのチームでは、 Woven by Toyota において Toyota Woven City で使われる決済システムの開発を行っており、普段はKotlin/Ktorでバックエンド開発とFlutterによるWeb/モバイルのフロントエンド開発をしています。 Flutter Webでは、Web固有のパッケージを使用しているとテスト実行でエラーになってしまうことがあります。 そのため、今回の記事ではFlutter Webのコードをテスタブルな状態に維持するために工夫していることを、特に単体テストにフォーカスしてまとめたいと思います。 なお、これまでのフロントエンド開発ストーリーについては過去の記事を参照していただけると幸いです。 KotlinエンジニアがFlutterに入門して1ヶ月でWebアプリケーションを作った話 バックエンドエンジニアたちが複数のFlutterアプリを並行開発していく中で見つけたベストプラクティス Flutter Webとは 初めに、Googleによって開発が進められているクロスプラットフォーム開発のフレームワークであるFlutterの内、 Webアプリ開発に特化したフレームワーク のことです。 Flutterの開発言語であるDartは、ソースコードを事前にJavaScriptに変換し、HTML、Canvas、CSSを使用して描画処理を行うことができるので、モバイルアプリで開発したコードをそのままWebアプリに移植することができます。 Flutter Webの実装方法 基本的な実装は、モバイルアプリ開発と同じ方法で実装できます。 一方で、DOM操作やブラウザAPIにアクセスしたい場合はどうすればいいでしょうか? これもDartの組み込みパッケージで dart:html などのWebプラットフォーム向けのパッケージが用意されています ^1 。例えば、ファイルダウンロード機能もJSによる一般的なWebアプリ開発と同じように実装することができます。 :::message 記事執筆時のSDKバージョンは、Flutter v3.16, Dart v3.2を対象としています。 ::: 下記のWidgetは、カウントアップした数値をテキストファイルとしてダウンロードするという何に使うかわからない機能を持ったサンプルアプリです。Floating Buttonをクリックするとテキストファイルのダウンロードが行われます。 import 'dart:html'; import 'package:flutter/material.dart'; class MyHomePage extends StatefulWidget { const MyHomePage({super.key, required this.title}); final String title; @override State<MyHomePage> createState() => _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> { int _counter = 0; void _incrementCounter() { setState(() { _counter++; }); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( backgroundColor: Theme.of(context).colorScheme.inversePrimary, title: Text(widget.title), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ const Text( 'You have pushed the button this many times:', ), Text( '$_counter', style: Theme.of(context).textTheme.headlineMedium, ), IconButton( onPressed: _incrementCounter, icon: const Icon(Icons.add), ) ], ), ), floatingActionButton: FloatingActionButton( onPressed: () { AnchorElement(href: 'data:text/plain;charset=utf-8,$_counter') ..setAttribute('download', 'counter.txt') ..click(); }, tooltip: 'Download', child: const Icon(Icons.download), ), ); } } Flutter Webのコードを単体テストする 先ほどのサンプルコードのテストコードを以下のように用意しました(ほとんど flutter create して出力されたときのままです)。 import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:sample_web/main.dart'; void main() { testWidgets('Counter increments smoke test', (WidgetTester tester) async { await tester.pumpWidget(const MyApp()); expect(find.text('0'), findsOneWidget); expect(find.text('1'), findsNothing); await tester.tap(find.byIcon(Icons.add)); await tester.pump(); expect(find.text('0'), findsNothing); expect(find.text('1'), findsOneWidget); }); } 次のテストコマンドを実行するかVS CodeのTestingタブから上記のテストコードを実行してみましょう。 $ flutter test このままテストを実行すると、次のようなエラーが出るかと思います。 Error: Dart library 'dart:html' is not available on this platform. // 省略 lib/utils/src/html_util.dart:4:3: Error: Method not found: 'AnchorElement'. AnchorElement(href: 'data:text/plain;charset=utf-8,$data') どうやら dart:html のインポートに何か問題があるようです。 プラットフォームごとのDartコンパイラ 公式ドキュメント を確認すると、Dartコンパイラの実行には、 JITコンパイラを行うDart VMとマシンコードを生成するAOTコンパイラの Nativeプラットフォーム DartコードをJSにトランスパイルする Webプラットフォーム の2つがあることがわかります。また、それぞれのプラットフォームで利用できるパッケージが一部異なるようです。 プラットフォーム 利用できるパッケージ Native dart:ffi, dart:io, dart:isolate Web dart:html, dart:js, dart:js_interopなど つまり、前述のテストはVM上で実行されていたため、 dart:html を利用できなかったということがわかりました。 Webプラットフォームパッケージのインポートエラーを回避する方法として、テスト実行時にプラットフォームを指定する方法があります。 下記のオプションをつけてコマンド実行することで、テストをChrome上で(つまり、Webとして)実行することを指定できます ^2 。 $ flutter test --platform chrome :::message オプションなしテストがVM上であることは、 flutter test --help --verbose で確認できました。 --platform Selects the test backend. [chrome] (deprecated) Run tests using the Google Chrome web browser. This value is intended for testing the Flutter framework itself and may be removed at any time. [tester] (default) Run tests using the VM-based test environment. ::: Flutter WebのテストコードはChromeで実行すべきか? ブラウザAPIの利用はWebアプリを開発する上で避けられないことだと思いますが、Flutter WebのテストコードはChrome上で実行すべきなのでしょうか? 個人的な意見ですが、なるべくChromeを使わない方が良いというのが私の考えです。 理由としては、 テスト実行の際にバックグラウンドでChromeを起動する必要があるため、テストの起動時間が増大してしまう CI環境にChromeをインストールする必要があり、CI環境のコンテナサイズが大きくなる。または、コンテナのセットアップに時間がかかってしまう ことが想像でき、CI環境の金銭的コストがかなり増えてしまうことが考えられます(もちろん、ローカルでささっと確認する程度や富豪な方でしたら問題ないです)。 実際に、オプションでプラットフォームを指定しない標準ケース(Native)とChromeを指定したケース(Web)を比較したローカル環境の実行結果を載せました。 プラットフォーム プログラム実行時間(秒) トータルテスト実行時間(秒) Native 2.0 2.5 Web 2.5 9.0 上記表から、Webの方は実際にテストの起動に大幅に時間がかかるようになりました。さらに、テスト実行時間も25%ほど増大していることもわかるかと思います。 ![tester](/assets/blog/authors/osugi/20240301/annoying.png =400x) Webプラットフォーム依存のコードは分離しよう Webプラットフォームを指定しないで前述のエラーを回避するにはどうしたら良いでしょうか? 実は、Dartにはパッケージを条件でインポート・エクスポートする方法と、プラットフォームがWebかNativeかを判定するためのフラグも用意されています ^3 。 フラグ 説明 dart.library.html Webプラットフォームかどうか dart.library.io Nativeプラットフォームかどうか これらを駆使することでエラーを回避することができます。 まずは、以下のようにしてWeb用・Native用のダウンロード機能モジュールを用意し、前述のWebパッケージ使用箇所をテスト対象のコードから分離しましょう。 import 'dart:html'; void download(String fileName, String data) { AnchorElement(href: 'data:text/plain;charset=utf-8,$data') ..setAttribute('download', fileName) ..click(); } void download(String fileName, String data) => throw UnsupportedError('Not support this platform'); そして、上記モジュールのインポートをプラットフォームごとに切り替える方法は以下のようになります。 import 'package:flutter/material.dart'; - import 'dart:html' + import './utils/util_io.dart' + if (dart.library.html) './utils/util_html.dart'; class MyHomePage extends StatefulWidget { // 省略 } class _MyHomePageState extends State<MyHomePage> { // 省略 @override Widget build(BuildContext context) { return Scaffold( // 省略 floatingActionButton: FloatingActionButton( onPressed: () { - AnchorElement(href: 'data:text/plain;charset=utf-8,$_counter') - ..setAttribute('download', 'counter.txt') - ..click(); + download('counter.txt', _counter.toString()); }, tooltip: 'Download', child: const Icon(Icons.download), ), ); } } エクスポートをする場合は、別途 util.dart などの仲介ファイルを用意してWidget側からインポートすることになると思います(ここでは省略します)。 export './utils/util_io.dart' if (dart.library.html) './utils/util_html.dart'; 以上で、Web依存のコードによるエラーを回避してNativeプラットフォーム上でテストを実行することができるようになりました。 Webプラットフォーム依存の外部パッケージにはNativeプラットフォーム用のスタブも作ろう 私たちのシステムは認証基盤にKeycloakを採用しています。 Flutter Webアプリ上でKeycloakの認証をするために以下のパッケージを使用しています。 @ card リンクを開いてもらえるとわかると思いますが、このパッケージはWebのみをサポートしています。 このパッケージのおかげで楽に認証処理を実装することができたのですが、認証モジュールという特性上そのインターフェースは色々なところで利用されるため、APIコールなど認証情報を必要とするようなWidgetがすべてWebプラットフォーム依存となってしまい、CIでテストできなくなる状況になってしまったことがありました(この間はローカルで --platform chrome オプションでテストして全PassしたらOKという性善説運用をしていました)。 ちなみに、このパッケージをインポートすると以下のエラーがテスト実行時に発生します。 Error: Dart library 'dart:js_util' is not available on this platform. そこで、前述のインポート分離と同様な処置を外部パッケージにも行っていくのですが、ここではエクスポートを使ったパターンで実践していきたいと思います。手順は以下となります。 1. 仲介パッケージの作成 ここでは例として inter_lib というパッケージをサンプルコードのパッケージ内に作成しています。 flutter create inter_lib --template=package 実際のプロダクトコードでは、外部パッケージに準じたコードをプロダクトコード内に混入させないため、プロダクトとは別のパッケージを作成して外部パッケージを仲介させています。 Melos を使うと簡単にマルチパッケージ開発ができるのでおすすめです。 2. Nativeプラットフォーム用のスタブの作成 keycloak_flutter のスタブを作るため、Githubのリポジトリを参照してインターフェースを模擬します(ライセンスの確認は適宜お願いします)。 プロダクトコード上で使用しているクラスやメソッドはすべてが必要になります。 @ card 作成したファイルは以下のようになっています。 src ディレクトリ以下の stub_ のプレフィックスがついているものが外部パッケージのインターフェースを模擬したものです。 inter_lib ├── lib │ ├── keycloak.dart │ └── src │ ├── stub_keycloak.dart │ ├── stub_keycloak_flutter.dart │ └── entry_point.dart また、 entry_point.dart は実際の外部パッケージと同じものをエクスポートするように定義しました(実際にはプロダクトコード内で使用しているインターフェースだけで十分です)。 export './stub_keycloak.dart' show KeycloakConfig, KeycloakInitOptions, KeycloakLogoutOptions, KeycloakLoginOptions, KeycloakProfile; export './stub_keycloak_flutter.dart'; この inter_lib をパッケージとして内部公開するため、以下のようにエクスポートの設定をします。 library inter_lib; export './src/entry_point.dart' if (dart.library.html) 'package:keycloak_flutter/keycloak_flutter.dart'; 3. 仲介パッケージを pubspec.yaml の dependencies へ追加 pubspec.yaml に inter_lib への相対パスを追加します。 // 省略 dependencies: flutter: sdk: flutter cupertino_icons: ^1.0.2 + inter_lib: + path: './inter_lib' // 省略 そして、元々外部パッケージを参照していたところを inter_lib に置き換えます。 - import 'package:keycloak_flutter/keycloak_flutter.dart'; + import 'package:inter_lib/keycloak.dart'; import 'package:flutter/material.dart'; import 'package:sample_web/my_home_page.dart'; void main() async { WidgetsFlutterBinding.ensureInitialized(); final keycloakService = KeycloakService( KeycloakConfig( url: 'XXXXXXXXXXXXXXXXXXXXXX', realm: 'XXXXXXXXXXXXXXXXXXXXXX', clientId: 'XXXXXXXXXXXXXXXXXXXXXX', ), ); await keycloakService.init( initOptions: KeycloakInitOptions( onLoad: 'login-required', enableLogging: true, checkLoginIframe: false, ), ); runApp( const MyApp(), ); } 以上、Webプラットフォーム依存パッケージのNativeプラットフォーム用スタブの作成フローでした。これでテストをVM上で実行できるようになります。 この方法は今回の例として用いたkeycloak_flutter以外にももちろん適用できます。 ![successful people](/assets/blog/authors/osugi/20240301/success.png =480x) まとめ 今回の記事では、Flutter Webのコードをテスタブルな状態に維持するために単体テストで工夫していることをまとめました。 Dartの実行環境には、WebプラットフォームとNativeプラットフォームがある flutter test はNativeプラットフォーム実行であり、 dart:html などのWebプラットフォーム用パッケージを使っているとエラーとなってしまう dart.library.io , dart/library/html のフラグを活用して、プラットフォームごとに実パッケージとスタブを切り替える実装をすると幸せになれる
Hello, I am _awache ( @_awache ), and I do DBRE at KINTO Technologies (KTC from now onwards). KTC operates a large number of databases such as Amazon Aurora on Amazon Web Services (hereinafter abbreviated as AWS) as a foundation for providing mobility services. It established the Database Reliability Engineering (hereinafter abbreviated as DBRE) organization and is implementing initiatives to balance business agility and governance. In this article, I will talk about why KTC needs DBRE. About Database Reliability Engineering (DBRE) DBRE stands for Database Reliability Engineering. It is responsible for using an approach that combines the best practices of software engineering and database management. Our main jobs are: Defining and measuring SLO/SLI and making an approach to a development organization accordingly Increasing productivity through automation and autonomy Increasing the uptime of backup, restore, and other services Database security guarantee and governance control Cross-functional collaboration with specialists from other disciplines These roles can be summed up as ensuring the reliability of services by using specialized knowledge and decisions on databases. Do Companies Need DBRE? The term "DBRE" may seem very appealing to some people. However, it is difficult to achieve sustainable results without thinking the reasons why it is necessary in an organization. The rapid growth of AWS and other public cloud services, the evolution and widespread acceptance of DevOps and SRE principles, coupled with the progress in AI technology, have collectively established a robust foundation. This foundation empowers individuals to address problems up to a certain complexity level through Cloud utilization. Consequently, the demand for specialized knowledge in database engineering is diminishing. On the other hand, the basic approach to databases has not changed even now. Separate environment Manage configuration Measure performance Backup/Restore Security guarantee, etc. Simply put, it can be said that it's the environment surrounding databases that has changed . You might also want to consider introducing DBRE at your company if you decide it is necessary given the changes in the times. Why KTC Needs DBRE Here's an explanation of why KTC is adopting DBRE practices. The relationship between software engineers and database administrators had changed The roles of software engineers and database administrators are clearly separated in this age When the company still focused on on-premises configurations, software engineers and database administrators generally had distinct roles and worked in their respective areas of expertise. For example, I think there were classifications like these: Software Engineering Maintaining service uptime and accelerating its growth Extracting necessary data at users' requests Tuning that does not require changes to the database DDL such as query changes Adapting to application according to requests from database administrators Database Administration Maintaining database uptime The major difference between the two is the speed and expertise in responding to requests and users' needs. Because software engineers are closer to the service provider side, they need to respond as quickly as possible to meet needs. Since database administrators focus on the database rather than on services, they use their expertise to solve various issues at the latest even if they respond more slowly. It is like the following figure. ![Relationship1](/assets/blog/authors/_awache/20231211/関係性1.jpg =500x) In an organization like this, the database administrator finds issues throughout the organization and solves them. The rise of public cloud services has decreased work done by database administrator and expanded the area of software engineers The rise of public cloud services such as AWS has drastically changed the roles of both sides. It can be said that the role of the database administrator has been drastically reduced because public cloud services can maintain database uptime. On the other hand, some of the jobs that database administrators were responsible for are done by database administrators and software engineers, specifically: Separate environment Manage configuration Measure performance Backup/Restore Security and Governance Control Software engineers in the workplace are now required to do these things. Because of that, there are times when the database does not get enough attention. ![Relationship2](/assets/blog/authors/_awache/20231211/関係性2.jpg =500x) KTC started facing issues in this regard. In order to keep accelerating business growth, software engineers must be able to focus on business growth. In today's world of cloud utilization, DBRE must be a hub with public cloud services in order for software engineers to focus on business activities. The Key Roles Required of DBRE Maintaining service uptime using databases Making engineers in the workplace more productive Handling database security Protecting the database from internal and external factors By fulfilling these roles, DBRE accelerates the continued growth of the company. ![Relationship3](/assets/blog/authors/_awache/20231211/関係性3.jpg =500x) The following chapters provide more information on each role. The Roles of DBRE in the Cloud Era Maintaining service uptime using databases Generally, a properly functioning database is required for a service to run properly. In other words, database downtime directly leads to service downtime. In order to maintain service uptime, the database must be restored correctly, appropriately, and quickly. So what DBRE needs is the skills to keep the database running on the cloud. king engineers in the workplace more productive Promoting standardized operation of a database can significantly contribute to companies. Examples include using a platform that provides company guidelines, automatically generating database documents such as ER diagrams, and automating routine operations. DBRE also plays an important role in validating new features and applying them to the company, addressing application bottlenecks such as slow queries, and troubleshooting. These do not scale even if only DBRE does them. DBRE has to work with much less resources than software engineers. It is important to note that outputs from DBRE go back to the software engineers and can be handled by anyone. Handling database security The database becomes more important as the business grows. When the database becomes more valuable, it is constantly targeted. A data leak can cause financial losses and tarnish the company's reputation. It is necessary to protect the database from risks like that for the company to keep growing. For that reason, it is important that DBRE implements database security measures as a business unit. ![ChangeinTimeandsValue](/assets/blog/authors/_awache/20231211/時間と価値の変化.png =500x) Protecting the database from internal and external factors Data is easily influenced by changes in external factors. Because of that, database operation requirements become more strict over time. In particular, companies have to address changes in governments, regulations, public opinion, and other factor outside of the company's or people's control. For example, the Personal Information Protection Act and other laws get revised regularly, and the penalties tend to get more strict each time. If each service individually addresses influences by external factors, it presents financial risks, the risk of a leak, and other risks. DBRE is required to properly understand the risks of external factors and implement optimal governance control across all databases in the company. KTC DBRE's vision At KTC, DBRE is a cross-functional organization. It provides value by having our outputs reflected on the business. I think that having a cross-functional organization without knowing how to leverage it is the same as paying taxes and not understanding where your money goes. It is important to know how to positively affect the business. These days, businesses change too fast to be handled with just rules and reviews. It is important that DBRE uses its knowledge of databases to contribute to engineers in the workplace and maintain the reliability of KTC as a company. For that reason, we try to solve database issues through engineering to the best of our abilities. We implement governance control in services and positively impact the business by working together with engineers in the workplace and quickly solving issues without being restricted by rules. Conclusion The Roles of KTC DBRE Provide an environment in which engineers in the workplace can focus on growing the business. The Key Roles Required of KTC DBRE Maintaining service uptime using databases Making engineers in the workplace more productive Handling database security Protecting the database from internal and external factors By fulfilling these roles, DBRE accelerates the continued growth of KTC. It is required to positively influence the business by working together with engineers in the workplace and quickly solving issues by focusing on these roles without being bound by rules. Do you want to discuss DBRE with us? We have just starting doing DBRE. We are still doing trial and error to see why we are doing DBRE, what we want to accomplish, and we will do it. By having frank discussions on what we are doing and what we want to do, we can expand our horizons. We also want to talk about our efforts to receive a lot of input. If you are interested, please send us a DM on Twitter. We are Hiring I think recruiting DBRE team members is a huge challenge. If you are interested in our activities, do not hesitate to send your application through our recruitment page . We're also open to casual conversations with anyone who wants to chat with us. We're also planning to organize events such as DBRE meetups and collaborate with companies that follow DBRE practices. We look forward to you joining us when that happens!
はじめに 私は2020年11月にKINTOテクノロジーズに入社し、KINTO WebのフロントエンドとAPIの開発に携わった後、現在はモバイルAndroidアプリチームの開発者を務めています👋🏾 IT業界で女性であることについての思いや経験を書いてほしいとの依頼でしたが、正直なところ、性別による違いはないと思いますし、あるべきでもないと思っています。本記事は、KINTOテクノロジーズにおけるさまざまな働き方や視点を掘り下げてダイバーシティを探求する全5回のシリーズの1つですので、ここでは私がKINTOテクノロジーズで担当する開発チームでのリーダーシップについて、私の思いと個人的な成長についてお話ししたいと思います。 簡単な自己紹介 : 😊 経歴: 💡 豆知識:高校生の時に始めた個人事業に10年間従事 🌱 ゲームプログラミングを学ぶために来日し、15年の開発経験あり 💼 前職:モバイルアプリエンジニア、ソフトウェア(SmartTV)エンジニア、Webエンジニア(フルスタック)、デジタルサイネージスタートアップ。 📫 2020年にKINTOテクノロジーズに入社。 優れたリーダーとは? 開発者から初めてチームリーダーになったとき、最初に自問したのは「優れたリーダーとはなにか」ということでした。 初めてリーダーを任せてくれた上司が、リーダーと上司の違いを説明してくれて、「こんなリーダーになりたい」と思ったのはその時でした。 リーダーとは、メンバーの力を結集し、優れた成果を生み出し、それを共有する人です。 そのためには、自分が前に立ってチームを引っ張り、裏ではサポート役となってチームを良い方向に導いていくことが大切だと考えます。 開発チームの最終的な使命は、今あるリソースを活用して、必要なサービスをできるだけ早く、完璧に作り上げることです。さらに、開発チームの目的は優れた開発を行うだけでなく、開発を通じて最大の価値を生み出すことでもあります。最終的にはこれがチームの成果として表れます。こういったチームが優れたチームであり、これを支えるリーダーが優れたリーダーであると考えます。 「チームワーク」の重要性 では、結果を出す優れたチームを作るにはどうすれば良いのでしょうか。優れたチームに最も必要なものは何でしょうか。チームメンバーをひとつのゴールに集中させるにはどうすれば良いのでしょうか。その原動力はチームワークだと私は思っています。チームワークを高めるために、私がチームのメンバーと行ったことを以下にご紹介します。 チームの全員がリーダーになる 「Team Goal Task Content Sharing Meeting (ゴール・タスク情報共有会)」で現在のチームゴールを共有し、各自がタスクを選択できるようにしています。通常、リーダーシップが求められるのはチームリーダーやグループリーダーのみで、実際に組織で役職に就いていない限り、リーダーシップが自分の役割だと考える人はあまりいません。私は、チームのメンバーが自分の仕事に対してリーダーシップを発揮し、与えられた仕事に対して「リーダーとしての」責任感を持てるようにすることもリーダーの役割だと考えています。そして、これがチームにとって重要な役割を果たしていると感じます。自主性があると人はやる気になり、個人の能力を最大限に高めることにつながるのだと思います。 仕事は開発文化によって生み出される 開発チームが良い仕事をするのは、「仕事」のためではなく、チームの「文化」があるからだと思っています。 例えば、作業プロセスを作ること、文書を作成すること、仕様を明確に伝えること、これらは「仕事」の基本です。 成功する開発チームは、チームの「文化」が物事をうまく機能させると信じているのだと思います。 チームのメンバー同士がどのようにコミュニケーションを取っているか。どのようにまとまっているか。結論が出た後はどうするのか。反対意見を言うのか、支持するのか、陰で批判するのか。などなど。 これが開発文化であり、チーム文化です。 多様性を認め、受け入れる チームのメンバーにはそれぞれ強みがあります。一番大切なのは、その強みを最大限に生かし、最大限の価値を生み出すことだと思います。 では、私たちのチームではどうしているのでしょうか? デイリースクラムリーダー:チーム全員が日替わりでリーダーになる 私たちのチームでは毎朝11時にミーティングを開いて、チームメンバーが集まり、昨日やったこと、今日やること、そして仕事上の問題を簡単に共有します。通常のチームミーティングと異なるのは、上下関係の報告形式ではなく、横並びの雰囲気の中でメンバーが交代でファシリテーターを務める点です。ミーティングは「共有」の形式で進められます。チームメンバーが問題を抱えていてタスクを進められない場合は、デイリースクラムの終了後に「助け合いミーティング」を開いて解決を目指します。また、昨日行ったすべての作業を覚えていない場合があるので、Confluenceに書き留めておくと、チームメンバーはそれを見て、スケジュールを立て、やるべきことを計画することができます。 KINTO ONEサブスクチームのデイリースクラムのやり方をご紹介します。その日のファシリテーターがデイリースクラムのアジェンダを説明します。最初の週は、まずは今後2週間でチームが改善すべき点や試みるべきことを共有します。毎朝10~15分程度の短い時間ですが、このように全員が交代でリーダーを務めることで、日々の仕事の中でリーダーシップを鍛えることができ、チームメンバーが率先してチームの現在のタスクやゴールを共有し、タスクに貢献することができます。 その重要性を理解することで、「リーダーシップのマインドセット」を磨くことができ、やがてそれが_自身の中のモチベーター_となって、より良い結果につながると思っています。 コードレビュー:モチベーションと自律性 多くの開発者は、創造性によって高い「モチベーション」を得ています。自分の仕事や表現を認め、高く評価してくれる人たちと分かち合いたいと思っています。私自身も、人がやったことを見たり、コードに没頭して、人の発想に驚かされたりするのが好きです。コードレビューの時間を通して、私たちは互いの仕事を共有し、互いに学び合うことができます。現在の方法よりも良い方法があれば、それを共有し、大胆な変更も取り入れます。もちろん、どんな変更にもチーム全体の合意が必要です。こうして、優れたコードについて議論し、互いにコードレビューを行うことで、私たちは日々成長しています。素晴らしい仲間と充実した共同作業の経験が、チーム内の信頼関係をますます強化していると感じています。 ブレインストーミング:一つのゴールに集中しよう 私たちは、なぜこの仕事をする必要があるのか、なぜこのサービスをユーザーに提供する必要があるのか、考える時間を取っています。どう作るか、どう伝えるか、どう実現させるか、そして私たちの作るサービスがユーザーに与える価値の大きさについて、深く考えます。コードの寿命やコンポーネントのスケーラビリティ、ユーザーの視点に立った全体の設計を考慮することで、開発チームの各メンバーが、目的や機能ごとに開発リーダーとして独立して技術的な意思決定を行うことができます。 私たちのチームのブレインストーミングは、次のルールに則って進められます。 トピックに集中しよう アイデアを自由に表現しよう アイデアを組み合わせよう できるだけ多くのアイデアを出そう アイデアは批評しない イメージしてみよう アイデアを決定する 自分で決めた場合は、複雑なコミュニケーションプロセスを経ずに自律的に「プロトタイプ」を作り、それを企画チームに届けてプロダクトにすることがゴールです。 開発文化の成長のために柔軟であり続ける 私自身の成長、チームメンバーの成長、そして皆の成長のために小さな行動を積み重ねていく過程で、優れた開発文化が生まれ、結果として皆がともに成長していくのだと思っています。 私個人としては、素晴らしい仲間がいるチームの一員でいられることに常に感謝しています。開発文化は良いサービスを作るための手段であり、ツールであって、ゴールではありません。チームの文化は今後も変化し続け、良い方向に向かっていくものと信じています。 最後に、私が考える優れたリーダーの理念とは、チームメンバーの成長をコントロールするのではなく、後押しし、サポートすることであり、チーム内で互いに助け合える優れた開発文化を築くことです。優れたチームを作ることが必ずしも大きな成功を保証するものではないかもしれませんが、その可能性を高めることはできると私は信じています。
はじめに こんにちは!KTCでAndroidエンジニアをしている長谷川( @gotlinan )です! 普段はmyrouteというアプリの開発をしています。myrouteのメンバーが書いた他の記事も是非読んで見てください! myroute Android AppでのJetpack Compose Compose超初心者のPreview感動体験 本記事ではKotlin coroutinesを使用したStructured Concurrencyを解説します。 Structured Concurrencyは知っているけど、coroutineを使う方法はどんな感じ?って方は、 並行処理のための便利関数 をご確認ください。 Structured Concurrency? Structured Concurrencyって何でしょう?日本語にすると「構造化された並行処理」みたいな感じだと思います。イメージとしては、二つ以上の処理を並行しながら、それぞれでキャンセルやエラーが発生した場合も正しく管理されていること、だと思います。本記事を通じて、Structured Concurrencyについて詳しくなりましょう! 今回は二つのよくある例をもとに紹介してみます。 1. エラーを協調したい まずよくある例として処理1と処理2を実行後、その結果に応じて処理3を実行したい場合です。 図にすると、以下のようになります。 処理1と処理2を実行後、その結果に応じて処理3を実行する この場合、処理1でエラーが発生した場合、処理2を継続しても無駄ですね。 したがって処理1でエラーが発生した場合、処理2をキャンセルする必要があります。 同様に処理2でエラーが発生した場合も、処理1をキャンセルして、処理3に進む必要はありません。 2. エラーを協調したくない 次によくある例として、画面内に複数のエリアがあり、それぞれ独立して表示する場合です。 図にすると、以下のようになります。 画面内に複数のエリアがあり、それぞれ独立して表示する この場合、仮に処理1でエラーが発生しても、処理2や処理3の結果は表示したい場合があります。 したがって処理1でエラーが発生した場合でも処理2や処理3はキャンセルせずに継続する必要があります。 二つの例は理解できましたか?coroutineでは上記のような例を、Structured Concurrencyの考えをもとに簡単に実装することができます! ただし理解するためにはcoroutineの基礎を理解する必要があります。次のセクションからは実際にcoroutineを学びましょう! 基礎は知っているよっていう方は、[並行処理のための便利関数](#並行処理のための便利関数)までスキップしてください。 coroutineの基礎 詳しい解説の前にcoroutineの基礎的な話をしましょう。 coroutineでは CoroutineScope から launch 関数を呼ぶことで非同期の処理を開始できます。具体的には以下のような形です。 CoroutineScope.launch { // 実行したいコード } ところでなぜ CoroutineScope を使用する必要があるのでしょうか?それは非同期処理では、「どのスレッドで実行するか」、「キャンセルやエラーが発生した時にどう振る舞うか」がとても重要だからです。 CoroutineScope は CoroutineContext を持ちます。ある CoroutineScope で実行されるcoroutineは CoroutineContext をもとに制御されます。 具体的には CoroutineContext は以下の要素などから構成されます。 Dispatcher : どのスレッドで動くか Job : キャンセルの実行、キャンセルやエラーの伝搬 CoroutineExceptionHandler : エラーハンドリング CoroutineScope を作成する際は、それぞれの要素を+演算子で渡すことが可能です。 そして CoroutineContext はcoroutineの親子間で継承されます。例えば以下のようなコードがあったとします。 val handler = CoroutineExceptionHandler { _, _ -> } val scope = CoroutineScope(Dispatchers.Default + Job() + handler) scope.launch { // 親処理 launch { // 子処理1 launch {} // 子処理1-1 launch {}// 子処理1-2 } launch {} // 子処理2 } この場合は CoroutineContext が以下のように継承されます。 CoroutineContextの継承 おや、画像を見ると Job は継承されずに新しく作成されているようですね? これは間違いではないです。「 CoroutineContext はcoroutineの親子間で継承されます」と述べましたが、厳密には「 Job 以外の CoroutineContext はcoroutineの親子間で継承される」の方が正しいです。それなら Job はどうなるんだ?と思いますよね。 次のセクションでは Job について理解を深めてみましょう! Jobとは coroutineにおける Job とは何でしょうか?それは短くまとめるのであれば、「coroutineの実行を制御する」ものだと思います。 Job には cancel メソッドがあり、開発者は開始されたcoroutineをいつでもキャンセルすることが可能です。 val job = scope.launch { println("start") delay(10000) // Long Process println("end") } job.cancel() // start (printed out) // end (not printed out) Androidエンジニアがよく利用するであろう viewModelScope や lifecycleScope に紐づく Job はそれぞれのライフサイクルの終わりの時にキャンセルされています。これによりユーザーが画面外にでた場合に継続中の処理があっても、開発者が意識せずに正しくキャンセルされます。 そんな超重要な Job ですが、coroutineの親子間でのキャンセルやエラーの伝搬の役割も持ちます。前のセクションでは Job は継承されない話をしましたが、その例を使うと、以下の画像のように Job は階層関係を持ちます。 Jobの階層関係 実際に Job の定義を一部抜粋すると、以下のようになっています。 public interface Job : CoroutineContext.Element { public val parent: Job? public val children: Sequence<Job> } 親子関係を保持できるになっており、キャンセルやエラーが発生したときに親や子の Job を操作できそうですね。 次の章からは Job の階層関係を通じて、どのようにcoroutineがキャンセルやエラーを伝搬しているか確認してみましょう! cancelの伝搬 coroutineがキャンセルされた場合、以下のような挙動になります。 自身の子coroutineを全てキャンセルする 自身の親coroutineには影響しない ※ CoroutineContext を NonCancellable に変更することで親coroutineのキャンセルの影響を受けないcoroutineを実行することも可能です。Structured Concurrencyのテーマとは離れるため、今回は割愛します。 つまりキャンセルは Job の階層関係において下方向に影響します。 下記の例だと、 Job2 がキャンセルされた場合、 Job2 、 Job3 、 Job4 で動いているcoroutineがキャンセルされます。 Cancelの伝搬 エラーの伝搬 実は Job には大きく分けて、 Job と SupervisorJob があります。 この種類によって、エラーが発生した場合の挙動が変わります。 自身のJobでエラーが発生したときと、子 Job でエラーが発生したときの挙動を二つの表にまとめました。 Job 内でエラーが発生したとき 子Jobを 自身のJobを 親Jobに Job 全てキャンセルする エラー終了する エラーを伝搬する SupervisorJob 全てキャンセルする エラー終了する エラーを伝搬しない エラーが子 Job から伝搬してきたとき 他の子Jobを 自身のJobを 親Jobに Job 全てキャンセルする エラー終了する エラーを伝搬する SupervisorJob 何もしない 何もしない エラーを伝搬しない 二つの表を参考にしてエラー発生時の挙動を表したイメージは、 Job と SupervisorJob の場合でそれぞれ以下のようになります。 Jobの場合 通常のJobのJob2でエラーが発生した場合 子Jobである Job3 、 Job4 はキャンセルされる 自身のJobである Job2 はエラー終了する エラーを親Jobである Job1 に伝搬する Job1 の他の子Jobである Job5 をキャンセルする Job1 がエラー終了する SupervisorJobの場合 通常のJobのJob2でエラーが発生した場合 子Jobである Job3 、 Job4 はキャンセルされる 自身のJobである Job2 はエラー終了する エラーを親SupervisorJobである Job1 に伝搬する 意識してもらいたい点として、エラーが伝搬された SupervisorJob1 は、他の子Job( Job5 )をキャンセルせず、自身も通常終了します。 ちなみに Job が、通常終了したのか、エラーにより終了したか、キャンセルにより終了したのかを確認する方法として、 invokeOnCompletion を使用することができます。 val job = scope.launch {} // Some work job.invokeOnCompletion { cause -> when (cause) { is CancellationException -> {} // cancellation is Throwable -> {} // other exceptions null -> {} // normal completions } } catchされなかった例外 ところでcoroutineで捕捉されなかった例外はどうなるのでしょうか? 例えば TopLevelの Job でエラーが発生したり、伝搬してきた場合はどうなるの? SupervisorJob でエラーが発生したり、伝搬してきた場合どうなるの? などの疑問があると思います。 答えは CoroutineExceptionHandler が指定されていれば、呼ばれる CoroutineExceptionHandler が指定されていなければ、スレッドのデフォルトの UncaughtExceptionHandler が呼ばれる となります。 coroutineの基礎 で前述のように、 CoroutineExceptionHandler も CoroutineContext の仲間です。以下のように渡すことができます。 val handler = CoroutineExceptionHandler { coroutineContext, throwable -> // Handle Exception } val scope = CoroutineScope(Dispatchers.Default + handler) もし CoroutineExceptionHandler が指定されていない場合、スレッドのデフォルトの UncaughtExceptionHandler が呼ばれます。 開発者が指定したい場合は以下のように記述します。 Thread.setDefaultUncaughtExceptionHandler { thread, exception -> // Handle Uncaught Exception } 自分が本記事執筆まで誤解していたこととして、 SupervisorJob を使用すれば、エラーが伝搬しないのでアプリケーションは終了しないという認識がありました。 しかし SupervisorJob はあくまでcoroutineの Job の階層関係上でエラーを伝搬しないだけです。従って上記の二種類のHandlerのどちらかを適宜定義しておかないと、意図した通りに動かない可能性があります。 例えばAndroidアプリではスレッドのデフォルトの UncaughtExceptionHandler は、開発者が指定しない限りアプリケーションが終了(クラッシュ)するようになっています。一方で通常のKotlinコードを実行すると、ただエラーログを表示するだけとなります。 また、少し話は逸れますが、 try-catch と CoroutineExceptionHandler のどちらを使用すればいいのか、という疑問があるかもしれません。 CoroutineExceptionHandler でエラーを捕捉したとき、coroutineの Job は既に終了しており、復帰することはできません。基本的に復帰可能なエラーは try-catch を使用して、Structured Concurrencyの考えをもとに実装する際や、ログを出しておきたいときなどは、 CoroutineExceptionHandler を設定する方針が良さそうです。 並行処理のための便利関数 ここまでの説明が少し長くなってしまいましたが、coroutineではStructured Concurrencyを達成するために、 coroutineScope() や supervisorScope() のような関数があります。 coroutineScope() 1. エラーを協調したい を覚えていますか?このような例では coroutineScope() を使用することができます。 coroutineScope() は起動した子coroutineが全て終了するまで待ちます。また子coroutineでエラーが発生した場合、他の子coroutineはキャンセルします。 以下のようにコードを記述すると、 子処理1と子処理2は並行で実行 子処理3は子処理1と子処理2が終わった後に実行 どの子処理でエラーが発生しても、他の子処理はキャンセルされる などを達成することができます。 scope.launch { coroutineScope { launch { // 子処理1 } launch { // 子処理2 } } // 子処理3 } supervisorScope() 2. エラーを協調したくない を覚えていますか?このような例では supervisorScope() を使用することができます。 supervisorScope() も起動した子coroutineが全て終了するまで待ちます。また子coroutineでエラーが発生した場合でも、他の子coroutineはキャンセルしません。 以下のように記述すると、 子処理1と子処理2と子処理3は並行で実行 どの子処理でエラーが発生しても、他の子処理に影響しない などを達成することができます。 scope.launch { supervisorScope { launch { // 子処理1 } launch { // 子処理2 } launch { // 子処理3 } } } まとめ Structured Concurrencyは理解できましたか? 説明のための基礎的な内容が多かったかもしれませんが、基礎的な内容を理解しておくと、いざ複雑な実装に取り組む際の助けとなります。 そしてStructured Concurrencyをうまく記述できるようになると、比較的簡単にサービスの局所的なパフォーマンスの改善に繋げることができます。もし無駄に直列で実行しているようなボトルネックがあれば、Structured Concurrencyを考慮してみてはどうでしょうか? 以上です〜
こんにちは。KINTOテクノロジーズのグローバルプロダクト開発グループで働いているティムです。 以前の記事では、 弊社のデザインシステムの開発 と、このシステムに基づいてデザインに関する決定が行われていることについて書きました。 私自身は、2歳になる娘と2023年の8月に生まれた息子の2児の父でもあります。こちらの記事では私の「父親業と仕事の両立」についてお話したいと思います。 本記事は、KINTOテクノロジーズにおけるダイバーシティとさまざまなワークスタイルを探るアドベントカレンダーの全5本中4本目の記事となります。 日本における父親業 初めて父親となったとき、私はすでに日本に住み、今とは別の会社ですが日本で働いていました。 そのときは、 世界でもトップクラスの長期育児休暇制度 を利用して、6か月間の休暇を取得することにしました。生まれたばかりのわが子との絆を深める時間はかけがいのないもので、 ほとんどの国では得られない恩恵 を受けられたことはとても幸運でしたが、その一方で、もっと早く職場に復帰して関連プロジェクトに貢献し、プロフェッショナルとしてのアイデンティティを取り戻すことができたのではないか、とも感じました。 妻が第2子を妊娠したとき、私はKINTOテクノロジーズで正社員として働いていましたが、もう一度育児休暇を取るかどうか悩みました。 手厚い制度があったとしても、自身や周囲の仕事の状況を考えると、実際には 必ずしも簡単に休暇を取得できるわけではありません 。特にまだ入社して間もない身で、休暇を取ることがどう見られるか心配でした。 育休を取るか、取らないか そんな中、KINTOテクノロジーズの多様性のある職場環境やフレックスタイム制について知り、不安が解消されました。新型コロナの流行を受けて、当時会社では午前中は在宅勤務、午後は出社を奨励する臨時措置が導入されており、これが仕事と家庭を両立させる現実的な方法となったのです。 これは私自身だけでなく、家族にとっても画期的なことでした。オムツを替え、泣いている赤ん坊を寝かしつけ、療養中の妻をサポートすると同時に、仕事についてもこなせる範囲の業務量を維持することができました。そしてふと、休暇を取らなくてもどうにかなるのではないかと思い、妻との長い話し合いの結果、私たちはあえて育児休暇を取らず、代わりに会社の柔軟性とサポート体制を活用し、個人的にも仕事面でも成長を続けるという決断をしました。 この決断がワークライフバランスにどう影響したか 息子が生まれてから徐々に職場環境に慣れるにつれ、同僚やラインマネージャー、そして人事部が、キャリア形成と並行して家庭を優先させることを真摯にサポートしてくれるのを目の当たりにするようになりました。同じような経験をしてきた彼らとの率直な対話と励ましのおかげで、会社のワークライフインテグレーションの理念を信頼を持って受け入れることができました。 生まれたばかりの息子と同僚からのお祝いの品 おわりに 今思えば、KINTOテクノロジーズの風土があったからこそ、仕事か家庭かの選択を迫られることなく父親になることができたのだと思います。大変な道のりでしたが、職場のサポートと理解に感謝しています。 これから家庭を持とうと考えている人には、その一歩を踏み出すことを心からお勧めします。家庭を持つ社員に対して、とても暖かい職場風土であることを知っているからです。KINTOテクノロジーズの柔軟性を身をもって体験した者として、ここは自身のキャリアと親としての役割のどちらも尊重できる場所だと自信を持って言えます。居心地の良い雰囲気の中で、キャリアを磨きながら子育ての挑戦も乗り切ることができます。 Credit Cover Image by BiZkettE1 on Freepik
はじめに こんにちは!KINTOテクノロジーズ(以下、KTC)の開発支援部に所属するriomaです。 普段はコーポレートエンジニアとして「全社利用するITシステムの維持管理」を行っています。 先日、「 KINTOテクノロジーズ MeetUp!~情シスによる情シスのための事例シェア4選~ 」というタイトルで「コーポレートIT領域に特化した、事例発表+座談会形式の勉強会」を開催しました。 今回は、その勉強会で事例発表した内容について、補足を交えながらテックブログ上で紹介します。 登壇の背景 初の社外勉強会を開催しよう!と社内で企画が上がり、ちょうど直近で発表ネタにできそうなPJを経験していたので認証基盤切り替えを実施したというテーマでお話しさせていただきました。 切り替えたのは当社ではなく兄弟会社であるKINTOの環境のお話しですが、スケジュールや当時起きたことなどを交えつつざっくりですがKTCのコーポレートITとして実施していることの紹介ができたと思っています。 この記事では発表時の資料を用いて簡単な補足と、改めて内容のご紹介をいたします。 前提 「なぜ認証基盤切り替えが必要だったのか。」 KINTOでは認証基盤周りの細かい課題が山積みで、利便性とセキュリティの面にいくつか困っていました。 そこでMicrosoft Entra ID(旧Azure AD)を検討したところ最適解となりそうなことからEntra IDへの切り替えを進めることに決定しました。 他の理由としては、KINTOテクノロジーズの認証基盤がすでにEntra IDだったためゆくゆくはテナント統合などの連携強化を見据えて実施しておきたかったという点や、Microsoft E3ライセンスを持っているのに活用できていなかったからといった理由も相まってこの選択になりました。コストカットという面でも大きなメリットがありました。 切り替えにあたって 切り替えにあたって考慮した事項が2つありました。 1つ目は、これまで証明書を利用して制御していたアクセスポリシーを証明書を使わずに同じような条件で実装することです。 条件付きアクセスを利用した設定ですが、概要としては「MDMに登録済みのデバイス」を条件として設定していて、その属性値に当てはまらないものはブロックされるといった設定を組み合わせて既存よりも強化かつ柔軟なアクセスポリシーを実装することができました。 2つ目は、切り替え時に全アカウントのパスワードがリセットされてしまうという仕様があるという点です。 結構強烈なインパクトがある仕様だったのですが、これに対していかに社内のスタッフに影響を出さずに対応するということが求められました。 それにあたって実施したこととしては、システムからの強制リセット後にこちらで一定のルールに基づいて全てのアカウントへのパスワード変更を実施しました。変更ルールはあらかじめ周知しており、ログイン後の詳細手順を合わせて展開していたため当日、ログインできないといった事象は特に発生せず問い合わせ件数もほとんどありませんでした。 ※実はほとんどの方はパスワードではなくPINでのログインを利用して入っていたため「変更されたパスワードはこれです」といった周知はそこまで意味がなく逆に少し混乱させてしまうことになった 切り替え作業周辺のトラブル 手順書の展開においてはよくあるケースの一つですが、展開した手順書や作業概要の説明がわかりにくいという声を結構いただいていました。これは私がほぼ100%悪かったのですが、認証基盤切り替えといった大きなインパクトを与える作業のリスクや影響を超丁寧に説明しすぎて普段そのシステムに触らない/意識しない人たちから見るととてもわかりにくいような言葉選びや説明をしてしまったことが反省点です。 そして前述しましたが、切り替え後のPCログインパターンの一つに「PINを入力してログインする」というものがあることに直前まで気づいていませんでした。 事前に展開していた切り替え後のログイン手順書では、そこに全く触れておらず普段PINでログインしている方にとっては「???」となる事態を生んでしまいました。幸い、ギリギリ資料は前日〜当日に直すことができたのですが何度も展開しなおす手間となり混乱を与えてしまいました。。 また切り替え作業中に設計ミスに気づき、設計書と手順書を直しながら切り替え手順を実施するというちょっと無理のある対応もしました。。根幹に関わる部分ではなかったのでそこは助かりました。 他にもあるので添付のスライドをぜひご覧ください。 最後に 細かい問い合わせや指摘はあったものの、何十分も業務ができないといったクリティカルなことは起きなかったようで結論としては無事認証基盤切り替えを実施できました。 のちに「こんな大規模な切り替えなのにあまり問い合わせや業務影響がなかったのはすごい」といった社内のスタッフから声があったと教えていただき、とても嬉しかったです。 今後もシステム変更/切り替えなどは継続して実施していくのでこの経験を踏まえて、よりよい社内環境を構築していきたいです。
Introduction I am Hand-Tomi, and I am developing the "my route" app for Android at KINTO Technologies. Recently, our project needed an event trigger for when a RecyclerView item in the RecyclerView was fully visible. There are several ways to do this, but we found a solution that could work by adding a little code to the RecyclerView Adapter. I am writing this article to share and exchange ideas about this method and its implementation. :::details What is a RecyclerView? A RecyclerView is an extensible and flexible UI component for viewing and managing dynamic datasets efficiently in Android applications. ::: The Goal of This Article I will explain how to trigger an event when multiple RecyclerView items are fully visible. For example, when the image on the left (←) is visible, Card1 and Card2 in Title1 and Card1 and Card2 in Title2 should be triggered. I will explain how to trigger Card3 in Title2 when you scroll Title2 and change the visibility as shown in the image on the right (→). Left image (←) Right image (→) Terms Parent RecyclerView : A RecyclerView placed in an overall layout that allows vertical scrolling. Child RecyclerView : A RecyclerView that can be scrolled horizontally as an item in the Parent RecyclerView. Parent RecyclerView Child RecyclerView Receiving Scroll Events First of all, when an item's visibility changes, it is necessary to track the following two events in order to check the item's visibility status. When the parent RecyclerView is scrolled vertically When a child RecyclerView is scrolled horizontally To track these events, set viewTreeObserver.addOnScrollChangedListener for the child RecyclerView. recyclerView.viewTreeObserver.addOnScrollChangedListener { // TODO: Check the visibility status } viewTreeObserver is used to register listeners that watch for global changes to the ViewTree, such as global layout changes, start of drawing, and changes to touch mode. By registering addOnScrollChangedListener in viewTreeObserver , you can get the scroll change events included in the screen. To get an event when the child RecyclerView is placed in the layout, write code that sets the onAttachedToRecyclerView() method in the Adapter of the child RecyclerView and releases it with the onDetachedFromRecyclerView() method. private val globalOnScrollChangedListener = ViewTreeObserver.OnScrollChangedListener { checkImpression() } override fun onAttachedToRecyclerView(recyclerView: RecyclerView) { super.onAttachedToRecyclerView(recyclerView) this.recyclerView = recyclerView recyclerView.viewTreeObserver.addOnScrollChangedListener(globalOnScrollChangedListener) } override fun onDetachedFromRecyclerView(recyclerView: RecyclerView) { super.onDetachedFromRecyclerView(recyclerView) recyclerView.viewTreeObserver.removeOnScrollChangedListener(globalOnScrollChangedListener) } private fun checkImpression() { // TODO Check } By implementing the above code, when the parent or child RecyclerView is scrolled or visible for the first time, the event is passed to the checkImpression() function. Checking That the Child RecyclerView Is Fully Visible If the child RecyclerView itself is not fully visible, the items in it are also considered not fully visible. Therefore, you first need to make sure that the child RecyclerView is fully visible. For that purpose, we created the following function. private fun RecyclerView.isRecyclerViewFullyVisible(): Boolean { if (!this.isAttachedToWindow) return false val rect = Rect() val isVisibleRecyclerView = getGlobalVisibleRect(rect) if (!isVisibleRecyclerView) return false return (rect.bottom - rect.top) >= height } View.getGlobalVisibleRect(rect: Rect): Boolean This method returns true if the view is at least partially visible on the screen, or false if it is not. rect stores the position and size of the view, with the origin at the top left of the screen. if (!this.isAttachedToWindow) return false The getGlobalVisibleRect method may return true if RecyclerView is not included in the layout layer. Therefore, it skips checking and returns `false<4>} if it is not in the layout layer. (rect.bottom - rect.top) >= height Check the height of the visible view and compare it with the height of the view to see if it is fully visible. With this function included in the checkImpression() method, the processing is skipped if the child RecyclerView is not fully visible. private fun checkImpression() { if (recyclerView?.isRecyclerViewFullyVisible() == false) { return } // TODO Check that the items in the Child RecyclerView are fully visible } Get the positions of the fully visible items in Child RecyclerView LinearLayoutManager provides a function that allows you to determine if the items in the RecyclerView are visible on the screen. findFirstCompletelyVisibleItemPosition() Returns the first position that is fully visible on the screen. findLastCompletelyVisibleItemPosition() Returns the first position that is fully visible on the screen. findFirstVisibleItemPosition() Returns the first Position that is at least partially visible on the screen. findLastVisibleItemPosition() Returns the first Position that is at least partially visible on the screen. In this article, we want to make sure that the items are fully visible . So, we used findFirstCompletelyVisibleItemPosition() and findLastCompletelyVisibleItemPosition() . private fun checkImpression() { if (recyclerView?.isRecyclerViewFullyVisible() == false) { return } val layoutManager = layoutManager as? LinearLayoutManager ?: return null val first = layoutManager.findFirstCompletelyVisibleItemPosition() val last = layoutManager.findLastCompletelyVisibleItemPosition() // TODO Trigger the event if there is a newly visible item } Event trigger for when a new item is fully visible When an event with the position obtained in the above session is triggered, it will trigger the event for each item that is currently fully visible. Since we don’t want to get duplicate information, let’s implement an event that triggers when a new item is fully visible. private var oldRange = IntRange.EMPTY private fun checkImpression() { if (recyclerView?.isRecyclerViewFullyVisible() != true) { oldRange = IntRange.EMPTY return } val layoutManager = recyclerView?.layoutManager as? LinearLayoutManager ?: return val newFirst = layoutManager.findFirstCompletelyVisibleItemPosition() val newLast = layoutManager.findLastCompletelyVisibleItemPosition() val newRange = newFirst..newLast for (position in newRange.minus(oldRange)) { // Sends the position of a new item that is fully visible. onImpression(position) } oldRange = newRange } fun onImpression(position: Int) { // Send the impression event here. } newRange contains the position of the item that is currently fully visible on the screen. To avoid triggering duplicate events, remove the previously triggered oldRange and then trigger a new event. This way, the position of the new item that is fully visible is passed to the onImpression() function. Then, by implementing the code that sends the event in this function, the process of sending the impression event is completed. Summary By using the above code, it is possible to monitor impression events on the Adapter side. This Project Manager created ImpressionTrackableAdapter , which has the impression function, to improve convenience, and decided that the required Adapter inherits ImpressionTrackableAdapter . I will attach the ImpressionTrackableAdapter code to the toggle below, so feel free to copy and paste it if you need it. :::details Complete code abstract class ImpressionTrackableAdapter<VH : RecyclerView.ViewHolder> : RecyclerView.Adapter<VH>() { private val globalOnScrollChangedListener = ViewTreeObserver.OnScrollChangedListener { checkImpression() } private var recyclerView: RecyclerView? = null private var oldRange = IntRange.EMPTY abstract fun onImpressionItem(position: Int) override fun onAttachedToRecyclerView(recyclerView: RecyclerView) { super.onAttachedToRecyclerView(recyclerView) this.recyclerView = recyclerView recyclerView.viewTreeObserver.addOnScrollChangedListener(globalOnScrollChangedListener) } override fun onDetachedFromRecyclerView(recyclerView: RecyclerView) { super.onDetachedFromRecyclerView(recyclerView) this.recyclerView = null recyclerView.viewTreeObserver.removeOnScrollChangedListener(globalOnScrollChangedListener) } private fun checkImpression() { if (recyclerView?.isRecyclerViewFullyVisible() != true) { oldRange = IntRange.EMPTY return } val layoutManager = recyclerView?.layoutManager as? LinearLayoutManager ?: return val newFirst = layoutManager.findFirstCompletelyVisibleItemPosition() val newLast = layoutManager.findLastCompletelyVisibleItemPosition() val newRange = newFirst..newLast for (position in newRange.minus(oldRange)) { onImpressionItem(position) } oldRange = newRange } private fun RecyclerView.isRecyclerViewFullyVisible(): Boolean { if (!this.isAttachedToWindow) return false val rect = Rect() val isVisibleRecyclerView = getGlobalVisibleRect(rect) if (!isVisibleRecyclerView) return false return (rect.bottom - rect.top) >= height } } ::: End I hope this article will help you with triggering impression events when working with multiple RecyclerViews. If you have any questions or feedback, please feel free to contact us. Thank you for reading!
Introduction I am Okapi, from the Quality Assurance Group. In the previous article, titled " Increased Awareness of QA ," I described how the QA Group participates in projects and performs QA tasks. In this article, I would like to write about the key characteristics of QA testing for native apps, drawing from my recent experience working on several native app projects. About QA Testing for Native Apps In basic QA design, a test scenario is created that clarifies the scope of the testing (what is to be tested or excluded), and test cases (preconditions, procedures, and expectations) are then created from that perspective. However, in addition to the above, a native app design requires consideration of the following points: ・ iOS and Android ・ OS versions and smartphone devices to be tested ・ Native app functionality As a result, even native apps with less complex functions exhibit characteristics that tend to increase the scale of testing. Item Characteristics iOS and Android ・ iOS is an operating system developed by Apple ・ Android is an operating system developed by Google Due to the differences in the development environments, the amount of testing simply doubles. OS versions tested and smartphone devices to be tested There are many types of smartphone devices available to the public. Testing all of these and OS version combinations is practically impossible. Native app functionality Due to the unique functions and specifications of mobile devices affecting app functionality, it is necessary to verify the operation of installed native apps based on these characteristics. ・ Push Notification:  The function that enables apps to send notifications to the user ・ Touchscreen:  The feature that allows users to interact with the app by touching areas on the screen ・ Permission:  The feature that enables a pop-up dialogue requesting the user’s consent before the app attempts to access certain functionalities of the device. iOS and Android As the operating systems of iOS and Android are different, the amount of testing is doubled, so we want to reduce the man-hours. However, the development team is also divided into iOS, Android and BFF. Even if there are no bugs on the iOS version, bugs may be found on the Android version. Same may be true on reverse: no bugs on Android, but issues on the iOS version. Issues with the API may require testing on both Android and iOS. These patterns may occur with each function. Therefore, if we proceed in such a way as to omit testing on Android because it has been confirmed on iOS, or vice versa, there is a risk that 'bugs affecting quality' may persist. So we are proceeding with a basic policy of implementing the system for both operating systems. OS Versions and Smartphone Devices to be Tested Since testing all combinations of OS and devices is impossible, we always confirm the recommended environment for the target app (e.g., iOS 15.0 or higher / Android 9 or higher), taking into account the market share of each OS. And then assign: Main OS: All tests conducted Non-main OS: Display check and locations where OS-related bugs occurred As device-dependent bugs are rare, and even if they occur, they are unlikely to be fatal, testing is basically conducted on devices that comply with the above-mentioned OS. However, there is an exception: Functional tests involving camera activation will be conducted on all types of devices available to us. The reason for this is that during the initial QA testing of a native app, there was a relatively high frequency of issues where the app would crash when the camera was launched on certain devices. Native App Functionality In the process of QA of native apps, it is necessary to verify whether the app operates in accordance with specifications, considering the functionality of the mobile device. However, the specification sheet typically outlines only the app specifications and often does not describe the functions of the mobile device. Therefore, bugs can occur when device functions affect the behavior of the app. Test designs based on an understanding of device functions can effectively detect bugs from a usability perspective. Item App Specifications Device Functions Common Bugs from a Usability Perspective Push Notifications ・ Push notifications'  - timing  - content  - transition destination when tapped ・ In the app notification badge's  - display timing  - deletion timing ・ Notification badge of app icon  - display when multiple accumulations  - display timing  - deletion timing ・ Specification by change of condition  - login/logout  - App foreground/background/lock screen ・ The number of app icons does not match the number of notifications when receiving multiple notifications ・ The app notification badge  - is not displayed  - display timing is slow ・ The app notification badge  - does not disappear  - deletion timing is slow ・ When logging out, the target notification  - cannot be received  - error occurs when notification is pressed ・ No notifications when the app is in the foreground Touchscreen Behavior when a button or link is tapped ・ Double-tap  - tap twice quickly ・ Flick  - swipe left and right with a fingertip ・ Swipe  - slowly slide left and right ・ Pinch in  - touch the screen with two fingers simultaneously and move them closer together to zoom out ・ Pinch out  - touch the screen with two fingers simultaneously and move them apart to zoom in ・ Double-tap  - to open the same screen twice  - registration details are registered twice ・ Display error occurs when performing flick/swipe/pinch in/pinch out Permissions ー ・ Allow/disallow push notifications ・ Allow/disallow location information ・ Allow/disallow camera ・ Push notification permission setting is not linked to settings ・ Crash occurs with location permissions and camera permissions Next Challenges In QA testing scenarios for native apps, we check for specifications according to system requirements and app-specific functions. As I became more fully involved in QA for app development, I learned that the iOS screening process is very strict. For example, ・ Rejected unless it is in "non-login accessible" format ・ Rejected if it contains a link to GooglePlay and so on. I believe that by adding these insights into our knowledge, we can incorporate them into further ad-hoc testing scenarios as one perspective and lead to further quality improvements. Conclusion In this article, I introduced the characteristics of QA testing for native apps that have a tendency to increase the scale of testing. However, in native apps, QA reports numerous bugs from various perspectives, including iOS, Android, and API. This places a substantial burden on the development side to address modifications, resulting in higher costs for both sides compared to web apps, making the current situation less efficient. The latest app development languages I worked with include Kotlin for Android and Swift for iOS, respectively. Native development for each platform has the advantage of individual optimization that allows for fine-tuning. Although there are cross-platform technologies such as Kotlin Multiplatform Mobile (KMM) and Flutter that can be developed using the same source code, there are disadvantages, such as the need to learn separately the parts that depend on individual OS, such as device control.[^1] [^1]: Camera, GPS, sensor system, etc. In that regard, native languages are directly supported by the corresponding OS, providing a high degree of freedom in leveraging device functions. In the future, I expect and hope that the most suitable development method will be chosen based on the preferences of development engineers, as well as the purpose of app development. As a QA engineer, I also would like to exert effort in flexibly improving the efficiency of QA testing in alignment with the development method.
はじめに 初めまして、KINTOテクノロジーズ株式会社でmy routeのAndroid開発をしているソミです。my routeは'おでかけ情報'(お出かけ·交通情報)・'地図で探す' (地図検索)・'おでかけメモ' (メモ)などの様々な機能を提供して、移動の体験を豊かにしているアプリです。 現在、my routeのAndroidチームはUI/UXの改善を目指してJetpack Composeを積極的に活用しています。このUIツールキットはコードの可読性を高め、迅速かつ柔軟なUI開発を可能にします。さらに、宣言的UIアプローチにより開発プロセスを簡素化し、UIコンポーネントの再利用性を向上させます。この背景を踏まえて、my routeのAndroidアプリで使用されているJetpack Composeの機能について、いくつかの例を通じて紹介したいと思います。今回は4つの機能を紹介します。 機能紹介 1. drawRectとdrawRoundRect Jetpack Composeでは、Canvasを利用して特定の範囲での描画を可能にします。そして drawRectとdrawRoundRect はCanvasの内部で定義できる図形に関連する関数です。drawRectは、指定されたオフセットとサイズで長方形を描画します。一方、drawRoundRectは、drawRectのすべての機能を含むと同時に、cornerRadiusパラメーターを追加し、角の丸みを調整することができます。 my routeには、端末のカメラでテキスト形式のクーポンコードを読み取る機能があります。コードを正確に認識するためには、テキストを認識する部分のみを透明にし、残りの部分を暗くする必要がありました。そのため、drawRectとdrawRoundedRectでUIを実装しました。 @Composable fun TextScanCameraOverlayCanvas() { val overlayColor = MaterialTheme.colors.onSurfaceHighEmphasis.copy(alpha = 0.7f) ... Canvas( modifier = Modifier.fillMaxSize() ) { with(drawContext.canvas.nativeCanvas) { val checkPoint = saveLayer(null, null) drawRect(color = overlayColor) drawRoundRect( color = Color.Transparent, size = Size(width = layoutWidth.toPx(), height = 79.dp.toPx()), blendMode = BlendMode.Clear, cornerRadius = CornerRadius(7.dp.toPx()), topLeft = Offset(x = screenWidth.toPx(), y = rectHeight.toPx()) ) restoreToCount(checkPoint) } } } 上のコードは下記のUIで実装されます。 コードを説明すると、overlayColorで色が指定されたdrawRectを使用して画面全体を暗くします。さらに、drawRoundRectを利用して角の丸い透明な四角形を作成し、テキストを認識できる領域であることを明確にしました。 2. KeyboardActionsとKeyboardOptions KeyboardActionsとKeyboardOptions はTextFieldコンポーネントに属するクラスです。TextFieldは入力を処理するUI要素で、KeyboardOptionsを使用して入力フィールドに現れるキーボードの種類を設定することができます。そして、KeyboardActionsは、Enterキーを押した時の動作を定義できます。 my routeのアカウント画面には、支払いのためクレジットカードの情報を保存するところがあります。カード番号を入力する部分は端末キーボードと関わっているので、KeyboardActionsとKeyboardOptionsで実装しました。 @Composable fun CreditCardNumberInputField( value: String, onValueChange: (String) -> Unit, placeholderText: String, onNextClick: () -> Unit = {} ) { ThinOutlinedTextField( ... singleLine = true, keyboardOptions = KeyboardOptions( keyboardType = KeyboardType.Number, imeAction = ImeAction.Next ), keyboardActions = KeyboardActions( onNext = { onNextClick() } ) ) } 上のコードは下記のUIで実装されます。 クレジットカード番号のみを入力するため、KeyboardActionsではKeyboardTypeをNumberに設定し、入力時に次に移動できるようにImeAction.Nextを設定しました。また、KeyboardOptionsでは、キーボードの「次へ」ボタンを押したときにonNextClick()メソッドが実行されるようにしています。ちなみに、onNextClick()はFragment内で以下のように設定されています。 CreditCardNumberInputField( ... onNextClick = { binding.creditCardHolderName.requestFocus() } ) この設定により、「次へ」ボタンを押すと、クレジットカード番号の入力から次のステップ、氏名を入力する部分に進みます。 3. LazyVerticalGrid LazyVerticalGrid は、アイテムをグリッド形式で表示します。このグリッドは縦スクロールが可能で、多数のアイテム(または長さが不明なリスト)を表示します。また、画面のサイズに応じて列の数が調整され、様々な画面で効果的にアイテムを表示することができます。 my routeのおでかけ情報の「今月のイベント」セクションは現在の位置が属するエリアで開催される多くのイベント情報を提供します。このように大量のイベント情報(タイトル、画像、開催期間)は、Columnで実装するには限界があるため、LazyVerticalGridを使って数列にわたって上下にスクロール可能なコンテナにイベントアイテムを表示しました。 private const val COLUMNS = 2 LazyVerticalGrid( columns = GridCells.Fixed(COLUMNS), modifier = Modifier .padding(start = 16.dp, end = 16.dp), horizontalArrangement = Arrangement.spacedBy(16.dp), verticalArrangement = Arrangement.spacedBy(20.dp) ) { items(eventList.size) { index -> val item = eventList[index] EventItem( event = item, modifier = Modifier.singleClickable { onItemClicked(item) } ) } } 上のコードは下記のUIで実装されます。著作権のため画像とタイトルは削除しました。 eventListに含まれるデータのサイズに基づいて、アイテムを一定の間隔でグリッドに表示し、絶えずイベント情報を見ることができるようになりました。 4. Drag And Drop draggable修飾子 は、画面のコンポーネント内部に何かをドラッグアンドドロップする機能です。ドラッグの流れ全体を制御する必要がある場合は、pointerInputを使います。 my routeには「myステーション」という、最大12個まで自分だけの駅またはバス停を登録する機能があります。カードリスト形式で表示されるため、一目で確認することができます。このカードリストは自由に順番を変更することができて、実装ためにはドラッグ&ドロップ操作が必要です。 itemsIndexed(stationList) { index, detail -> val isDragged = index == lazyColumnDragDropState.draggedItemIndex MyStationDraggableItem( detail = detail, draggableModifier = Modifier.pointerInput(Unit) { detectDragGestures( onDrag = { change, offset -> lazyColumnDragDropState.onDrag(scrollAmount = offset.y) lazyColumnDragDropState.scrollIfNeed() }, onDragStart = { lazyColumnDragDropState.onDragStart(index) }, onDragEnd = { lazyColumnDragDropState.onDragInterrupted() }, onDragCancel = { lazyColumnDragDropState.onDragInterrupted() } ) }, modifier = Modifier.graphicsLayer { val offsetOrNull = lazyColumnDragDropState.draggedItemY.takeIf { isDragged } translationY = offsetOrNull ?: 0f } .zIndex(if (isDragged) 1f else 0f) ) val isPinned = lazyColumnDragDropState.initialDraggedItem?.index == index if (isPinned) { val pinContainer = LocalPinnableContainer.current DisposableEffect(pinContainer) { val pinnedHandle = pinContainer?.pin() onDispose { pinnedHandle?.release() } } } } 上のコードは下記のUIで実装されます。 drag操作はpointerInputによって検出され、detectDragGestures関数はdragイベントを処理します。アイテムをdragする際、lazyColumnDragDropStateオブジェクトのonDrag、onDragStart、onDragEnd、onDragCancelメソッドを呼び出してドラッグ状態を管理し、drag中のアイテムのY軸位置を更新して視覚的に移動する効果を提供します。また、drag中のアイテムがスクロールによって画面外に出るのを防ぐために、このコードはisPinned変数とLocalPinnableContainerを活用しています。 まとめ 簡潔に書いたため、すぐに理解できない部分もあるかもしれませんが、my routeの活用方法をご紹介させて頂きました。最初、私はJetpack Composeに慣れていなかったため、my routeのUIをXMLレイアウトから書き換えるのがやや複雑に感じることもありましたが、Jetpack Composeで書かれたコードはすぐに理解することができ、可読性やメンテナンスの面で非常に効率的な方法だと感じています。今後もさまざまなJetpack Composeを基にしてmy routeのUX改善に努めたいです。最後までお読みいただき、ありがとうございました。
Introduction  Hello! I am mmm from the QA team.  I am mainly involved in the QA of KINTO web systems. The systems we handle include the frontend visible to customers, the back-office area used by dealerships, and several systems for internal operations. Between these systems, various types of data are linked.  Recently, we have adding been more apps such as " KINTO Unlimited " and " KINTO Easy Application ". As new features and requirements are added, organizing their relationship to and impact on existing systems is essential. Therefore, it is necessary to understand not only the specifications for each system unit, but also the series of operations in KINTO services to grasp the flow of data.  I personally consider organizing QA work challenging and crucial, especially as each new system or functional requirement increases the complexity and impact on QA as well.  In this article, I would like to focus on test design, discussing challenges from my past projects and considerations when interacting with the development team. Common Challenges Specifications for the same function exist in multiple documents  Since a project may involve multiple development teams, each team can have documentation for the same function. Therefore, the following are likely to occur. There are differences in content between documents, and the correct specifications are unknown. There are wording fluctuations between documents, leading to miscommunication. Specifications that should be considered on the development side are buried, leading to bugs. A specification for the status under review exists.  While reading documents, you may find specifications listed with expressions such as "under confirmation" or "?" as it had points not yet finalized. These have the following effects on test design and implementation. Test design: The number of test cases is tentative (may increase or decrease), as we proceed with a tentative testing perspective. Test implementation: Testing of undefined ranges cannot proceed, and when specifications change, case modification work will be required. No documentation that provides a bird's eye view of the status of each system.  While the actual front-end area that is visible to users may display status, on the back-office, level, behind the scenes may retain detailed status, but there is not always documentation that can be seen at a glance.  For example, when purchasing a product on an e-commerce site, a store's status is more (actually, probably more) detailed than the information seen on the customer's side  In the absence of documentation that provides a bird's eye view of both, the QA side needs to organize test design in terms of data linkage between systems. The specification of the calculation system is described in code.  If there is a test that requires the calculation of amounts or days, we check the development specifications, but they are sometimes written in code. In such cases, there are the following effects. Deciphering the code takes time. If specifications are not summarized so that a third party can understand its contents, by the time a test is implemented the QA work becomes a silo that depends on the knowledge of specific people. Considerations When Interacting with the Development Team Get an overview of the project  Given that the above-mentioned issues are inevitable due to the nature of the project. I believe that first inquiring about the background of the project's inception and understanding its purpose and history can help the QA side better comprehend the specifications. This, in turn, enables questions about potential excesses or deficiencies in the specifications. Request an outline of scenario operations.  Although the specifications for each system are available and you can understand if you read documentation, receiving explanations directly from the development side who understands the system has the following advantages. (The QA side often makes its own materials to understand the specifications and gain a bird's-eye view.) Easier to understand the operation of the intended actor/user Easier to visualize data flow Timely questioning promotes a deeper understanding Check the segregation of documents  We will simply check to see which documents contain the correct specifications for this functional requirement and screen design, and whether other documents exist but are outdated notes, as it cannot be determined.  As the content of the area in which each development team is in charge is often up-to-date and correct, we aim to minimize the burden on the developer side by asking closed questions. "Is it correct that Document A accurately reflects the specifications for ●●? May I confirm that Document B also contained the same information, but slightly differently? " Review the Restrictions  If there are currently known restrictions in QA work, asking for sharing in advance will make it easier for the QA side to consider how to respond. Request a separate briefing for complex specifications such as calculation systems.  Since various prices and plans exist, there are multiple formulas for calculating them. Even with similar formulas, the values obtained may differ slightly. To prevent mistakes in advance, we are conscious of clarifying the following when asking for explanations from developers. The original meaning of the amount The meaning of values used in the formula and where they are referenced How to count the number of days Considerations to keep in mind when testing Existence of calculation tools  For calculation systems, the QA side often creates its own calculation tools for efficient testing. Specifications are compiled and finally reflected in the tools through discussions with developers.  In addition, although I mentioned the calculation system this time, if a specification proves challenging to understand, direct consultation with the developer is often sought. Conclusion  When I was struggling with the content of this writing request, a member of the development team who is also in charge of this blog said, "It would be helpful to know what kind of information the QA team wants when requesting QA."  Despite not directly aligning with this content, I wrote it with the hope that it will be useful for awareness of QA work other than testing by sharing information on the challenges in test design, which is the starting point of QA work.  In addition, the development team has already given consideration to the challenges mentioned above, and I feel that the work environment is quite organized compared to when the QA team was first formed.  Nevertheless, I believe that one of QA work is to organize information that cannot be seen from documents alone, so I will continue to exert effort not only to read the specifications but also to seek and organize the impact on other specifications.
はじめに オリジナルマスコットキャラクターができるまでのお話を執筆します。 第一弾は、キャラクター欲しいよねーという声を受けて形になるまでです。 クリエイティブ室の杉本です。私が所属するクリエイティブ室は、超簡単にいうとクルマのサブスクKINTOのお客様とコミュニケーションをとるアウトプットを、企画・制作する担当をしています。少し、小難しくいうと、事業側が持つコミュニケーションの課題を理解し(内製だからできる!)解決方法を可視化して、ビジネス側から発するすべてのコミュニケーションメッセージをぶれずに一貫性をもって伝える(=ブランディング)、そんなことを担当しています。 そんな私たちが、KINTOのマスコットキャラクターを開発したので、どのように作っていったかをお伝えさせていただきます。かっこいいキャラクター制作のHow toというわけではもちろんないです。産み出した我が子の母子手帳の記録のような、いや小学生のときの夏休みの宿題の絵日記のような、そんな感じで読んでいただければと思います。 いよいよ始まるキャラクター誕生ストーリー 2021年11月、公式マスコットキャラクター開発についてキックオフ。マーケティング企画部広報・ブランディングチームのメンバーと、我々クリエイティブからはCDの役目をもつ私と、AD1名が参加。「マスコットキャラクタープロジェクト(以下PJ)」を結成しました。プロジェクトというと大々的な感じですが、企業CMやポスターデビューといった企業ブランディングという大きな枠ではなく、若者や女性のクルマ離れの一つの解決施策で、小さく生んで大きく育てようという立ち位置で始めました。とはいうものの、メンバー全員、誰からも愛される我が子を産み育てたいという気持ちでいっぱいでした。 実は、公式キャラクターではないですが、もともとサービスサイト内で使用していたイラストがありました。社内での通称はクロコくん。このクロコくんをどう活かすかを検討する会が発端でした。彼らは、「クルマの保有を迷っている人、運転に不安がある人」をひっそり応援するという設定。クロコくんに愛着を持ちながらも、この機会にKINTOのキャラクターについてをゼロから考えて新たに誕生させてもいいのではないかという意見が多く、一気にキャラクター誕生プロジェクトになりました。 クロコくんのいい仕事っぷり! なにからはじめた? まず、キャラクター開発の目的の明確化。 【目的:「生活者にKINTOのファンになってもらう」】。 クルマを購入したことのない方やクルマにまだ興味のない方をターゲットに、クルマライフの楽しさを知ってもらいたい。教えるという設定よりも「一緒に移動することを楽しんでくれる」キャラクターが最適なのではないかということになりました。 いきなりイラストレーターさんに依頼するのではなく、インナーブランディングも兼ねて、社員も巻き込みキャラクター案を自由に出してみましょうということになりました。 サービスをつくるビジネス人材と、技術者から構成される社内の視点から多彩なものが出てくるだろうと想定し、マスコットキャラクターPJとしてはワクワクどきどき。 募集期間中に、キャラクターを選定する際の軸をつくるために、チームでキャラクターのアイデアを出し合いました。集めたアイデアから以下3つのステップで、キャラクターの方向性を決めていきました。 1. 【アーキタイプに沿っているか確認】 ここからは、アウトプットに結びつくステップがはじまるので、クリエイティブ室がリードしていきました。 また別の機会にお話しさせていただきますが、KINTOにはブランドパーソナリティなるものが存在します。ブランドを人間に例えるとどんな要素、性格で構成されているかを示したものです(アーキタイプ)。KINTOブランドを構成するパーソナリティ要素=KINTOさんは、自由な移動のスタイルの実現を探し続ける「Explorer」、親しみやすく共感力のある「Every person」、ユーモアのある「Jester」、専門知識を提供する「Sage」。一見、バラバラのようですが、「好奇心が強く、周りの人を楽しませ、自分の持っている知識で役に立ちたいと思っている人」がKINTOさんのパーソナリティです。もちろん、キャラクターはブランドを表す大きな存在になるので、設定に関してこのパーソナリティにも沿う形にしましょうということになりました。 2. 【キャラクターの性質・モチーフ】 出し合ったキャラクターのアイデアを「役割・属性」「モチーフ」と整理し、性質を出していきました。 こちらはアイデアの一部 3. 【課題を解決できそうなキャラクターアイデアの発散】 2.で整理した性質とモチーフを4つの方向性のカテゴリーに分けました。 A: ドライブの楽しさや自由を表し、ブランドストーリーを語る「KINTOのDNAそのものを表すキャラクター」 B: 親近感のある「ドライバーに寄り添う姿勢を表すキャラクター」 C: 「自由、前進の移動要素を象徴するキャラクター」 D: 「先進性、知性を体現したキャラクター」 社内から集まった愛すべきキャラクターたち 社内に募集告知ポスターを貼りました。 PJメンバーでキャラクターの軸を先行して議論する中、募集していた22年10月から11月の一ヶ月間で、有志から24ものキャラクター案が集まりました。絞り込みを行う際、「KINTOに合っていて好きなキャラクターはどれですか?」と全社にアンケートをとっています。ちなみに、マスコットキャラクターを制作する経過途中、KINTO/KTC社員の希望や意向もなるべく吸い上げたいというPJの想いもあり、進捗がある度に全社にアンケートをとりました。これは、今になって思うと、社員一人ひとりに、キャラクターへの愛着の種を蒔いていた時期だったなと実感しています。 話は戻して、初回アンケートの結果、KINTOの社名の由来でもある“雲”をモチーフにしたアイデアに人気が集まったので、方向性として、「雲のキャラクター」という軸が決まりました。 ここから、審査員(プロジェクトメンバー)の意見と回答者の回答を比較し、共通項や違いを見つけ、キャラクターにどのような要素をプラスしていくかをまとめていきました。 どんな容姿で、どんな性質をもつ子に育つのか、核となる部分になります。 さあ、今回はここまでにさせてもらいます。次回のブログでは、どのように形になっていったかをお伝えさせていただきます!
こんにちは。KINTOテクノロジーズ(トヨタグループ)でサイバーセキュリティエキスパートとして働いているサジュ・マタラダンです。 このブログでは、文化の微妙な違いからプロフェッショナルとしての成長の機会まで、私が日本のIT企業で働いた経験についてお話しします。 本記事は「ダイバーシティ」をテーマにしたシリーズ記事の1つで、このシリーズではKINTOテクノロジーズにおけるさまざまなワークスタイルや考え方を掘り下げています。 日本での経験 日本企業の技術革新については、その魅力的な立地と最先端かつハイエンドなテクノロジー環境から、常に注目をしてきました。 26年間のキャリアのうち日本で暮らしたのは15年で、その間別の国に滞在したり、グローバルプロジェクトに関わったりしました。ありがたいことに、日本を代表する企業のIT環境の管理や最新のテクノロジーへの変革に関われました。KINTOテクノロジーズに入社する前は、世界第3位の日系アパレル企業で6年間ITセキュリティ部門に勤務し、それ以前は、アジア最大の日系製薬企業をはじめ、国内外の大企業のITアウトソーシング案件に携わってきました。 文化的なつながり 私の経験から見て、日本のテクノロジーは変革期にあります。日本企業は以前よりもさらにグローバルな視野で事業展開を進めており、多様なグローバル人材を採用する動きが活発です。中でもテックカンパニーでは世界に通用するレベルとなるため、グローバルな働き方とプロセスが採用され、チームワークや連携、組織の共通目標への貢献が重視されています。KINTOテクノロジーズも決して遅れを取っておらず、100%トヨタ所有の企業として、「トヨタウェイ文化」の中でうまく機能し、社員が地域を越えて連携できるようなグローバルな環境を提供しています。世界をより良いものにするため、モビリティの未来を創造しています。 求められる言語能力 グローバル人材が日本で働くために、言語は常に障壁です。テックカンパニーでは英語が広く使われるようになってきていますが、基本的な日本語が理解できることは武器となりえます。KINTOテクノロジーズでは、日々の業務で英語を使うことは特に大きな課題はありません。異なる国から多様なチームメンバーが集まっているため、日本語に加えて英語も共通言語として使えるようになりました。 チャレンジ 私の経験から、日本の文化として「悪いことは起きない」という考え方があり、「信頼」が非常に重要なものとして位置づけられるため、一般的にサイバーセキュリティに対する意識が低いと感じています。しかし、不正アクセスやデータハッキング、マネーロンダリングなどがインターネット上で蔓延している昨今、脅威に国境はありません。この点で、日本企業はあまりにも脆弱に見えます。サイバーセキュリティの専門家として、企業の重要なアプリケーション、インフラ、データ、およびサービスへのインターネットからの安全なアクセスを確実に保護するセキュリティコントロールとフレームワークを構築することは、大きな挑戦です。「Mobility as a Service(MaaS)」の新しい技術要件や関連するeコマース取引、顧客と社員によるオンラインサービスの利用が増えてくると、特にこれを実感します。 下図のようなセキュリティフレームワークを構築し、エンドツーエンドでデータとそのアクセスの安全に保護するためには、複雑であらゆる方面を考慮したアプローチが求められます。そのような難しい目標であっても、KINTOテクノロジーズではテックに精通した社員やサポートしてくれる経営陣がいるので、比較的スムーズに対応できています。それに加え、MaaSのIT環境にさまざまなレベルで携われることをとても嬉しく思っています。 テクノロジー環境 私自身の経験では、日本のIT企業では少し古くて時代遅れの技術を使い、アップグレードが遅れている印象がありました。一方で、KINTOテクノロジーズでは最新のクラウド技術やツールがすべて導入されています。KINTOテクノロジーズでは、AIからその他最先端のソフトウェア開発まで、幅広い分野で社員が持つ興味や情熱をキャリアと繋げるためのさまざまな機会を提供しています。また、懇親会などのネットワーキングイベントや、業界カンファレンスへの参加を通じて、同じ界隈の知見者と繋がり、最新のトレンドを把握する機会も多くあります。 職能開発 管理職であれ技術職であれ、日本のテックカンパニーではプロフェッショナルとしての成長の機会が十分に与えられています。ありがたいことに、私の経験上これまでの企業でもそのような素晴らしい機会を得られてきました。KINTOテクノロジーズでは、グローバルな業界水準と同じレベルにあります。マネジメント層は、社員がカンファレンスに出席してトップクラスの知見者と交流したり、学習曲線を最大化してスキルを向上させ、キャリアを積むことを奨励しています。 ワークライフバランス これまでの会社でも、現在のKINTOテクノロジーズでも、健全なワークライフバランスを維持することの大切さと、それが日本の倫理観の基礎であることについて、人事から説明がありました。近年ではさまざまな企業が社員の健康を重視するようになり、フレックスタイム制やリモートワーク、ウェルネスプログラムなどを提供するようになっています。仕事とプライベートのバランスを取ることは、奨励されるだけでなく、豊かで有能なチームメンバーの証とされます。 さいごに KINTOテクノロジーズをはじめとする日本のテックカンパニーで働くことは、プロフェッショナルとしての成長と技術革新に満ちたエキサイティングな旅になると思います。日本が製造業とハイエンドテクノロジーの世界的なテック大国である限り、日本のテックカンパニーで働くということは、日本文化の美しさと結びついたテクノロジーの未来へのチャンスに溢れたキャリアの転機となりえるでしょう。気になる方はぜひチャレンジしてみてください!
Hello I am HOKA from the Human Resources Group at KINTO Technologies. I am responsible for organizational human resources and recruitment PR. I also manage our X (formerly Twitter) account . As part of the annual Advent Calendar event, my focus on this Tech Blog article will be about how the Human Resources Group started. I hope you will stay with me until the end. The Establishment of KINTO Technologies, a Development Organization KINTO Technologies Corporation (hereinafter, "KINTO Technologies") was founded in April 2021 and is now in its third year. There are more than 320 employees as of December 2023. The company is growing rapidly. KINTO Technologies was originally the IT Development Group of KINTO Corporation (hereinafter, "KINTO") and mainly consisted of IT engineers. Before KINTO Technologies was founded, KINTO was in charge of recruiting IT engineers, but it was a seller's market where every company had a shortage of IT engineers. It was hypothesized that in order to recruit the best IT engineers, you had to change hiring methods to fit the IT industry. So, Iwamoto, who had experience in hiring engineers in the IT industry, started hiring IT engineers for KINTO Technologies. She was the first member of our team. Our Mission Was To Just Hire Engineers At first, Iwamoto's mission was to just hire engineers. When Iwamoto joined in November 2021, KINTO Technologies had about 130 employees. There was no talk of setting up a human resources department, and Iwamoto was the only person in charge of hiring. Iwamoto started with the secretary, Shida. IT Engineers Cannot Be Hired as Automotive Professionals First, Iwamoto set out to change the hiring style to match the IT industry. ● Replacing PDF job applications with an applicant tracking system (ATS) Due to the job application form being in PDF format, candidates were required to enlist with a recruitment agency in order to complete the application process. We renovated our corporate website and implemented an applicant tracking system (ATS) so that interested candidates could submit their applications easily. ● Engaging in dialog instead of scrutinizing during interviews Having job interviews with 6 interviewers per candidate was oppressive and unpopular. We trained interviewers and changed the interview style so they engaged in dialog and casually asked about the applicant's career. ● Switching to a flexible selection process that involves getting close to the candidate We listened to candidates' requests, did casual interviews with engineers in the workplace and offline interviews, and aimed to schedule interviews to accommodate the candidates. We switched to a more flexible selection process. ● Contacting successful job applicants five business days sooner After the final interview, a paper offer letter with the interviewer's comments was mailed to Nagoya, and it took five business days for the employment conditions to be approved. We reviewed the process and implemented an electronic approval system. Thanks to that, approval was done the day after the final interview. ● Holding company briefing sessions for recruitment agencies To better explain our prospects and position, we held an online briefing session that was about three hours long and had explanations by Mr. Kageyama, the Executive Vice President of KINTO Technologies, with other several managers. About 15 recruitment agencies participated in this event, and they are still supporting the recruitment of KINTO Technologies. Finally, Sheer Effort! Iwamoto held interviews from 9:00 to 21:00 while implementing the aforementioned Kaizen (improvement). Takeno, a talented recruitment assistant, joined the company in January 2022 and sped up the hiring process further. (She goes home around 18:30 these days, so don't worry lol) Establishing the Human Resources Group After Iwamoto joined, we hired 140 people in one year (from January 1, 2022, to December 31, 2022). On the other hand, as our organization expanded, organizational issues came to the surface. Then, Tsun-Tsun, our labor expert, joined the company in April 2022. We gradually started doing HR work other than hiring, such as consultations for new employees and retirees. We now play a role in delivering the voices of our employees to management. Tsun-Tsun also started making Kaizen projects in the office (link to his article). Then, Mr. Kageyama finally told us to make official our Human Resources organization. In August 2022, the President Said, “I Calmly Stand at a Staircase Landing” In July 2022, KINTO Technologies will hold its first "all-employee interviews." Of course, this will be the first time for the employees, so the company received many requests and concerns, and the Human Resources Group was wondering how to solve them. Looking at the situation, Kotera, the president and CEO of KINTO and KINTO Technologies, gave us this advice: "Stand calmly at a staircase landing. Build management functions appropriate for the size of the organization, then hire the required people." That was in August 2022. Since then, we hired less frequently but focused on training our management and improving our organizational capabilities. Managers gradually started cooperating more with each other. Reviewing the Hiring Requirements in October 2022 Since then, KINTO Technologies developed services other than for the KINTO brand and did more as a company as engineers had bigger roles. We thoroughly reviewed the hiring requirements and targeted people with experience in developing their own services to better fit our organization. Around that time, we were joined by Muranaka, who had experience in HRBP at a mega venture. By building the aforementioned management functions, hiring less frequently, and reviewing the hiring requirements, the Human Resources Group's leadership strengthened its organizational capabilities, but Muranaka was overwhelmed by how quickly KINTO Technologies grew. He smiled bitterly, saying, "I have no memory of anything from the moment I joined in October 2022 to March 2023." The Human Resources Group Enters Its Second Phase KINTO Technologies has 320 employees as of December 2023. As the organization has grown, we have added more layers in departments and increased the number of roles. We think about the kind of people we want to have in the company as the Human Resources Group started to receive more requests, and we conduct more personal interviews with managers and employees. We also train more employees to become interviewers and constantly get more employees to be involved in the recruitment process. The hiring activities that Iwamoto started alone is by now a series of activities conducted cross-functionally. There are currently ten people in the Human Resources Group, and it is divided into the Recruitment Team, the Labor Affairs and General Affairs team, and Organizational Human Resources team. The Human Resources Group intends to keep working together in 2024 so that we can keep growing and everyone can challenge themselves even further. Well... many of you may look back on your life, review your life plans, or consider changing jobs at the end of the year. KINTO Technologies is looking for new colleagues in various positions, so please feel free to apply if there is a job that you are interested in. We are also happy to conduct interviews. You can check job openings at KINTO Technologies here. That concludes my article "Let's Make a Human Resources Group." Since this is part of our Advent Calendar, let me finish by saying... Merry Christmas to you!
はじめに こんにちは、KINTO FACTORY のフロントエンド開発を担当しているきーゆのです。 今回は Visual Studio Code(以下 VS Code)で React プロジェクトをデバッグするための環境を構築した件について、まとめてみたいと思います。 これまで VS Code は優秀なメモ帳程度にしか使って来なかったので、結構苦戦しました(主に言語の壁)。 今まさに VS Code にデバッグの環境を構築しようとしている皆さん。 どうぞ私の屍を超えていってください。幸あれ。 環境情報 OS : macOS Sonoma 14.1.2 VS Code : ver1.85.1 Node.js : 18.15.0 terminal : zsh 構築手順 1. launch.json のセットアップ launch.json を追加して、デバッグ用の起動構成を構築していきます。 VS Code 左サイドメニューにある「実行とデバッグ」メニューを選択します。 選択後に表示される「launch.json ファイルを作成します」をクリックすると、 launch.json ファイルがプロジェクト内に作成されます。 :::message 初回作成時にデバッガーを選択することになりますが、今回は React なので Node.js を選択します。 ::: launch.json を作成します 作成直後の launch.json には、デフォルトの起動構成が追加されています。 自動生成直後はデフォルトの起動構成が追加されています 2. 新しい起動構成の追加 先ほど追加した launch.json に、デバッグ用の起動構成を追加します。 追加する起動構成は以下になります。 { "name": "[localhost]Chromeデバッグ", "type": "node-terminal", "request": "launch", "command": "npm run dev", "serverReadyAction": { "pattern": "started server on .+, url: (https?://.+)", "uriFormat": "%s", "action": "debugWithChrome" }, "sourceMaps":true, "trace": true, "sourceMapPathOverrides": { "webpack:///./*": "${webRoot}/src/*" } } 編集後の launch.json は以下画像のようになります。 私はデフォルトの起動構成を削除しましたが、残しておいても特に問題はありません。 また、起動時のコマンドを変えたい場合は command プロパティを編集したり、デバッグ時に複数のコマンドを実行したい場合は preLaunchTask プロパティでタスクを追加したりすることも可能です(この辺の話は今回は割愛します)。 name プロパティの値が起動構成の名前になります 3. デバッグ実行開始 あとは F5 キーを押すだけで、デバッガーが起動してくれます。 起動に成功すると中央上部にデバッグ用のボタンが表示されます 中央上部に表示されたデバッガーボタンや F キーを使って、ステップイン/ステップオーバー等を実行することができます。 困った時は 以下は私が実際に遭遇した"困った!"たちです。 運悪く遭遇してしまった方の一助になれば幸いです。 ◽ デバッグ時のターミナルが"sh-3.2$"で実行され、"npm command not found"になる VS Code を再起動することで解決します。 どうやら Microsoft によって自動的に VS Code が起動した場合に発生するようです。 私の環境の場合、PC 起動時 Microsoft365 にログインするのですが、ログイン成功時に VS Code も自動で起動するところでこの問題に遭遇しました。 ◽️ npm インストール済みにも関わらずデバッグを開始すると"npm command not found"になる .vscode/settings.json ファイルに以下を追記してください。 ファイル自体を作成していない場合は、作成しておきましょう。 { // npm scripts コマンドを実行するためのpath設定 "terminal.integrated.profiles.osx": { "zsh": { "path": "/bin/zsh", "args": ["-l", "-i"] } } } :::message ターミナルの実行環境が bash の場合は、プロパティ名と path を zsh → bash に変更してください ::: これによりデバッガーの実行ターミナルに path が通るようになり、npm コマンドを実行できるようになります。 まとめ 今回は VS Code で React プロジェクトをデバッグするための環境構築についてまとめてみました。 まだまだ VS Code で 遊べる 効率化できると思うので、次回は tasks.json でさらに効率化していければと思います。 これは私見ですが、開発時にデバッグできると QOL が爆上がりして開発生産性が向上します。 これによる副次的効果として、オフィス内での笑顔率向上や通勤時幸福度の向上が見込める...かもしれません。 皆さんのデバッグライフに幸あれ。 読んでいただき、ありがとうございました! 最後に、私の所属する KINTO FACTORY では一緒に働く仲間を募集しています。 ご興味があればぜひ下記の求人をチェックしてみてください! @ card @ card
自己紹介 はじめまして!モバイルアプリ開発Gにて my routeアプリ のAndroid側の開発を担当しておりますRomieです。 前職からAndroidの開発を始めて2年経ちましたが、個人開発も含めてレイアウトは全てxml形式で実装してきたためComposeにきちんと触れたのはお恥ずかしながら2023年12月にKINTOテクノロジーズ株式会社(以下KTC)に入社してからになります。 TechBlogに記事を書くのも初めてになります! こんな人に読んでほしい 本記事は以下が対象です。 Androidの開発超初心者の方 さまざまな事情でxml形式でしかレイアウトを書いたことがなくComposeを全くご存知ない方 実機で動作確認する際なかなか修正した箇所を表示できず困っている方 Previewとの出会い 入社して一番最初Composeのコの字も知らないということで、以下の画面をxml形式からComposeで実装し直す作業を行いました。 ![About my route画面](/assets/blog/authors/romie/2024-02-08-compose-preview-beginner/03.png =200x) About my route画面 早速コードリーディングを始めると、Previewを冠した謎の関数が。 @Preview @Composable private fun PreviewAccountCenter() { SampleAppTheme { AccountCenter() } } その関数はアカウントセンターのボタンのPreviewを表示するためのものでした。 ですがprivate関数にもかかわらず同じktファイル内のどの箇所でも呼び出されていなかったため「この関数は使用されていないのだろう」と考えPreview周辺のコードは触らずに実装を進めることにしました。 そして無事Compose化の作業を終え、pull requestを出したところcommentが来ました。 「Previewがないので作ってください!」 「呼び出されない関数を作って何の意味があるんだろう」と思いつつ他を真似して画面全体のPreviewを実装し、buildして実機が問題なく動いていることを確認しましたが、この時は実機ばかり見ていました。 Previewは何のためのものなのだとまだボンヤリしつつ何気なくAndroid StudioのSplit画面を見ると、そこには実機で表示されているのと全く同じ画面がAndroid Studioに表示されているではありませんか。 「Previewってそういうことか、関数を呼び出さなくてもSplit画面で表示するためのものなのか!」 公式ドキュメント ^1 にもきちんと書いてありました。 アプリをデバイスやエミュレータにデプロイする必要はありません。幅と高さの制限、フォントのスケーリング、テーマがそれぞれ異なる、特定のコンポーズ可能な関数を複数プレビューできます。アプリの開発を行うとプレビューが更新され、変更内容をすばやく確認できます。 Previewと動作確認 ある日、my routeアプリのルート詳細画面に駅出口からの進行方向セクション8方向の画像と文言を追加する作業を行いました。 実装自体はすぐにできましたが、問題は動作確認でした。8方向分の画像と文言が正しく追加できているかどうか確認するのに実際に動作させていると非常に時間がかかります。 再現手順は以下の通りです。 ![進行方向セクション表示の再現手順](/assets/blog/authors/romie/2024-02-08-compose-preview-beginner/01.gif =150x) 進行方向セクション表示の再現手順 では、効率的に8方向分全て確認するにはどうすればよいでしょうか。 更にUI表示が崩れていたり違った画像が表示されていたりして修正し最初から確認しようとすると、余計時間がかかります。 ここで活躍するのがPreviewです。 以下の通り実装します。 @Preview @Composable fun PreviewWalkRoute() { SampleAppTheme { Surface { WalkRoute( routeDetail = RouteDetail(), point = Point( pointNo = "0", distance = "200", direction = Point.Direction.FORWARD, ), ) } } } Buildを通しAndroid StudioでSplit画面を確認すると、ご覧の通り。 進行方向セクションのプレビュー画面 確認したい方向を入れるだけで正しい画像と文言が入っているか確認できるのです。 そして実際に表示が崩れているかは1パターンのみ確認できればOK!これで大幅に動作確認の時間を短縮できます。 まとめ これからもPreviewだけでなくComposeの諸々を体感することがもっと増えると思います。 とりあえず軽く触っただけでもこのような感動があったので共有しました! Compose超初心者の感動体験、これからもお付き合いのほどよろしくお願いいたします。
Introduction Nice to meet you! I am yama from the Quality Assurance Group. I joined KINTO Technologies in May 2023. I mainly do QA work for KINTO Unlimited . About Today's Theme Today, I will talk about the quality improvement initiative that I have been working on since I joined the company. The Quality Assurance Group and Its Future Prospects The figure above shows the future prospects of the Quality Assurance Group as given by our manager zume during a company announcement. It shows that QA was formed three years ago and the team is now stable, so what will we do next? I joined the company around that time. At the system development company where I previously worked, I worked in QA with the goal of contributing to quality improvement by providing high-quality tests, but as I gained experience, I encountered the difficulty of ensuring quality through testing. There is a limit to what you can do by just fixing bugs. You have to make sure that there are no bugs in the first place. I wanted to work somewhere I could do this, so I joined KINTO Technologies. Understanding the Current Situation After I joined the company, I first tried to understand the in-house development and QA processes and the current state of the project I was in charge of ( KINTO Unlimited ). I found out that waiting time in development was costly (My previous job also demanded speed, but not as much as KINTO Technologies). That was because KINTO and KINTO Technologies are businesses that create new services, and delays lead to lost business opportunities. It made sense. Therefore, when taking measures to improve quality, I felt that it was necessary to do something that did not slow down development. Also, I knew from my previous job that the manager could not handle everything for long, so I decided to do things as simply as possible. However, I realized that the current project lacked an objective indicator of current quality. If you can't see the quality of the products you’re making, you can't see what you need to improve so you don't make bugs, so we first made a system that would visually indicate quality. What We Did However, even though we went, "Let's do ___ to improve our quality!" it was hard to casually accept that. So, we first conducted a quality analysis in a slightly more in-depth manner through the development project, and fed the analysis results back to the project. At that time, the bug report did not have enough quality analysis items, so the QA team investigated the resolution history of each bug report, categorized them, and did a quality analysis. Analysis Materials (Excerpt) At the end of the project, we presented analysis results at a review session and said, "We can see the quality and current issues if we have these classification items in the bug report!" By narrowing down the classification items and simplifying the input methods, we also minimized the burden on the person in charge. Newly Added Classification Items Thankfully, this proposal was accepted, and we started using the bug report with the above quality classification items added. What I Want To Do in the Future Now that we have a starting point for quality improvement, we would like to give feedback regularly to the project of the information we get and consider quality issues that need to be solved together with development and QA. We also plan to continue these efforts to make the progress in quality (improvements) visible. We also aim to expand this trend horizontally to other projects and hope it will be widespread. We will continue with the belief that it will lead to the Quality Assurance Group's future prospects and overall quality improvement, which I mentioned at the beginning.