TECH PLAY

KINTOテクノロジーズ

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

969

Actually, We're Quietly Evolving! KINTO Technologies-Style In-House Creators Merry Christmas! I'm Aya Sugimoto, a CD in the Creative Group. Today, I'm gatecrashing the blog to introduce you to (& brag about) our team. We belong to KINTO Technologies, a company where at its core is driven by the engineers behind KINTO's car subscription services. It's a tech company as the name implies so engineers take the spotlight; but we're flourishing away there too, in our own little corner. I have a soft spot for challenging new projects, ones that have never been attempted before :-). Our 23 team members (as of December 2022) work on a wide range of projects, from ones we really need to roll our sleeves up for (such as improving the KINTO car subscription site, producing static content, supporting dealers, and branding new services) to more relaxing ones (such as designing in-house goods). We approach them all with creative ideas and a perspective grounded on the customers' points of view. Let me tell you about our work in a dramatic way! Season 1: 2021: Becoming a group that thinks from the customers' points of view. Now, we produce things by discussing them with business side members starting from the upstream processes, but when I first joined the company, the stance was to just produce whatever the business side asked us to. If things had gone on like that, there wouldn't have been much point in having in-house creators at all. So first, we launched a project that originated from us, to raise awareness that there is an in-house group that produces high-quality deliverables and is very willing to discuss things. It was called "A Project Anyone Can Understand." It's harder for us to get to know a lot about cars than it is for business side, but that means we can plan and improve the UX from the point of view of people who aren't very knowledgeable about them—namely, the customers. With that in mind, a cross-functional in-house team system was created to launch the project. It was a daunting prospect because the project had been launched by members who'd only been with the company for a few months (myself among them), but the department managers supported us. We presented the president with a plan created based on the customers' points of view, and got the green light. Once word about the project had spread and it was produced results, we started to gradually gain recognition within the company. Season 2: Teaming up with like-minded colleagues in 2022 Web directors and designers are very busy people. The former get involved starting with the planning for handling the upstream of new services, and organize the requirements and move the production work forward as well. The latter give the services form. We urgently needed to up our creativity levels in order to meet the in-house expectations, too. We strengthened recruiting and boosted the number of members who would relate to our vision for taking a creative leap upward. Then, we made changes to the organizational structure, reviewed the design processes, and more, and successfully shifted to a work style where the business side would ask us to discuss and work on things together with them, instead of just ordering things from us. We're now a highly dependable group with a diverse range of members! Thank you everyone! In addition to web-related work, we're also involved in the graphics, logos, naming, and other branding work for new services, and are playing a part in the bulk of the visual aspects related to customer touchpoints. Season 3: Our evolution will continue in 2023 and beyond An attractive environment for in-house creative group staff is one where the team can talk directly with the business side, without information ending up being relayed like in a telephone game where things get lost along the way. Conveying to each other passions and dreams then giving them form together feels rather deeper than just client work this way. It's rewarding to be able to design something completely, starting from the brand concept. In particular, in our company, the marketing team sits across the aisle, and the development team is also nearby. I also think it's pretty appealing that senior managers like our vice president and president are quite approachable figures in our company. Of course, it also means you can get the negative feedback directly, too :-) ! A project was launched in 2021 to showcase the group's potential far and wide within the company. 2022 was a year where I got to bond with fellow team members who felt the same way about taking this creative leap upward. So now, there are only a few days left in the year. We want to go on the offensive in 2023, covering a wide playing field so that we can evolve and deepen our approach even further, all with the aim of impressing customers through even better mobility services. I'm going to put my brainpower to work as I tackle issues, keep feeling my way forward as I work, also eat a lot and sleep a lot. As I keep working at KINTO Technologies, I fell I will rejuvenate along with the growing business :-). I wish you all a very happy New Year. But first, have a Merry Christmas!
アバター
はじめに こんにちは!KINTOテクノロジーズ(KTC)プロジェクト推進GのRisako.Nです。 普段はPjMとして色々なプロジェクトに参画しています。 KTCにおけるプロジェクトやPjMとは…については、前回 横断プロジェクトの始まり方とPjMのお仕事紹介 という記事でご紹介させていただきましたので、ご興味ある方はそちらの記事をご覧になっていただけるとうれしいです。 いったんここでもざっくりと「PjMとは何か」を紹介させていただくと、KTCでは基本的にプロダクトごとに開発Gやチームがあり、各々のPdM中心に開発が進められています。ただ新しいサービスの立ち上げなどプロダクトを横断する案件、また横断的でなくとも規模が大きい案件などはPjMを置き、プロジェクトを立ち上げ推進させていくという役割をPjMが担うこととなります。 ![](/assets/blog/authors/risako.n/1.png =500x) プロジェクトとしてはKINTO-ONE(サブスク)やKINTO-FACTORYといったKINTOの既存サービス事業に関わるもの、KTCオウンドメディアやサービス、さらにはKINTOとしての新たな事業立ち上げなど多種多様なものが日々発生しており、それぞれのプロジェクトは発生した段階では何がゴールかまだわかっていないことも多くあります。色々なプロジェクトに携わるたびに、そんな不透明な状況下でもプロジェクトを前進させていく力、また自分自身としても前に進んでいく力が必要だなというのを感じており、今回は「前に進んでいく力」=「自走力」をテーマに思うことを書いてみたいと思います! ![](/assets/blog/authors/risako.n/2.png =500x) 「自走力」とは 前書きがだいぶ長くなってしまいましたが「自走力」とは。 近年よく聞こえてくる(特に採用市場などで多いかも)ワードかと思います。なんとなくわかるような、わからないような。。定義を探してみたところ、「自走力」という言葉自体にしっかりとした定義はないようですが、コトバンクには「自走」の定義が記されていました。 他の動力によらず、自身の動力で走ること つまり「自走力」とは「自身の動力で走ることができる力」、仕事に置き換え+αの表現を加えると「(どのような状況下においても、)自分の考え・行動でその仕事を前に進めることができる(、完遂することができる)力」かと思います。カッコ部分はそこまでできるとパーフェクトですね!と思う理想像です。 ![](/assets/blog/authors/risako.n/3.png =250x) 自走力=自分自身の動力で走れること! 「自走できる人」ってどんな人 「自走力」をさらに噛み砕いて、具体的に「自走できる人ってどんな人?」を考えてみます。 ゴールがはっきりしていなくても、前進することができる人 やり方がわからなくても、前進することができる人 誰かの真似をするだけではなく、自分の考えをアウトプットできる人 「自走できない人」で考えるとこんな人かなと思います(自走できる人の逆ですね!)。 指示がないと動けない人 自分がわからないことはできないと決めつけてしまっている人 言われたことをするだけの人 「自走できる人」の表現として「前進」と書きましたが、これもポイントです。「暴走」はダメで、あくまで「きちんと前に進めることができる人」が自走できる人!と思っています。 ![](/assets/blog/authors/risako.n/4.png =250x) くれぐれも暴走してしまわないように! どうやったら「自走できる人」になれるかな そんなやり方があればぜひ教えてほしい…とわたし自身も思っているのですが、ただ色々なプロジェクトに携わり、少なくとも一輪を担っている中でわたし自身がどういうことを心掛けているかを今回ご紹介しようと思います。 対話を大事にする 初めて仕事をするもの同士だと、色々なところにギャップがあるのが当然 思い込みはついついしてしまうものなので、思い込みや決めつけをしないように注意しながら、まずは考えていることを出し合う 考えを出し合っていくことで関係性もできてくる 小さく作ってアウトプットしていく、フィードバックを大事にする わからない状況下では、何か作ることや発信することにも恐れがちになるけれど、まずは小さいものからアウトプットしてまわりの人とのギャップを埋めていく 色々受ける意見は自分の不備不足と捉えず、フィードバックと捉える(後ろ向きに受け取らず、前向きに受け取る) 完了の定義に誤解がないようにする 何をもって完了なのかは人によって違いがちなので、関係者間で共通認識を持つようにする 例えば、チームのタスク運用を見直すことになって担当をアサイン、その後見直しタスクがいつのまにか完了になっていたけれど、マニュアル変更しただけで終わったことになっていた…見直し内容をチーム内で確認・周知して完了と思ってたのに…(自分の当たり前は他人の当たり前ではない) 余計な問題に対して余計に悩まない 悩むのは適切なときに悩む(今悩んでも無駄なことを今悩まない) 今一生懸命考えたとしても、本当に必要なときになると状況が変わっていることもしばしば(一生懸命考えたことが結局無駄になる) 価値とは何か この仕事は何のための仕事なのか、できあがるものは誰の何のためのものなのか、何をもたらすものなのかを意識する(ついつい「やること=目的」になりがち) 開発途中に変更要求があった場合、開発サイドとして途中変更は辛いと思ってしまいがちだけど、今作っているものの価値を考えると、この変更やった方がいいね!と前向きにがんばることができるし、結果的にうれしい 今自分がここに存在する意味、価値を意識する(ただ与えられた仕事をしているだけでは自分の価値って出てないよね、と思う) 自分のできることを意識する 「できないこと」は目につきやすいけど、「できること」(自分の価値)を意識する 例えば、この仕事をする前と今を比べて変わったこと(できるようになったこと)ってなに?と考えて、「できること」「できるようになったこと」を見つける まわりを見習う まわりの人で、あの人のこういうところいいな!と思うところを意識する 意識することで自分もいいなと思う姿に少し近づけることもある 例えば、○○さんは口数は少なめだけど、作る資料はとってもわかりやすい!なにがわかりやすいって感じるポイントなんだろう?といった視点で見ると、わかりやすい資料のコツを見つけることができたり 挙げていくと意外とキリがないな、そして教科書っぽいことばかりだな…と思いましたが、基礎となる意識やスタンスを文章化するとやはり教科書っぽくなってしまうのは仕方ないのかな…ということでご了承ください(この記事をご覧いただいている方にとって、何か1つでも心や目に留まるものがあればいいなぁと思っています)。 自走力とアジャイルマインド 前述したわたしの心掛けを眺めると、随所にアジャイルマインドを感じる方もいらっしゃるかもしれません。 アジャイルの原則にあるself-organizing teams(自律した組織)を形成するには、そもそも1人1人が自律する・自走できる必要があり、そうなれるためのエッセンスがアジャイルやスクラムにあって、これまでの経験の中で植え付けられたんじゃないかな、と自己解釈しています。 The best architectures, requirements, and designs emerge from self-organizing teams. 最良のアーキテクチャ・要求・設計は、 自己組織的なチームから生み出されます。 KTCではプロダクトによってスクラムを採用しているグループもありますし、ウォーターフォール型のグループもあります。ただ開発プロセスがウォーターフォール型であっても、KTC全体総じてのマインドとしてはアジャイル(小さな単位で価値を生み出して、対話と協調を重んじながら改善を繰り返していく)だと思います。そういったアジャイルマインドの環境下で働いてみたいなと思う方がいれば、ぜひKTCにジョインいただけるとうれしいです。 また、もちろんご自身の自走力を発揮したいぞ!と考えられている方にとっても、KTCは思う存分発揮できる環境と思いますのでぜひお待ちしています。 おわりに 今回はKTCの実態がわかるような事例のご紹介はほぼなく、概念的なお話ばかりになってしまいましたが 「自走力」ってなんだろうなぁと思っている方に対して、一例のご紹介 自分自身の「自走力」について考えるきっかけ になれば幸いです。
アバター
はじめに こんにちは!KINTOテクノロジーズの開発支援部に所属する「植村」です。 普段はコーポレートエンジニアとして主にMDM構築・管理などを行っています。 先日、「 KINTOテクノロジーズ MeetUp!~情シスによる情シスのための事例シェア4選~ 」というタイトルで「コーポレートIT領域に特化した、事例発表+座談会形式の勉強会」を開催しました。 今回は、その勉強会で事例発表した「Windowsキッティング自動化のススメ ~Windows Autopilot導入~」の内容について、補足を交えながらテックブログ上で紹介します。 Windows Autopilotとは Windows Autopilotとは、IntuneにWindowsデバイスを登録する方法の一つです。事前にデバイスのハードウェアハッシュ(HWハッシュ)を登録しておくことで、セットアップ時に自動でIntuneに登録される仕組みとなっています。 今回はこのWIndows AutopilotをKTC環境に導入し、PCキッティングの効率化を行ったことについてお話します。 Windows Autopilotによるキッティング自動化の仕組み まずはWindows Autopilotによるキッティング自動化の仕組みについて説明します。 はじめにベンダーまたは管理者がキッティングを行う前にあらかじめPCのHWハッシュをIntuneに登録しておきます。 次にユーザー(または管理者)がPCを起動し、サインインを行います。事前にHWハッシュを登録していたことでPCは自動でIntuneに登録されるようになります。 Windows Autopilotで登録されたPCが所属する動的グループを作成しておくことで、Intune登録したPCは自動でその動的グループに所属するようになります。 各構成プロファイルやアプリの配信設定の割り当て対象に3で示した動的グループを割り当てておくことで、自動的にデバイス制御やアプリ配信が行われるようになります。 Windows Autopilot自体はデバイス登録の機能を担うので1、2のところに該当します。 登録されたデバイスが3、4のところで自動でプロファイル制御やアプリの配信が行われるように動的グループを活用する必要があります。 つまりWindows Autopilot登録の設定だけでなく、自動でデバイス制御設定やアプリ配信が走るように構成することでキッティングの自動化が実現できるわけです。 実際にWindows Autopilotの導入を行ってみた Windows Autopilotの導入に至るまで調査や検証も含めて一か月半くらい掛かりました。 KTC環境においては、既に全PCのHWハッシュ登録自体は完了していたため、行ったことは以下の2点のみでした。 キッティングの際に一番最初に実行されるAutopilotプロファイルというものを、既に登録済みであったHWハッシュに割り当てる。 これまでキッティング時に使用していたキッティング用静的グループを動的グループに差し替える。 Autopilotプロファイルの設定 AutopilotプロファイルをHWハッシュに割り当てることで、「該当するPCはWindows Autopilot方式でIntuneに登録すること」を決定づけることになります。 プロファイルの内容としては主にPCのセットアップ画面で選択する「言語の設定」や「WIndowsライセンス同意」等の選択をスキップさせるか否かを設定することができます。 動的グループの設定 Autopilot登録したデバイスはAutopilotデバイス属性を持つので、この属性を動的メンバーシップルールに設定します。 ※詳しくは以下MSのサイトを参照ください。 Windows Autopilot のデバイス グループを作成する | Microsoft Learn さらに作成した動的グループを構成プロファイルやアプリ配信設定の「割り当て」に指定します。 これによりデバイス登録~デバイス制御までの流れを自動化することが可能になります。 これでWindows Autopilotによるキッティングの自動化の完了です。 導入してみた結果 Autopilotを導入してみてどれくらいの効果が出たのかというと、、、 定量的な結果ですと、導入前と比べて約40%程の作業項目を削減することができました。一方で作業時間についてはそこまでの効率化はできませんでした。アプリのインストールやWindows Updateの実施に多くの時間が掛かっているため、そこの改善には至りませんでした。 また、定性的な効果ですとキッティング作業の自動化・簡略化によって作業漏れなどの人為的ミスが発生しにくい状態にはなりました。 最後に 理想的には所謂ゼロタッチキッティングの実現ができればと思っているのですが、Autopilotの導入で実現てきたかと言われればまだまだ手作業は必要な状態です。 しかしながらデバイス登録からデバイス制御までの一連の流れを自動化できたことは、大いにPCキッティングの効率化に繋がったと思います。 今後も新たな機能を取り入れつつ、更なる効率化に向けて取り組んでいきたいと思います! !
アバター
こんにちは こんにちは、KINTOテクノロジーズ テックブログチームのmaya.sです! 本記事では2023年10月入社のみなさまに、入社直後の感想をお伺いし、まとめてみました。 KINTOテクノロジーズに興味のある方、そして、今回参加下さったメンバーへの振り返りとして有益なコンテンツになればいいなと思います。 IU 自己紹介 KINTO ONE開発部新車サブスク開発GのIUです。KINTO ONEの会員画面や契約のシミュレーション画面等のフロントエンド開発を担当しています。 所属チームはどんな体制ですか? 基本的にフロントエンドチームで活動していて、6人体制です。同期入社の白濱さんと一緒に参画しています。 KINTOテクノロジーズ(以下KTC)へ入社したときの第一印象?ギャップはありましたか? 入社前は大企業のグループ傘下の会社ということで、ある程度古さや固さを覚悟していましたが、思ったよりも伸び伸びと働けています。 特にスピード感は強く感じていて、アイデアの立案から実装、リリースまで短期間で行い、小さなサイクルでサービスを向上させていると感じます。 現場の雰囲気はどんな感じですか? 各自作業に集中して単独の色も強いですが、MTGでは各自の取り掛かっているタスクの共有を行い、手詰まりがあれば皆で解決法を話し合ったり、定期的に輪読会を行って知識の向上をさせています。 ブログを書くことになってどう思いましたか? テックブログを運営していることは入社前に知っていましたが、限られた人だけが執筆を出来るのかと思っていました。ですが基本的に誰でもウェルカムで、むしろ執筆を推奨している環境でした。 何か記事を書くことは好きなので、積極的に参加したいと思います。 ベ ジョンソク(Jongseok Bae) 自己紹介 韓国から来た10月入社のべジョンソクです。プラットフォーム開発部モバイルアプリ開発GのPrismチームのAndroid担当してます。 所属チームはどんな体制ですか? PrismのチームはAgileの形式で色々会議を通じてスケジュールを管理してます。 KTCへ入社したときの第一印象?ギャップはありましたか? 初めにOJTなどを通じて会社の説明に充実していると感じました。今まで経験した会社の場合適当に一日ぐらい説明した感じだったので違うと思いました。また社内勉強会もたくさん実施されていて、見逃しやすい情報もキャッチアップできて本当にいいなと感じました。 現場の雰囲気はどんな感じですか? チームメンバーは親切です。質問したらよく教えてくれて、仕事の中で色々意見共有などもしながら、作業をするのでコミュニケーションしながら作業するのに良い環境だと感じました。 ブログを書くことになってどう思いましたか? 最初は負担でしたが、一ヶ月間感じたことを書くから考えを整理できていいと思います。 ヒュイット マーティン(Martin Hewitt) 自己紹介 マーティンです、フランスから来ました。KINTO-TECHNOLOGIESでPlatform Engineeringをやっています。 所属チームはどんな体制ですか? プラットフォーム・グループには6つのチームがあり、SRE、DBRE、プラットフォーム・エンジニアリング、クラウドなど、それぞれの専門分野に特化しています。 KTCへ入社したときの第一印象?ギャップはありましたか? 真面目に会社を紹介してくれてます!フランスでそんな事ありませんでした。近代的な会社だと感じました、日本の会社のイメージと違う。 現場の雰囲気はどんな感じですか? 皆んなめちゃ優しい!最初は緊張していましたが、すぐに馴染むことができました。 ブログを書くことになってどう思いましたか? 早ー U.A 自己紹介 KINTO ONE開発GのU.Aです。プロデューサーやPdMとして、トヨタの販売店に対するDX支援を担当しています 所属チームはどんな体制ですか? オウンドメディア&インキュベートグループ内のDX Planningチームに所属しています。 直接トヨタの販売店に訪問してお困りごとをお伺いし、ITの力で課題を解決するチームです。 プロデューサー2名、ディレクター3名、デザイナー1名の計6名で業務にあたっています。 KTCへ入社したときの第一印象?ギャップはありましたか? 服装も自由、髪色も自由、出社時間もフレキシブルで何もかもに驚きました。 自分のこれまでの経験を活かして働きつつ、各部署には専門分野に特化した方々が集まって働いているので、顔が広くなればなるほど能力もストレッチでき、とても贅沢な空間に感じています。 現場の雰囲気はどんな感じですか? DX Planningチームは個性豊かで、話題が尽きることがないです。 普段の会話の中からもDX化のヒントになる内容を思いつくことも多いので、コミュニケーションの大切さを日々感じています。 ブログを書くことになってどう思いましたか? もう入社から1カ月経過したことに驚いています!これからも1日1日を大切にしていきたいと思います。 ラセル アハサン(Ahsan Rasel) 自己紹介 プラットフォーム開発部モバイルアプリ開発Gラセル(Rasel)です。バングラデシュから参りました。MyRouteアプリのAndroid版を対応しています。 所属チームはどんな体制ですか? 私を含めて4名体制Multi Nationalチームです。日本、バングラデシュと韓国の方がいます。チームはAgile methodologyで仕事をやっています。 KTCへ入社したときの第一印象?ギャップはありましたか? オリエンで会社のStructureや全ての部のStructure/mission/visionなどについて詳細に知ることができました。前職にもオリエンがあったが、そんなに詳細にはなかったです。自分の意見をCIO/CEOまでに伝えるのはもっと簡単だと思いました。 現場の雰囲気はどんな感じですか? 皆さんがとても優しくて仕事しやすいです。入った後質問多かったですが、皆さんは詳細に説明してくれて嬉しいです。日本語で難しいところがあれば英語も使えるのでもっとスムーズにコミュニケションすることができるので良い感じだと思っています。 ブログを書くことになってどう思いましたか? 早い感じでした。テクニカルブログ書いたことありますが、ノンテクニカルブログは初めてです。また、入社したばかりなので、びっくりしました。 宮澤 祐平(Yuhei Miyazawa) 自己紹介 プラットフォーム開発部オペレーションシステム開発Gの宮澤です。 前職はベンダー側(SIer)でECサイトの開発に従事していました。 KINTO ONE 中古車に関連したバックオフィス業務で扱うシステムの開発をしています。 所属チームはどんな体制ですか? 内製チーム5名、ベンダー様約20名で開発を推進しています。 内製化を推進するため技術レベルの高い方が多く在籍し、よくある「プロパーは推進のみ、技術はベンダーに頼りっきり」は決して無い。 KTCへ入社したときの第一印象?ギャップはありましたか? 働き方の自由度が高く、裁量で働けること。 相手を尊重したコミュニケーション文化がGood!! 現場の雰囲気はどんな感じですか? 騒がしくもなく静かすぎもなく、穏やかな雰囲気です。 「あの店で飲み会してみたい」と言えば開催されるくらいチームの人間関係も良好です。 ブログを書くことになってどう思いましたか? 自己紹介や会社紹介の内容で安心しました。 Ryomm 自己紹介 プラットフォーム開発部モバイルアプリ開発Gの 松阪 / Ryomm (@ioco95) です。iOS版のMyRouteアプリを担当しています。 所属チームはどんな体制ですか? MyRouteのiOS開発チームは、私を含めて6名体制です。 KTCへ入社したときの第一印象?ギャップはありましたか? 入ってみると割と保守的な印象でしたが、こういうことをやりたいと提案してみると後押ししていただけて、のびのびと過ごせています。 現場の雰囲気はどんな感じですか? 作業時間が多く、黙々と集中してタスクを進めることができます。 フルフレックスなので好きな時間にきて好きな時間に帰れるのが新鮮です。在宅の日は5時台から出勤している方もいて、自由だな〜と思いました。 ブログを書くことになってどう思いましたか? ブログがGitHubで記事を作成する形式で新鮮です。 オルソン パウリーン(Pauline Ohlson) 自己紹介 こんにちは!Pauline Ohlsonです。10月からプラットフォーム開発部モバイルアプリ開発GにてAndroidエンジニアを担当しています。 所属チームはどんな体制ですか? 大阪オフィス勤務で、iOSエンジニアと同じエリアに席があります。Androidプロジェクトのメンバーのほとんどが東京なので、自分はリモートで大阪から参加しています。 KTCへ入社したときの第一印象?ギャップはありましたか? 第一印象はKINTOの経緯が面白いなと思ったのと、未来像やアンビションが高い組織で、ここで働けたら面白そうだなと思いました。入社後は、最新のツールや技術を活用できる機会もたくさんありそうでワクワクしましたし、たくさんの方と仲良くなれる機会も思ったより多そうだなと感じてます。CIOやCEOと直接お話しできるチャンスもあって嬉しかったです。 現場の雰囲気はどんな感じですか? みなさん情熱を持ってお仕事されつつもちょっぴり遊び心も持ち合わせた、優しい方が多いです。相手の立場から物を考える方も多いことから効率よく働きやすい環境です。 ブログを書くことになってどう思いましたか? こういった形でブログを書くのは初めてなので、面白くて良いアイデアだなと思いました! 白濱 宏樹 (Hiroki Shirahama) 自己紹介 KINTO ONE開発部新車サブスク開発Gの白濱です。KINTO ONEのフロントエンドを担当しています。 所属チームはどんな体制ですか? 10月入社の私、IUさんを含めて6名体制です。 KTCへ入社したときの第一印象?ギャップはありましたか? フルフレックスで好きな時間に働けるのがとても新鮮でした。 現場の雰囲気はどんな感じですか? 担当タスクを黙々と進めていく感じです。日次の作業報告や週次の振り返り等もあるため、チームメンバーがどのようなことをしているのかを把握できたり、担当タスクについての相談がしやすい環境だと思います。 ブログを書くことになってどう思いましたか? 気になってはいましたが、まさかこんなに早く書くことになるとは…。 さいごに 入社直後に感想をお伺いするという急なお願いであったにも関わらず、 快く引き受けて頂き本日はみなさまありがとうございました! 今回、KINTOテクノロジーズの新たな側面を記事に残せることができたのではないかなと思います。 今後もたくさん面白いコンテンツを作成頂けること、楽しみにしています(笑)
アバター
Introduction Hinomori, Kinoshita, and Nakaguchi from the Mobile Development Group all attended the iOSDC Japan 2022 . It was held as an in-person event for the first time in two years, but also streamed online, giving it a hybrid format. This allowed us to participate in both forms, according to our circumstances. In this article, we'll share our thoughts on the event, and talk about some of the talks that made a particular impression on us. Thoughts Hinomori Let me start by saying that this is the fifth year I've attended, and the novelty items are always amazing! The pamphlet is also getting thicker every year, and I'd like to go through it slowly when I have time. The T-shirt has a very chic design again this year, and has quickly become a favorite to wear at home. Novelty items Pamphlet ![Novelty](/assets/blog/authors/HiroyaHinomori/IMG_1407.jpg =400x) ![Pamphlet](/assets/blog/authors/HiroyaHinomori/IMG_1487.jpg =400x) The event was held this year online and also in person for the first time in three years, but still being a parent of a young child myself, it's tricky to spend the whole day away. So, I grudgingly made do with joining in online. (The Niconico live broadcast will apparently be running until 23:59 on October 12, 2022 , so don't forget to check it out if you haven't already.) There were lots of sessions that really grabbed my attention this time around, but I'd like to talk about the ones that had to do with SwiftUI. Yeah, I know. Everyone's using SwiftUI. There was a case study on View splitting that was very helpful. Documenting rules is important, but I also thought that it's probably important to make sure the documents are easy to find. All about SwiftUI Navigation Point-Free's talk about swiftui-navigation was very informative. I get the feeling it could be used in connections that go all the way to NavigationStack. Adopting SwiftUI for large-scale UIKit-based projects This showed how hard it is to switch an already released app from UIKit to SwiftUI. Sure enough, the initial version of iOS 13 is the major problem, right? Making SwiftUI and UIKit get along A talk about the dos-and-don'ts and how-to's for when you use a mixture of UIKit and SwiftUI. I still go back and forth between UIKit and SwiftUI a lot, so this was very helpful. With the release of iOS 16, the minimum required operating system for many apps is now iOS 14, so I think it's time to start considering using SwiftUI full-on. That's probably why there were so many sessions about it, too. We're planning to steadily shift to SwiftUI in the future. However, keeping past OS versions in mind, we'll need to be careful because there seem to be a few issues to iron out before we can make an application with it entirely. That said, we'd also like to learn more based on what we learned here. Kinoshita I've gone to every iOSDC since the one in Nerima, and this year's narration was spectacular, too. When I heard That voice, the rush of nostalgia made me feel right at home. These are the sessions from this year's iOSDC that left a memory in me: Starting to deliver real-time AR using Unreal Engine and iPhones This session has stuck in my memory partly because I watched it while feeling nice and fresh in the morning, and partly because at the time, I was trying out Uaal with Flutter using Unity's libraries as part of running PoC experiments at work. I'd like to talk about Uaal with Flutter in another blog article if I get a chance. A blog article and resources from the session's speaker can be found at the link below. I gave a talk at iOSDC 2022 The example in the talk used Unreal Engine instead of Unity (which has a proven track record on a variety of platforms), so it was rare and very interesting. The real-time AR synthesis demo scene at the beginning of the session was very nice, and got a lot of enthusiastic comments on Niconico, too. Personally, I think that despite the dearth of information on integrating 3D rendering software into mobile devices to make things easier to handle, there's probably a surprising demand for doing it. I didn't have a lot of information to go on when I was using Uaal with Flutter to build an app either, and had wanted to try it with Unreal Engine if it could handle it. So as I was watching the session, I was wishing I'd know about it sooner. I think it'll very interesting if metaverses get more popular worldwide and more and more knowledge about them gets pooled and shared. Lunches and drinks weren't provided in the venue at this year's iOSDC, so I hope the COVID-19 situation settles down soon, and those lovely pickles make a welcome return. I'm looking forward to the return of an iOSDC where we enjoy a drink with the other participants. A big thank you to all the staff who worked so hard this year. Looking forward to seeing you all again next year! Nakaguchi I've been an iOS engineer for about 2 years, and this was my first iOSDC. I was finally getting the hang of MVVM and Combine, and wanted to make my next step learning about SwiftUI and Concurrency. This year's iOSDC had some very helpful sessions on these. There are lots of aspects that are difficult to understand from written materials like books and technical articles alone, but hearing about them in the presentations made some of them a lot clearer. I want to review the presentations to deepen my understanding. Yeah, I know. Everyone's using SwiftUI. This session had a presentation that focused on splitting in SwiftUI. This part is tricky because there's no one right answer for where to do the split, so I found it very helpful. Important takeaways for me included "Put things into words" and "Document the rules on a by-project basis," and I felt it that for parts everyone has trouble with, it's all the more necessary to explain things clearly. The basics for understanding reactive programming in the Swift Concurrency era As I said above, I'd just started learning about Concurrency, and still couldn't really see why it was useful in the first place. The session also clarified where you can switch from mobile app reactive programming to Concurrency and where you can't, so I felt much happier about that, too. I'd like to use Concurrency as much as possible due to its high readability, so I plan to keep on referring to this presentation. Conclusion iOSDC Japan 2022 was great fun! Hope to see you all again at iOSDC Japan 2023! If you can't wait until then, go and check out KINTO Technologies Careers . We're not just looking for engineers. Also hope to meet some of you after you join us here!
アバター
KINTOテクノロジーズのソフトウェアエンジニアの五条です。 『 Prism Japan 』という、AIがお出かけ先を提案してくれるモバイルアプリのバックエンドを開発しています。 今回は、Prism Japan のプロダクトオーナーをしている 齋藤と共著で、比較的規模の大きいアジャイルチームで、どのように Prism Japan を改善していったのかを紹介します。 Prism Japan とは はじめに、私達が開発している Prism Japan について、簡単に紹介させてください。 Prism Japan は一言でいうと、「AIがお出かけ先を提案してくれるアプリ」です。 「休日にお出かけしたいけど、どこに出かけようか決まってない…」 そんなときに Prism Japan が使えます。 たとえば「気分で検索」機能では、好みの写真を選ぶだけで、ユーザーにおすすめな旅行スポットを提案します。 ユーザーは写真を眺めながら、気分に合った旅行スポットを探すことができます。 2022年8月にiOSアプリをリリースし、Android版は2023年4月にリリースしました。 2023年10月末現在で累計の会員登録数は3万人を超えています。 Prism Japan の開発体制 Prism Japan は iOS と Android で利用できます。モバイルアプリを開発するチームは、 iOS 開発チーム Android 開発チーム で分かれています。 バックエンドは API 開発チーム AI 開発チーム で分かれています。それぞれの分野の専門性を持つメンバーが開発を担当します。 開発チーム以外にも企画・分析・デザインを担当するチームがあります。 プロジェクト全体の方向性を決めて、ユーザーの必要とするであろう機能を考えているのがプロダクトオーナーです。 Prism を主担当としているメンバーで 15人、サブ業務として開発に関わっているメンバーを含めると 20 人となります。 アジャイルチームとしては比較的大所帯と言えるかもしれません。 アジャイル開発を軌道に乗せるためにやったこと Prism Japan 開発チームは、現在はひとつのチームで開発していますが、以前はフロントエンドとバックエンドチームで別チームとして開発を進めていました。 チーム間の作業調整が必要なときは、Slack のチャンネル内でお互いに作業を依頼していました。 作業を依頼したあとは、各々のチームに完全に任せる形です。 組織上、所属部署が分かれていることが分業の背景にありましたが、チームの運営において以下のような課題がありました。 フロントエンド⇔バックエンド間で開発過程を共有しないため、チーム間で意見のすり合わせが行われない フロントエンド⇔バックエンドをまたいだチーム全体の改善が生まれない 作業の依頼ベースで開発が進むため、プロジェクトマネージャーと遠いメンバーはオーナーシップを持ちにくい Prism Japanの初期開発が一段落した段階で、アプリの改善フェーズに適した開発方法に切り替えるべきという議論が始まりました。 上記の課題に対処できる開発方法を検討する中で、ユーザーの反応を見ながら柔軟に仕様変更を行うことができ、モバイルアプリとの相性がよい「アジャイル開発」に切り替えるという点は早々に決まりました。 中でもチーム内でも有識者がいて、課題であったコミュニケーション面でのメリットも大きい「スクラム」を手法として採用することになりました。 ゼロからスクラムを立ち上げる手順 チームメンバー全員がスクラムを経験してきたわけではありません。そのため、スクラムとはどのようなものかをチームメンバー間で共有する必要がありました。 スクラムマスターとなった小山が講師となり、チームでスクラムを学ぶところから始まりました。 詳細は下記の小山の記事を参考にしてください。 iOSエンジニアが認定スクラムマスター研修を受けてスクラムマスターをやってみた話 先ず(勉強)会より始めよ 「これからはスクラムで開発していきたい」というときに、まずはスクラムについての勉強会から始めるのは非常に有用だったと感じています。 チームメンバー全員に前提となる基礎知識を共有した上で、「これからスクラムを始めるぞ」と意識付けができたからです。 スクラムイベントを設定する 勉強会の次の週から、スクラムイベントを設定しました。最初は手探りだったので、スクラムマスターとプロダクトオーナーが中心となって、それぞれのイベントで何をやるかを話し合いました。 はじめに行ったのは以下のような作業です。 2週間のスプリントを設定する プロダクトオーナーがストーリーを作成する 毎日15分のデイリースクラムを始める スプリントプランニングを設定する スプリントレビューを設定する スプリントレトロスペクティブを設定する スクラムマスターが主導して、まずイベントを設定して走り出しました。 この記事ではスクラムイベントの詳細については触れませんが、 スクラムマスターが主導してイベントを設定する スクラムマスターとプロダクトオーナーが密に連携して、イベントを動かす といった具体的なアクションを起こすことで、スクラムが動き出した感覚があります。 特にスクラム開始時点の手探り状態では、チームを動かしていくスクラムマスターの情熱と、やりながら改善していく姿勢が非常に重要だと思います。 チームビルディング スクラム開発を通じて感じたのは、チームが育つ感覚です。 特に、次のような点でスクラムの成長を感じています。 仕様についての議論が気軽に行えるようになった アプリの機能に対するメンバーの意見交換が活発になった スクラムマスターだけに頼ることなく、スクラムを回せるようになった どの組織でも同様かと思いますが、最初からスクラムがうまく回っていたわけではありません。 スクラムでチームを組み始めて最初の1ヶ月は、プロダクトオーナーの齋藤とスクラムマスターの小山が中心となって、常に「チームを良くする方法」を探っていました。 スクラムが淀みなく回るようになるまで、だいたい2ヶ月を要しました。 プロダクトオーナーの役割と悩み 一般的なスクラム開発におけるプロダクトオーナーの役割は開発要件をプロダクトバックログという形で管理し、開発の方向性を定めることでプロダクトの価値を最大化することです。 Prism Japanにおいてもこの点はもちろん最重要ではありますが、『プロダクトの価値の最大化』のために意思決定を行うことは簡単ではなく、当初は自分の決定が正しいのか、本当にユーザーに必要な機能なのかと、悩みは尽きませんでした。 悩みを解決するために2つのことを実行し、今では確かな判断基準をもって意思決定できるようになりました。 やったこと①:Prism Japanが解決するユーザー課題を再定義 ジョブ理論のフレームワークを元にしたワークショップをスクラムメンバー全員で実施しました。 理論についての細かい説明は割愛しますが、これによりPrism Japanがどのような価値をユーザーに提供するアプリであり続けるべきなのかという根幹となる部分を定義することができました。 やったこと②:データドリブンな意思決定の導入 プロダクトオーナー自身がデータエンジニア・データアナリストとしての経験を持つことから、ユーザーログの設計やアプリ利用状況の可視化、課題仮説を元にした分析などを積極的に行っています。 これにより、既存のアプリにおける課題、リリースした機能がユーザーに受け入れられているかなどを確かめつつ、開発方針に落とし込めるようになりました。 スクラムで発生した課題と解決するためにやったこと 最後に、スクラムで開発を進める中で発生した課題と、それを解決するために私達が何をしてきたかを紹介します。 [課題]プロダクトオーナーとエンジニアで「やりたいこと」の認識が合っていなかった スクラムでの開発を始めた当初は要件や仕様を正確に適切なタイミングで伝えることに苦労しました。 そもそも開発着手まで具体的な仕様が決まっていなかったり、口頭でのすり合わせに頼ってしまい、メンバー毎の認識に違いがあったりと原因は様々でした。 [解決方法]スプリントリファインメント、スプリントプランニングの活用 関係の良いスクラムでも礼儀は必要です。 依頼の仕方やタイミングがスクラムイベントにきちんと組み込まれたことは非常に効果がありました。 スプリントリファインメント 次回スプリントが開始になる前の週に実施されます。 プロダクトオーナーがユーザーストーリーについて説明を行い、要件の合意と簡易的な見積もりを行います。 プロダクトオーナーはこの日までに次回スプリントで対応したいユーザーストーリーを確定しておく必要があります。 スプリントプランニング ユーザーストーリーやタスクの優先度を決めた上で、スプリントで達成するゴールを合意します。 過去の実績と照らし合わせて実現可能な作業内容であることも確認するので、エンジニアがスプリント内で実施する内容に責任と自信を持つことができます。 アジャイルスクラムを導入した効果 導入当初の細かな課題はあったものの、以前の開発方法における課題に対してはどのような成果があったかを振り返ってみたいと思います。 フロントエンド⇔バックエンド間で開発過程を共有しないため、チーム間で意見のすり合わせが行われない デイリースクラムで双方が取り組んでいる内容が把握できるようになったという効果がありました。 その他にもリファインメントやプランニングといったイベントではバックエンドの実装方針に対して、フロントエンド側の要望を元にした議論も活発に行われるようになり、開発をスムーズに開始できたり、細かい手戻りなども大幅に減らすことができました。 フロントエンド⇔バックエンドをまたいだチーム全体の改善が生まれない エンジニアがアプリをよりよいものにしたいという主体性・責任感をもって議論に参加するようになったため議論のレベルも以前より数段上のものになったと感じています。 パフォーマンスや今後の拡張性などを意識したアーキテクチャレベルでの議論も行われるようになり、チームが分かれていては議論にすらならなかった内容が、きちんとまとまるようになりました。 作業の依頼ベースで開発が進むため、プロジェクトマネージャーと遠いメンバーはオーナーシップを持ちにくい 作業依頼ではなく、あくまでも依頼するのはユーザーストーリーの実現となったため、エンジニアがそれぞれ実現のための最適な方法を考え、ときには提案まで行うようになっています。 開発自体の改善もそうですが、スプリントをこなすごとに多くのメンバーが成長し続けられていることも実感できています。 最後に 開発メンバーと企画・運用メンバーが共通の目的を持ち二人三脚で改善を進めているという点は特徴的かと思いますが、アジャイルスクラムでの開発を進めている方・これから始めようという方のご参考になれば幸いです! また「Prism Japan」はリリースから1年少し経過したばかりでアプリもメンバーも絶賛成長中です。 こんな体制で開発されたアプリがどのような成長を遂げていくも是非体感してみてください! Prism Japan を触ってみたい方へ Prism Japan は下記のリンクからインストールできます。 iOS: App Store Android: Google Play
アバター
こんにちは。Woven Payment Solution開発グループの大杉です。 私たちのチームでは、 Woven by Toyota において Toyota Woven City で使われる決済システムの開発を行っていまして、普段はKotlin/Ktorによるバックエンドの開発とFlutterによるフロントエンドの開発をしています。 以前の記事 でフロントエンドの技術選定をしてからWebアプリの開発を進めていましたが、今ではWebだけに留まらずモバイルアプリも含めて7つのFlutterアプリを運用するまでに開発規模が大きくなってきました。 今回の記事では、バックエンドエンジニアしかいなかった私たちのチームで、効率的に複数のFlutterアプリをバックエンド開発と並行して開発していくために工夫してきたことを紹介したいと思います。 Flutter and the related logo are trademarks of Google LLC. We are not endorsed by or affiliated with Google LLC. 要約 私たちは、決済システムのバックエンド開発とフロントエンド開発をしているチームです。開発しているアプリは、Webは管理画面、モバイルはWoven CityでのPoCに参加される方の利用を想定した決済関連のアプリを提供しています。 決済システムのバックエンド開発と並行して、Flutterアプリを開発していくためには効率的に開発を進めていくことが必要であり、以下のことを実施してきました。 アプリ共通のアーキテクチャを設計 がんばらないUIコンポーネントの設計方針を策定 技術スタックの統一と開発の流れを定義 アプリ共通のアーキテクチャを設計 バックエンドに限らずフロントエンドのアーキテクチャも時代と共に色々な形のものが提唱されていますが、開発チームやプロダクトのフェーズや性質にあったものを選び、改善していくことが良いことだと思います。 私たちは、バックエンド開発ではクリーンアーキテクチャを採用していることもあり、それに類するLayer-firstなディレクトリ構造でMVVM、リポジトリパターンのみを採用したアーキテクチャをFlutterアプリに適用しています。具体的には以下のようなディレクトリ構成となっています。 ディレクトリ構成 lib/ ├── presentations │ ├── pages │ │ └── home_page │ │ ├── home_page.dart │ │ ├── home_page_vm.dart │ │ ├── home_page_state.dart │ │ └── components │ ├── components │ ├── style.dart // 共通のスタイル定義 │ └── app.dart ├── domains │ ├── entities │ └── repositories // リポジトリのinterface ├── infrastructures │ └── repositories └── main.dart ディレクトリの役割 大きく3つのディレクトリでレイヤー構造を表しています。それぞれのディレクトリの役割について簡単に説明すると、以下のようになっています。 Directory Layer Role presentations プレゼンテーション層 View, ViewModel, 必要であれば状態を定義 domains ドメイン層 ドメインモデルやロジック、リポジトリへのインターフェースを定義 infrastructures インフラ層 APIコールなど、リポジトリ層の実装を定義 レイヤーパターン中心の設計ではユースケース層が欲しくなるかと思いますが、現時点ではフロントエンドにほとんどビジネスロジックが存在しないため、ViewModelに集約しています。 私たちが開発しているアプリは、まだ複雑な機能がなく、基本的にページ = 1つのドメインが成り立っているため、この設計で今のところ上手く開発が回っています。PoCなどで新しくアプリを作成する際は、基本的にこのテンプレートで作り始めるようにし、アプリごとにアーキテクチャの差異が生まれないようにしています。 がんばらないUIコンポーネントの設計方針を策定 UIコンポーネントを設計する際、 Atomic Designは採用しないこと と 積極的に共通コンポーネント化しないこと にしました。 少しネガティブですが、以下のような理由があります。 Atomic Designの分類のレベル感をメンバー全員で揃えるのは難しい 共通コンポーネントを作り込むことよりもページの実装にフォーカスしたい 特に、Flutterで抽象的なWidgetを作り込むのはかなり気合いが必要だった 共通コンポーネントを作る方が正統な進め方だとは思いますが、柔軟に仕様変更しながらアプリを作り変えていくフェーズである現時点ではあまり共通化しない方が短期的なメリットが大きいと判断しました。 技術スタックの統一と開発の流れを定義 状態管理や画面遷移のフレームワークについても色々な技術が生まれては消えていったと思います。初学者はその情報の多さにどのライブラリを使えばいいのか混乱してしまうというのは自分も経験したのでよくわかります。そこで、私たちは以下の技術スタックをどのアプリでも共通して使用することにしています。 技術スタック Target Library 状態管理とProviderの作成 riverpod モデルの定義 freezed 画面遷移 go_router APIクライアント dio , openapi-genrator プロジェクト管理 melos :::message 私たちはOpenAPIを用いたスキーマ駆動開発をしており、バックエンド開発時にで作成したOpenAPIスキーマのyamlファイルを元にフロントエンドのAPIクライアントをopenapi-generatorを使って自動生成をしています。 ::: この 状態管理とProviderの作成 にはRiverpodを利用しています。RiverpodのProviderという概念はバックエンド開発ではあまり馴染みがなく、また、ハンドコードで実装すると思い思いのProviderを実装できてしまうため、実装フローや適用場所をやや厳密に定義しています。 必ず riverpod_generator を使用してProviderを生成する インフラ層のリポジトリとドメイン層のインターフェースをバインドする際は、Providerを使用する @riverpod Future<HogeRepository> hogeRepository(HogeRepositoryRef ref) { final apiClient = await ref.watch(openApiClientProvider); return HogeRepositoryImpl( apiClient: apiClient.getHogeApi(), ); } ViewModelはAsyncNotifierProviderで実装し、Viewで必要となるリポジトリのProviderはViewModelに集約する @riverpod class HogePageViewModel extends _$HogePageViewModel { @override Future<List<Hoge>> build() { return _fetchData(); } Future<List<Hoge>> _fetchData() { repository = ref.watch(hogeRepositoryProvider); return repository.getList(); } Future<void> registerData(Hoge hoge) { repository = ref.watch(hogeRepositoryProvider); return repository.register(hoge); } } Viewは、ViewModelからのAsyncValueを監視してUI表示を行う。または、ViewModelを介してリポジトリへCRUD処理を行う 以上のように、リポジトリの実装〜UIとバックエンドの組み込みのフローを規定しており、スプリントのタスクを作成する際もフローに準じた粒度でチケットを分割するようにしています。 おわりに プロジェクトの中でクライアントアプリの開発優先度が上がったことでフロントエンドの開発ポリシーを規定し、チームで開発が円滑に進められるように工夫して来ました。 Web管理画面の多くが、リスト画面 / 詳細画面 / 編集画面 が基本セットになっていることが多いので、今後はコードジェネレーターを活用してさらに効率よくUI実装ができるような仕込みを取り入れていくことも考えています。
アバター
Hello. I am Ono, from the Woven Payment Solution Development Group. Our team is working on a payment platform that will be used in Toyota Woven City . While the content may be a bit dated, you can check out more about our activities here: 20220422 Woven City Tech Meetup Tech Talk by Rie Ono This article is about using EventStorming , a DDD modeling technique that we use to design our payment system. We're still figuring things out, but I wanted to talk about what we've learned so far. What is EventStorming? EventStorming is a workshop-based method for modeling systems. It was created by Alberto Brandolini. The EventStorming website says: EventStorming is a flexible workshop format for collaborative exploration of complex business domains. Also, in chapter 12 of Learning Domain-Driven Design , titled "EventStorming", the below mention can be found: EventStorming is a low-tech activity for a group of people to brainstorm and rapidly model a business process. In a sense, EventStorming is a tactical tool for sharing business domain knowledge. In other words, it's a workshop for achieving the following goals: - Smoothly extracting domain knowledge from experts Sharing domain knowledge with stakeholders Designing domain models Preparing for our EventStorming Who Should You Ask to Join? The book mentioned above, Learning Domain-Driven Design, states that there should be no more than 10 participants. If there are too many, participants may be reluctant to speak or consensus may be difficult to reach. It also mentions that more insight could be gained if you gather a diverse group of people together. With that in mind, choosing the right group of participants might be the first challenge. Facilitation It's a good idea to have a facilitator to do timekeeping for each of the phases and guide the conversations so that they don't stray too far off topic. In our case, the engineers doing the design work performed this role themselves. Engineers The engineers design and develop the applications. In our case, in addition to our own team's members, we also invited some from the App Team who develop native apps to join in, to share their domain knowledge. Domain Experts The domain experts are those who have deep knowledge of the domains in question. Gathering people who have extensive knowledge about the existing systems, other companies' businesses, and related fields is key. The goal of EventStorming is not only to design software, but also to draw out knowledge from domain experts. In our case, we are lucky to have on board lots of members from the Business Team who've worked on payment systems. Also, our own team leader Kamei has worked in the payment field in the past, and as such, can give input from both engineering and payment points of view. UI/UX Designers and QA Engineers We couldn't do so in this example, but I think it'd also be good to bring in UI/UX designers and QA engineers involved in application development. That way, they'll be able to share their knowledge on how to streamline the UI/UX and QA design. What to Prepare EventStorming can be time-intensive. So I recommend to prepare things in advance so you can do it smoothly. Schedule it properly with all the participants If possible, limit the time to half a day or just one day. You'll be inviting people from various teams, so it might be difficult to schedule it so that everyone can take part. However, it's very important to do so. Place I think gathering participants face-to-face makes it easier for people to speak up. Sticky notes of various colors Prepare some sticky notes in the following colors: Whiteboard and markers Prepare a large whiteboard and markers for the sticky notes. Snacks and drinks for relaxing We wanted to prepare some...but had to give up due to COVID-19. What to know in advance Before the EventStorming began, via text, we shared with the participants information like the goals, prerequisites, and matters already decided on regarding the system we wanted to build, so that we could assume everyone already knew a certain amount about it. We also provided time for brief self-introductions, to make it easier for participants who'd never met before. This time, we did the EventStorming session in English, so we checked beforehand the English terminology needed for the domain knowledge and when facilitating. Thinking Ways to Reduce Face-to-Face Time to Prevent Contagion In Remote EventStorming , Alberto Brandolini writes that EventStorming is hard to do remotely. However, given the current times, we decided to try it online. If you do it face-to-face, to prevent contagion, it is better to prepare a meeting room or open space that's large enough to keep social distancing, while allowing the participants to concentrate. We found Miro to be a helpful tool to proceed online. It has easy-to-use templates such as this one: Judith Birmoser's Event Storming template | Miroverse To reduce the face-to-face time, a small group of team members did the groundwork for Phase 2 (which I'll talk about below). When you do it, get all the participants to review everything, and point out any mistakes or add anything that's missing. How We Did It Now, I'll talk about how we did the EventStorming session. Phase 1: The Big Picture First, create a Big Picture to clarify the overall business process. As you're brainstorming, write the domain events on orange sticky notes. E.g.: "A payment was made." Ask the people who've given opinions on the domain events to explain their ideas, remove any duplicates, check with domain experts to ensure things have been understood correctly, and put the sticky notes in chronological order. Phase 2: Process Modeling Next, model the processes between the Events. Add the sticky notes below to the identified Events. Add the Actors using yellow sticky notes. Think about who or what will carry out the Commands. Add the Commands that cause the Events using blue sticky notes. If you can do View models, write them on green sticky notes. Write the Policies on purple sticky notes. This Policy concept is difficult for me. The idea seems to be to write out the prerequisites and conditions for the Commands. If there are any questions or risks, write them on red sticky notes. Get the domain experts to check that the Events and timeline are correct, and answer any questions. Phase 3: Software Design Next, think in more detail about the context that’s shaping up, and get things ready for the coding to begin. Summarize the picture that seems to be forming regarding maintaining data integrity in terms of the business domains, writing the ideas as Aggregates (bright yellow). If going via external systems, add them with pink sticky notes. Try arranging those that seem like they can be grouped together as a subdomain. If it looks like a UI can be defined at this point, it might be a good idea to create a paper prototype. When you're mostly done, you can discuss the red sticky notes in detail. Alternatively, if you can't reach a conclusion right away, you can do another EventStorming session to dig deeper. What To Do After Once you've gotten this far, go back through the steps in reverse order to make sure nothing has been left out. Software design as a cooperative game with EventStorming Also, in our case, we narrowed the scope and did more EventStorming sessions after identifying the overall domain. Final Thoughts After the Sessions Below are some thoughts after trying out EventStorming: With this method, you can get a more comprehensive, unbiased picture from several colleagues than from just talking individually with them, and then creating the requirements and specifications based on that. Consequently, the communication cost may be lower in the long run. -I thought it was good that knowledge could be shared and checked among multiple members all in one go. Having tried it, I thought it would be very effective when building something from scratch, and also felt it enabled us to compare answers on things we'd been thinking about. While doing the modeling, -We found issues that had been forgotten about or hadn't been seen before. Sometimes, members who were new to a given aspect of the work asked questions and raised issues about it that reflected beginners' perspectives. Members who don't usually get to see each other were able to build connections with each other. These are the difficulties I identified: It takes time to do it, so it was difficult to coordinate all the members' schedules and maintain concentration. Facilitating was difficult. This is just a question of getting used to it, so maybe it'll get better the more we do it. Personally, I also found it tough to facilitate and discuss things in English, so I want to practice that as well. I think it was especially difficult for domain experts to explain in English about payments that were specific to Japanese business practices. Future Issues If we get to do more EventStorming sessions, I hope team members get more and more opportunities to facilitate. I also want to do a bit of studying on the steps for taking the domains identified in the EventStorming and incorporating them into the actual code. Examples: Incorporate the domain description mini language . Apply it to event sourcing . So, those are my impressions from trying out EventStorming!
アバター
こんにちは、KINTOテクノロジーズの森です。ブログを書くのは共著も合わせるともう10回目になりました。驚きです。 ほとんどがイベントレポートなのですが、今回は久しぶりに実務のお話です😎 さて、私は普段、グローバル開発部で Global KINTO Web のPdMと個人情報関係のタスクリードを務めております。個人情報関係は主に各国KINTOサービスのユーザープール関連で入ることが多く、Webサイトの話とはあまり交わらなかったのですが、今回WebでもGDPR対応しましたのでそのお話です。ようやく2つの本業が交わるときがやってまいりました👏👏👏 ユーザープール関係で発生したグローバルデータ移転にGDPR等個人データ関連法の対応をしたお話は私の前回の記事をご参照ください。記念すべき初執筆で、ガチガチな文章なのがおもしろいです😂笑 KINTOのグローバル展開におけるGDPR等個人データ関連法対応 そもそもGDPRとは?対象は? この執筆にあたり、前回記事を読み直していたのですが、肝心の 「GDPRとは?」 が抜けていました。なんてこった🤦 改めてご説明します。 GDPRとは、正式にはGeneral Data Protection Regulation(一般データ保護規則)という名称の法令です。欧州連合(EU)および欧州経済領域(EEA)において、個人データ保護や処理に関して詳細に取り決めています。その目的は域内に居住している人の個人データを保護することです。国籍ではなく、「居住者」が対象です。 欧州企業はもちろん対象となりますが、欧州域外の企業であっても以下の場合は対象となります。 欧州域内に拠点(子会社など)がある場合 欧州向けに商品やサービスを提供している場合 欧州個人データの取り扱いを委託している場合 たとえば、英語のWebサイトだとしても、欧州顧客をターゲットとしてないのであれば対象とならないケースもあります。一方で、インバウンド顧客をターゲットとしたサービスなどは対象となる可能性が高いです。 Global KINTO WebとGDPR対応 Global KINTO Web は世界40か国以上で展開するKINTOサービスのブランドサイトという位置づけです。KINTOは欧州でも多くの国でサービス展開をしているので、欧州のお客様もこのサイトのターゲットです。そのため、GDPR対応は避けられません。 もちろん、リリース時にもプライバシーポリシー等整備していましたが、当初とは運営体制が変わったり、機能が追加されたり、法令も改正されたりと、状況が変わったため、外部法律事務所の力を借りて今回再整備することとなりました。 Global KINTO Webでは問い合わせフォームで問い合わせいただく際にお客様のお名前やメールアドレス、電話番号などの情報を取得している他、サイト改善に役立てるためにCookieも取得しています。 このCookie、日本では「個人関連情報」と位置付けられていますが、GDPRでは「個人情報」の扱いです。ですので、Cookieを取得しているだけでも欧州をターゲットとしていればGDPR対応は必要になります。尚、Cookieについて、GDPR本文では Recital30 に軽く言及されているだけで、その詳細については ePrivacy指令(EPD) 通称Cookie法で具体化・補完されています。 では、Cookieを取得しているWebサイトは何をしないといけないでしょうか?🤔 具体的には以下です。 Receive users’ consent before you use any cookies except strictly necessary cookies. Provide accurate and specific information about the data each cookie tracks and its purpose in plain language before consent is received. Document and store consent received from users. Allow users to access your service even if they refuse to allow the use of certain cookies Make it as easy for users to withdraw their consent as it was for them to give their consent in the first place. (引用) Cookies, the GDPR, and the ePrivacy Directive - GDPR.eu Strictly necessary cookieを除いて、Cookie取得の前にユーザーから 同意を得る 同意を得る前にCookieが追跡するデータとその目的について、 正確で具体的な情報をわかりやすい言葉で提供 する ユーザーから受け取った 同意情報を保管 する ユーザーが特定のCookie取得を拒否してもサービスを利用できるようにする ユーザーが同意を取得したときと同じように、 同意を簡単に撤回 できるようにする (筆者翻訳) GDPR対応ではプライバシーポリシーの整備なども求められますが、Cookieにおいては上記のようなことが別途求められます。皆さんもいろいろなWebサイトを見てCookie取得に関するポップアップ(Cookie同意ポップアップ)やバナーを見かけると思いますが、主にアレです。 Cookieの種類 Cookieはその用途のカテゴリとして4つの区分に分けられます。 カテゴリ 内容 Strictly necessary cookies (厳密に必要なクッキー) サイトが正常に機能するために必要なクッキー。例えばECサイトのカート内商品を保持したり、言語設定をキープしたりするのもこのクッキーの一例です。このクッキーを止めるとサイトが正常に機能しなくなります。ユーザーの同意は不要です。また、個人を特定できる情報を保存することはありません。 Functionality cookies (機能性クッキー) ユーザーが過去に行った選択を記憶することでパーソナライズするクッキー。例えばどの地域の天気情報が必要か、ユーザー名は何か、など。Preferences Cookiesとも呼ばれます。個人を特定できる情報を保存することはありません。 Performance cookies (パフォーマンスクッキー) 主にサイト改善のために利用されるクッキー。どのページを訪れたか、どのリンクをクリックしたかなどのサイト利用情報を収集します。すべて匿名化され、個人を特定できる情報を保存することはありません。Statics Cookiesとも呼ばれます。 Targeting cookies (ターゲット型クッキー) ユーザーの興味からパーソナライズされた広告を表示します。広告のパフォーマンスを測定するために他の広告主と共有することができます。ターゲティングクッキーは、主に広告に使用されるため、Advertising Cookies (広告クッキー)とも呼ばれます。 Global KINTO WebではこのうちStrictly necessary cookiesとPerformance cookiesを取得しています。Strictly necessary cookiesは例えば言語設定の保持や、カート内のアイテム情報保管など、システムを正常に動かすために必要なので同意は不要です。しかし、Performance cookiesに関しては 同意の取得 と 簡単に撤回できる仕組み が必要でした。 Cookie同意ポップアップの課題 Cookie同意ポップアップの設置によって、今まで取れていたユーザーデータの母数がかなり減ってしまうことが懸念されました。理由として、Cookieというよくわからない情報を取得されることに不安を感じてCookieの取得を拒否するユーザーはかなり多いためです。画面に何度も表示されて邪魔なのでとりあえず拒否🙅‍♀️!なんて方もいるのではないでしょうか? そこで、このCookie同意ポップアップの必要性について改めて紐解きました。先述の通り、欧州GDPRでは必須となりますが、日本法の(改正個人情報保護法や電気通信事業法に基づくCookie規制)の解釈ではこのポップアップも不要な場合があります。Global KINTO Webについても、欧州は必要だけどそれ以外は不要という判断となりました。尚、Cookie同意ポップアップをあえて表示することで、CSRの観点ではきちんと対処していることのアピールともなり得ます。今回は実務とのバランスで判断しました。 尚、日本の改正個人情報保護法での解釈については トップコート国際法律事務所のこちらの記事 にわかりやすくまとめられておりますのでぜひご参照ください。 事前調査 欧州にのみ表示するソリューションで実装する流れにほぼ決定しましたが、念のため他のWebサイトも調査することになりました。この夏たまたま欧州へ帰省するチームメンバーがいたため、日本ではポップアップ表示のないWebサイトが欧州ではどうなっているのか、を調べてもらいました。以下がその画面キャプチャです。 Site Japan Europe Facebook ![facebook_ja](/assets/blog/authors/M.Mori/20231205/Facebook_ja.PNG =200x) ![facebook_eu](/assets/blog/authors/M.Mori/20231205/Facebook_eu.PNG =200x) Google ![google_ja](/assets/blog/authors/M.Mori/20231205/google_ja.PNG =200x) ![google_eu](/assets/blog/authors/M.Mori/20231205/google_eu.PNG =200x) Booking.com ![booking.com_jp](/assets/blog/authors/M.Mori/20231205/Booking_ja.PNG =200x) ![booking.com_eu](/assets/blog/authors/M.Mori/20231205/Booking_eu.PNG =200x) Toyota Motor ![toyota_ja](/assets/blog/authors/M.Mori/20231205/toyota_ja.PNG =200x) ![toyota_eu](/assets/blog/authors/M.Mori/20231205/toyota_eu.PNG =200x) 欧州でアクセスすると驚くのが、どのサイトを訪問しても同意を求めるポップアップの嵐!!!😵‍💫 それだけ欧州では個人情報の取り扱いがセンシティブなものになっているということですね。例に挙げたのはCookie同意ポップアップ表示有無ですが、そのほかにもユーザーの所在によって表示するサイトをリダイレクトで切り替えていたり、表示するポリシーを切り替えていたりするサイトもありました。 同意取得フロー というわけで、Cookieにおいて欧州には特別対応をしているサイトが存在すること・そしてポップアップを表示しないとまずい、という事実確認もできたので、いざ実装です。今回は外部法律事務所のアドバイスに基づいて欧州居住者の場合は以下の流れで同意を取得するようにしました。 同意の撤回は プライバシーポリシー の中に [Cookie Preference] ボタンを埋め込んでいつでも撤回できるようにしています。また、この同意は6ヶ月ごとに失効するようにしています。これは、ユーザーに対して定期的にプライバシー設定を見直す機会を提供し、より透明性を確保するためです。 実際の画面 そんなこんなで無事、11月1日にプライバシーポリシーの改訂と共にCookie同意ポップアップをリリースしました。それぞれの地域で見え方が違います。 Japan Europe プライバシーポリシー更新のお知らせはあらゆる国で求められるので、どの国でも見られるようにしています。 Cookie同意ポップアップは欧州だけに表示されるようになっています。 ポリシー更新のお知らせを閉じたら何のバナーも表示されません。 Cookie Preferenceを開くとPerformance Cookiesへの同意を管理できるようになっています。 さいごに GDPRをはじめとする個人データ関連法対応は奥が深く、私自身2年以上従事してもまだまだわからないことだらけです。新たに違反判決が出たり、あらゆる国で法令やガイドラインが制定・改正されるなど、日々状況が変わります。かと言ってユーザビリティを落としたくない、できることを減らしたくない。このバランスの見極めが非常に重要となります 🤔⚖️ 今回のお話もあくまでGlobal KINTO Webの対応として外部有識者の力を借りて実施した実例であり、他のケースに必ずしも当てはまるわけではありません。例えば今回のような拒否率が上がってしまうという課題も、ポップアップをRejectされにくいようなUIUXで回避するといったソリューションも考えられます。そのWebサイトの目的やターゲット、内容などの状況によっても大きく変わりますし、こういった法令自体が解釈による部分が大きいので、一概にこうしておけば絶対大丈夫!とも言い切れないものです。 今後も、Global KINTO Webや各国の状況によって様々なアップデートは必要になってきます。もしCookie同意ポップアップが表示されたら、「あ、ちゃんとやってんだな👍」と思っていただけたら何よりです🙇‍♀️ Reference Cookies, the GDPR, and the ePrivacy Directive - GDPR.eu クッキーは個人情報?規制の対象となるクッキーについて | BizRis Cookie(クッキー)同意ポップアップの実装率と設置場所 Global Reach - グローバル展開する企業を支援 Cookie types
アバター
はじめに KINTOテクノロジーズ(以降KTC)で社内システムのPdMをしている小林です。KTCに入社後は[KINTO ONE]( 【KINTO】新車のサブスク、トヨタから|フルサービスのカーリース (kinto-jp.com) )のサイト再構築プロジェクトにアサインされ、PjM、テスト推進、移行推進を担当しました。アサインされた時にはすでにプロジェクト開始から1年が経過し、開発は完了しており、結合テストを実施してリリースするという局面でしたが、このあと様々な課題にあたることになります。今回はテスト推進担当として最初に遭遇した課題についてご紹介します。 サイト再構築プロジェクトとは KINTOのメインサービスであるKINTO ONE 新車のECサイトをリニューアルする内製開発プロジェクトです。アーキテクチャおよびデータ構造を全面的に見直し、開発生産性を高めることを目的としており、関連するプロダクトやサービスは20以上、ビジネスサイド、開発サイドの窓口担当者は合わせて50名ほどの規模でした。 まずは状況把握 私が入社したのは2022年2月です。まずは週次定例会に参加し状況を把握しつつ、テスト推進や移行推進を実施するという立ち位置でのスタートです。 入社する少し前の2022年1月中頃から内部結合テストが開始されていました。数日程度の遅延はあるものの順調との報告がなされており、この時点では内部結合テストの完了は2022年5月末の予定でスケジュールに違和感は無し。内部結合テストの計画は開発者が作成し、開発に携わっていないメンバーで構成されたテストチームがテストケースの作成と実施を担当するといった理想的な状況でした。 最初の課題 内部結合テストは7つにフェーズ分け進める計画でしたが、2月後半に内部結合テストの進捗に陰りが見え始め、3月最初の報告では1週間遅れとなりました。 このとき後続テストフェーズに影響がある不具合が10件以上あり、修正だけで1週間程度かかる苦しい状況であることがわかりました。このまま計画通りに行けるのか感覚を確かめるため、開発者に状況をヒアリングしたところ以下のような話が出てきました。 単体テストの終了条件がわからない 結合テストの開始条件がわからない 内部結合テストの内容が想定と異なる 開発者がまとめていた資料にはどのようなテストを実施するか記載はあるものの、確かに開始条件や終了条件に関する記載はなく、その手の情報は方針書や計画書にあるだろうと探しますが見当たりません。なるほど確かに。。 想定していたテスト内容について、開発者がまとめていた資料では「一通りの画面遷移を通してテストを行う」と記載があり、テスト項目として以下のような記載がありました。 正しいデータを入力した際に、正しい画面表示・遷移がなされるか DBに正しくレコードが作られているか 途中で処理を中断した際のデータは回復可能か この内容からストーリーをベースにしたテストを想定しているように見え、テストチームが用意したテストケースとも合致しますので、何が混乱のもとになったのか考える必要が出てきました。 ふと開発者がまとめた資料を見るとテストの実行方法について以下のように記載されていることに気づきます。 ブラウザごとにテストを実施する(画面テストと同様) DBについてはDBの値を直接SQLで確認する 単体テストで実施した画面テストと同様との記載。この記載では単体テストと同様のテストをフロントエンドとバックエンドを結合して実施するように見えます。なんとなく混乱の原因がわかりました。認識齟齬のない計画の重要性が感じられる課題です。 課題への対応 テストを止めず無理に進めても遅延が拡大する可能性があるため、この課題への対応は「2週間内部結合テストを止めること」でした。この対応により次のような効果が得られます。 開発サイドのリカバリが可能 テストを止めることにより修正に時間を使うことができ、遅延リカバリが可能となりました。また修正に注力することができるため品質が上がります。開発者とテストチームはテストが進まないストレスから解放されます。 品質管理方針と品質評価基準の整理が可能 テストを止めることにより、内部結合テストのみならずテスト全体の方針や基準の整理が可能となり、以下の内容を周知することができました。 各テストフェーズで確保すべき品質を品質管理方針として定める 各テストフェーズでの品質評価基準を定める 後付けでの方針と基準の提示でしたが、開発者は受け入れてくれました。この柔軟性がKTCの良いところですね。内部結合テストは無事回り始めました。 その後 その後も様々な課題が出てきます。他案件との関係でスケジュール変更が発生した際には、品質強化テストを追加したりと、状況に応じて品質を上げるよう計画を見直しつつ、外部結合テスト、他案件の後追い取込、QAテストを実施し、2023年8月に無事リリースを果たしました。 規模の割にリリース後の障害が少ないと言われています。しかしながら障害が発生すると少なからず業務影響がでてしまいますので、今後もより品質を高められるよう、できることを考えていきたいと思っています。 最後に 今回の経験から、次の2点が重要と考えています。 認識齟齬の無い計画 品質管理方針および品質評価基準の設定 開発者に任せるにしても何かあった際の道標として、計画、方針、基準はきちっと整備すべきと思いました。 また今回課題への対応について気を付けた点は、いずれの対応も開発者に作業指示を行わないという点です。サイト再構築プロジェクトでは詳細設計から内部結合テストまでを開発者に任せる方針としていました。これはプロジェクトの方針であり守りたかった部分です。そこもなんとか守れたかな。 以上、サイト再構築プロジェクトで最初に苦労したことのご紹介でした。
アバター
Introduction Hi everyone! I am Chris from Global Frontend team. Few weeks ago I went to Vue Fes Japan 2023 and I would like to summarize the contents and share some of my thinking. By the way, as a little personal background, I began using Vue early in my career as a web developer, and despite using it consistently since then, it's a bit embarrassing to admit that this was my first time attending this event, which made its debut back in 2018. Nevertheless, I was excited about my first attendance and the opportunity to meet Evan You, the creator of Vue and Vite, so I was looking forward to the event all morning! Once I finally arrived at the venue, I noticed a whiteboard near the entrance, so I took the opportunity to promote our company's presence! Keynote by Evan You In the keynote session, Evan You primarily reflected on Vue 3, discussing both the positives and areas for improvement. He began by acknowledging the challenges faced during the migration from Vue 2 to Vue 3, highlighting specific points: Numerous breaking changes Underestimating the impact on libraries within Vue's ecosystem Not releasing all changes at once On the positive side, he also mentioned the improvements related to version upgrades: Enhanced TypeScript compatibility Introduction of the Composition API Investment in Developer Experience (DX) The continued growth of Vue 3 Based on these points, Evan emphasized the importance of stability in future versions. He wants to ensure that future updates minimize disruptive changes, making it easier for developers to adopt improvements and new features while providing a better development experience. As an example, he mentioned the introduction of a Vue ecosystem CI (Continuous Integration) tool. This tool aims to check for any disruptive impacts on libraries that depend on Vue when upgrading Vue versions. While the tool passed all tests for most cases, it was essential to involve the community, especially for releases like v3.4 alpha, which involved significant refactoring. Feedback was sought from the community regarding this release, which included improvements in memory management and more efficient re-computation. Listening to the keynote, I appreciated Evan addressing the challenges of migrating from Vue 2 to Vue 3, as I had also faced difficulties in that process with products I am working on at the moment (In addition we also use Vuetify which makes things more complicated). Updates on Nuxt Next, Sebastian Chopin, the CEO of NuxtLab, and Daniel Roe, a Nuxt Core team member, presented updates on Nuxt. Sebastian introduced recent updates to Nuxt, including demonstrations of Hybrid Rendering (allowing per-page rendering methods) and deploying Nuxt apps to the edge using Cloudflare. He also called for recruiting more contributors to expand the Nuxt ecosystem. On the other hand, Daniel discussed the Nuxt ecosystem and provided updates on various tools and libraries: Enabling features from Nuxt 3, such as Composition API, in Nuxt 2 using nuxt/bridge Offering various testing capabilities for Nuxt with nuxt/test-utils Allowing page creation using Markdown files (.md) through nuxt/content They concluded by discussing the direction for Nuxt 4, focusing on improving Developer Experience (DX), providing many opt-in tools, and promoting a more open development process to encourage more community participation. Listening to the presentations by both speakers, I, as a Vue/Nuxt user and developer, was inspired by the active community and thought about contributing as a contributor to this community in the future. Perspectives on Open Source Software (OSS) Activities In this session, Anthony Fu shared his experiences and insights about his OSS activities. He began by using the example of the vscode-vue-i18n-ally extension, which focuses on accessibility tools for users of Visual Studio Code, the Vue framework, and the i18n feature. This limited the extension's user base significantly. Anthony discussed two approaches to expand the user base: Removing limitations on tool usage. For instance, making the vscode-vue-i18n-ally extension compatible with frameworks other than Vue, resulting in the creation of i18n-ally . Forming a union of tools: Expanding the user base by creating an ecosystem of tools that cater to different user needs. For example, Nuxt's ecosystem has several support tools that different users require. Such unions help users build a substantial development community. His insights highlighted the importance of not only expanding the user base but also potentially increasing contributors by broadening the utility of OSS tools. After hearing his presentation, I realized that, similar to conventional products, user acquisition is a critical concept in OSS, and attracting more users should always be considered. Q&A Session The last session was a Q&A session where Evan You, Sebastian Chopin, and Daniel Roe answered various questions from the audience. Although the session was one hour long, there were so many pre-submitted questions that only a portion of them could be answered. Here are four questions that left a strong impression: Q: What are your thoughts on Server-Side Components? A: Ultimately, static has its advantages, so it depends on your needs. One use case for Server-Side Components could be e-commerce websites. Q: Migrating from Vue, Nuxt, and Vuetify from version 2 to version 3 is challenging. Can you provide guidance on how to do it? A: It's entirely my fault that it's this challenging, so I apologize! One approach would be to create a new project from scratch and migrate gradually. Q: Are there any recommended design patterns for components in Nuxt? A: There's no specific pattern recommended. Vue was created with the idea of not being tied to specific patterns. Start small, develop gradually, and decide on patterns that work for your specific use case. Q: Are there plans for Vue to support Native app development? A: With the current team, there's no capacity to support it. It would likely require several times the current resources. Consider using Ionic or NativeScript for Vue Native app development. Afterparty The afterparty was a standing buffet, featuring Western and sushi dishes provided by sponsors. I'm quite introverted and usually hesitant to initiate conversations at events, but I mustered the courage to talk to core team members, including Evan, who came to Japan for the event. It was a great experience to have various conversations. I even asked Anthony for advice for those new to open-source development. His advice was, "1. Start with small projects, 2. Have courage." I found his advice very convincing as the biggest difficulty I have is mostly looking at well matured open-source library/tools and I would feel anxious that I am not good enough to contribute. So pick the right-sized project and suddenly the first step should feel easier. Conclusion To be honest, as it was my first time attending a Vue and Nuxt community event, I had a lot of fun. However, what struck me the most was the incredible dedication and mindset of the core open-source contributors. The Vue and Nuxt core teams are not particularly large, and this allows them to make deliberate choices, such as deciding not to pursue Vue Native despite its potential, and to think about how their tools can benefit not just Vue but other frameworks as well. I would like to attend Vue Fes Japan again next year, and I would like to recommend anyone who is interested in such events to join as well!
アバター
はじめに こんにちは! KINTOテクノロジーズの新車サブスク開発グループに所属している丁(Jeong)です。 私たちの日々の業務は、ただコードを書くだけにとどまりません。技術の進化に伴い、マイクロサービスやサーバーレスアーキテクチャのような新しいトレンドに適応し、システムの健全性を維持することが重要になってきています。この記事では、Observability(オブザーバビリティ/可観測性)の重要性を理解し、Grafanaを活用してシステム監視とパフォーマンス最適化を行っているかを共有します。 Observabilityとは Observability、または可観測性は、システムの状態やパフォーマンスを監視し、理解する能力を指します。この概念は、システム内で発生する問題を早期に特定し、解決するために不可欠です。特にマイクロサービスやクラウドベースのアーキテクチャでは、多数の動的コンポーネントが関連し合って動作するため、システム全体を継続的に監視する必要があります。 Observabilityの主な要素には、以下の三つがあります: ログ: システムのアクティビティやエラーを記録する詳細なデータ メトリクス: システムのパフォーマンスや状態を示す定量的データ トレース: E2E[^1]のリクエストやトランザクションのパスを追跡するデータ [^1]: E2E (End-to-End):システムやプロセスが最初から最後まで完全に連携して機能すること Grafanaとは Grafanaは、Observabilityのための強力なオープンソースツールです。データの可視化、監視、および分析を行うために広く使われています。Grafanaの最大の特徴は、その柔軟性とカスタマイズ可能なダッシュボードにあります。ユーザーは、異なるデータソースからのメトリクスやログを統合し、簡単に理解できる形で表示することができます。 Grafanaの主な利点は以下の通りです: 多様なデータソースへの対応: Prometheus, Elasticsearch, InfluxDBなどと統合可能 データ分析とアラート: システムの異常を即座に検出し、アラートを通知 ダッシュボード: ユーザーのニーズに合わせてダッシュボードをカスタマイズできる Grafanaでの実践例:分かりやすさを重視 新車サブスク開発グループでは、技術的な背景が異なるメンバー全員がデータを理解しやすいよう、Grafanaのダッシュボードを活用しています。ここでは、実際ダッシュボードのパネルで使用しているPromQLのクエリ例とその機能をご紹介します。 APIリクエストの監視 ![Statパネルを使ってリクエスト数を表示](/assets/blog/authors/jeong/grafana_promql1.png =500x) 契約数 #値はサンプルです 最も基本となるクエリで、URIパターンに対するHTTPリクエストの数を追跡します。このクエリは、時間範囲内でのリクエスト数の増減を示し、トレンド分析に役立ちます。 sum( increase( http_server_requests_seconds_count{ uri=~”/foo”, method=“POST”, status=“200” }[$__range] ) ) or vector(0) sum(...): 集計関数で、結果を合計します。ここでは、条件を満たすリクエストの総数を計算しています。 increase(...): 時間範囲でのメトリックの増加量を計算します。リクエスト数の増減を捉えることができます。 vector(0): 結果がない場合に0を返します。データがなくてもダッシュボードに表示するためのものです。 1時間ごとの申込数の計測 ![Time Seriesパネルを使って1時間ごとの申込数の計測を表示](/assets/blog/authors/jeong/grafana_promql2.png =500x) 時間帯別契約数(サンプル) #夜はお眠り APIリクエストの監視 のPromQLを活用して1時間ごとにリクエスト数を計測し、時間帯による需要の変化を把握します。時間帯に応じて動的にリソースを割り当てるなどの参考データとして使います。 最も時間がかかるリクエストの特定 ![Tableパネルを使って最も時間がかかるリクエストを表示](/assets/blog/authors/jeong/grafana_promql3.png =500x) 4秒以上かかるものもあります パフォーマンス分析において、時間がかかるリクエストを特定することは、システムのボトルネックを見つける上で重要です。以下のPromQLクエリは、この目的を達成するのに役立ちます。 平均応答時間の計算 各リクエストの平均応答時間を計算します。応答時間の合計とリクエスト数をそれぞれ求め、その後これらの数値を使って割り算を行います。 sum by(application, uri, outcome, method) ( increase(http_server_requests_seconds_sum[$__range]) ) / sum by(application, uri, outcome, method) ( increase(http_server_requests_seconds_count[$__range]) ) sum by(...): ラベル(ここではapplication, uri, outcome, method)に基づいて結果をグループ化し、それぞれのグループの合計を計算します。 increase(...): 時間範囲($__range)内でのメトリックの増加量を計算します。ここでは、応答時間の合計とリクエスト数を計算しています。 リクエスト数の集計 リクエスト数を集計します。 sum by(application, uri, outcome, method) ( increase(http_server_requests_seconds_count[$__range]) ) 最も長い応答時間を持つリクエストの特定 時間範囲内で最も応答時間が長かった上位10のリクエストを特定します。 topk( 10, max( max_over_time(http_server_requests_seconds_max[$__range]) ) by(application, uri, outcome, method) ) topk(10, ...): 最も大きい値を持つ上位10の要素を返します。ここでは、最も応答時間が長かったリクエストのトップ10にしています。 max(...): 各グループの最大値を計算します。 max_over_time(...): 時間範囲内で各メトリックの最大値を計算し、ラベルで結果をグループ化します。これにより、応答時間が最も長かったリクエストを抽出できます。 クライアント側のエラーの監視 ![Tableパネルを使ってクライアント側のエラーを表示](/assets/blog/authors/jeong/grafana_promql4.png =500x) 認証トークンの期限切れ クライアント側で発生するエラーを監視し、原因を分析します。 label_replace( sum by (application, method, uri, exception, status) ( increase(http_server_requests_seconds_count{ status=~”5..|4..”, exception!~”None|FooException” }[$__range] ) ) > 0, ‘uri’, ‘$1/*$2’, ‘uri’, ‘(.*)\\/\\{.+\\}(.*)’ label_replace(...): ラベルを変更または追加を行います。 '> 0': 合計された値が0より大きい場合に結果を返します。つまり、エラーが発生している場合のみデータを表示します。 Grafana導入のメリットと実践事例 新車サブスク開発グループがGrafanaを活用し、どのようにシステム監視の質を向上させたかを詳しくお話しします。 包括的なシステムの可視化: AWS Managed Grafanaを使うことで、私たちはAWSサービス(RDS、SQS、Lambda、CloudFrontなど)からのデータと、アプリケーションのメトリクスを同時に可視化できるようになりました。これは、システム全体のパフォーマンスやボトルネックを一目で把握できることを意味し、問題解決へのアプローチが大幅に加速されました。 効率的なデータ分析とトラブルシューティング: 異なるデータソースを統合することで、問題発生時に必要な情報をすばやく取得できます。これにより、問題の根本原因を迅速に特定し、効率的な対応が可能になりました。 APIの監視: Grafanaを通じて、特定のExceptionが異常に多発していることを発見しました。調査結果、フロントエンドで想定外のAPIが呼ばれていることが判明。この問題をフロントエンドチームへ伝え、修正を行った結果、APIの効率が向上し、システムの全体的な安定性とパフォーマンスが大きく改善されました。 さいごに Observabilityは、ただシステムを見ること以上の意味を持ちます。それは、システムの健全性を保ち、問題が生じた際に迅速に対処できるようにするためのキーです。Grafanaの活用により、我々は複雑なデータを簡単に理解し、システム全体のパフォーマンスを効果的に管理できるようになりました。APIリクエストの監視からエラーの特定まで、Grafanaは多様なニーズに対応しています。 これは、SREや開発者に限らず、全てのチームメンバーが協力して取り組む価値があります。結局のところ、システムの透明性と信頼性の向上は、より良いサービスを提供し、お客様の満足度を高めるために不可欠です。 我々の経験が、皆さんの業務において新たな視点を提供し、より効率的なシステム監視とパフォーマンス最適化の手助けになれば幸いです。
アバター
こんにちは! 人事採用グループ労務総務チームのつんつんです。 私たちは社員の声を聞きながら、オフィス環境整備に取り組んでいます。本日は2023年に実施した内容をご紹介いたします。 日本橋室町オフィス 受付にTOYOTAのミニカーを設置 KINTOテクノロジーズは、クルマのサブスクリプションサービスをはじめとするモビリティサービス「KINTO」を主に開発している会社です。ある日、社員から「クルマの会社だけど、クルマに関するものが少ないよねー」と言う声がありました。「それならミニカーでも置いてみよう」と思い、設置したのがこちら↓ ↓16階受付 こちらに写っているのはTOYOTAの車種です。受付が賑やかになりました。 ごめんなさい! こちら非売品です。 ちなみに車種わかる方、いらっしゃいますか? 上段左から:GR スープラ・シエンタ・ヴォクシー・GR ヤリス・ハリアー・ランドクルーザー 下段左から:プリウス・クラウン b Z4X・カローラスポーツ・RAV4 ↓7階受付 左奥より ハリアー・ヤリス・カローラクロス・パッソ・アルファード 左奥より:カローラ・アクア・GR ヤリス・CーHR・ヤリス・ルーミー コロナ5類移行後、働き方の見直し 新型コロナウイルスが5類に移行したタイミングで、出社制限を一部緩和し、在宅勤務と出社勤務を融合したハイブリットな働き方になりました。 出社制限が緩和されたことにより、オフィスに来る社員が増えました。社員からは「個室ブースが欲しい」、「会議スペースが足りない」といったリクエストをいただきました。そこで会議室の増設と、気軽にミーティングできるスペースを2つ作りました。 会議室は2つの使われてない部屋に、スモークガラス、エアコン、会議室予約機能を追加しました。 ![](/assets/blog/authors/tsujimoto/6.jpg =500x) ![](/assets/blog/authors/tsujimoto/7.jpg =500x) 2部屋とも会議室として快適にミーティングできるようになりました。 そして気軽にミーティングできるスペースは、2種類あります。 一つ目は個室ブース。KOKUYO foreシリーズです。 ![](/assets/blog/authors/tsujimoto/8.png =500x) ![](/assets/blog/authors/tsujimoto/9.png =500x) 弊社は個室ブースの設置場所が窓際にあるため、夏でも暑くならないよう4面囲まれていないタイプを選びました。 二つ目は昇降机。KOKUYOのjionというシリーズです。 ![](/assets/blog/authors/tsujimoto/10.png =500x) 底面写真 ![](/assets/blog/authors/tsujimoto/11.png =500x) この椅子は底面が丸くなっており、「ゆらゆら」とバランスボールみたいに動きます。 神保町オフィス緑化計画 室町オフィスには休憩スペースやエントランスなど、観葉植物が点在していますが、神保町オフィスはほとんど緑がありません。 社員から「植物がなくて寂しい・・・」という声が上がり、神保町オフィスにも観葉植物を増やす「緑化計画」が動き出しました。 ↓下記の写真は執務室と会議室の緑化予想図です。 ![](/assets/blog/authors/tsujimoto/12.png =500x) ![](/assets/blog/authors/tsujimoto/13.png =500x) オフィスの環境整備はまだまだ続きます 来年は、さらにオフィスの改善に取り組む予定です。 現在決まっているのは、コミュニケーション施策の一環として休憩室を改造予定です。完成したらまたこちらのテックブログなどでお伝えできればと思っております。 日々オフィスを走り回っていますが、会社が成長している事を実感しています。 時代の変化と共にオフィスも変化する コロナ禍を経て、ハイブリットな働き方を導入する企業が多くなったと感じています。 今後は、ハイブリットな働き方に対応したオフィスや、出社したくなるオフィスなどが求められることを予想しています。特に、弊社は年齢や国籍が様々な社員で構成されているため、多様な価値観にフィットしたオフィス環境づくりをしていきたいと考えています。 最後になりましたが、ファシリティは他社がどのようにやっているかの情報がつかみにくいため、私たちの取り組みが少しでもご参考になればと思い、今回ペンを握りました。 KINTOテクノロジーズのアドベントカレンダーはまだまだ続きますので、明日以降も是非お楽しみに!
アバター
はじめに 「私は、それほど賢くはありません。ただ人より長く、ひとつのことと付き合ってきただけなのです。(筆者翻訳)」 — アルバート・アインシュタイン (物理学者) 問題によって、とても落ち込んでしまうことがあります。しかし、自分の陽気(merry)な性格が、自分の名前にぴったりで、課題の克服に役立ってきたとも思います。こんにちは、Maryです。KINTOテクノロジーズグローバル開発グループの一員です。 フロントエンドの経験はあまりなかったのですが、あえて挑戦したく、2022年1月に入社しました。様々なツールの使い方や、様々なプログラミング言語の作業方法まで、前から試してみたかったことの一環として取り組みました。時間が経つにつれて、ウェブ開発できるようになった自分に驚きました。 グローバルチームの一員として、各種スキルを習得しながら、いろいろな人々の文化、歴史、信念を理解できるようになりました。初めに担当したプロジェクトでの使用言語は英語で、馴染みのある言語だったので、通常どのようにウェブサイトが書かれるかを理解するのは簡単でした。しかし、その後あまり馴染みのないアラビア語を使うカタール向けプロジェクトにアサインされました。アラビア語は左から右に書くのです。とはいえ、私はこれをアラビア語の読み書きシステムを学ぶだけではなく、自分が作成するWebページでどのように対処するかを学ぶチャンスだととらえました。 KINTOテクノロジーズは昨年設立されたばかりで、改善の余地や学ぶべき点、克服すべき課題がたくさん残っています。課題をチャンスとしてとらえるタイプなので、私はコンフォートゾーンから飛び出る選択をいつもします。自分の成長に繋がると信じているからです。 概要 言語は、水平方向、垂直方向、右から左、左から右など、様々な方向に書くことができます。ある説によれば、このちがいは昔の人々がどんな媒体を使用して筆記作業を行っていたかと関係があるようです。 アラビア語やヘブライ語など、右から左に書く中東の言語は、古代には石に刻んで記述されていました。ほとんどの人は右利きで、ノミを使うとなれば右から左に書く方が自然に思えたのでしょう。東アジアの言語は、巻物に書かれたと言われ、左から右に書く方が簡単です。さらにインクを使う場合は、左から右に書くことでにじみが防げます。 これらはあくまで仮説に過ぎず、古くから使われていた手段の中には今や一般的ではなくなったものもありますが、新聞、本、さらにはウェブサイトなどを見れば、さまざまな媒体に記述方向が適応されているのがわかります。言語の記述方向をサポートできれば、読者はその言語を元の形式のまま読めるので内容が理解しやすくなります。 課題 エンドユーザーの立場だと、私は通常、自分で使い慣れた言語、つまり英語でウェブを閲覧しています。しかし、来日して、日本のすべてのサイトで言語が選択できるわけではないことがわかりました。そこでネットで調べてみると、ウェブサイト上での設定で、馴染みのない言語も自動翻訳する設定ができることがわかりました。 エンドユーザーのニーズには自動翻訳で十分だと思いました。しかし、機械翻訳はまだ完璧ではないため、誤訳があったり、ひどい場合には、未翻訳のまま単語が残ったりします。このような背景から、私はこの課題を解決する方法を見つけることに興味を持ちました。 運よくカタール向けのタスクをアサインされ、以下の課題に直面しました。 ブラウザ言語の検出 ブラウザ言語の変更 特定ページの言語設定 $vuetify.rtl と document.dir を使用した表示 付記:私の担当はフロントエンドですから、今あるリソースを使ってウェブサイト自体を開発することが主な仕事です。英語からアラビア語への翻訳は別のお話です。その方法については次のリンクを参照してください。 Language Localization at KINTO Technologies ソリューション 企業がグローバル化をするにあたり、「真のグローバル化をどう実現するか」は課題の一つです。サービスや製品を紹介する方法として、情報をWeb上で公開することが考えられます。しかし、これは単純な話ではないのです。読み手がたとえ世界のどこにいたとしても、伝えたいメッセージを理解されるよう気をつけないといけません。 ウェブサイトを希望する言語で表示するには、まずエンドユーザーが使用するブラウザ言語を把握する必要があります。これにより、読み手が希望する言語でサイト閲覧ができるようになります。 ブラウザ言語の検出 @nuxtjs/i18n オプションの使用 @nuxtjs /i18n の設定方法については、 こちら の手順を参照ください。 nuxt.config.js 内で i18n locale を入力し、 detectBrowserLanguage: true を設定します。 i18n: { locales: [ { code: 'en', iso: 'en', file: 'en.json' }, { code: 'ar', iso: 'ar', file: 'ar.json', dir: 'rtl' }, ], detectBrowserLanguage: true, }, code (必須) - ロケールの固有識別子 iso (SEO機能を使用する場合は必須) - SEO機能に使用される ISO コードで、 detectBrowserLanguage 機能を使用する際、ブラウザロケールのマッチングに使用される。形式は次のいずれかにする必要があります。 ISO 639-1(例: 'en' ) ISO 639-1 及び ISO 3166-1 alpha-2をハイフンで区切る(例: 'en-US' ) file - ファイル名ファイルからロケールメッセージを読み込む際、 LangDir パスを基準に決定されます dir ( v6.19.0 以降) dir プロパティが要素とコンテンツの方向を指定します。 値は 'rtl' 、 'ltr' 、 'auto' のいずれでもかまいません。 記述方向の設定にあたり、レイアウトで $nuxti18nHead メソッドを使用します。 <script> export default { head() { return this.$nuxtI18nHead() } } </script> window.navigator.language を使用する const lang = window.navigator.language console.log('language:' + lang) // language: ar ブラウザ言語の変更 上記の言語検出が機能しているかどうかをテストするために、ブラウザの言語設定を明示的に変更する場合があります。広く使用されているブラウザでの言語の変更手順を以下に示します。 (以下の手順は Windows 向けです。) Chrome パソコンで Chrome を開きます。 右上のその他 [⋮] > [設定] をクリックします。 下部にある [詳細] をクリックします。 [言語] > [言語] をクリックします。 使用したい言語の横にあるその他 [⋮] をクリックします。言語がリストにない場合は、[言語を追加] をクリックして追加します。 [Google Chrome をこの言語で表示] をクリックします。(このオプションは、Windows パソコンでのみ使用できます。) Chrome を再起動して変更を適用します。 Firefox メニューボタン ☰ をクリックし、[設定] を選択します。 [一般]パネルの言語セクションで、ドロップダウンメニューから言語を選択します。 Firefox を再起動します。 Edge [設定など] > [設定] の順に移動します。 [設定] の一覧から [言語]を選択します。 [優先する言語] の一覧に言語を追加するには、[言語を追加する] を選択します。 言語を追加したら、言語の横の[...]を選択し、[Microsoft Edge をこの言語で表示] を選択します。 特定ページの言語設定 KINTOはまだ新しい会社ですが、お客様のニーズとご要望に基づいたサービスを提供しようと最善を尽くしています。グローバル展開のためには、国ごとに十分に時間をかける必要があります。そこで気づいたのは、KINTOではそのときに必要なことにリソースを集中させ、残りはあとで行う方針を採っていたことです。つまり優先順位の問題だと気づきました。 私が携わったプロジェクトでは、まず英語とアラビア語の両方を読み込めるページを設定しました。その際は、 Get the KINTO App - Mobility for all のページが、カタール向けのランディングページの役割をしていました。そのため、サービスのリリース前にまず翻訳しておく必要がありました。 特定のページだけ言語設定することは一般的ではありません。通常、翻訳が完了すると、ウェブサイト上のページがすべて翻訳されます。ただし、こういうシナリオの場合、次のようなソリューションが適用できます。 状況 :アラビア語に設定されたブラウザを使って /sample ページに移動する場合のみ、ページをアラビア語に変換し、言語方向を右から左 (RTL)にする //アラビア語に設定されたブラウザで/sample に移動しているかどうかを確認 const toArabic = this.$route.name === 'sample' && window.navigator.language === 'ar' //ドキュメント文書の方向を設定 document.dir = toArabic ? 'rtl' : 'ltr' //Vuetify の RTL 方向を設定(true or false) this.$vuetify.rtl = toArabic //@nuxtjs/i18n を使用してロケールを設定 //nuxt.config.js で使用するコードを選択 this.$i18n.setLocale(toArabic ? 'ar' : 'en') 上のコードを見ると、ブラウザ言語が特に ar を使用している場合にのみ機能することが分かります。ブラウザが ar-XX 形式の場合はどうなるでしょうか? 言語の決定方法はいくつかありますが、その1つが split() の使用です。この方法を使えば、ロケールの設定も簡単です。 const x = "ar-XX" console.log(x.split("-")[0]) //ar this.$i18n.setLocale(x) $vuetify.rtl と document.dir を使用した RTL 表示 開発中に RTL のレイアウトが全コンポーネントで正しく機能しない理由を時間をかけて調べてみました。 document.dir = ‘rtl' を設定してみたり、 this.$vuetify.rtl = true も試しましたがうまくいかず、最終的には両方を設定することで問題を解決しました。手探りで色々と試しただけでしたが、両方を使用することでコンポーネントが正しく表示されるということが確認できました。ただそれでもこの課題の背景を知りたいという思いが残っていました。 問題の原因は次のとおりです。 $vuetify.rtl のみを使用する <template> <v-icon class="left-arrow" size="100px">mdi-chevron-double-left</v-icon> </template> ... <script> export default { mounted() { this.$vuetify.rtl = true } } </script> ... <style lang="scss" scoped> [dir='rtl'] .left-arrow { transform: rotateY(180deg); } </style> 状況 $vuetify.rtl = true を設定し、同時に [dir='rtl'] を使用する 結果 アイコンは左向きで反転していない 原因 ドキュメントを点検すると、html の方向が示されていないことがわかります。これでは [dir='rtl'] は動作しません ソリューション HTML の方向を RTL に設定するには document.dir = 'rtl' を設定する document.dir のみを使用する <template> <v-expansion-panels> <v-expansion-panel v-for="(item,i) in 5" :key="i" > <v-expansion-panel-header> Item </v-expansion-panel-header> <v-expansion-panel-content> Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. </v-expansion-panel-content> </v-expansion-panel> </v-expansion-panels> </template> ... <script> export default { mounted() { document.dir = 'rtl' } } </script> 状況 document.dir = 'rtl' を設定し、vuetify コンポーネントを使用 結果 一部のコンポーネントが正しく表示されない 原因 RTL がブートストラップ中に正しく動作しない ソリューション this.$vuetify.rtl = true を設定する。画像を見ると、矢印は左側に移動しています。 <script> export default { mounted() { this.$vuetify.rtl = true document.dir = 'rtl' } } </script> まとめ ほとんどのプログラミングフレームワークが複数言語をサポートしていますが、正しく動作させるためには微調整が必要です。 上で書いたような課題に直面しましたが、予定通りにタスクを終えることができて良かったです。ぜひご覧ください! https://www.kinto-mobility.com/app/ KINTOテクノロジーズのサービスを導入する国が増えるにつれて、KINTOのその他プロジェクトでも翻訳が必要になるでしょう。そういった機能を実装する基礎ができたことをうれしく思います。 思い フロントエンドの役割は、息をのむようなデザインやレイアウトでウェブサイトを作り上げることだけではありません。ユーザーエクスペリエンスについても考える必要があるのです。デザインは優れているのに、ターゲットのお客様に満足してもらえない状況を想像してみてください。私たちは本当にお客様のニーズや要望を満たせていると言えるでしょうか? [Desire Path] (/assets/blog/authors/hundanmz/desirePath.png =750x) 参考画像: https://images.squarespace-cdn.com/content/v1/5c6afc627eb88c46e4f41468/1563183013744-RWOU1N19RO77MT0EAH3M/IMG_0873.jpeg 参考 Overview of Language Directions Why Do We Read English From Left To Right? Right to Left Languages | Why are they written this way | Pangeanic Omniglot index by writing direction @nuxtjs/i18n Introduction window.navigator.language Navigator.language - Web APIs | MDN Updating Browser Language Settings Change Chrome languages & translate webpages - Computer - Google Chrome Help Use Microsoft Edge in another language - Microsoft Support Use Firefox in another language | Firefox Help Vuetify Vuetify — A Material Design Framework for Vue.js
アバター
こんにちは! KINTO ONE開発部 新車サブスク開発グループ バックエンドチーム所属の朝日です。 弊グループではKINTO ONEサービスを提供するためのシステムの運用開発をしています。 2023年8月に大規模なシステムリニューアルのリリースを行いました。 長期間にわたるシステム開発の中でチーム一丸となり切磋琢磨・試行錯誤を繰り返してきたわけですが、その中でチームで導入した プルリクエスト(以下PR)レビューを促進するGitHub Actionsのワークフロー がとてもお気に入りなので紹介します。 処理詳細は後述 後回しにされてしまうコードレビュー みなさん、コーディングとコードレビュー、どちらがお好きですか。私は圧倒的にコーディングです。 コードレビューがシステム品質を保つ上でとでも重要な工程ということは重々承知しているものの、タスクの期限が迫っていると自身のコーディングを優先させてしまい、ついついレビューを後回しにしてしまうこともあるのではないでしょうか。 システムリニューアルプロジェクトで実装に着手し始めた当初も、コーディングは終わっているのにレビューがされずOpen状態のPRが多く残ってしまうことがありました。 その結果、以下のような事象が多く起こりチームの課題となっていました。 タスクを完了にすることができずチームとしての進捗状況が把握しにくい 依存関係のあるタスクに着手できない or 影響を与えてしまう mergeの際にコンフリクトが発生しやすくなる これらの事象を防ぐために、今回紹介するPRレビュー促進するGitHub Actionsのワークフローを導入しました。 GitHub Actionsとは 弊社ではソース管理ツールにGitHubを使用しています。 GitHub ActionsとはGitHub上で利用できる自動化ツールで、自動化したい処理をワークフローに記述、リポジトリ内に配置しておくことで任意のタイミングで処理を実行することができます。 GitHub上の機能ということもあり、GitHubのイベントと親和性が高い特徴があります。 @ card 自動レビュワー設定ワークフロー 本題です。 今回紹介するのはPRを作成したタイミングでレビュワーをランダムで自動設定、Slack通知するGitHub Actionsのワークフローです。 ついでにPR作成者の自動アサインとPR上にコメントも残します。 ワークフロー処理詳細 PRが作成されたタイミングでワークフローが実行されます。 ワークフローからAWS Lambda(以下Lambda)[^1]にリクエストします。 Lambda処理の中にチームのメンバー情報を事前に定義しておきます。その際に"仕様担当"と"技術担当"の属性を持たせています。自分以外のチームメンバーの中から仕様レビュー担当と技術レビュー担当のレビュワーを一人ずつランダムで選択します。計二人を選択しているのはチームのルール(PRマージには二人以上のレビュー必須)にも由来しています。 Lambdaから取得した二名のメンバー情報をPRのReviewersに、自身の情報をAssigneesに自動設定、レビュワーにメンション付きのコメントを残します。 設定されたレビュワーに指定したSlackチャンネルでお知らせします。 Slack Webhookに連携 [^1]: AWS Lambdaは関数として定義した処理をサーバーレスで実行できるAWSサービスの一つです。 おすすめポイント レビュワー設定を最小人数に絞る レビュワーをメンバー全員でなく最小人数を絞ることで「自分がレビューしなくても誰かが見てくれるだろう…」という他力本願を排除し、レビューの後回しを抑制します。 チームメンバーに"仕様担当"と"技術担当"の属性を持たせる レビュワー人数を絞るとアサインされたメンバーの得意不得意によってレビューの品質が左右されてしまうことが懸念されました。 そのため、メンバーそれぞれに"仕様担当"と"技術担当"の属性を事前に設定し、ドメイン知識を得意としているメンバーには仕様中心に、技術が得意なメンバーや新規参画したばかりで仕様を勉強中のメンバーには技術面を中心に見てもらうようにしました。 リポジトリを跨いだチームメンバー情報の共有 複数のリポジトリで同じ処理を実行するには、各リポジトリに同じワークフローを配置する必要があります。 メンバー情報の定義をワークフロー内に記述した場合、メンバー新規参画などでメンバー情報を更新したい際に設定している全てのワークフローを修正しなければなりません。 そのためレビュワーランダム選択処理をLambdaで実装し、各リポジトリから参照できるように共通化にしました。 メンバー情報を更新の際はLambda処理の修正のみで完結するようにしています。 Slack通知で即確認可能 ワークフロー内でSlack通知処理を行うことによって、設定されたレビュワーに即時に確認してもらえます。 GitHubもメールでの通知をしてくれますが、利用するコミュニケーションツールを統一することによって通知の見落としを防ぐ効果があります。 自動化による"手間"の削減 GitHub ActionsはCICD自動化ツールとして注目されることが多い機能です。しかしそれ以外にも、今回のワークフローのようなちょっとした手間を自動化することもできます。 元々はPRレビューの促進を目的としたツールでしたが、実際使ってみることにより自動化のメリットを実感することができました。 もし今回のワークフローの処理を自身で行うとなると、チームメンバーの状態によってレビューの依頼に忖度してしまったり、別途Slackでメッセージを送る必要があったりします。 それぞれ小さな手間ではありますが、私のチームでは2年以上今回のワークフローを利用しており、"削減した手間"は相当数に上ります。今ではなくてはならないツールです。 紹介したワークフローのサンプルコード 今回紹介したワークフローのサンプル(Lambda不要ver.)を紹介します。 カスタマイズして使ってみてください。 name: "Auto Assigning Reviewers" on: pull_request: types: [opened, ready_for_review, reopened] jobs: assigning-reviwers: runs-on: ubuntu-latest if: | github.event.pull_request.draft == false steps: - uses: actions/checkout@v3 - uses: actions/setup-node@v3 with: node-version: 16 - run: npm install @slack/webhook - uses: actions/github-script@v6 with: github-token: ${{ secrets.GITHUB_TOKEN }} script: | const { IncomingWebhook } = require('@slack/webhook') // メンバー情報を定義 const MEMBER_INFO = [ { slackName: "@仕様把握子", speciality: 'spec' ,slackMemberId: "XXXXXX", githubName: "XXXXXX"}, { slackName: "@仕様得夫", speciality: 'spec' ,slackMemberId: "XXXXXX", githubName: "XXXXXX"}, { slackName: "@仕様得得", speciality: 'spec' ,slackMemberId: "XXXXXX", githubName: "XXXXXX"}, { slackName: "@技術剛", speciality: 'skill' ,slackMemberId: "XXXXXX", githubName: "XXXXXX"}, { slackName: "@技術任路", speciality: 'skill' ,slackMemberId: "XXXXXX", githubName: "XXXXXX"}, { slackName: "@新参者", speciality: 'skill' ,slackMemberId: "XXXXXX", githubName: "XXXXXX"}, ] // PR作成者を取得 const user = context.payload.sender.login; const author = MEMBER_INFO.find(member => member.githubName === user); // レビュワーをランダムで二人選出 const skills = MEMBER_INFO .filter(member => member.githubName !== author.githubName) .filter(member => member.speciality === 'skill'); const specs = MEMBER_INFO .filter(member => member.githubName !== author.githubName) .filter(member => member.speciality === 'spec'); let getRandomNumber = (min,max) => Math.floor(Math.random() * (max - min + 1)) + min; const chosenSkillMember = skills[getRandomNumber(0, skills.length - 1)]; const chosenSpecMember = specs[getRandomNumber(0, specs.length - 1)]; const issue_number = context.issue.number const pull_number = context.payload.pull_request.number const { repo, owner } = context.repo // PR Reviewersの設定 await github.rest.pulls.requestReviewers({ owner, repo, pull_number, reviewers: [chosenSkillMember.githubName, chosenSpecMember.githubName] }); // PR Assigneesの設定 github.rest.issues.addAssignees({ owner, repo, issue_number, assignees: [user] }); // Slack通知 const title = context.payload.pull_request.title const html_url = context.payload.pull_request._links.html.href const message = `オッス! <@${chosenSkillMember.slackMemberId}> <@${chosenSpecMember.slackMemberId}> \n <@${author.slackId}> の新しいPRのレビュアーに選定されました 🥳 \n <${html_url}|${title}>` // 通知したいSlackチャンネルのwebhook URLを指定 const webhook = new IncomingWebhook('https://hooks.slack.com/xxxxxxxxx') await webhook.send({ text: message, }) // PRにコメント await github.rest.issues.createComment({ owner, repo, issue_number, body: `@${chosenSkillMember.githubName},@${chosenSpecMember.githubName} レビュアーに選定されました 🚀` }) 最後に 今回我が物顔で紹介しましたが、いつも便利ツールを導入してくれる同じチームの丁さん、素敵なツールとアイデアをありがとうございます。 スーパー丁さんの記事は明日公開なのでお楽しみに。 みなさま、GitHub Actionsで素敵な自動化ライフをお過ごしください!
アバター
はじめに KINTOテクノロジーズで、 モビリティマーケット の開発・運用兼テックブログの運用を担当しているリナ( @chimrindayo )です。普段はフロントエンジニアとして、主にNext.jsを用いて実装しています。 最近は、おでんが美味しい季節がやってきてワクワクしています🍢 今年はトマトおでんが食べたいな・・・🤤 さて、KINTOテクノロジーズでは、社外のイベントへの登壇やテックブログの執筆など「習得した知識・スキルのアウトプット」を全社でサポートしています。 今回はテックブログの記事がリリースされるまでにどんなことをやっているか、公開されるまでの過程と各工程でアウトプットを推進するための取り組みをご紹介します! テックブログ運用プロジェクト まずはじめに、KINTOテクノロジーズの「テックブログ運用プロジェクト」というチームが存在します。 テックブログ運用プロジェクトではテックブログの運用を始めとして、社員の知識のインプット・アウトプットを推進することを目的としています。 所属するメンバーは8名で、全員が兼任者です。 他プロジェクトでPdMやエンジニアとして活動する傍ら、楽しくアウトプットできる方法を日々模索しています! https://www.wantedly.com/companies/company_7864825/post_articles/510568 これからご紹介する取り組みは、テックブログ運用プロジェクトに所属するメンバーがアウトプットを推進するために実施している取り組みの一部です! テックブログの公開フロー テックブログの公開フローは、大きく3つのフェーズに分かれています。 1. 執筆 1つ目は、記事を執筆するフェーズです。記事のネタ探しおよび執筆テーマの決定、プロットの作成から記事に落とし込んで執筆するまでが執筆フェーズに該当します。 2. レビュー 2つ目は、執筆された記事のレビューフェーズです。KINTOテクノロジーズでは3段階のレビューを行い、誤字脱字チェックや記事の内容を担保しています。 3. リリース 3つ目は、記事をリリースするフェーズです。リリースフェーズでは、記事の翻訳やGitHubを利用したリリース作業を実施しています。 では、各フェーズでどんなサポートをしているのか、実際に取り組んでいる内容をご紹介します! テックブログの記事が公開されるまで まずはじめに、執筆フェーズでのテックブログ運営チームの取り組みをご紹介します。 相談窓口 アドベントカレンダーの執筆期間は、毎日1時間テックブログチームのメンバーがSlackチャンネルのハドルミーティングで待機し、執筆者が気軽に相談できる体制を作りました。 執筆する上での悩み・マークダウンの書き方など、ちょっとした悩みを解消できる場として、多い時は1日に5~6人ほどが利用しています。 実際に「他の人にも相談窓口勧めておきましたー!」などの声があり、思ったより好評だったと思います✨ 執筆者インタビュー 「ネタが思い浮かばないけど、記事を書いてみたい!」「ネタはあるけど、記事にうまくまとめられるか不安」と考えている執筆者向けに、テックブログの運営メンバーがインタビューする時間を設けています。 インタビューは30分ほど行い、入社してから現在までどんな業務に取り組んできたのか、また各業務における課題や問題の解決方法などを中心にヒアリングしています。 そして、ヒアリング結果をもとに記事のプロット作成までをインタビューの時間内で行います。 インタビューを通して「記事書くの不安だな・・・」と思っている人に「思ったより書けそう!」と記事を書くことに対するハードルを下げること、そして日々取り組んでいる業務にいかに価値があるか再認識してもらうことで、執筆者の魅力を最大限に引き出すことを目的としています。 また、記事の内容は業務で携わった内容に絞っています。 そして、技術に携わる人全員がアウトプットできるよう、いわゆる「技術」だけでなく、最大限に技術を活かすためのマネジメントやオフィスの環境など、技術を支えるスキルをアウトプットすることも推進しています。 レビュー 記事を執筆した後は、異なる観点で3段階のレビューを行います。 3段階のレビューを通して記事のクオリティを担保し、読み手にわかりやすい記事を作成することを目的としています。またレビューをする上で「執筆者へ感謝すること」を大切にしています。 コンテンツレビュー まずはじめに、記事の内容の正誤をチェックするためのコンテンツレビューを実施しています。 主に以下の観点で執筆者のチームメンバーやマネージャーがレビューします。 多くの記事は2~3名以上のレビュアーがつき、より読みやすい表現の提案や記事のGood Pointなどもフィードバックをしています! レビュー観点 ・ 専門家として内容の正誤を確認 ・ 秘匿情報の有無を確認 テックブログチームレビュー 次にテックブログ運用チームによるレビューです。 テックブログチームでは、以下の観点でレビューを行っています。 執筆者の文調を大切にしつつ、読みやすい文章・記事構成の提案、誤字脱字や「てにおは」をチェックします。 また読み手の立場として「この情報も知りたい!」と内容の追加を提案する場合もあります。 レビュー観点 ・ 誤字脱字 ・ 著作権 私自身テックブログチームとしてレビューすることがほとんどなのですが、レビューを通じて「JIRAでGitHubのデプロイ履歴が追えるのか・・・!」「このレビューガイドラインは自分のプロジェクトにも導入したい」など、新たな学びや発見があっておもしろいです💡 CIOレビュー さいごに CIO 景山さん によるレビューです。 すべての記事を景山さんがレビューしており、景山さんの承認をもって記事のリリース準備が完了します🎉 私自身このレビュー工程があるおかげで、執筆者が自信をもって記事をリリースできるようになっていると考えています。 リリース すべてのレビューが終了した後は、リリースに向けて最終調整をします。 今回は特に力を入れている「記事の翻訳」についてご紹介します! 記事の翻訳 KINTOテクノロジーズに在籍する社員のうち約25%が外国籍の社員です(2023年11月時点) したがって「母国語で執筆したい」「日本語で書くのが不安」と考えているメンバーもいます。 そういったメンバーのニーズに応えるためにすべての記事を日⇔英翻訳し、執筆者が日本語 or 英語どちらの言語で執筆するか選択できるようにしています🔤 翻訳のベースはLSP (Language Service Provider)、外部協力会社を使用し、最終的にLQAの作業を内部で実施しています。 LQAとは『Linguistic Quality Assurance』の略で「言語品質保証」のことを指しています。 外部リソースだけで構成された文章では、どうしても不適切な言い回しになってしまったり、執筆者の意図とは異なる文章になってしまうことがあります。 こうした不自然な表現やスペルミスをLQAの段階でチェックしています。 (参考: 外国籍社員の活躍 ) さいごに 今回は、テックブログが公開されるまでに実施しているアウトプット推進の取り組みをご紹介しました。引き続き、KINTOテクノロジーズのメンバーがアウトプットがしやすい環境を作るために試行錯誤しながら改善してきたいと思います! また、弊社のテックブログによるアウトプットがみなさまのお役に立てれば幸いです。 テックブログの運用や技術広報について、みなさまと意見交換ができれば嬉しいです! ご意見は X までご連絡ください🕊 https://twitter.com/KintoTech_Dev
アバター
こんにちは、Shweta Oza です。 2022年4月に、KINTOグローバル開発グループに加わりました。アプリケーション開発担当で、最近は DevOps に興味を感じています。現在の所属はクーポンチームです。 私たちのチームは、世界中のお客様/取引先向けにクーポン API の開発・管理を行っています。各種機能の開発と展開を継続的に進めています。どのプロジェクトにおいても、データのバックアップやロールバックを含むデータベースのメンテナンスは重要です。メンテナンスしておけば、新バージョンのリリース時に問題が発生しても、変更を安全にロールバックし、バックアップを復元することができます。 オープンソースのデータベース移行ツールとして、当社では Flyway などのソリューションを使用しています。Flywayは、コンフィギュレーションよりもシンプルさと規則性を重視しています。基本のコマンドは7つあります:Migrate、Clean、Info、Validate、Undo、Baseline、Repair 以下に、スクリプトを使用してデータベースを手動でバックアップする方法について、いくつかの概念と簡略化した手順を示します。 概要 クーポンシステムは、KINTO のサービスや提携先のサービスで利用できるクーポンを簡単に発行・管理できるツールです。クーポンシステムでは、新しい機能を継続的に開発することで改善やアップグレードを行っています変更が発生するたびに、クーポンシステムで使用するデータベースを管理する必要があります。これはソフトウェアプロジェクトにおいて非常に重要な作業です。私たちの場合は MySQL を使用しています。 クーポンシステムの新しいリリースがある場合は、毎回次の手順を踏んでいます: 機能を開発する。 ローカルでテストしてから AWS テスト環境でテストを行う。 リリース前に DB のバックアップを取る。 新機能をリリースする。 問題があれば、リリース前のバージョンにロールバックする。 これらを実施するため、バックアップとロールバックのスクリプトを用意し、CLI でのインストラクション数を減らしています。これで時間の節約にもなりますし、バックアップやロールバックで構造の一貫性も維持できます。 基本を知る データベースのバックアップとは? データベースのバックアップとは、サーバーに保存したデータのコピーのことです。 データベースのバックアップは何のため? バックアップを取るのは、予期せぬデータ損失を防ぐためです。 障害が発生した場合、元データが失われたり破損したりしても、バックアップがあれば簡単にデータを復元することができます。 データベースのバックアップにはどんなタイプがあるの? 物理バックアップ ^1 物理バックアップは、物理的なファイルのバックアップで、データベースの保存と復元に使用されます。これらには、様々なデータファイル、コントロールファイル、アーカイブ REDO ログなどが含まれます。通常、物理バックアップのデータは、クラウド、オフラインストレージ、磁気テープ、ディスクに保存されます。 物理バックアップには方法が2つあります: オペレーティング・システム・ユーティリティ リカバリマネージャー メリット: データの完全管理。 保管コストが低い。 バックアップデータの迅速な取得。 デメリット: どんなデバイスに保存してもデータは破損する可能性があり、データ復旧が困難になることがある。 自然災害でデータが破壊される可能性がある。 記憶装置自体の紛失。 論理バックアップ ^2 論理バックアップには、データベースから取得した論理データが含まれます。ビュー、プロシージャ、ファンクション、テーブルなどです。 これは、ユーザーがデータベースのコピーを復元したり、別の場所に転送したりする場合に便利です。 論理バックアップは、データ損失の防止では物理バックアップほど安全ではありません。構造の詳細を提供するだけです。そのため毎週、完全な論理バックアップを実行しなければなりません。 論理バックアップは物理バックアップの補助として使用されます。 メリット: データベース全体を以前のインスタンスに復元する必要がある場合に便利。 より複雑で、細かなリカバリ機能を備えている。 デメリット: 特別なコンポーネントのリカバリには利用できない可能性がある。 物理バックアップに比べて安全性が低い。 構造の詳細を提供するのみ 論理データベースのバックアップについてローカル及び AWS CLI の両方で見ていきます。DB でのデータ操作は、開発者の観点でも重要なステップだからです。 AWS CLI を使用して Amazon RDS(リレーショナルデータベースサービス)を管理する Amazon RDS [^3] [^3]: https://aws.amazon.com/rds/ Amazon RDS はサーバーを用意することなく、クラウド上の RDB を安全に利用できるサービスです。Amazon RDSは、オープンソースで広く使われているMySQLやMariaDB, PostgreSQLをはじめ、Oracle Database, microsoft SQL serverなどもサポートしています。支払いは使った分だけです。 Amazon CLI [^4] [^4]: https://aws.amazon.com/cli/ AWS CLI は、AWS サービスを管理するための統合ツールで、Linux や macOS ではターミナルから、Windows ではコマンドプロンプトから実行します。 基本的には専用ツールからコマンド入力で実行しますが、スクリプトに記述することで処理の自動化もできます。 AWS CLI から Amazon RDS を管理する場合は、コマンドを実行してもすぐ反映されるとは限らない点に注意が必要です。 というのも、Amazon RDS には複数の障害対策が施されており、設定変更がすべての対策に反映されるまでに時間がかかるからです。 同様に簡単なスクリプトを各種環境で書くことで、バックアップとロールバックのタスクを実行することができるでしょう。 開発者レベルで CLI から DB のバックアップやロールバックを行うにはどうすればいい? SQL フォーマットで mysqldump の使用 mysqldump とは [^5] [^5]: https://dev.mysql.com/doc/refman/8.0/en/mysqldump.html mysqldump クライアントは、 Igor Romanenko が最初に書いた論理バックアッププログラムです。データベースは通常、バックアップや別のデータベースサーバー(MariaDB や MySQL とは限りません)に転送するための SQL ステートメントのリスト(「SQL ダンプ」)の形をしています。ダンプには通常、テーブル作成やテーブル入力を行うための SQL ステートメントが含まれています。 ダンプファイルの使用 ダンプファイル [^6] とは [^6]: https://dev.mysql.com/doc/refman/8.0/en/mysqldump.html ダンプファイルは、リリース後のアップグレードの際にも便利です。アップグレードの際に、古いリリースを使用しているデータベースをダンプし、新しいリリースでロードすることができます。 ハイライト データありの状態(つまり DDL 及び DML の両方)で DB ダンプを取得する場合 mysqldump -u root -p DB_Name > backup.sql # AWS CLI mysqldump -u $user -p$password -h $rds_endpoint > backup.dump # To avoid warnings like below you can use the following options and add the login credentials in a CNF file. # mysqldump: [Warning] Using a password on the command line interface can be insecure. # --set-gtid-purged=OFF # Warning:A partial dump from a server that has GTIDs will by default include the GTIDs of all transactions, even those that changed suppressed parts of the database.If you don't want to restore GTIDs, pass --set-gtid-purged=OFF.To make a complete dump, pass --all-databases --triggers --routines --events. # --defaults-extra-file=path/to/extra.cnf # cat << EOF > path/to/extra.cnf # [client] # user=${MYSQL_USER} # password=${MYSQL_PASSWORD} # host=${RDS_ENDPOINT} # EOF mysqldump --defaults-extra-file=path/to/extra.cnf --skip-column-statistics --single-transaction --set-gtid-purged=OFF --databases $DB > backup.dump データなし(つまり DDL のみ)で DB ダンプを取る場合 mysqldump -d -u root -p DB_Name > backup.sql # AWS CLI mysqldump -d -u $user -p$password -h $rds_endpoint > backup.dump # Below are some useful options to consider # --no-data, -d # Do not write any table row information (that is, do not dump table contents).This is useful if you want to dump only the CREATE TABLE statement for the table.For example, when you want to create an empty copy of the table by loading the dump file. # --defaults-extra-file=file_name # Read this option file after the global option file but (on Unix) before the user option file.If the file does not exist or is otherwise inaccessible, an error occurs.If file_name is not an absolute path name, it is interpreted relative to the current directory. # The --single-transaction option and the --lock-tables option are mutually exclusive because LOCK TABLES causes any pending transactions to be committed implicitly. mysqldump --no-data --defaults-extra-file=path/to/extra.cnf --skip-column-statistics --single-transaction --set-gtid-purged=OFF --databases $DB > backup.dump 以前のバージョンにロールバックするには mysql -u root -p DB_Name < backup.dump # AWS CLI mysql -u $user -p$password -h $rds_endpoint < backup.dump バックアップ用スクリプト ディレクトリを使用する前にスクリプトを編集する必要があるもの: pathToParameterStoreKeyValueMYSQL_USER pathToParameterStoreKeyValueMYSQL_PASSWORD pathToParameterStoreKeyValueRDS_ENDPOINT DB_Name ######################################################################### ######################################################################### ##### ##### Description:Shell Script to take backup of existing data AWS ##### 1.Choose the env and switch the db connection ##### 2.Create backup directory with today's date ##### 3.Create dump file to backup folder with current db ##### 4.Check dump file size ##### ######################################################################### ######################################################################### read -p "Enter Your Env(env): " x echo "Welcome to ${x} Env!" # MySQL server credentials MYSQL_USER=$(aws ssm get-parameter --with-decryption --name /${x}/pathToParameterStoreKeyValueMYSQL_USER | jq -r .Parameter.Value) MYSQL_PASSWORD=$(aws ssm get-parameter --with-decryption --name /${x}/pathToParameterStoreKeyValueMYSQL_PASSWORD | jq -r .Parameter.Value) RDS_ENDPOINT=$(aws ssm get-parameter --with-decryption --name /${x}/pathToParameterStoreKeyValueRDS_ENDPOINT | jq -r .Parameter.Value) # Create a configuration file to maintain MySQL login details cat << EOF > mysql-dbaccess.cnf [client] user=$MYSQL_USER password=$MYSQL_PASSWORD host=$RDS_ENDPOINT EOF # Set the folder name with date format (example:2022-08-15) DATE_FORMAT=$(date +"%Y-%m-%d") TIMESTAMP=$(date +%H%M%s) # Path to local backup directory BACKUP_DIR="tmp/release/backup/${x}/dbbackup/${DATE_FORMAT}" # Use database's names DB="DB_Name" echo "########################################DATABASE########################################" echo "Using Database (DB_Name)" echo "########################################DATABASE########################################" # Create backup directory with today's date mkdir -p ${BACKUP_DIR} FILENAME_PREFIX="backup_${x}_DDL_DML_${TIMESTAMP}_" FILENAME_POSTFIX=".dump" read -p "Enter version eg: v0.0.1: " d FILENAME=$FILENAME_PREFIX${d}$FILENAME_POSTFIX echo "########################################FILEPATH########################################" echo "Created directory" ${BACKUP_DIR} echo "File will be saved as ${FILENAME} " mysqldump --defaults-extra-file=mysql-dbaccess.cnf --single-transaction --set-gtid-purged=OFF --databases $DB > ${BACKUP_DIR}/${FILENAME} echo "File saved at ${BACKUP_DIR}/${FILENAME}" echo "########################################FILESPATH########################################" # check File size file=${BACKUP_DIR}/${FILENAME} filesize=$(ls -lh $file ) echo "########################################FILESIZE########################################" echo "$file has a size of $filesize" echo "########################################FILESIZE########################################" # Remove the file after executing shell rm mysql-dbaccess.cnf バックアップの手順 AWS CLI にシェルスクリプトを配置する AWS にログイン AWSマネジメントコンソール → AWSシステムマネージャー→ セッションマネージャー →セッション開始の順にクリックする バックアップを取りたい env を入力する 例: {env}-{project_name}-{maintenance_server_name} セッション開始 dbbackupDDL_DML.sh ファイルが存在するかチェックする ファイルが存在しない場合は、AWS CLI に dbbackupDDL_DML.sh ファイルを配置する ファイルを実行する sh-4.2$ ls dbbackupDDL_DML.sh mysql.sh tmp sh-4.2$ sh dbbackupDDL_DML.sh Enter Your Env(env): env Welcome to env Env! ########################################DATABASE######################################## Using Database (db_name) ########################################DATABASE######################################## Enter version eg: v0.0.1: v0.0.1 ########################################FILEPATH######################################## Created directory tmp/release/backup/env/dbbackup/2022-08-12 File will be saved as backup_env_DDL_DML_06311660285870_v0.0.1.dump File saved at tmp/release/backup/env/dbbackup/2022-08-12/backup_env_DDL_DML_06311660285870_v0.0.1.dump ########################################FILESPATH######################################## #######################################FILESIZE######################################## tmp/release/backup/env/dbbackup/2022-08-12/backup_env_DDL_DML_06311660285870_v0.0.1.dump has a size of -rw-r--r-- 1 ssm-user ssm-user 1.7M Aug 12 06:31 tmp/release/backup/env/dbbackup/2022-08-12/backup_env_DDL_DML_06311660285870_v0.0.1.dump ########################################FILESIZE######################################## sh-4.2$ ダンプファイルの内容を確認するには sh-4.2$ less tmp/release/backup/env/dbbackup/2022-08-12/backup_env_DDL_DML_06311660285870_v0.0.1.dump sh-4.2$ バックアップは tmp/release/backup/env/dbbackup/{currentDate}/{FileNameWithTimestamp&Version} のフォルダに入ります。 同じ日に複数回、同一フォルダへダンプしたい場合、タイムスタンプ付きのファイルを準備します。 sh-4.2$ cd tmp/release/backup/env/dbbackup/ sh-4.2$ ls 2022-08-09 2022-08-10 2022-08-12 sh-4.2$ cd tmp/release/backup/env/dbbackup/2022-08-12/ sh-4.2$ ls backup_env_DDL_DML_06311660285870_v0.0.1.dump backup_env_DDL_DML_06371660286257_v0.0.1.dump sh-4.2$ ロールバック用スクリプト ディレクトリを使用する前にスクリプトを編集する必要があるもの: pathToParameterStoreKeyValueMYSQL_USER pathToParameterStoreKeyValueMYSQL_PASSWORD pathToParameterStoreKeyValueRDS_ENDPOINT DB_Name ######################################################################### ######################################################################### ##### ##### Description:Shell Script to rollback to target SQL ##### 1.Choose the env and switch the db connection ##### 2.Create rollback directory with today's date ##### 3.Choose and input the backup file ##### 4.Input the version of dump file ##### 5.Copy backup dump file to ROLLBACK folder ##### 6.Create dump file to ROLLBACK folder with current db ##### 7.Rollback db with backup dump file ##### 8.Comparison.... ##### ######################################################################### ######################################################################### read -p "Enter Your Env(env): " x echo "Welcome to ${x} Env!" # MySQL server credentials MYSQL_USER=$(aws ssm get-parameter --with-decryption --name /${x}/pathToParameterStoreKeyValueMYSQL_USER | jq -r .Parameter.Value) MYSQL_PASSWORD=$(aws ssm get-parameter --with-decryption --name /${x}/pathToParameterStoreKeyValueMYSQL_PASSWORD | jq -r .Parameter.Value) RDS_ENDPOINT=$(aws ssm get-parameter --with-decryption --name /${x}/pathToParameterStoreKeyValueRDS_ENDPOINT | jq -r .Parameter.Value) # Set the folder name with date format(eg:2022-08-15) DATE_FORMAT=$(date +"%Y-%m-%d") TIMESTAMP=$(date +%H%M%s) # Path to local rollback directory history ROLLBACK_DIR="tmp/release/rollback/${x}/dbRollback/${DATE_FORMAT}" # Use database's names DB="DB_Name" echo "########################################DATABASE########################################" echo "Using Database (DB_Name)" echo "########################################DATABASE########################################" # Create rollback directory with today's date mkdir -p ${ROLLBACK_DIR} read -p "Enter full dumpFile Path to which you want to rollback: " df echo "dumpFile ${df} selected!" FILENAME_ROLLBACK_PREFIX="rollback_${x}_DDL_DML_${TIMESTAMP}_" FILENAME_BACKUP_PREFIX="backup_${x}_DDL_DML_${TIMESTAMP}_" FILENAME_POSTFIX=".dump" read -p "Enter version eg: v0.0.1: " d FILENAME_ROLLBACK=FILENAME_ROLLBACK_PREFIX${d}$FILENAME_POSTFIX FILENAME_BACKUP=FILENAME_BACKUP_PREFIX${d}$FILENAME_POSTFIX echo "########################################FILEPATH########################################" echo "Created directory" ${ROLLBACK_DIR} # copy dump file to backup folder cp ${df} ${ROLLBACK_DIR}/${FILENAME_ROLLBACK} ROLLBACK_FILE=${ROLLBACK_DIR}/${FILENAME_ROLLBACK} BEFORE_ROLLBACK_DUMP=${ROLLBACK_DIR}/"BeforeRollback_${FILENAME_BACKUP}" AFTER_ROLLBACK_DUMP=${ROLLBACK_DIR}/"AfterRollback_${FILENAME_BACKUP}" mysqldump -u $MYSQL_USER -p$MYSQL_PASSWORD -h $RDS_ENDPOINT --databases $DB > ${BEFORE_ROLLBACK_DUMP} echo "Dump Before Rollback ${BEFORE_ROLLBACK_DUMP}" echo "Rollback to DDL_DML of sql file located at ${ROLLBACK_FILE} " mysql -u $MYSQL_USER -p$MYSQL_PASSWORD -h $RDS_ENDPOINT --databases $DB < ${ROLLBACK_FILE} echo "Rollback successfully done with ${ROLLBACK_FILE}" mysqldump -u $MYSQL_USER -p$MYSQL_PASSWORD -h $RDS_ENDPOINT --databases $DB > ${AFTER_ROLLBACK_DUMP} echo "Dump After Rollback ${AFTER_ROLLBACK_DUMP}" echo "########################################FILESPATH########################################" # check File size before Rollback fileBeforeRollback=${ROLLBACK_DIR}/${BEFORE_ROLLBACK_DUMP} filesizeBeforeRollback=$(ls -lh fileBeforeRollback ) echo "########################################FILESIZE BEFORE ROLLBACK########################################" echo "$fileBeforeRollback has a size of $filesizeBeforeRollback" echo "########################################FILESIZE BEFORE ROLLBACK########################################" # check File size after Rollback fileAfterRollback=${ROLLBACK_DIR}/${AFTER_ROLLBACK_DUMP} filesizeAfterRollback=$(ls -lh fileAfterRollback ) echo "########################################FILESIZE AFTER ROLLBACK########################################" echo "$fileAfterRollback has a size of $filesizeAfterRollback" echo "########################################FILESIZE AFTER ROLLBACK########################################" Footer ロールバックの手順 AWS CLI にシェルスクリプトを配置する AWS にログイン AWSマネジメントコンソール → AWSシステムマネージャー→ セッションマネージャー →セッション開始の順にクリックする ロールバックしたい env を入力する 例: {env}-{project_name}-{maintenance_server_name} セッション開始 dbRollbackDDL_DML.sh ファイルが存在するかチェックする ファイルが存在しない場合は、AWS CLI に dbRollbackDDL_DML.sh ファイルを配置する ファイルを実行する sh-4.2$ ls dbRollbackDDL_DML.sh mysql.sh tmp sh-4.2$ sh dbRollbackDDL_DML.sh Enter Your Env: env Welcome to env Env! ########################################DATABASE######################################## Using Database (DB_Name) ########################################DATABASE######################################## Enter full dumpFile Path to which you want to rollback: tmp/release/backup/env/dbbackup/2022-08-12 Enter version eg: v0.0.1: v0.0.1 ########################################FILEPATH######################################## Created directory tmp/release/rollback/env/dbRollback/2022-08-13 Dump Before Rollback tmp/release/rollback/env/dbRollback/2022-08-13/BeforeRollback_backup_env_DDL_DML_06311660285870_2022-08-13.dump Rollback to DDL_DML of sql file located at tmp/release/rollback/env/dbRollback/2022-08-13/backup_env_DDL_DML_06311660285870_2022-08-13.dump Rollback successfully done with tmp/release/rollback/env/dbRollback/2022-08-13/backup_env_DDL_DML_06311660285870_2022-08-13.dump Dump After Rollback tmp/release/rollback/env/dbRollback/2022-08-13/AfterRollback_backup_env_DDL_DML_063116602859099_2022-08-13.dump ########################################FILESPATH######################################## ########################################FILESIZE BEFORE ROLLBACK######################################## tmp/release/rollback/env/dbRollback/2022-08-13/BeforeRollback_backup_env_DDL_DML_06311660285870_2022-08-13.dump has a size of -rw-r--r-- 1 ssm-user ssm-user 1.7M Aug 13 06:31 tmp/release/rollback/env/dbRollback/2022-08-13/BeforeRollback_backup_env_DDL_DML_06311660285870_2022-08-13.dump ########################################FILESIZE BEFORE ROLLBACK######################################## ########################################FILESIZE AFTER ROLLBACK######################################## tmp/release/rollback/env/dbRollback/2022-08-13/AfterRollback_backup_env_DDL_DML_063116602859099_2022-08-13.dump has a size of -rw-r--r-- 1 ssm-user ssm-user 1.6M Aug 13 06:31 tmp/release/rollback/env/dbRollback/2022-08-13/AfterRollback_backup_env_DDL_DML_063116602859099_2022-08-13.dump ########################################FILESIZE AFTER ROLLBACK######################################## sh-4.2$ 普段の業務でどう活かすか? 上記のようなスクリプトやコマンドを使用することで、入力やログインの手間を減らし、また多くのドキュメントを何度も参照する必要がなくなります。これにより、迅速なバックアップの取得が可能になります。 コマンドを手入力する際の時間とエラーを削減できます。 必要なときにいつでも参照できる、体系的で整ったフォルダ構造が提供されます。 DB のアップグレードの際にデータの不整合が発生した場合や、次のバージョンのシステムをリリースする必要がある場合、障害が発生しデータをロールバックする必要がある場合など、これらの手順が必要になります。
アバター
あなたのプロダクトマネージャーとしての「説明」を考えよう -- 書評:プロダクトマネージャーのしごと第2版 自己紹介 Woven Payment Solution開発Gの亀井です。 我々のチームの仕事を一言でいえば「Woven Cityにおける決済システムの開発」になります。 私はWoven by Toyotaというトヨタグループの別会社に出向しており、普段はそちらで活動しています。 Woven by Toyotaはいくつか事業を持っているのですが、Woven City開発はそのうちのひとつです。 この記事はプロダクトマネージャー発売記念プレゼントに応募した際の、書評を書いて公開するというお約束に対する回答でもあります。 https://www.attractor.co.jp/news/product-management-in-practice/ PdMになったきっかけ 実は私はプロダクトマネージャー(Pdm)ではありません。現時点でもPdMという肩書きはもっていません。自称PdMです。 もともとはバックエンド中心のソフトウェアエンジニアで、求めに応じてフロントエンドも書いたりするユーティリティプレイヤーでした。 コード決済システムの経験もあり、それが今の仕事にもつながっています。 その後エンジニアリングマネージャー(EM)もやってきたので、評価とか組織とか採用とかそういった仕事もできます。 ですが、このプロジェクトのために出向するにあたって私に期待されている役割は技術寄りのPdMみたいなものと言われました。 今になって思えば、この時もう少し具体的に期待値を聞いておくべきでしたが、おそらく誰も明確な答えは持っていなかったでしょう。 自分自身でもよくわからないなあと思いながら、技術選定を行い最初のプロトタイプを構築し、採用活動をしてメンバーを増やしと リードエンジニアとEMを混ぜたようなことをやっていました。 一方で他のチームの人から質問が来ることも多く、それに対して資料を用意したり対面で会議(トヨタ用語で面着といいます)をしたりと説明することにも多くの時間を割いていました。 それでも自分で手を動かすということは続けていたのですが、メンバーが増えてくると明確に私が作業のブロッカーになりはじめました。 そこでメンバーとも話し合い、決済について知見のある自分が作るべき機能を説明して、それを他のメンバーが作るという役割分担のほうが生産性があがるだろうということになり、手を動かさないことを決断しました。 この時から自分自身でも、自分はPdMであるという自己規定ができるようになったと思います。これがだいたい一年ほど前ぐらいのことです。 手を動かすのを止めるという決断については、以前EMをやったときも似たような判断をしていたので、そこに対する葛藤はありませんでした。 また機会があれば自分で手を動かす日もくるでしょう。 この本に興味を持ったきっかけ PdMという自覚を得たのはよいのですが、具体的にPdMって何するんだっけ?ということについて確信が持てずにいました。 目先でやることはあるので、何もしないということは無くなにかしら出来上がってはいくのですが、自分自身がどんな成果を出しているのか説明しきれないというもやもやがありました。 プロダクトマネージャーについて書かれた記事や本を乱読するもののいまいち腹落ちはできず、消化不良が続きました。 この本の第1版は読まなかったあたり、そこまで本気で調べてもいなかったともいえますが。 そんな折、この本の感想がチラチラ目に入るようになりました。どれも評価が高く、サブタイトルの「1日目から使える実践ガイド」というのも心に響いたのですが、そのうち買うかーぐらいの気持ちでした。 完全に言い訳ですが、その時みかけた感想はどれも現役バリバリのプロダクトマネージャーのもので、わかっている人に伝わるタイプの本なのかなーという疑念があったのです。 そんなときプレゼント企画を発見し、ここまで気になっていて応募しない手はないなと申し込んだのでした。 ここから書評がはじまります 結論から書くと、この本はとてもよい本です。私のようなPdMになりたての人、PdMを目指す人だけでなく、これからPdMという役割を導入したい組織にも役立ちます。 あるいは自分たちの組織にいる肩書きのないPdMを発見するのにも役立ちますし、自分たちの開発プロセスにプロダクトマネジメントを導入するためにも読んだ方がよいです。 その意味ではこの本は0日目から使えるとも言えます。 プロダクトマネジメントとは この本の偉大なところはPdMという役割は組織によって異なり、世の中の組織が多様であるのと同様に、あるいは世の中の組織が立ち向かう問題が多様であるのと同様に、PdMに求められる役割も多様であるということを認めているところです。 ですがそれだとPdMという役割は存在しないということになってしまいます。この本ではPdMのあるいはプロダクトマネジメントの定義を断念しています。 その代わりに 説明 によってその輪郭を描こうとしています。優れた 説明 の例として別の本『 プロダクトマネジメント ―ビルドトラップを避け顧客に価値を届ける 』から引くかたちで、『ビジネスと顧客のあいだの価値交換の管理人』をあげていますが、PdMやそれを必要とする組織はこれや他の 説明 を元に自分たちの組織にあった 説明 を構築する必要があるでしょう。 これは本には書かれていませんが、それぞれの考える 説明 が互いにマッチするかどうかというのは、PdMを採用したり内部から登用にするにあたって重要なポイントになるのだと思います。 もちろんこれは他の職種にも言えることです。ただPdMという役割は他の職種より抽象的であるだけ 説明 の重要度が大きくなると考えます。 あるPdMがどんな経歴だったとしても、 説明 にミスマッチがある組織では活躍できないでしょう。 COREスキル ではそのような抽象的なPdMという役割にどのようなスキルが必要なのかについて、この筆者は次の4つの能力をあげています。 Communicate (コミュニケーション) Organize (組織化) Research (リサーチ) Execute (実行) そして、この4つの能力の頭文字をとって COREスキル と名付けています。 この本のほとんどはこのCOREスキルをどのように発揮するかについて書かれています。 といっても、例えば「Communicateの章」みたいなものがあるわけではありません。 開発を行う上でのあるあるネタに対して、こんな感じにCOREスキルを発揮できるといいんじゃない?みたいな 説明 がされるというのがこの本の構成です。 焦点をコミュニケーションに当てたり、組織作りに当てたりと章によってテーマはあるのですが、発揮すべきCOREスキルは混然として描かれます。 ところで自分の問題は解消されたのか 自分の肩書きはともかくとして、やっていることの半分ぐらいはPdMと言えるなという実感を持てています。それだけでもこの本を読んだ価値はありました。 チームに自分以外のPdMを入れるとしたら、こういうことをやってほしいという 説明 を作ることができたら、より 組織化(Organize) 出来そうなので、挑戦してみてもよいかもしれません。 一方でもし自分が本気で今後もPdMを続けていく、目指すというのであれば自分流のPdMの 説明 をCOREスキルを絡めて構築するとよさそうなのですが、自分の中にはまだその覚悟はありません。 おわりに 改めてこの本はPdMという役割について考える上でとてもよい導き手となってくれる良い本です。 PdMの人もそうでない人も、PdMがあるいはプロダクトマネジメントが自分たち組織にどのように関わっているのか、 説明 を構築するよい機会を与えてくれると思います。 この本というプロダクトを届けてくださった、著者の Matt LeMay 様、訳者のみなさま、O'Reilly 社様、本をプレゼントいただいた株式会社アトラクタ様ありがとうございました。
アバター
はじめに KINTOテクノロジーズの新車サブスクグループに所属している森本です。バックエンドエンジニアとして日々開発しています。 2022年度のアドベントカレンダーでは、社内の若手メンバーでGraphQLの勉強会を実施したことについて書きました。 今回は、入社した2021年8月から2023年8月のリリースまでの2年間携わった、サブスクサイト(KINTO ONE)のリニューアルについて記載します。その中でも、入社して初めての仕事であるテーブル設計の見直しについて記載します。 テーブル設計で盛り込んだこと 以下の表はリニューアルする前後のテーブルのイメージです。実際に業務で使用しているテーブルではありません。 このテーブルは社員情報を格納するもので、更新があった場合はそのレコードを論理削除し、新しいレコードを追加することで履歴管理できるようになっています。 ※レコードを削除せず、フラグを立てることで削除扱いにすることを論理削除と呼びます。大切な値の更新履歴を追うことができるというメリットがあります。 リニューアル前 id employee_id mail_address pref group_and_role delete_flag 1 E-001 email1@XX.XX 23 HR_Manager false 2 E-002 email2@XX.XX 25 HR_Staff true 3 E-002 email2@XX.XX 14 Development_Manager false 4 E-003 email3@XX.XX 1 Development_Staff false リニューアル後 id employee_id mail_address prefecture group staff is_deleted 1 E-001 email1@XX.XX AICHI HR Manager false 2 E-002 email2@XX.XX SHIGA HR Staff true 3 E-002 email2@XX.XX KANAGAWA Development Manager false 4 E-003 email3@XX.XX HOKKAIDAO Development Staff false 各カラムは以下の情報を表します。 id :社員テーブルでレコードを一意に特定するID employee_id :社員を一意に特定するID mail_address :メールアドレス pref / prefecture :都道府県 group_and_role / group ・ role :所属グループとロール delete_flag / is_deleted :そのレコードが削除されているかどうか 4つの見直しポイント カラムに略称を使用しない 値を数字に置き換えない 1つのカラムに複数の意味をもたせない フラグ系のカラムは _flag と命名しない 1. カラムに略称を使用しない カラム名 pref のように、カラム名を省略しないようにしました。 これが都道府県を表すことは何となく理解できるでしょう。 しかし、これをドキュメントやソースコードで使用する場合、 pref や prf などの表記揺れが発生する可能性が高くなります。また、そもそも何の略称かわからなくなってしまう場合もあります。 そのため略称は使用せず、長くても prefecture のように表記するようにしました。 2. 値を数字に置き換えない リニューアル前のテーブルを見ると、 pref の値として23が入っています。47あるうちの都道府県のどこかを表すと推測できますが、23番目は一体どこなのでしょうか。 直感的でないため値が何を表しているかが分かりません。値を見るたびに数字と都道府県の組み合わせを参照する必要があり、可読性が下がります。また誤りに気づきにくくなります。そのため AICHI のように、都道府県をそのまま格納するようにしました。 そして不正な値が格納されている場合は、プログラムで読み込み時にエラーになるので気づくことができるというメリットもあります。 言語はJava、O/RマッパーはJPAを使用する場合を例として挙げます。 prefecture のenumクラスを以下のように定義し、Userエンティティではその型を使用することで AICHIA のような不正な値があればエラーになり、気づくことができるというメリットもあります。 public enum Prefecture { HOKKAIDO("北海道"), . . . AICHI("愛知県"), . . . OKINAWA("沖縄県"); private String value; } @Entity public class Employee { /** ID */ @Id private Long id; . . . /** 都道府県 */ @Enumerated(EnumType.STRING) @Column private Prefecture prefecture; . . . } マッピングできない場合に、以下のようなExceptionが発生します。 java.lang.IllegalArgumentException: No enum constant com.example.demo.values.Prefecture.AICHIA 3. 1つのカラムに複数の意味をもたせない リニューアル前の group_and_role ようなカラムは、1つのカラムに所属グループとロールの2つの情報を持っていました。一見すると問題ないように見えますが、値を追加・削除・変更する場合の柔軟性が下がります。 元々以下のような4つの定義があった場合を想定します。 HR_Manager HR_Staff Development_Manager Development_Staff 仮に Development グループの名称を NewDevelopment へ変更しようとなった場合、更新SQLの条件が複雑になります。 HR_Manager HR_Staff Development_Manager → NewDevelopment_Manager Development_Staff → NewDevelopment_Staff また検索やソートをする観点でも問題があります。検索のクエリが複雑になったり、ソートをする場合も_(アンダースコア)以降で区切るという手間が必要になってきます。もちろん、可読性も下がります。 そのためサイトリニューアルする際には、1つのカラムは1つの意味を持たせるように分離する作業を行いました。 4. フラグ系のカラムは _flag と命名しない delete_flag は削除したことを表すカラムだと理解できます。しかしtrueは削除済を示しているかどうか迷ってしまいます。 そのため is_deleted のようにbe動詞+過去分詞の形式にしました。そうすることでtrueは削除されたこと、falseは削除されていないことを理解できます。 ここで大切なことは、カラム名と中身の値の認識に相違がないことです。 例えば is_deleted は、社員情報に更新があった場合に古いレコードを論理削除するためのカラムとなります。 true:削除済み false:未削除 フラグ系のカラムでtrueかfalse以外の第3の状態が出てきた場合は、フラグ系のカラムから変更すべきです。 改善し続ける テーブル設計は1回だけではなく、リリースする数ヶ月前まで継続して行いました。 さまざまな種類のテストが行われる中、テーブルを変更することは大きな影響を持ちます。開発チーム内の修正だけでなく、場合によってはバックエンド・フロントエンドを結合したテストを再実施しなければなりません。 バックエンドのプログラムとテーブル間だけで修正を完結することもできましたが、リニューアルするにあたってはAPIのインターフェースまでも一貫して綺麗にしたいと考え、APIを利用する各チームと連携して足並みを揃えて修正しました。 プロジェクト全体で、目先の大変さより、リニューアルする目的を意識していたことが共通認識にあったと思います。 またバックエンドチーム内で改善事項を積極的に伝え合える雰囲気もありました。テーブル設計だけではなく、開発全体で改善し続けられたことが、リニューアルプロジェクトを成功に導くことができた1つの要因だと考えています。 さいごに 実際にはテーブルの構成自体の変更や、使用していないカラムの削除など、大きな構成変更をしました。 これは入社して初めて携わったタスクで、サブスクサイトの構成や機能を100%理解できていない状態で設計することとなりました。開発途中で動作確認をして永続化できていない値に気づいたり、テストなどで指摘があって初めて定義が誤っていることを知ったりすることがありました。 リリースの終わった今でさえ、 「なぜこのような命名をしてしまったのか?」 「ここのテーブルは分離しておくべきだったかも…」 と思うことが多くあります。 しかしながら、チームメンバーをはじめプロジェクト全体でテーブル変更を快く受けれてくださった方々のおかげで無事リリースすることができました。 今後も、わかりやすいテーブルを引き続き目指して改善していきます。
アバター
Hello I'm Koyama from the KINTO Technologies Mobile App Development Group. I work on mobile app development and maintenance as an iOS engineer. Today, I'd like to talk about how MVVM was adopted for Global KINTO's mobile app development. MVVM and Combine There are a few things I'd like to touch on briefly before I begin. MVVM MVVM is an architecture in software development. It consists of Model-View-ViewModel, which is derived from the MVC model. The details are a little complex, so a simple overview is as follows: Model The part that's responsible for collecting data and carrying out simple processing. It doesn't do any complicated processing. View The part responsible for rendering the screen. It receives the data needed to produce the screen, then simply draws it. ViewModel This part is in the middle between the Model and View, and is responsible for bringing together multiple pieces of Model data and holding values needed to maintain the View. That's the general idea (or at least my personal take on it). I found even this much of it difficult to understand when I first started working for iOS. So, if you apply this to iOS development, this is how it looks: Model Brings together all of the data that needs to be handled. Rather than using a dictionary-based approach, you might as well create a single Model instead. The Model only contains simple methods related to the data inside it. View This can be a ViewController or an individual View. It only contains code related to rendering, and simply displays the data it receives without manipulating it at all. Basically, the aim is to have no if statements. ViewModel Receives triggers from the ViewController and returns data. A ViewModel is in a one-to-one relationship with a ViewController, and in relation to an individual View, it can also take on the role of a higher-level ViewController's ViewModel, depending on the situation. This is what I'd like to achieve. In today's article, I particularly want to talk about the differences between a View and a ViewModel. Combine First launched in June 2019, Combine is a framework for creating Apple's official reactive architecture. Before then, reactive architectures were mostly achieved using the third-party RxSwift, but now, an official, strongly supported library has been released. "Hang on a moment. What is a reactive architecture anyway?" Some of you may be wondering this. (I certainly did.) Here's what Wikipedia has to say: reactive programming is a declarative programming paradigm concerned with data streams and the propagation of change. -- Wikipedia | Reactive programming Not very easy to understand, is it? I've heard that to easily understand this, you should picture it in terms of spreadsheet software. Suppose you enter a simple formula like this: A B C 10 20 =A1+B1 Once you've entered it, the result goes in cell C1 (of course). A B C 10 20 30 But what do you think will happen if you change the value in cell A1 to 20? A B C 20 20 40 Of course, cell C1 will change to 40. A change in cell A1 has spread to cell C1 without us directly modifying C1 at all. This is what's called reactive architecture. Based on this, I could now clearly see that you just declare the data processing ( =A1+B1 ) first, then run the actual data through it (assigning 20 to A1) at the end. Background The preamble got a bit long. This is why we decided to adopt Combine and MVVM. Back when I first started working on a Global KINTO development project, we'd just switched from using outsourced code to producing our own in-house. The codebase that had already been written were on a “spaghetti code” state, so it was extremely difficult to maintain. Global variables were being called all over the place, and there was no way to tell which class depended on which. Fortunately, we had a fairly small amount of code and screens, so we tried to solve the problem by reviewing the overall architecture. At the same time, the Mobile App Development Group had some knowledge of using Combine-based MVVM. So, we decided to proceed based on the MVVM architecture. The aim was to design things thoroughly based on the architecture so that even new team members could easily understand the projects and safely modify them. And now comes the implementation! Since we've decided to use MVVM, we'll separate the roles between the View and the ViewModel. This implementation uses UIkit. View - Part1 Instead of doing any processing in the View, we only want to tell the ViewModel the timing of event firings. To do this, we'll use Combine's Publisher. For example, if you need to send the timing of viewDidLoad, define the PassthroughSubject using the Void type. private let didLoad: PassthroughSubject<Void, Never> = .init() Make it so that this gets sent when viewDidLoad is called. override func viewDidLoad() { super.viewDidLoad() didLoad.send() } It's also a smart approach to use tapPublisher[^combineCocoa] when a button is tapped. First, connect the button to the Outlet and prepare the Publisher used for sending. If you want to pass a String value from the displayed screen when the button is pressed, define Publisher as String instead of Void. @IBOutlet weak var button: UIButton! @IBOutlet weak var textArea: UITextField! private let tapButton: PassthroughSubject<String, Never> = .init() Then, use tapPublisher to detect the event and send it to the ViewModel. button .tapPublisher .sink { [weak self] in self?.tapButton.send(textArea.text ?? "") } .store(in: &cancellables) Next, let's take a look at the ViewModel. ViewModel The ViewModel retrieves and generates information based on the event triggers received from the View, then returns the necessary information to the latter. Here, we'll create it using Combine's Operator and Subscriber. First, to prepare to connect it with the View side, define Input and Output structs separately from the ViewModel. Input is the data transferred from the View, and Output is the resulting data you want to pass from the ViewModel. When exchanging data, processing will be possible only if the data and error types have been properly set, so use the type AnyPublisher across the board. AnyPublisher<String, Never> // Want text when the button is tapped! } struct ViewModelOutput { let onShowWelcome: AnyPublisher<String, Never> // Want it to display the Welcome text on the screen let onShowText: AnyPublisher<String, Never> // Want it to display arbitrary text on the screen let onShowOnlyFirst screen: AnyPublisher <Void, Never> // Want it to display a fixed value on the screen only when the button is pressed for the first time } ``` Next, let's create the part that processes the data. This amounts to adding the part to receive input and turn it into output. For example, if you want viewDidLoad to trigger a specific API, connect viewDidLoad's Publisher to Operator and run an API request. ```swift func transform(input: ViewModelInput) -> ViewModelOutput { let apiResponse = input .viewDidLoad .flatMap { api.requestData() } Similarly, when a button is tapped, if you want to generate data based on the text on the screen at the time, use map. let text = input .tapButton .map { createText(text: $0) } You might also want something to happen when viewDidLoad is called and a button is tapped at the same time. In cases like that, Publishers can be connected to each other. There are many ways to connect them, but we'll use zip this time. let buttonTap = input .tapButton let didLoadAndButtonTap = didLoad .zip(buttonTap) .map { _ in } Operators are already available in Combine and its extended libraries, so complex processing can be achieved while still keeping the code simple. There are a lot of Operators, but I've listed the ones I think it's useful to remember below. map flatMap compactMap filter share materialize [^combineExt] merge zip combineLatest withLatestFrom [^combineExt] And finally, add the proper return that goes with the output's type. When you use Combine, the types will keep getting expanded each time an Operator is connected, so in the end, return the created data to View after gather it all together into AnyPublisher. return .init( onShowWelcome: apiResponse.eraseToAnyPublisher(), onShowText: text.eraseToAnyPublisher(), onShowOnlyFirst: didLoadAndButtonTap.eraseToAnyPublisher() ) } View - Part 2 The ViewModel side is now complete, so connect it to the View and render the screen. Let’s call the transform method we prepared earlier to get the results of the processing carried out in ViewModel. let output = viewModel.transform( input: .init( viewDidLoad: didLoad.eraseToAnyPublisher(), tapButton: tapButton.eraseToAnyPublisher() ) ) Once we've gotten the processing results and decided what sort of screen rendering to do for each, we're all done. output .onShowText .receive(on: DispatchQueue.main) .sink { [weak self] in self?.textArea.text = $0 } .store(in: &cancellables) output .onShowOnlyFirst .receive(on: DispatchQueue.main) .sink { [weak self] in self?.showCustomMessage("First!") } .store(in: &cancellables) ``` # Finished code Here's the finished code. ```swift:ViewController.swift class ViewController: UIViewController { @IBOutlet weak var button: UIButton! @IBOutlet weak var textArea: UITextField! private var viewModel: ViewModelProtocol! private let didLoad: PassthroughSubject<Void, Never> = .init() private let tapButton: PassthroughSubject<String, Never> = .init() private var cancellables: Set<AnyCancellable> = .init() func configure(viewModel: ViewModelProtocol) { self.viewModel = viewModel } override func viewDidLoad() { super.viewDidLoad() bind() didLoad.send() } func bind() { let output = viewModel.transform( input: .init( viewDidLoad: didLoad.eraseToAnyPublisher(), tapButton: tapButton.eraseToAnyPublisher() ) ) button .tapPublisher .sink { [weak self] in self?.tapButton.send(textArea.text ?? "") } .store(in: &cancellables) output .onShowWelcome .receive(on: DispatchQueue.main) .sink { [weak self] in self?.textArea.text = $0 } .store(in: &cancellables) output .onShowText .receive(on: DispatchQueue.main) .sink { [weak self] in self?.textArea.text = $0 } .store(in: &cancellables) output .onShowOnlyFirst .receive(on: DispatchQueue.main) .sink { [weak self] in self?.showCustomMessage("First!") // Assumptions for which methods are prepared in advance } .store(in: &cancellables) } } ``` ```swift:ViewModel.swift protocol ViewModelProtocol { func transform(input: ViewModelInput) -> ViewModelOutput } struct ViewModelInput { let viewDidLoad: AnyPublisher<Void, Never> let tapButton: AnyPublisher<String, Never> } struct ViewModelOutput { let onShowWelcome: AnyPublisher<String, Never> let onShowText: AnyPublisher<String, Never> let onShowOnlyFirst: AnyPublisher<Void, Never> } struct ViewModel: ViewModelProtocol { let api: SomeApiProtocol init(api: SomeApiProtocol) { self.api = api } func transform(input: ViewModelInput) -> ViewModelOutput { let didLoad = input .viewDidLoad share() let apiResponse = didLoad .flatMap { api.requestData() } // Future<String, Never> return is assumed let buttonTap = input .tapButton share() let text = buttonTap .map { createText(text: $0) } let didLoadAndButtonTap = didLoad .zip(buttonTap) .map { _ in } return .init( onShowWelcome: apiResponse.eraseToAnyPublisher(), onShowText: text.eraseToAnyPublisher(), onShowOnlyFirst: didLoadAndButtonTap.eraseToAnyPublisher() ) } func createText(text: String) -> String { "\(text) show!!" } } ``` Clearly dividing the roles between the View and ViewModel like this helps keep the code nice and organized. You can use the same structure for any View, which makes the code easier to read and maintain. Also, having the roles all set in stone makes forcible coding less likely. However, one con is that if you haven't done much reactive programming with (e.g.) Combine or RxSwift before, you'll find it **as difficult as to understand as a totally different language** from the Swift you've known up to then. In Global KINTO's iOS projects, all Views are created based on this structure. It's easy to read and maintain, making it less likely to deviate from even when creating new Views. # In conclusion The Mobile App Development Group will share knowledge like in this article with other products besides Global KINTO ones, and take on the challenge of new architectures that are completely different from this one. I want to keep rising to the challenge of using various iOS development methods as part of my work in the Mobile App Development Group. [^combineCocoa]: 拡張ライブラリの[CombineCocoa](https://github.com/CombineCommunity/CombineCocoa)を使用しています [^combineExt]: 拡張ライブラリの[CombineExt](https://github.com/CombineCommunity/CombineExt)を使用しています
アバター