はじめに こんにちは。KINTOテクノロジーズでバックエンドを担当している森本です。 KINTO ONE開発グループに所属しています。Javaをメインに利用してKINTO ONEの開発をしています。今回は業務とは別に実施しているGraphQLの勉強会について紹介させていただきます。 GraphQLとは GraphQLとはクエリ言語の一つです。SQLのような他の言語とは異なり、特定のデータストアに限らず複数のデータソースとデータをやりとりすることができます。 バックエンド側でスキーマを定義しておけば、フロントエンド側はその定義に従って自由にオブジェクト内の項目を取得することができます。REST APIとは異なり、フロントエンドがバックエンドから返却してほしい情報を柔軟に指定できます。利用しない情報を無駄に取得する必要がなくなり、またネスト構造になっているオブジェクトを取得するために複数回APIを呼ぶ必要もなくなります。 勉強会の目的 勉強会の目的は以下の2つです。 技術力をアップしたい グループを超えて交流したい 技術力をアップしたい 普段の業務で利用している技術とは別に新しい情報をキャッチアップしたいが、ハードルが高いとメンバーそれぞれが感じていました。例えば言語の知識が足りないことが挙げられます。今回のGraphQLのチュートリアルではTypeScriptが利用されていました。そのため、GraphQLを学ぶ前にまずTypeScriptを学ぶ必要がありました。知識や経験が異なるもの同士で補うことで、このような課題をクリアして学習のコストを下げることができます。 グループを超えて交流したい 所属するグループ、チーム、プロジェクトがそれぞれ異なる年齢の近いメンバー同士が交流するきっかけにしたいという思いもありました。既にお互いを認知している仲の良いメンバー同士ではありますが、定期的に集まることでより仲良くなろうという魂胆です。また勉強会を通してお互いの持っている新たな一面を知ることができる機会になるとも考えました。 実施内容 なぜGraphQLか バックエンドでAPIを普段実装しているメンバーは、要件が発生するたびAPIを作成する必要があることに悩みを抱えていました。もちろんサーバー側で処理を行った方が実行速度が速いものはありますが、情報をそのまま返却するAPIを増やしていくのは面倒だと感じていました。そこでGraphQLが解決策として良さそうだという話を聞き、ぜひ試したいと常々思っていました。 また既にGraphQLを利用したことがあるメンバーもいましたが、全体の処理の流れを把握したいという思いがあり、GraphQLを学習することにしました。 勉強会に利用したチュートリアル GraphQLのライブラリにはApollo GraphQLを選び、チュートリアルは以下リンク先のものを使用しました。 GraphQL Tutorials 理由としては、チュートリアルの量が充実しており、入門編として適切だと感じたためです。また勉強会メンバー内に業務でApollo GraphQLを利用したことがある人がいたため、既に社内で実績があると判断したためです。 勉強会の概要 日時 週に1回、18時以降でメンバーの時間が揃うところで実施しました。 メンバー メンバーは25歳から28歳までの若手8人です。それぞれ所属が異なり、Webアプリのフロントエンド・バックエンド、またモバイルアプリのフロントエンド・バックエンドと多様な経験を持つメンバーが集まりました。 実施した範囲 チュートリアルのLift-off IからVまでの全5章を実施しました。GraphQLを実装するにあたり、基本的な事項が記載されています。 形式と工夫した点 勉強会では以下の流れで実施しました。 もくもく会でチュートリアル実施 チュートリアルの内容を発表形式でアウトプットして知識定着 初めはいわゆるもくもく会を実施しました。もくもく会とは、集まったメンバーがそれぞれ黙々と作業・学習をする勉強会のことを指します。 Apollo GraphQLを利用したことのあるメンバーもいると記載しましたが、それでも処理の全体像を把握しているメンバーはいませんでした。そのため、まずはそれぞれが同じチュートリアルを実施し、分からないことが出てきたタイミングで話し合いをして解決する、という流れで実施しました。 しかし本当に理解できているか怪しく、このまま先に進むよりはまずは復習するべき、との意見が出ました。そこで同じチュートリアルを持ち回りで発表し合うことにしました。 発表形式では、発表者はチュートリアルを一言一句理解する必要がありました。もくもく会ではなんとなく進んでいた箇所も、あらためて読み直すと答えが書いてあったということもありました。発表の際には質問ができたり、ソースコードを変更して試してみたり、1人では見つけられなかった新たな気づきもありました。 勉強会の様子。手前には勉強会用に用意したサンドイッチが写っています。 さいごに まずはGraphQLについて深く理解できました。それぞれが持っている知識・経験を使ってお互いを補い合うことで、1人よりも速く・確実に進められました。また学習を共にする仲間がいることで、挫折しそうなときも諦めずに続けられました。 今後はApollo GraphQLのチュートリアルの残っている章を進めたり、さらに新しい技術事項を取り上げて学んでいったりする予定です。 また、何らかのアプリケーションを作ってみたいという話も上がっています。それぞれがトライしてみたい言語やフレームワーク、アーキテクチャを利用してみて、さらに技術力向上に努めていきたいと思います。
About me I am Bharath from Global KINTO. I am a part of Global KINTO ID Platform team and product engagement team. I have also been supporting ad hoc projects like TechPortal and UserPool management screen. I am well versed in OIDC and AWS Cognito. Feel free to reach me anytime at bharath.tv@kinto-technologies.com if you would like to discuss these. I am always excited to talk about ID. About today's topic In order to create contact points with overseas group companies, cultivate the ability to play an active role in the global arena, and further evolve as a global technology company, KINTO Technologies (hereinafter referred to as KTC) participated in the global hackathon "toyotaswarm" hosted by Toyota Motor North America (hereinafter referred to as TMNA) on July 2022. Since I participated in this event from Japan remotely as well, today I'd like to share what we did for this event. Regarding TMNA “SWARM“ Hackathon The second year of this initiative was held over three days from July 27 to 29, 2022, with a total of 270 participants in 40 teams from Toyota Group companies around the world. There were 3 challenges announced this year. #1 Safety #2 Innovate to Carbon Neutral #3 Open Global Technology Innovation Since we thought digital identity is a major challenge and my team has been working on this topic so far, we were selected to participate in the hackathon this year. Idea from our team We at Global KINTO, are focused on making it all easy and simple for the end user to access our mobility services. Our Global KINTO App, aims to be the single app that connects all KINTO services. My team, Global KINTO ID aims to make it easy for end users to access any KINTO service across the globe by registering only once. So the problem we wanted to address in the hackathon also was a similar one. How an end user can share their information across services hassle-free. It doesn’t make sense when a user has to share their name and other details repeatedly to start using a different service. We decided to do a PoC (proof of concept) of a service that can share user information with other services upon consent from the user. About our team Due to visa restrictions, we only have 3 members who participated in the event at Dallas (HQ of TMNA in Texas, US) and the remaining members supported the activity from Japan remotely. Onsite team: Captain: Feng Xu (Group manager) PdM: Dai Sasaki FE: Alex Remote team: FE: Chris.L BE: Bharath During the hackathon We got all warmed up in a few days, and finalized our concept and how to achieve it. Finally, even though we only had 3 members onsite, that didn’t stop us from going forward. We brushed up the idea with the team and built 9 screens, 11 APIs, including many AWS components and over 1100 LoC (Lines of Code). The event started upbeat when the Olympic gold medal winner, Erin Jackson joined us in the event. This is the closest we would have ever gotten to a Olympic medal. The teams were given separate tables to start working on their concepts. It was nice to see all the hyped-up teams get into action. Meanwhile, my team had a tough time getting work done due to a few of us working offsite in a different time zone. It was quite clear what actions each of us should do but we have to integrate everything to come up with the working model. Typical problems like connecting our server to a different IP address or working with AWS CDN in a different region happened but overall things went well. As a result, we cleared round 1. Since only half the teams would pass round 2, it was stressful. We did our best but unfortunately that we didn't pass round 2. What impressed me here was the high degree of perfection of the other teams' ideas. As an example, the idea of prevention of drunken driving and the idea of reducing carbon emissions passed round 2. For both of these, they do not only have a software part, but they also had an embedded solution proposal which included both software and hardware. What we learned from participating TMNA senior management was around the hackathon the whole day, anyone can easily talk with them if they wanted. It was a great opportunity to get close to them and understand their thoughts directly. The people who came from other countries were very open and kind to connect with us. It was a perfect opportunity to make connections between several different organizations which hopefully creates some synergy. Since this event is going to be a regular event from now, we are looking forward to participating in it next time as well and bringing more value for Toyota and the whole world through this activity.
はじめに こんにちは、KINTOテクノロジーズでコーポレートエンジニアをしている及川です。 今回は、KINTOテクノロジーズのIT管理チーム(情シス)ではどのような環境やチームの活動基準で仕事をしていて、何を目指しているのかについて書きたいと思います。 目次 IT管理チームの業務内容や雰囲気 IT管理チームが挑戦したいこと IT管理チームが目指す姿 IT管理チームの業務内容や雰囲気 IT管理チームは幅広い社内業務システムを担当していて、ロケーション的にも遠隔の複数拠点(東京2拠点・名古屋・大阪)で業務をするため自律的に活動していく必要があります。 各拠点のエンジニアが非エンジニアリング業務に割く時間をカットして、エンジニアリングに集中できる時間や環境を提供するために、各拠点にIT管理チームのメンバーを配置しています。 チーム内で前提となる活動基準に合意して、同じ価値を目指して日々業務に取り組んでいるのですが、その活動基準をまとめると以下になります。 Mission ㅤㅤ従業員(社員、パートナー)を中心として、仲間を尊重し、利便性と ㅤㅤセキュリティーを追求して会社の発展に寄与する。 Value ㅤㅤ自律 ㅤㅤㅤㅤ目的、品質、プロセス ㅤㅤ協調 ㅤㅤㅤㅤ相互尊重、情報共有、スキルの平準化 ㅤㅤレスポンス ㅤㅤㅤㅤ従業員目線、素早さ、正確性 IT管理チームの立ち位置は、 生活基盤インフラ(電気・水)をイメージして、従業員から頼られる必然の存在を目指す=エンジニアがエンジニアリング以外に時間を取られない環境整備 ために、開発環境をITの側面から支えていくことがIT管理チームの大切な役割となります。 社内システム(SaaS)環境再構築、ゼロトラスト推進、自動化、効率化といった軸で業務を進めて従業員の利便性とセキュリティーを高めつつ、全体最適化を実現するためのソリューションやシステムを導入するプロジェクト業務を実施しています。 日常的なオペレーション業務も実施していて、サービスデスクやオンボーディング、アカウント作成/削除、PCキッティング、PC/スマートフォンやモニターなどのIT備品等の調達・管理といった業務があります。 プロジェクト業務とオペレーション業務の両輪を上手く回転させながらバランスを取りつつ、開発スピードを上げるために必要なことをチーム一丸となって実施しています。 IT管理チームが挑戦したいこと 一年間で従業員が100名以上増え、急成長しているフェーズのため、IT管理チームに求められるスキルセットも変化してきたことにより、上記で書いた、IT管理チームの活動基準、Valueに同意してプロジェクトとオペレーションの2つを実行できるコーポレートエンジニアにJoinしてもらいました。 今ではIT管理チーム内に5つのミニチームがあり、臨機応変かつスピード感のあるスキルを持ったメンバーで業務を推進しています。 なお急速に従業員が増え続ける状況でも、従業員が開発に集中できる環境を構築するため、システムの再設計、ルール構築や変更を実施しています。 KINTOテクノロジーズではWindows、Macの2種類からPCを選択可能ですし、社内システム(SaaS)についても多数活用しているという多種多様な環境です。 多様な環境下でも開発スピードを落とさないことを前提に、適切なシステム環境を実現するため、現状の設計を見直してスピード感を持って最適化を実施しています。 スピード感も必要ですが、環境変更に伴うメリットやデメリットをしっかり把握しチーム内レビューで効果を見極めた上で本番環境へリリースします。本番環境リリース後のレビューもしっかり行い、反省点など次回に活かすことも目的にチーム内プロセスも重要視しながらスキルやノウハウの平準化も進めています。 新しい価値や要望に応えるため現在の使い勝手や内容に満足せず、自分たちで能動的に要望やトレンドをキャッチアップしてシステムやルールをブラッシュアップしています。 IT管理チームが目指す姿 上記で書かせていただいた気持ちの根底は、IT管理チームのやりたいことだけを実現したいという気持ちでは無く、従業員の皆さんをサポートする・したいという気持ちを持ってそれをITで実現することです。 従業員の皆さんに近い運用(現場)にこそ改善のヒントがあり、最上級であるべきだと考えに基づいて、各ミニチームにも様々な運用を担当してもらっています。 その運用業務の中から出てくる改善要望を深堀して、設計やソリューションに当てはめて開発現場が集中できる環境を能動的に創出できるようなチームを目指しています。 私自身、コーポレートエンジニアキャリアの経験はほとんどありませんでしたが、ITで現場環境を改善することや技術には興味がありました。 つまり、バックヤードは様々でも、上記視点やマインドがあればコーポレートエンジニアのキャリアは築けると思います。 従業員に頼りにされる、オープンなIT管理チームを目指して業務に精励していきたいと思います。
Hello, My name is Shweta Oza. I joined KINTO in April 2022 at the Global Development Group. I am an application developer who has recently gained interest in DevOps. I am currently working in the Coupon Team. Our team develops and maintains Coupon APIs for customers/ merchants all over the world. There are many features that are being continuously developed and deployed. For any project it is important to maintain the databases, including data backup and rollback. This way, if anything goes wrong when releasing a new version, you can safely rollback the changes and restore the backup. As an open-source database migration tool, our company uses solutions such as Flyway. Flyway strongly favors simplicity and convention over configuration. It is based around just 7 basic commands: Migrate, Clean, Info, Validate, Undo, Baseline and Repair. Below are a few concepts and simplified procedures about how to back up the database manually using a script. Overview Coupon System is a tool that easily issues and manages coupons that can be used for KINTO services or other partner services. The coupon system is in continuous development to improve and upgrade with new functionalities. With every new change, it is necessary to manage the databases that the coupon system uses; which plays a key role in any software project. In our case, we use MySQL . In coupon, whenever we have any new release the steps we follow are: Get the functionalities developed. Test them locally and then on AWS test environments. Take a backup of the DB before release. Release the new functionalities. If any issues, roll back to the previous version of the release. In order to achieve these we have backup and rollback scripts to reduce the number of instructions to follow in CLI. This saves time and maintains consistency of structure for backups or rollbacks. Let's learn some basics What is a database backup? A database backup is a copy of data that is stored on a server. What is the purpose of a database backup? Backups are used to prevent unexpected data loss. In an event of failure, if the original data gets lost or corrupted, with the help of a backup, it is easy to restore the data again. What are the types of database backup? Physical backup ^1 Physical database backups are backups of physical files that are used to store and recover databases. These include different data files, control files, archived redo logs, and many more. Typically, physical backup data is kept in the cloud, offline storage, magnetic tape, or on a disc. There are two methods to perform a physical backup : Operating system utilities Recovery manager Advantages: Total control of the data. Storage cost is cheaper. Speedy process retrieving backed up data. Disadvantages: Stored data in any device can get corrupted making it hard to recover data. Data can be ruined by natural disasters. Loss of storage devices. Logical backup ^2 Logical backups contains logical data which is retrieved from the database. It contains a view, procedure, function, and table. This is useful when users want to restore or transfer a copy of the database to a different location. Logical backups are not as secure as physical backups in preventing data loss. It only provides structural details. Every week, complete logical backups should be performed. Logical backups are used as a supplement to a physical backup. Advantages: Useful when the user needs to restore the complete database to a previous instance. It is more complex and provides granular recovery capabilities. Disadvantages: Critical for recovery of special components. Less secure compared to physical backup. It only provides structural details. We are going to see all about Logical Database backups in both local and in AWS CLI. Because it is an important step with reference to a developers' perspective to operate with data in DB. To manage Amazon RDS(Relational database service) using AWS CLI Amazon RDS [^3] [^3]: https://aws.amazon.com/rds/ It is a service that can safely use RDB on the cloud without setting up a server. Amazon RDS supports MySQL, MariaDB, PostgreSQL which are open source and widely used, Oracle Database and microsoft SQL server. Pay for only what you use. Amazon CLI [^4] [^4]: https://aws.amazon.com/cli/ The AWS CLI is an integrated tool for managing AWS services that you run from a terminal on Linux or macOS or a command prompt on Windows. Basically, commands are entered and executed from a dedicated tool, but they can also be used to automate processes by writing them in scripts. One thing to keep in mind when managing Amazon RDS from the AWS CLI is that executing a command may not always be reflected immediately. This is because Amazon RDS has a number of failure countermeasures, and it takes time for the settings to be reflected in all of them. Same we will be writing simple scripts for executing tasks of Backups and Rollbacks easily on different environments. How exactly can we take Backups and Rollback DB from CLI at developer's level? Using mysqldump in SQL Format What is mysqldump [^5] [^5]: https://dev.mysql.com/doc/refman/8.0/en/mysqldump.html The mysqldump client is a logical backup program originally written by Igor Romanenko. Database and is usually in the form of a list of SQL statements ("SQL dump") for backup or transfer to another database server (not necessarily MariaDB or MySQL). The dump typically contains SQL statements to create the table, populate it, or both. Using a dump file What is a dump file [^6] [^6]: https://dev.mysql.com/doc/refman/8.0/en/mysqldump.html A dump file is also useful when you upgrade from one release to the next. You can dump a database using an older release and load it under a new release as part of the upgrade process. QUICK HIGHLIGHTS To take DB Dump with data (i.e. both DDL & DML) mysqldump -u root -p DB_Name > backup.sql # AWS CLI mysqldump -u $user -p$password -h $rds_endpoint > backup.dump To take DB Dump without data (i.e. only DDL) mysqldump -d -u root -p DB_Name > backup.sql # AWS CLI mysqldump -d -u $user -p$password -h $rds_endpoint > backup.dump To Roll Back to previous version mysql -u root -p DB_Name < backup.dump # AWS CLI mysql -u $user -p$password -h $rds_endpoint < backup.dump SCRIPT for Backup Things you will need to edit in script before using directly: pathToParameterStoreKeyValueMYSQL_USER pathToParameterStoreKeyValueMYSQL_PASSWORD pathToParameterStoreKeyValueRDS_ENDPOINT DB_Name ######################################################################### ######################################################################### ##### ##### Description: Shell Script to take backup of existing data AWS ##### 1. Choose the env and switch the db connection ##### 2. Create backup directory with today's date ##### 3. Create dump file to backup folder with current db ##### 4. Check dump file size ##### ######################################################################### ######################################################################### read -p "Enter Your Env(env): " x echo "Welcome to ${x} Env!" # MySQL server credentials MYSQL_USER=$(aws ssm get-parameter --with-decryption --name /${x}/pathToParameterStoreKeyValueMYSQL_USER | jq -r .Parameter.Value) MYSQL_PASSWORD=$(aws ssm get-parameter --with-decryption --name /${x}/pathToParameterStoreKeyValueMYSQL_PASSWORD | jq -r .Parameter.Value) RDS_ENDPOINT=$(aws ssm get-parameter --with-decryption --name /${x}/pathToParameterStoreKeyValueRDS_ENDPOINT | jq -r .Parameter.Value) # Set the folder name with date format(eg: 2022-08-15) DATE_FORMAT=$(date +"%Y-%m-%d") TIMESTAMP=$(date +%H%M%s) # Path to local backup directory BACKUP_DIR="tmp/release/backup/${x}/dbbackup/${DATE_FORMAT}" # Use database's names DB="DB_Name" echo "########################################DATABASE########################################" echo "Using Database (DB_Name)" echo "########################################DATABASE########################################" # Create backup directory with today's date mkdir -p ${BACKUP_DIR} FILENAME_PREFIX="backup_${x}_DDL_DML_${TIMESTAMP}_" FILENAME_POSTFIX=".dump" read -p "Enter version eg: v0.0.1: " d FILENAME=$FILENAME_PREFIX${d}$FILENAME_POSTFIX echo "########################################FILEPATH########################################" echo "Created directory" ${BACKUP_DIR} echo "File will be saved as ${FILENAME} " mysqldump -u $MYSQL_USER -p$MYSQL_PASSWORD -h $RDS_ENDPOINT --databases $DB > ${BACKUP_DIR}/${FILENAME} echo "File saved at ${BACKUP_DIR}/${FILENAME}" echo "########################################FILESPATH########################################" # check File size file=${BACKUP_DIR}/${FILENAME} filesize=$(ls -lh $file ) echo "########################################FILESIZE########################################" echo "$file has a size of $filesize" echo "########################################FILESIZE########################################" Procedure for taking backup Place the shell script in AWS CLI Login to AWS Click AWS management console → AWS Systems Manager → Session Manager→ Click Start session Enter the env you want to take backup eg: {env}-{project_name}-{maintenance_server_name} Start session Check if dbbackupDDL_DML.sh file is present If file is not present place dbbackupDDL_DML.sh file in AWS CLI To execute file sh-4.2$ ls dbbackupDDL_DML.sh mysql.sh tmp sh-4.2$ sh dbbackupDDL_DML.sh Enter Your Env(env): env Welcome to env Env! ########################################DATABASE######################################## Using Database (db_name) ########################################DATABASE######################################## Enter version eg: v0.0.1: v0.0.1 ########################################FILEPATH######################################## Created directory tmp/release/backup/env/dbbackup/2022-08-12 File will be saved as backup_env_DDL_DML_06311660285870_v0.0.1.dump mysqldump: [Warning] Using a password on the command line interface can be insecure. Warning: A partial dump from a server that has GTIDs will by default include the GTIDs of all transactions, even those that changed suppressed parts of the database. If you don't want to restore GTIDs, pass --set-gtid-purged=OFF. To make a complete dump, pass --all-databases --triggers --routines --events. File saved at tmp/release/backup/env/dbbackup/2022-08-12/backup_env_DDL_DML_06311660285870_v0.0.1.dump ########################################FILESPATH######################################## #######################################FILESIZE######################################## tmp/release/backup/env/dbbackup/2022-08-12/backup_env_DDL_DML_06311660285870_v0.0.1.dump has a size of -rw-r--r-- 1 ssm-user ssm-user 1.7M Aug 12 06:31 tmp/release/backup/env/dbbackup/2022-08-12/backup_env_DDL_DML_06311660285870_v0.0.1.dump ########################################FILESIZE######################################## sh-4.2$ To check the content of dump file sh-4.2$ less tmp/release/backup/env/dbbackup/2022-08-12/backup_env_DDL_DML_06311660285870_v0.0.1.dump sh-4.2$ Backup will be taken into folder with tmp/release/backup/env/dbbackup/{currentDate}/{FileNameWithTimestamp&Version} If you want to take dump multiple times in a day in individual date folder you will have the file with timestamp. sh-4.2$ cd tmp/release/backup/env/dbbackup/ sh-4.2$ ls 2022-08-09 2022-08-10 2022-08-12 sh-4.2$ cd tmp/release/backup/env/dbbackup/2022-08-12/ sh-4.2$ ls backup_env_DDL_DML_06311660285870_v0.0.1.dump backup_env_DDL_DML_06371660286257_v0.0.1.dump sh-4.2$ SCRIPT for Rollback Things you will need to edit in script before using directly: pathToParameterStoreKeyValueMYSQL_USER pathToParameterStoreKeyValueMYSQL_PASSWORD pathToParameterStoreKeyValueRDS_ENDPOINT DB_Name ######################################################################### ######################################################################### ##### ##### Description: Shell Script to rollback to target SQL ##### 1. Choose the env and switch the db connection ##### 2. Create rollback directory with today's date ##### 3. Choose and input the backup file ##### 4. Input the version of dump file ##### 5. Copy backup dump file to ROLLBACK folder ##### 6. Create dump file to ROLLBACK folder with current db ##### 7. Rollback db with backup dump file ##### 8. Comparison.... ##### ######################################################################### ######################################################################### read -p "Enter Your Env(env): " x echo "Welcome to ${x} Env!" # MySQL server credentials MYSQL_USER=$(aws ssm get-parameter --with-decryption --name /${x}/pathToParameterStoreKeyValueMYSQL_USER | jq -r .Parameter.Value) MYSQL_PASSWORD=$(aws ssm get-parameter --with-decryption --name /${x}/pathToParameterStoreKeyValueMYSQL_PASSWORD | jq -r .Parameter.Value) RDS_ENDPOINT=$(aws ssm get-parameter --with-decryption --name /${x}/pathToParameterStoreKeyValueRDS_ENDPOINT | jq -r .Parameter.Value) # Set the folder name with date format(eg: 2022-08-15) DATE_FORMAT=$(date +"%Y-%m-%d") TIMESTAMP=$(date +%H%M%s) # Path to local rollback directory history ROLLBACK_DIR="tmp/release/rollback/${x}/dbRollback/${DATE_FORMAT}" # Use database's names DB="DB_Name" echo "########################################DATABASE########################################" echo "Using Database (DB_Name)" echo "########################################DATABASE########################################" # Create rollback directory with today's date mkdir -p ${ROLLBACK_DIR} read -p "Enter full dumpFile Path to which you want to rollback: " df echo "dumpFile ${df} selected!" FILENAME_ROLLBACK_PREFIX="rollback_${x}_DDL_DML_${TIMESTAMP}_" FILENAME_BACKUP_PREFIX="backup_${x}_DDL_DML_${TIMESTAMP}_" FILENAME_POSTFIX=".dump" read -p "Enter version eg: v0.0.1: " d FILENAME_ROLLBACK=FILENAME_ROLLBACK_PREFIX${d}$FILENAME_POSTFIX FILENAME_BACKUP=FILENAME_BACKUP_PREFIX${d}$FILENAME_POSTFIX echo "########################################FILEPATH########################################" echo "Created directory" ${ROLLBACK_DIR} # copy dump file to backup folder cp ${df} ${ROLLBACK_DIR}/${FILENAME_ROLLBACK} ROLLBACK_FILE=${ROLLBACK_DIR}/${FILENAME_ROLLBACK} BEFORE_ROLLBACK_DUMP=${ROLLBACK_DIR}/"BeforeRollback_${FILENAME_BACKUP}" AFTER_ROLLBACK_DUMP=${ROLLBACK_DIR}/"AfterRollback_${FILENAME_BACKUP}" mysqldump -u $MYSQL_USER -p$MYSQL_PASSWORD -h $RDS_ENDPOINT --databases $DB > ${BEFORE_ROLLBACK_DUMP} echo "Dump Before Rollback ${BEFORE_ROLLBACK_DUMP}" echo "Rollback to DDL_DML of sql file located at ${ROLLBACK_FILE} " mysql -u $MYSQL_USER -p$MYSQL_PASSWORD -h $RDS_ENDPOINT --databases $DB < ${ROLLBACK_FILE} echo "Rollback successfully done with ${ROLLBACK_FILE}" mysqldump -u $MYSQL_USER -p$MYSQL_PASSWORD -h $RDS_ENDPOINT --databases $DB > ${AFTER_ROLLBACK_DUMP} echo "Dump After Rollback ${AFTER_ROLLBACK_DUMP}" echo "########################################FILESPATH########################################" # check File size before Rollback fileBeforeRollback=${ROLLBACK_DIR}/${BEFORE_ROLLBACK_DUMP} filesizeBeforeRollback=$(ls -lh fileBeforeRollback ) echo "########################################FILESIZE BEFORE ROLLBACK########################################" echo "$fileBeforeRollback has a size of $filesizeBeforeRollback" echo "########################################FILESIZE BEFORE ROLLBACK########################################" # check File size after Rollback fileAfterRollback=${ROLLBACK_DIR}/${AFTER_ROLLBACK_DUMP} filesizeAfterRollback=$(ls -lh fileAfterRollback ) echo "########################################FILESIZE AFTER ROLLBACK########################################" echo "$fileAfterRollback has a size of $filesizeAfterRollback" echo "########################################FILESIZE AFTER ROLLBACK########################################" Footer Procedure for Rollback Place the shell script in AWS CLI Login to AWS Click AWS management console → AWS Systems Manager → Session Manager→ Click Start session Enter the env you want to take rollback eg: {env}-{project_name}-{maintenance_server_name} Start session Check if dbRollbackDDL_DML.sh file is present If file is not present place dbRollbackDDL_DML.sh file in AWS CLI To execute file sh-4.2$ ls dbRollbackDDL_DML.sh mysql.sh tmp sh-4.2$ sh dbRollbackDDL_DML.sh Enter Your Env: env Welcome to env Env! ########################################DATABASE######################################## Using Database (DB_Name) ########################################DATABASE######################################## Enter full dumpFile Path to which you want to rollback: tmp/release/backup/env/dbbackup/2022-08-12 Enter version eg: v0.0.1: v0.0.1 ########################################FILEPATH######################################## Created directory tmp/release/rollback/env/dbRollback/2022-08-13 Dump Before Rollback tmp/release/rollback/env/dbRollback/2022-08-13/BeforeRollback_backup_env_DDL_DML_06311660285870_2022-08-13.dump Rollback to DDL_DML of sql file located at tmp/release/rollback/env/dbRollback/2022-08-13/backup_env_DDL_DML_06311660285870_2022-08-13.dump Rollback successfully done with tmp/release/rollback/env/dbRollback/2022-08-13/backup_env_DDL_DML_06311660285870_2022-08-13.dump Dump After Rollback tmp/release/rollback/env/dbRollback/2022-08-13/AfterRollback_backup_env_DDL_DML_063116602859099_2022-08-13.dump ########################################FILESPATH######################################## ########################################FILESIZE BEFORE ROLLBACK######################################## tmp/release/rollback/env/dbRollback/2022-08-13/BeforeRollback_backup_env_DDL_DML_06311660285870_2022-08-13.dump has a size of -rw-r--r-- 1 ssm-user ssm-user 1.7M Aug 13 06:31 tmp/release/rollback/env/dbRollback/2022-08-13/BeforeRollback_backup_env_DDL_DML_06311660285870_2022-08-13.dump ########################################FILESIZE BEFORE ROLLBACK######################################## ########################################FILESIZE AFTER ROLLBACK######################################## tmp/release/rollback/env/dbRollback/2022-08-13/AfterRollback_backup_env_DDL_DML_063116602859099_2022-08-13.dump has a size of -rw-r--r-- 1 ssm-user ssm-user 1.6M Aug 13 06:31 tmp/release/rollback/env/dbRollback/2022-08-13/AfterRollback_backup_env_DDL_DML_063116602859099_2022-08-13.dump ########################################FILESIZE AFTER ROLLBACK######################################## sh-4.2$ How can you use this in your daily work? You can take quick backups by using script or commands like above to avoid multiple inputs, references to many documents and links for login etc. This will save your time and errors while writing commands by hand. This will provide you systematic and clutter-free folder structure which you can refer whenever you need one. You will need this procedure in case your DB is upgraded, there is data inconsistency, in case you need to release the next version of your system or rollback the data when there is a failure.
こんにちは こんにちは、KINTOテクノロジーズ CIO室でアシスタントをしてます村山です! 本記事ではオフィスや社員のデスク環境をゆるりとご紹介します( ˘ω˘ ) オフィス紹介 まずはオフィス紹介から。 本社の名古屋オフィスです。 社長小寺さんの強い想いが込められていて、 『ナチュラル』『明るさ』を前面に押し出した内装となっています。 また、小寺さんこだわりポイントの右下の焚火は時間によっては炎がつきます。 オフィスの中心位置にあり、ランチ時にはみんなが集まります! 2つめの拠点は室町オフィスです。 室町オフィスには2フロアあり、東京ジャンクションも併設されています。 撮影にも利用されており、おしゃれなスポットですね! コレド室町2の中に入っているので近くにお店が多くて助かります。 食べたいと思ったもの、なんでもありますヨ。 3つめの拠点は神保町オフィスです。 大きい会議室にプラットフォームGのみなさんがいたので撮らせていただきました。 神保町オフィスは会議室が一番多く人気です。 あと、神保町周辺はランチの価格が安いのもいいですよね おいしいカレーやさんがたくさん!行く度カレーをたべてます🍛 写真右下は神保町オフィス限定KINTOテクノロジーズロゴ入りの自動販売機です。 4つめの拠点はOsaka Tech Labです。 大阪オフィスではないです。Osaka Tech Labです!(ここ重要) 4月に開設されたばかりでまだ社員の少ない拠点ですが みんなで意見を出し合いTech Labをアップデートし続けています。 写真右下の屋上が開放的で人気です。 心斎橋もランチが安い!粉ものおいしい! 関東出身なので、お好み焼き定食は慣れません・・ デスク環境の紹介 つぎはデスク環境の紹介をします。 仕事のしやすい環境というのは人それぞれで、 無駄がなく機能的であることは”環境が整っている”ことになると思いますが、 それだけじゃないですよね。 という前置きをしておいて・・っと! わたしのデスクです。 大量の応援部隊です。なんと整った環境でしょう・・! 副社長景山さんのデスクにもいます。 たまに一匹転がってたりして、あれ、一匹いなくなった!と探している姿にほっこりします。 みなさんもシルバニアを手にしていただけるとわかると思いますが、母性がわきます。 あれ、気が付いたらシルバニアの布教ブログになってしまいそうなのでここら辺で次のデスクにいきますか。  キーボードもかっこいい! PCとガンプラを作成するのが好きとのこと。良い趣味! 自宅のデスク環境ではたくさんのガンプラに見守られているようですが、 オフィスにも小さいのを連れてきたようです。 シルバニアひとつあげたら仲間が増えてました。いまではシルバニア布教部の一員です!  こんなこと言ってますが仕事できるひとです。 KINTOテクノロジーズのコミュニティにe-sports部があり、先日もみんなでスプラトゥーンをしました。 IT系の会社なだけあってか、ゲーム好きが多いです。 最近の社内ブームは麻雀です!  わかる。 各オフィスを簡単に行き来できたらいいのにな~とか夢みたいなことはおいといて・・ 用があれば他オフィスにも出張させていただいてますが、 各オフィスそれぞれの良さがあって、どのオフィスで仕事するのもすきです!  書籍が好きな方も多く、弊社の貸出書籍もたくさんありますが 社員同士で書籍の貸し借りをしているのもよく見られます。 あと、KINTOテクノロジーズにきてからSlackというツールを知ったのですが、 かわいい絵文字がたくさんでゆるーいかんじでコミュニケーションとれて 素晴らしいツールですよね・・!  トリプルディスプレイ、エンジニアっぽい!! 機能的でありつつささやかな癒しもありでステキ環境ですね ここまでくればシルバニアは万人受けということがわかっていただけたでしょうか。 さいごに リモートワークが流行ってるご時世ですが、 出社して好きな環境でみんなとコミュニケーションとりつつ仕事するのが いちばんだなーってわたしは思います( ˘ω˘ ) その上で髪色や服装、デスク環境も自由にさせてもらえて 働きやすい環境で仕事できるので楽しいです! それでは、ここまで読んでいただきありがとうございました!
はじめに こんにちは。共通サービス開発グループで複数のサービスが利用する決済プラットフォームの開発チーム[^1][^2]に所属している鳥居です。 この記事をご覧の皆さんは、ローカル開発環境の構築時にこんな経験ありませんか?IDE の設定を環境構築手順書に従って構築したが動かなかったり、プロダクト毎に異なるバージョンの SDK をどちらも動く方法を調べてインストールしたりなど、経験ある方は多いのではないかと思います。 ローカル開発環境構築は、新たに入ってきたプロダクト開発メンバーに襲いかかる、面倒な作業のひとつです。 本記事では Visual Studio Code (以下 VS Code)の Dev Container で開発環境の構築と簡略化・共通化をした事例を紹介します。 VS Code の Dev Container とは Dev Container 環境とは VS Code の拡張機能 Dev Containers を使って構築する開発環境で VS Code と Docker が使うことが出来れば利用可能です。(Windows / Mac / Linux) 次の図のように、Dev Container を立ち上げると VS Code から Docker コンテナをフル機能の開発環境として使用できます。 ホストマシンからソースコードをマウントし、VS Code の拡張機能は コンテナ内にインストールされます。 そのためすでに入っているライブラリとの競合の考慮の必要がありません。 例えばホストマシンで開発をする場合、別のプロジェクトの node.js の指定バージョンが違う場合は n package で node.js のバージョン管理、状況に応じてバージョンの切り替えが必要となるかもしれません。 Developing inside a Container より引用 Dev Container 環境の VS Code の設定や拡張機能、インストールするライブラリや起動するサービスなどは devcontainer.json 、 Dockerfile 、 docker-compose.yml に定義することが出来ます。 Dev Container 環境をビルドする際はこれらの設定を利用して自動的に行わるので、別個にインストールするのに比べ大幅に手順が簡略化されます。 環境構築手順書も Dev Container のビルドまでの手順のみとなります。複数人で開発する際の OS の差異の考慮も不要です。 Dev Container 環境構築手順 今回は Create React App で新しい React アプリケーションの構築を目指します。 今回使用した環境 PC: Surface Laptop4 Windows10: 21H2 (WSL2) WSL2: Ubuntu 20.04 VS Code: 1.73.0 前提 VS Code のインストール Docker のインストール (Windows の場合は WSL2 上にインストール) Docker Desktop for Windows/Mac のインストール 1. 拡張機能 - Dev Containers のインストール まずは Workspace としたいディレクトリで VS Code を開きます。 そして VS Code の拡張機能 Dev Containers をインストールします。 Dev Containers が含まれている Remote Development でも OK です。 2. Reopen in Container でセットアップ開始 左下のアイコンをクリックし、画面上部のメニューから Reopen in Container を選択 (Create Dev Container は別のディレクトリに Dev Container を構築してしまいます) 3. 作成したい Dev Container 環境の設定を選択 今回は Ubuntu を選択しました。 4. Docker コンテナイメージのバージョンを選択 5. 追加する機能を選択 今回は React アプリケーションの作成に必要な Node.js (via nvm) and yarn を選択しました。 追加する機能を選択し 🆗 をクリックすると、Dev Container のビルドが開始されます。 6. Dev Container のビルド完了 ビルドが完了すると、自動で Dev Container に接続された VS Code が起動します。 .devcontainer/devcontainer.json というファイルが生成されており、左下の表記が Dev Container となっていることがわかります。 (以前は .devcontainer/Dockerfile も生成されていました。) VS Code 内のターミナルを開き( ctrl + shift + @ )、次のコマンドを実行すると必要なライブラリがインストールされていることがわかります。 $ node -v v18.12.1 $ yarn -v 1.22.19 次に Create React App を実行します。 $ npx create-react-app typesript プロンプトが表示され、このまま構築できることがわかります。 (React アプリケーションの構築と起動確認は本題からそれるため割愛します。) いかがでしょうか。手動で node.js yarn をインストールして create-react-app をしたことがある方には如何に簡略されているかおわかりいただけるかと思います。 次は今回構築した Dev Container 環境をビルドしてみましょう。 既存の Dev Container 環境のビルド 前提 前提条件は環境構築手順と同様とします。 VS Code のインストール Docker のインストール (Windows の場合は WSL2 上にインストール) Docker Desktop for Windows/Mac のインストール 1. Dev Container の Workspace をホストマシンで開く まずはホストマシンの VS Code で先程作成した Workspace を開きます。 * 拡張機能 Dev Containers がインストールされていない場合 拡張機能 Dev Containers がインストールされていない場合は、次の図のように拡張機能のインストールを推奨されるので、表示に従いインストールしてください。 2. Reopen in Container を選択 拡張機能 Dev Containers をインストール済みの場合は、次の図のように Dev Container の設定ファイルがあるので Dev Container で開き直すかを聞かれます。 Reopen in Container を選択すると Dev Container のビルドが開始されます。 2. 環境構築完了! 構築済みの Dev Container 環境をビルドする際は以上だけです。 2 回目以降の起動も同様に Reopen in Container の選択で開くことが出来ます。Windows の場合は次の図のようにタスクバーの VS Code コンテキストメニュー から直接開くことも出来ます。 React アプリケーションの起動を構築完了とする場合は以下のようなコマンドが必要ですが、そちらについての設定は後述いたします。 // node.js packageのインストール $ yarn install このように構築済みの環境構築は非常に少ない手順で構築出来ます。 devcontainer.json のサンプル 次に私の Dev Container の設定の例を紹介します。 VS Code の設定や拡張機能は linter や formatter などを定義しています。 その他開発支援の機能も設定していますので興味がありましたら調べてみてください。 { // Dev Container の名称設定 "name": "test-devcontainer", // コンテナ立ち上げ時のオプションの指定 // --name ビルドするDocker containerの名称の指定 // これを指定しないとランダムに生成される "runArgs": ["--name=test-devcontainer"], // Docker コンテナイメージ "image": "mcr.microsoft.com/devcontainers/base:jammy", // VS Codeの設定 "settings": { "stylelint.validate": ["css", "scss"], "scss.validate": false, "css.validate": false, "editor.formatOnSave": true, "editor.defaultFormatter": "esbenp.prettier-vscode", "editor.codeActionsOnSave": { "source.fixAll.eslint": true, "source.fixAll.stylelint": true }, // sticky scrollの設定 "editor.stickyScroll.enabled": true, "editor.stickyScroll.maxLineCount": 5, "workbench.colorCustomizations": { "editorStickyScroll.background": "#00708D", "editorStickyScrollHover.background": "#59A2B5" }, // typescript で絶対パスで import する設定 "typescript.preferences.importModuleSpecifier": "non-relative" }, // 追加する VS Code の拡張機能 "extensions": [ "ms-vscode.live-server", "dbaeumer.vscode-eslint", "stylelint.vscode-stylelint", "Syler.sass-indented", "esbenp.prettier-vscode", "ms-python.python", "streetsidesoftware.code-spell-checker", "naumovs.color-highlight", "burkeholland.simple-react-snippets", "formulahendry.auto-rename-tag", "MariusAlchimavicius.json-to-ts", "dsznajder.es7-react-js-snippets", "styled-components.vscode-styled-components", "Gruntfuggly.todo-tree", "42Crunch.vscode-openapi", "mhutchie.git-graph" ], // コンテナ内の実行ユーザーの設定 (推奨設定) "remoteUser": "vscode", // インストールするライブラリ "features": { "ghcr.io/devcontainers/features/node:1": { "version": "lts" }, "ghcr.io/devcontainers/features/python:1": { "version": "3.9" } }, // Dev Container 作成時に実行するコマンド "postCreateCommand": "sh .devcontainer/post-create.sh" } 先程の構築済み環境で別途 yarn install を実行しなかった件は、 postCreateCommand オプションに定義することで環境構築時に自動で実行できます。 私の場合は別途スクリプトを作成しコンテナ構築後に実行する必要がある一連のコマンドを実行しています。 .devcontainer/post-create.sh #!/usr/bin/env sh # bashのgitコマンド補完の追加 echo 'source /usr/share/bash-completion/completions/git' >> ~/.bashrc # yarn global のpathを通す設定 echo 'export PATH="$HOME/.yarn/bin:$PATH"' >> ~/.bashrc # openapi-generator-cliのインストール yarn global add @openapitools/openapi-generator-cli yarn install 導入してみて感じたメリット・デメリット Dev Container 環境で開発をしていて感じたメリット・デメリットを記載します。 メリット 環境構築の手順が単純化出来る 複数人で開発する際にホストマシンの OS を気にしなくて良い 各種ライブラリ、パッケージの手順が不要 バックエンドの場合 mysql や redis を利用することが多いのでより強力 環境構築を宣言的に定義して、共通化出来るので個人によるばらつきが出ない VS Code の設定や拡張機能も定義できる バージョン違いによる思わぬバグの遭遇が起きない ローカル環境を汚さない、別 Workspace との競合が発生しない デメリット VS Code 以外の IDE を使いたい人の要望に応えられない 好みのターミナルが使えない そこそこのマシンスペックが要求される Zoom の画面共有しながらだと厳しい Windows は WSL2 がなんだかんだ重い Mac はファイル I/O に問題を抱えている devcontainer.json の Named Volume の設定で解決できるようです[^3] さいごに いかがでしたでしょうか。Dev Container の便利さが少しでも伝わっていれば幸いです。 私自身、過去には長い環境構築手順書に沿って環境構築をしていた記憶もあり非常に楽な時代になったなと感じています。 また、実際にモブプロに別プロダクトを担当しているメンバーをゲストに迎えた際もすぐに実装に取り組んでもらうことが出来たので Dev Container の利便性を実感しました。 今後の試行錯誤としては以下のようなことを実践してみたいと考えています。 公式記事を参考に Dev Container のパフォーマンスチューニング Amazon EC2 上に Docker を入れて Remote Development using SSH 経由で Dev Container を利用してみたい 応用で IntelliJ IDEA の Remote Development での開発 GitHub Codespaces 上での環境構築 [^1]: 決済プラットフォームの他の取り組みその1、[ グローバル展開も視野に入れた決済プラットフォームにドメイン駆動設計(DDD)を取り入れた ] [^2]: 決済プラットフォームの他の取り組みその2、[ 入社 1 年未満メンバーだけのチームによる新システム開発をリモートモブプログラミングで成功させた話 ] [^3]: 公式記事 Improve container performance
自己紹介・記事要約 my route開発Gのグループマネージャーをやっております、岩元です。 KINTO Technologies(以下KTC)の前身であるKINTO開発部立ち上げ時から在籍している最古参開発メンバーでもあります。 今回は、私が担当しているmy route開発Gの紹介をさせていただきます。 my routeとは my route by KINTO (以下my route)とは何かということを一言で言うなら、 トヨタグループのMaaSアプリ です。 MaaSとはモビリティ・アズ・ア・サービスの略であり、直訳すれば「サービスとしてのモビリティ」という意味になります。 「さまざまな形態の輸送サービスを統合した、オンデマンドでアクセス可能な単一のモビリティサービスとしており、MaaSオペレーターは利用者の要求を満たすべく、公共交通、ライドシェア、カーシェア、自動車シェア、タクシー、レンタカーなどさまざまな交通手段のメニューを用意すること」 ※MaaSアライアンスの定義より 簡単に言うなら、「ICTを活用してマイカー以外の移動をシームレスにつなぐ」ということでしょうか。 トヨタグループでは2018年の福岡での実証実験を皮切りに順次エリアを拡大してきています。 そのためのモバイルアプリとして生まれたものがmy routeであり、実証実験開始当時はトヨタ自動車で開始されたプロジェクトです。現在はKINTO親会社のトヨタファイナンシャルサービス社が主管として運営されており、KTCが開発・運用を行なっています。 これまでとこれからと 内製開発化とリニューアル 2018年のmy routeの実証実験開始時はまだKTCどころかKINTOも存在していませんでした。そのため開発・運用は全て委託先パートナー企業に外注されていました。 そのため、KTCが開発携わるようになって最初に取り組んだ事は、なにはともあれ内製化を進めることでした。また、元々が実証実験用に作られたものから建て増しを繰り返して発展してきたという経緯もあり内部構造的にも疲労限界を迎えていたため、アーキテクチャの刷新も喫緊の課題となっていました。 その第一弾として、まずはAPIを提供するバックエンド部分のリニューアルが行われました。これはマイクロサービスアーキテクチャの導入、開発言語の変更、プラットフォームのAWS化などを含む大規模なものであり、委託先パートナー企業からの巻き取りも同時に進めていたためかなり困難なプロジェクトでしたが、2021年の8月に無事リリースを完了させることができました。このリニューアルにより従来の数倍の速度での地域展開が可能となり、現在に至るまで無事に運用を行い、ほぼ完全に開発の内製化も実現できました。 現在進行中プロジェクト そして現在は、バックエンドリニューアル完了から間も無く開始された新プロジェクトを2023年1月のリリースに向けて絶賛開発中です。 詳細についてはまだオープンにはできないのですが、my route開発エンジニア総勢20名ほどが関わった一大プロジェクトになります。 基盤となるアーキテクチャも大幅に刷新されるため非常に大変なプロジェクトでしたが、完成の見えてきた今、プロジェクトメンバー一丸となって開発にあたっています。 my route開発グループについて my route開発グループはmy routeサービスに紐づいたグループですので、最大のミッションは my routeサービスの成長・発展に寄与すること に尽きます。 そのため、事業とワンチームという意識を持って開発・運用を行う人たちが集まっています。 グループ内は主に以下の3チームに分かれています。 バックエンド開発チーム 運用チーム モバイルアプリ開発チーム 現在はモバイルアプリ開発チームは別グループとして独立してしまいましたが、引き続きmy route開発チームの一員として活動しています。 先述のリニューアル関係の開発だけではなく、現行アプリに関しても、日々の運用、地域展開を通して多くの改善要望が寄せられています。これらを事業と相談し、優先度を付けながら開発を進めています。 新しい仲間へ 元々、実証実験の委託開発プロジェクトとして始まったmy routeですが、2023年1月リリースのプロジェクト完了により完全に内製化され、本当にやりたいことができる準備が整います。そういう意味では、2023年はmy routeにとって勝負の年だと考えています。 この記事を読んで興味を持たれた方、是非とも一緒にmy routeを日本一のMaaSアプリとして成長させていきましょう!
はじめに こんにちは。プラットフォームGでDevOpsエンジニアをしている島村です。 KINTOテクノロジーズのプラットフォームGのDevOpsサポートチーム(とSREチーム)では、CI/CD以外にも、監視ツールの整備やモニタリングツールの改善を実施しています。 プラットフォームGは他にもSystemAdministratorチーム、CCOE、DBREなどがあり、AWSを中心としたインフラの設計・構築運用の他にも会社全体のシステム改善や標準化・最適化を担っています。 その中で、昨年にGAとなったAmazon Managed Service for Prometheus(以降Prometheus)とAmazon Managed Grafana(以降Grafana)とX-Rayを使用したAPMの仕組みを導入しましたので、そのことを記事にしようと思います。 背景 私が入社した2021年5月時点でKINTOテクノロジーズ(当時はKINTO)では、AWSリソース監視・ログ文言の監視を行っていました。ただし、CloudWatchを使用したもので、プラットフォームGが設計・設定を行っていました。 当時はアプリケーション運用としてのメトリクスなどは取得されていませんでした。ログ監視も設定の柔軟性は低く、エラー検知はAWSのメトリクスもしくはログによるものか、外形監視からの通知による受動的な検知と対応が主でした。 よく言われるようなO11yの成熟度からすると「Lv0:計測を始める」もできていない状態です。ただ、プラットフォームG内部では問題と認識していたので、まずは計測を始めようということで、APM+X-Rayを導入することとしました。 O11yの成熟度モデルとかの参考はこちら 要素 APM(Application Performance Management) アプリケーションやシステムの性能を管理・監視すること。アプリケーションパフォーマンス管理とも言い換えられます。アプリケーションやシステムの応答時間や構成要素のパフォーマンスを調査することで、アプリケーション全体の稼働状況を把握し、ボトルネックやシステム障害の要素を素早く把握し、改善に役立てるために使用します。 X-Ray AWSで提供している分散トレーシングの仕組みです。 システム全体におけるサービス間の呼出関係の可視化 特定リクエストにおけるサービス間の呼出関係の可視化 (特定リクエストの処理経路の可視化) システム全体のボトルネックを迅速に特定する 上記のことが可能となります。 タスク(Action) 検討する まずは「Lv0:計測を始める」を満たすことを考えました。実装時には、「Prometheus+Grafana」という前提が提示されており、ちょうどAWSでマネージドサービスとしてプレビューされていたタイミングだったので、そちらを選択しました。 Datadog / Splunk / NewRelic / DynaTraceなど、一般的によく使われているSaaSもありますが、前提条件から検討せずにAWSを使用することとしました。あとから、ここらへんのSaaSを使わなかった理由も分かってきました。その理由だと思うことは後述で。 実装する Prometheus Prometheusへのメトリクス出力については Amazon Managed Service for PrometheusにECSからアプリケーションメトリクスを収集する としてまとめています。2021のKINTO Technologiesのアドベントカレンダーとして作成した記事です。 X-Ray 検討当時のメンバーの資料を引継ぎしまして、 AWS X-Ray SDK for Java をベースとして、ECSタスク定義への取り込みなどを整理し、資料化を行いました。 初期構成 改善する X-Ray SDKのOpenTelemetory化 Java17を使い始めたチームから、X-RayのServiceMapがうまく表示されないという相談がありました。よくよく見ると、 AWS X-Ray SDK for Java はJava8/11には対応を明言していますが、Java17への対応は明言されていません。今は AWSDistro for OpenTelemetry Java が推奨されているようなので、全体的にこちらに切り替えることにしました。APMのCollectorと同居できることも良いことの1つです。 Java aws-observability/aws-otel-java-instrumentation から最新のReleaseのjarファイルをダウンロードし、 src/main/jib 以下に保存するだけで導入ができます。SDK for Javaではサンプリング設定の定義ファイルなどもありましたので、導入が簡素になっている印象です。 ECSタスク定義の環境変数 JAVA_TOOL_OPTIONSにAgentの定義を追加します。OTELの環境変数も追加しています。ECSのタスク定義のjsonを確認すると { "name": "JAVA_TOOL_OPTIONS", "value": "-Xms1024m -Xmx1024m -XX:MaxMetaspaceSize=128m -XX:MetaspaceSize=128m -Xss512k -javaagent:/aws-opentelemetry-agent.jar ~~~~~~" }, { "name": "OTEL_IMR_EXPORT_INTERVAL", "value": "10000" }, { "name": "OTEL_EXPORTER_OTLP_ENDPOINT", "value": "http://localhost:4317" }, { "name": "OTEL_SERVICE_NAME", "value": "sample-traces" } のような形になります。(実際はParameter Storeなどを使うのでちょっと変わりますが) OpenTelemetryColletorのConfig Configuration を参考にしつつ、以下のような形でCollectorのConfigを修正します。 APMとX-Ray両方が入っているかつ、タスクごとにメトリクスにラベルがついている形です。 exporterとして使用している「awsprometheusremotewrite」はAWS-OTEL-COLLECTORのv0.18から非推奨に、v0.21からは機能が除かれており、「PrometheusRemoteWrite」+「Sigv4Auth」を使用するようになってますので、ご注意ください。 receivers: otlp: protocols: grpc: endpoint: 0.0.0.0:4317 http: endpoint: 0.0.0.0:4318 awsxray: endpoint: 0.0.0.0:2000 transport: udp prometheus: config: global: scrape_interval: 30s scrape_timeout: 20s scrape_configs: - job_name: "ktc-app-sample" metrics_path: "/actuator/prometheus" static_configs: - targets: [ 0.0.0.0:8081 ] awsecscontainermetrics: collection_interval: 30s processors: batch/traces: timeout: 1s send_batch_size: 50 resourcedetection: detectors: - env - ecs attributes: - cloud.region - aws.ecs.task.arn - aws.ecs.task.family - aws.ecs.task.revision - aws.ecs.launchtype filter: metrics: include: match_type: strict metric_names: - ecs.task.memory.utilized - ecs.task.memory.reserved - ecs.task.cpu.utilized - ecs.task.cpu.reserved - ecs.task.network.rate.rx - ecs.task.network.rate.tx - ecs.task.storage.read_bytes - ecs.task.storage.write_bytes exporters: awsxray: awsprometheusremotewrite: endpoint: [apm endpoint] aws_auth: region: "us-west-2" service: "aps" resource_to_telemetry_conversion: enabled: true logging: loglevel: warn extensions: health_check: service: telemetry: logs: level: info extensions: [health_check] pipelines: traces: receivers: [otlp,awsxray] processors: [batch/traces] exporters: [awsxray] metrics: receivers: [prometheus] processors: [resourcedetection] exporters: [logging, awsprometheusremotewrite] metrics/ecs: receivers: [awsecscontainermetrics] processors: [filter] exporters: [logging, awsprometheusremotewrite] 現行構成 使ってもらう 特に難儀した点です。 「はじめに」に記載した通り、私はプラットフォームGの所属で、ツールを検討して提供しています。いびつな形ではありますが、DevOpsとしてツールなどを全体最適としたかったので、全社横断(横串)で活動するプラットフォームGに所属しています。そのため、 (問題と思っている)プラットフォームG (そこまで問題と認識していない)アプリケーション担当 という状態に陥りました。最近は地道な活動を通じて、必要性を理解をしてもらえて来ていると思います。 SaaSを使わなかった話 ここは個人的な考えです。 特にO11y関連のSaaSってデータ量も多く、総じてお値段がお高くなるイメージがあります。上記の必要性を理解してもらうまで、「使わない」ツールに対して多額の費用を払うというのは、やはり費用対効果を考えると難しいところです。 O11yの成熟度Lv2の積極的対応くらいまで進むと、俯瞰的にボトルネックやパフォーマンスを見たいという要望が出てきて、そこで使用する価値が生まれてくるのではないかと思います。ログやメトリクスとイベントと紐づけたりなど。受動的な対応までなら、ツールが分割されていても、各人のタスク負荷的や量としては許容されるのかなと思っています。 一時的にGrafanaダッシュボートおじさんができるのは仕方がない。ダッシュボートおじさんのコストよりも、SaaSのコストが低くなれば移行していく…はず。 所感 GrafanaもPrometheusもX-Rayも、マネージドサービスであり導入はSaaSほどは簡単ではありませんが、コストという意味では比較的安価で導入できるものです。DevOpsやSRE活動の初期として、O11yを導入するにあたっては、検討する価値はあるのではないでしょうか。 SaaSを使わなかった話にもありましたが、使い始めてからO11yの価値を見て、改善や活動を見直し、コストを比較してから各種SaaSを使い始めるということでも、良い気はしています。 いや、DatadogとかNewRelicのダッシュボードとかHostMapとか、画面上は綺麗に見えますしなんか動いてて監視・モニタリングしている実感を感じますし、見栄えが良いから(`・ω・´)イインジャネ!!と考えるのは、よくわかります。カッコイイもの…
グローバル開発グループ紹介記事の最終弾として、本日はプロダクト開発チーム、導入開発チーム(導入・DevOps・保守・テスト )、業務エンハンスチームの紹介をいたします。 目次 目次 プロダクト開発チーム はじめに プロダクト開発チームとは メンバーの雰囲気 業務の進め方 これから挑戦したいこと 導入開発チーム 概要 ミッション チームの様子 業務の様子 これからのチャレンジ 業務エンハンスチーム 業務エンハンスチームとミッション 現在取り組んでいること 1言語面での一貫した顧客体験 2グローバルKINTOの社会的責任担保 3グローバル開発グループ全体のポテンシャル底上げ チームの雰囲気 今後挑戦していきたいこと グローバル開発Gで一緒に働く仲間を募集中 プロダクト開発チーム はじめに KINTOテクノロジーズのグローバルKINTO開発グループでアシスタントマネージャーをしている石原です。 本記事ではGlobalグループのプロダクト開発チームの紹介をさせて頂きます。 プロダクト開発チームとは グローバルKINTO開発グループは親会社のトヨタファイナンシャルサービス、海外現地法人、外部パートナー、KINTOテクノロジーズの4社間で進めるKINTOサービスの世界展開を迅速に行えるように、ID、グローバルアプリ、クーポンシステムといったプラットフォーム等を開発しています。 メンバーの雰囲気 ECサイト開発、ソーシャルゲーム開発等多種多様なバックグラウンドを持つメンバーで構成されており、 国籍も様々で中国、インド、フィリピン、ベトナム、フランス、韓国、日本等のメンバーが在籍しています。 コミュニケーションは基本的には日本語がメインですが、英語をメインに使っているチームもあります。 上記の通り様々なスキルを持っているメンバーが集っているので色々な観点での意見がでる事が多く、それぞれの意見を尊重しあう雰囲気です。 業務の進め方 プロダクト毎に開発メンバーをアサインしており、プロダクトマネージャーと共に仕事を進めています。 開発の進め方はチームによって様々ですが、2週間のスプリントでアジャイル開発を行っているチームが多いです。 どういったものを作るのかを決めるのはプロダクトマネージャーの意見だけではなく、エンジニアからも意見を出し合い、より良いプロダクトを作り続けています。 また、プロダクトを成長させる為に様々な技術の取り込みや自分達で必要だと思う仕組みの開発も行っています。 例えば下記のような仕組みを利用しています。 名称 利用プロダクト 導入目的 KMM Global KINTO App iOS・Androidで共通のビジネスロジックを1コードでSDK化する事により、開発効率・テスト効率を促進させる Gatsby Global KINTO Web 高速なWeb Siteを開発する為 マイクロサービスDevelopment Kit(GKMDK) バックエンド、バックオフィス管理画面 サブスクサービス、リースサービスをマイクロサービスで容易に構築する事ができる自社エンジニアによって開発を行った仕組み 認証サービスパッケージ IDプラットフォーム IDサービスを容易に導入する為に作られたパッケージ。Global Login機能(KINTO IDで連携先のサービスの利用が可能な機能)、Social Login機能、Sign up/Sign in機能などIDサービスに必要な機能を持つ自社エンジニアによって開発を行った仕組み KMMやGatsby等の世の中に普及している技術を利用するだけではなく、プロダクトにあった仕組みを自分たちで創り出しています。 例えばマイクロサービスDevelopment Kit(GKMDK)はサブスク、リースサービスを立ち上げる際に短期間でバックエンド及び管理画面の開発を行えるようにする為にエンジニアの発案を基に開発を行っています。 このようにプロダクト開発チームでは決まった要件をただ開発するのではなく、自分で必要だと思う事、良くする為には何が必要かを考え動く自主性・積極性を重要視して業務を行っています。 これから挑戦したいこと 今までは基本機能をメインとして新規開発、改善を行っていましたが、これから世界中に通用するようなプロダクトにアップデートしていきたいと思っています。 導入開発チーム 概要 導入開発チーム(導入チーム、DevOpsチーム、保守チーム、テストチーム)のマネジメントをしている水野です。 私たちは、基盤開発チームで開発したプロダクトを元に各国へグローバルKINTOサービスのIT導入や運用をしています。 表題のチームは多いですが、みなさん各国へKINTOサービスを導入する仲間なので、まとめて紹介します。 ミッション 各チームで細かいミッションは違いますが、共通して3個のミッションがあります。 開発の生産性を上げること 必要としているITサービスを提供すること グローバルKINTOサービスを横展開すること 私たちは、実際にKINTOサービスをカスタマへ提供しようとしている各国KINTO事業者向けにGlobal KINTO AppやKINTO BackOfficeシステムを導入して、各国KINTO事業の迅速な立ち上げに貢献することを目標にしています。 チームの様子 現在、グローバル開発Gの約2割の方が所属しています。 所属メンバーの出身地域は日本、インド、中国、台湾、韓国、ミャンマー、エクアドルなど国際色豊かです。国籍、経歴、文化、年齢が違いますが、それぞれ得意なことを主力に、導入に必要なことは経験が無くてもチャレンジして自分たちの力で開発しています。 まだまだ、勉強中のエンジニアもいますが、周りが手厚くフォローしていて着実に育ってくれています。 また、仕事終わりに出身国のレストランへ食事へ行ったり、スポーツを楽しんだりしている人もいます。 業務の様子 KINTO事業を実施している国は世界中なので、事業者とのコミュニケーションは英語が中心です。 自社内では関係する他のグループとのコミュニケーションは日本語で、自グループ内は英語だったり日本語だったり得意な言語で話しています。 各プロジェクトでは2週間のスプリントでAgile開発を標準にしているチームが多く、パートナーのエンジニアとも活発に意見交換しながら業務を進めています。 それぞれのチーム別に見ると。 チーム 業務内容 導入チーム 現在導入を進めている国の事業メンバーと日々コミュニケーションを取り、ニーズを捉えて必要な機能を導入しています。 DevOpsチーム 各プロジェクトやプロダクトチームのインフラ構築、CICDの構築、運用のサポートを行い、開発の生産性を上げています。 保守チーム 最近発足したチームで、KINTOサービスが稼働している国の事業者の求めに応じて迅速な改善を行っていきます。 テストチーム テスト自動化基盤の構築、将来的にはグループ全体のテストを自動化することを目標に活動しています。 これからのチャレンジ もっと世界にKINTOサービスを展開するため、より迅速なKINTO事業者への貢献が求められていると考えています。 そこで。 旧来の組織やアーキテクチャ・技術に固執せず、生産性を上げる施策を取り続けて世界最速のテックチームになりたい。 特に勉強中も含めた各エンジニアからのボトムアップ提案力を上げて技術力の底上げを行い続けられるチームになりたい。 もっといろいろな経歴を持つ仲間と切磋琢磨していきたい。 これを目指した先により良く、迅速にKINTOサービスを提供できるチームができます。 業務エンハンスチーム 業務エンハンスチームとミッション グローバル開発グループ業務エンハンスチームについて、リーダーの森より紹介いたします。 チームは現在5名のメンバーで構成されていて、ほとんどのメンバーが日本語・英語の両方を話します。 我々はグローバルKINTOビジネスのPublic Relationsとして、以下の3つを提供することをミッションとしています。 言語面での一貫した顧客体験 グローバルKINTOの社会的責任担保 グローバル開発グループ全体のポテンシャル底上げ 現在約60人規模のグローバル開発グループは、社内でも最も大きな組織であり、異なる国籍・カルチャー、経歴を持ったメンバーで構成されています。そんな中、グループ全体を横断したタスクを関係各所を巻き込みながら遂行するのが我々です。 現在取り組んでいること 具体的に我々がどんなタスクをもっているか、先述した3つのミッションに分けてご説明します。 1言語面での一貫した顧客体験 こちらはいわゆるローカライゼーション ^1 の対応となります。 実際に翻訳を行うだけではなく、グループ内で開発しているプロダクトの多言語化の進捗管理やプロセス改善、スタイルガイドの作成など、多言語化に関する全てのタスクを管理しております。 2グローバルKINTOの社会的責任担保 こちらは法務タスクです。関係各社との契約締結や、個人データ保護の対応 ^2 などを担当しております。グローバル開発グループで取り扱う案件は海外案件がほとんどなので、日本の法令のみではなく海外の法令も加味してタスク遂行する必要があります。法務エキスパートではないですが、法務目線を持ちながら、ビジネス・プロダクトの詳細を理解した上で間に立って会話するのが我々です。 3グローバル開発グループ全体のポテンシャル底上げ 冒頭でも記載の通り、国籍・言語・経歴など、多種多様なメンバーが所属しております。その方々が自身の持つ力をストレスなく、最大限に、効果的に発揮できるように環境を整えるのも我々のタスクです。以下がその一例です。 上記はあくまで一例ですが、他にもこのタスクの一環として、12月末にチーム内で”KINTO Global Innovation Days”と題し、社内ハッカソンのようなイベントを実施予定です。この様子は後日、別の記事として公開いたします。 チームの雰囲気 グループ内外様々な業務を広く浅めに多数のタスクを並行してこなしていくことが多いので、チーム内ではコミュニケーションを重視して誰がどのようなタスクを持っているか、工数に無理はないか、などを確認しあっています。「困ったことがあれば業務エンハンスに」と宣言しているので、チーム内でも困ったら常に相談しあえるような雰囲気づくりを心掛けています。 今後挑戦していきたいこと グローバル開発グループはKINTOテクノロジーズ全社でも最大の組織です。 一般的なことですが、コミュニケーションの取り方が難しくなったり、プロセスが複雑になったり、組織が大きくなるにつれて出てくる課題は多々あります。今後組織がさらに拡大した場合にも対応できるよう、各タスクにおいてまずはきちんとプロセスを整備したいです。特にローカライゼーションの部分に関しては今後ビジネスが拡大する上でもプロセスの整備は急務です。 また、コミュニケーションの部分についてはグループ全体のモチベーションを向上できるようなイベントやしかけを今後も企画してまいります。 🔻業務エンハンスチーム主催English Cafeの様子 ※普段はリモートでも対応していますが、写真撮影のタイミングだけ同じ部屋に集まりました。 グローバル開発Gで一緒に働く仲間を募集中 これまで3日連続でグローバル開発Gの紹介をしてまいりました。チームメンバーのインタビューは KINTOテクノロジーズのコーポレートサイト にも掲載しておりますので、ぜひこちらもご覧ください。 KINTOサービスは展開が始まって約2年経ちますが、KINTOサービスを立ち上げたり、リプレイスする国はまだまだこれから出てきます。私たちはこれから各国の期待に応えるため成長できるチームメンバーが必要です。ご興味のある方は 弊社採用サイト からご応募ください!
自己紹介 グローバル開発グループのBharath (バラト) です。 Global KINTO ID Platformチームとプロダクト・エンゲージメント・チームに所属しています。また、Technology PortalやUserPool管理画面など、アドホックなプロジェクトのサポートも行っています。 OpenID Connect (OIDC)とAWS Cognito関係に詳しいので、これらについてご相談があればいつでもお気軽に bharath.tv@kinto-technologies.com までご連絡ください。IDの話ができるのを楽しみにしております。 ハッカソンのテーマ KINTOテクノロジーズは、海外グループ会社とのコネクションを作ること、グローバルな舞台で活躍する力を養うこと、グローバルテクノロジーカンパニーとしてさらに進化することを目的に、2022年7月、Toyota Motor North America(以下、TMNA)が主催したハッカソン「toyotaswarm」に参加しました。 私も日本からリモートで参加しましたので、今回の記事ではその時の様子をお伝えしたいと思います。 TMNA "SWARM" Hackathonについて 2回目の開催となる今年は、2022年7月27日~29日までの3日間、世界中のトヨタグループ各社から40チーム、総勢270名が参加しました。 今回のチャレンジは以下の3つでした。 #1 Safety(安全) #2 Innovate to Carbon Neutral(カーボンニュートラル) #3 Open Global Technology Innovation (オープンイノベーション) もともと我々は「デジタルアイデンティティー」を大きなチャレンジと捉えており、私のチームは本業でもこれまでこのテーマに取り組んできたので、今回のハッカソンに参加することとなりました。 目指すべき姿 我々グローバル開発グループは、エンドユーザーがいかに簡単にモビリティーサービスを利用できるようにするか、を重視しています。例えば同グループで開発しているGlobal KINTO Appは、すべてのKINTOサービスをつなぐことを目指していますし、私の所属するGlobal KINTO IDチームは、エンドユーザーが一度サインアップするだけで世界中のあらゆるKINTOサービスを簡単に利用できるようにすることを目標としています。このハッカソンで解決したかった課題も同様で、「どのようにしてサービス間でエンドユーザーの情報を簡単に共有するか?」というものです。ユーザーが別のKINTOサービスを利用する度に、氏名等の情報を何度も入力しなければいけないのは効率的ではありません。そこで、ユーザーの同意を取得した上で、ユーザー情報を他のサービスと共有できるサービスのPoC(Proof of Concept)を行うことにしました。 チームについて ビザの問題で、ダラス(USのテキサス州にあるTMNAの本社)現地で参加したメンバーは3名のみで、残りのメンバーは日本からリモートでサポートしました。 現地参加メンバー キャプテン: Feng Xu (グループマネージャー) PdM: Dai Sasaki FE: Alex リモート参加メンバー FE: Chris.L BE: Bharath ハッカソンイベント中: 数日間で準備しコンセプトとその実現方法を決めました。現地には3人だけでしたが、大きな障壁とはなりませんでした。アイデアをチーム内でブラッシュアップし、9画面、11API、多くのAWSコンポーネントと1100以上のLoC (Lines of Code)を構築しました。 イベントの具体的な内容としては、まずオリンピック金メダリストであるエリン・ジャクソン選手が登壇し、オープニングを盛り上げてくれました。オリンピックのメダルを最も近い距離で見た瞬間です。 その後、各チームは別々のテーブルを与えられ、コンセプト作りに取りかかります。気合いの入ったチームが一斉に動き出したのは見ていて気持ちよかったです。一方、我々のチームはオフサイトメンバー数人が異なるタイムゾーンで作業をしていたため、作業完了までは少し苦労しました。各自が遂行すべきタスクは明確でしたが、すべてを合わせてしてワーキングモデルを作り上げなければならなかったので、そこに労力を費やしました。サーバーを異なるIPアドレスに接続したり、異なる地域のAWS CDNと連携したりといったよくある問題が発生しましたが、全体的にはうまく進めることが出来たと思います。 結果、我々はラウンド1をクリアすることができました。ラウンド2を通過するのは全体の半分だけなので、さらに大変でした。我々もベストを尽くしましたが、残念ながらラウンド2を通過することはできませんでした。 印象に残ったのは、他チームのアイデアが、非常に完成度の高いものだったということです。たとえば、飲酒運転防止のアイデアやCO2排出量削減のアイデアがラウンド2を通過しました。この2つのアイデアは、いずれもソフトウェアだけでなく、ソフトとハードの両方を組み込んだソリューションでした。 イベント参加から学んだこと TMNA役員の方々は常にハッカソン会場にいて、誰でも気軽に話しかけられたので、彼らをとても身近に感じることができ、また、彼らの想いをダイレクトに理解できました。私にとって素晴らしい経験でした。また、様々な国から来た参加者たちもとてもオープンで、親切に我々に接してくれ、他の参加企業とのコネクションを作るのにも非常に良い機会でした。このイベントから企業同士の相乗効果が生まれることを期待します。今後も、同イベントは定期的に開催される予定ですので、ぜひ次回も参加して、トヨタそして世界中にさらなる価値を提供したいです。
グローバル開発グループ紹介記事の第2弾として、本日はPdMチームとUIUXチームの紹介をいたします。 目次 目次 PdMチーム はじめに PdMチームの役割 メンバーの経歴 手がけているプロダクト KINTOで働くことの難しさと醍醐味 UIUXチーム ミッション Design System Global KINTO App 課題と今後 PdMチーム はじめに グローバル開発グループの佐々木と申します。担当プロダクトのProduct Manager(以下PdM)をやりつつ、PdMが所属するPdMチームのマネジメントもしている私から、チーム紹介をさせて頂きます。 PdMチームの役割 PdMチームは、海外の各KINTOサービスを支えるプロダクトの開発ライフサイクルの中で、各国へどのようなものを提供すべきかに関するプロダクト企画を行っています。もっと具体的に言うと、各国のニーズを確認し、各種リサーチ結果に基づいて、解決すべきペインそして提供すべき価値を特定してプロダクトのあるべき姿を描く。そのうえで、それを実現していくロードマップを策定してプロダクト開発を推進していく役割を担っています。 メンバーの経歴 KINTOはモビリティサービスを提供するブランドなので、PdMチームもその領域の経験者が揃っていると思われがちなのですが、実はそんなことはありません。結構多様なバックグラウンドを持つ人達で構成されているんです。例えば、日本の大手テック企業出身の人、新聞を中心としたメディア企業でPdMだった人、中国大手テック企業にいた人、金融業界の経歴がすごく長い人など、いろいろな経歴やスペシャリティを持つ人が、まだ歴史の浅いモビリティーサービスという領域へ強い興味を持ち、KINTOテクノロジーズへジョインしてくれました。 手がけているプロダクト グローバル開発グループでは複数のプロダクトを開発していますが、どんなプロダクトなのか、1つ実例を紹介してみましょう。複数国のカーシェアリングやレンタカーを1つのアプリで提供できる Global KINTO App(GKA) がそれです。アジアのAという国へ行ったときはその国のカーシェア、中東のBという国へ行ったときはその国のレンタカー、といったように、そのアプリにおいて、複数の国にあるKINTOサービスを切り替えて迷わずスムーズに使うことができる体験を提供するものです。グローバルでサービスを提供しているKINTOならではのスケール感だと思いませんか? KINTOで働くことの難しさと醍醐味 海外の各KINTOサービスへプロダクトを提供し、そのプロダクトを通じてビジネス成果を最大化することがPdMチームのミッションですが、それゆえに困難なこともあります。例えば、各地へプロダクトを提供するにあたっては、事業を運営している現地法人と話して現地のニーズやターゲットを理解しようと考えます。しかし、物理的な距離や商習慣の距離、また文化的な距離、など様々な”距離”が存在するため、実際にどんな人がどのようにプロダクトを使ってKINTOを利用しているのかを把握することに、高いハードルがあります。それでも何とかして有益な情報を入手しなければなりません。そのため、現地とのコミュニケーションにおける深さや質の向上は、私達が提供する価値を左右する非常に重要な要素になっています。 (↓の写真は今年世界各地のTOYOTAグループ企業を集めてアメリカで開催されたHackathonイベントの模様。このような場でグローバルのメンバーと交流することも私達にとっては非常に意義があります。ちなみにこのイベント、2023年も実施予定のようです!) 上記で書いたこと以外にも困難なことはいろいろあるものの、まだ正解がわからないモビリティーサービスにおいては試行錯誤しながらやるからこその魅力があります。各地で実施する新しい取り組みや、新しい形での「移動の喜び」の届け方へ今の段階から関わることができるのは、他の組織ではなかなか味わえないKINTOならではの醍醐味なのではないでしょうか。 モビリティーサービスの将来像について、いろいろな人や各企業が想像力を膨らませて考えています。それでも、この領域では確定的なものがまだまだはっきり見えません。その中で、世界各国にいるKINTOユーザーの気持ちへ寄り添いつつ「移動の喜び」を具現化するプロダクトの実現に興味がある方は、ぜひ一緒に前進させていきましょう。 UIUXチーム ミッション UIUX担当の渡辺です。 「KINTO」のブランドで世界展開している各種モビリティサービスに対して、UIUXチームでは Global KINTO App(GKA) というアプリの制作から、 Global KINTO Entrance Web まで規模の大小問わずたくさんの開発活動に関わっています。 ビジュアル的な制作と論理的な情報設計が私達のミッションです。 トヨタグループという大きなバックボーンがあるのでルールや制約が多いと思われる方もいると思いますが、実は決まっていないことも作っていないものもたくさんあります。 Design System 最近一段落ついた大きなプロジェクトはDesign Systemの構築です。 サービスを構築していくには、職種や技術力の壁を超えて同じクオリティを制作することはとても困難です。その問題を解決する1つがDesign Systemでした。 簡単に言えばデザイナーもエンジニアもコピペするだけで同クオリティのものが簡単に作れるコンポーネント集です。 ゼロからの作成だったので、ロゴ規定やカラートーン、アイコンにいたるまで約1年の製作期間は本当に大変でした(本当に…) 現在は社内での導入はもちろん世界のKINTO各社から導入についてお問い合わせが来るようになりつつあり、その1件1件に涙が出そうなくらい嬉しいです。 Global KINTO App GKAの愛称で親しまれています。「世界中のKINTOサービスを1つのアプリでつなぐ」というコンセプトを持たせたモバイルアプリです。現時点ではタイとカタールのKINTOサービスが実装されています。 GKAではアジャイル開発の1つであるスクラムをベースに体制を作っているためプロデューサー・エンジニア・デザイナーがとても近い距離で話し合いながら進められています。 スクラムといえばエンジニア以外は敷居が高いイメージですが、社内では著名な方を講師に迎えてのワークショップの実施などを積極的に行っているので、とても参加しやすくなってきました。 課題と今後 一方、理想通りにはいかないことも多くあります。グループ企業との関係性が複雑な部分があり、ステークホルダーが多すぎて「なかなか判断が進まず長い期間足踏みをしている場面」もあります。「仮説を立てる段階でのリサーチや検討が不十分な場合」もありプロセス整備をしていくことも今後の課題です。 学ぶことも挑戦することも多いので大変ですが裁量も大きいので、やりたいことに手を上げて発言しやすい環境です。チームメンバーの言語も経歴も様々がゆえに新しい発想や表現に出会う毎日でもあります。未来のモビリティーを想像し、いま無いものを生み出すことにやりがいを感じて、想像力全開で取り組める人にぜひ参加して欲しいと思っています。
こんにちは KINTOテクノロジーズ モバイルアプリ開発Gの小山です。 モバイルアプリの開発・運用に携わっています。担当はiOSです。 今回はGlobal KINTOのモバイルアプリ開発の中でMVVMを採用した内容を紹介したいと思います。 MVVM と Combine 話をする前にまずこれらについて軽く説明をしておきたいと思います。 MVVM ソフトウェア開発におけるアーキテクチャのひとつです。 MVCモデルの派生系で Model-View-ViewModel という構成を取ります。 細かいことを言うとややこしいので簡潔に示すと、 Model データをひとまとめにした存在であり、データのまとまりと簡単な処理を担います。複雑な処理は行いません。 View 画面描写を担う存在です。画面を生成するのに必要なデータを受け取り、ただ描写します。 ViewModel ModelとViewの中間地点の存在で、複数のModelのデータをまとめたり、Viewの状態を保持するための値を持つ役割を担ったりします。 といった感じです。個人の見解です。 ただこれでもiOSやり始め当初の私には理解が難しかったです。 そこでiOSの開発に当てはめるとこんな感じになります。 Model 扱うデータをただひとまとめにします。基準として辞書型を採用するくらいならModelを一つ作ります。Model内のデータに関わるシンプルなメソッドのみを持ちます。 View ViewControllerや個別のViewがこれに当たります。描写に関わるコーディングのみ行い、データの操作は行わず、受け取ったデータをただ描写します。基準としてif文が全くない状態を目指します。 ViewModel ViewControllerからトリガーを受け取ってデータを返却します。ViewControllerとは1対1で、個別のViewに関しては場合によって上位のViewControllerのViewModelが役目を担っても良いです。 これを実現していきたいと思います。本記事の中では特にViewとViewModelの分割をご紹介したいと思います。 Combine 2019年の6月に登場した、Apple公式のリアクティブアーキテクチャを実現するフレームワークです。 それまではリアクティブアーキテクチャを実現するにはサードパーティ製のRxSwiftを使うことがほとんどでしたが、公式でサポートの厚いライブラリがリリースされました。 ちょ、ちょっと待ってリアクティブアーキテクチャってなんだっけ と、思った方もいるかと思います。 私がそうでした。Wikipediaによると以下の通りです。 reactive programming is a declarative programming paradigm concerned with data streams and the propagation of change. -- Wikipedia | Reactive programming これを和訳すると以下の通りです。 データストリームと変更の伝播に関係する宣言型プログラミングパラダイムです。 いまいち分からないですね。 こちらもややこしくせず理解するには、表計算ソフトをイメージすると良いと聞いたことがあります。 以下のような簡単な計算式を入力したとします。 A B C 10 20 =A1+B1 これを入力完了するともちろんC1セルに計算結果が入ります。 A B C 10 20 30 この状態でA1セルを20に書き換えた場合はどうなるでしょうか。 A B C 20 20 40 もちろんC1セルが40に書き変わります。 このようにA1の変更に対応してC1に直接手を加えずに変更を伝播させていくことを、リアクティブアーキテクチャと呼びます。 初めにデータ処理の宣言( =A1+B1 )のみを行い、最後に実データを流す(A1に20を代入)、といったイメージが私はしっくりきました。 背景 前置きが少々長くなりました。我々がなぜCombine、MVVMを採用するに至ったかという話です。 Global KINTOの開発案件を担当した当初、外注していたソースコードを内製に切り替えて間もない状況だったこともあり、既に出来上がっている部分はいわゆるスパゲッティコードの状態でメンテナンス性が非常に悪い状態でした。 あらゆるところでグローバル変数が呼ばれ、どのクラスがどのクラスと依存関係にあるか全く分からない状態でした。 しかし幸いなことに、コードと画面数が比較的少なかったため全体的にアーキテクチャを見直すことで解決をしようと試みました。 この際に、モバイルアプリ開発GにてCombineを使ったMVVMの知見があったため、MVVMのアーキテクチャに則り進めることとなりました。 アーキテクチャに則った設計をきちんとすることで、新規参入したメンバーでも理解しやすく、安全に改修できるようなプロジェクトにすることが目標です。 いざ、実装! 今回はMVVMに則ると決めたので、ViewとViewModelは責務を分離します。 なお、今回の実装ではUIKitを使用します。 View - Part1 Viewでは一切の演算処理を行わない代わりに、イベント発火のタイミングだけをViewModelに伝えたいです。 これを実装するために、CombineのPublisherを利用していきます。 例えば、viewDidLoadのタイミングを送信する必要がある場合、PassthroughSubjectにVoid型を持たせて定義しておきます。 private let didLoad: PassthroughSubject<Void, Never> = .init() これをviewDidLoad時に送信するようにします。 override func viewDidLoad() { super.viewDidLoad() didLoad.send() } また、ボタンのタップ時にはtapPublisher ^combineCocoa を利用するのがスマートです。 まず、対象のボタンをOutlet接続し、送信用のPublisherを準備します。 もしボタンを押したタイミングで表示されている画面のString値を渡したい場合は、VoidではなくStringでPublisherを定義します。 @IBOutlet weak var button: UIButton! @IBOutlet weak var textArea: UITextField! private let tapButton: PassthroughSubject<String, Never> = .init() その後、tapPublisherを利用してイベントを検知してViewModelに送信します。 button .tapPublisher .sink { [weak self] in self?.tapButton.send(textArea.text ?? "") } .store(in: &cancellables) 続いて、ViewModelを見てみましょう。 ViewModel ViewModelではViewから受け取ったイベントトリガーを元に、情報を取得したり生成したりすることで、Viewに必要な情報を集めて返してあげます。 こちらではCombineのOperator、Subscriberを使って実現していきます。 初めに、View側との接続準備のため、ViewModelとは別にInput,Outputの構造体を定義します。 InputはViewから渡されるデータ、Outputはその結果ViewModelから渡したいデータです。 データのやり取りの際に、渡されるデータとエラーの型がきちんと決まっていれば処理は可能であるため、型は一律AnyPublisherを使います。 struct ViewModelInput { let viewDidLoad: AnyPublisher<Void, Never> // 画面が最初に表示されたときにトリガーが欲しい! let tapButton: AnyPublisher<String, Never> // ボタンがタップされたときにテキストが欲しい! } struct ViewModelOutput { let onShowWelcome: AnyPublisher<String, Never> // Welcomeテキストを画面に表示してほしい let onShowText: AnyPublisher<String, Never> // 任意のテキストを画面に表示してほしい let onShowOnlyFirst: AnyPublisher<Void, Never> // 最初にボタンを押された時のみ固定値を画面に出してほしい } 続いて、データの処理部分を作成します。Inputを受け取ってOutputに変換する部分の実装になります。 例えば、viewDidLoad時に特定のAPIを実行するような動作であれば、viewDidLoadのPublisherからOperatorを繋げてAPIリクエストを実行します。 func transform(input: ViewModelInput) -> ViewModelOutput { let apiResponse = input .viewDidLoad .flatMap { api.requestData() } 同様にボタンタップ時、画面上のテキストを基にデータを生成するような動作をしたい場合は、mapを使います。 let text = input .tapButton .map { createText(text: $0) } また他にも、viewDidLoadが呼ばれ、且つボタンのタップもされたときに処理させたいことがあるとします。 そういった場合はPublisher同士を接続することができます。いくつか接続方法がありますが、今回はzipを使います。 let didLoad = input .viewDidLoad let buttonTap = input .tapButton let didLoadAndButtonTap = didLoad .zip(buttonTap) .map { _ in } このように様々なOperatorがCombineやその拡張ライブラリにはすでに準備されているため、コードをシンプルに保ちながら複雑な処理を実現することができます。 Operatorはいくつもありますが、個人的に便利でこれさえ覚えておけばOKというものを以下に挙げておきます。 map flatMap compactMap filter share materialize ^combineExt merge zip combineLatest withLatestFrom ^combineExt そして最後にOutputの型に合わせてreturnしてあげます。 Combineを利用する場合、Operatorを接続するたびに型がどんどん拡張されてしまうため、最後にAnyPublisherにまとめてから生成したデータをViewに返します。 return .init( onShowWelcome: apiResponse.eraseToAnyPublisher(), onShowText: text.eraseToAnyPublisher(), onShowOnlyFirst: didLoadAndButtonTap.eraseToAnyPublisher() ) } View - Part2 ViewModel側が完成したので、Viewと接続して画面描写を行います。 先ほど用意したtransformメソッドを呼び出して、ViewModelでの処理結果を受け取りましょう。 let output = viewModel.transform( input: .init( viewDidLoad: didLoad.eraseToAnyPublisher(), tapButton: tapButton.eraseToAnyPublisher() ) ) 処理結果を受け取ったら、各結果の際にどんな画面描写をするか実装して完成になります。 output .onShowWelcome .receive(on: DispatchQueue.main) .sink { [weak self] in self?.textArea.text = $0 } .store(in: &cancellables) output .onShowText .receive(on: DispatchQueue.main) .sink { [weak self] in self?.textArea.text = $0 } .store(in: &cancellables) output .onShowOnlyFirst .receive(on: DispatchQueue.main) .sink { [weak self] in self?.showCustomMessage("First!") } .store(in: &cancellables) 完成 こうして完成したコードがこちらになります。 class ViewController: UIViewController { @IBOutlet weak var button: UIButton! @IBOutlet weak var textArea: UITextField! private var viewModel: ViewModelProtocol! private let didLoad: PassthroughSubject<Void, Never> = .init() private let tapButton: PassthroughSubject<String, Never> = .init() private var cancellables: Set<AnyCancellable> = .init() func configure(viewModel: ViewModelProtocol) { self.viewModel = viewModel } override func viewDidLoad() { super.viewDidLoad() bind() didLoad.send() } func bind() { let output = viewModel.transform( input: .init( viewDidLoad: didLoad.eraseToAnyPublisher(), tapButton: tapButton.eraseToAnyPublisher() ) ) button .tapPublisher .sink { [weak self] in self?.tapButton.send(textArea.text ?? "") } .store(in: &cancellables) output .onShowWelcome .receive(on: DispatchQueue.main) .sink { [weak self] in self?.textArea.text = $0 } .store(in: &cancellables) output .onShowText .receive(on: DispatchQueue.main) .sink { [weak self] in self?.textArea.text = $0 } .store(in: &cancellables) output .onShowOnlyFirst .receive(on: DispatchQueue.main) .sink { [weak self] in self?.showCustomMessage("First!") // あらかじめメソッドが準備されている想定 } .store(in: &cancellables) } } protocol ViewModelProtocol { func transform(input: ViewModelInput) -> ViewModelOutput } struct ViewModelInput { let viewDidLoad: AnyPublisher<Void, Never> let tapButton: AnyPublisher<String, Never> } struct ViewModelOutput { let onShowWelcome: AnyPublisher<String, Never> let onShowText: AnyPublisher<String, Never> let onShowOnlyFirst: AnyPublisher<Void, Never> } struct ViewModel: ViewModelProtocol { let api: SomeApiProtocol init(api: SomeApiProtocol) { self.api = api } func transform(input: ViewModelInput) -> ViewModelOutput { let didLoad = input .viewDidLoad .share() let apiResponse = didLoad .flatMap { api.requestData() } // Future<String, Never> が返される想定 let buttonTap = input .tapButton .share() let text = buttonTap .map { createText(text: $0) } let didLoadAndButtonTap = didLoad .zip(buttonTap) .map { _ in } return .init( onShowWelcome: apiResponse.eraseToAnyPublisher(), onShowText: text.eraseToAnyPublisher(), onShowOnlyFirst: didLoadAndButtonTap.eraseToAnyPublisher() ) } func createText(text: String) -> String { "\(text) show!!" } } このようにViewとViewModelで明確に役割分担を行うことでコード内に秩序が生まれます。 どのViewでも同じ構成をとることができるため、コードの可読性が向上しメンテナンスもしやすく、役割が決まっているので無理矢理なコーディングが生まれにくいです。 ただしデメリットとしてはCombineやRxSwiftなどのリアクティブプログラミングを経験していない場合、それまでのSwiftとは 言語が変わったかと思うほど理解が難しい 点です。 Global KINTOのiOSのプロジェクトでは、全てのViewをこの構成に則って作成しています。 可読性が向上したことでメンテナンス性も上がり、新規のViewを作成する場合でも作成の仕方にばらつきが出づらくなりました。 最後に モバイルアプリ開発Gでは、本記事のような知見をGlobal KINTO以外のプロダクトに共有したり、これとは全く別の新しいアーキテクチャに挑戦するといったようなことも実践しています。 引き続き、モバイルアプリ開発Gの責務として、iOSの様々な開発手法に挑戦していきたいと思っています!どうぞよろしくお願いします。
KINTOのグローバル展開をサポートしている開発組織を紹介します。 目次 目次 自己紹介 グローバル開発グループの体制について 今後の活動方針の紹介 おわりに 自己紹介 KTCでグローバル開発グループでグループマネージャーをしているFengです。 私は中国でソフトウェア開発を学び、2002年より日本の大学院に進学し、その後は大手電気機器メーカーでソフトウェアエンジニア、自動車部品メーカーでプロジェクトマネージャー、プロダクトオーナーを経て、前職はモビリティ関連部品を取り扱う外資系メーカーの日本トップを務めていたことでKINTOに入社しました。 KINTOに入社してから、各国のステークホルダーたちと密に連携し、KINTOとして必要と思われるグローバル共通なものはどういうものなのかを常に考えながらプロダクト開発及び組織作りをやってきました。 グローバル開発グループの体制について 2019年グループ立ち上げの初期頃は、私がPdMを兼任しながら数名メンバーしかいない中で開発をスタートしていましたが、ちょうど3年経った今(2022年11月)は社員59名になり、その内39名が海外出身ということで、かなりダイバーシティな環境となってきたと感じています。 我々のグループとして、常に海外のKINTOチームとコミュニケーションを取りながら、下記のような業務を遂行してきました。 要件・ニーズを確認してプロダクトを企画・検討 グローバルで共通化できるようなプロダクトを基盤として開発 開発してきたプロダクトを必要に応じて各国への導入開発を実施 よって、今の開発体制もそれらの業務をサイクルとして回せるよう、下記のように構築しています。 PdMチームが企画したプロダクトを、開発チームが開発し、導入チームが各サービスへ導入する流れです。 また、各プロダクトやグループ全体に係る共通のチームとしてUIUXチームと業務エンハンスチームが存在します。 今後の活動方針の紹介 モビリティ事業はまだまだ分からないことがいっぱいある中で、各国KINTO事業をより効率よく支えるために、グローバルで共通化可能なものを念頭に置いて下記四つの方面から活動していく予定です。 おわりに KTCのグローバル開発グループ全体について、簡単ではありますが紹介しました。 各チームの詳細についてはこのあと第二弾でPdMチーム・UIUXチーム、第三弾で開発チーム・導入チーム・業務エンハンスチームの紹介をいたします。 まだ組織として成長している最中であるので、色々課題はありますが、KINTOが目指している Ever Better Mobility For All を実現するために、少しずつ居心地の良い組織をメンバーと一緒に仕上げていきたいと思っています。 また、この記事を見てグローバルでKINTOはどのようなサービスを展開しているのか、グローバル開発グループの雰囲気に興味・関心を持っていただいた方は、ぜひ下記のサイトにもアクセスしてみてください。 KINTOのグローバルポータルサイト KTC採用ページ内グローバル開発Gの紹介記事
はじめに KINTOテクノロジーズで、複数のサービスが利用する決済プラットフォームの開発チーム[^1]に所属している金谷です。 新規プロジェクトに対して、リモートでモブプログラミングを行い、オンスケジュールで開発完了させた事例を紹介します。 [^1]: 決済プラットフォームの他の取り組みは、 グローバル展開も視野に入れた決済プラットフォームにドメイン駆動設計(DDD)を取り入れた をご参照ください。 背景 社内向けの決済に関する業務システムを新規で作るプロジェクトが発足し、東京勤務のプロダクトオーナーと、東京勤務のエンジニアに、私含む大阪勤務の2名による3名のエンジニアの体制で開発することになりました。 素早く作りたいことと、運用コスト(特にAWSコスト)を減らしたいことから、フロントエンドには AWS Amplify をベースとしたReactによるSPAを採用し、いくつか必要なAPIの開発には AWSサーバレスアプリケーションモデル を採用しました。 課題 プロジェクト開始直後の時点で、私は既に不安がありました。 具体的には、私自身が今まで使ったことのないAWSのサービスを使うことです。 自分の不安も共有するべくそれぞれのメンバーに話を聞くと、どうやら集まった3人は、技術的にも得意な領域が異なることが分かりました。 具体的には、フロントエンド・バックエンド・インフラ(AWS)の3領域に分けた場合、開発チームの各メンバーは、2つの領域は得意だが1つは自信がないことが分かりました。 例えばKさんこと私の場合、フロントエンドとバックエンドは大丈夫な反面、AWSに自信がない、といった具合です。奇跡的にも、各技術領域について2人は詳しい事が分かりましたので、誰かに頼り切りになる局面がなさそうだという点で安心しました。 メンバー フロントエンド バックエンド インフラ(AWS) Kさん ◎ ◎ △ Tさん ◎ △ ◎ Nさん △ ◎ ◎ スキルの領域と開発メンバーの得意領域の星取表 また会話する上で分かった一番の不安は、チームで開発していく上での共通認識がないことでした。 例えばフロントエンドだけでも、コンポーネントの粒度はどうするのか?状態管理はどの粒度で行うべきか?Promiseベースかasync/awaitで行くか?テストをどこまで書くべきか?などなど、コードが1行もない状況ですので、すべてを決めていく必要があります。一方で、入社後1年未満のメンバーで構成されたことから一緒に仕事をするのが初めてのメンバーなので、あのプロジェクトのように〜といった共通認識がありません。共通認識の構築はいち早く行う必要がありました。 改めて課題を整理しますと、プロジェクトを成功させるために、以下の2つが大きな課題であると考えました。 共通認識が揃っていない状況でスタートするため、共通認識を早めに構築したい (認識齟齬を減らし良い品質のプロダクトを作りたい) 各々の得意な領域を共有・補完することで、技術力を底上げしたい 上記2つの課題を解消するために、今回のプロジェクトでは、開発のスタイルとしてモブプログラミングを採用することにしました。 モブプログラミングとは モブプログラミング(以下モブプロとします)とは、 モブプログラミング・ベストプラクティス という本では「3人以上の人々が1台のコンピューターの前に座って協力しながら問題を解決していくこと」とされています。 ひとつのPC(画面)で皆が参加するのですが、実際には2つのロールがあります。 3人以上で行うため、モブが2人以上いる状態で、議論しながら方針を決め、タイピストが方針に従いコードに落とし込みます。 タイピスト (PCを操作しコードを書く) モブ (開発の方針を議論して指示する) ひとつの仕事に3人以上が関わるモブプロを理解する上で、リソース効率とフロー効率という考え方が重要になります。詳細は フロー効率性とリソース効率性について #xpjug - SlideShare に譲りますが、モブプロはフロー効率に全振りする仕事の仕方です。 フロー効率最大化が目指したいことではないのですが、モブプロをする以上、フロー効率に軸足を置いた仕事の仕方になります。 試行錯誤した内容・工夫したこと チームメンバーは地理的に離れている関係上、リモートでの開発を余儀なくされます。そのためZoomを使ったリモートモブプロが基本になります。 プロジェクトを成功させるために、課題の解消が必要だと考えていたため、以下の工夫をしながらモブプロを取り入れていきました。 情報量を増やす 情報量を増やすために、画面はタイピストのデスクトップ全体を共有しながら作業しました。共有ウィンドウだけでは、ウィンドウ外で何が起きているか分かりません。OSやツールの使い方も共有したいため、デスクトップ全体を共有しました。 次にタイピストは、考えていることをなるべく声に出しながら作業するようにしました。特に実際にアウトプットの作業を出来るのはタイピストだけなので、ズレがないかを確認する意図もあります。 気がつけば、タイピストも議論に加わることも多くなりました。 本来のモブプロのタイピストとは異なるのですが、共通認識を作ることが大事であると考えていたため、議論参加をヨシ!としていました。 時間を管理する 3人以上が自然に集まってモブプロすることは難しいと考えたため、カレンダーで各員の予定を押さえて、モブプロの時間を確保するようにしました。 モブプロの時間を事前に設定しておくことで、1日の開発にリズムが生まれやすくなります。 また、 ZoomのTimerアプリ を入れて、制限時間を設けるようにしました。モブプロは集中した作業のため、時間を区切らず続けてしまうことで疲労しやすくなります。適度な交代と適度な休憩を入れたいため、タイマーで時間管理するようにしました。書籍や他社事例 [^2] を見ると10分交代と非常に短い時間で交代するようですが、次のタスク単位で分ける関係もあり、30分にしました。 [^2]: https://techblog.yahoo.co.jp/entry/2020052730002064/ フィーチャーチケットを更に細分化してTODO コメントとして残す モブプロ対象のフィーチャーについて、作業を事前に細分化して、修正対象の中心となるソースコードに TODO コメントとして残すようにしました。 TODOコメントを残すには2つのメリットがあります。一つは、フィーチャーのゴールの解像度が上がること。もう一つは、区切りのいいタイミングで交代しやすくなることです。 例えば決済処理を行うユーザー情報の更新APIを新しく作る際に、以下のようなTODOコメントを、コードを書く前に書いていました。この時点で作業内容も作業順番もクリアになるため、段取り良く進める事ができますし、交代や休憩もスムーズでした。 # TODO openapiにユーザー情報更新の定義を追加 # TODO infrastructure - ユーザー情報更新のインターフェース作成 # TODO infrastructure - ユーザー情報更新の処理実装 # TODO application - バリデーション処理実装 # TODO application - ユーザー情報更新の処理実装 # TODO sam template に lambda 定義を追加 # TODO デプロイ、動作確認 def lambda_handler(event, context): pass モブプロする・しないの基準を相対的に設定する すべてをモブプロで開発するのか?という疑問は早い段階で上がりましたが、実際にモブプロを進める上で、難しそう・議論が必要そうなフィーチャーを優先的にモブで実施することにしました。 あくまでスプリント内のフィーチャーのうち、相対的に難しそうなフィーチャーという選び方ではありますが、難しそうなフィーチャーに対する認識も揃ったため、皆に納得感がありました。 また簡単なフィーチャーは、モブプロ以外の時間で各自対応するようにしてバランスを取っていました。 難しそうなフィーチャーはモブプロで片付いているため、モブプロ終了後に簡単なフィーチャーのプルリクエストがガンガン飛んできました。 モブ「プログラミング」以外のこともモブプロで行う モブプログラミングなので「プログラミング」だけを対象にしていたかというとそうではなく、プログラミング以外の作業もモブプロ時間にやっていました。例として2つ挙げます。 コードレビュー コードレビューもモブプロでやっていることがありました。すべてをモブプロでやっていたわけではありませんので、どうしても開発過程を見ていないコードが出てきます。 モブプロの時間の最初に、モブプロ以外で作ったコードを説明する場を設けて、皆が聞いて質問するようにしました。この時間があったおかげで、詰まりがちなレビュー工程も素早くパスできました。 運用に関わる作業 運用に関する作業もモブプロの時間を使って行いました。具体的には、初めてCognitoのユーザープールを作るときや、GitHub Actionsに関する設定を追加するときには、モブプロの時間を使って皆で見ながら作業しました。これにより、運用面のイメージも付くようになりました。 プロダクトオーナーとのコミュニケーションをメンバー全員で出るようにする 開発チーム内の共通認識はできるようになったのですが、プロダクトオーナーも入れた会話がないとずれる恐れがありますし、実際にずれたことがありました。最初の頃は、プロダクトオーナーと私ではよく会話していたのですが、特に議事録も残さず意思決定も曖昧なまま進めていたため、認識のずれが発生していました。 対策として、プロダクトオーナーとのコミュニケーションは全員で参加するようにし、合わせて議事録もリアルタイムで残すようにしました。 以降は認識のずれも減ってきて、手戻りも少なく開発を進められるようになりました。 合わせて、お互い出張する機会を作り、重要な意思決定やオフラインでモブプロする機会を増やしました。 オフラインでモブプロする図 スプリントゼロを設ける (モブプロとは直接関係ない) スプリントゼロを設けさせてほしいとプロダクトオーナーにお願いして、1週間のスプリントゼロ期間を作りました。 スプリントゼロでは、スプリント1でいきなり多くのアウトプットを出せるようにするための準備を行ったのですが、特に私がこだわりをもって行った作業は、以下の2点です。 create-react-app が最低限動くリポジトリを作る create-react-appをデプロイできるようにデプロイ先やGitHub Actionsを整備する つまり、開発環境でワンクリックで動作確認出来る状況を一番はじめに作りました。 継続的デプロイができることで、より良いフィードバックを素早く得ることができます。 継続的デプロイの仕組みを早い段階で作ったことで、いつでも誰でも開発環境に最新のコードをデプロイできるようになりました。それどころかスプリント0のスコープを飛び越えてスプリント1のフィーチャーを終わらせる人も出てきて嬉しい悲鳴でした。 モブプロとは直接の関係はありませんが、新規開発の際にスプリントゼロを設けるのはオススメです。 他にも準備することが盛りだくさんのスプリントゼロの詳細については 【資料公開】スクラムプロジェクト開始のベストプラクティス | Ryuzee.com をご参照ください。 結果の分析と次へのトライ 結果 プロダクトオーナーが求める品質を満たしつつ、オンスケジュールで開発を完了することができました。 その際、「これほどの高品質でスケジュール通り終わったプロジェクトは経験したことがなかった」とご評価いただきました。 また、利用者である業務部門のご担当者にも実際に画面を触っていただいてフィードバックを反映し、最終的に「とても使いやすい」とご評価いただきました。 我々開発メンバーも、苦手意識のある領域も自走できるようになりましたし、自分の強みを更に発揮する方向に進んでいます。 次に活かすための分析 プロジェクトとしては非常に良い成果を出すことができたと思いますが、再現性の高そうな理由を考えると、以下になると考えています。 共通認識を早い段階で揃えることにより、品質を高めて後戻りを減らすことができた スプリントゼロで開発の基盤を作ったことで、作業効率が最初から高くなっていた モブプロを通すことでコミュニケーションが多く発生し、早い段階で関係性構築ができた 総じて早期にコミュニケーションや開発基盤の構築など、よい準備ができたことが良かったのでしょう。 フロー効率に全振りするモブプロを積極的に行うことでスケジュールに遅れが出るのでは?と予想していましたが、そうなりませんでした。その理由として、難しい部分をモブプロで対応する一方で簡単な部分は各メンバーが対応したためではないか?と考えています。開発を進める上でのボトルネックとなる部分に集中できたことが良かったのでしょう。 次へのトライ まずは自分たちだけで言いますと、自分たちだけでは解決できないときに、その問題を解決できる人と一緒にモブプロできるといいなと考えています。技術習得面でも、横のつながりの面でも、良い効果が得られそうだと考えています。 また、もう少し規模の大きいプロジェクトでもモブプロをやってみたいです。フロー効率全振りでは難しいという状況になるとは思いますが、まずは早い段階で共通認識を作ることがどのくらい良いのか?試してみたいです。 まとめ フロー効率を最大化するモブプロですが、揃っていない共通認識を揃えるためにも非常に有用でした。難しい実装に集中してモブプロを行うように使い所を明確にすることで、プロジェクトの遅れを出さずに開発することができました。 KINTOテクノロジーズは、東京と大阪に開発拠点があります。どのような人たちが働いているのか?募集職種はどのようなものがあるのか?気になる方は、 募集職種一覧 をご覧ください。お待ちしております!
はじめに はじめまして、KINTOテクノロジーズで分析グループのマネージャをしています西口です。Tech企業の中に分析グループということで不思議に思われる方もいらっしゃることでしょう。今回はそのあたりのお話をさせてもらいたいと思っています。 分析グループの役割 KINTOテクノロジーズの事業内容は、「デジタル分野における情報システムの設計、開発、運用管理および販売等の情報処理サービス」「企業経営戦略、マーケティング戦略の企画、立案およびコンサルティングに関する業務」となっています。私たち分析グループは後半部分を担っており、日本のKINTO事業はもちろんですがトヨタファイナンシャルサービス株式会社のグループ会社が運営する各種サービスでのデータ利活用にも協業で参画し、データを使ってビジネスに価値をもたらすということを目指しています。 データに価値をもたらすために(分析グループの守備範囲) DIKWピラミッドをご存じでしょうか。膨大な情報の活用プロセスをData, Infomation, Knowledge, Wisdomの4つの頭文字から名づけられたものとなります。私たちは膨大な量の情報をいかに新たな知識体系に変換していきながらビジネスで価値をもたらすことができるかということにこだわっています。 そのDIKWピラミッドを実現するために、下記に述べる大きく4つのパートに分けて業務に携わっています。 1. データ取得設計・実装 通常、ビジネスデータはユーザーのご注文フローなどでのアクションから「発生する」トランザクションデータと商品管理等のマスタデータというものがあります。私たちはトランザクションもマスタもともに「発生させる」というところも意識しています。近年、個人情報保護の観点もあり、データは取りにくくなってきています。あるタイミング(例えば、ユーザー登録時など)でしか取れないデータというものもあり、そこで取り漏れるとその後取ることができなくなるものも少なくありません。私たちはWEBサイトへ適切に分析タグを設置していくことはもちろんのこと、ユーザーアクションによりデータを発生させる仕掛けや仕組みの企画までも取り組んでいます。 2. データ蓄積 そして発生した/させたデータを迅速に適切な形でデータレイク・データウェアハウスに格納して、次の工程(データ分析)に進めるようにしなければなりません。ここでのこだわりは分析者への負担の軽減です。分析者の「データの取得・加工」にかける工数を必要最小限にして、より分析に注力できるようしたいと思っています。簡単に取得したいデータが取得できないと分析者の思考の進化・深化の障害になりかねません。たとえば、「Aサービスの会員とBサービスの会員を結合したい」といった場合に容易に突合ができるような仕組み作りが重要になってきます。よくメンバーには「受け手のことを考えてパスを出せるように」ということを言っています。 3. データ分析 分析とはある切り口で分けて、それらを比較すること。そこから差を見出すことだと考えています。そして、その差を広げることあるいは縮めることを促進させるのが「施策」だと考えています。私たちはその差を適切な切り口で見つけ出し、ビジネスメンバーに知らせること、さらには施策を提案するところまでが役割です。 これらに必要なスキルとして、メンバーには2つのことを伝えています。ひとつは、「多角的な視点を持つこと」。具体的には、“虫の目、鳥の目、魚の目、蝙蝠の目”でものごとを見ていける力を養ってもらっています。もう一つは「なぜなぜ分析」です。ものごとの本質に行きつくためには何度もなぜを繰り返しながら、正しい因果関係のなかで根本要因を見つけ出すという姿勢が必要です。 4. AI/機械学習 高度なデータ分析の位置づけとしてAI/機械学習を位置づけています。過去のデータから未来を予測することはこの領域です。この分野では、トヨタ自動車未来創生センターともコラボレーションしアカデミックな情報も得ながら、高度なアルゴリズムを使ったモデルの作成も行っています。またビジネス分野におけるAI/機械学習はモデルの見直しは常に行われます。そういったモデルの改修~実装をしやすくなるようなMLOpsの環境構築も必要不可欠で、このあたりも当社プラットフォームグループと協力しながら作り上げていっています。 データに価値をもらたすために(担当・役割) 繰り返しになりますが、特にBtoCビジネスにおいて、データに価値をもたらすためには、適切なタイミングでデータを取るということが非常に重要です。その時しか取れないデータというのがあるからです。そして次の工程で使う人が使いやすいようにデータを貯めるということが次に重要な点です。それらを成し遂げるために、分析グループには次のような肩書のメンバーがそろっています。 1. データアナリスト KINTOテクノロジーズのデータアナリストは単なるアナリストではありません。WEBサイトやアプリの適切な箇所で適切なタイミングで、また適切な形でデータを取るためのデータ取得設計から関わり始め、タグの設置も担っているからです。データ分析で必要になりそうなデータを事前に想定して漏れなく取っておくという動きは、実際に分析するデータアナリストこそがすべきタスクだと考えているからです。そして発生したデータはデータレイク・データウェアハウスに蓄積されています。そこからBIツールやSQLを使って自由自在にデータを抽出し、適切な切り口でドリルダウンしながら事象の根本要因を見つけ出すことで、予兆の発見や施策の提案などを行っていきます。 2. データエンジニア データエンジニアにも高いスキルを求めています。事業会社ならではだと思いますが、バックエンドのデータベースからそのまま情報系のデータウェアハウスにデータを格納するだけではなく、データ分析に関わるメンバーが使いやすくなるように配慮してデータを作っていくようにしています。KINTOではどんどん新しいサービスが立ち上がっており、サービス立ち上がりと同時にデータ分析が始められるように、効率な開発のための、開発ガイドライン策定、共通関数化、CI/CDの仕組みなどの構築も担っています。 3. BIエンジニア ビジネスメンバーにデータを情報に変換して迅速にビジネスの状況を知らせていくことは非常に重要です。またマネジメント層と現場サイドでも見たい情報の粒度は異なります。それらを適切な形で伝えていくためのダッシュボード開発・改修・メンテナンスがBIエンジニアの役割です。 4. データサイエンティスト KINTOではさまざまなサービスがこれからも展開されていきますが、そこでもAI/機械学習関連はどんどん出てきそうです。数値データはもちろん、画像関連なども幅広い対応が求められます。ビジネス現場ではいくら精度の高いモデルを作ってもそれが使われるとは限りません。マーケターなどに説明して納得してもらう必要性があります。そのためには旧来の統計手法なども適切に使いながら説明を行う必要があり、ビジネスの理解や消費者への興味・関心というところもKINTOテクノロジーズのデータサイエンティストには必須のスキルとなります。言うなれば、KINTOビジネスにおけるデータサイエンティストは、マーケティング・データサイエンティストという役割になってきます。 5. 分析プロデューサー 2022年9月に新しく設けたポジションです。上記1~4のそれぞれの役割を横串でコーディネートするのが分析プロデューサーの役割です。ビジネスサイドでの課題を聞き出してそれを適切な分析問題に置き換えるビジネス力が必要とされます。もちろん1~4のデータに関わる業務の経験・知識も必要とされ高度な能力が要求されます。KINTOテクノロジーズ分析グループが今後さらなるプレゼンスを発揮していくためにも非常に重要な役割です。 分析グループのメンバー・雰囲気・業務の進め方 1. メンバー・雰囲気 非常に難易度の高い業務に日々取り組んでいる私たち分析グループですが、メンバーの経験はそれぞれいろいろです。ただ共通しているのは、データアナリスト、データエンジニア、データサイエンティストなどの柱を持ちながら、さらにデータに関わる領域で幅を広げたいという志向性・モチベーションを持った好奇心旺盛なメンバーばかりです。新メンバーがジョインしたときにはいつもメンバー全員で自己紹介をする時間を設けているのですが、各自の趣味やマイブームは大変ユニークなものとなっています。 2. 業務の進め方 分析グループは、メンバーが東京・名古屋・大阪と3拠点に分かれています。データアナリスト、データエンジニア、BIエンジニア、データサイエンティスト、分析プロデューサー、それぞれのチームがあり、各リーダーのもとで、オンライン・オフラインをうまく使って気軽に相談しながら業務を進めています。 今後、挑戦したいこと 今、私たちの関わっているKINTOおよびその他のサービスについても、サービスを跨いでご利用いただいているユーザーを繋げて分析するということに挑戦したいと思っています。そのためには、分析グループの担う機能・役割が有機的に結びつかないと実現しません。難易度は高いですが、これを実現することでユーザーをより深く捉えていきたいと考えています。 モビリティプラットフォーマーのトップランナーを目指すKINTOにおいては、GPSなどの移動データからWHEREが、お金のデータから、HOW MUCHを捉えることができます。これらのログにWHOとWHENが記されることで、ユーザーの嗜好性やライフスタイルの分析を深めていきたいと考えています。そして、行動の先読みをできるような仕掛け・仕組みの構築、さらには予測モデルなども作っていくというのがこれからの挑戦です。 おわりに 分析グループは、今後もKINTOの新しいサービス発展への貢献、トヨタファイナンシャルサービス株式会社グループへの支援を通してさらなるプレゼンスを発揮していけるように、アンテナを張りながら情報をキャッチし、日々精進していきたいと思っています。
はじめに KINTOテクノロジーズでKINTO ONE新車サブスクリプションシステムのフロント開発を担当しているカンと申します。 現在担当している、プロジェクトから簡単にご紹介させていただきます。 KINTO ONE新車サブスクリプションシステムは新アーキテクチャの適用を順次取り込んでいます。 フロントチームはNext.jsとTypeScript、デザインパータンとしてAtomic Designを採用して開発を進めていています。 本記事では実際にプロジェクトで使っている「Atomic Design」についてご紹介させていただきます。 Atomic Designとは? Atomic Designの定義 Brad Frost氏によって作成されたUI設計方法論で、パーツ単位でUIデザインを設計する手法のことです。 最小のコンポーネント単位を原子(Atom)に設定してそれを基に上位コンポーネントを作ってコードの再利用を最大化することができます。 最近のJavaScriptを用いた、Webフロント開発はフレームワーク・ライブラリとして主にVueやReactを使用して開発を行う事例が増えてきています。 Vue、Reactはコンポーネント単位で開発を進めることが特徴であるため、コンポーネント中心の設計パターンであるAtomic Designがさらに注目されています。 Atomic Designのメリット Atomic Designでは、段階別にコンポーネントを分けることでコンポーネントのリサイクル性を高める仕組みを設計することができます。 コンポーネントのリサイクル性を高めることができる。 アプリケーションと分離してコンポーネントを開発・テストできる。 (別のライブラリであるStorybook、Jest等を利用するとコンポーネント単位で確認やテストが可能です) 特定のコンポーネントにCSSが強く結合されているため、CSSを管理しやすい。 既存のコンポーネントを再利用しているため、デザインを一貫して統一できる。 Atomic Designのデメリット コンポーネントのリサイクル性を高める設計が必要になるため、事前のページ要素の確認が必要になり、構成コンポーネントが増えることによる複雑性が高くなる可能性があります。 リサイクル性の高いコンポーネント設計がないと簡単に進められない。 コンポーネントの修正が頻繁に発生すると、複雑になり、メンテナンスが困難になる可能性がある。 試行錯誤した内容・工夫したこと フロントエンド開発のコンポーネント構造 PresentationalコンポーネントとContainerコンポーネントを分ける Presentationalコンポーネントは画面の見た目を構成する役割を、ContainerコンポーネントはAPIのコールやフロント側のロジックを実行する役割を担っています。 const MypageContainer: React.FC<Props> = ({ setErrorRequest }) => { const { statusForCancellation, cancellationOfferDate, callGetCancellationStatus } = useCancellationStatus(); const { authorizationInfo, memberType } = useAuth(); useEffect(() => { ... }, []); useEffect(() => { ... }, [currentItem]); const initSet = async () => { try { // APIコールやレンダーする際に必要なデータの設定 ... } finally { ... } }; const onChangeCurrentItem = (currentSlideNumber: number) => { // イベントロジック処理 ... }; /** * 渡されたPropsや設定されたstateとflagなどでレンダー判定を行う * 判定に従うcomponentをレンダーする。 * 各コンポーネントはPresentationalコンポーネントに組み立てている */ return isLoading ? ( <Loading /> ) : ( <div className="p-mypage"> <div className="l-container"> <div className="l-content--center"> <MypageEstimateInfo data={estimateInfo} getEstimateInfo={getEstimateInfo} setErrorRequest={setErrorRequest} /> </div> <div className="l-content--center"> <MypageContactContent data={entryInfo} memberType={memberType} /> </div> <ErrorModal isOpen={errorDialogRequest.isOpen} errorDialogRequest={errorDialogRequest.error} onClose={() => setErrorDialogRequest({ ...errorDialogRequest, isOpen: false })} /> </div> </div> ); }; export default MypageContainer; const MypageContactContent: React.FC<Props> = ({ data, isContactForm = true, memberType }) => { return ( <> <div className="o-contactContent"> <ContentsHeadLine label="お問い合わせ" /> // --> atom {isContactForm && <ContactAddressWithFormLink />} // --> molecules <TypographyH4>電話でのお問い合わせ</TypographyH4> // --> atom <ContactAddressWithForAccident // --> molecules tel={CONTACT_ADDRESS_TEL.member} isShowForAccident={memberType === MEMBER_TYPE.MEMBER} /> </div> </> ); }; export { MypageContactContent }; 上記の container コンポーネントで API から取得した値やロジック処理後に設定した値に基づいて、コンポーネントの構成を判定していることが確認できます。 そして、レンダーされるコンポーネントは container から Props として渡された値を通じて presentational コンポーネントを組み立てて画面を表示するようになります。 コンポーネントグループの構成(Atoms, Molecules, Organisms, Template, Pages) Atom これ以上分解できないデフォルトのコンポーネントです。Atomを結合してMolecule、Organism単位で有用に使用できます。 Molecules 複数のAtomを結合して、独自の特性を持ちます。Moleculeの重要な点は一つ仕事をすることですね。 Organisms 前のステップよりも複雑で、サービスで表現できる明確な領域と特定のコンテキストを持ちます。Atom、Moleculeに比べてより具体的に表現されるかつ、コンテキストを持つため相対的に再使用性が低くなる特性を持っています。 Template ページを作成できるように、複数のOrganism、Moleculeで構成できます。実際のコンポーネントをレイアウトに配置し、構造化するワイヤフレームです。 Pages ユーザーが見ることができる実際のコンテンツを盛り込んでいます。Templateのインスタンスと言えます。 Storybookとのシナジー効果がある StorybookはオープンソースのUIテスターツールです。 Storybookを使えば、UIコンポーネントを作成しながらすぐに描画内容を確認できます。 Storybookライブラリと連携でUI管理がもっとしやすくなります。(UIテストが簡単にできます。) まとめ Atomic Designをプロジェクトに適用しながら、感じたことについてまとめてみました。 実際に適用するにあたって曖昧な部分があり、コンポーネントのグルーピングの範囲や分類をプロジェクトに合わせて変形させました。(Organismの範囲外に想定されているコンポーネントはFeaturesという単位で管理するとか) 最初から明確な設計がされていないと途中でコンポーネントを再設計・再作成したり、再分類するケースも発生するので注意を払う必要がありました。(階層構造にもっと多くの段階を置くとか) デザインチームと開発チームの連携やコミュニケーションがすごく重要だと思いました。(デザインから Atom、Molecule、Orgaism に細分化されて設計されなければならないため) 各コンポーネントグルーピングの基準についての認識合わせが必要があります。 設計はデザイナーが担当して開発は開発者がするため、コンポーネント単位の体系化された設計になるためには開発者とデザイナーが一緒に会議で認識を合わせる必要があります。 Atomic Designはメリットとデメリットが確実に存在するため、適用する前にチーム全体として明確に理解して設計すれば協業しやすくメンテナンスが容易なフロント開発環境を構築できると思います。 お読みいただきありがとうございます。 参考 atomic-web-design Brad Frost design systems are for user interfaces
モバイル開発グループ勉強会(黄) KINTOテクノロジーズのモバイル開発グループでAndroidアプリ開発をしている黄です。 本記事ではKINTOテクノロジーズのモバイル開発グループ勉強会についてご紹介します。 チーム文化誕生の源泉 本格的にソフトウェア開発文化について話をすると、一つ目は「共有の文化」だと思います。 複数の人と情報を共有することによる長所は様々でありますが、 一番は様々な視点を持つ人々の知識と意見が反映され、プロジェクトリスクが減少するのはもちろん、未知の情報まで得ることができます。 勉強会、必要ですか? 会社でコーディングするのも忙しいのに、技術共有の勉強会は必要ですか?このように考えがちですが、ソフトウェア業界は バブルドットコム時代を過ぎて過去20年間、ソフトウェア業界は非常に急速に高度化し、規模が拡大し、開発技術も急速に変化し複雑になりました。 それに応じて、ソフトウェア開発のスキルが増すにつれて、習得すべき知識の範囲が広がり、効率的な意思決定とコミュニケーションのために情報とスキルを共有する開発文化が非常に重要になっています。 解決したい課題 入社してからずっと一つのプロジェクトに集中しているため他分野の知識習得が難しい中、前職でもやっていた勉強会を実践することによって色々な技術の知見を身につけることができるので開催しました。 一人が勉強する時間は限られているし、情報を取得するのも一人よりメンバーでやった方が効率的であります。 実現したいこと 技術共有文化が発達すると組織メンバーの成長が速くなり、成長した組織メンバーが複数の技術コミュニティに貢献し、これにより良い人材が成長できる会社に発展するようになります。 技術共有文化の形成 私たちのモバイルチームの勉強会は、APIチーム、Androidチーム、iOSチームで構成されています。 基本的に毎週一人ずつ、自分が持っているノウハウや関心のある技術トレンド、研究中の技術について発表をします。 発表が終わると、そのスキルに対してお互いにオープンな姿勢で意見や質問をする時間があります。 また知りたい技術などがあれば掲示板に記載し、誰かノウハウを持っているメンバーが共有する形式も運営しています。 すべてのモバイルチームエンジニアは、入社後、モバイル研究会で技術共有文化を学びます。 モバイル開発グループ勉強会はこんな形 課題は自由(自分が興味がある分野) 毎週木曜日共有 参加者20人ぐらい ファシリテーターは順番制 発表の内容は、Confulenceで作成して共有 私たちは正しい方向に向かっていますか? 勉強会を1年間進行しながら、いくつかの注意点が見られました。 Problem: 非常に多くの人が参加するプロジェクト、私たちが正しく行っているかどうかを勉強会をさらに活性化する方法が必要でした。 Try : 問題が生じたら、みんなで目標を握って統合してうまくいくのかみんなで集まり、会議をします。 うちの勉強会は正しく行っているのか、互いに理解してコミュニケーションをとっているのか、皆が主導的になって、勉強会や開発文化を作っています。 社内情報共有に投資 モバイルチーム勉強会では、社内情報共有のための文書システムとしてConfluenceを使用しています。 勉強会のメンバーは、Confluenceに自分が知っている技術の内容、仕事上必要なさまざまな文書を作成して整理しています。 会社で情報を円滑に共有するためには、このような社内文書システムConfluenceの役割が非常に重要だと思います。 そのため、モバイルチームは、他のチームメンバーが何かを必要とする知識を検索するときにより効率的に文書システムを利用できるように、 着実にConfluenceに勉強会の内容を作成しています。 Tryしたいこと 外部イベント発表者のサポート モビリティサービスは日本で数少ない会社であり、技術カンファレンスやコミュニティで良い事例を発表して着実に共有しようと努力しています。 社内開発文化や社内で開発した技術、ノウハウなどを他社開発者と共有するイベントにしていきたいと思います。 終わり 多くの開発者が良い開発文化を望んでいます。 私が考える良い開発文化は Core Value: サービスの核心価値を理解しており、互いに知識を共有し、その根幹となる技術開発に積極的な文化 Dev Ops: 短い開発サイクルと障害耐性、高品質コードのためのコードレビュー/テストを共感する文化 Professional: 結果に対する責任感と仕事に対する誇り、専門性を互いに向上させることができる文化 ではないかと考えます。 実際、このような技術共有活動を着実に続けて文化を作っていくためには、少なくない努力と時間がかかりますが、 良い開発文化を持つチームは、開発段階中に遭遇した問題と解決ノウハウを共有して試行錯誤を減らし、最適なサービスを提供できるからです。 また、会社と開発組織のメンバーが絶えず成長するには、これらの活動が必ず必要であると確信しています。 モバイルチームは、さまざまな方法で外部開発者との関係を継続し続けます。 技術を共有し、一緒に成長していく開発文化、モバイルチーム勉強会で追求する価値かと思います。 一緒に勉強したい方は、いつでもご連絡をお願いいたします。
はじめに こんにちは、KINTOテクノロジーズのグローバル開発グループでPdMをしている高羽です。 今回はPdMとして様々なステークホルダーと話をしながら進めていくために効果的なコミュニケーションの取り方、またそのメソッドについて書いていきます。 これまでプロダクトに関わる仕事を長年やってきて、人とのコミュニケーションがプロジェクトの雰囲気やプロジェクトの成功にダイレクトに繋がると感じてきました。 その中で私がこれまで経験した、また今でも実践し日々トレーニングしている事を紹介します。 ピラミッド型の話し方 PdMの仕事をする中で人に何かを伝えるという事が多々あります。その中で私は相手に分かりやすく、相手がより理解できるために、あるメソッドを意識してコミュニケーションを図っています。 そのメソッドとは ピラミッド型のロジカルな話し方 です。 そのメソッドを使うかの根拠としては3つあります。 1つ目は、自分がそもそも口下手で人前で話すのがあまり得意でないので、何かしらのメソッドが必要だと感じている事です。 例えば、プレゼンやディスカッションしているとき、本当に伝わっているか心配になり、さらに上手く伝えられなくなってしまうというループに陥らないためにそのメソッドを使います。 2つ目に、PdMという仕事はいろんな人々と話す事が多々あり、必然的にいろんな意見が出てきて調整するのに大変ですが、そのメソッドを使うことで割とスムーズに進めることができます。 例えばPdMはプロダクトに関わる多くのステークホルダーと話をして、プロダクトをまとめ上げる役割ですが、ステークホルダー同士の意見が違ったり、どの意見を選択すればいいのかわからなくなりがちです。そのときにこのメソッドを使えば割とスムーズに解決することができます。 3つ目として、物事を相手に正確に伝える為には、ロジカルに話す事が必要です。 ロジカルに話すことで聞く相手の理解を高める事ができます。例えば他人に何かを説明する際に、一般的には、ロジカルに話す方が話を聞く側が理解しやすいという傾向があります。ロジカルな内容、伝え方をすることで相手に対して理解しやすい親切な伝え方を実現できるという事があります。親切な伝え方はプロジェクトの雰囲気や成功にも関係してくるので、物事の伝え方は非常に大切です。 さて、今話した内容はピラミッド型のロジカルな話し方を意識して話しました。 このメソッドは以前私が勤めていたインターネットの会社でセミナー等で直接指導して頂いた伊藤羊一さんが執筆された本「1分で話せ」[^1]に記載されています。 その一部を使って私が最初にPdMとして必要なものとしてピラミッド型のロジカルな話し方について説明しました。 では、ここでピラミッド型の伝え方について説明します。 このピラミッドの1番上にあるとおり、まず最初に「結論」を伝えます。「一番伝えたいことを最初に言う」 ということです。 次の段がその「根拠」となります。なぜその結論となるのかの理由を述べます。根拠の数は1つだけでは弱いので、3つ位が良いとされています。 そして3段目は、「たとえば」と事例を説明していきます。人は具体的な事例があればあるほど聞き手は納得する傾向にあるので、この部分は納得の肉付け(または理解の肉付け)のようなものとなります。より具体的なもの、イメージしやすいものであればベターです。 ではまたサンプルを使って説明します。 サンプルとして「プロダクト定例会議は週に1回の頻度が好ましい」という結論を、ピラミッド型で説明すると以下のようになります。 このピラミッド型のロジカルな話し方は初歩的で、ビジネスマンとして話をする上で当たり前とされている事が多いですが、実際に日々の仕事の上で使っている人はどれ位いるのでしょうか? 実際に使っている人はそれほど多くないような気がします。おそらく、自分が理解できているので相手も理解できているであろう前提で、端折って話す人も多いのではないでしょうか。また私のように単純にめんどうくさいから端折って話す人もいるでしょう。 ロジカルに話すことはトレーニングです。日常的に使っていればより精度の高いものになり、聞く側が理解しやすいコミュニケーションをとる事につながっていきます。 ピラミッド型の話し方は仮説思考でもある ロジカルシンキングの教科書には逆のことを記載されていることもあるようです。 ピラミッド型のように結論から作ると、後付けで根拠を考える事になるので「唯我独尊なロジック」になりがちです。 しかし著者は言います。「でもスピードが大事な時代ですから僕は『唯我独尊だろうが何だろうが早く作ればいいじゃん』と思っています」。 たとえそれが不完全なものであったとしても、根拠までステークホルダーに共有するだけでもその結論を見つけるプロセスになりうるからです。 まさにその通りで、例えばプロジェクトにおいては早い段階でプロジェクトメンバー全体で議論し、認識を合わせながらブラッシュアップしていくことがスピードアップにもプロジェクトの成功にも繋がります。共有して議論する上で、それぞれのメンバーの主観(意見)が出てきます。 そしてそのたくさんの主観をすり合わせていく中で、みんなにとってより良い客観的な結論を出していくことができると思うのです。 スピード重視の現場の中でこのような仮説思考(上の結論からものを考える)は効率が良く、さらに客観性を持つものであれば鬼に金棒だと感じます。 聞く力のトレーニングにも使えるピラミッド ここまで相手に話す側の視点でのピラミッドを話してきましたが、このメソッドは聞く側の力のトレーニングにもなりえます。 先日、仕事上ある人から話を理解する力を伸ばすようにとの指摘をいただきました。その時に頭にピンと思いついたのが、このピラミッドを使った理解方法でした。 ピラミッドの「結論+根拠」にあたる情報の箱を頭の中につくって、話を聞きながら、箱の中に振り分けていくのです。箱に振り分けながら話を聞いていると、何が話の幹なのか、そこに何が足りないのかということがわかりやすくなります。 引用元:伊藤羊一(著), 「1分で話せ」, SBクリエイティブ, 2018 [^1] これもトレーニングですぐに出来るわけではないので日常の会話の中でも実践しながら聞く力、理解する力を高めていけるので、日々このメソッドを話す、聞くという両面でトレーニングしています。 最後に ここまで私が日々の業務の中で使っているコミュニケーションのメソッドについて紹介しました。 皆さんも普段意識して使っているメソッドなどはありますでしょうか?もしこのピラミッド型の話し方に興味を持たれたら、ぜひ試してみてください。 より良いPdMを目指していくにはコミュニケーションスキルは非常に大事だと考えているので、私も皆さんと一緒にこれから邁進していきたいと思っています。 参考文献 [^1]: 伊藤羊一(著), 「1分で話せ」, SBクリエイティブ, 2018
こんにちは、KINTOテクノロジーズ CIO室の志田です。 普段は一緒に働く皆さまのサポート全般を担当しています! 人と人を繋ぐ架け橋のような存在を目指して、日々いろんな方とコミュニケーションを取ることを大切にしています。 こちらの記事では、社外で行っているコミュニティ活動についてご紹介いたします。 コミュニティ活動とは? KINTOテクノロジーズでは、社員同士の親睦や交流を目的として、様々なコミュニティ活動が行われています。「○○が好き」同士のコミュニティから派生するなど、小さな共通点やきっかけから生まれることが多いです! 私自身も複数(多分21個くらい…)のコミュニティに参加しており、コミュニケーションの場が広がることで、以下のメリットを感じております。 社員同士のコミュニケーションの場が広がるメリット 仕事中には発見できない相手の一面を知ることで、より深い人間関係が築ける ちょっとした気分転換と気持ちのリフレッシュができる 業務上接点の少ない人と親睦・交流を深められる 部活を通じて仲良くなり、その後業務に活きるということもよくあります コミュニケーションの活性化=相手を知る機会が増えること 社内コミュニティは企業カルチャー・社風のボトムアップにも繋がっていきます。 また、コミュニケーションは強い組織の土台であり、経営の活性化にも通ずると思います。 なんだか壮大な話になってしまいましたので、後半はカジュアルに! 普段デスクワークのため運動も大事ということで、今回は運動系コミュニティをご紹介させていただきます(^o^)/ 運動系コミュニティ紹介 🏀バスケ部🏀 \特徴/ メンバー数:25名 バスケを楽しむ気持ちがあれば大歓迎 休日にも関わらず毎回10名程参加 互いに声を掛け合いながらわきあいあいと、パス練習やシュート練習で汗を流す コミュニティーを通して普段の業務中とは異なるコミュニケーションも生まれる 会社近くの綺麗な体育館で開催 ガチ試合で躍動感のあまりピントが全く合いません(笑) ⚽️フットサル部⚽️ \特徴/ メンバー:39名 初心者も経験者も楽しめる空間(元Jリーガーの方も!?) 他事業部の方と楽しい時間を共感できるとても良い機会に 応援だけでもいい、参加したい時にフラッと参加も可能 年齢、国籍の垣根を超えてコミュニケーションを図れる(スポーツは言語の壁を超える ) 記念の私の初シュートシーン⚽(腕に落ち着きがないですね) この後華麗なゴールインしたかは・・・皆様のご想像にお任せします( ˘ω˘ ) 突然のゲリラ豪雨でも試合続行。社会人になっても青春は味わえます✨ ⛳️ゴルフ部⛳️ \特徴/ メンバー:21名 部門や役職の垣根を超えたコミュニティー 打ちっぱなし練習に行ったりラウンドを回ったり(シーズンになると月1ペースで開催) マネージャから直接OJTを受けられるので親睦を深めると共に成長へも繋がる 1日を通してコミュニケーション(合宿もあり) 大自然の中でのドライバーショットは都会では味わえない爽快感(カッキーンと響き渡る打球音を目覚ましにしたいほど) 入社してからゴルフデビューするメンバーが多いです(仲良し女子) 緊張感もピークのグリーン周りで互いに心の中で入れ~!と応援しています。 🎾テニス部🎾 \特徴/ メンバー数:20名 経験者多めなのでベテランメンバーが優しくレクチャーしてくれる 試合は初心者もベテラン勢も本気! 会社帰りに会社近くのコートを利用し開催 テニスのあとは美味しいビールをいただきます(皆さんお酒好き) 練習風景。初心者が半数なので、経験者のメンバーから打ち方など丁寧にレッスンしてくれます \( ˙-˙ \)(/ ˙-˙ )/ 丁寧な指導のお陰もあり、 皆さんキャッチアップが早い! おわりに みなさん気になる部活はありましたか?⸜( ˙-˙ )⸝ 社内のコミュニティは作り放題なので、きっとこの時間にも新たなコミュニティが存在していることでしょう。。。。! アフターシックスや休日の過ごし方は人それぞれ。 そのうちの選択肢としてKINTOテクノロジーズには社員同士の親睦を深めながらスポーツできる場があるので、がっつり身体を動かしてリフレッシュしたい!と思う方も有意義なひとときを過ごすことができます! ご入社された際はぜひご参加お待ちしています🤗
概要 グローバル開発グループの崔です。現在はGlobal KINTO Appチームのプロジェクトマネージャーを担当していますが、以前はグローバル開発Gで開発しているバックオフィスシステムのプロジェクトマネージャーを担当していました。 今回は、そのバックオフィスシステム開発チームにてソースコードを管理する際に採用した、Gitのブランチ管理方法(Gitflow)についてお話します。他のプロダクトにも流用できると思いますので、ぜひ参考にしてください。 Gitflow 注意:今回は、あくまで私の開発チームで採用したGitflowについて説明します。以下の説明ではブランチ名を「master」と書いていますが、GitHubを利用する場合、masterは古い呼び名になるため、現在ではデフォルトブランチは「main」になります。役割は全く同じです。 全体図は以下の通りです: 各ブランチの役割 master: リリース済みのソースコードを管理し、本番環境に動いているアプリケーションと同じソースバージョンを持つブランチ。リリース毎にタグが付けられます。 develop: 開発済のソースコードをまとめるブランチ。本番環境にまだリリースされていない機能を含み、常に最新機能を持ちます。一般的には回帰テスト(regression test)はこのブランチでデプロイして実施されます。 feature: 新機能、修正機能の開発用ブランチ。開発する機能や、タスクなど単位でdevelopから分岐し、結合テストが完了したら、developにマージします。一般的には1つのユーザーストーリーにつき1つfeatureブランチが作れらますが、開発チーム内である程度自由に決めることができます。 hotfix: リリース後のバグフィックス用ブランチ。masterから分岐し、バグを修正しテストを通ったら、このブランチで本番環境へデプロイします。本番作業が完了したら、該当ブランチをmaster, develop共にマージします。必要に応じて、一部のrelease, featureブランチにもマージします。 release: プロダクトリリース用のブランチ。 リリース予定の機能が反映された状態でdevelopブランチから分岐します。 このブランチを使って本番環境へデプロイします。本番作業が完了したら、masterとdevelopブランチにマージして、該当ブランチを削除します。 support: 旧バージョンをサポートし続けなければいけないプロジェクトでは support ブランチが必要です。support ブランチでは、旧バージョンの保守とリリースを行います。サポートが必要なバージョンの master ブランチのコミットから派生させ、サポートを終了するまで独立してバグフィックスやリリースを行います。 bugfix: 上記5つ標準のブランチ種別以外、bugfixというブランチ種別も定義します。 詳細は後述しますが、リリースの前にバグが見つかった場合、releaseブランチからbugfixブランチを分岐して修正対応を行います。 開発の流れ ① 初期化作業 masterからdevelopを作成します。 注意: masterとdevelopブランチはGitflowのメインブランチとして常に存在するものになり、一度作成されたら、削除できません。(GitHubで設定する) ② 新機能、修正機能の開発 1.developブランチからfeatureブランチを作成し、新機能、修正機能の開発作業を開始します。 2.featureブランチの命名規則:feature/xxxx その「xxxx」は開発チームが命名ルールを決めて良いです。 例:feature/GKLP-001、feature/refactoring、feature/sprint15 また、結合テストを行う前にpull requestを作ってソースレビューを行うため、主とするfeatureブランチからさらに作業用ブランチを作るのがお勧めです。具体的なパターンは後述します。 3.ソースコード修正のコミットは作業ブランチにて行い、終わったらPR提出し、他の人にレビューしてもらいます。 4.ソースレビューが完了したら、主とする機能ブランチにマージし、結合テストを行います。 5.結合テストが完了したら、developブランチにマージするPRを提出し、マージします。 注意:リリース計画により開発完了してもdevelopにマージしてはいけない時がありますので、 マージするタイミングを常に確認してください。 6.developにマージした後にfeatureブランチを削除します。 パターンNo.1: 機能ブランチと作業ブランチ このパターンでは、該当機能ブランチから切ったすべての作業ブランチがマージ済みになってから、結合テストが行われます。 1つの機能の開発規模が大きく、複数スプリントを跨ぐことが見込まれる場合、このパターンを採用するのが適切です。 パターンNo.2: スプリントごとのブランチと作業ブランチ このパターンでは、すべての作業ブランチがマージ済になってから結合テストを行うことに限らず、スプリント内の1機能に必要な開発分がマージ済みになったら、その機能に対して結合テストを行うのもいいです。 開発する機能が規模が小さく、1スプリント内で完了と見込まれる場合、このパターンを採用するのが適切です。 推奨しないパターン(No.3):機能と作業ブランチを同一にする このパターンでは、PR提出と結合テストのタイミングがはっきりせず、developにマージする頻度も高くなるので、QAとリリース計画を作る際にとても面倒になると想定されます。そのような無計画なやり方は推奨しません。 その代わりに、システム開発、運用の際にちゃんとリリース計画して、それに合わせてfeatureブランチの切り方を決めるのがお勧めです! ③ リリース&デプロイ developブランチからreleaseブランチを作成する。 releaseブランチにタグを付ける。(命名規則は下記のTag命名規則を参照) 本番環境へのデプロイが終了したら、releaseブランチをmasterブランチにマージする。 マージ完了後にreleaseブランチを削除する。 リリース計画 本番環境にリリースする予定がある開発については、早めにリリース計画を作りましょう。 featureブランチの運用ルール、featureの機能ブランチをdevelopにマージするタイミングはリリース計画に合わせて決められます。 一番簡単なリリース計画としては、developブランチに開発済みの機能をすべてリリースすることで、releaseブランチを作るだけで済みます。 但し、同時に複数開発チームがそれぞれ違う機能を開発し、複数回リリースするように計画する場合、先にリリースブランチを作成して、対象となる機能を1つずつマージするようにしましょう。 例えば、feature 1, 2, 3を同時に開発するが、先にfeature 1, 2をリリースし、数週後にfeature 3をリリースする場合: 上記release 1.0, 2.0のようなreleaseブランチは、一旦developから分岐して作成したら、原則的には二度とdevelopから修正ソースコードをマージしない、というルールにしましょう。 理由は、複数回リリース計画がある場合、releaseブランチ作成後、別の機能がdevelopにマージされることがあるので、さらにdevelopからマージしてしまうと、テストが終わっていないにも関わらず誤って機能がリリースされてしまいます。 以下の図のように: また、featureブランチは開発完了したらすぐにdevelopにマージする訳ではないです。一旦developにマージすると、次回のリリースに含まれますので、 featureブランチをdevelopにマージするタイミングは、リリース計画に合わせて確認しましょう。 リリースの前にバグが見つかった場合 bugfixブランチを該当releaseブランチから分岐して作りましょう。そのうえでバグを修正し、PRを提出しreleaseブランチにマージします。修正されたバグはリリース作業後、releaseブランチがmaster, developにマージされると反映されます。 以下の図の通り: ④ 本番環境のバグ修正 本番環境でバグが発生した場合、以下の手順で修正します。 まず、masterブランチからhotfixブランチを作成する。 hotfixブランチにて修正が終わったら、タグを付ける。(命名規則は下記のTag命名規則を参照) 本番環境へのデプロイが完了したら、hotfixブランチをmasterとdevelopブランチにマージする。 マージ完了後にhotfixブランチを削除する。 保守用ブランチ プロダクトのバージョンアップポリシーにより、マイクロサービス単位でバージョン管理され、各メジャーバージョンに対しては、一定の保守期間があります。ですので、該当マイクロサービスのGitHubリポジトリには、各メジャーバージョンの保守用ブランチを作る必要があります。 例えば、「自動車」というマイクロサービスは、これまでV.1, 2, 3の3つメジャーバージョンがリリースされた場合、保守用ブランチは以下のようになります: 古いメジャーバージョンにおいて、マイナーチェンジや、バグ修正などを行うには、もちろん該当する保守用ブランチから分岐するのですが、開発規模によって適当なリリース計画を作って、併せて開発用ブランチや、リリースブランチなどを決めたらいいです。 Branchコミット規則 Gitのブランチに修正ソースコードをマージするには、2種類の方法があります: ・直接コミットする方法 ・pull requestを提出してレビュー担当者が承認してからマージする方法 原則的には、pull requestを作ってからマージする方法を採用しましょう。 但し、以下のブランチには、直接コミットしてもいいです: 1.新機能、修正機能を開発する作業用featureブランチ 2.リリース直前のバグ修正用bugfixブランチ 3.リリース後バグ修正用hotfixブランチ Tag命名規則 dev環境 1.1 GitHubの上に、手動的にリリース時(非推薦) → gitブランチにタグを付ける 命名規則:x.x.x-SNAPSHOT 例:1.0.0-SNAPSHOT → ECRに登録する時、イメージのタグはタグ&時間により、自動的に付ける。 イメージのタグ名:x.x.x-SNAPSHOT_yyyyMMdd-hhmmss 例:1.0.0-SNAPSHOT-20210728-154024 1.2 JIRAチケットを利用し、自動的にリリース時(推薦) → gitブランチにタグを付けない。 → ECRに登録する時、イメージのタグは現在のブランチ&時間により、自動的に付ける。 イメージのタグ名:ブランチ名_yyyyMMdd-hhmmss 例:develop-20210728-154024 stg&prod環境 releaseブランチ又はhotfixブランチにタグを手動的に付ける。 命名規則:release.x.x.x 例:release.1.0.0 このGitブランチ戦略で解決した課題 私たちの開発チームは1年前に発足しました。当初、メンバー達の持っている開発経験とバックグラウンドがそれぞれ違うため、ソースコード管理についてかなり混乱していました。 また、同じプロジェクトに本社にいる開発者たちが組んだ「コア」チームと、オフショアにいる開発者達が組んだ「スター」チームがありました。 両チームはそれぞれ違う機能を分担して開発しますが、やはり同じソースファイルを同時に修正することが避けられません。 よって、以下の問題が発生しました: ソースコードのコンフリクトが発生する時、誤って他人の修正分を削除してしまう 古いソースコードをベースに機能開発してしまう 段階的なリリースが実現できない 我々はシステム開発をする上で、チームワークを大事にしています。全員に認められ、そして実行できるルールが不可欠です。 Gitflowはまさにこういうものです。 それぞれ違う機能の開発を担当する人は、それぞれ違うfeatureブランチを作成して、互いに影響しないようにソースを修正できるでしょう。 また、スプリントの開発周期に合わせて最新ソースコードをdevelopブランチに保持することで、次の開発周期がスタートする際に、みんなが最新ソースコードをベースに各自の担当分を展開できるでしょう。 さらに、「リリース計画」ごとにreleaseブランチを作成することで、開発される機能を少しずつリリースすることが実現できますので、開発者の負担を減らすことができ、プロジェクト自身のリスクも減らせるでしょう! このGitブランチ戦略を導入したことで、私が当初リードしていたバックオフィスシステムの開発チームは混乱期を乗り越えて、安定的に機能開発・リリースすることができました!今後、アプリ開発のプロジェクトマネージャーを担当することになったのですが、アプリ開発でも似たような課題に直面するときに、この経験を参考にして、また改めてチャレンジしてみたいと思っています。皆さんもご自分自身のプロダクト開発に参考してみてはいかがでしょうか?