TECH PLAY

KINTOテクノロジーズ

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

936

KotlinエンジニアがFlutterに入門して1ヶ月でWebアプリケーションを作った話 こんにちは。Woven Payment Solution 開発グループの大杉です。 私たちのチームは、 Woven by Toyota において Toyota Woven City で使われる決済システムの開発を行っていて、普段はKotlin/Ktorによるサーバーサイドの開発をしています。 私たちは、Woven Cityを一緒に作っていく協力企業や社内のビジネスチームと協力してPoC (Proof of Concept, 概念実証)を繰り返し、 決済システムの機能を拡充させていっています。そして先日、第一弾として実店舗での小売販売を想定した決済システムのPoCを実施しました。 この記事では、私たちがPoCの中でクライアントアプリの開発にFlutterを採用するに至った経緯を紹介したいと思います。 はじめに PoCで小売販売をするために、決済システムの他に次のような店舗運営向けの機能開発も行いました。 店舗で販売する商品の管理 POSレジ 商品のスキャン ショッピングカート機能 店舗への売上報告と支払いの管理 棚卸し 特に、数万件に及ぶ商品情報の定期更新や月末の締日に合わせた売上報告、棚卸しを実施するためには、決済APIだけではなく、技術者ではない店舗担当者が作業できるGUIアプリケーションが必要でした。これが、普段はサーバーサイドの開発を行っている私たちが急遽クライアントアプリを開発に着手するに至ったきっかけです。 言語・フレームワークの選定 クライアントアプリを開発するに当たり、WebだけでなくiOS/Androidでもアプリ開発ができるクロスプラットフォームのフレームワークに候補を絞りました。 s 言語 / フレームワーク 選定理由 Dart / Flutter , Flutter on the web - 最近注目されているトレンドな技術である - 社内のモバイルアプリ開発チームでも採用されているため、チーム間の親和性が高い TypeScript / Expo (React Native) , Expo for web - Web開発に関しては最も成熟した技術の一つであるReactで開発できる - Reactの開発経験のあるチームメンバーが多く、キャッチアップに時間がかからない Kotlin / Compose Multiplatform , Compose for web - 採用事例がまだ少ないため、チャレンジングな開発ができる - 開発経験のあるチームメンバーはいないが、Kotlinを書ける人にとっては実装しやすそう 技術検証 言語・フレームワークを選定するために、クライアントアプリを開発する上で大事な要素である状態管理と画面遷移を組み合わせたWebアプリを作成して技術検証を行いました。 作成したアプリは、 + - ボタンを押下すると数値がカウントアップして、左の画面(Home Page)の next ボタンを押下すると右の画面(Detail Page)に遷移して数値を表示するというすごく単純なものです。 それぞれの言語・フレームワークの組み合わせについて、UIコンポーネントの実装方法、パフォーマンス、ライブラリ・ドキュメント・コミュニティサポートの点で開発体験の違いを見てみました。 UIコンポーネントの実装方法 まずは、上図右のDetail Pageのコードを例にFlutter on the web, Expo for web, Compose for webを比較してみました。 Dart / Flutter on the web DOMではなくオブジェクト指向なコンポーネントでUIを実装できるので、とても直感的だと感じています モバイルとWebでほとんど同じコードを利用できます スタイリングにはMaterial Designがデフォルトで適用されるので、一長一短ありますが、エンジニアがデザインもする必要がある状況ではとても助かります Canvaskitでレンダリングする場合、ほとんど同じ見た目のUIを描画することができます class DetailPage extends StatelessWidget { const DetailPage({super.key}); @override Widget build(BuildContext context) { final args = ModalRoute.of(context)!.settings.arguments as DetailPageArguments; return Scaffold( appBar: AppBar( title: const Text("Flutter Demo at Detail Page"), ), body: Center( child: ConstrainedBox( constraints: const BoxConstraints(minWidth: 120), child: Center( child: Text( args.value.toString(), style: const TextStyle(fontSize: 72), ), ), ), ), ); } } TypeScript / Expo Flutter同様に、DOMではなくオブジェクト指向なコンポーネントでUIを実装できるので、とても直感的だと感じています ただし、フレームワークが用意してくれるコンポーネントは最小限であるため、実装が必要です モバイルとWebでほとんど同じコードを利用できます スタイリングは、StyleSheetというCSSに似た記法でスタイリングしますが、適用されるスコープが限定されるためCSSほど辛くないです このサンプルの画面遷移の実装には react-navigation を使用しています const DetailPage: React.FC = () => { // from react-navigation const route = useRoute<RouteProp<RootStackParamList, 'Detail'>>(); return ( <View> <Header title={'Expo Demo at Detail Page'} /> <CenterLayout> <Counter value={route.params.value}/> </CenterLayout> </View> ); } const Header : React.FC<{title: String}> = (props) => { const {title} = props; return ( <View style={styles.header}> <Text style={styles.title}> {title} </Text> </View> ) } const CenterLayout: React.FC<{children: React.ReactNode}> = (props) => { const {children} = props; return ( <View style={styles.layout}> {children} </View> ) } const Counter: React.FC<{value: number}> = (props) => { const {value} = props; return ( <View style={styles.counterLayout}> <Text style={styles.counterLabel}>{value}</Text> </View> ) } const styles = StyleSheet.create({ header: { position: "absolute", top: 0, left: 0, width: '100%', backgroundColor: '#20232A', padding: '24px 0', }, title: { color: '#61dafb', textAlign: 'center', }, layout: { display: 'flex', flexDirection: 'row', justifyContent: 'center', alignItems: 'center', height: '100vh', }, counterLayout: { minWidth: 120, textAlign: 'center' }, counterLabel: { fontSize: 72, } }); Kotlin / Compose for web モバイルやデスクトップで扱うCompose UIではなく、HTMLのDOMをラッパーしたようなWeb専用のコンポーネントでUIを実装します モバイルとWebでコードの流用はできません スタイリングは、CSSで実装する必要があります コンポーネントに対してCSSのようなプロパティをコンポーネントに直接定義するか、StyleSheetオブジェクトとして切り出して実装します このサンプルの画面遷移の実装にはCompose multiplatformのWebとデスクトップ向けの routing-compose というライブラリを使用しています @Composable fun DetailPage(router: Router, params: Map<String, List<String>>?) { Div { components.Header(title = "Compose for web Demo at Detail Page") CenterLayout { params?.get("value")?.get(0)?.let { Counter(it.toInt()) } } } } @Composable fun Header(title: String) { H1(attrs = { style { position(Position.Fixed) top(0.px) left(0.px) paddingTop(24.px) paddingBottom(24.px) backgroundColor(Color("#7F52FF")) color(Color("#E8F0FE")) textAlign("center") width(100.percent) } }) { Text(title) } } @Composable fun CenterLayout(content: @Composable () -> Unit) { Div(attrs = { style { display(DisplayStyle.Flex) flexDirection(FlexDirection.Row) justifyContent(JustifyContent.Center) alignItems(AlignItems.Center) height(100.vh) } }) { content() } } @Composable fun Counter(value: Int) { Span(attrs = { style { minWidth(120.px) textAlign("center") fontSize(24.px) } }) { Text(value.toString()) } } パフォーマンス 次に、それぞれの言語・フレームワークで作ったサンプルアプリに対してビルド時間とバンドルサイズの比較をしました。 ビルド時の最適化オプションはデフォルトのものを使用しています。 検証環境は、MacBook Pro 2021 (CPU: M1 Pro, Memory: 32GB)です。 言語 / フレームワーク ビルド条件 ビルド時間 バンドルサイズ Dart / Flutter on the web - Flutter v3.7.7 - Dart v2.19.2 14s 1.7MB (CanvasKit) 1.3MB (Html) TypeScript / Expo for web - TypeScript v4.9.4 - Expo v48.0.11 10s 500KB Kotlin / Compose for web - Kotlin v1.8.10 9s 350KB Flutterで同機能を提供するために必要なバンドルサイズはReactのおよそ10倍であり、初回レンダリングにはかなり時間がかかってしまう可能性が高いことがわかります。 Flutterで生成されるJSコードについては、ビルドオプションに --dump-info を追加することで詳細を確認することができ、 主に、dartとFlutterのフレームワーク部分のコードが含まれていました。 ライブラリ・ドキュメント・コミュニティサポート 最後に、それぞれの言語・フレームワークについてライブラリ・ドキュメント・コミュニティサポートなどの情報をまとめました。 言語 / フレームワーク ライブラリ ドキュメント・コミュニティサポート Dart / Flutter on the web Flutter packages でFlutterで利用可能なライブラリを検索できる。 その中でも Flutter Favorite が付いているものは公式が人気があり使いやすいライブラリであることを提示してくれている。 公式ドキュメント や 動画 が充実しており、また、公式が状態管理などで推奨するライブラリや設計指針を提示してくれている。 TypeScript / Expo for web 基本的なライブラリはかなり充実しており、デファクトスタンダードなものも検索すると見つけやすい。各ライブラリのメンテナンスはコミュニティに依存している部分が大きいため、よく考えて選定する必要がある。 基本的な実装については Reactの公式ドキュメント や Expoの公式ドキュメント が充実している。ライブラリを含めた有効な設計指針については、 ネット上のReactの議論を参考にすれば大丈夫そう。 Kotlin / Compose for web JVMのライブラリ自体はかなり多い。ただし、AndroidやCompose UI関連のライブラリはCompose for webでは利用できない場合が多い。 ドキュメントはあまりないため、 GitHubリポジトリ を探すか、コミュニティの Slackチャンネル で情報を探す必要がある。 そして、Flutterを採用へ 上述した技術検証を元に、私たちはFlutterをPoCにおけるクライアントアプリ開発の技術スタックとして採用しました。 理由は以下の3点です。 クライアントアプリ開発に不慣れなメンバーであっても、ドキュメントや参考情報が充実しており、本業のサーバーサイド開発の工数を圧迫しにくいと予想 フレームワークの開発が活発でありながらもメンテナンス体制が整っているため、バージョンアップやライブラリの導入が容易であること PoCという特性上、通信環境が安定した環境で実行されるアプリであるため、パフォーマンスの欠点はあまり問題にならないこと また、後付けの理由になってしまいますが、Flutterだけでは解決できない課題に遭遇した際にDart上でJSを実行できることもとても心強かったです。 私たちのシステムではKeycloakを認証基盤に使用しており、Keycloakの公式からFlutter用のKeycloakのクライアント向けライブラリは提供されていないため、JS用ライブラリをDartで動作させて認証を行っています。 おわりに この記事では、PoCで使用するクライアントアプリの開発でFlutterを採用した経緯について紹介させていただきました。 現在、私たちはサーバーサイド開発と並行してクライアントアプリの開発も行っています。 今後さらに技術的な知見を深められたらこのブログで情報をアップデートしていきたいと思います。
アバター
こんにちは。 KINTO テクノロジーズの DBRE チーム所属の p2sk です。 DBRE(Database Reliability Engineering)チームでは、横断組織としてデータベースに関する課題解決や、組織のアジリティとガバナンスのバランスを取るためのプラットフォーム開発などを行なっております。DBRE は比較的新しい概念で、DBRE という組織がある会社も少なく、あったとしても取り組んでいる内容や考え方が異なるような、発展途上の非常に面白い領域です。 弊社における DBRE の取り組み例としては、あわっち( @_awache )による DBRE ガードレール構想の実現に向けた取り組みについて というテックブログや、 今年の AWS Summit の登壇内容 を是非ご覧ください。 今回の記事は、データベースに関する課題解決の事例として「Aurora MySQL でレコードが存在するのに SELECT すると Empty set が返ってくる」という不思議な事象を調査した話をご紹介します。 発生した事象 プロダクトの開発者から、「踏み台サーバーからデータベースに対して特定のクエリを実行すると挙動がおかしくなる」という問い合わせを受けました。データベースは Aurora MySQL 2.07.2 (MySQL 5.7.12)を使っており、 MySQL クライアントのバージョンは 5.7.38 for Linux (x86_64) でした。その時に共有してもらった挙動のイメージは以下の画像の通りです。 画像の中にある通り、レコードが存在しているテーブルに対して select * from t1; という全レコードを取得するクエリを実行したところ、 Empty set が返ってきます。また、その直後にクエリを実行すると ERROR 2013 (HY000): Lost connection to MySQL server during query  というエラーが返ってきます。さらにその後は ERROR 2006 (HY000): MySQL server has gone away No connection. Trying to reconnect... というエラーが返ってきました。それ以降は、以下の画像のように Empty set / ERROR 2013 / ERROR 2006 のループになります。 一方で、 select * from t1 limit 1; というクエリの場合は、期待通り 1 レコードが返ってきました。 この時点では原因について全く思い当たる節が無く、また別の環境で再現する方法も分からない状態でした。幸い、事象が再現するテーブルが複数あったので、様々な条件で再現の有無や事象解消の有無について調査を実施しました。 調査の実施 再現の有無を調査 以下のクエリ達は、事象が発生するクエリと取得対象のデータは同じ(全レコード、全カラム)ですが、全て問題なく結果が返ってきました。 select c1, c2 from t1; -- 全カラム指定 SELECT * FROM t1; -- 予約語を大文字にして実行 Select * from t1; -- 最初の1文字だけ大文字にして実行 他にも、以下のような確認を実施しました。 ライターインスタンスでは再現するが、リーダーインスタンスでは再現しない ライターインスタンスでも、同一データベース内で再現するテーブルと再現しないテーブルがある MySQL クライアントを 8.0 系に変えると再現しない テーブルを構成するカラムに特殊なものはなく、入っているデータもおかしい点は無さそう 事象解消の有無を調査 続いて、データやメタデータを変更することで事象が解消するかを調査しました。結果は以下の通りです。 再現するテーブルを対象に analyze table を実行しても、解消しなかった テーブルを新規作成し、ダンプファイルから同じデータを投入した場合、解消した 再現するテーブルのダンプファイルを作成後、 DROP & CREATE で同名のテーブルを再作成し、ダンプファイルからデータを投入した場合、解消した 再現するテーブルのレコードを全件 DELETE 後にダンプファイルから同じデータを投入した場合、解消した Aurora のアーキテクチャを踏まえた切り分けの実施 ここまでの調査ですと、テーブル再作成で解消するためデータに問題があるようにも見えますし、MySQL クライアントを 8.0 系に変えて解消するためデータには問題がないようにも見えます。そこで、Aurora のアーキテクチャを改めて確認しました。 こちらの AWS 公式資料 によると、以下のことが確認できます。 Aurora のコンピュートレイヤとストレージレイヤは完全に分離されている ライターインスタンスもリーダーインスタンスも同一のクラスターボリュームを参照する 最もわかりやすい図を下図に引用しました。 出典: Amazon Aurora アーキテクチャ概要 このアーキテクチャを踏まえて、コンピュートレイヤとストレージレイヤのどちらが関係していそうかを切り分けるために、 Aurora クローン を作成して再現の有無を確認しました。 クローンを作成しても、データはコピーされずにクローン元と同一のストレージを参照し続けます。 下図のように、どちらかのクラスタで新たなデータ更新が行われた時だけ新しいデータページが作成されますが、更新が行われない限り、ストレージレイヤに変更はありません。 出典: Aurora クローン作成の仕組み 作成したクローンに接続して同様のクエリを実行したところ、事象が再現しました。したがって、ストレージレイヤは今回の問題とは無関係な可能性が高いと判断しました。ライターインスタンスでは再現するが、リーダーインスタンスでは再現しないという結果もこの判断を補強してくれそうです。 ということで、Aurora のコンピュートレイヤが今回の事象に関連していると推定しました。コンピュートレイヤが保持している何らかのデータに関連があると考え、改めてアーキテクチャ図を確認したところキャッシュ機構の関連性を疑いました。 現在の設定がどうなっているかを以下のクエリで確認したところ、クエリキャッシュは有効化されていました。 select @@session.query_cache_type; そこで、以下のようにクエリキャッシュをセッションレベルで無効化したときに事象が再現するか確認しました。 set session query_cache_type = 1; -- クエリキャッシュON select @@session.query_cache_type; -- 確認 SELECT * FROM t1; -- 再現しなかった select * from t1; -- 再現した set session query_cache_type = 0; -- クエリキャッシュOFF select @@session.query_cache_type; -- 確認 SELECT * FROM t1; -- 再現しなかった select * from t1; -- 再現しなかった(!) ということで、クエリキャッシュを無効化すると事象が再現しなくなることが確認できました。MySQL 8.0 ではクエリキャッシュは廃止されているため、8.0 系のクライアントを使うと事象が再現しなかったという結果も納得できます。 また、クエリキャッシュを RESET すると、クエリキャッシュを ON にしていても再現しなくなりました。ちなみに FLUSH QUERY CACHE だと引き続き再現しました。 RESET でキャッシュを削除してあげる必要があるようです。 set session query_cache_type = 1; -- クエリキャッシュON select @@session.query_cache_type; -- 確認 RESET QUERY CACHE; -- クエリキャッシュのリセット SELECT * FROM t1; -- 再現しなかった select * from t1; -- 再現しなかった これまでの結果から、今回の事象はクエリキャッシュに関連していることが分かりました。 似た事例の調査 原因の切り分けが進んだところで、似た事例が報告されていないかを調査しました。その結果、 こちら のバグレポートに辿り着きました。内容としてはバグレポートのタイトル「Query caching with two different clients causes errors」にある通り、2種類のバージョンの MySQL クライアントを使うと、片方のバージョンでキャッシュされた内容にもう片方からもアクセスしようとしてエラーになる、というものです。 こちらのレポートをもとに事象を再現できるか試したところ、バージョン 5.6.35 と 5.7.38 を使った場合に再現することができました。再現手順を記事末尾の appendix に記載しておりますので、ご興味のある方はお試しください。(appendix では 5.7.41 を使っていますが、再現します。) 異なるバージョンの MySQL クライアントを使用した可能性について、担当者に確認したところ「踏み台サーバーを新しく構築したタイミングで事象が発生するようになった」ということが分かりました。以前使っていた踏み台サーバーの MySQL クライアントまでは分からなかったので断定はできませんが、バグレポートの内容と起きている事象は同じです。したがって、今回の原因は異なる MySQL クライアントで select * from t1 というクエリが実行されてキャッシュされたことで、エラーにつながった可能性が非常に高いと判断しました。 対応策の検討 事象が発生してしまった場合は、 RESET QUERY CACHE を実行するのが最も簡単な解消方法ですが、そもそも事象が発生しなくなる方法についても検討しました。 試しに Aurora MySQL のバージョンを 2.07.2 からバージョンアップした時の発生有無について調査しました。その結果、2.07.x 系の最新パッチバージョンである 2.07.9 だと引き続き事象が再現しました。しかし、マイナーバージョンも上げて 2.11.x 系で試したところ、 2.11.1 でも 2.11.2 でも事象が発生しなくなりました。マイナーバージョンアップに伴って、クエリキャッシュに関連した何らかの修正が入った可能性があります。したがって予防策としては Aurora のバージョンを 2.11.x 系にバージョンアップすると良さそうです。 まとめ 本記事では、 DBRE 活動の一環として行なっているデータベースに関する課題解決の事例として「Aurora MySQL でレコードが存在するのに SELECT すると Empty set が返ってくる」という不思議な事象を調査した話をご紹介しました。原因は、クエリキャッシュが有効化された Aurora MySQL 2.07.x 系に対して異なる MySQL クライアントから同じクエリを実行すると結果がおかしくなるという MySQL のバグ によるものでした。事象発生時の解消方法としては(一時的なパフォーマンス劣化に注意は必要なものの) RESET QUERY CACHE を実行するのが最も簡単な方法です。また、Aurora 2.11.x 系では事象の発生が確認できなかったため、Aurora のバージョンアップを実施するのが最も確実な対応かと思います。もしくは 2024 年 10 月 31 日には Aurora 2 系がサポート終了となるため、早期に Aurora 3 系へとバージョンアップするのも手かと思います。 そもそもかなりレアケースですので、あまり気にする必要はないかもしれませんが、どなたかの参考になれば幸いです。なお、今回の調査はいろいろな方のご協力によって行うことができました。 KINTO テクノロジーズ DBRE チームでは一緒に働いてくれる仲間を絶賛募集中です!カジュアルな面談も歓迎ですので、 少しでも興味を持っていただけた方はお気軽に Twitter DM 等でご連絡ください。併せて、 弊社の採用 Twitter もよろしければフォローお願いします! Appendix : 再現手順 以下の再現手順は、OS が Amazon Linux 2 の踏み台サーバー上で動作することを確認しています。また、Aurora MySQL のバージョンは 2.07.x 系であることを前提とします。(全てのパッチバージョンで再現するかは確認しておりませんが、少なくとも最新のパッチバージョン 2.07.9 での再現は確認しています。) まず、踏み台サーバーに接続し、MySQL 5.6 系のクライアント(5.6.35)をインストールします。 sudo mkdir -pvm 2755 /usr/local/mysql-clients-56; sudo curl -LO https://dev.mysql.com/get/Downloads/MySQL-5.6/mysql-5.6.35-linux-glibc2.5-x86_64.tar.gz; sudo tar -zxvf mysql-5.6.35-linux-glibc2.5-x86_64.tar.gz -C /usr/local/mysql-clients-56/; cd /usr/local/mysql-clients-56/; sudo mv -v mysql-5.6.35-linux-glibc2.5-x86_64 mysql56; sudo ln -s /usr/local/mysql-clients-56/mysql56/bin/mysql /usr/local/bin/mysql56 次に、MySQL 5.7 系のクライアント(5.7.41)をインストールします。 sudo mkdir -pvm 2755 /usr/local/mysql-clients-57; sudo curl -LO https://dev.mysql.com/get/Downloads/MySQL-5.7/mysql-5.7.41-linux-glibc2.12-x86_64.tar.gz; sudo tar -zxvf mysql-5.7.41-linux-glibc2.12-x86_64.tar.gz -C /usr/local/mysql-clients-57/; cd /usr/local/mysql-clients-57/; sudo mv -v mysql-5.7.41-linux-glibc2.12-x86_64 mysql57; sudo ln -s /usr/local/mysql-clients-57/mysql57/bin/mysql /usr/local/bin/mysql57 そのまま MySQL56 でデータベースに接続します。 mysql56 -h xxx -u xxx -p サンプルのデータベースとテーブルを作成し、データを INSERT します。 create database d1; use d1; create table t1 (c1 int, c2 int); insert into t1 (c1, c2) values (1, 1); insert into t1 (c1, c2) values (2, 2); insert into t1 (c1, c2) values (3, 3); クエリキャッシュをセッションレベルで有効化し、クエリを発行してキャッシュさせます。 set session query_cache_type = 1; select * from t1; 次に、別のウインドウから同一の踏み台サーバーに接続し、 MySQL57 でデータベースに接続します。 mysql57 -h xxx -u xxx -p クエリキャッシュをセッションレベルで有効化します。 use d1; set session query_cache_type = 1; MySQL56 から実行したクエリと 1 文字違いのクエリを実行すると、正常にデータが返ってきます。 Select * from t1; MySQL56 から実行したクエリと同じクエリを実行すると、 Empty set が返ってきます。 select * from t1; これで事象が再現できました。 解消するためには、クエリキャッシュをリセットします。 RESET QUERY CACHE;
アバター
Hi, KINTOテクノロジーズのFloです! 本日の記事では、先日社内で初めて開催したハッカソンイベント、Innovation Daysで優勝したチームにインタビューをします!🥳 背景 Global KINTO Innovation Daysは、2022/12/14~21にかけて開催されたハッカソンのようなチームビルディングイベントです。グローバル開発Gのメンバー30人が6チームに分かれ、チームワークを高めつつ新しいアイデアを生み出すことを目的にしていました。 前回・前々回の記事で 本イベントの準備 についてや 本イベント当日の様子 についても読めますので、ぜひそちらもご参照ください。 はじめに まずはじめに、優勝チームである United Nations チームを紹介します。 (左から時計回りに)アンワル、モジ、クリス、アンジェラ、ドゥック アンワル :自動車業界で5年間事業開発マネージャーを務め、現在はKINTOテクノロジーズのグローバルビジネス開発に携わる。 モジ :UI/UXプロダクトデザイナー。主にユーザーファーストのプロダクト開発を重視し、クロスファンクショナルに他のチームと連携しながら、ビジネスゴールとユーザーニーズの双方を満たすようなデザインを行う。 クリス :フルスタックエンジニアで、KINTOテクノロジーズでは主にフロントエンドエンジニアとして従事。 アンジェラ :DevOpsチームに所属。5年以上のクラウド経験とシステムアーキテクチャ経験2.5年以上を持つクラウドエンジニア。15年以上のシステム開発経験がある。 ドゥック :IDプラットフォームチームに所属するバックエンドエンジニア。フロントエンドの開発経験もある。 インタビュー開始! Q01. Innovation Daysに参加した理由は何ですか? アンワル :エンジニアとの協力し、共に学びながら、会社に取って意味のあるモノを開発するという、滅多にない機会だと思ったからです。 モジ :デザインの課題に触れつつ、モビリティの新しいソリューションの開発に貢献したいと思ったからです。 クリス :KINTOに対して何か新しいものを作れる良い機会でしたし、新しい技術やアイデアを試してみたいという気持ちもありました。 ドゥック :このようなイベントに参加するのは初めてだったので、面白そうだと思いました。 アンジェラ :他チームメンバーとのコミュニケーションを増やしたかったのと、新しいことに挑戦してみたいという気持ちから参加しました。 Q02. チーム名”United Nations”の由来は? クリス :国籍やバックグラウンドなどバラバラなチームだったので「United Nations」と名付けました。(他のチームもほとんど同じ感じですが😅) Innovation Daysでマネージャーからフィードバックを受ける様子 Q03. 技術スタックとメンバーの役割・責任について教えてください。 クリス :運営チームがメンバーの経歴などを見ながら、それぞれのチームに分けたので、バックエンドやインフラなどの役割はほぼそれに従いました。私はフロントエンド開発を担当し、Nuxt.jsを利用しました。グローバル開発Gで利用していることと、私自身がこのメタフレームワーク経験が豊富だったためです。 ドゥック :私はバックエンドを担当しました。Pythonは普段から最も一般的な言語で、また私にとっても使いやすいので、これを使うことにしました。 アンジェラ :私はプラットフォーム担当でした。KINTO社内で使われているもので、プロジェクト関連の機能が備わっていたので、環境はAWSを選定しました。 モジ :最終ピッチ時のプレゼンデッキを担当しました。2日間しかなかったので、我々はモックアップのデザインより、開発プロセスやプレゼンデッキを優先することにし、限られた時間の中で調査結果やアイデアのゴールを効果的に伝えるため、Figmaを使ってプレゼンデッキを作成しました。また、バリュープロポジションを作るのはMiroボードで共同作業し、ユーザーとビジネス両方のニーズをさまざまな角度から総合的に検討することができました。 アンワル :私はビジネスモデル開発がメイン担当で、このハッカソンで企画するプロジェクトが市場ニーズに合うか、実現可能なビジネスモデルか、さらにステークホルダーに対して効果的なアプローチができるかなどを検討しました。チームメンバーが自由に意見を出し合い、それぞれ期待された役割以上のものを手際良くこなしたことがポイントだったと思います。 Q04. チームには英語と日本語を話す人がいますが、コミュニケーションはどうでしたか? アンワル :ほとんどのメンバーが英語と日本語の両方を話せるので、基本はどちらかで話しましたが、わからないことがあれば都度、誰かが通訳して全員が同じ理解でいることを確認しました。 モジ・アンジェラ :そうですね、わからない部分があれば逐一声を上げ、チームの誰かが説明してくれました。 Q05. 企画・設計フェーズはどんな感じでしたか? アンワル :とにかく楽しかったです。本番前、4回しかミーティングできませんでしたが、メンバーそれぞれ違うアイデアを出し合ってスムーズに進めることができました。 モジ :時間の制約を考えると、そこまで深くアイデアを練ることはなかったですね。急いで行うタスクが多く、メンバーがヘルプを求めた際には他の誰かがサポートするようにしていました。 クリス :本番前のチームミーティングは4回ほどしかありませんでした。他のチームではどうだったかわからないが、私たちには足りなかったです。 アンジェラ :それが逆によかったのかもしれません。 クリス :そうですね、限られた時間を最大限に活用しました。 アンワル :良かったのは、全員がいろいろなアイデアを出してくれて、それをひとつにまとめようとしたことです。例えば、モジはkudos機能(Likeのようなもの)、アンジェラはGoogle プラットフォームについてアイデアを持っていて、それをどう組み合わせるかを議論しましたね。こうやって私たちのプロジェクトは始まりました。 ドゥック :そうそう、考えたアイデアをすべて実現したかったんです。 クリス :最初は多数決でアイデアを選定するつもりだったのですが、1つに絞るのはなかなか難しかったので、アイデアをいくつか組み合わせることにしました。 Q06. 今回のイベントで最も驚いたことは何ですか? アンワル :メンバー同士のコミュニケーション能力ですね。私たちのチームダイナミクスは、とても良いバランスだったと思います。 クリス :私は、みんなのモチベーションと協調性の高さに驚きました。モジも言っていた通り、一人ひとりが主体性をもって、それぞれのタスクに取り組んだことが効いたと思います。 アンジェラ :普段は一緒に仕事をしないメンバーだったので、私としてはチームメンバーそれぞれの強みを持っていたことにびっくりしました。 ドゥック :メンバーのみんながとてもエネルギッシュだったので、本当に驚きました。普段のチームはバックエンドがメインになるので、雰囲気が違います。 Innovation Daysにてグループマネージャーにアイデアをプレゼンしている様子 Q07. 今回、一番大変だったのは何でしょう?また、それをどう越えましたか? クリス :作りたいものがたくさんあったので、2日間という短時間で何を開発するか絞るのが大変でした。実際にどの機能を開発するか、また、プレゼンの際にはビジュアル的にどの部分を見せるか、などの優先順位をつけなければいけませんでした。 モジ :ターゲットが若年層で、近年のソーシャルメディアを活用したコミュニケーションを念頭に、考えているビジョンを実現できる技術があることもアピールしたかったです。 クリス :そうですね、内部ではやったことのない技術を導入したかったので、それもチャレンジでした。 アンジェラ :そのために、チームからフィードバックをもらいつつ技術的な分析を行い、自分の持つ知識と組み合わせて、機能的に満足できるまでテストを繰り返しました。 クリス :あとは、本業でアサインされているチームやプロジェクトがバラバラなので、全員が集まって議論する時間がなかったことも大変でした。 アンジェラ :時間が限られているので、次の会議までに各自が行うアクションアイテムを整理し、次の会議でフォローする、という流れにしていました。 ドゥック :技術以外の部分でお話すると、Innovation Daysは神保町オフィスで開催されたのですが、会議室の数に限りがあり、広さもまちまちでした。運営チームは会議室をくじ引きで割り当てましたが、我々チームは一番狭い部屋になってしまいました。 (ごめんね😅💦 by 運営チーム) モジ :そう、一番狭い部屋だったのですが、こればっかりはどうしようもなかったですね。笑 アンワル :そうですね、お互いにサポートし合って、できるだけ心地よく過ごせるよう、最善を尽くしたつもりです。笑 クリス :もうひとつ、大きなチャレンジは、アンワルが残念ながら一身上の都合でイベントの第2部まで参加できないことでした。オフラインでは一緒にいられませんでしたが、都度Slackでステータス共有したり、常にアンワルをループに入れ続けることでなんとか乗り越えました。 Q08. 優勝する自信はありましたか? アンワル :そうですね、おそらくイベント参加者全員が同じような勝利のスピリットを持って参加していたと思います。私たちは 「We win as a team or we lose as a team (チームとして勝つか、チームとして負けるか)」 というモットーを掲げ、それを貫いてきました。 モジ :勝つことよりも、ネットワークを作り、コミュニケーションをとり、楽しみながら、既成概念にとらわれないクリエイティブな発想に挑戦することに重点を置いていたと思います。 ドゥック :他のチームのアイデアもとてもよかったと思いますが、私たちのアイデアには誇りを持っています。 チームランチ! Q09. このような短期間のアイデア検討と開発で学んだこと:今回の経験から今後活かしたいことはありますか? クリス :全体として大きく変えることはないですが、*"Keep, Problem, Try Retrospective"* と、振り返りは行いました。当時は時間が足りないことに問題があると思いましたが、これは計画の課題でもあります。おそらく、時間が足りないことが課題なのではなく、限られた時間をうまく使えるようにもっと事前に計画を立てることが必要なんだと思います。 Q10. ハッカソンなどのこういったイベントは、PoC的なものですが、実際に日々取り組んでいるプロジェクトとはどういった違いがありますか? モジ :先ほども言った通り、時間的な制約やリソースが違いました。 アンワル :私にとっては、普段関わらなかったメンバーのバックグランドを知って、有意義な意見交換ができたことですね。普段とは違って新鮮に感じたし、ワークしやすかったです。 アンジェラ :上下関係がなく、全員がフラットな関係でそれぞれが得意とすることができたことが要因だと思います。 クリス :アンワルと自分がリーダーの役目を途中で交代したのですが、正直このロールは必要なかったですね。 アンワル :そうですね、必要次第でみんながリードしていました。みんながリーダーです! クリス :そうですね、共和国みたいな感じで。 Q11. どういったポイントが優勝につながったと思いますか? モジ :協調性ですね、間違いなく!メンバー全員がとても個々のタスクをうまくこなし、互いに協力し合ったことが結果に繋がったと思います。クリスは「selfless extra effort(無償の努力)」という表現を使いましたが、これは単に仕事を引き受けるだけでなく、何がベストかを研究し、アイデアや意見をチームと共有するという精神をうまく表してくれたと思います。 クリス :我々はWebサービスを開発したのですが、それぞれの得意分野がうまく活かせました。あと、メンバー一人ひとりが自分の意見や考えを積極的に発言することに加え、相手の意見にも耳を傾け、尊重していたことも挙げられると思います。会議中は意見が一致することもあれば反発することもありましたが、最終的には必ず結論を出して、前に進ことができました。 アンジェラ :そうですね、メンバーに対する尊敬と信頼が大きな勝因でした。普段は違うチームなので、準備期間中に違うトピックで会議するのですが、次の会議までのTo Doを決めた際にはそれぞれがそのタスクをやってきてくれると信じていました。 ドゥック :また、KINTOテクノロジーズでは今までなかったアイデアだったので、それを実現した方法などもとてもよかったように思います。 Q12. 最後に、こうしたハッカソン等のイベントに参加したいと思っている方々へアドバイスをお願いします。 クリス :私がアドバイスするならば、迷わず新しいことに挑戦し、チームメイトとのコミュニケーションを大切にすべき、という点ですかね。 アンワル :また、あまり形式ばらないほうがいい気がしますね。 (特に日本では形式や型を守りがちなので )。より効果的に、そして 本音で コミュニケーションをとるために堅苦しさを捨てるべきですし、今回私たちがカジュアルな感じでなければ、こういった結果にはならなかったと思います。 アンワルは授賞式には出席できず。。 参考 今回のインタビューが、読んでくださった方々がアイデアソンやハッカソンに参加するきっかけとなったなら嬉しいです! また、Toyota Motors North Americaが主催したハッカソンにグローバル開発Gが参加した際のレポート記事もぜひご覧ください: TMNA Swarm Hackathon参加レポート また、グローバ開発Gにご興味のある方は以下の記事をご参照ください: グローバル開発グループ Pt.1 グローバル開発グループ Pt.2 グローバル開発グループ Pt.3
アバター
こんにちは、KINTO Technologiesグローバル開発部でフロントエンド開発をしているクリスです。 普段フロントエンド開発でコンポーネントを開発する際はpropsを利用して必要な情報を渡す、という話はよく耳にすると思います。Angular, React, Vue, Svelteといった今よく使われているフレームワークではそれぞれの書き方でこの機能を実現しています。すべてのフレームワークを言及すると非常に長い記事になってしまうので、今回はグローバル開発部で良く使っているVueについて話したいと思います。 コンポーネントの再利用性を考える時に、propsだけでは実現しにくい可能性があります。そこで登場するのがslotという機能です。本記事は両者について説明し、利用事例を比較したいと思います。 Propsで情報を渡す 例えば、タイトルがついているテーブル情報を再利用できるコンポーネントとして実装する必要があるとします。タイトル、ヘッダーとデータを渡すためにそれぞれのpropsを渡せば、やりたいことがすぐ実現できます。 # コンポーネント <template> <div> <h5>{{ title }}</h5> <table> <tr> <th v-for="header in headers" :key="header">{{ header }}</th> </tr> <tr v-for="(row, i) in data" :key="i"> <td v-for="(column, j) in row" :key="`${i}-${j}`">{{ column }}</td> </tr> </table> </div> </template> <script> export default { name: 'DataTable', props: { title, headers, data }, } </script> # コンポーネントを呼び出す親 <template> <DataTable :title="title" :headers="headers" :data="data"/> </template> <script> // コンポーネントのimport文を省略します export default { data() { return { title: 'Title', headers: ['C1', 'C2', 'C3', 'C4'], data: [ { c1: `R1-C1`, c2: `R1-C2`, c3: `R1-C3`, c4: `R1-C4`, }, { c1: `R2-C1`, c2: `R2-C2`, c3: `R2-C3`, c4: `R2-C4`, }, ] } }, } </script> 上記のコードで以下のテーブルを作成することができます。(CSSによる簡単なスタイリングをつけていますが、本記事と関係ないため割愛します。) Propsの利用について少し補足すると、Vue.jsではTypeScriptを利用しなくても、簡単な型チェックができたり、呼び出し元からもらったデータに対してバリデーションをかけられたりします。以下が例になりますが、詳しくはVue.jsの 公式ドキュメント から確認してみてください。(本記事の全サンプルコードにこのような設定をつける長くなってしまうため、割愛します) <script> export default { props: { title: { // String型のprop。二つ以上の型があり得る場合は[String, Number]などで書きます type: String, // このpropは必ず呼び出し元から渡してもらう必要があります required: true, // propのバリデーションチェック。Booleanを返すことで結果を判定します validator(value) { return value.startsWith('Title') } }, }, } </script> Propsのみを利用する際の問題点 型の指定、値のバリデーションなどの機能がついているpropsは確かに便利ですが、やりたいことによっては物足りないと感じてしまう時があります。例えばこのような要件を聞いたことありませんか? テーブルセルに表示している値を条件によって太文字だったり、斜体だったり、テキストの色を変えられるようにする テーブルの各行に一つ以上のアクションを起こすボタンを表示できるようにし、条件によってdisabledできるようにする 聞くと普通に納得できそうな要件ですが、propsのみで実現しようとすると、複雑なコードになりがちです。 セルのスタイルを変更するには、判定のロジックもpropsとしてコンポーネントに渡すようにするか、この値はスタイル変更が必要というマーキングをデータオブジェクトに追加する必要があり、データ行ごとにボタンをつけるには以下のようにボタンの情報をpropsとしてコンポーネントに渡す必要があります。 例えば最初に出したサンプルコードを追加実装すると、以下のようなコードになります。 <template> <div> <h5>{{ title }}</h5> <table> <tr> <th v-for="header in headers" :key="header">{{ header }}</th> </tr> <tr v-for="(row, i) in data" :key="i"> <!-- 受け取ったスタイルを判定する関数でクラス情報を取得 --> <td v-for="(value, j) in row" :class="cellStyle(value)" :key="`${i}-${j}`" > {{ value }} </td> <!-- ボタンがある場合はボタンの列を別途用意 --> <td v-if="buttons.length > 0"> <button v-for="button in buttons" :class="button.class" :disabled="button.disabled(row)" @click="button.onClick(row)" :key="`${button}-${i}`" > {{ button.text }} </button> </td> </tr> </table> </div> </template> <script> export default { props: { title, headers, data, // セルのスタイルを決めるロジックをpropsとして受け取る cellStyle, // ボタン情報をpropとして受け取る buttons, }, } </script> <template> <!-- クラス情報を返す関数とbuttonsに関する情報をpropsとして渡す --> <DataTable :title="title" :headers="headers" :data="data" :cell-style="cellStyle()" :buttons="buttons" /> </template> <script> export default { data() { return { // その他のdata情報を省略 buttons: [ { text: '編集', class: 'btn-primary', disabled: (rowData) => { // ボタンをdisabledにするかどうかの判断ロジック }, onClick: (rowData) => { // ボタンの押下後ロジック }, }, { text: '削除', class: 'btn-danger', disabled: (rowData) => { // ボタンをdisabledにするかどうかの判断ロジック }, onClick: (rowData) => { // ボタンの押下後ロジック }, }, ], } }, methods: { cellStyle() { return (val) => { // 必要なスタイルクラス情報を返すロジック } } } } </script> こちらのスクショはボタンの表示とともに、条件に応じてセルのテキストにスタイルをかけたり、表示したボタンをdisabledにするロジックを入れた結果になります。 ただ、もしさらにセル内のhtml構造そのものを制御したい場合(例: <p> タグ、 <span> タグ、 <li> タグなどを入れる)、 htmlコードを文字列のpropsとして子コンポーネントに渡し、v-htmlを使って表示する必要があります。 v-htmlというのも便利なやり方ですが、htmlコードを文字列で構築するため、たくさんの動的な要素を入れると読みづらくなってしまいます。 上記の話をまとめると、propsのみを利用する場合は 子コンポーネントとしてどう受け取るか を深く悩む必要があります。 Slotでpropsの足りない部分を補う そこでslot機能の出番です。 公式ドキュメント にもこの機能の説明がありますが、コンポーネントにslot枠を作成し、呼び出し元から指定したtemplate枠内のhtml情報を渡すことによって、実装したい内容を対象のslot枠に当てはめることができます。 上記のイラストはあくまでイメージですが、左側の箱はpropsを利用したコンポーネントで、右側の箱はslotを利用したコンポーネントです。 Propsの場合、各入口が狭く、型も決まっているため、実装者からはコンポーネントが決めた情報しか渡せないイメージですが、slotの場合は入口がだいぶ広くなるため、何をコンポーネントに渡すかは実装者がより決定権を持っています。 例えば、前半で例としてあげたデータテーブルの実装でslotを使ってみるとします。 <template> <div> <!-- default slot --> <slot /> <!-- tableというslot --> <slot name="table" /> </div> </template> <template> <DataTable> <!-- コンポーネントの中にhtmlコードを書くと、自動的にコンポーネント側で宣言されたslotに当てはめます --> <!-- 特にtemplateで囲まなければdefaultのslotに当てはめます --> <h5>Title</h5> <!-- tableというslotに当てはめます --> <template #table> <table> <tr> <th v-for="header in headers" :keys="header">{{ header }}</th> </tr> <tr v-for="(row, i) in data" :keys="`row-${i}`"> <td v-for="(column, j) in row" :class="{ 'font-italic': italicFont(column), 'font-weight-bold': boldFont(column) }" :key="`row-${i}-col-${j}`" > {{ column }} </td> <td> <button :disabled="editDisabled(column.c1)" @click="edit(column.c1)">編集</button> <button :disabled="destroyDisabled(column.c1)" @click="click(column.c1)">削除</button> </td> </tr> </table> </template> </DataTable> </template> <script> export default { // data情報を省略 methods: { edit(id) { // その行のデータを編集するロジック }, destroy(id) { // その行のデータを削除するロジック }, italicFont(val) { // 斜体にする判断ロジック }, boldFont(val) { // 太文字にする判断ロジック }, editDisabled(id) { // 編集ボタンをdisabledする判断ロジック }, destroyDisabled(id) { // 削除ボタンをdisabledする判断ロジック } }, } </script> この例でいうと、コンポーネントにpropsを渡しておらず、かなりすっきりして見えますが、一つ問題があります。それは、実装者の好きなようになんでも実装できてしまうことです。例えば上記のコンポーネントを利用すると、親ファイルで以下のように指定のタグ(タイトルはh5タグ、テーブルはtableタグなど)を利用し、適切なスタイルをつけるべきなのに、実装者への共有不足、もしくは実装の知識不足で別のタグを利用してしまう可能性があります。そうなると、実際の見た目では違く見えてしまうかもしれませんし、テストの際に各画面サイズで崩れていないか再確認する必要があります。 <template> <DataTable> <!-- h5ではなく、h1を利用 --> <h1>Title</h1> <template #table> <!-- <table>, <tr>や<th>を利用せず<div>を利用 --> <div> <div> <div v-for="header in headers" :keys="header">{{ header }}</div> <div></div> </div> <div v-for="(row, i) in data" :keys="`row-${i}`"> <div v-for="(column, j) in row" :key="`row-${i}-col-${j}`"> {{ column }} </div> <div> <button :disabled="editDisabled(column.c1)" @click="edit(column.c1)">編集</button> <button :disabled="destroyDisabled(column.c1)" @click="click(column.c1)">削除</button> </div> </div> </div> </template> </DataTable> </template> テーブルなのにすべて <div> タグを利用するとは極端な例かもしれませんが、必要以上な自由度は与えない方が無難です。会社やチームによって解釈が違いますが、私にとっての理想は、デザイナーと相談した上で、必要な部分だけ自由を与えることです。どの部分が実装時の仕様に応じて自由に実装してもらっていいか、どの部分が必ず一つのやり方に従ってもらわないといけないかを決めてから、propsの利用や、slotを使い分けるべきと思います。 <template> <div> <!-- 必ずテキストを<h5>に入るようにpropsを利用 --> <h5>{{ title }}</h5> <!-- 必ず<table>タグを利用 --> <table> <tr> <!-- header情報をpropsで渡すことで、必ず<th>を利用する --> <th v-for="header in headers" :key="header">{{ header }}</th> </tr> <!-- 渡されたデータの行数に応じて動的にslotを生成 --> <!-- v-bindを利用して、呼び出し元のtemplateにデータを渡す --> <slot name="table-item" v-for="row in data" v-bind="row" /> </table> </div> </template> <script> export default { data() { return { title, headers, data } } } </script> <template> <!-- タイトルとヘッダーはpropsで渡す --> <DataTable title="Title" :headers="headers" :data="data"> <!-- コンポーネント側のv-bindされたデータを受け取る --> <template #table-item="row"> <!-- 行のデータを受け取り、表示方法を定義 --> <tr> <td v-for="(column, i) in row" :key="`col-${i}`"> {{ column }} </td> <td> <button :disabled="editDisabled(column.c1)" @click="edit(column.c1)">編集</button> <button :disabled="destroyDisabled(column.c1)" @click="click(column.c1)">削除</button> </td> </tr> </template> </DataTable> </template> ちなみに、slotを利用する際に、コンポーネント側で this.$scopedSlots を利用することで、どのslotが呼び出し元に利用され、またどのように利用されているか確認することができます。ユースケースは様々ありますが、例えば利用されているslotの中でどんなタグが利用されているか調べることができます。これは先述したslotの自由度が高すぎる問題に対して、一種のマイルドなバリデーションをかけることが可能になります。 <template> <DataTable title="Title" :headers="headers" :data="data"> <template #table-item="row"> <!-- <tr>ではない場合は何かしらの方法で実装者にお知らせするなど --> <div> <td v-for="(column, i) in row" :key="`col-${i}`"> {{ column }} </td> </div> </template> </DataTable> </template> まとめ 最後のまとめですが、Vueによる再利用コンポーネントの開発ではpropsを利用するのが一番簡単なものの、本記事にある事例のように柔軟性が欠けています。一方、slotを利用すると、実装者がより自由に実装できますが、様々な理由で、想定しなかった実装方法を利用したことによって、品質担保ができなくなる可能性があります。 そこで、コンポーネントの開発関係者を交えてあらかじめコンポーネントのどの部分にどのレベルの自由を与えるかを決めた上、与えてもいい自由度に合わせてpropsとslotを使い分けて開発し、コンポーネントの利用者に対してもドキュメントなどを通して、仕様を理解してもらったほうがいいと思います。
アバター
こんにちは(こんばんは)、Svelte不定期連載その2です。 過去の記事はこちら SvelteKit + Svelte を1年間くらい使ってみた知見など※SvelteKit メジャーリリース対応済み Svelteと他JSフレームワークとの比較 - Svelte不定期連載-01 今回はSvelteのユニットテストについて書いていこうと思います。 モジュールはこちら。 Vitest + jsdom + @testing-library/svelte の3つを使用して行います。 Vitest viteというツールを使ったテストフレームワークです。 viteを使用しているため非常に高速に動作します。 https://vitest.dev/ jsdom Node.jsでDOMを使うライブラリです。 HTMLをパースし、web APIをコールすることができます. https://github.com/jsdom/jsdom @testing-library 様々なフレームワークをサポートしているテストライブラリです。 Svelteだけではなく、ReactやVueなどももちろんサポートしています。 https://testing-library.com/docs/svelte-testing-library/intro/ 環境設定 まずは以下でモジュールたちを追加します。 ※パッケージマネージャーはお好みで、今回はyarnで行います。 yarn add vitest jsdom @testing-library/svelte @types/jest ※今回はTS(TypeScript)で行うので @types/jest も追加します。testファイルにも型を追加したいためです。 無論、TSで書かれている場合は必要ありません。 config 次はvite.config.jsにtest用の記述を追加します。 import { sveltekit } from '@sveltejs/kit/vite'; /** @type {import('vite').UserConfig} */ const config = { plugins: [sveltekit()], // ここから下を追加 test: { // testの対象ファイル include: ['src/**/*.{test,spec}.{js,ts}'], globals: true, // testの環境 environment: 'jsdom' } }; export default config; jsdomは environment で設定しています。 package.json package.jsonにも以下を追記します。 ※書かなくても yarn vitest で実行できます。 "test": "vitest" これでテストの準備ができました。 実際にテストをしてみよう よくある加算減算ボタンのあるコンポーネントでテストを書いていこうと思います。 コンポーネント側 まずテストしたいコンポーネントを用意します。 <script lang="ts"> let count:number = 0; </script> <!-- 減算するボタン --> <button on:click={() => (count -= 1)} aria-label="減算">-</button> <!-- 定義したcount変数 --> {count} <!-- 加算するボタン --> <button on:click={() => (count += 1)} aria-label="加算">+</button> testing-libraryの方で加算・減算とそれぞれ読み取る必要があるため本記事ではaria-labelで設定します。 これでコンポーネントの作成は終わりです。 簡素ですが以下のようなコンポーネントが画面に描画されます。 プラスボタンを押すと加算処理、マイナスボタンを押すと減算処理が実行されます。 完成図 テスト では単体テストのファイルに移ります。 コンポーネントの数や好みにもよりますが、同階層に置くほうが、視線やカーソルが行ったり来たりしなくて好きです。 import { render, fireEvent, screen } from '@testing-library/svelte'; // $lib はsrc/libのエイリアス import Counter from '$lib/components/Counter.svelte'; describe('Counter.svelte', async () => {  // 初期値 test('カウンターの初期値は0', async () => { render(Counter); expect(screen.getByText('0')).toBeTruthy(); }); test('減算処理', async () => { render(Counter); // ボタンを定義 const decreaseButton = screen.getByLabelText('減算'); // イベントを定義 await fireEvent.click(decreaseButton); const counter = await screen.findByText('-1'); expect(counter).toBeTruthy(); }); test('加算処理', async () => { render(Counter); const increaseButton = screen.getByLabelText('加算'); await fireEvent.click(increaseButton); const counter = await screen.findByText('1'); expect(counter).toBeTruthy(); }); }); これでテストも用意できました。 テスト単位で紐解いてみてみましょう。 test('カウンターの初期値は0', async () => { render(Counter); expect(screen.getByText('0')).toBeTruthy(); }); カウンターの初期値は0 というテスト項目に基づいて、 まず import Counter from '$lib/components/Counter.svelte'; で呼び出している Counter コンポーネントをrender(描画)してます。 そして、** Counter コンポーネントが初期値で持つ値が0かどうかの審議を toBeTruthy というマッチャーで行っています。** ※マッチャーとはテストを評価する際の関数といった理解でおおよそ大丈夫です。 詳しくはJest公式をご覧ください。 Jest 続いて減算処理のテストについて。 加算処理・減算処理ともに、同じようなロジックなので今回は減算処理のみ触れます。 test('減算処理', async () => { render(Counter); // ボタンを定義 const decreaseButton = screen.getByLabelText('減算'); // fireEventでイベントを定義 await fireEvent.click(decreaseButton); // const counter = await screen.findByText('-1'); expect(counter).toBeTruthy(); }); 減算処理 のテストでは、下記の流れでテストをしています。 コンポーネントを描画 コンポーネント内のボタンを定義 クリックイベントを設定 実際に減算された値は-1であるかの真偽 testing-library、async/awaitでスッキリしていて、わかりやすくSvelteとの親和性よいですね。 加算処理のテストは findByText の値が違うだけで、他部分は重複するので割愛します。 実行してみる これを yarn test すると このような感じでテストをパスすると、グリーンでPASSしました。というような結果がコンソールに出力されます。 ではテストに失敗してみます。 <script lang="ts"> // 0 => 1 let count:number = 1; </script> <!-- 減算するボタン --> <button on:click={() => (count -= 1)} aria-label="減算">-</button> <!-- 定義したcount変数 --> {count} <!-- 加算するボタン --> <button on:click={() => (count += 1)} aria-label="加算">+</button> テストファイルでは初期値は 0 を想定しているので、 1 という値がセットされているとエラーになります 間違えてた際でも、以下のようにエラーが出力されます。 またこのように下にエラーの詳細が次のように並びます。カウンターコンポーネントの初期値は0を想定しています。 というようなエラー文が表示されているのがわかります、 簡単ではありますが。上記で単体テストができました。 設定ファイル、テスト実行ファイルともに記述が少なくかけるので重宝しそうです。 以上、Svelteでユニットテストの回でした。 次回は SvelteKitにStorybook を導入してみます。 次回もお楽しみに!
アバター
KINTOテクノロジーズ株式会社 開発支援部の有留です。 全社会議体などの運営や、エンジニア育成、研修などを担当しています。 KINTOテクノロジーズ(以下、KTC)では、業務を通じたエンジニア自身の成長を、会社として応援しています。そのため、社外コミュティ参加や、外部イベントでの登壇も積極的に後押ししています。(社長の小寺、副社長の景山も、外部主催のイベントで度々登壇しています) 2023年2月8日、分析グループ所属の若手エンジニア和田さんが、一般社団法人中部経済連合会さま主催のイベント、中経連×デジタルリテラシー協議会「 デジタル人材育成セミナーin中部 」にゲストとして招かれ、パネルディスカッションに登壇しました! どんな内容で登壇したの? KTCの業務は? など、気になったことを、登壇者の和田さんにインタビューしました。 ー 自己紹介をお願いします! 和田 : こんにちは! KTCでデータサイエンティストとして働いている、和田と申します!社内外からの分析リクエストに対応したり、自社開発アプリのAI機能を開発することが主な仕事です。 本日はよろしくお願いします! 有留 : よろしくお願いします! ー 和田さんは、どんなキャリアを経て、KTCに入社されたのですか? 和田 : 大学では社会情報学を専攻していました。社会情報学というとあまり馴染みがないかもしれないですが、情報通信技術を社会実装して、社会課題を解決するぞ!という、情報学の中では応用寄りの分野です。 大学卒業後、2019年に自動車部品メーカーに新卒入社し、生産管理システムに関わる仕事を経験しました。その後、2022年に現職へと至っています。 ー 今回はどんなテーマ、どんなキッカケで登壇されたんですか? 和田 : 「 デジタル人材育成セミナーin中部 」は、中部圏の様々な企業の経営層、中堅層の方々を対象に「これからは全社員がデジタルリテラシーの獲得をすべきだ」という内容を説くイベントでした。 イベントでは、デジタルリテラシーの獲得に繋がる具体的な資格を3つ推奨していました。「ITパスポート」「データサイエンティスト検定」「G検定」です。 イベントの後半に、日本ディープラーニング協会 理事 事務局長の岡田隆太朗氏と、資格の取得を通じてデジタルリテラシーを身につけたパネリスト4名とで、「資格を取得してよかっとこと」「苦労はあった?」「仕事への影響は?」などのディスカッションを行いました。 私はG検定と、その発展であるE資格を保有しています。資格保有者の参加するコミュニティ内でイベントのパネリスト募集があり、登壇の機会を得ることができました。 イベント会場の様子 有留 : 「G検定」私も最近耳にする機会が多いです。 資格について、詳しく教えてください! ー G検定について、詳しく教えてください! 和田 : G検定は、ディープラーニングの基礎知識を問われる資格です。 GはジェネラリストのGで、専門用語の意味はもちろん、技術の歴史、法規制についての知識もカバーした資格です。数学やコーディングの知識はあまり問われないので、非エンジニアの方にもおすすめの資格です!また、関連資格としてE資格という、ディープラーニングの理論理解や実装能力が問われる資格もあります。 どちらかの資格を保有していると、CDLEというコミュニティに参加することができます。パネリスト募集があったコミュニティというのは、このCDLEです。 CDLE とは、日本ディープラーニング協会(以下、JDLA)が実施する検定・資格試験(G検定およびE資格)の合格者のみが参加できるコミュニティです。合格者同士の交流・情報交換の場を提供しています。このコミュニティは、非営利目的で活動しています。 ※ CDLEコミュニティサイト 、CDLEガイドラインより引用 有留 : 合格者同士のコミュニティがあるんですね。 共通の学びがあることで、話も盛り上がりそうですね! ー そもそも、なぜ資格を取得されたのですか? 和田 : 体系的な知識の獲得に、資格の取得が最も効率の良い方法だと考えたからです! 私がAIの勉強を始めた頃、最初はインターネット上のサンプルコードを参考に、仕組みもよく分からないまま機械学習やディープラーニングのコーディングをしていました。初めは手元で何かが動くことが楽しいだけでしたが、次第に仕組みにも興味が湧き、少し難易度の高い書籍や、技術解説のブログを読むようになりました。 しかしそのような学習法ではピンポイントの知識は得られても、 分野を体系的かつ網羅的に学ぶのは大変で・・・。 そこで、体系的な知識の獲得に「資格試験のシラバス」という先人の知恵が詰まった教材を活用するべく、資格試験に取り組むことにしました。例えるならば、知識の容器に好きな石ころ(ピンポイントの知識)を詰めて隙間だらけだったところに、シラバスから水(体系的な知識)を注ぎ、容器の隙間を埋め尽くそう!といった感じです(伝わるかな?) ![知識のイメージ](/assets/blog/authors/s.wada/image.png =250x) 知識獲得のイメージ 有留 : 確かに新しいことを始めるとき、「何から始めよう」と迷ってしまうことって、私もよくあります。独学で学んでも、その知識が断片的なものだと心もとないですよね。 ー 資格を取得するにあたって、苦労したことや工夫したことは? 和田 : 技術の使い方については独学の経験からある程度理解していましたが、その背景や基礎技術、技術に至る歴史、法律関係については学び直しでした。 加えて、当時のE資格は特定のフレームワークを用いず、numpyによるスクラッチ実装を前提とした問題形式だったため、scikit-learnやkerasなどを使用していた身からすると、慣れない記法に苦労しました。ただ、不足する知識を補いたい!という当初の目的にはピタリと合致していたので、苦労のし甲斐はありました(笑) 有留 : 資格だからこそ、苦手意識のある分野も含めた網羅的・体系的な学びが必要で、その分苦労しそうですね。 ー 新しい資格やジャンルを学んだことで、変化はありましたか? 和田 : 人工知能に関連する用語を一通り学んだことで、これまで読めなかった難易度の高い書籍や、論文にも手が伸びるようになりました。スラスラとはいきませんが、「読める、読めるぞ・・・!」といった感じです(笑) ![読めるぞ!](/assets/blog/authors/s.wada/yomeru.png =250x) 有留 : 自分自身が成長している実感を得られると、苦労も報われそうですね! ー 資格を取得してよかったことはありましたか? 和田 : 昨今、「AI×○○」で高い価値を生み出せるシーンが多いですよね。 今後自分が接する様々な領域に対し、「ここにAIを掛け合わせれば・・・!」という目線を得られたことは、自身の強みに繋がると考えています。今話題のChatGPTに代表されるような、敷居の低いAIサービスがこれからも続々と出現し、この流れは一層強くなっていくのではないかと考えています。 ー KTCでは、「学び」を後押しする制度や文化はありますか? 和田 : 自身の学びを共有する文化があり、グループ内、全社向けなど様々なスコープで勉強会が開かれています。ちょっとした情報共有も盛んで、Slackの情報共有チャンネルでは、日々気になるTechニュースが飛び交っています。 また、業務に役立つ書籍は気軽に購入申請ができ、拠点間で共有する オンライン本棚 で様々な書籍にアクセスすることができます。 機会があれば、今回の私のようなイベントへの登壇も比較的自由にすることが可能です! ー KTCには、どんな社員が多いですか? 和田 : KTCの社員について、入社直後の感想は「色んな人がいる!!」でした(笑) 前職は、ほぼ新卒100%の会社だったので、中途採用100%という環境は衝撃でした。誰もが過去の経験で培った得意分野を持っていて、その長所を活かし合って仕事を成すのはとても刺激的です! 私自身も、AI領域のプロとしての仕事を求められるので、とてもやりがいがあり、成長できる環境だと思います! ー 和田さん自ら、「学びカルチャー」を推奨する工夫はされていますか? 和田 : 自身のスキルや直近学んだこと、興味のあることなど、できるだけ自己開示に努めています。 すると「こんな記事を見つけたよ」と他者からインプットを受けたり、「ここ教えて」とコミュニケーションが生まれて、教える中で新しい気づきがあったりと、いいことづくめです。 ー 最後に、記事を読まれている方にメッセージをお願いします! 和田 : 今回は技術的な話があまりできませんでしたが、機会があれば担当しているAIプロダクトの話などについてもブログにできればと思います! ここまで読んでいただき、ありがとうございました!! We are hiring! KINTOテクノロジーズでは一緒にモビリティの未来を創る仲間を募集しています。カジュアル面談なども行っておりますのでご興味をお持ち頂けましたらぜひお気軽にご連絡ください。 @ card
アバター
こんにちは(こんばんは)、始まりました。 Svelte不定期連載その1です。 前回 はざっくりとSvelteKitを動かすまで、を書いてみました。 (SvelteKitのメジャーアップデートに伴って内容もアップデートしましたのでよかったら一読ください) 今回は、 Write less code をコンセプトとしたSvelteと他のJSフレームワークで、それぞれ書き方にどんな特徴があるのかを比較してみます。 Svelte / React / Vue.js 仮想DOMであるReactとVue.js、仮装DOMを持たないSvelteではありますが、比較対象としてはよくあげられるので改めて比較します。 ※Svelte(v3系) / React(v18系) / Vue.js(v3系) で行います。 Fetch&ループ処理 Fetchとループ処理はセットで書かれることが多いので、まとめて記述してそれぞれを比較してみます。 Svelte まずはSvelteから。 Svelteは独自の構文がありますね、 await/ block構文があるのですっきり書けるのが特徴です。 <script> const fetchItems = async function() { const items = await fetch('URL'); return await items.json(); } </script> {#await fetchItems()} <p>Loading</p> {:then items} {#each items as item} <p>{item.name}</p> {/each} {:catch error} <p>{error.message}</p> {/await}   React Svelteと比べるとコードの行数が増えました。 とはいえ、本来であれば分割なりをするので、このまま書くことは滅多にないでしょう。 useStateではじめに定義するあたりがReactらしいのでしょうか。 function FetchComponent() { const [error, setError] = useState(null) const [isLoaded, setIsLoaded] = useState(false) const [items, setItems] = useState([]) useEffect(() => { fetch('URL') .then(res => res.json()) .then( (result) => { setIsLoaded(true); setItems(result) }, (error) => { setIsLoaded(true); setError(error) } ) }, []) if (!isLoaded) { return <div>Error: {error.message}</div>; } else if (!isLoaded) { return <div>Loading...</div>; } else { return ( <ul> {items.map(item => ( <li key={item.id}> {item.name} </li> ))} </ul> ); } } Vue.js 次はVue3、 2系と比べるとReactに書き心地が似てきた感覚があります。 <script setup> import { ref } from 'vue' const items = ref(null) const error = ref(null) fetch('URL') .then((res) => res.json()) .then((json) => (items.value = json)) .catch((err) => (error.value = err)) </script> <template> <div v-if="error">{{ error.message }}</div> <div v-else-if="items"> <ul v-for="(item,index) in items"> <li>Number:{{index+1}} Name:{{item.name}}</li> </ul> </div> <div v-else>Loading</div> </template> Reactive これもコンポーネントを使い回す現代のフロントエンドで必須の仕組みですね。 Svelte Svelteでは接頭辞に $: をつけることでリアクティブになります。 $: をつけない限り依存する値が変更されても実行されません。 他のフレームワークになれているとこのあたりは最初面食らうかもしれません。 いざ使うと明示的だなぁと感動します。 $: foo = false React 細かい説明を省きますが、Reactでは useState で定義するのが一般的です。 Svelteと比較すると、 useState が $: と同等の役割でしょうか。 import {useState} from 'react' const [foo] = useState(false) Vue.js Vueの場合は reactive で生成すると変更されます。 <script setup> import { reactive, computed } from 'vue' const state = reactive({ count: 0 }) const increment = () => { state.count++ } </script> propsについて 今回は基礎である文字列を受け渡す方法で比較してみます。 三者三様かと思いきや、ほとんど同じような書き心地になってきました。 Svelte <script> export let name </script> <p>Hello, I'm :{name}</p> <script> import ChildComponent from './ChildComponent.svelte' </script> <ChildComponent name="Svelte" /> React const ChildComponent = (props) => { return <h1>Hello, I'm :{props.name}</h1>; } export default ChildComponent; import ChildComponent from './ChildComponent.jsx' function App() { return ( <ChildComponent name="React" /> ); } export default App Vue.js <template> <p>Hello, I'm {{name}}</p> </template> export default { props: { name: String, } } <script> import ChildComponent from './ChildComponent.vue' </script> <template> <ChildComponent name="Vue" /> </template> 子コンポーネントをimportして値を渡す。 どれもわかりやすい書き方でした。 総評 短いですがSvelteと、React、Vue.jsを比較してみましたがいかがでしたでしょうか。 それぞれ良さがある中で、 Write less code をテーマにしたSvelte。 とっつきやすさがやはり際立っている印象があります。 よきSvelteライフをお過ごしください。 そして初めての方はSvelteを是非書いてみてください、とても楽しいフレームワークです。
アバター
Hi, I’m Flo from KINTO Technologies! Today I would like to interview the winners of our first-ever Innovation Days! 🥳 Background Global KINTO Innovation Days was a hackathon-like event held between the 14th and 21st of December 2022, where 30 of our colleagues in Global KINTO, divided into 6 teams joined forces in an effort to bring new ideas to our organization while improving our teamwork. You can read more about how we prepared for the event and how the event went . Team Introduction Before starting, let me introduce you to our winners: team United Nations ! (from left, clockwise) Anwar Moji Chris Angela Duc Anwar : Business development manager with 5 years of experience in the Automotive industry, working to expand KINTO Technologies Globally. Moji : Product designer with expertise in UX/UI, mainly focusing on creating user-centered digital products by working closely with cross-functional teams, to ensure that the designs meet both business goals as well as user needs. Chris : Full stack engineer but mainly working in frontend field in KINTO Technologies. Angela : From the DevOps team. Cloud Engineer with over 5 years of cloud experience, and 2.5 years of System Architecture experience. Over 15 years of system development experience. Duc : Backend engineer working on ID platform team and also have a little frontend experience. Let's Begin! Q01. Why did you join the Innovation Days? Anwar : Because it offered a unique opportunity to learn, collaborate with engineers, and create something meaningful for the company. Moji : To gain exposure to new design challenges and contribute to the development of innovative solutions that have the potential to revolutionize the mobility industry. Chris : I see this as a breakthrough opportunity for us to create something new for KINTO, I also wanted to try out some new technologies and ideas. Duc : I wanted to join because this is my first time participating in such an event. Angela : I wanted to have a deeper collaboration with colleagues from other teams. Also, I wanted to try something new. Q02. Where did you get the team name from? Chris : We named our team “the United Nations“ because we have a very diverse team (but to be honest, the other teams are mostly the same as well! 😅) getting feedback from managers during the innovation days event Q03. Can you tell us more about your tech stack and the team's roles and responsibilities? Chris : The events planning team divided all teams so that there is one person in each role, so we more or less followed that template. I was in charge of Frontend development and we used Nuxt.js for it because it is being used in Global Development Division and I am familiar with this meta-framework. Duc : I was in charge of the backend. Since Python is the most common language among us and also quite easy to use, so we decided to use it. Angela : I was in charge of the platform. For it, we chose AWS because it is what was used in the company and had features relevant to the project. Moji : I was in charge of the presentation deck for the pitching. Since we had limited time, we prioritized the development process and presentation deck over designing mockups. So I used Figma to create the presentation deck to effectively present our findings and goals to the audience within the limited time we had. We also used Miro to collaborate on creating a unique value proposition. By working together and gaining a holistic understanding of both the user's and business's needs from various perspectives. Anwar : I was in charge of business model development. To help ensure that the hackathon project is aligned with market needs, has a viable business model, and is effectively communicated to stakeholders. I think the key was that each member of our team shared their thoughts freely and executed their responsibilities (and even more) in a timely and organized manner. Q04. You have both English and Japanese speakers in the team - how did communication go? Anwar : Almost all of the members spoke both English and Japanese so we would speak in either, and when a team member did not understand something, somebody was translating for them to make sure everyone understood and was on the same page. Moji and Angela : Yes, we would usually be the ones raising our voices so that when we needed to understand something, someone from the team would explain. Q05. How were the planning and designing phases? Anwar : First of all, it was fun. Although having only 4 meetings together, everyone came up with different ideas which made it easier to move forward as we had a lot of ideas to work around with. Moji : We didn’t do that much planning considering the time constraints. A lot was done on the fly and whenever a team member felt there was a gap that needed to be filled, someone would volunteer and do the job. Chris : We only had about 4 team meetings before the actual event. I don’t know how it was for the other teams but for us, it was not enough. Angela : And yet, we were effective. Chris : Yes, so we made it worth our time. Anwar : The good thing was that everyone contributed with different ideas which we tried to combine together into one. So for example, Moji had an idea about kudos, Angela about Google Platform, and then we discussed how to combine them together. And that's how our project came to be. Duc : Yeah, I wanted to make all of these amazing ideas real. Chris : At the beginning, we tried to pick out the best idea by voting, but it was difficult to choose only one so we decided to combine a few. Q06. What surprised you the most during this event? Anwar : My surprise was how good the communication between the teammates was. Our team dynamics were very well balanced, in my opinion. Chris : For me, it was everyone's level of motivation, and how cooperative we were. Moji mentioned it before but each of us took initiative and ownership of what needed to be done and contributed effectively. Angela : As it was the first time for us to work together, what surprised me the most was discovering the strengths of each one in our team. Duc : I was really surprised by how energetic all the attendees were. My current team is mainly backend, and it is a little bit quieter there. presenting their ideas to our GM during the event Q07. What were your biggest challenges and how did you overcome them? Chris : The product we wanted to create was huge so I would say narrowing down the scope of what to develop in the short period of time we had, which was two days. To overcome this, we had to prioritize the features and choose which features to actually develop and which to mock visually on the frontend side for the presentation. Moji : Since we were targeting young generations, and having in mind the way they communicate in social media nowadays, we wanted to showcase as well that we have the technical capabilities to materialize our vision. Chris : Yes, we also wanted to implement a technology that none of us had tried before, so working with that was also challenging. Angela : For that, I did a technical analysis with feedback from the team, combined it with previous knowledge I had, and tested it until we were happy with the functionality. Chris : Another challenge was to find time for all of us to gather and discuss the project since we are all in different teams and projects. Angela : To overcome this, since the time we had was limited, we used to discuss action items to do till the next meeting and followed them up at the beginning of the next one. Duc : As the Innovation Days was held at our company and the number of meeting rooms was limited and varied in size, the organizing team decided to raffle the rooms leaving it to the luck of the team leads and unfortunately, we had the smallest room out of all the participants. Moji : Yes, we had the smallest room and probably this was the only point where we could not do anything about it. Anwar : well, we tried our best to support each other and make it as comfortable amongst each other as possible. Chris : another big challenge was the fact that Anwar was unfortunately not able to stay with us for the second part of the event due to personal reasons. We tried to overcome it by sharing our status and keeping Anwar in the loop through Slack. Q08. Did you think you would win? Anwar : Well, I think all participants of the event had the same winning spirit! We had a team motto, “ we win as a team or we lose as a team “ and we stood by it. Moji : I think the focus for us was not about winning, but networking, communicating well together, having fun while challenging ourselves to think creatively and out-of-the-box; and this mindset and how we approached it, is what helped us to win the competition. Duc : I think other teams' ideas were also very good, but I am so proud of mine that I feel ours had the most potential. team lunch! Q09. About lessons learned in this period of quick ideation and development: would you change anything from how you did it this time? Chris : Although we wouldn’t change any major things of what we did overall, we did conduct a Keep, Problem, Try Retrospective , where we reflected on what we did. A problem we identified then was the time constriction, although that was part of the challenge by design. Probably what we would all change is to plan more in advance so that not having time to review well wouldn’t be an issue. Q10. Events such as these, or hackathons, are more PoC-like; how is it different from projects that you work on a daily basis? Moji : I mentioned it a bit earlier, but time constraints and resource constraints, mainly. Anwar : For me, what was very interesting was to get to exchange ideas with friendly colleagues from different backgrounds. It felt fresh and also easy to work with than with my usual work. Angela : There was no hierarchy among us, no power structure, it was just equal colleagues working together on what each does best. Chris : Anwar and myself switched the leading role, but to be honest, it felt like it was not needed. Anwar : Everyone led when necessary! Chris : Yeah! Like a republic. Q11. What would you say were the elements that make you stand out from the rest of the teams? Moji : I would definitely say it was our collaborative spirit. All of the members worked very well, collaborating with each other and I think that made us create an impact and stand out as a result. Chris used the expression “selfless extra effort“, which I think summarizes very well that spirit: not only taking ownership of tasks but also going the extra mile of researching what could be best and sharing ideas and input with the team. Chris : I think we utilized well the fact that each of us has our own expertise in terms of web development. Secondly, every one of us in the team expresses their opinion and idea proactively, yet listen and respect others at the same time. During our meetings, there were agreements and disagreements, but in the end, we could always come up with a final conclusion and make progress. Angela : I also think that respect and trust were big factors. We are from different teams so during our preparation period we would normally work on different topics, but when we set the goal to finish something for this event until the next time we meet, I fully trusted that each of us would accomplish those goals. Duc : I think our idea was very novel and how we implemented it was also quite good. Q12. To close this interview, I would like to ask you one last question: what would you advise those interested in joining hackathons or events such as this one? Chris : I would advise them to not hesitate to try out new stuff and make sure to communicate with their teammates. Anwar : I would add that sometimes I think it’s good to not stay too formal ( especially here in Japan where people tend to keep forms ). Dropping formalities for more effective and honest communication is necessary, and I think it wouldn’t have been what it became without us being as casual. missing Messi during the awarding Further reading I hope the above interview has inspired you to go and join an ideathon/hackathon! You can also read the article about the time the global group participated in a hackathon organised by Toyota Motors North America: Swarm Hackathon Event Report And if you want to learn more about the global group, you can read the following articles: Global Group Introduction Pt 1 Global Group Introduction Pt 2 Global Group Introduction Pt 3
アバター
はじめに 愛車をリフォーム・アップグレードできるサービス KINTO FACTORY プロジェクトに参画している金谷です。今回は、JIRAとGitHub Actionsを活用し、複数環境へのデプロイのトレーサビリティを向上した取り組みを紹介します。 なお前回は、決済チームで リモートモブプログラミングに関する記事 を書きました。 背景と課題 私はKINTO FACTORYプロジェクトの開発工程の後半から参画しました。プロジェクトのうちECサイトのフロントエンドチームリーダーを担当することになりましたが、担当する中で以下の課題があると考えました。 GitHub issues, JIRA, Excelがタスク管理に使われており進捗が管理しにくい どのタスクがどの環境にデプロイされているか分かりにくい テスト環境へのデプロイ時のリリースノート作成が面倒 ![ExcelのWBS+ガントチャートの例](/assets/blog/authors/kanaya/traceability_excel_gantt.png =480x) ExcelのWBS+ガントチャートの例 はじめに進捗の管理のしにくさですが、私が参画した時点では、タスクの管理ツールはGitHub issues, JIRA, ExcelのWBS+ガントチャートの3種類が存在し、どれも使っている、という状態でした。そのために、必要な情報を一元管理できていないことで、スケジュールやタスクの管理が難しくなっていました。 次に、どのタスクがどの環境にデプロイされているか分かりにくい問題です。開発中では、デプロイの対象環境が2つあり(開発環境とテスト環境)、開発中のタスクがどの環境にデプロイ済みなのかを把握することが難しくなっていました。 最後に、テスト環境へのデプロイ時のリリースノート作成が面倒問題です。テスト環境には、我々エンジニアだけでなく、品質保証を担当するQAチームの方々もテストに使っていたため、いつどの内容をテスト環境にデプロイしたのかを連絡する必要がありました。連絡方法としてリリースノートを作る運用にしていたのですが、このリリースノート作成に毎回5分程度は時間を取られており、非常にストレスに感じていました。 これらの課題に対して、デプロイのトレーサビリティを上げることを目標としました。少なくとも、課題の2と3 (環境ごとのデプロイ管理問題、リリースノート生成問題)は解消されることが期待されます。また、後述の仕事の仕方の変化により、課題の1(進捗管理が難しい問題)の解消も狙っていきます。 デプロイのトレーサビリティを上げる方針 まずトレーサビリティですが、 DevOps 技術: バージョン管理 | DevOpsの能力 には以下のよう書かれております。このうち複数環境の差分を出さない、または出てもすぐに確認ができることが求められます。なお、全ての依存関係のバージョン管理は、フロントエンドの場合に npm の package.json, package-lock.json で管理できるため、本記事では省略します。 どの環境を選んでも、その環境を作成するために使用するすべての依存関係のバージョンをすばやく正確に判断できなければなりません。 また、環境の 2 つのバージョンを比較し、その環境間の変更点を理解する必要があります。 どのタスクがどの環境にデプロイされているかを管理できるようにする、トレーサビリティを上げる方針として、以下を行いました。 JIRAでタスクやデプロイを全部管理する リリースノートは自動生成に頼る JIRAでタスクやデプロイを全部管理する JIRAには、 課題の開発情報を見る機能 があります。コードの状況、レビュー状況、ビルド、デプロイの状況が分かるため、開発に必要な情報をすべてJIRAに集約することにしました。 JIRAとGitHubの連携には、以下の作業を行う必要があります。 JIRAとGitHubを連携する設定を行う JIRAのチケットとGitHubのプルリクエストを紐付けるためにブランチ名にJIRAチケット番号を含める GitHub Actionsでデプロイする際に環境を設定する このうち2.については、エンジニア各人の作業に委ねられる部分でした。エンジニア各人にJIRAチケット番号を含めていただくにあたり、GitHub issuesとExcelの利用を廃止し、JIRAに統一することにしました。JIRAに統一することで、エンジニア各人もタスク管理がしやすくなり、進捗管理する側も、JIRAの ロードマップ を使うことで一元管理できるようになりました。 JIRAロードマップの例 3.については、 environment パラメータを渡してデプロイすることで、 environment に渡したデプロイ状況がJIRAにも連携されます。 参考までに我々が使用しているGitHub Actionsによるデプロイの一部コードを掲載します。 environment パラメータにて、更に $${ inputs.env }} を渡しており、環境ごとのキーが作られるようになります。 $${ inputs.env } にデプロイ先の環境名が入るため、デプロイ先がJIRAに連携されるようになります。 DeployToECS: needs: [createTagName, ecr-image-check] if: ${{ needs.createTagName.outputs.TAG_NAME != '' && needs.ecr-image-check.outputs.output1 != '' }} runs-on: ubuntu-latest environment: ${{ inputs.env }}-factory-frontend steps: - 具体的な処理 結果として、開発状況はJIRAのロードマップとチケットで管理し、各チケットを見ることで、レビュー中なのか、マージ済みだが未デプロイなのか、どの環境までデプロイできているのかが管理できるようになりました。 JIRAの各チケットに記載されるステータス また各チケットではなく、チケット全体を通してデプロイ状況を見える化することもできます。各チケットがいつどの環境にデプロイされたのかを見ることができて便利です。 各環境へのデプロイ状況の見える化 :::message GitHubにもProjectの機能である程度実現できますが、ロードマップの機能や QAチームが使っているツールとの連携 も鑑み、JIRAに統一しています。 ::: リリースノートは自動生成に頼る リリースノートの自動生成は、 GitHubの自動生成リリースノート 機能を使うことにしました。リリースノート自動生成は、 GitHubのリリース機能 のうち、リリースノート部分をプルリクエストのタイトルとリンクを一覧表示する機能です。リリースノート自動生成は、いくつかのルールを設定することで、よりよい対応ができます。こちらを紹介します。 リリース内容のカテゴリを決める リリースノートに列挙されるプルリクエスト一覧は、デフォルトではカテゴリ分類されておらず、非常に見にくくなります。カテゴリ分類を使うことで、リリースノートが整理されて見やすくなります。 カテゴリはラベルで表現されます。今回は、特に主要な変更と不具合修正をリリースノートのカテゴリとして表示したかったため、それぞれを表現するラベル(enhancement, bug)を作成しました。 また、対象リポジトリに .github/release.yml というファイルを作り、以下の内容を書くことで、カテゴリごとのプルリクエストタイトル一覧を生成できます。 changelog: categories: - title: 主要な変更 labels: - 'enhancement' - title: 不具合修正 labels: - 'bug' - title: その他 labels: - '*' 生成されたリリースノートのイメージは以下の通りです。enhancementラベルが付いているプルリクエストのタイトルが「主要な変更」に、bugラベルが付いている場合は「不具合修正」にそれぞれ分類されるようになりました。enhancement, bugラベルが付いていないプルリクエストは、すべて「その他」に分類されます。 プルリクのレビュー時点でカテゴリ仕分けやタイトル修正を行う リリースノートを生成してから手動で仕分けることもできますが、リリースノートを生成した時点では、記憶を取り戻して仕分けをする必要があり、大変です。そのため、プルリクエストのレビュー時点で、カテゴリに相当するラベルを付けることで運用しています。合わせてタイトルも、修正内容に合ったタイトルかどうか見ています。 細かいtipsとして、ラベルの付け忘れを防ぐために、リファクタリングなどに対しては、 others ラベルを付与しています。これにより、レビューしてカテゴリ仕分け済みであることが分かるようにしています。 結果 以上の取り組みにより、抱えていた課題は無事に解決できました。また、特にJIRAロードマップは他のチームでも参照され、今はKINTO FACTORYプロジェクト全体で使われるようになっています。 GitHub issues, JIRA, Excelがタスク管理に使われており進捗が管理しにくい → JIRAのチケットとロードマップに集約され一元管理できるようになった どのタスクがどの環境にデプロイされているか分かりにくい → チケットに環境ごとのデプロイ状況が見えるようになった テスト環境へのデプロイ時のリリースノート作成が面倒 → 2〜3分かかっていた作業が10秒に激減した 今後の展開 本番環境へのデプロイによって、 DevOps Four Keys のうち速度に関する2つがJIRAで計測できます。デプロイ頻度や変更のリードタイムについて、現状と目指すべき指標をチーム内ですり合わせて、継続改善を行っていきます。 本番環境へのデプロイ頻度 マージから本番環境へのデプロイまでのリードタイム KINTO FACTORYプロジェクトでは、サービス成長を一緒に実現してくれるチームメンバーを募集しています。この記事やKINTO FACTORYに興味を持った方は、ぜひ以下の求人一覧もご覧ください! 【KINTO FACTORYフルスタックエンジニア】KINTO FACTORY開発PJT/東京 【KINTO FACTORYバックエンドエンジニア】KINTO FACTORY開発PJT/東京 【KINTO FACTORYフロントエンドエンジニア】KINTO FACTORY開発PJT/東京
アバター
はじめに KINTOテクノロジーズでQAグループのリーダーをしている遠藤です。 今回は、QA業務を日々行っていく中で、QA業務に携わっている人であれば誰しもあるあると思われる事柄を題材に、普段我々が行っている取り組みを通して、QAの作業風景をご紹介できればと思います。 既にQAの作業に関しては、 マネージャーのzumeが記述している QAグループ紹介 メンバーのokapiが記述している QA業務の認知度向上 がありますので、合わせて読んでもらえればと思います。 本記事をきっかけに、 『自分たちの現場はこう対応しているよ』 『この作業はどう対応しています?』 など、いろいろご意見いただけると幸いです。 QA作業の流れと注意している視点 QAの作業については、主に 開発プロジェクトのQA実施 保守改修のQA実施 テスト自動化 QA内部改善 QA作業の改善活動 勉強会 があります。  今回は、この中で、プロジェクトに対して行うQA作業の流れを以下に示し、意識している視点についてご紹介します。 それぞれのQAへの思い QA作業の概要に関しては、入社時のオリエンテーションや、各プロジェクトのkickoffで説明しています。ですが、プロジェクトに携わるメンバーは誰もが経験豊富な社員なので、それぞれの経験に基づいたQAへの理想イメージを持っています。そのため、QAではこういうところまで見てもらえるのでは?といった認識相違は担当者間でもしばしば起こります。ただ、QA範囲の認識違いはあっても、根本的には共通して  「品質で漠然と不安が残る部分を、QAのタイミングで何とかしたい」 と思っている方が多いかなという印象です。この不安部分にいかに寄り添い、不安を解消し、無事リリースを迎えるかがポイントになるかと思います。 まず最初に、Kickoffのタイミングで関係者にQA作業をイメージしてもらえるように、大きく下記の3点を説明しています。 ①全員で一緒に品質を作りこんでいきましょう!! ②品質面で何か不安を感じたらいつでも遠慮なく相談してください ③QAの指摘は絶対ではなく、プロジェクトとして対応可否を検討しましょう 品質は「一緒に」作りこむ!! ①は、言わずもがなですが、QAだけの力では品質は向上しません。前述の認識相違の結果として、 単体レベルの細かい部分の確認 ソース、データの流れを意識した確認 等の、ホワイトボックステスト寄りの依頼が出てくることがあります。 QAの作業はブラックボックステストが中心であり、ホワイトボックステストはQAよりむしろ開発側での実施が適しているので、そうお伝えしています。 このような依頼があるのは、今までQAで対応してもらった経験があったり、自分達では気づかない品質の不安をQAなら「きっと」見つけてくれる、という期待があるからかと思います。 QAの役割として、システムテスト、受け入れテスト、各工程のタイミングで確認することについては、関係者間での認識がある程度一致していても、その人の経験によっては、QAがソースやデータの流れを意識したシステム寄りな確認をしていた、ということもあれば、QAがユーザー視点を意識した確認を中心にしていた、ということもあります。 ただ、全般的には、QAとは開発全工程の標準化を含め、独自の指標を持って品質管理をサポートしてチェックしている部署、として幅広く認識されていると思います。 QAとしてそれぞれのその思いに応えたいとは思うものの、QAの気づきも限界がありますし、テストの期間も無尽蔵にあるわけではないので、限られた期間の中で、目標の品質を保証するべく、品質を「一緒に」作りこむという意識をもってもらうことが必要です。 ただ、QAが認識した要件を検証、指摘し、それを淡々と解消してもらうという関係ではなく、QAの仕様理解やテスト計画、テスト観点のタイミングで、 要件を 『一緒に』 すり合わせ 確認観点を 『一緒に』 認識合わせを実施し 改善内容を 『一緒に』 考える ことで、品質にとって良い効果が生まれると思って活動しています。また、そこでしっかりお話しをすることでテスト設計にいかすことができます。 開発側の不安は、品質向上のヒントの泉 テスト観点のレビューなどで、開発側が思っている不安の内容が具体的になっているものは認識できるのですが、漠然とこの処理が怖いという内容のものもあります。この、具体的には言えないけどここの機能が品質的に不安、という情報は、QAがテスト観点を纏めたり、テスト設計をする上で重要です。根拠はないが、漠然とこの処理が不安というのは、例えば、コーディングが複雑とか要件がきちんと定まってない(処理としては要件を定めたつもりでも、細部まで詰め切れたか不安)など、特に問題なく開発できているので言葉にはできないけれども、なにかしら品質上不安に感じるところがある、という開発に携わっている方の 『勘』 ではありますが、意外とテストをしてみると想定していなかった不具合が発生したりするので、軽視できません。 QAとしては、要件を確認し、具体的な不安点に対して、テスト計画&設計を行いますが、こういうお話こそが、QAとして試験を強化できるヒントであり、想定より厚めにテストを実施する対策を講じることができます。 QAの工程では、本来、単体・結合レベルの不具合を見つけるというよりは、要件に基づいて何百ケースを実施して、重要、もしくは、致命的な不具合を1件見つけることが腕の見せどころではあるので、この『勘』が外れることの作業の無駄を考えるより、そのヒントを多く聞き出せる雰囲気づくりをして、自由に意見を言ってもらい、確認観点の想定以外で強化できる点を認識することで、品質向上に向けたテスト設計を行うことができます。 例えば、テスト観点の打ち合わせの段階で、 「こういう観点で進めます何かあればご連絡ください。」 で終わるのではなく、一言、 「今回の仕様でどこか気になる点はあります?」 と付け加えることで、「そういえばね・・・」みたいな話しになることがあります。不安点はあるものの、漠然としていて、何を伝えればよいのかわからない、伝えることは無いなと思うより、QAにとりあえず話してみよう!!という雰囲気づくり、そこには、前述した、「一緒に」という姿勢も合わせて対応することで、検証観点の強化、ヒントの泉がそこには隠されています。 QAの指摘に惑わされず、本来のゴールを見失わない 一方的にQAの作業スコープしかテスト実施しないとお伝えすると、QAは 『コードも理解せず、要件をなぞるだけ、細かい指摘ばかりで、リリーススケジュールに影響を与えるところ』 というような誤解をされやすい部分もあります。 ですが、前述のような説明を事前に行うことで、QAは『一緒に』品質を作り上げていく集団であると、180度違った見方をしてもらえることにも繋がります。 とはいえ、見方が変わったからこそというのもありますが、逆に、信頼してもらえることによって、QAの意見を全て受け入れ、テスト実施時に『指摘』したものを全てクリアしないとサービスインできないのでは、と不安に思う方もいます。 ここでいうQAがテストを実施して『指摘』するのは、下記の三つになります。   不具合:QAが認識している仕様と異なった動作(表示)を指摘 改善要望:仕様としては問題なく、機能として改善したほうが良いのではないかという提案 質問:仕様で不明確な点を質問 不具合に関しては、こちらが認識している仕様と異なっていた場合に指摘を行い、開発側で確認の上、改修し、改修後にQAの確認作業が入ります。改善要望に関しては、仕様としては問題ないが改善したほうが良いのでは、という提案になります。例えば、その他の画面は右上にボタンがあるのに、途中の1画面だけ左上にあったりした場合、ボタンの機能としては問題ないが、右上の方が良いのではないか?といった内容です。質問に関しては、テストを実施してみると不明確な仕様部分に気づき、不具合として挙げる前に確認、という点から指摘します。 リリースまでにこれらの指摘を全てクリアにするべきかというと、そうではありません。全ての指摘の改修対応を行い、質問も解消されることが一番望ましいですが、プロジェクトの状況によっては何らかの理由で全て対応することが難しい場合があります。全ての対応が難しい場合、指摘した事象の重要度、優先度を加味して、サービスインまでに対応すべきかそうでないかの判断をQAではなく、プロジェクトとして判断してもらいます。 QAの作業は、要件をもとに 『本来こうあるべきだ』 という内容のもと、テスト実施をしていきます。当然、要件をしっかり詰めたはずでも、仕様が不明確な部分が出てくることもあります。それらの全てが対応されることも望ましいですが、限られた時間の中で、QAの指摘を通じて、最終的な仕様を確認し整理することで、本来のゴールを見失わないことが重要になります。 そこで、③のお話しになり、プロジェクトとしてどういったサービスをどのタイミングで提供するかを整理し、ゴールを見据えたうえで、そこに向かって目標の品質を作りこむ。そこにQAは、しっかり品質面でサポートしていくことが重要になります。 ここで、時間が無いのでやりたい要件だけ満たせばその他の品質に目をつぶって妥協してサービスを提供する、とも聞こえるかもしれませんが、そうではありません。 QAは、定義された要件が十分満たされているか、というプロジェクトのゴール設定に向かって対応するとお伝えしましたが、あわせて、使用されるお客様に不利益になるような問題に関しては粘り強く関係者と議論します。決して妥協するのではなく、また、QAの指摘を一方的に押し付けるのではなく、プロジェクトのゴールやサービスの提供を受けるお客様を意識して、プロジェクトとしてどう対応するのかを『一緒に』考えて、対策を講じます。 万が一、リリース後に致命的な不具合が出ると影響が大きいので、QAもしっかり付き合い品質向上に貢献していきます。とはいえ、規模の大きい案件ほど、懸念部分は②のヒアリングで早めに対処していくのと、開発工程の特性上、最後の最後で致命的な不具合が発生することはまずないので、QAの作業が理由でリリース日が変更されることはほぼありません。 あくまで目標のリリース日に向かって、『一緒に』目標の品質を作りこんでいく姿勢が大事になります。 なので、繰り返しにはなりますが、QAとしては、ただ指摘して改修を求めるのではなく、プロジェクトの本来のゴール、例えば、目標の期間までにお客様に提供したいサービスを確認し、そこを見失わず、お客様に不利益やご不便が無いことを配慮しつつ、プロジェクトで合意した品質の確保のため、とことん付き合い『一緒に』品質を作りこみます。 さいごに 今回は、QAとして取り組む姿勢や、コミュニケーションで気を付けている点を中心にご紹介しましたが、技術的な側面で言いますと、QAは各プロジェクト、プロダクトを横串でかつ、俯瞰して見れる唯一の部署です。その点ならではのサポートや、その他、テスト計画やテスト観点、テスト設計の仕方などについて、機会があればご紹介しようと思います。 今回、ご紹介した作業の流れで、恐縮されながらQA依頼を受けることがあります。ここは『一緒に』の姿勢なので、遠慮せずに協力し合える関係性を築いていきたいと考えています。そしてリリース後には、 『とても助かりました!!』 『ありがとう!!』 と声をかけられます。その時私はいつも、いえいえ、QAの指摘に対して、真摯に向き合って対応してもらってありがとうございます。と、プロジェクト関係者全員の取り組む姿勢に頭が下がるとともに、感謝をしています。提供するサービスに対して、QAが品質面でしっかりサポートする、という信頼関係をしっかり築けるよう、日々QAとして精進し、そうやって『一緒に』つくったサービスがお客様にとっても役立つものになれば、それこそがQAの醍醐味というものではないかなと私は思っています。今回の記事でQAという仕事に興味を持っていただければ幸いです。また、同じQAという立場で意見交換できればと思いますので、ご意見お待ちしています。ご意見はTwitterまで。 https://twitter.com/KintoTech_Dev/status/1619979941856280577?s=20
アバター
こんにちは。KINTOテクノロジーズにてQAグループのグループマネージャーをしているzumeです。 QA歴はそれなりに長く広かったりするのですが、特にこれまで知見や情報を発信するといったことは意識していませんでした。とはいえここらでいったん自分の考えをまとめる時間が取れたらまとめたいなと思いつつ、除夜の鐘とともに2022年も終わっていたのでした。 普段仕事に追われていると難しいですね。と、自分で時間を作れないことの言い訳にしています。来月やろう、とあとN回つぶやいたらもれなく新しい年明けを迎えます。 テスト管理について さて今回は、私のグループで使用しているテスト管理ツールのメリットや、導入に至るまでの経緯についてご紹介したいと思います。 本記事をご覧のQAエンジニアの皆様、テストケースの管理はどうされていますか? すでに何かしら、有償のテスト管理ツールをお使いの方もいらっしゃるかもしれません。 一般的に、テストケースやテスト実施の管理にはExcelやSpreadsheetが使われる傾向があります。ただ、ExcelやSpreadsheetを使ってテスト管理を行う場合、以下のような課題がありました。 テストプロセスの課題 - 課題 - ⇒懸念(として起こり得ること) テストケースの構成は、テスト設計者によって属人化しがちであり、ケースの項目分類や形式も異なる ⇒設計担当者が変わった場合に作業引き継ぎが大変 ⇒フォーマットが共通化されていないため、プロジェクトが変わるとケースの解読に時間が掛かる ケースを確認するには、都度ファイルを開いて中身を見なければいけない ⇒チーム内でのドキュメントやノウハウの共有が難しい 関係者(QA以外のステークホルダー)がテスト内容、結果を俯瞰しづらい ⇒QA側で報告用にレポートを整える必要がある リグレッション実施の際、テストサイクルの度にファイルを新しくする必要がある ⇒どのケースを再利用したのかが把握しづらい テストケースの変更履歴や更新経緯を追いにくい ⇒ケース内容の更新等、メンテナンスの際に時間が掛かる(加えてExcelはonlineでの複数同時編集に向かない) テストの実行結果が手入力のため、正確な実施時間までは分からない ⇒不具合発生時刻の特定がしにくい テストケースとバグレポートの紐付けが出来ていない ⇒機能毎の不具合発生率などの集計ができない(がんばれば手集計で出来なくもない。けどしんどい) などなど。 これらの課題を解決する手段として、テスト要件管理、テストケース作成、テスト実行、テストレポート など、一連のテスト活動を支援するツールの導入を検討しました。 というか、はなからExcelやSpreadsheetを使うことは考えていませんでした。 なぜなら一度Excel運用が浸透してしまうと、そこから脱却するのに時間が掛かるのも経験則で分かっていたからです。 導入するツールの検討 当初、候補に挙げていたのは以下 TestLink :オープンソースのWebベーステスト管理ツール。無償。 TestRail :Webベースのテスト管理ツール。有償。 Zephyr for JIRA :JIRAのプラグイン。有償。 (2021年にZephyr Squadという名称に替わっていました[^1]) [^1]: Zephyr for Jira is now Zephyr Squad , SmartBear Software., 2021 TestLinkは、元々自分がこれまで過去の職場でも使っていた実績があった、というのが理由のひとつ。 ローカル環境でもDocker立ててインストールすればとりあえずすぐ試せる、という利点もあります。 実際、検証用のMac1台をTestLink兼用にしていたこともありました。 ですが、私がKINTOテクノロジーズに入社したのが2020年3月(当時は株式会社KINTO在籍)、ツール導入のターゲットとなるプロジェクトのリリース予定は2か月後の5月、というスケジュール。しかも新型コロナウイルスの感染拡大による最初の緊急事態宣言が、その間の4月に発令される。 というなかなかしびれる状況の中、その時最も適した選択肢として採用したツールはどれでしょうか? 実は、 Zephyr for JIRA なのでした。 すでに社内で使われていたJIRAのアドオンですぐ導入できた、また、コロナ禍で予期せず発生したリモートワーク=社外からの利用を考慮しても便利、というのが大きかったです。 有償とはいえ、5月のリリースさえ乗り切ればそのあとの利用継続については再検討する、という考えの元、運用を開始しました。 当時のメモ見ると JIRAプラグインだから言語設定変えればいけるかと思ったら日本語は一部のみ対応っぽい や、 Zephyrのレポートはシナリオ単位であって、ケース(ステップ数)単位でのレポート機能無いんやなやっぱり など ^2 、 試行錯誤していた形跡が残っていましたね。懐かしい。 すぐ導入できたといいつつも、使う側の慣れやスキルも必要不可欠ではあります。そういう意味でも、当時の混沌とした時期を柔軟に乗り切ってくれたメンバーには今でも感謝しかありません。 Zephyrを使ってみて もう3年近く(!)昔の話にはなりますが、Zephyr for JIRAを使ってみた感想としては。 JIRAのプラグインなので、テストケースは任意の課題タイプを選択する手順で通常の課題と同じ様に作れます。 ケースの項目には、ステップと期待値、結果、ステータス、コメントの他にファイル添付があり、ステップごとに画面のキャプチャを証跡として残せるのは便利だなと感じました。 一方で、プラグイン自体の読み込みに結構な時間が掛かっていました。画面を遷移するたびに、数秒掛かるというのが悩みでした。こちらもAtlassianのコミュニティに同様の お悩み相談 が投稿されていたので、Zephyr固有の問題なのかもしれません。 そしてTestLinkへ さてどうにか当時のリリース予定スケジュールを乗り切って、2020年5月にプロジェクトが手を離れたあとのテスト管理についてですが。 あらためて費用面も踏まえて検討しました。 JIRA連携が前提なのと、ツールの利用者数は10~20人程度、とした場合に 2020年当時の価格は以下で、 Zephyr for JIRA:11-100 Users ¥510/user/month ⇒20人利用で¥10,200/月 TestRail:1-20 Users $32/user/month ⇒20人利用で$640(約¥83,200)/月 2023年現在の価格は以下です。 Zephyr Squad:11-100 ¥570/user/month ⇒20人利用で¥11,400/月 TestRail:1-20 Users $37/user/month ⇒20人利用で$740(約¥96,200)/月 当時とは料金体系が若干変わっていたのと、やはり多少値上がりしてますね。 ※いずれも1ドル130円として換算 ぱっと見、Zephyrはお得にみえますが、JIRAのプラグインということもあり、実はJIRAの既存ユーザーライセンス数と同数分が必要になります。 そこに関しては社内でも開発部署全員が使うわけではなく、QAメンバーのみの利用と考えると、組織拡大に伴って費用がかさんでいくのは避けたい。 にしてもTestRailはお高いですね。コストを考えると、フリーのTestLinkに勝る選択肢が見当たりません。 TestLinkに関してはUIがイマイチというのはありますが(オープンソースだから贅沢言わない)、テスト管理ツールとして、少なくとも先述の課題はそれぞれ以下のように解消できます。 テストプロセスの課題とその解決策 テストプロセスの課題 ツールを導入した場合 懸念に対して 1. テストケースの構成は、テスト設計者によって属人化しがちであり、ケースの項目分類や形式も異なる テストスイートやテストケース、テストステップ等を、一定のフォーマットかつ適切な細かさで記載することで、ある程度粒度が揃う 引き継ぎもケースの解読も楽! 2. ケースを確認するには、都度ファイルを開いて中身を見なければいけない 実施項目の視認性が高く、テスト要件とのトレースもとりやすいため、カバレッジがわかる チーム内外でのドキュメント共有が楽! 3. 関係者がテスト内容、結果を俯瞰しづらい テストの進捗状況をリアルタイムで把握できるのと、結果をレポートで一見できる QA側でレポート作成する必要なし! 4. リグレッション実施の際、テストサイクルの度にファイルを新しくする必要がある テストスイート単位での再利用が可能 再利用の対象がつかみやすい! 5. テストケースの変更履歴や更新経緯を記録に残しづらい テストケースの追加・変更に加えて、その履歴が残せる ケースのメンテナンスが楽! 6. テストの実行結果が手入力のため、正確な実施時間までは分からない バグレポート、実行時間、実行者の記録も正確にロギングされる 実施時間帯を絞り込める! 7. テストケースとバグレポートの紐付けが出来ていない テスト進捗率や不具合発生率など、要件/リリース毎のトラッキングがしやすくなる 機能毎の不具合発生率等、集計がしやすい! というわけで、2020年6月以降はTestLinkを導入することにしました。 まあ、楽と書いてしまうとメンバーからは怒られそうですが、実際のところツールも万能ではないので、Excel等のデータファイルを使うよりかはだいぶ楽ですよ、という感じでしょうか。 追記 フリーといっても、運用に伴うインフラコストは掛かります。TestLink用にAWSのインスタンスを使わせてもらっているのですが、年間数万くらいの費用感です。なお利用開始から3年近く経ちますが、これといった大きな問題もなく今のところ運用出来ています。 今回は、QAグループにおけるテスト管理ツールとして、TestLinkの導入までをお伝えしました。 実際のプロジェクトでのTestLinkの利用例、JIRAとの連携等については、次回以降でまた機会があればお話できればと思います。
アバター
こんにちは、またはこんばんわ。 美味しい紅茶を飲みながら機械の分解をするのと写真を撮るのが大好きなグローバル開発 UIUXチームのazがお送りします。 UIガイドラインとは そもそもUIガイドラインとは何でしょうか? どんな人が使うものでしょうか? 誰がHappyになれるのか? 少しずつ紐解いていきたいと思います。 ブランドガイドラインとUIガイドラインの違い 一般的なブランドガイドラインとUIガイドラインの違いについて説明します。 KINTOにも一般には公開していませんが、ブランドガイドラインは存在しています。 ブランドガイドラインとは? 主に「ブランディング」として守るべき、以下のようなルールが提示されています。 ブランドの思想、理念 ブランド名やライティングの表記ルール 利用するカラーやイメージ 求めるべきユーザー体験 ※画像はイメージです このルールをベースにデザイナー達が表現方法やデザインを考えていきます。 参考: ブランドガイドラインとは? UIガイドラインとは? 一方UIガイドラインはデザインシステムなどを用いて「これを使えばOK」という具体的な事例を提供します。 ボタンやテキストなどに使う色や形 画面レイアウトの比率 アイコンやイメージの使い方 基本的にデザインも実装もそのまま使えばいい状態のパーツやスペックが載っています。 要件から実装すると何が起こる? 例えば「KINTOらしい」ボタンを作るにはどんな条件があるでしょうか。 決まっている色を使って 四角くて 読みやすいラベルがあって ボタンだと分かるもの イメージできましたね。 出来上がったものがこちら。 どれも条件は合っていますが、思っていたボタンと違ってる部分が出てしまいました。 1段目…4方の角丸がない 2段目左…ボタン内の余白の縦横比がおかしい 3段目左…他では使わない影が入っている 同じ条件で同じものを作っているのになぜ細かい部分で揃わないのでしょう? 何が違っていたのでしょうか? それは 共通認識 です いつものメンバーがいつものように制作できれば同じものができる可能性が高いですが、実際にはメンバーもチームも流動的です。 出来てから「あぁ…思ってたのと違う…」となると作り直しが発生するのは時間も気持ちもロスが大きいです。 UIガイドラインではメインボタンは「角丸:30px、上下余白:10px、幅:画面比1/12…」といった細かい数値まで設定しているのでアウトプットにずれが生じなくなります。 UIガイドラインの考え方・使い方 フォーマットに則ってデザインすれば、デザイナーもフロントエンドもラクになります。 実際によくある例で説明していきたいと思います。 デザイナーがいなくてもガイドラインに沿えばOK 避けては通れないテキストサイズやスタイルの設定も、使われるスタイル数が決まっているので細かい設計に悩む必要が少なくなります。 サイズや余白が適切に設定されておらずクオリティの安定しないケースもよくありますが、 ガイドラインで設定された余白に沿っていればデザイナーが調整しなくても、見た目が崩れることなく画面が構成しやすいです。 画面サイズ問題が減る デザインファイルの立ち上げ時によく起こる「スクリーンサイズ何pixelにするか」問題。 ガイドラインで比率やブレイクポイントの設定しているのでそこで差異がでないようになっています。 「いつもの仕様」で作れるものが多い 入力フォームの配置 同じような内容や項目が多い代表例が入力フォームですが、項目追加や順番の変更も圧倒的に多いです。 最近のプロジェクトでも変更が何度もありましたが、ガイドラインを使ったデザインだったためデザインファイルの修正と実装を並行で行うことができました。 メッセージの出し方 結果画面やエラー画面のように文言や組み合わせが多数ある場合はとても多いです。 ステータス毎のアイコン、テキスト間のレイアウトが決まっているので、特殊な例をのぞけば全てのパターンを網羅して準備する必要がありませんでした。 困る人が少なくなーれ! 経験値やスキルの差があっても最低限同じアウトプットを出せること 口頭や文字での伝達ロスを減らし共通認識を保つ この2つがガイドラインの大きな役割です。 困ったらここ見ればいい!解決! となれるようにこれからも拡充していきたいと思います。
アバター
Hello! 👋 This is Ruoyang Wen from Global Group, KINTO Technologies . I work as a full-stack engineer in Global KINTO App team. You can read more about the Global group here. Objective As a startup company, there are a lot compromises within our first generation product. For the greater benefits, apart from the service we provide, workflow is also where we need to improve. This article shares why and how we changed our workflow from Git-flow to GitHub-flow. Attempt As part of the Toyota group companies, we implement the principle of improvement (or kaizen in Japanese) in our daily work. With customer feedback and analytical data, there are numerous amount of improvements that we can do to better serve our customers. Through automation, the process of kaizen can be sped up rapidly. Committing source code is a daily job for engineers, it’s not just about handing over our daily work but also testing the code in our server environment. We introduced Continuous Integration and Continuous Deployment (CI/CD) practice into the team to help us reduce repetitive work. CI/CD is a method to frequently deliver apps to customers by introducing automation into the stages of app development. ^1 We were following Git-flow to manage our source code and development progress. Soon after CI/CD was implemented we found that our work process did not get any faster. Reasoning git flow diagram It was a mess to use or manage the CI/CD scripts. We have so many different branches, when merging branches from one category to another different scripts are needed: feature-to-develop , develop-to-release , release-to-main , hotfix-to-main , main-to-develop , develop-to-feature , etc… Conflicts would show up everywhere! And they cannot be solved via automation scripts. The automation increased our daily workload, why?! After some research we found that: Git-flow, by definition, is a manual/time delayed integration workflow. OK, we need a new workflow then. Reattempt Among several workflows we found during previous research, we decided to give GitHub-flow a try as we store our source code on GitHub. github flow diagram This time we only have two type of branches: the main and the 'change-of-anything' , and we block any commits to main directly, the only way to update main is to do a Pull Request towards main . As a result, there is only one CI/CD script required and any conflicts can be discovered and resolved during Pull Request . New Issue We have simplified our branching strategy; we have only one script to do CI/CD; we have Pull Request to help managing conflicts. All happy? Yes, but no. Let’s check the pros. We have handed over the repetitive work to automation scripts, so everyone can do more productive work. The release and deploy process is handled by scripts, so any change we made locally can be deployed to server within 2 minutes. However, there are the cons. We lost feature/version control of our system. As features are no longer stored in individual branches but in main directly after Pull Request , we cannot decide for next version which features may be on-hold and which may be released. Also, the main branch just kept updating with latest source codes, version number can ramp up hundreds in a single day. Next Step Fortunately we are not alone. There are people who already faced these issues, and we can follow their journey and see their solutions. Feature toggle can be used to enable/disable any feature at any time. It can be stored in parameters, so no new release/deploy needed; even better than Git-flow which is to release different versions with different combinations of features. Version number only ramps once we deploy a specific commit to the production environment. For the rest of the environments, we use the commit hash as the version number. This helps us to locate any bug or defect to that commit instantly. Summary Undoubtedly there is no perfect solution to solve all problems. GitHub flow has its flaws as well, my team and I are still working together to kaizen not just our products but also the way we work. Personally, Git-flow is like a web portal: it categorizes everything with defined rules; if no one makes any mistake it will work just fine. On the other hand, GitHub-flow is like a search engine: we tag the version number to the commit we release/deploy to the production environment, with other environments having the commit hash as version numbers; so if there is any issue, those versions can be easily found through search. References トヨタ生産方式 What is CI/CD? What is Continuous Integration? Git flow for agile teams is a no no Please stop recommending Git Flow! Git Flow vs GitHub Flow GitHub flow - GitHub Docs Continuous Integration Contradicts Features Branches! How to Achieve Continuous Deployment with Feature Flags
アバター
概要 グローバル開発G業務エンハンスチームの森、榊原、Floです。グローバル開発G主催のKINTO Global Innovation Daysの連載記事2本目となります。 前回の記事はこちら 👈 本記事では、12/14~12/19の4日間で行ったプレイベント3本と、Innovation Days当日の様子をお届けします。一連のスケジュールは以下の日程で行いました。 Design Dash Workshop 組織が拡大するにつれ、メンバーのタスクが細分化されるため、同じチーム内であっても役割が異なるとどういった考えでタスクをこなしているのか見えにくくなってきます。この課題を踏まえ、ターゲットの抱える課題を解決するためのソリューションを検討するワークショップを行いました。 あるお題に基づいてインタビューを行い、インタビュイーの悩みを解決するプロダクトを企画、提案、デモする形式です。 このワークショップの目的は以下の4つ: Ideation Rapid Desision Making Discovery Practice Improve Communication 学びを参加者自身に感じてもらいたかったので、あえて目的を言わなかったのですが、後日行ったアンケートでは72%の参加者に満足いただき、「課題発見力を得られた」「限られた時間内でどのようにソリューションを見つけ出すかを知った」「ユーザーセントリックな商品設計の仕方を学べた」「チームワークがとても重要だと理解した」などの声をいただき、自然と目的を達成することができました。 Communication Workshop 各自のタスクが細分化されてくると、プレゼンやアウトプットの機会も減ってきます。参加者の中にも普段はほとんどそういった機会が無いメンバーもいました。「機会」を与えることは本イベントの最終ピッチで実現できましたが、その事前準備として「相手が求めているものを把握する力」が必要だと考えました。相手の立場によって求めているものは異なり、それを把握できないと的外れなアウトプットをしかねないからです。事前準備として、ステークホルダーの立場と状況をしっかりと把握し、何を求めているのか、相手は今何を欲しているのかを把握できるようなコミュニケーションのワークショップを設けました。 コピーライター、デザイナー、開発者、ダイレクター、セールスという別のロールに分かれ、プロジェクトの各自の悩みを踏まえて共有し、それぞれのTO DOに落とし込んでいくというような形式でした。 こちらも56%の満足度を得られ、「必要な情報を相互へシェアする重要さ」「それぞれ違うところにフォーカスがある人たちの意見を集約し、どのようにDeliverableとして出すか学んだ」「異なるロールの視点を見ることができた」のような声をいただきました。 Toyota Way Workshop Innovation DaysをKINTOテクノロジーズで行うベネフィットとして、トヨタグループならではの学びを得たいという理由から、アジャイルの由来でもあるトヨタ生産方式を学べる機会を設けました。 具体的な方法については割愛しますが、トヨタウェイやトヨタ生産方式についてトヨタ自動車史や創業者の精神に沿って学べるセッションを設けました。ただ一方的なインプットではもったいないので、各自感じたことをチームで共有しあい、翌日からのInnovation Daysで大切にしたいマインドを宣誓していただきました。 これも72%と非常に高い満足度を得ることができ、「トヨタウェイが言葉としてだけでなく歴史を通じて理解できた」という声がありました。 Innovation Days 前日までのワークショップで学んだことを踏まえ、いよいよInnovation Daysの当日です。 当日のスケジュール🔻🔻 Opening Ceremony 事前収録した社長からのメッセージや細かいルール説明、アイスブレークを行いました。 Code of Conduct ルールはいろいろな事例を参考にしながら以下を規定。Code of conductを遵守することを強調しました ✨ 発表コンテンツ プレゼン時間の規定(pitch 5分 + Q&A 2分) 開発に使用して良いツール Deliverables提出方法 評価方法・ポイント 運営チームからのアドバイス ![code-of-conduct](/assets/blog/authors/M.Mori/20230314/code-of-conduct.png =450x) Icebreaker IcebreakにはTelestration = お絵描き伝言ゲームを使いました。けっこう難しかった様子。 Ideathon もともと検討いただいていたアイデアの種を育ててもらうところから始めました。自分たちのアイデアに対してValue Proposition Canvasを用いてターゲットと課題、そこに対するソリューションが提供する価値を検討してもらいました。 普段、グローバル開発Gではこういった検討はPdMが担当していますが、エンジニアも含めてチームで検討できる、滅多にない機会となりました。 Hack Start!!! さて、実際の開発スタートです。各チーム、オフィス内の部屋にわかれてそれぞれの開発をスタートしていただきました。様々なメンバーがいるのでコードを書くだけでなく、翌日のPitchに向けた準備や、デザインなどを担当するメンバーもいます。 2日間の内、実際の開発時間は7.5時間程度でした。限られた時間の中で、どこに優先度を置いてどこまで開発し、また、Final Pitchではどのようなポイントを訴えるかを検討してもらいました。 2日間のInnovation Daysではランチセッションもありました。Day#1はチームでランチに行ってもらうことで、アイデアを深めました。Day#2はチームの1人が別のチームに交じってランチをすることでアイデアや開発内容に対するフィードバックをしてもらいました。交流の目的もありますが、雑談の中で生まれるアイデアはInnovation Daysのみではなく、実業務に対しても良いものが生まれたようです。 Final Pitch!!! Day#2の夕方、全員で集まってPitchをしてもらいました。持ち時間は各チーム5分+QA2分です。 それぞれ詳しいアイデアは割愛しますが、既存プロダクトの改善提案や新しい機能の提案、新サービスの提案など、各チームユニークなPitchとDemoを行ってくれました。 尚、公平を期すために発表順はその場でくじ引きにしました。自分の番がいつ来るかわからないのでドキドキです! Judgement 全てのPitchが終了してから、グループマネージャーとアシスタントマネージャー4名による評価の時間です。うち2名はリモート参加だったので、4人で集中してJudgeされていた姿が印象的でした。 Judgementの評価軸は以下 🔻🔻 評価軸 / Evaluation axis 割合 / Ratio オリジナリティ / Originality 10% UIUX 10% 技術力 / Tech Skill 30% チームワーク / Team Work 20% 実用性、事業性 / Practicality, Feasibility 20% ワクワク感 10% 評価の結果、優勝したチームにはオリジナルスマホスタンドとケーキの賞品を贈呈しました。優勝したチーム以外も楽しそうに参加いただいたのが印象的で、会場内はとてもいい雰囲気でした。 ※撮影のタイミングだけマスクを外しました。 まとめ KINTOテクノロジーズ初のイベントとしてグローバル開発Gが企画したイベントでしたが、もともと目的としていた「コミュニケーション活性化」はもちろん、KINTOに対する新しいアイデアや普段の業務では使うことのできなかった新しい技術の利用など、様々な結果を出すことができました。 参加者はもちろん、マネージャーや役員など、全てのステークホルダーにとって好評なイベントとなりました。我々は運営側でしたが、ハプニングへの対応、ファシリテーション、タイムマネジメントや多方面の調整力など、運営チームとしても様々なスキルを得られました。そして何より、チーム内の絆が深まりました ✨ さて、ここまで運営チーム目線で企画段階の様子と当日の様子を記事にしてきましたが、参加者目線ということで優勝チームのインタビュー記事を掲載予定です。乞うご期待!
アバター
概要 グローバル開発G業務エンハンスチームの森、榊原、Floです。グローバル開発Gでは12/14 21の6日間にわたり、「KINTO Global Innovation Days」と称した社内Hackathonのようなイベントを開催しました。12/14 19までの4日間でセミナーを3本と、実際の開発は2日間の構成です。このようなイベントを開催するのはKINTOテクノロジーズ内でも初めてのことでした。本記事では本イベントに関する連載記事の1本目として開催に至るまでの様子をお伝えします。 きっかけ KINTOテクノロジーズは現在約300名で構成され、2年ほどでおよそ倍の人数になりました。その中で、グローバル開発Gも現在60名の大所帯です。組織としては5~10名程度のチームに細分化され、それぞれタスクをこなしていますが、チームを横断したコミュニケーションは常に課題でした。同じグローバル開発G内でも顔と名前が一致しないこともしばしば…。また、コミュニケーションとスキル向上のために内部で勉強会を企画・運営していたものの、どうしても一方的な知識共有になってしまっていました。 エンジニア自身が手を動かして学べる機会を模索していましたが、7月にグループ内の数名が Toyota Motor North America (TMNA) のHackathon に参加したこともあり、これを我々グループ内で開催することで、上記の課題解決に繋がるのでは?と考えて、8月末から計画をはじめ、提案に移すことにしました。 目的と開催時期 一般的にHackathonイベントから得られるベネフィットは様々ですが、今回我々は「コミュニケーション活性化」を第一目的としました。ビジネスに寄りすぎないことである程度自由な発想が得られたと思います。 また、開催時期については遅くとも2022年内開催を目標としました。グループ横断で携わっていた大きなプロジェクトが11月に目途が着きそうなこと、4Qまで差し掛かると先のタスクが読めないことが理由です。 調査とコンテンツ検討 初めてのイベント開催だったため、実際のイベントがどうあるべきか検討すべくまずは世界中のHackathon事例を調べました。調査担当は榊原です。 他社のテックブログやHackathonのイベントサイトを中心として様々なお手本を調べているうちに、パターンが見えてきました。そのパターンの要素をピックアップし、最も我々の組織や目的に則したポイントを組み合わせることによってInnovation Daysの内容を組み立てる事ができました。調査結果の例はたくさん取り上げることができるのですが、その中から3点解説します。 調査結果1:ベネフィット イベントの準備を進めるにつれて、参加者や関係者、ステークホルダーに対して、参加することによってどういった効果が見られるかを伝える必要性を感じました。例えば、組織へのベネフィットは知的財産権になるアイデアを得る機会であることや、メンバーのエンゲージメントを高め、新たな強みの発掘ができる機会であることを挙げられます。また、個人へのベネフィットは、普段の業務では取り組めないアイデアを考えたり、あまり関わることのない業務工程やメンバーと接することによって、いろんな面での学びとなることを訴えかけました。 調査結果2:コンテンツのアイデア 上記のベネフィットを踏まえ、コンテンツとして取り入れたのがセミナーです。Hackathonのようなイベントが開催される際には、ゲストを招いてトークを行ったり、Hackathonのテーマや目的に沿った講義やワークショップが開催されることがあることを知り、今回Innovation Daysでは普段経験しない上流工程のworkshop、コミュニケーションworkshop、そして「KINTOテクノロジーズが開催するHackathon」なので、トヨタウェイのworkshopを準備しました。 IT企業が開催するイベントといえばノベルティー!という方も少くはないかと思います。今回我々はステッカー、パーカーとクリアファイルを参加者とサポートメンバーへお渡ししました。他にも、最終ピッチや成果物の判定基準やルールの設定、コードに付与する時間、アイスブレイクやプライズ等も様々なイベントからアイデアを拝借しました。 ※イベントの名称が決まってからグローバルG内のUIUXチームにロゴも作成いただきました。おかげで素敵なノベルティーができました。Appreciate it a lot!!!! 調査結果3:テーマ設定 最後に取り上げたいポイントが「テーマの設定」です。多くのHackathonでは開催側からテーマや目的が絞って掲げてあり、各種テーマをスポンサーする存在もいる場合もあることからヒントをもらい、我々のイベントではマネージャーが「チャレンジテーマ」を1つ決め、各テーマのスポンサーとして「チャレンジオーナー」となって参加者にテーマの説明を実施しました。こういったテーマの設定によってマネージャーから参加者へのサポートも示すことができ、エールも送ることができました。 参考: Council Post: Four Tips For Running A Successful Hackathon Urban Mobility Hackathon Find & Organize Hackathons Worldwide - Mobile, Web & IoT Hackathon Guide テーマの検討 テーマの内容については、実際に当日評価をするマネージャー4名(グループマネージャー+アシスタントマネージャー3名)に4テーマを選定いただきました。 テーマ 1-2 テーマ 3-4 Encouraging members 社内で初めての試みということもあり、企画を始めてから調査、コンテンツ検討、テーマの選定を経てメンバー募集するまでに3ヶ月ほどかかりました。最終的にテーマが決定した11月頭にグローバル開発G全員を集めた企画説明を行い、11月8日にメンバー募集を実施しました。この際、公式イベント名を「KINTO Global Innovation Days」と題しました。 尚、参加者については全員強制で参加させる案もありましたが、自主性を尊重するために参加したい人に挙手する形式としました。実際に募集したSlack 🔻🔻 説明会でマネージャーからもエールの言葉をいただいたり、CEOやCIOのサポートもいただいている旨を伝えたりしましたが、当初はなかなか参加者が集まりませんでした。 そこで、ベネフィットなどをメンバーに直接訴えかけるようにしました。担当はFloです。オフィスで直接会話する際やDMなどでベネフィットを伝えることにしました。それによって、参加できないメンバーにその理由を聞いて改善できるからです。 まず、本イベントに参加することで、どのような体験ができてどのようなスキルが得られるか伝えました。普段使えないプログラミング言語を試せる、新しいツールを提案できる、優先度が低かった改善案を提案できる、など様々な体験が得られると訴えました。 また、イベント内での提案はグローバルGでのプロセス改善に活用されたり(テーマ 3)、新サービスとして製品化されたり(テーマ 1, 2)、もしくは他のHackathonイベントへの参加も検討され得るなど、当事者意識と投資意識にも訴えかけました。 中でも、我々が一番に心がけたのはサポート環境です。アイデアは評価されて表彰されますが、あくまで切磋琢磨するイベントなので、こういったイベントに参加したことがない、技術者ではないから貢献できない、自分は役に立たない、と思っている方にも、「普段体験しないことを経験できるイベントだからこそ参加してほしい」と説明しました。 会話の中で気づいたこともあります。開催日程がクリスマス前だったため、連休を予定したり母国へ帰省したりする方が複数名いました。そのため、イベントを数日前倒しすることにしました。各workshopの講師とスケジュールを調整し、最終的に12/14-19をプレイベント、12/20-21をInnovation Daysとしました。これによって参加できるメンバーが少なくとも2-3名増えました。 余談ですが、運営メンバーはたったの3名+サポート1名だったため、土日を挟んだのは我々にとっては好都合でした。1週間ぶっ通しでのイベント開催だったら体力的に厳しかったと思います。 グルーピングと事前ワーク リクルートの甲斐もあって、実際の参加者は30名となりました。グループマネージャーやアシスタントマネージャー、我々運営メンバーは参加対象外なのでグローバル開発グループの半数以上が参加してくれました。 Business development, PdM, UIUX, Frontend, Backend, Testing, DevOpsなど様々なチームから参加者が集まったので、それぞれを①できるだけ普段業務上関わらない人②パワーバランスを取るためにチームリーダーは分けることを条件として配分しました。5名×6チームです。(30名というキリの良い人数だったのでちょうどいい感じのメンバー数に分けられました 😊 ) チームメンバーは11/18に発表、その後2週間で以下を検討・提出してもらいました。 Team name Theme of choice Team leader 普段、グループ内で最もいろんな人と関わる我々チームとしては、参加メンバーがうまくコミュニケーションを取れるか?積極的にイベントに参画してくれるか?など親心的に心配しておりましたが、その心配は無用でした。参加者は自主的に参加してくれていることもあり、それぞれのチームがSlackの独自チャネルを作ったり、ミーティングを開催したり、思った以上に積極的に動いてくれたので、今後のイベントにも期待が持てました! 🎉 事前準備の振り返り 社内はもちろん、前職での経験も完全に何もないところからスタートした企画だったので、いろいろな調査や様々な方からのアドバイスを受けながら準備を進めてきました。特に承認プロセスは時間がかかりましたが、CIOや社長まで巻き込めたことは成果の一つであり、今後のイベントにも繋がる大きな要素だったかと思います。 また、アイデア出し(調査含む)、企画+上位層への報告、状況把握とメンバーの鼓舞、と業務エンハンスチームメンバーそれぞれが得意なことを組み合わせてうまくタスクを分散できたことで、構想から約4ヵ月の短期間で実行に移せた要因です。 プレイベント期間や当日も様々な課題がありましたが、その様子は次回の記事に記載いたします。 最後に 余談ですが、KUDOSや本イベントなどの企画は業務エンハンスチーム内の日常会話から生まれています。我々チームは会話をとても大事にしていますが、「 この課題を解決するにはこういう手があるかも 」「 こういうのあったらいいよね 」「 前職ではこういうことしていた 」などのカジュアルな会話から企画・実行・結果に残すことまでできる力を持った業務エンハンスチームを誇りに思います。
アバター
はじめに みなさまはじめまして。KINTOテクノロジーズのIT管理チームでコーポレートエンジニアをしておりますT.S.と申します。 こちらに IT管理チームの紹介記事 がございますので合わせてご覧ください。 私たちIT管理チームは日々KINTOテクノロジーズというエンジニア組織の生産性を高められる社内IT環境を目指して奮闘しております。 社内IT環境は様々な要素で構成されていて一度に全てをご紹介するのは難しいので、本記事ではデバイス管理についてご紹介できればと思います。 デバイス管理って? 前提 KINTOテクノロジーズでは全スタッフに1台ずつ ノートPC(Windows or Mac) スマートフォン を貸与しております。そのため社内の誰がどのデバイスを利用しており、デバイスの状態はどうか?といった把握・管理ができることで「快適な開発環境を支える」を実現しやすくなる訳です。 MDMとは? 前提の通り、全スタッフがモバイルデバイスをご利用されるということで、「Mobile Device Management(モバイル・デバイス・マネジメント)」ツールを導入しております。 そうです。一般的にMDMツールなどと呼ばれているものです。 MDMで何ができるの? そもそもMDMとはノートパソコンやスマートフォン、タブレット端末といったモバイル端末に対してデバイスの設定を実施したり、アプリケーション配布を行ったりといった管理・運用を行うツールです。 それだけならそこまで頑張って管理する必要があるの?と思われる方も多いと思いますが・・・ KINTOテクノロジーズにオンプレはありません よって日々業務に利用するSaaS(※)から見て信頼できるデバイス(≒管理されたデバイス)であるかどうかはセキュリティ的に重要事項です。しかしセキュリティがガチガチなだけでなく開発環境として利便性を落とさないためには、デバイスの何を管理しどこをお任せするか、きちんと考え運用する必要があります。 ※SaaS = 「Software as a Service」 クライアントに導入インターネットなどネットワーク経由で利用するサービス デバイス管理としてここは管理したい、利用者にお任せし端末利用の利便性を下げない区分けはこんなイメージでした 管理できた方が良さそう お任せできると素敵 ・セキュリティ関連ツールの動作 ・データ漏えい措置 ・紛失時等のデータ消去手段 ・不正な接続先への通信 ・資産管理 ・業務に必要なアプリケーション ・利用者ごとの環境設定 ・キーボード、マウス等の周辺機器 ・物理的な端末の保管、管理 KINTOテクノロジーズのデバイス管理 概要 結論としてKINTOテクノロジーズのMDMはこのような構成です。 項目 利用サービス IdP(※) Azure Active Directory Windowsデバイス スマートフォン Microsoft Intune Macデバイス Jamf Pro ※IdP = 「Identity Provider」認証サービスの提供、アカウント情報管理を行う仕組み 課題 KINTOテクノロジーズは急成長フェーズということで毎月多くのスタッフが入社されており、スタッフの数だけデバイスは増加しさすがに人力で管理運用し続けるの厳しい状況になります。 そこで以下の課題解決をするため、デバイス管理のシステム化に至りました。 デバイスキッティング工数 デバイス情報の管理 インストールするアプリケーションの管理 OSアップデートサイクルの制御 暗号化の適用、回復キーの管理 リモートロック、リモートワイプ 導入 改めて業務環境を考えると・・・ 業務環境 PC -> WindowsとMacが選択可能 スマートフォン -> 全員支給 環境 -> フルクラウド グループウェア -> Microsoft 365 という前提から、WindowsデバイスとスマートフォンはMicrosoft 365と親和性の高いAzure Active Directory + Microsoft Intuneです。 MacもMicrosoft Intuneで管理し、MDMプラットフォーム統一!という手段もありますが、ことApple製品運用においては圧倒的な実績もあり、設定の同期が早く管理ポリシー・項目の柔軟性からJamf Proで管理することとしました。 構成はこのようなイメージです デバイス管理の概要 結果 No. 項目 結果 1 デバイスキッティング工数 →設定周りを含めキッティング時の工数は下がった △ 2 デバイス情報の管理 →さらば台帳、管理コンソールようこそ ○ 3 インストールするアプリケーションの管理 →個別管理が一元管理に △ 4 OSアップデートサイクルの制御 →デバイス利用者任せから一元管理に ○ 5 暗号化の適用、回復キーの管理 →1台1台の作業からシステム管理に、特にキー管理のシステム化は嬉しいポイント! ○ 6 リモートロック、リモートワイプ →対応可能に! ○ 以上の結果から当初の課題に対しては概ねクリアでき、ようやくデバイス管理のスタートラインに立てた状態ではないでしょうか。 スタッフの皆様により良い体験をお届けするため、デバイス管理運用の改善を進めていきたいと思います。 今後やっていきたいこと 1. ゼロタッチキッティングの実現 キッティング要件等を整理しゼロタッチを実現し、デバイスにかかるオンボーディングの時間を削減し、業務に向けたオンボーディングの時間にできればと思います。 2. アプリケーション運用の効率化 一元管理は実現できたものの、必要になった際より柔軟にタイムリーにお応えできるようここの運用をより洗練させていきたいと思います。 3. デバイス状態の管理運用 MDMに登録された管理対象デバイスとしてだけでなく、インベントリ等のデバイス状態に応じた細かい制御・運用を実現しデバイスの状態をより良い状態に保てるようにしていきたいと考えております。 最後に ここまで読んでいただきありがとうございました。これからも全社に貢献し事業に貢献できる社内IT環境を目指し業務に精励していきたいと思います。 We are hiring! KINTOテクノロジーズでは一緒にモビリティの未来を創る仲間を募集しています。カジュアル面談なども行っておりますのでご興味をお持ち頂けましたらぜひお気軽にご連絡ください。 https://www.kinto-technologies.com/recruit/
アバター
はじめに グローバル開発グループで言語ローカライゼーションをリードしている榊原です。スペインと日本、2つの文化間で育った私は、異文化間コミュニケーションに深い関心を持ってきました。2回に渡ってお届けするテーマは「ローカライゼーション」です。これまでの私の学習と経験を通じて、その重要性をお伝えした上で、このテーマに少しでも興味を持っていただくのが本記事の目的です。 前編では、弊社における言語ローカライゼーションの考え方や取り組み方について、後編では、これまでの取り組みについて、掘り下げてご紹介します。 翻訳、ローカライゼーション、インターナショナライゼーションの違いとは? 翻訳 の定義は複数ありますが、私が共感したのは、Hatim and Mason(1997)の「翻訳とは、文化や言語の境界を越えて、別のコミュニケーション行為を中継しようとするコミュニケーションの行為である。(筆者翻訳)」という言葉です。 一方、 ローカライゼーション (localization/l10n)とは、メッセージ、ストーリーやアイデアをターゲットオーディエンスの言語や文化になじませることです。そのために、まずは インターナショナライゼーション (internationalization/i18n)が必要です。ソフトウェア開発におけるi18nの作業は、まずコードを準備し、将来的にどのような言語・文化にも対応できるよう、事前にすべての開発上の決定を行う必要があります。 私自身のタスクは文言面の調整することですが、「ローカライゼーション」の定義の中にはデザイン面も含んでいます。そのため、我々はUI/UXライター、デザイナー、エンジニアと密に連携し、ターゲットユーザーに最高の体験を提供できるよう務めています。 ![](/assets/blog/authors/Maya.s/20221027/Esquema1.png =900x) KINTOにおけるローカライゼーション KINTOでは、多様なメンバー間でうまくコラボレーションできるよう、日々"カイゼン"に取り組んでおり、開発プロセス全体の中にローカライゼーションを組み込むことを重視しています。 我々の製品に対し、まずi18nを適用した上で、どのようにローカライゼーションが行われるか、もう少し詳細にご説明します。 以下の英語版 アプリ の画面をご覧ください。 ![](/assets/blog/authors/Maya.s/20221027/PPT&C_en.png =300x) まず前提として、ベース言語が実装されている必要があります。(我々の場合、ベース言語は英語)そして次に、そのデータをどのように管理するかを定義します。アプリやウェブページなどのソフトウェア翻訳では、キーバリュー形式でストアされることが多いです。この画面の文章は、iOS用の英語の「localizable.strings」ファイルでは、以下のような英文になっています。(ロゴと上部のキャッチコピーは対象外) <string name="agreement_description">By using our app, you acknowledge our Terms and Conditions and Privacy Policy.</string> <string name="agreement_accept_all">Accept all</string> ローカライゼーションのために、我々は開発コードとは別に、言語ごとのファイルを作成します。2022年10月現在、我々のアプリ用に日本語・タイ語・アラビア語のファイルを用意しており、それらのファイルにはすべて同じ翻訳キー[^1]が入っていますが、そのキーに対応する文言はそれぞれの言語で記載されています。例えば、アラビア語の「localizable.strings」ファイルには、次のデータが格納されています。 [^1]:Bodrov-Krukowski, Ilya (2020), Translation keys: naming conventions and organizing. < Translation keys: naming conventions and organizing - Lokalise Blog > <string name="agreement_description">باستخدام تطبيقنا، فإنك توافق على الشروط‏ والأحكام و سياسة الخصوصية الخاصة بنا.</string> <string name="agreement_accept_all">قبول الكل</string> このように、英語とアラビア語を比較すると、キーは同じであることがわかります。最初のパラメーターにキーを指定することで、各言語のローカライゼーションファイルから対応する値を取得し、ユーザーが指定した言語に合わせた文言をアプリ上に表示することができます。この例では、言語設定を英語からアラビア語に切り替えると、先ほどの画面は以下のように表示されます。 以上が、ファイルの種類やプラットフォームに関係なく、言語ローカライゼーションを進めていく仕組みです。 ローカライゼーションチームの活動について続けて読みたい方は、ぜひ次回の記事をご覧ください。お楽しみに! 参考文献と推薦図書 Hatim, B., & Mason, I. (1997), The Translator as Communicator. London, Routledge Khokhar, Sahil (2021), Connecting the dots '96 Web Accessibility through Internationalization and Localization < Connecting the dots '96 Web Accessibility through Internationalization and Localization > Lokalise Academy (2022), Crash course in localization < Crash course in localization >
アバター
Flyway導入
背景紹介 自己紹介 こんにちは。KINTOテクノロジーズ Globalグループ、DevOpsチームの李琳です。2017年までは中国でエンジニア、プロジェクトマネージャー、大学の先生を経験し、 2018年から日本で働き始めました。 子供を二人持っている母ですが、リスキルしながら仕事をしています。 DevOpsチームの紹介 GlobalグループのDevOpsチームは今年から実働開始しました。Globalグループは多国籍のグループで、DevOpsチームメンバーたちの母国語はそれぞれ日本語、中国語、英語ですので、仕事中は参画者の言語能力を考慮しながらコミュニケーションを取っています。 新しいチームとして、チームメンバーそれぞれの経験は違いますが、普段トラブルあった時には積極的に協力しながら進めています。チームワークがうまくできていると思っています。 DevOpsチームの責任範囲 現在Globalグループ内では複数のチームがあります。DevOpsチームは共通チームの位置付けで、Global全体を見ています。 具体的に言うと担当範囲は下記です。 タスク 作業内容 CI/CD、開発環境(Git/AWS/Grafana/SonarQubeなど)のGlobalチーム展開基準策定 共通部品のGlobalチーム展開基準の策定 Globalチーム共通のDevOps改善 上記内容のFB収集、PDCAの実施 個別カスタマイズサポート 上記内容以外の、全グループ通用可能な改善ではない要望については緊急度と必要性を判断した上で、実施策(基本DevOpsチームはサポートで、アプリチーム独自実装)の検討とサポート エラー解決サポート CI/CDと環境利用中のエラーについて、DevOpsが解決サポート グループ内DevOpsとAWS知見の向上 勉強会実施、個別問い合わせ受け取ること プラットフォームグループとの窓口 Globalグループとプラットフォームグループ間の問い合わせについて、DevOpsがフォローと収集を行い、グループノウハウを蓄積すること 運用の基準策定 運用業務の標準策定、一部外部業者さんに依頼すること コストの監視と方針策定 環境コストの最適化 問い合わせ対応 上記範囲の問い合わせ受付 本記事のターゲット 本記事の対象読者は、開発経験者としてFlywayの導入を検討中または導入済の方です。以前、自分でFlywayの導入を始めたとき、ネットでも色々調べたことがありますが、全体図を書かれている資料が少ないと痛感しました。本記事は、一つのFlyway導入案として執筆しています。ご参考になると光栄です。 Flywayの紹介 Flywayとは Flyway はOpen-Sourceのデータベースマイグレーションツールです。 Flywayを使うことで、複数環境のデータベースのバージョン管理が簡単にできます。 各コマンドの適用シナリオは下記の様です。 Baseline Baselineのコマンドを実行すると、Flywayの初期バージョンを作ります。Baselineのデフォルトバージョンが「1」です。 Community Editionだと、Baselineは一回しか作れません。更新できません。 対象データベースの中に既に一部のテーブルが存在している場合、Baselineを実行しないとエラーになって、Migrateコマンドが実行できません。 【シナリオ】 Step1)Flyway導入前に既に適用済のSQL文のバージョンを「1」より小さい数字にする Step2)Baseline実行 Step3)Migrate実行 結果はバージョン「1」以上のSQL文が適用されます。 【参照】 Baselines an existing database Clean 対象スキーマが丸ごとクリアされます。スキーマが空っぽになるので、本番環境には使わない様な仕組みを入れないといけないです。 【シナリオ】 最初のバージョンに戻したい時は下記のステップでできます。 Step1)Cleanのコマンド実行 Step2)Migrateのコマンド実行 【参照】 Wiping your configured schemas completely clean Info Flywayの情報が出力されます。このコマンドでFlywayからデータベースに繋がるかどうかの確認ができます。 【シナリオ】 実行後下記の様な情報出力(一例) | Category | Version | Description | Type | Installed On | State | +-----------+---------+-------------+------+--------------+---------+ | Versioned | 00.01 | initial | SQL | | Pending | | Versioned | 00.02 | initial | SQL | | Pending | +-----------+---------+-------------+------+--------------+---------+ 【参照】 Prints the details and status information about all the migrations Migrate 新バージョン(まだ適用していない)のSQLファイルが適用されます。一番使われるコマンドです。データベースバージョンアップの時毎回使うコマンドです。 【参照】 Migrates the schema to the latest version Repair エラーになったSQL文の実行履歴を消します。 実行結果は消せません。 Repairコマンドはデータベース中のflyway_shema_historyテーブル(Flywayのバージョン管理テーブル)からエラーになったSQL文の実行履歴を消しただけです。 下記の状況がよくあることです。その場合はきちんとどこまで適用されたかを確認して、全てのSQL文を適用するまで対応してください。 1つのSQLファイルの中に複数のSQL文があって、エラーになった前のSQL文が適用済み、エラーになったSQL文の後のSQL文が適用されていない場合。 【シナリオ】 【例】 V01_07、V01_08、V01_09、適用する時、V01_07、V01_08が成功、V01_09がFailした場合、下記のステップで対応可能です。 Step1)V01_09修正 Step2)Repairコマンド実行 Step3)再度Migrateコマンド実行 【参照】 Repairs the schema history table Validate プロジェクト中のSQL文がデータベースに適用されたかどうか、バージョンがあっているかどうかのチェックができます。 今のDBバージョンがクラウド上のバージョンと同じかどうかをチェックしたい場合も利用できます。 【参照】 Validates the applied migrations against the available ones Flyway導入の背景 Flywayのようなツールを利用しなければ、デプロイする度にデータベースに対する踏み台サーバにログインして、アップデートのシェルなどを実行する必要があります。Globalグループのサービスはほとんどがマイクロサービスで構成されているため、環境が多くなると従来の様な踏み台サーバを利用してデータベースをアップデートするオペレーションでは工数もリスクも大きくなるということが課題になりました。 こういった経緯でFlywayの導入を視野に入れました。最初はAWS上のLambda経由で、GitHubのジョブでコマンドを実行できるジョブを導入してみました。実際に使ってみたところ、下記の課題がありました。 ローカル環境等でのSQLそのものの検証が不十分な状態でAWSにマイグレートしてしまうと、マイグレートが失敗し復旧に手間がかかってしまった。 ローカル環境でFlywayの環境を構築しないままで、手動でデータベースをアップデートすると、AWS上のデータベースと違う構造になるリスクが高いです。 上記の課題をもちまして、一回目のPDCAで、Flywayの仕組みを下記のように作ってみました。 KINTOテクノロジーズ Global チームのFlyway導入方法 SpringBootアプリケーションでFlyway利用するために、下記のところに機能を入れました。 アプリケーションの中にFlywayを導入 利用タイミング:ローカルでアプリを立ち上げる時と、AWSにデプロイする時に自動的にマイグレートされる 意図:ローカルでマイグレーション用のSQL文テスト、自動的にマイグレーションされるので手間がかからないため Flywayのプラグイン導入 利用タイミング:ローカル開発の時 意図:ローカルで自動マイグレートができなかった場合は、プラグインでFlywayのコマンドを実行するため Flywayコマンドを実行可能なGitHubジョブ導入 利用タイミング:AWSにデプロイする時に自動的にマイグレーションできない場合は、GitHub上のジョブでFlywayコマンドを実行します。 意図:AWSに入らなくてもFlywayのコマンドを実行できるようにするため つぎに、それぞれの完成図を紹介いたします。 アプリケーションの中にFlywayの導入 Flywayをプロジェクトの中に導入したら、下記のことができます。 アプリケーション起動後各環境上のデータベースが自動的にマイグレート AWSのデータベースにマイグレート前、ローカル環境でマイグレーション用SQL文検証 詳細は下記の通りです。 下記のコマンドでローカルでMySQLのDockerイメージを起動→アプリケーション起動したら、 自動的に最新のSQL文がマイグレートされます。 docker-compose up -d ./gradlew bootRun Flywayのプラグイン導入 手動でもFlywayのコマンドでローカルデータベースを維持できます。下記のようにプラグインを使えばコマンド実行できます。 Flywayコマンド実行可能のGitHubジョブ導入 AWSにデプロイされると、Auroraまで自動的にマイグレートできますが、できない場合はFlywayコマンドを実行する必要があります。 AWS上にはLambdaを経由でFlywayコマンド実行します。 構成図は下記の通りです。 GitHubジョブ実行からFlyway実行終了までのフローは下記の通りです。 GitHubジョブから実行用のファイルをS3にアップロード Payload(JSON)から必要なパラメータを抽出 AWS CLIを利用し、Flyway実行に必要な情報を抽出 S3バケットからSQLを含むzipファイルを取得 Flyway実行(Lambda上のDocker imageで) 結果をS3バケットに配置 GitHub上コマンド実行時のイメージは下記の通りです。AWSに入らなくても実行できるように構築しました。 これで各環境に下記のことができるようになりました。 アプリケーション起動後各環境上のデータベースが自動的にマイグレートされた AWSのデータベースにマイグレート前、ローカル環境でマイグレーション用SQL文検証できた 各環境にFlywayコマンド実行用のツールが用意された Flywayを使うことによって、下記のメリットがありました。 デプロイ時間が大幅に(半分以上)削減できた 各環境のデータベース差分が無くなったことで開発時の不要なバグ混入や認識齟齬を減らせた 各環境データベースバージョン管理の工数を極小化(SQL文の名前でバージョンを明確すればOK)できた テストやReviewによって不完全なクエリを流すことを防ぐことができた AWS上に構築した踏み台サーバにログインしてオペレーションをしなくて良くなった もちろんFlywayを利用することによって、下記注意しないといけないこともあります。 開発者が多い場合は使い方を決めた上で徹底すること エラーになるときのトラブルシュッティングと復旧は手数をかかること 上記の仕組みで理論上はGitHub ActionsのCI/CDジョブ実行中にもデータベースを立ち上げることもできますが、まだ検証していないです。CI/CDの自動テスト用のデータベースもFlywayで構築した方がいいかなと思っているところです。 Flywayを使うことによって、便利なところもありますが、Flywayが起因となったトラブルが発生したこともあります。この点は利用基準のPDCAで改善の余地があります。環境と利用するシーンによって段階的に導入することで、より安全に効率的に利用できると思います。興味ある方は是非お試しください。
アバター
自己紹介 KINTOテクノロジーズにてCIO室セキュリティチームのチームリーダーを担当している森野です。 趣味は子ども時代を過ごした埼玉県大宮市(現さいたま市)のサッカーチームである大宮アルディージャの応援です。 本記事では脆弱性診断の主担当者であるヘビーメタル大好き中辻さんと共に私たちの脆弱性診断の取り組みについて紹介させて頂きます。 脆弱性とは 脆弱性とは何でしょうか。 脆弱性とはソフトウエアのバグ(欠陥、不具合)の内、情報セキュリティのCIAを損なうものを指します。 CIAは下記3つの単語の頭文字を取ったものです。 Confidentiality(機密性) Integrity(完全性) Availability(可用性) 機密性とは情報に対して許可された者のみアクセス可能であることが保証されることを指します。 例えば給与明細参照用アプリがあったとして私の給与明細(情報)について会社の人事担当者と私(許可された者)のみアクセス可能である状態は機密性が保たれた状態です。 これがソフトウエアのバグにより私の給与明細に他人がアクセス可能である場合、機密性が損なわれた状態となります。 機密性が保たれた状態 アクセス権を持っている人だけ給与明細にアクセス可能 機密性が損なわれた状態 アクセス権を持っていない人も給与明細にアクセス可能 完全性とは情報に欠損や改ざんがなく完全に(正確に)保たれることが保証されることを指します。 前述の給与明細で例えると会社の人事担当者以外は私の給与明細の内容を消したり、書き換えたりできない状態は完全性が保たれた状態です。 私の給与明細を他人が消したり、書き換えたりすることが可能である場合、完全性が損なわれた状態となります。 完全性が保たれた状態 編集権限のある人だけ給与明細の削除、編集可能 完全性が損なわれた状態 編集権限のない人も給与明細の削除、編集可能 可用性とは必要な時にいつでも情報にアクセス可能であることが保証されることを指します。 人事担当者や私が必要な時にいつでも給与明細にアクセス可能である状態は可用性が保たれた状態です。 人事担当者や私が必要な時に給与明細にアクセスできない場合、可能性が損なわれた状態となります。 可用性が保たれた状態 いつでも給与明細にアクセス可能 可能性が損なわれた状態 給与明細にアクセス不可 私たちが取り組んでいる脆弱性診断について 前述した情報セキュリティのCIAを損なうバグを検出することが脆弱性診断の目的です。 私たちの会社では以下のような脆弱性診断を実施しています。 Webアプリケーション診断 プラットフォーム診断 スマートフォンアプリ診断 Webアプリケーション診断 Webアプリケーション診断には大きくわけて静的診断と動的診断があります。 静的診断はアプリケーションを実際に動かして診断するのではなくソースコードから安全ではないコードを発見する手法です。 動的診断は実際に動いているWebアプリケーションを診断する手法です。 いづれも自動診断と手動診断があります。 自動診断は設定に従ってプログラムがソースコード診断やWebアプリケーション診断を自動で実施します。 手動診断は人間がソースコード診断やWebアプリケーション診断を手動で実施します。 静的診断はSAST(Static Applilcation Secuirty Testing)、動的診断はDAST(Dynamic Application Security Testing)とも呼ばれます。 Webアプリケーション診断においてセキュリティチームは主に動的診断を担当していますので動的診断の自動診断と手動診断について説明します。 自動診断 私たちの会社では自動診断ツールに AppScan を使用しています。 例えばWebアプリケーションに SQLインジェクション の脆弱性があるのかないのか診断する場合、入力項目にSQLインジェクションを誘発する攻撃コードを入力、実行して診断します。 すべての入力項目に様々な攻撃コードを埋め込んでWebアプリケーションを診断するのは骨の折れる作業です。 診断中にWebアプリケーションのセッションが切れたらログインからやり直しです。画面遷移の順番が決まっている機能もあり、その画面遷移を繰り返し行うのも大変です。 自動診断ツールはそのような作業を自動化してくれるとても素敵なツールです。 手動診断 手動診断ツールは BurpSuite を使用しています。  なぜ自動診断ツールがあるのに手動診断を行うのでしょうか。 セキュリティに関するコミュニティの OWASP は、セキュリティインシデントの発生原因を OWASP Top 10 というランキング形式で公開しています。 OWASP Top 10の3位に入っているインジェクションは自動診断ツールが検出を得意とするものです。人間より自動診断ツールの方が様々な攻撃コードを漏れなく入力項目に入力(インジェクション)して診断できそうです。 では、1位のアクセス制御の不備はどうでしょうか。先程、例に挙げた給与明細参照用アプリの機密性が担保されているのか否かを診断するようなケースです。残念ながら自動診断ツールはWebアプリケーションの仕様を理解した上で、その挙動が適切か不適切かを判断するのは得意ではありません。このような種類の脆弱性は人手で診断を行います。 プラットフォーム診断 プラットフォーム診断はファイアウォールやロードバランサなどのネットワーク機器やWebアプリケーションが実行されているサーバの設定やサーバOSやミドルウエアの脆弱性を診断するものです。 プラットフォーム診断ツールは nmap を使用しています。 プラットフォーム診断では以下のような項目をチェックします。 ・不要ポートの解放 ・脆弱なソフトウエアの利用 ・設定の不備 ・プロトコル固有の脆弱性 参考: 政府情報システムにおける脆弱性診断導入ガイドライン P.7 スマートフォンアプリ診断 スマートフォンアプリ診断は通常、アプリ部分の診断とWebAPI部分の診断があります。WebAPI部分はWebアプリケーションと同様の脆弱性診断を実施します。 アプリ部分はOWASPの OWASP Mobile Application Security Testing Guide (MASTG) を参考に静的診断を行っています。 アプリ部分の診断については今後、動的診断および静的診断に対応している MobFS を活用することを検討しています。 脆弱性診断についてもっと学びたい人向けの書籍、資料、ウェブサイト ここまで記事を読んで脆弱性診断についてもっと学びたい人もいらっしゃるのではないでしょうか。 そのような人向けに役に立つ書籍、資料、ウェブサイトを紹介します。 書籍 体系的に学ぶ 安全なWebアプリケーションの作り方 第2版 脆弱性が生まれる原理と対策の実践 通称、徳丸本と呼ばれている脆弱性診断について学ぶ人たちのバイブル的書籍です。鈍器に使えそうな分厚さなので持ち歩いて読みたい方は電子書籍版の購入をお勧めします。 資料 安全なウェブサイトの作り方 IPAが公開している名前の通り安全なウェブサイトの作り方に関する資料です。先程の徳丸本と比べてページ数少な目なので初めて脆弱性診断について学ぶ人はこちらから読み始めるのをお勧めします。 ウェブサイト WebSecurityAcademy 前述した脆弱性診断ツールBurpSuiteの開発元であるPortSwigger社が運営している脆弱性の学習サイトです。 脆弱性に関するテキスト教材とハッキング演習から構成されています。 ブラウザ上で実際に手を動かしながら学習が可能です。 おわりに この記事ではセキュリティチームの脆弱性診断の取り組みについて紹介させて頂きました。 最近WebAPIはREST APIではなく GraphQL による実装が流行っています。 このようにITの世界は技術の流行り廃りが早いため、新しい技術で実装されたアプリケーションの脆弱性診断を適切に行えるよう日々情報収集および業務改善に努めたい行きたいと思います。
アバター
はじめに はじめまして、KINTOテクノロジーズでモビリティマーケットの開発・運用を担当しているリナです。 普段はフロントエンジニアとして、主にNext.jsを用いて実装しています。 最近のマイブームは、ガンプラの塗装とスプラトゥーンをやることです🎮 さて、KINTOテクノロジーズでは業務に役立つ書籍を経費で購入しています。 購入した書籍は CIO室 で管理され、従業員が自由に貸出できるようになっています。 そこで今回は、購入した書籍の管理方法をラクにした話を紹介します! 従来の書籍管理の方法 従来の書籍管理の方法は Confluence を使用して、貸出状況を手作業で更新していました。 管理の流れは、以下の通りです。 管理担当者が、購入書籍をConfluenceの書籍貸出一覧に記載 貸出希望者は、Confluenceの書籍貸出一覧から書籍を選んで、管理担当者にSlackで貸出連絡 管理担当者がSlackの内容をもとにConfluenceの貸出状況を更新 このように書籍を貸出する上で、 貸出・返却希望者Slackで管理担当者に連絡する 管理担当者は、都度貸出状況を手作業で更新する という手間が発生していました。 この手間を解消すべく、書籍の管理方法を一新しました! 新しい書籍管理の方法 新しい書籍の管理方法は JIRA のワークフローとカンバン方式のボードを使用して、管理者を介さずに貸出状況が分かるようにしました。 カンバン方式のボード 管理の流れは、以下の通りです。 管理担当者が、購入書籍をボードのライブラリーにチケットを登録 貸出希望者は、ライブラリーから書籍を選んで、ステータスを「貸出中」に変更 これだけです! 管理者はボードに購入書籍を登録するだけで、貸出状況が一目見てわかるようになり、貸出状況を手作業で更新する必要がなくなりました。 一方、貸出・返却希望者は、管理担当者を介さずにステータスを変更するだけ貸出登録が可能になり、Slackで貸出・返却時に連絡する手間を省くことができました。 ラクするためのJIRAワークフロー このボードを作成するにあたって、以下のようなワークフローを設定しています。 ワークフロー 「ライブラリー」「貸出中」「破棄・紛失」3つのステータスを作成し、ステータスを移動する際に一部の情報を自動入力することで、入力する負担を軽減しました。 各ワークフローに設定している内容は、以下の通りです。 Check out (ステータス「ライブラリー」→「貸出中」に変更) 貸出日に本日の日付を自動入力する 担当者(貸出希望者)を自動で割り当てる 貸出回数をカウントする Check in (ステータス「貸出中」→「ライブラリー」に変更) 貸出日・返却予定日を自動消去する 担当者(貸出希望者)を自動消去する さらにラクするための一工夫 全オフィスの書籍の管理状況がわかる さらにオフィスごとに何の書籍があるか、アイコンを設定することで視覚的に把握できるようにしました。 タイプを選択するとオフィス毎の管理状況を絞り込んで表示できるようになっています。 KINTOテクノロジーズは、東京に2拠点と名古屋・大阪に1拠点ずつオフィスがあります。 従来は、各拠点ごとに書籍を管理していましたが、全拠点の書籍が1つのボードで一元管理できるようにしました。 Slackで変更履歴を通知 また、JIRAの通知機能を使用して、管理者用にステータスの変更履歴をSlackに通知しています。 Slackに通知することで、新しく購入された書籍や誰がステータスを変更したかなどが把握できるようにしました。 ラクにした結果 書籍の管理方法を見直した結果、それぞれ以下のようなメリットがあると思っています。 管理担当者のメリット 書籍の管理状況を手作業で更新する必要がなくなった パッとみて貸出状況・貸出者が誰かわかるようになった オフィスごとに管理していた書籍を全オフィス一元管理できるようになった 貸出・返却希望者のメリット 書籍の貸出・返却時にSlackで連絡する必要がなくなった ステータスを変更するだけで、貸出登録ができる(文字の入力不要!) さいごに 今回の記事では、書籍の管理方法をラクにした話をご紹介しました。 ちょっとした手間を省くことで管理者も利用者もハッピーになればいいなと思っています✨
アバター