TECH PLAY

BASE株式会社

BASE株式会社 の技術ブログ

579

はじめに こんにちは、ネイティブアプリチームの筧です。 自分はモバイルアプリの開発は今まで Android でしか経験がなかったのですが、最近は iOS アプリ開発にコンバートしました。 はじめは Storyboard の扱いに慣れなかったり、AutoLayout の設定に色々と苦戦していたのですが少しずつ慣れていきました。 今回 UITableView を用いた新機能の開発を経験し、iOS エンジニアの先輩にレビューで課題点をあぶり出してもらい勉強になったので、いくつかポイントをピックアップして紹介します。 開発のお題 まずは簡単に今回のお題となった機能を実現するにあたって、求められる挙動をいくつか紹介します。 主に 2 種類の UITableViewCell を実装します 1 つは文字を入力できる UITableViewCell。 UITableViewCell の中に UITextView が埋め込まれている。 UITextView に入力するテキストを増やしたら、テキストに応じて UITextView ならびに UITextView の親となる UITableViewCell の高さを変更する。 データによって UITableViewCell の高さは異なる。 もう 1 つは選択形式の UITableViewCell。 各 Section の中から必ず 1 つの Cell が選択状態になっていること。 選択された Cell はチェックマークが表示される。 UITableViewCellの高さの計算について UITableViewCellの高さの見積もりをとる UITableView(UICollectionView)では estimatedRowHeight による UITableViewCell の高さの見積もり を指定することで、実際の高さの計算処理のパフォーマンスの向上が狙えます。ただ適当な高さを指定すればいいわけでなく、できるだけ実際の高さとのズレが小さく済むように指定しないといけません。ズレが大きいと結局スクロールがカクついたりする原因になることもあります。 すべての Cell において高さが等しいのであれば estimatedRowHeight を指定すればいいのですが、今回は Cell によって高さが異なります。そこで tableView(_:estimatedHeightForRowAt:) を用いて各 Cell ごとに見積もりの高さを指定しました。 func tableView (_ tableView : UITableView , estimatedHeightForRowAt indexPath : IndexPath ) -> CGFloat { if isHigh(at : indexPath ) { return 300 } return tableView.estimatedRowHeight } func isHigh ( for at : IndexPath ) -> Bool { // 高いheightを指定するべきか判定する // ... } (実際のコードではありません。 高さのキャッシュを設定して再利用する Estimating the Height of a Table's Scrolling Area The table view asks for estimates for every item in your table, so do not perform long-running operations in your delegate methods. Instead, generate estimates that are close enough to be useful for scrolling. 公式ドキュメントにこう書いてあるように UITableView は逐一見積もりの高さを要求するので、できるだけ省エネで各 Cell の見積もりの高さを求めて返す必要があります。そこで tableView(_:willDisplay:forRowAt:) で実際の Cell の高さを取得してキャッシュを生成します。 あとは tableView(_:estimatedHeightForRowAt:)にてキャッシュされた height があればそれを見積もりの高さとして返すような処理を追加します。 private var cacheCellHeights : [IndexPath: CGFloat] = [ : ] func tableView (_ tableView : UITableView , willDisplay cell : UITableViewCell , forRowAt indexPath : IndexPath ) { cacheCellHeights[indexPath] = ceil(cell.bounds.height) } func tableView (_ tableView : UITableView , estimatedHeightForRowAt indexPath : IndexPath ) -> CGFloat { if let height = cacheCellHeights[indexPath] { return height } if isHigh(at : indexPath ) { return 300 } return tableView.estimatedRowHeight } AutoLayoutの制約はできるだけシンプルに はじめは UILabel の height の制約を指定し、UILabel の width と表示するテキストに応じて必要な高さを計算し、制約の値を上書きするようなやり方で動的に高さを変更できる UILable を実装していました。以下のコードのような感じです。 @IBOutlet weak var label : UILabel ! @IBOutlet weak var labelHeight : NSLayoutConstraint ! // Viewの初期化をする func apply (text : String ) { // heightの制約の値を上書き labelHeight.constant = text.height(withConstrainedWidth : label.frame.width , font : UIFont.systemFont (ofSize : 12 )) } extension String { public func height (withConstrainedWidth width : CGFloat , font : UIFont ) -> CGFloat { let constraintRect = CGSize(width : width , height : .greatestFiniteMagnitude) let boundingBox = self .boundingRect(with : constraintRect , options : .usesLineFragmentOrigin, attributes : [.font: font] , context : nil ) return ceil(boundingBox.height) } } しかし今回は以下のような要素が絡むことで、制約のコンフリクトが起こりやすい設定となってしまっていました。 - 端末の画面サイズによって、諸々の View のサイズは異なる。 - UILabel の親 View となる UITableViewCell の高さが不変ではない。 なので高さの制約は削除し、親 View との上下左右の制約に留めることで、制約の不整合が起こりにくくなるように修正しました。 特に端末の画面サイズが異なることはモバイルアプリ開発においては常あることなので、できるだけ AutoLayout の制約をシンプルに保たねばと改めて自分を戒めました。 UITableViewCellのデータ更新について reloadRows(at:with:)は極力使用しないこと reloadRows(at:with:) は指定された Cell をリロードするメソッドです。リロードする際のアニメーションを指定できるので、ユーザーにわかりやすくリロードしている Cell を示すのには有効です。しかしこれは Cell の高さの再計算などの初期化処理を促すのでパフォーマンス観点で多用することは好ましくありません。これもまたスクロールなどがカクつく原因になりえます。 自分が今回やらかしたのは、ユーザーが Cell を選択するたびに Cell の表示を更新するために reloadRows(at:with:)を実行していました。これでは選択するたびに reloadRows(at:with:)が走るので、選択時に TableView の表示が崩れるかもしれません。 func tableView (_ tableView : UITableView , didSelectRowAt indexPath : IndexPath ) { // indexPathに応じたデータを更新 ... // 更新したデータをUITableViewCellに反映するために、該当するCellのリロードを促す tableView.reloadRows(at : [indexPath] , with : .none) } reloadRows(at:with:)を使わずにどう解決したかは次の章で紹介します。 UITableViewの各Cellの選択の制御について 前の章で述べたとおり、はじめは選択されるたびにデータの更新及び reloadRows(at:with:)による Cell の更新をする実装にしていました。 しかしこれでは以下の課題があります。 選択するたびに reloadRows(at:with:)することで、Cell の高さ計算などの初期化が走りパフォーマンスがよくない。 今どの Cell が選択状態なのか Model のデータを参照しないとわからない。UITableView のどの Cell が選択されているについての選択状態の管理が Model 側に侵入し、ロジックの境界が曖昧になっている。 これらは先輩のレビューによって以下のように改修されました。 方針としては、各 Section ごとにどの Cell が選択されているかについて選択状態を自前で管理していたところを、UITableView で管理するようにしたという感じです。 override func viewDidLoad () { super .viewDidLoad() // 複数のrowを選択状態として扱うことを許可する tableView.allowsMultipleSelection = true } override func viewWillAppear (_ animated : Bool ) { super .viewWillAppear(animated) // 画面初期化時は、各sectionのはじめのrowを選択状態にする if presenter.numberOfSections() > 0 { for i in stride(from : 0 , to : presenter.numberOfSections (), by : 1 ) { let index = IndexPath(row : 0 , section : i ) tableView.selectRow(at : index , animated : false , scrollPosition : .none) } } } func tableView (_ tableView : UITableView , willSelectRowAt indexPath : IndexPath ) -> IndexPath ? { if let selectedRows = tableView.indexPathsForSelectedRows { let selectedInSection = selectedRows.filter { $0 .section == indexPath.section } for deselectingIndexPath in selectedInSection { tableView.deselectRow(at : deselectingIndexPath , animated : false ) } } return indexPath } func tableView (_ tableView : UITableView , didSelectRowAt indexPath : IndexPath ) { // 選択に反応してなにか実行したいロジックがあればここに書く } @IBDesignable class CustomCell : UITableViewCell { @IBOutlet weak var selectIcon : UIImageView ! override func setSelected (_ selected : Bool , animated : Bool ) { super .setSelected(selected, animated : animated ) selectIcon.isHidden = ! selected } } まず tableView.allowsMultipleSelection = true を実行して、一度に複数の Row が選択状態として扱えるようにします。特に今回は「各 Section の中で、必ず 1 つだけが選択されている状態にする」という仕様があったため、選択状態を UITableView で管理する上で必須の設定となります。 そしてここが変更のポイントなのですが、選択されるたびに willSelectRowAt 内で tableView.indexPathsForSelectedRows から選択状態になってる indexpath を割り出し、さらにその中から Select の対象になってる IndexPath と section が一致する IndexPath を特定し、その IndexPath を deselect します。 willSelectRowAt 後は didSelectRowAt が走るので、didSelectRowAt 内で選択後に実行したい処理があれば実行します。このとき didSelectRowAt 内で対象になった Row を deselect するようなことはしません。選択状態は保持したままにします こうすることによって、選択中の Cell はデータを参照せずとも indexPathsForSelectedRows によって判別可能となりました。 そして 選択状態の管理は willSelectRowAt 、選択によって実行したい処理は didSelectRowAt と処理の分離ができて見通しが良くなりました。 また UITableViewCell 側の表示の変更は、UITableViewCell の setSelected(_:animated:) 内に書くことで、UITableViewCell 内で完結できます。選択状態の Cell のスタイルについては、 selectionStyle を用いて以下のように設定しておけば問題ありません。 cell.selectionStyle = .none おわりに 他にもレビューでもらったアドバイスは色々あるのですが、今回は UITableView に焦点を当てて紹介しました。今回の内容はもちろん同じくコレクション系の View を実装する UICollectionView でも有効です。 今回紹介したようなパフォーマンス観点での実装の工夫は、iOS アプリ開発に入門した方にとってははじめは仕様を実現するので手一杯で、なかなか意識をするのは難しいのではないでしょうか。ですがスクロールのカクつきや View の表示などのパフォーマンスが悪いと、ユーザー体験にモロに響くので、実際のプロダクト開発においては必須のテクニックだと思います。
アバター
こんにちは。BASE BANK 株式会社 Dev Division にて、 Software Developer をしている東口( @hgsgtk )です。 TL;DR バーティカルスライスでのデリバリーを可能な限り保つユーザーストーリーの分割はどうすればいいか ユーザーストーリーが 1 回のイテレーションで収まらないような場合に分割する 3つの分割軸「データ境界に沿って分割する」・「パフォーマンス制約をストーリーにする」・「アクターのモチベーションで分割する」をもって、ユーザーストーリーを分割する ビジネス価値の内訳要素に基づいて優先順位を考える 当記事の背景 BASE BANK Dev Division での開発プロジェクトでは、アジャイル開発の考え方・取り組みを取り入れています。その導入については、「 少人数でのアジャイル開発への取り組み実例 (一歩目の踏みだし方) | 詳説 | July Tech Festa 2020 登壇レポート 」にて紹介しています。 devblog.thebase.in アジャイル開発において、ユーザーストーリーをイテレーションごとに収まるような形で分割することは、プロダクトに対する検査と適応のリズムを得るために重要な取り組みです。ユーザーストーリーが大きすぎるものをそのまま取り組もうとすると、大きさゆえに見積自体の精度もより落ちてしまったり、その実現に期間を要することから顧客からのフィードバックも得づらくなります。 今回は、実際にネットショップ作成サービス「BASE」のプロダクト開発で行われた、「お金管理画面のリニューアル」を題材にします。プロジェクトや開発現場ごとにベターな形がありますので、完全な正解を提示するものではありません。しかし、いかにユーザーストーリーを分割するか、今回のプロジェクトを参考事例にしていただければ幸いです。 前提となるバーティカルスライスの考え方 「ユーザーストーリー分割を行うぞ」という行為の前提として、バーティカルスライスの考え方を紹介させていただきます。 これは、『 More Effective Agile “ソフトウェアリーダー”になるための28の道標 』という書籍では、より効果的にアジャイルの価値を引き出すための 1 つの基本原則として紹介されています。 短いスプリントがうまくいくには、動く機能を少しずつ頻繁にデリバリーする能力をチームが養わなければならない。こうした活動をサポートするために用いられる設計アプローチはバーティカルスライスと呼ばれる。バーティカルスライスは、増分的に機能または価値をデリバリーするために各アーキテクチャ層で変更を行う、というものである。 9.4 基本原則:バーティカルスライスでのデリバリー つまり、「XxxAPI を実装します」・「テーブルの設計をします」といったレイヤごとの 作業ではなく て、「ユーザーとして自身のメールアドレスを変更できる」といったように、 1 つのフルスタックの機能 を指します。 この考え方は、『 達人プログラマー 職人から名匠への道 』では、「 曳光弾(Tracer Bullets) 」という言葉で表現されています。エンドツーエンドで、フィーチャに必要なシステムの論理層すべてをまたいで実装すること、を指します。 ユーザーストーリーとは ユーザーストーリーという言葉を当たり前のテンションで使っていましたが、一応の扱い方も含めて、改めて定義しておきます。 ユーザーストーリーとは、ユーザー・顧客視点での「フィーチャ」記述を指すものとされています。そして、フィーチャとはソフトウェアを使う側の視点で記述される、xxx 画面等の機能ではなく「欲しい商品が注文できること」といった使い手にとっての価値のことです。 そして、ユーザーストーリーを実現するということは、使い手にとって価値のある動く機能が求められるため、結果的にバーティカルスライスでのデリバリーが必要になります。 特に決まった記述形式は無いですが、当プロジェクトでは、次の形式としていました。 <ユーザー>が、<機能・性能>にする。なぜなら<ビジネス価値>のためだ。 開発チーム内でそのユーザーストーリーの価値が明らかな場合は、 なぜなら<ビジネス価値>のためだ。 を省略可能としました。 いつ分割し、どう分割するか ストーリーが大きすぎて 1 回のイテレーションには収まらない場合や、より正確な見積が必要な場合に、ユーザーストーリーを分割することは有用でしょう。 イテレーションで収まる範囲にストーリーを分割することで、イテレーションで生まれた成果物に対してのフィードバックを得ることが出来ます。 『 アジャイルな見積りと計画づくり 価値あるソフトウェアを育てる概念と技法 』では、一例として次のような考え方をあげています。 データ境界に沿って分割する 操作の境界で分割する パフォーマンス制約をストーリーにする 優先度に沿って分割する 今回は、「 データ境界に沿って分割する 」と「 パフォーマンス制約をストーリーにする 」という 2 つの考え方を、実際のプロジェクトでどのように適用したかについて解説します。 また、プロジェクトを進めていく中で 「アクターのモチベーションで分割する」 という考え方で分割したものもあるので、こちらもご紹介いたします。 なお、どういうプロジェクト運営するかによって、分割後の単位をユーザーストーリーにするべきかは異なるでしょう。たとえば、『 締め切りが厳しいプロジェクトで、プロジェクト初期にまずやっておきたいこと 』では、 分解単位はユーザーストーリー単位にこだわらず、タスク単位になっても良い 、というやり方でプロダクトバックログをスプリントで終わる単位に分解する考え方を紹介されています。 当プロジェクトでは、プロダクトに対するフィードバックを早期にもらうことを重要視したこともあり、ユーザーストーリーを分割単位として進めました。 画面リニューアルプロジェクト 当記事にて実例として取り上げる、ネットショップ作成サービス「BASE」のお金管理画面のリニューアルプロジェクトを軽く紹介させていただきます。2020 年 7 月 30 日(木)、ショップオーナーが注文の売上を確認したり、その後の振込申請など BASE 上のお金を引き出したりする導線の起点である「お金管理画面」がリニューアルされました。 ↓リニューアル前 リニューアル前のお金管理画面 ↓リニューアル後 リニューアル後のお金管理画面 当プロジェクトでは、UI デザイン・体験を改善することはもちろんなのですが、その裏側では様々なレイヤーでテーマを持ったプロジェクトでした。 フロントエンドに焦点を当てると、Vue.js + TypeScript を利用した MPA(multi page application)化への対応が行われました。その背景については、『 「BASE」の管理画面リニューアルプロジェクトのこれまでとこれから 』や『 次世代の管理画面を作るフロントエンドの取り組み 』にて公開しております。 また、バックエンド側では、ここまでの継ぎ接ぎを続けて複雑化してきたコードのドメイン分析をもっての再整理、規模の大きいショップを運営しているショップオーナーでも快適に扱えるようパフォーマンスを改善する、といったところが主たる関心として高いものでした。 プロジェクト特性を分析する プロジェクトごとに有効な施策が異なるため、自身が携わるプロジェクトを分析することがまず第一ステップとして必要です。 お金管理画面とは、ショップオーナーが、E コマースプラットフォーム「BASE」での活動で発生した入出金が入出金履歴として反映されたり、銀行口座への振込申請や、資金調達サービス「YELL BANK(エールバンク)」での資金調達や支払状況の確認をするといった、売上・お金にまつわる導線の入り口になります。 さらに、E コマースプラットフォーム「BASE」はショップ開設数が 110 万ショップを超え、業態から規模まで多種多様です。ショップオーナーのビジネス状況によっては、たくさんの売上データを扱う必要が生まれ、パフォーマンス特性の問題も取り扱います。 以上の特性から、次のようにユーザーストーリーの分割の考え方を実践しました。 データ境界に沿って分割する たとえば、次のようなユーザーストーリーがありました。 ショップオーナーが、入出金履歴を一覧確認できるようにする この一文だけ見ると、1イテレーションで収まりそうな雰囲気のあるものですが、実際はとても大きなユーザーストーリーです。 E コマースプラットフォーム「BASE」で発生する入出金は、注文売上・振込申請・ショップコインによる売上・資金調達などと、バリエーションが多種多様です。 データ境界に沿って分割するという考え方は、たとえば、多様な情報を持つ検索画面があった場合に、「まず債務情報が見える」といった形である程度のデータ境界を設けて分割する、といったやり方です。 この分割では、エンドツーエンドのバーティカルスライスは保たれているので、特定のデータ境界の成果物を持ってスプリントレビューで顧客に見せることが出来ます。 1 つのユーザーストーリーを実現するために考慮するデータバリエーションが多い場合、有効な考え方になります。 この分割の考え方に基づいて、ユーザーストーリーは次のように分割しました。 Before: ショップオーナーが、入出金履歴を一覧確認できるようにする ↓ After: - ショップオーナーが、売上を確認するため、入出金履歴で売上を見れるようにする - 振込申請をしたショップオーナーが、入出金履歴で振込申請を見れるようにする 入出金履歴のデータ境界(売上・振込申請...etc)ごとに、ユーザーストーリーを分割しています。 パフォーマンス制約をストーリーにする たとえば、次のようなユーザーストーリーがありました。 ショップオーナーが、期間で入出金履歴を検索出来るようにする これは、特定期間内に発生した入出金履歴を検索するという機能ですが、入出金履歴が数十件のショップと、数十万件あるショップでは、求められるパフォーマンス要件が異なります。 『 The Elements of Programming Style, 2nd Edition 』にて、 Brian W. Kernighan 氏・P. J. Plauger 氏は、" Make it right before you make it faster. "(動くようになってから、早く動かすことを考えろ)と言っていました。 また、『 Martin Fowler @ OOP2014 "Workflows of Refactoring" 』にて、 Martin Fowler 氏は、Adding Function(機能追加)と Refactoring(リファクタリング)の帽子のかぶり直しという Kent Beck 氏の TDD のメタファーを紹介していました。その説明の中で、 「いかに早くするか」という観点における Performance の帽子 や、アイデアをスケッチしているような Exploring の帽子があることを口頭で補足していました。 パフォーマンス制約自体をユーザーストーリーにして分割することで、そのストーリーでは、 Performance の帽子をかぶることに集中できるので、作業効率上も利点があるといえるでしょう。 この分割の考え方に基づいて、ユーザーストーリーは次のように分割しました。 Before: ショップオーナーが、期間で入出金履歴を検索出来るようにする ↓ After: - 売上50件以上〜100件未満の順調売店ショップオーナーが、期間で入出金履歴を検索出来るようにする - 大規模売店ショップオーナーが、期間で入出金履歴を検索出来るようにする パフォーマンス要件が発生するような大規模ショップオーナーの考慮は、別のユーザーストーリーに分割しています。 アクターのモチベーションで分割する たとえば、次のようなユーザーストーリーがありました。 ショップオーナーが、お金管理画面を見れるようにする この画面は、様々なモチベーションを持ったショップオーナー、に対してユースケースが想定されています。 一例を上げると、 ショップ開設直後のショップオーナーが、BASE の機能を把握するために開く 振込申請中のショップオーナーが、振込申請の進捗を確認しに来る 資金調達中のショップオーナーが、支払状況を確認できるようにする ショップオーナーが、お金管理画面を見れるようにする。 と言っても、そのショップオーナーがやりたいこと・できることは様々です。そもそも振込申請ができるショップオーナーは、売上をあげていることが前提になります。また、まだ売上を上げていないアカウント登録したばかりのショップオーナーもいます。それぞれ状況が異なり、違うモチベーションを持つショップオーナーというアクターを一概に扱うとスコープが広がってしまいます。 このように、これらのアクターのモチベーションが交わり合っているようなユーザーストーリーは、バリエーションを吸収するために大きくなってしまいます。それに対して、アクターのモチベーションで分割することを試みました。 たとえば、ショップの状態によって異なる導線が用意されるような場合は、1 つのユースケースにまとめず次のように分けることができます。 Before: ショップオーナーが、お金管理画面を見れるようにする ↓ After: - ショップ開設直後のショップオーナーが、「お金管理画面で出来ること」を確認できるようにする - 振込申請中のショップオーナーが、振込申請の進捗を確認できるようにする - YELL BANK で資金調達中のショップオーナーが、支払状況を確認できるようにする 当該画面で、アクターが何をしたいのか、というモチベーションベースで、ユーザーストーリーを分割しています。 場合によっては、最初からユーザーストーリーを分けており、この観点で分割するまでもないケースもあるでしょう。しかし、まだユーザーストーリーの扱いに不慣れなチームであれば、異なるモチベーションが 1 つのユーザーストーリーに混在しているときに、「大きなユーザーストーリーになっていないか」見直す機会になります。 (追記)「ロールモデリング」の一種類が該当することを、Twitterでの反響の中で教えていただきました。目的・方法・状況・熟練度によってロールを分けることで詳しくペルソナを想定するといった発想です。今回の分割の考え方は「目的」でロールを分けていますね。詳しくは、角征典さんが2010年に実施された『 ユーザーストーリー ビギンズナイトの資料 』をご覧ください。 優先順位の考え方 いざ、ユーザーストーリーがそれぞれスプリントに収まる大きさに分解できると、その前後で優先順位をどう考えるかが顔を出すでしょう。 優先順位の高いものから着手していくことを計画する際に、ビジネス価値の高さを意識することになりますが、「ビジネス価値」ってなんだろうってなりませんか。最初、このビジネス価値という言葉に対してこれを言葉で説明できない自分がいるなと思いました。これについても、『 アジャイルな見積りと計画づくり 価値あるソフトウェアを育てる概念と技法 』では、そのビジネス価値の要素に次のような項目を上げています。 1. フィーチャの金銭価値 2. 開発(サポート)にかかるコスト 3. 開発を通じて学べる知識の量とその意義 4. 開発によって低減できるリスク これらのどの要素を重要視するかについては、各プロジェクトで異なるでしょうが、当プロジェクトにおいては、 3. 開発を通じて学べる知識の量とその意義 を重要視しました。 これは、以下のプロジェクト事情を加味した結果です。 実際にエンドユーザーに届けるリリースは、ユーザーストーリーごとに分割できるものが少ない。そのため、対象の機能リリースによる「1. フィーチャの金銭価値」は、優先順位にそこまで影響しない。 Sketch 段階のデザイン検討において必要なスコープの見通しを立てているため、ユーザーストーリーそれぞれは、画面リニューアルリリース時にはだいたい必要な開発になる(MUSTではない機能もいくつかスコープ内にあったため、ある程度可変ではありました)。そのため、「2. 開発(サポート)にかかるコスト」についても優先順位に対しての影響度は少ない。 「3. 開発を通じて学べる知識の量とその意義」は、優先順位の判断に影響のある観点たりうる(その理由は後述します)。「4. 開発によって低減できるリスク」の観点は、3 を考慮していく中で結果的に考えることになる。 リリース計画を考察する際、「スケジュール」・「スコープ」・「リソース」のどれを重要視するかという視点があります。当プロジェクトは、「スコープ」を重要視し、スケジュール・リソースの可変性が高く捉えておりました。 開発を通じて学べる知識の量とその意義 プロジェクトを考えるにあたり、「何が不確実な箇所でリスクとなりうるか」は重要なポイントです。リスクのアンテナを張るに当たり、Alexander Laufer の不確実性理論が、参考になります。 Alexander Laufer の不確実性理論は、ざっくり次のような不確実性の整理です。 目標の不確実性 (end uncertainty) (開発 PJ に対応させると、)何を開発するのか、スコープ・プロダクトの性質 方法の不確実性 (means uncertainty) (開発 PJ に対応させると、)どうやって開発するのか、技術・スキル・連携方法 これらの不確実性を低減させるためにプロジェクトを通して、開発対象のプロダクトについて知り、その作り方について知識を深めていかねばなりません。 取締役 EVP of Development の藤川(えふしん)が、『 ウィズコロナ時代にBASEを成長させていく2つの技術課題 』にて、コンポーネント化、アーキテクチャ刷新に対する学習コストへの適応、を技術的課題としてあげていました。 実際、これは、Alexander Laufer の不確実性理論でいうところの 方法の不確実性 (means uncertainty) が存在する状況です。 実際に当プロジェクトでは、frontend コードの実現方法が見えきっていない箇所の早期着手、backend コードにおけるドメインモデリングのコード化、が早期に来るような形で、ユーザーストーリーに優先順位をつけていきました。 分割したユーザーストーリーを用いたリリース計画とスプリント計画 このプロジェクトでは、リリース計画の中で、プロダクトバックログにユーザーストーリーを入れ、直近 2 スプリントくらいに何をするか、を優先付けし、それらは Miro を用いて可視化していました。 それぞれのユーザーストーリーに対して、開発メンバーで、プランニングポーカーを行いストーリーポイントをつけています。 以下の Miro は実際に使用したボードの一部です。すでに完了したプロジェクトなので、すべてのユーザーストーリーはどれかのイテレーション内に割り当てられていますが、計画策定当初は仮割当で置いておいて、直近のスプリントだけ実際に消化するユーザーストーリーとして割り当てていました。 実際に利用していたMiroのボード 実際に、スプリントが始まると、スプリント計画で実際の作業タスクに落とし込んでいくことになるので、作業時に使用するツールとシームレスになるように、別途タスク管理として GitHub Project を用いました。 さいごに これらの分割を実施したことにより、バーティカルスライスでのデリバリーが実現でき、次のような利点がありました。 プロジェクトの終盤の最終 QA で仕様の齟齬がたくさん見つかってしまう、といった事象を避けることが出来た 毎スプリント動くものを見せ続けたことで、進捗が明確だった プロジェクト開始当初まだ詰められていない要件・仕様についてスプリントレビューで動くプロダクトを見ながら確認できた ストーリーポイントを用いた見積もり方法や、顧客からフィードバックを貰いながら、目標の不確実性や方法の不確実性に対応していくプロジェクト運営において、イテレーションの成果物をいかに作業ではなく、ユーザーにとって価値のあるものにするか、は基礎的ですが重要なポイントでしょう。 大きなユーザーストーリーを目の前にしたそこの貴方にこの記事が参考になれば幸いです。
アバター
<アイキャッチ画像はメンバーの許可を得て掲載しています> 挨拶 はじめまして。デザイナーの河越です 2020年卒の新社会人として、4月からBASEのDesign Sectionで働いています。 なんだかんだでもう社会人5ヶ月目。この記事では、未経験からデザイナーとして入社するまでにしたことと、入社してから4ヶ月で取り組んだ業務や考えたことを振り返っていきます。 「当たり前のことしか言ってねぇ!」と思っても、優しく見守っていただけると嬉しいです🙇‍♀️ デザイナーとして入社するまでにしたことと まず簡単に私がBASEにデザイナーとして入社するまでのことを説明します。 私は2018年6月にBASE BANK株式会社のインターンとしてBASEグループに入社しました。 鶴岡さんのツイートがきっかけだったと思います! やるぞ!!! - 銀行をかんたんに。新会社BASE BANK立ち上げメンバー募集!! by BASE株式会社 ( @BASEec ) #wantedly https://t.co/nicGHulNbI — 鶴岡 裕太 / BASE, Inc. CEO (@0Q7) May 2, 2018 そこから約2年間(途中留学やお休みなどいただきながら)、BASE BANKでお金にまつわるプロダクトのリリースやグロースに携わりました。 BASE BANKで働く過程で、「 YELL BANK 」や BASE BANKのロゴ を作成した吉岡さんの活躍を見たことや、グロースにおける細かいUI改善を行いたくても自分にUI変更を扱えないもどかしさから、「私もデザイナーとしてプロダクト作りに関わりたい」と思うようになります。 デザイナーになりたい旨を社内のデザイナーの先輩方に相談したところ、なんと私専用の研修プログラムを作っていただけることに、、、! (今思えば、本当にやりたかったらこの時点でもっとUIデザインについて勉強しておくべきだったし、未経験からスタートという無茶なお願いを受け入れてくださったデザインチームの先輩方にも、温かく送り出してくださったBASE BANKチームにも感謝が止まりません😭) 2019年10月から、未経験の私をデザイナーとして育成するための、デザイン研修を開催していただきました。 デザイン研修 デザイン研修でやったこと デザイン研修では月ごとに cocoda! にお題として掲載されている、 ギフトサービスTANPのiOSアプリデザイン セールや送料無料キャンペーンなどを想定したバナー作成 「BASE」で過去に行ったキャンペーンのLP作成 progate を使ってコーディング自習 最終課題:「BASE」のデザインマーケットの改善案 などの課題が出され、それに対するアウトプットを作り、毎週一度BASEのデザイナーに1on1のレビューをしていただく機会をいただいていました。 最終課題に取り組んだ1ヶ月は週に2度レビュー会があり、Product Managementのメンバーや社内の他のメンバーにもフィードバックをもらいながらデザイン案を作りました。3月に最終的なアウトプットを神宮司さん(執行役員VP of Product)やDesign Sectionのマネージャーなどにプレゼンし、4月からBASEのデザイナーとして働く内定をいただきました。 デザインレビューは怖くない デザイン研修では週に1時間、レビューの時間をいただいていたのですが、最初の3~4ヶ月はとにかくレビューが怖くて、逃げ出したかったのを覚えています。 レビュー時間を「時間をかけて作ったアウトプットにダメ出しをされる時間」と捉えてしまい、「こんなに頑張ったのにまた振り出しに戻ってしまった...辛い...」「レビューでは私が考えられてなかったポイントばかり指摘されてぐうの音も出ん。デザイナーは視野が広い人しかできない職業で自分には向いていないんじゃないか...辛い...」と、レビューのたびにネガティブになっていました。 あるレビュー会のあと、落ち込んでいる私を見て先輩デザイナーが 「 レビューはアウトプットに対してのフィードバックであって、誰も人格を否定してるわけじゃないよ。ユーザーのためになるプロダクトを作るために、自分にはない視点から意見を受けれる機会だよ! 」 と声をかけてくれました。 自分にない視点を持っている人がアドバイスをくれる機会なんて超貴重です。 頭ではわかってたはずなのに、素直に受け入れられなくなってしまっていた私にはこの言葉がすごく響きました。 それからは、自分へのアドバイスとデザインに対するフィードバックを切り分けて聞くことができるようになり、レビューはもう怖くありません。逆に、レビューをせずにプロダクトが世に出る方が怖いです。 これからも、レビューは「Be Hopeful」な気持ちを持って参加したいと思います。 競合プロダクトのデザインをパクっても意味がない プロダクトを作る時、多くの人が競合プロダクトを調査すると思います。 私はデザイン研修の最終課題「BASEのデザインマーケットの改善案」の最初のレビュー時に、競合サービスを完全に模倣したアウトプットを提出しました。今思うと、競合調査をしました!というアピールもしたかったのかもしれません(笑) デザイナー「ここはどういう意図でこういうデザインにしたの?」 私「OO(競合サービス)がそうなってたからです!OOはたくさん使われてるすごいサービスなので、真似したらいいとおもいました!(アホ)」 というやりとりがありました。先輩は相当頭を抱えていたと思います。 そもそも本当にターゲットは同じ属性なのか 競合サービスはどういった意図でUIを作ったのか 利用者数の多い競合サービスには本当に使いづらいところがないのか を考え、理解しないと、「BASE」のユーザーにとって優しいサービスは作れません。 ユーザーの属性が違えば悩みや欲しいものも違うし、それに合わせたUIを設計しないと、ユーザーにストレスを感じさせてしまうからです。 今は、競合サービスやよく使うアプリやWebサービスを見るときには、そのデザインになった意図を考えるようにしています。今まで気づけなかったデザイナーの気遣いに気付けるようになり、インターネットサービスを使うのが100倍楽しくなりました! また、アプリの自動アップデートをOFFにし、アップデート前後でどうUIが変わったのか、なぜその変更が行われたのかもチェックするようにもしています。 careerhack.en-japan.com 入社して4ヶ月でやったこと 4月からは社員としてBASEで働いていて、「プロジェクト化されないような粒度の細かい改善を行うプロジェクト」という奇妙な名前のプロジェクトにアサインされています。 プロジェクト化されないような粒度の細かい改善を行うプロジェクト(以下改善PJ)は、「ユーザーからの改善要望は届いているけど、粒度が細かくてプロジェクト化するほどでもなく改善を後回しにしていた箇所を改善する」ためのプロジェクトです。 プロダクトはチームで作る プロジェクト内では、メンターの先輩がデザインの壁打ちをしてくれたり、デザインチーム内でレビューをもらったり、PMとバックエンド、フロントエンドエンジニアとQAをしたりと、とにかくたくさんの人と関わりを持ちます。 チームのメンバーと関わる機会が増えれば増えるほど、自分一人ではなく、チームでプロダクトの改善を行っているんだ!という実感が湧いて、勇気が出たし、プロダクトについてより深く考えるようになりました。 私は1Qの間に3つのデザイン改善を担当し、その中の2つがリリースされています。 自分がデザインしたものが世に出る体験が初めてで、リリースの瞬間はドキドキと、「早く使われて欲しい!」という期待と、少し不安もある、複雑な気持ちがありましたが、「もしユーザーからアンハッピーなフィードバックがたくさん来ても、みんなで考えれば解決される」という気持ちの余裕も今は持てています。 大きなプロジェクトだけがプロダクトを支えているわけではない デザイナーを志した当初は、「YELL BANK」のLPやサービスUIを作る先輩を見て「はやく新しいプロダクトを担当できるようになりたい!」と意気込んでいました。 しかし、改善PJを経験し、改善を喜んでくださるユーザーの声を聞くことで、大きなプロジェクトにならないアップデートでも、ユーザーのストレスを解消する小さな改善をMove Fastに行うことで「BASE」のブランド作りに貢献できるとわかるようになりました。 それに気づいてからは、改善PJでも扱われないさらに粒度の細かいもの(例えば1文字の誤字など)も、積極的に改善するようにしています。 作業は急いでする 改善PJにアサインされて、一番怖かったのが、私のデザインの遅延によってリリースが遅れたり、別の方の作業着手を遅らせてしまうことです。 これを防ぐために、自分がどのくらいの時間でどの分量の作業を終わらせられるかをなるべく正確に見積ってPMに伝えることを意識しています。 具体的には、実作業の時間が目標時間に対してどのくらい誤差があるのかを調べ、どのくらいの時間で全体の作業が終わりそうかを伝えていました。 自分の作業能力がわかるようになってきたので、間に合うかな?くらいの努力目標を自分の中で設定することで、作業に集中しないといけいない環境を作るようにもしています。 最初から最後までなるべく全速力で作業することで、作業効率が上がっている気がします。 最後に 長くなりましたが、デザイナーとして働き始めて感じたことを振り返ってみました。 未経験デザイナー・新卒ということもあり、とても手厚くサポートしていただいている最高の環境です。 私はインターンを卒業して社員になってから、もっとBASEが大好きになりました。 まだまだ先輩デザイナーの背中は見えないし、甘えてばかりの日々ですが、一刻も早く恩返しできるように日々努力していきたいです。これからも頑張ります!
アバター
Service Dev所属、サーバサイドエンジニアの宮村です。 現在私は、Service Devのチームに所属し、ネットショップ作成サービス「BASE」及びショッピングアプリ「BASE」の機能開発を担当しています。 BASEでは最近、機能開発の際に設計レビューを行うようにしています。その取り組みについて紹介したいと思います。 開発チームについて BASEの開発チームは、メンバーが増えるに従って専門化する形でチームを分割してきました。 現在、サービスの機能開発を主に担当しているService Dev Sectionは、バックエンドが担当領域を分担して2Group、フロントエンド、ネイティブアプリを担当するそれぞれ1Groupの計4つのGroupから成り、Service Devのエンジニアはいずれかのチームに所属する形となっています。 (組織図について興味を持たれた方は、こちらの 会社説明資料 をご確認いただければと思います。) 機能開発の際はそれらの職能別のGroupを横断する形でプロジェクトチームを編成し、プロジェクトチームが主体となって機能開発を行っていきます。チームのメンバー構成はプロジェクトの特性や規模により様々ですが、多くの場合、同じグループから1〜2名がプロジェクトにアサインされることとなります。 設計レビューを始めたわけ 機能の増加、開発メンバーの増加、並行するプロジェクトの増加により、新しく課題が見えてきました。エンジニアに対して多くのプロジェクトが立ち上がったことで顕著に現れたものもありました。 サーバサイドの領域を専門にするエンジニアが自分以外にプロジェクト内にはいない・少ない状態になるため、機能の品質が個人に依存しがちな状況になってしまっていた。早い段階で多くの目で見ることで、機能の品質を担保したい。 チームのコードレビューのプロセスは取り入れていたが、コードレビューの段階で指摘を受けると手戻りが大きいので、早い段階で既存のシステムに詳しいチームメンバーによるレビューを取り入れ、手戻りを少なくしたい。 コードレビュー時に初めてコードを渡されても仕様把握が難しいので、チームメンバー側が事前に新機能を把握しておきたい。 以前からコードレビューは必須としており、チームで品質を担保することを重視してきていましたが、設計段階でレビューを行うことで改善できることもあるのではないかと思い、設計レビューを始めてみることにしました。 設計レビューの実践 いつやるのか 設計レビューのタイミングは、プロジェクトにエンジニアが参加し、テーブル設計、画面設計がある程度できた頃、実装に入る前を目安としました。これまでの実績では、開発チームのキックオフから数日〜数週間の間に行われています。 どうやるのか プロジェクトの担当エンジニアが、レビュー用のドキュメントを作成します。それをもとに、チームメンバーに説明しフィードバックを受ける形でレビュー会を開催します。対面もしくはオンラインミーティングで、同期的に行うこととしており、所要時間は機能によりますが、30分から2時間程度としています。長くなる場合は、複数回に分けて実施することもあります。 ドキュメントとして必ず用意するようにしているのは、以下の内容です。 データベース設計 一度動き出すと変更のコストが大きいので、ER図を必ず用意して確認するようにしています。 アプリケーションのスケーラビリティ クリティカルな処理への影響 決済などお金に関わる箇所を中心に、不具合が発生すると影響が大きい部分は特に注視します。 その他、画面設計やシーケンス図など、機能を理解するための資料を必要に応じて用意します。 設計レビューを運用してみて 最初にやってみてうまくいかなかったこと 仕様理解のために画面イメージから説明を進めていたところ、機能の仕様やUIへのフィードバックが盛り上がってしまい、時間がかかりすぎてしまいました。サービスに対する思いが各々にあることはとても良いと思いますが、効率の面からはバランスを取る必要があります。 それらの部分は、プロジェクトチームでディレクターやデザイナーも含めて検討されたものであり、一番その機能を考えているのはプロジェクトチームのメンバーということは間違いないでしょう。これらの点についてはプロジェクトチームでの決定を尊重し、設計レビューでは実現方法にフォーカスしてレビューするようにしました。 機能の仕様やUIについては明らかに問題があると考えられる場合のみフィードバックすることにして、再度仕切り直すことにしました。 良い点 進め方は未だ改善を繰り返していますが、総合するとポジティブな面が多いと感じています。いくつかを箇条書きで挙げてみます。 ドキュメントを起こして他の人のレビューを受けるというプロセス自体で、設計が整理される効果がある 担当エンジニアが責任を持って設計するので大筋では問題ないことが多いが、やはりいくつかの有効な指摘がある レビュアが現在担当しているリリース前の機能との整合などを検討できるといったメリットもある レビューを受ける側のメリットとして、まだ実装を始める前なので指摘を受け入れて修正しやすい コードレビューの時点では、当然ですがコードはある程度書き終わっているので、その時点でこの変更は…という躊躇も生まれますが、それがないのは個人的にとても大きいと感じています。 もちろん、このレビューは開発を始める前のプロジェクトチームを含めて新機能に対する理解が深くはないタイミングで行われるので、すべてがここでレビューを受けたとおりには進みませんし、すべての問題を軌道修正できるわけでもありません。ただ、レビューを行ったほうが、開発起因での手戻りや課題が早い時点で発生するようになったというような気はします。 課題や注意点 準備段階に関しては、レビューのフォーマットが各人に任せるのではなく統一したほうがレビューする側がやりやすいのではないかということで検討しています。ただ、プロジェクトの特性などもあるので型を決めて守るというのが最善ではない気がしており、試行錯誤しているところです。 また、レビューまでにどのくらい詰めるか、という点も個人の感覚に頼るところが多いかもしれません。レビューを受けても大きな変更は必要ないだろうという程度には詰める必要がありますが、詰め切るのに時間をかけすぎても機能の提供に時間がかかってしまいます。 やりたいことは Move Fast にプロダクトを改善することなので、それを念頭に置いてよりプロセスの方も良い形に改善していきたいと思います。 設計レビュー後のプロセス 設計が承認されると、それをもとに見積もりを行い、各チームの開発に進んでいきます。この時点でプロジェクトのスケジュール、人員計画の見直しを行うようにしています。とりあえず始めてみて、遅れそうになったところ調整を行うのではなく、この段階で相談できるようになったことも、プロジェクト管理の観点からはポジティブな面と言えるかもしれません。(もちろん、開発開始後でも、必要があれば柔軟に対応していきます。) まとめ メンバーの増加、プロジェクトの増加という状況の中、サービス品質と開発スピードを維持するために、設計レビューを取り入れました。 最初は所属Group内で試験的に行っていましたが、最近では別チームも含め、ほぼすべてのPJを対象にレビューを行っています。また、チーム内でのレビュー後にマネージャやCTOといった意思決定者も交えたレビューを実施するようにもなり、少しずつ役割を広げて運用されています。 より良い開発が行えるよう、私たちも日々試行錯誤しながらやっているのですが、同じような課題を抱える方の取り組みの第一歩として、少しでも参考になれば幸いです。
アバター
BASE株式会社 Product Dev Division 基盤グループ Engineerの田中 ( @tenkoma )です。 ある出来事がきっかけで2要素認証にYubiKeyを購入して使ってみたので紹介します。 フィッシングに引っかからない自信を無くした話 1ヶ月半前の土曜夕方に、外出先で以下のツイートを見ました。 GitHub Japanさんからフィッシングらしきメールが来てたのでみなさんもお気を付けください pic.twitter.com/CsmkObwUS9 — Eiji Kitamura / えーじ (@agektmr) June 13, 2020 ちょうど数時間前に同じメール文面を見たような気がしたのと、同じく数時間前にPCでGitHub.comにログインしていたので、 「これはマズいかもしれない、引っかかったか?」と焦りました。 その場ではパスワード変更だけしました。急いで帰宅して、状況を調べました。その結果— GitHub またはGitHub Japanを騙るメールは見当たらなかった ブラウザの履歴に偽サイトにアクセスした履歴は無かった GitHub.com のSecurity logに自分の部屋とモバイル回線以外のIPからログインされたり操作されたログは無かった ので、フィッシングにひっかかったというのがほぼ思い違いであることがわかりました。(一安心) もしかすると、上記ツイート以外の注意ツイートを見たことを、メールで受信したと思い込んだのかもしれません。 この経験をしたことで、「上記のようなメールを受信したら、冷静にフィッシングかもと疑うこと無く、偽サイトにワンタイムパスワードごと渡してしまうかもしれない、入力情報を中継されたら、ログインされてしまうかもしれない」と思いました。 YubiKeyを購入して、普段の2段階認証にはそれを使う いままで、GitHubログインするときは、ID/パスワード入力には1Passwordを、 2段階認証(2FA)では、Authyを使った時間同期式のワンタイムパスワード(TOTP)を入力していました。 しかし両方とも人間が入力する情報なので、フィッシングで偽サイトにアクセスした場合に、両方を入力してしまうことができます。 (1Password ならURLも保存しておけば、ブラウザ拡張の補完候補で絞り込まれるので、偽サイトにアクセスしたとき、「候補に表示されない」ことで気づける可能性はあります) YubiKeyなどのキーデバイスを使うと、2FAではサーバー側のホスト名なども含め認証に必要なデータを生成します。 よって本物のGitHubログインにつかう認証データを、人間のミスで渡してしまうことはありません。 僕が購入したのは、YubiKey 5Ciです。 Amazon | Yubico - YubiKey 5Ci - USB-C / Lightning 認証セキュリティキー | Yubico | USBメモリ・フラッシュドライブ 通販 ワンタイムパスワードトークンYubiKey |(株)ソフト技研 MacBook Pro, iPhone X, Pixel 4で使いたかったので、USB Type-C, Lightning端子が付いている5Ciにしました。 GitHub.com の2FAにYubiKeyを設定すると、ログインはどうなる? 使い始める前は、「TOTPより面倒くさくなるのでは、端末毎に設定が必要とかだとつらいな」と思っていましたが、むしろ簡単になりました。 設定は、ページにアクセス後、YubiKeyをさして金属部分を触るだけなので省略します。 (日付をみると、GitHubフィッシングが話題になってすぐYubiKeyを買って設定したことが分かります) GitHub.com に設定したデバイスキー一覧 ログインで、ID/パスワードを入力して送信すると、次のように表示されます。 ID/パスワード送信後は、まずセキュリティキーを使った2要素認証をうながされる キーデバイスを設定すると優先してそれで認証するように促されるんですね。 「Use security key」をクリックして、YubiKeyを差し込み金属部分を触ると、ワンタイムパスワードが送信され、ログインが完了します。 僕の場合、Authyを使ったときより簡単になりました。 Authyの場合、 Authyを起動し GitHubのエントリを検索して表示し(設定が15個くらいある) クリップボードにコピーするか6桁の数字を覚え 貼り付けるか入力して送信 という感じなので、検索しなくてよいので楽です。 また、MacのChromeで有効にすると、他のデバイスでの設定は不要です。 FirefoxやSafariでも使えますし、Pixel 4(Chrome)やiPhone X(Mobile Safari)でもYubiKeyでの2FAが出来ました。 他にどんなサービスで使える? YubiKeyが使えるか試したところ、以下のサービスでは使えることが分かりました G Suite GitLab Twitter Facebook Sentry AWSマネジメントコンソール(rootアカウント、IAMともに) AWS 以外ではTOTPとキーデバイスの両方を同時に設定できるので、YubiKeyを紛失したときのリカバリーとしてTOTPも有効にしています。 個人でAWSのrootアカウントを持っているのですが(全然使えていませんが…)、現時点ではTOTPかキーデバイスの片方しか設定出来ず、YubiKeyを紛失したときのリカバリができないため、rootアカウントには設定していません。 まだ買ってないけど、TOTPのかわりに使える方法ってある? YubiKeyを使い始めてから知ったのですが、最近のMacではTouch IDが使えます。 2段階認証が、Touch IDを指で押さえるだけになるので、オススメです。 またPixel 4の顔認証もキーデバイスとして使えました。 iPhone XのFace IDは使えませんでした。 まとめ YubiKeyを使った2段階認証は、お金はかかるけど、フィッシングでの不正ログイン対策になります ログイン操作も、時間同期式のワンタイムパスワード(TOTP)より簡単になりました YubiKey紛失時のリカバリがかなり面倒なので、AWSマネジメントコンソールのrootアカウントは複数の2FAを設定出来るようになって欲しい YubiKey持ってない場合でも、MacのTouch IDやAndroidの顔認証が使える可能性はあります YubiKey、小さいので、財布とかに入れるとすぐに無くしてしまいそうです。 いまのところ、IDカードホルダーに付けているのですが、リモートワークだと常時付けてないので、別のネックレスのようなものか、リストバンドに付けられるといいかもしれない、と思っています。よい案があれば @tenkoma まで教えてください。
アバター
こんにちは。BASE BANK株式会社 Dev Division にて、 Software Developer をしている東口( @hgsgtk )です。 先日開催された July Tech Festa 2020 にて、「 少人数でのアジャイル開発への取り組み実例 (一歩目の踏みだし方) 」という表題で、発表させていただきました。 https://speakerdeck.com/hgsgtk/a-first-step-to-agile-movement 今回はその発表内容について、その思考過程で出会った書籍などを詳説として加えつつ、ブログとして書き下していきます。 発表資料 今回の発表資料は、Speakerdeck に公開しています。 発表内容を通じて解決したいことは、 率直に書くと、 数ヶ月前の自分が教えてほしかったこと です。数ヶ月前の自分は次のような状況でした。 漠然とチーム開発に課題を持っていている その課題に対して、アジャイル関連の施策が、有効な気がしているが、一歩踏み出せていない この資料は、そんな状況を目前としている方々のなにかの役に立てば、と思いまとめたものです。 また、アジャイルと一口に言っても様々な要素が絡んできますが、当該資料は、「チーム環境」に焦点を当てたものです。「 An Agile Way: アジャイルの「ライトウィング」と「レフトウィング」 」の整理では、 アジャイルのレフトウィング (協働でゴールに向かう「チーム環境」)の範囲をスコープとしています。 まずは現状を見る その状況において、私が出会って解決の糸口となったのは、「 みんなでアジャイル ―― 変化に対応できる顧客中心組織のつくりかた 」という書籍です。 www.oreilly.co.jp その中で、次のようなフレーズがありました。 成功するアジャイルの適用は、常に厳しく正直に現状を見ることから始まる。 何がうまくいっていて、何がうまくいっていないのか。アジャイルを今の仕事 のやり方をちょっと変えるだけのことと思っているなら、アジャイルから得られ るメリットもちょっとだけになるだろう。今のやり方を選んだ元になっている現 2章 自分たちの北極星を見つける そして、章の名前「2章 自分たちの北極星を見つける」にある通り、自分たちが大事にしたい・目指したい目標を、言語化することから始めています。 そこからアジャイルとの親和性を考える 自分たちの現状の課題・目指す方向性を言語化出来ると、アジャイル開発の考え方・取り組み方法との対応を考えることができます。 Manifesto for Agile Software Development に示されているアジャイル開発宣言や、「 アジャイルな見積りと計画づくり 価値あるソフトウェアを育てる概念と技法 」にて説明のあるような基本的な仕事の進め方、から、その実践の有無を考察しました。 アジャイルプラクティスを漸進的に導入する すべてのプラクティスを一気に導入することは、手一杯になりますし、一気に生まれた新しい手段・HOWを理解しようとすることに、関心が持っていかれる重力が生まれるでしょう。そのため、優先度にそって、少しずつプラクティスを拡大していくような変遷を、結果的にたどっています。 具体的には、 1. チーム自身の検査と適応 → 2. プロダクトに対する検査と適応 というフェーズをたどりました。 ここで取り組み始めたのは、まずチーム自身の検査と適応のリズムを作ることです。 1週間の スプリント(イテレーション) を設定、最初に計画( スプリント計画 )し、スプリント終了後に、自分たちチームの取り組みについて振り返る レトロスペクティブ を行う 見積もりに、相対的な大きさによって図る ストーリーポイント を利用 現在の状況を可視化するために カンバン管理 を行う カンバン管理は、かつてから Asana を使っていたのですが、うまく管理できていませんでした。今回の取り組みでは、 アジャイルの境界を開発チーム内とした ので、開発作業との親和性を考え、 GitHub Project を利用しました。 それ以外には、日々のコミュニケーションの機会であるデイリースタンディングは、以前から行ってました。 そして、その後、チームが作り出す成果・プロダクトの作り方に対してプラクティスを適用していきました。 プロダクトへのフィードバックループがより頻繁に回るように、スプリントごとの成果物に対する振り返り( スプリントレビュー )を行う フィードバックをもらうために、リリース計画を行う際の前提の考え方を変える( バーティカルスライス でのデリバリーへ) 不確実性を段階的に低減させていく意図 この変遷になった背景には、主に2つの理由を考えていました。 1つ目は、まずは具体的なプラクティスではなく、目標に目を向け、その方法の WHY に着目し、自身の取り組み・振る舞いに対する検査と適応のリズムを作り出すのを優先するという意図。 2つ目は、「 アジャイルな見積りと計画づくり 価値あるソフトウェアを育てる概念と技法 」にて紹介されている、開発プロジェクトの不確実性の考え方「 Alexander Lauferの不確実性理論 」です。この概念では、プロダクトにおける不確実性を次の2つに整理しています。 目標の不確実性 (end uncertainty) (開発PJに対応させると、)何を開発するのか、スコープ・プロダクトの性質 方法の不確実性 (means uncertainty) (開発PJに対応させると、)どうやって開発するのか、技術・スキル・連携方法 すべてのことを一気に片付けようとすると混乱が生まれがちです。そのため、まずは、チームの連携方法や動き方、についての方法の不確実性を低減させ、その後「どういうプロダクトを作るのか」という目標の不確実性を低減させるような取り組みを続ける、フェーズ分けとなりました。 バーティカルスライスでのデリバリーを前提とする 「プロダクトに対する検査と適応」を考えると、その具体的な施策は、プロダクト成果物に対するレビューを行うスプリントレビューが、一つ考えられます。これは、そもそも、イテレーションで生まれる成果物(スクラムの言葉では「インクリメント」でしょうか)に対して、フィードバックが得られるような計画をしていることが前提にあります。その前提において重要なことが、 バーティカルスライスでのデリバリー でしょう。 これは、「 More Effective Agile “ソフトウェアリーダー”になるための28の道標 」という書籍では、より効果的にアジャイルの価値を引き出すための一つの基本原則としても紹介されています。 短いスプリントがうまくいくには、動く機能を少しずつ頻繁にデリバリーする能力をチームが養わなければならない。こうした活動をサポートするために用いられる設計アプローチはバーティカルスライスと呼ばれる。バーティカルスライスは、増分的に機能または価値をデリバリーするために各アーキテクチャ層で変更を行う、というものである。 9.4 基本原則:バーティカルスライスでのデリバリー つまり、「XxxAPIを実装します」・「テーブルの設計をします」といったレイヤごとの 作業ではなく て、「ユーザーとして自身のメールアドレスを変更できる」といったように、 一つのフルスタックの機能 を指します。 実際に、プロダクトに対する検査と適応のループを回そうとすると、この考え方が必要になってくるため、リリース計画自体もこの考え方を前提にリプランニングをしました。 取り組みを始めてから 実際にこのような取り組みに踏み切ったのは、COVID-19の流行がはじまった今年の春からの話です。しかし、1weekのスプリントを通して、チーム開発についてのフィードバックループが、以前よりも早く回っていることを感じます。 具体的に目に見えるような差で言うと、JTF2020から2月弱前の2020年6月4日に、コネヒトマルシェというイベントで「 小さなチームが始めたアジャイル開発 」という発表をしました。目指す方向性は変わっていませんが、その中のチームの取り組み・実装方法は改善され、変容を遂げています。 チームやその周りを取り巻く状況は日々変わっていきますが、その不確実性の高い外部要因に対しても、スプリント単位での適応の議論ができているように感じています。 さいごに:July Tech Festa 2020 への謝辞 July Tech Festa 2020 とは、2020年7月25日(土)に開催された、カンファレンスです。今回、600名強?の参加申込みがあったとのことした。 techfesta.connpass.com ITに関わる全ての人の知的好奇心を満たすお祭りを作りたい。 その思いからJuly Tech Festaは生まれました。 興味のある技術に触れ、普段会えない人の話を聞き、 参加したエンジニアがスキルやキャリアについて考えるきっかけになれば。 そんな場を作ろうと今年も July Tech Festa 2020 を開催します。 個人的な話で恐縮ですが、自分が社会人エンジニアになってから初めてカンファレンス参加したのが、July Tech Festa でした。たしか July Tech Festa 2016 だったと思います。その際、「外の世界は想像以上に先へ進んでいる...!」と衝撃を受け、今回のカンファレンステーマであるように「 Extend Your Engineering Life! 」したことを、強い記憶として思っています。今回、そんなJuly Tech Festaのスピーカーとしてプロポーザルを採択いただいたのは、感慨深い想いでした。 今回、初のオンラインでのカンファレンスのなか、運営スタッフの方々には、この場を借りて感謝申し上げます。ありがとうございました。
アバター
CTOの川口です。 今回はアプリケーションのエラートラッキングツールについてです。 これまでの経緯 BASEでは主にPHPアプリケーションのエラートラッキングにRaygunを利用していました。 https://raygun.com/ これを採用したのは3年以上前で、当時BASEではPHP5.3を利用していたために利用できるツールは限られておりRaygunはPHP5.3でも動いたため採用されたようです。 今では7.3を利用していたのでこの条件は気にする必要はありませんが、積極的に変更する理由もなく使っていました。 当初はPHPのエラートラッキングにのみ利用していましたが最近はfrontendのエラートラッキングにも利用しています。 ここが辛いよRaygun まず料金が結構高めです。 APMやRealUserMonitoringなどの利用はしておらずエラートラッキングのみ使っています。 https://raygun.com/platform/crash-reporting APMはPHPに対応していませんし元々Newrelicを利用しています。 small business プランを利用しており、$249/month です。 https://raygun.com/platform/crash-reporting#pricing これは非常に高額というほどでもありません。 ですが、更にこの上のプランになると$899と一気に値段があがります。これは一気に高額になります。 これのしきい値がエラーイベントの件数になっており、月間の件数を超えてしまうとプランを変更する必要があります。 25万エラーを超えて100万エラーまで受け付けてくれるのはありがたいですが、結果的には30万イベント程度で済むであろう事が想定されコストパフォーマンスが悪くなります。 さらにRaygunはあまりPHPのサポートに積極的でなく、UTF-8以外を含むと問題が出たりします。 https://github.com/MindscapeHQ/raygun4php/pull/69 https://github.com/MindscapeHQ/raygun4php/pull/69#issuecomment-514351759 https://github.com/MindscapeHQ/raygun4php/issues/94 PRに対する反応も鈍いため弊社ではforkしてパッチを当てて運用していました。 WEB UIもあまり良いものでなく全体的に遅く、日付範囲でのサマリなどがし辛いと感じていました。 GitHubへのissue連携も古いAPIなどを利用しているようで、Raygunへ渡す権限があまりに強くこのあたりも問題です。 別のエラートラッキングツールに変更も話題には登っていましたが、ずるずると先延ばしていました。 エラー量増加に依るトラッキングの停止 現在のプランでは250,000 error events/monthを処理することが出来ます。 基本的にこの数を超えることはないのですが、ミドルウェアの変更などで想定外のエラーが出たりするとアクセス量が多いサービスの場合エラーイベントが一時的に大量に発生します。 ある時、これが複数回起きてしまいlimitを超えてしまいました。 完全にトラッキングが止まったわけではありませんが、エラートラッキングツールとして常用出来ません。 当然プランを上げれば動作するわけなのですが、3.5倍近い値段を払ってまで?という気持ちもありこのタイミングで乗換を検討しました。 Raygunのようなサービスでは過去のデータはそこまで重要度は高くありません。 まだ修正出来ていないエラーが有る場合別のトラッキングツールに乗り換えたところで発生することはわかっているので改めて収集すればいいので問題にはなりません。 料金も安く、機能的に優れているものに乗り換える方がメリットになると考えました。もちろん安いからと言って機能が十分でなければraygunでもいいですし別の高額なツールでも問題はありません。 乗り換え先 同様のエラートラッキングツールはいくつかあり Bugsnag Rollbar newrelic Airbrake などがあります。 今回は弊社で利用したことがあるメンバーもいたので、Sentryにすることにしました。 値段もRaygunより安価ですし、機能も豊富そうに見えます。 仮に特定の月にエラーが大量に発生しても、その量に応じて追加課金になるので今回のような事態になっても毎月のプランまで変える必要がなくて安心感があります。 https://sentry.io/pricing/ https://docs.sentry.io/accounts/pricing/#faq 14日はトライアルもできるので一時的な乗り換えに使って不満があればRaygunに戻る事もできます。 SDKも用意されています。弊社で使っているCakePHPを完全にサポートされているわけではないですがRaygunを利用しているコードに少し修正を加えるだけで動きそう、ということで移行をはじめました。 https://sentry.io/for/php/ 移行の実装は数時間で完了しました。 ここがすごいよSentry Sentryのエラートラッキング自体はraygunと別に変わりはありませんが、圧倒的に多機能である事がわかりました。 WEB UIを見てみると、動作も速く情報量が非常に多くあります。 Raygunの場合、環境毎にアプリケーションを登録しapi keyを差し替える必要がありますがSentryの場合はenvironmentを指定することで一つのアプリケーションとして登録することが出来ます。 特に便利だと感じたのはエラー時のスコープにある変数がSentry上で確認できるので、どのような変数によってエラーが起きるのかの調査が捗ります。 下記のような事象は「Undefined index: id」というエラーが発生した場合の表示です。 $favorites に特定indexのみないのかそもそも空を参照していたか、というのが判断可能であり、さらにitem_idも空配列ということがわかるのでなぜこのようなエラーが発生したかの調査にとても有用です。 error PHPのコードも一部表示されるので出先でちょっとした調査くらいならできそうです。 とはいえ勝手にSentryにコードがアップロードされているのでは?ということでセキュリティ的にちょっと心配になるのも事実です。 Spike Protectionという機能がデフォルトでオンになっており、一時的に大量にイベントが発生した場合などにイベントを破棄してくれるようです。 DB障害などがあると全てのアクセスでエラーイベントが発生し情報の精度は下がりつつイベント回数を消費してしまい課金額が上がってしまいがちですがこれはそれを回避してくれそうです。 https://docs.sentry.io/accounts/quotas/?_ga=2.25032318.591036687.1594604704-646375577.1594038001#spike-protection docs.sentry.io ここがつらいよSentry トライアルしている状態なので明確な不満があるわけではまだありません。 通知も細かく設定できるのですが、ここはちょっと慣れていないと大変だなという印象です。 RaygunはとりあえずSlack通知くらいだったらwebhookのurlを渡せばよかったのですが、どういう条件で通知するかというのを登録しないといけないので慣れるまでは戸惑いがありました。 他には「Allow Shared Issues」という機能がありカジュアルにエラーのURLを公開出来てしまうのは危険なのでオフにしました。 多機能であるがゆえに、どこを見ればいいか迷ってしまうなとは感じています。あまりにシンプルだったRaygunからの移行なため余計にそう思ってしまうのかもしれません。 おわりに まだトライアル段階なので使いこなせているというほどではないですが、SentryはRaygunよりも優れているツールだと感じました。 もちろんエラーは通知さえできればいいというものではなく解消していき狼少年にならないようにアプリケーションを改善していくことが重要ですが、どのような場合にエラーが出ているのかの調査がわかりやすく改善もしやすいです。 どのデプロイによってどのようなエラーが引き起こされたか、などを観測していきアプリケーションの改善に役立てていきたいと思います。
アバター
フロントエンドチームの右京です。 Vue.js 3 が 八月上旬にリリース予定 ということで、BASE でもバージョンアップに向けて少しづつアクションを始めています。 Vue.js 3 では多くの機能が追加され開発の幅が広がりますが、一方で削除や非推奨となる機能も多く頭を悩まされている方もいるのではないでしょうか、BASE もそうです。 この記事では ESLint とその Vue.js 向けプラグインの eslint-plugin-vue を利用した、deprecated となる機能へのアプローチを紹介します。 なぜ eslint-plugin-vue か ESLint には Vue.js の SFC のためのプラグインとして eslint-plugin-vue が開発されていて、BASE でもこれを利用しています。 このプラグインには vue/no-deprecated- で始まるルールがいくつかあります。これは非推奨になった機能を使用しているかどうかを検出するためのルールです。 これまでは Vue.js 2.6 で非推奨になった slot と scope に関する以下のルールのみでした。 vue/no-deprecated-slot-attribute vue/no-deprecated-scope-attribute vue/no-deprecated-slot-scope-attribute しかし、最近リリースされ開発の勧められている v7.0.0-alpha.x で多数のルールの追加が行われています。 例えば BASE の場合、Vue.js 3 の移行で特に課題だったのが filter の廃止です。 金額の変換に多くの filter を使用していたため、範囲も広く置き換え方法を模索していたところでした。 eslint-plugin-vue では、これを vue/no-deprecated-filter ルールを適用することで検出することできます。 このほかにも多数のルールが現在進行形で追加されています。 ルールの一覧は 7.0.0-alpha.x タグの docs や、@ota-meshi さんによる日本語での解説から参照できます。 https://github.com/vuejs/eslint-plugin-vue/tree/v7.0.0-alpha.9/docs/rules https://qiita.com/ota-meshi/items/ff0d70ef326657249727 BASE では多数あるルールの中でも、特に効果の大きそうな <template> に関するルールを中心に追加していくことにしました。 <script> 部分には vue-class-component を使用していることもあり、一旦優先するべきところではないと判断しています。 Monorepo と GitHub Actions を利用した別ラインでの継続的な改修 現行の Vue.js 2.x での開発ももちろん行われているため、突然 ESLint のルールを変更するのはハードルが高く感じ、しばらくの間は既存のルールを共存させることにしました。 これを実現するため、 yarn workspace で Monorepo として 7.0.0-alpha.x をインストールし、普段使用しているものとは別のルールを適用できるようにしています。 . ├── packages │ └── eslint-for-vue3 │ ├── node_modules │ │ └── eslint-plugin-vue // 7.0.0.alpha-x │ ├── .eslintrc.js // Vue 3 に向けた設定 │ └── package.json ├── node_modules | ... │ ├── eslint │ └── eslint-plugin-vue // 普段使っているバージョン ├── src // lint する対象 ├── .eslintrc.js // 普段使っている設定 ├── package.json └── yarn.lock eslint-for-vue3 の package.json で 7.0.0-alpha.x を dependencies に追加します。そして、この Monorepo のコンテキストで ESLint を実行する scripts を設定しています。 { " name ": " @base/eslint-for-vue3 ", " private ": true , " dependencies ": { " eslint-plugin-vue ": " 7.0.0-alpha.9 " } , " scripts ": { " lint ": " eslint -c ./.eslintrc.js --ext .vue ../../src " } } .eslintrc.js には普段使用しているルールに加えて、 vue/no-deprecated-* のルールを追加しています。 module.exports = { extends : "@baseinc/eslint-config-base" , rules: { 'vue/no-deprecated-slot-attribute' : 2, 'vue/no-deprecated-scope-attribute' : 2, 'vue/no-deprecated-slot-scope-attribute' : 2, 'vue/no-deprecated-filter' : 2, 'vue/no-deprecated-v-bind-sync' : 2, 'vue/no-deprecated-v-on-number-modifiers' : 2, 'vue/no-deprecated-events-api' : 2, 'vue/no-deprecated-functional-template' : 2, 'vue/no-deprecated-html-element-is' : 2, 'vue/no-deprecated-vue-config-keycodes' : 2, 'vue/no-deprecated-dollar-listeners-api' : 2, 'vue/no-deprecated-v-on-native-modifier' : 2, 'vue/no-deprecated-dollar-scopedslots-api' : 2, } } この状態で以下のコマンドで、普段使っているものとは別のルールを適用した ESLint を実行できます。 $ yarn workspace @base/eslint-for-vue3 run lint 対応が終了したらこの .eslintrc.js を普段使うものに昇格させ、Monorepo を削除してしまえばあとの片付けも簡単です。 GitHub Actions でデイリービルドを構築し、モチベーションを高める フロントエンドチームでは毎日十四時ごろからスタンドアップミーティングを行っており、これに合せて ESLint を実行するような GitHub Action を設定しています。日々進捗を確認する機会を設けることで、Vue.js 3 へのモチベーションも高めるようにしています。 name: lint for Vue.js 3 on: schedule: - cron: 0 5 * * * jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - uses: actions/setup-node@v1 with: node-version: '12.x' - uses: actions/cache@v1 with: path: node_modules key: ${{ runner.os }}-lintvue3-${{ hashFiles('**/yarn.lock') }} restore-keys: | ${{ runner.os }}-lintvue3- - run: yarn install --pure-lockfile --ignore-optional --network-concurrency 1 - run: yarn workspace @web/eslint-for-vue3 run lint - if: failure() env: SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }} run: | curl \ -X POST\ --data-urlencode "payload=\ {\ \"text\": \":bomb: Vue.js 3 マデアト: https://github.com/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID} \",\ }"\ ${SLACK_WEBHOOK} 以上です。 まとめ ESLint を利用した比較的気軽に導入できる Vue.js 3 への対応方法を紹介しました。 もちろんこのエラーがなくなったからといって Vue.js 3 への対応が終了するわけではありませんが、対応のとっかかりなどになれば幸いです。 ちなみにですが、特に Vue.js の比率の高いリポジトリにこれを実行した結果は以下でした。 ✖ 500 problems (500 errors)
アバター
こんにちは!デザイナーの渡邊です。 今年の5月に入社し、UIデザイン業務などを担当しています。リモート下でチームメンバーとなかなか直接会えない状況ですが、毎日楽しくお仕事させていただいております! そんなリモート中でも、活発に新たな知見を得たり情報交換をしていこうと、BASEでは『BDI NIGHT』という勉強会を行っています。 今回は『BDI NIGHT』ロゴの制作過程についてお話します! そもそも『BDI NIGHT』とは? 2018年の秋頃から活動している、BASEのデザイナーがやりたいことを持ち寄って、デザインに関する幅広い知見をみんなで楽しく学ぶことを目的とした任意参加の社内勉強会です。 デザイナーだけでなく誰でも参加することができ、時々PMやエンジニアが覗きにきてくれることも! 『BDI』は「BASE Design Inspiration」の略。Inspirationの名の通り新たなひらめきにつながる新しいトピックを取り上げることも多くあります。 『NIGHT』がつく理由は主に夜に開催されること、また仕事よりちょっとリラックスモードで、お酒やお菓子などを片手に気軽に参加してほしいなという想いからついたそうです。 今までに行ったBDIの例だと Figma講座 フォントかるた webレイアウトトレース アプリトレース 内製UIコンポーネントライブラリ「bbq」のロゴを作る Instagramのデザイナードキュメンタリーを見る 開発ブログの微妙なとこ洗い出し カスタマージャーニーワークショップ などなど....勉強だけでなくコミュニケーションの場としても活用しています。 『BDI NIGHT』のロゴをつくろう! BDI NIGHTのお題としてこの勉強会のロゴを作ることに。ロゴ決定までの全3回を振り返っていきます! 1.BDIを知る・分析する DAY1では新入社員の私が参加するということで、改めてBDIについての概要を教えていただきました。 その後、BDIをどう思っているか、どんな場にしたいかを明確にするためにmiroを使ってブレストを行いました。 ざっくりとですが「自由度の高さ」や「わいわい感」「敷居の低さ」などに関わるキーワードが多めの印象でした。 「なるほど、堅苦しさや過度なアカデミック感を出すと温度感がズレてしまうかも...?」などの見当がつき、BDI初参加の私にとって大いに参考になるありがたい時間でした。 2.デザイン制作 第1回の後半〜第2回はみんなで作業タイム。 zoomを繋ぎ、雑談しながら資料探しやラフ作業などを進めていきました。 個人的にですが人と話ながら作業をするとかなり手が進みます!特にラフなどの精密さよりも量や勢いが必要な作業は特に...。オフィスなら何気なく隣の人とお話したりできますが、リモート下だとこういったコミュニケーションも少なくなっていたので、久しぶりの感覚でとっても楽しかったです。 私のデザインの進め方 デザインの進め方は人それぞれですが、一例として私の進め方を紹介します! とりあえず資料をpinterestで集め 「なんかいい感じなの作りたい...」ぐらいのゆるい感覚でロゴを眺め観察します 「BDI」と短いアルファベットなので、文字そのものにユーモアな図案が加わってるものorグラフィック主体のロゴじゃないと持たなそうだな....など考えながら見当をつけてピンしていきます BDIのらしさを演出するアプローチを複数考えながらラフを書く ラフを出す際は似たような案が並ばないよう、がっつり見た目が変わることを意識します。アプローチから全然違う考え方にするとアウトプットがばらけて良い感じに! インパクトある象徴的なグラフィックをメインにしたい→ひらめきの象徴の電球を使いたい、それだけだと情報量すくないので、手を入れたり背景に星空いれたり... 勉強会であることをアイコン化したい→知見を得る....インストールアイコンとかを落とし込む?先進感・シンプル・ラボっぽさ 勉強会なんだけども、敷居を低くしたいという意志を汲みたい→BARとかお酒みたいな夜の楽しいプライベートな雰囲気 ラフを書きながらもpinterestに戻って、どうやったら想像してる図案がうまいこと収まるのか、参考資料を集めたりを繰り返します。 ラフはipadで書いてます。 気に入った案をブラッシュアップしながら、オリジナリティを担保する 資料をいっぱい見ながら書いていると、資料にデザインが似てくることがかなりあります。他のデザインと差別化できるオリジナリティを加え、影響を受けていると思われる資料からはリスペクト精神を持って意識的に離していきます。 トレスやレイアウトの一致などのトラブルが起こらないよう、パーツは01で手書きで書き起こしたりします。 イラストレーターで整え、仕上げ加工などする Aiを使ってガッツリ詰めていきます。基本的にはモノクロで制作→コントラストがあってちゃんと視認できるかチェック→着彩の順に進めています。 打ち文字をそのまま使うことはせず、基本的にはアウトライン化してバランスを見ながら調整します。 3. お披露目&投票タイム 最終回でメンバーの案をお披露目!各案にコンセプトを添えて、自身の作品を紹介していきました。1つのお題でも製作者によって全然解釈が違い、個性的なロゴが集まりました。 各自コンセプトを発表した後、1人3票で好きなロゴに投票する形で決定しました。 最終的に投票で私の案が選ばれました...!ありがとうございます...! 採用されたロゴ 結果、こちらの案が採用されました!BASEの象徴ティピからひらめきを外に向けて発信するイメージの図案です。グランジの加工を施し、ティピ×夜=ナイトキャンプな雰囲気に仕上げました。 グランジ加工はwebにおいてはあまり相性が良くなく、サイズによっては潰れてしまうこともあります。 グランジは私の好みで付けた点でもあるので、どんな環境でも快適に使ってもらえるようにグランジ無しverと横組verも用意しました。 また、slackのコミュニケーションのために小さくても視認性をある程度保った絵文字verも用意しました。割と好評でうれしかったです! まとめ 普段UIデザインの仕事がメインの中、他のメンバーのグラフィックデザインを見れた事が新鮮でとっても楽しかったです。発想も1人1人全然違い、自分では考えつかないような素敵な案が沢山出ていました。 UI/UXと違ってロジカルには解決しづらいグラフィックデザイン領域ですが、こういった解釈の広がりを見ると明確な答えがないからこその面白さを改めて実感します。 柔軟な発想力を持って、これからも色々なデザインにチャレンジしていきたいと思います!次のBDI NIGHTが待ち遠しい...!
アバター
こんにちは。BASE BANK株式会社 Dev Division にて、 Software Developer をしている東口( @hgsgtk )です。私のいる開発チームでは、アジャイル開発の考え方・取り組みを取り入れています。アジャイル開発の導入については、「 小さなチームが始めたアジャイル開発 」という資料を公開しています。 今回は、アジャイル開発において、重要な振り返りについて、 Mad Glad Sad(喜、怒、哀) というレトロスペクティブ(振り返り)のワークを紹介したいと思います。 TL;DR Mad Glad Sad(喜、怒、哀)は、感情データを集めるためのワーク イテレーションで起きた、喜んだり、怒ったり、哀しかったりした時間やイベント、を書き出していくイベント 素直な感情ベースでイベントを振り返ることで、 "理性のフィルター"で見つからない潜在的課題をチームが見つける きっかけになる Mad Glad Sad(喜、怒、哀)とは レトロスペクティブのワークの一つです。感情データを集めるために使用する取り組みです。このワークは、「 アジャイルレトロスペクティブズ 強いチームを育てる「ふりかえり」の手引き 」で、データを収集するアクティビティとして紹介されてます。 このワークでは、イテレーションで起きた、喜んだり、怒ったり、哀しかったりした時間やイベント、を書き出していきます。書き出されたそれらのイベントを深堀りしていきながら、グルーピングしていきます。 「 アジャイルマニフェスト 」では、 プロセスやツールよりも個人との対話に価値を置いている ことを宣言しています。 また、「 More Effetive Agile ソフトウェアリーダーになるための28の道標 」では、 ソフトウェアチームのEQ(Emotional Intelligence、感情知性)向上の重要性 を示しています。 自分の感情の状態と他者の感情の状態によく注意し、感情をうまくコントロールし、他者との良好な関係を築くことは、技術職メンバーにとってプラスに働く可能性がある。 チーム開発において、自己・他己の感情に向き合うことは、個人との対話に価値を置くアジャイル開発の価値をより引き出すために重要と言えます。 Mad Glad Sad(喜、怒、哀)は、そんな自己・他己の感情に向き合うきっかけとなります。 オンラインワークにmiroを用いる 2020年7月8日現在、WFH(Work From Home)での業務のため、オンラインでのレトロスペクティブを行っています。オンラインでのコラボレーションツールとして、 miro を使用しています。 miroには、テンプレートがいくつか用意されていますが、その中に「 Retrospective Mad/Sad/Glad 」というテンプレートがあります。miroで同じく実施してみる場合、こちらのテンプレートを使うと手軽に始められそうです。 miro.com 実際に実施したワーク 実際に、本ワークを実施した際のmiroのボードです。 (中身はお見せできないのですべてxxxxになっています。「実際にどういう話があったの?」と気になった方は、お気軽にカジュアル面談などでお話しましょう...!) open.talentio.com 書き出しやすいように、Mad Glad Sadは、次にように翻訳していきました。 Mad: イラッとしちゃったこと Sad: 内省したこと Glad: 楽しかったこと 書き出されたイベントは、それぞれ深堀りしていくなかで、チームにとっての「やっていったほうがいいこと」が見つかってくるので、それらは"Action Item"とつなげています。これは、「 アジャイルレトロスペクティブズ 強いチームを育てる「ふりかえり」の手引き 」で紹介されている深堀り質問例のひとつ 次に行うべきステップは何か? に対する議論の結果をカードとして言語化しています。 どういう効果があったか 言語化しきれていない生の感情データが浮き彫りになる よく用いられる振り返り手法としては、KPT(Keep Problem Try)がありますが、人によっては個人的な課題を、あげることをためらってしまうことがあるかと思います。たとえば、「個人的にはこの作業はやらないといけないことがわかっているが、ちょっとしんどい」や「他チームとの調整において、ちょっともやっとした」といったものが例で挙げられます。 こういった課題は、論理的に言語化しきれていないものだったりするので、チームのProblemとして上げづらいこともあるでしょう。 感情ベースのデータ収集であることがワークとして明確なMad Glad Sadは、こういった発生イベントを明らかにしてくれます。 深堀りしていく中でチームとして取り組むべき潜在的課題が見えてくる 浮き彫りになったイベントに対する感情データに対して、深堀りしていくなかで、チームとしての潜在的課題が見つかってきます。 たとえば、「アルゴリズム設計の心理的負荷が、チームで背負いきれていない」、といった課題がワークを通じて発見されました。 KPTを重ねてきた中では明らかになっていなかった課題だったため、感情データに着目するこのワークが、潜在化された課題を見つけるきっかけになりました。 最後に チームの取り組みを振り返るワークの一つして、まだ試していなければ試してみてはいかがでしょうか。なにか新しい自分たちが見つかるかもしれません。
アバター
こんにちは。BASE BANK株式会社 Dev Division にて、 Software Developer をしている東口( @hgsgtk )です。Stay Homeで仕事していく中で、ちょっとした面倒事を解消するような小ネタが溜まってきたので、一つお届けします。 TL;DR リポジトリ作成時には、毎回CIの設定・ .editorconfig ・ .gitignore の配置、最低限のHello Worldコードの作成など、定形作業が存在する 自分たち用のボイラープロジェクトを作成するCLIを用意するのも一つの手だが、気軽にサクッと出来る方法として、GitHubのTemplate Repositoryがある Goプロジェクト作成時に実際に活用しているTemplate Repositoryをご紹介する 背景 BASE BANKでは、「 YELL BANK(エールバンク) 」というサービスの開発・運営を中心に、Goを用いてアプリケーション開発を行っています。 よくある面倒事として、新規にGoプロジェクトを作成する際に毎回作業するようなCI (Contenious Integration) の設定などがあります。 これは、Go言語に限った話ではありませんが、新規リポジトリを作成すると毎回脳死してコピペを行いがちなものがあります。 今回は、そんな面倒事をGitHubのTemplate Projectを用いてちょっとだけ省力化するお話です。 GitHubのTemplate Repositoryとは Template Repositoryとは、「 テンプレートリポジトリを作成する 」で説明のある通り、既存のリポジトリをテンプレートにして同じディレクトリ構造・ブランチ・ファイルで新しいリポジトリを生成できるようになる機能です。 既存のリポジトリをテンプレートにして、自分や他の人が同じディレクトリ構造、ブランチ、およびファイルで新しいリポジトリを生成できるようにすることができます。 help.github.com 始め方は、非常にかんたんで、リポジトリの Settings にて、 Template repository にチェックを入れるだけです。 こうして、チェックを入れると、新規リポジトリ作成時に、次のようにテンプレートが選択できるようになります。 実際にテンプレートを選択すると、Template Repositoryの内容をコピーした形で新規リポジトリを作成できます。この機能を用いて、自分たちの定形作業をTemplate Repositoryに入れておくと、面倒事を少し解消することができます。 以下、具体的にどういう内容を入れているか、実際に活用しているGoプロジェクト用のTemplate Repositoryをご紹介します。 どういうことをしているか BASE BANK Devでは、Goアプリケーションの新規リポジトリを作成することが多いのですが、次の内容をTemplate Repositoryに入れています。 CircleCIの設定 GitHub Actionの設定 Hello WorldなGoコード( go.mod ・ go.sum と一緒に) $ tree -L 2 -la . ├── .circleci │   └── config.yml ├── .editorconfig ├── .github │   ├── PULL_REQUEST_TEMPLATE.md │   └── workflows ├── .gitignore ├── .golangci.yml ├── README.md └── app ├── Dockerfile ├── Makefile ├── go.mod ├── go.sum └── main.go CircleCIの設定 GoコードにおけるCircleCIの自動Lint・テストは、次世代型イメージのcimg/goを使用した設定をしています。毎回コピペするのも面倒なので、レポジトリ作成時に生まれてくれると面倒事をさばく人生の時間が減って少しだけ幸せです。 version : 2.1 jobs : test-app : environment : TZ : "Asia/Tokyo" GO111MODULE : "on" docker : - image : cimg/go:1.14.4 environment : GOCACHE : "/tmp/go/cache" TEST_RESULTS : /tmp/test-results steps : - checkout - run : mkdir -p $TEST_RESULTS - restore_cache : key : gopkg-{{ checksum "app/go.sum" }} - run : name : mod download command : | cd app go mod download - save_cache : key : gopkg-{{ checksum "app/go.sum" }} paths : - /home/circleci/go/pkg/mod - restore_cache : keys : - build-cache-{{ .Branch }}-{{ .Environment.CIRCLE_PREVIOUS_BUILD_NUM }} - run : name : install-gotools command : | cd app go get github.com/fzipp/gocyclo - run : name : build command : | cd app mkdir -p $GOCACHE go build -v - run : name : test command : | cd app gotestsum --junitfile ${TEST_RESULTS}/unit-tests.xml -- -p 6 -race -cover ./... - run : name : calcurate-cyclomatic-complexities command : | cd app gocyclo -avg $(find . -name "*.go" | grep -v _test) - save_cache : key : build-cache-{{ .Branch }}-{{ .Environment.CIRCLE_BUILD_NUM }} paths : - /tmp/go/cache - store_artifacts : path : /tmp/test-results - store_test_results : path : /tmp/test-results workflows : version : 2 test : jobs : - test-app この設定内容自体については、同僚の @budougumi0617 さんが「 [Go]次世代イメージcimg/goとcircleci/go Orbsを使った2020年版CircleCIの環境構築 」で解説しているのでそちらをご覧になってみてください。 budougumi0617.github.io GitHub Actionの設定 .github ディレクトリ以下も、おまじないのように作るものが多い場所の一つでしょう。たとえば、 PULL_REQUEST_TEMPLATE.md などは初手で作ることが多いです。それに加えて、GitHub Actionの設定もある程度共有できるものが多くなるため、それらもTemplate Repositoryという形で共有しています。 ReviewDogによるgolangci-lint実行 GoコードのLintにgolangci-lintを用いているのですが、その指摘内容をGitHubのコメントとして反映するため、ReviewDogを用いています。 具体的には、 reviewdog/action-golangci-lint を活用しています。 github.com 実際に行っているのは、公式の解説どおりですが、以下のような設定を配置しています。 name : reviewdog on : [ pull_request ] jobs : golangci-lint : name : runner / golangci-lint runs-on : ubuntu-latest steps : - name : Check out code into the Go module directory uses : actions/checkout@v1 - name : golangci-lint uses : reviewdog/action-golangci-lint@v1 with : github_token : ${{ secrets.GITHUB_TOKEN }} golangci_lint_flags : "--config=.golangci.yml ./..." golangci-lint自体の設定もTemplate Repositoryに同梱しているため、レポジトリを作成すれば即座にgolangci-lintのチェック下に置かれます。 Hello WorldなGoコード Go Modulesを用いる前提で Hello World コードを用意しています。主にAPIサービスを作る際に、 app/ ディレクトリ以下にアプリケーションコードを置くことが多いため、 app/ 以下にmain.goを配置します。配置しているコードは、Go公式ブログの「 Using Go Modules 」に例示されているコードを微修正して置いています。 package main import ( "fmt" "rsc.io/quote" ) var ( revision = "default" ) func main() { fmt.Println(hello()) } func hello() string { return quote.Hello() + "by " + revision } また、テンプレ的に毎回用意するようなDockerfileも合わせて用意しています。 FROM golang:1.14.3-alpine3.11 as deploy-builder WORKDIR /app RUN apk add --no-cache git ENV GO111MODULE=on COPY . . ARG REVISION="default" RUN go build -ldflags "-X main.revision=${REVISION}" FROM alpine as deploy RUN apk add --no-cache tzdata && \ cp /usr/share/zoneinfo/Asia/Tokyo /etc/localtime && \ echo "Asia/Tokyo" > /etc/timezone COPY --from=deploy-builder /app/app . CMD ["./app"] これらでどう嬉しくなったか 上記で紹介した以外にも脳死してコピペしていた .editorconfig なども最初から用意されるので、新規リポジトリ作成時の心のハードルが下がりました。 これからの課題としては、既存リポジトリの知見をTemplate Repositoryに反映していく流れや、デプロイフローについてのある程度の共有を図りたいと思っています。 最後に Template Repositoryは、とくに特別な工数が発生するわけではない手軽な方法として、身近な面倒事を減らせる手になります。活用の仕方として参考になれば幸いです。
アバター
こんにちは。BASE株式会社 取締役EVP of Developmentの藤川(えふしん)です。 新型コロナの影響で、広告やインバウンドなど成長と期待されてきたWebサービスの分野によっては採用等を抑える動きがある中で、多くの商品ジャンルを取り扱えるECプラットフォームを担う当社の採用活動に関するスタンスは引き続きポジティブです。その中で、解決したい問題についてお話させていただければと思います。 技術課題1:コンポーネント化、アーキテクチャ刷新に対する学習コストへの適応 手前味噌ではありますが、「BASE」はサービスを止めずにアーキテクチャを進化させてきました。本来であれば、BASE バージョン2のようなものを別ラインで作って入れ替えるような作業を、CTO、テックリードの元で動的にサービスを入れ替える形で実現しています。 思い起こせば、元々の「BASE」は、よくもわるくも、ただのシンプルなCakePHP2を活用したWebアプリだったように思えます。ソースコードは多いながらも、一つ一つは低い学習コストのpost / getとデータベースへの入出力コードの繰り返し。これを駆使してサービスを作ってきました。 それ故に、オンボーディングのコストは比較的低かったものの、増えゆくソースコードの中で全体の冗長性、複雑性は上がっていくばかり。 開発工程が破綻しなかったのは「シンプル」を売りにするからこそのサービス性に恵まれたところは否めません。 しかし、サービスがシンプルであるということには、時に高度さを求められるところがあります。 ユーザーにとっては1クリックで実現できるところが、サーバサイド、すなわち裏側では実はそんなにシンプルではなかったというケースが存在し、それを増えゆくトラフィックの中で維持しつづけるのは意外と大変だったりします。 とりわけエンジニア視点では機能分割したくなる部分も、ユーザビリティのためにそうせずに頑張らなくてならない部分などが存在します。 言い方を変えると、他社であればスケーラビリティを優先した仕様変更を行っていたところを、対応コストをユーザー負担に押し付けるのではなく、どうにか1クリックのままで収まるようにユーザ体験を維持するためには、様々なシステムリソースと引き換えに頑張っていかねばならないシーンというのがあります。 このような部分は、実に細かいことですが差別化要素とも言えます。 そういう頑張りの結果、CakePHP2に依存するWebアプリケーションを作るという範囲のメンタリティだと、ソースコードの複雑性が高くなるところが出てきます。 そのような部分では、「BASE」を織りなすアーキテクチャとしてソフトウエア構造を整理し、メンテナンス性を高くしていかないと、技術的負債として将来の機能追加が破綻する可能性を秘めていたわけです。 このような話は、歴史を紡げるだけの価値を生み、常に発展を模索してきたWebサービスであれば、多かれ少なかれ存在しているはずです。我々も幸いなことに、このような改善を行う機会に恵まれました。   機能が整理されてサービスが高度化する代償としては、学習コストの増加です。 Amazon.comがRestfulなマイクロサービスの集合体になる前は、モノリシックなシステム構造の中で、データベースのマイグレーションに伴うトラブルが部署間で起きていて、その結果、トップダウンでデータベースを分割するマイクロサービス化を行ったと、Amazon CTOのWerner氏の講演を聞いたことがあります。 つまりそうすべき理由があったからというのが一番重要で、それをする理由も見えてない段階で高度化しすぎるのも開発コストや組織的なリスクをもたらします。 そうすべき理由とは、さらなるサービスの成長によってもたらされるものです。 このような構造下で、ビジネスとサービスを成長させるエンジニアリングの両方を満たしていくためには、技術系のリーダーシップを取っていく人員が不足しているように考えています。BASEは基盤整備とサービス性の向上との距離感が、比較的シームレスに存在しているのが特徴です。 これがゲーム基盤とゲームアプリケーションであったり、配信基盤とコンテンツなどと、キレイにレイヤー分けされていれば、もう少しメリハリの効いたチーム構成になるところが、まだ当社においては、そこまでは切り分けられていません。 冒頭に書いた1クリックによるユーザ体験を優先しているが故に、そんな簡単に組織をレイヤー化できないというのがあり、普通にサービスを拡張するにあたって必要な技術とサービス知識が、相応に多岐に渡っています。 少しややこしい言い回しをしてきましたが、Vue.jsやTypeScriptを書ければフロントエンドが書けるわけではないし、PHPやCakePHPが書ければサーバサイドが作れるわけでもなく、サービス知識と設計技術、基礎技術を兼ね備えていることが求められます。 こういった課題解決にピンと来る方に技術リーダーシップを発揮していただいて、チーム全体の技術力の底上げをしていける方を募集しています。 技術課題2:新型コロナで時間軸が圧縮されたことで見えてきたもの 5月15日に100万ショップを超えたことを発表させていただきましたが、ここしばらく新型コロナの影響で新規のショップ開設数が急増しております。また、香取慎吾さんを起用したテレビCMなどの影響もあり、引き続き増加しています。 4月後半、5月のゴールデンウイークでは、新型コロナに対する不安や、自粛に伴う巣ごもり消費によりサーバ負荷やトランザクション数が急増しました。 ショップオーナー様にはピークタイムの大規模販売を5分10分ずらしていただいたりなどご迷惑をおかけ致しましたが、社内では増えゆくPV、増えゆくトランザクションを安全に処理するために、CTOやSREを始めとしたメンバーが日夜、サービスを改善してきたという経緯があります。 その結果、単位時間あたりの処理トランザクション数や受け入れ可能なPVを一気に増やすことに成功し、受け入れ可能な流通総額の向上が成果として現れています。 某製菓会社様がコロナ禍で販売した応援セール商品について自社ECで処理しきれなかった販売が、急遽設立されたBASE支店では難なく処理できたというのも、この改善の成果です。 そもそも歴史ある企業様のネットショップが1日で作れるというのも「BASE」の特徴で、今後もこのような状況が増えていくことを望んでいます。 一方で、このまま時間軸が進んでいき、サービスが成長していくと、最後は、ものすごくプリミティブなところに問題が集約していって、将来に解決できない技術的な壁が存在するところも見えてきました。このような部分もさらなる改善でカバーしていく必要があります。 結局、Webアプリケーションというのは最後はTCP/IPでどう通信するか?という問題に落ちていくので、開発言語がGoだのPHPなどに留まらない視野での改善が不可欠です。mixiやFacebook、Twitterなどはその問題を解決してきたサービスなのだと思います。我々も、こういった問題もまた並列で解決していかねばならないイシューとして顕在化してきてワクワクしています。 今回の改善でしばらく時間稼ぎができましたし、5月は間違いなく異常事態だったと思いますので、新型コロナの第二波、第三波が来ても、物流や経済の流れというECの基本要素さえ壊れない限り、ウイズコロナでは問題なく「BASE」というビジネスを継続していく見通しは立ちました。 繰り返しになりますが、この短期的な時間軸の圧縮を経験し、3年後5年後に向けてのサービス成長への道筋もまた見えてきたというのがあり、これも取り組んでいきたい課題になります。 その際のお話は、弊社のオンラインイベントで、私とCTOの川口との対談でお話させていただくので、是非ご覧ください。 こちらから参加登録いただけます。 base.connpass.com サービス開発を通じて、人の成長とサービスの成長を一緒に実現していきたいと思いますので、長くBASEのチームで働いてくれる方を募集しています。
アバター
はじめに こんにちは、Data Strategy所属の岡です。グループ会社BASE BANKで分析/モデリングなども兼務しています。 テキストデータを特徴量にもつ不均衡データ分類問題をDNNで解きたくなった際、下記の論文を参考にしたのでその内容を紹介します。 https://users.cs.fiu.edu/~chens/PDF/ISM15.pdf 不均衡データ分類問題ってなに? 何かしらのカテゴリを機械学習などで分類予測しようとする際、カテゴリごとのデータ件数に偏りがある、特に正例のデータが極端に少ないケースで予測精度が上がりにくい、という問題をこのように呼んでいます。 例: 不正決済と正常な注文、不正商品と健全な商品、がん患者と正常な患者 普通はどうやって対処するの? ベースとなるアプローチは下記3つにまとめられます。 アプローチ 内容 デメリット アンダーサンプリング 多数派データをランダムに減らして少数派データと均一にする 多数派データの多くを捨てるため、情報損失が生じうる オーバーサンプリング 少数派を増やし、多数派データと均一にする 例: SMOTE, ADASYN 少数派データの水増しになるため、過学習が懸念される 損失関数のカスタマイズ 損失関数に対して多数派データのコストを少数派データとの割合に応じて割り引くなど あまりデメリットについて明言されてるケースは少ないと思いますが、これも過学習になりうるという印象 個人的には、アンダーサンプリングの情報損失という弱点をカバーしている under sampling + bagging をよく使用しています。DNNでこれと似たようなアプローチができないか調べていたところ、冒頭の論文を発見しました。 ミニバッチでunder samplingするアプローチ 今回紹介する論文では、ミニバッチ作成時にラベルごとのサンプルサイズを合わせる方法を提案しています。 この処理フローは下図のようにまとめられます。 まず、多数派データ(P個)のみをバッチサイズ(ここではN個)分に分割してミニバッチ作成します。ここでミニバッチ1つあたりのサイズは(P / N)個です。 続いて少数派データ(Q個)から(P / N)個を重複がないようにランダムサンプリングし、最初に作った1個目のミニバッチのデータに混ぜます。このサンプリングを続いて2,3,4...N個目のミニバッチごとに繰り返し、ラベルの比率が均等なN個のミニバッチが作られます。 注意点として、ミニバッチ1個あたりに対して少数派データ(Q個)から(P / N)個を選び出すときは非復元抽出となるため、ミニバッチ単体で見ればデータの重複はありません。ただし、別のミニバッチを作るときには同じようにQ個から(P / N)個選び出すことになるので、バッチ全体としては少数派データの重複を許しつつサンプリングされています。 実装例 PyTorchでの実装例を示します。図示したミニバッチ作成部分だけ簡略化したコードだと下記のようになります。 class BinaryBalancedSampler : def __init__ (self, features, labels, n_samples): self.features = features self.labels = labels label_counts = np.bincount(labels) major_label = label_counts.argmax() minor_label = label_counts.argmin() self.major_indices = np.where(labels == major_label)[ 0 ] self.minor_indices = np.where(labels == minor_label)[ 0 ] np.random.shuffle(self.major_indices) np.random.shuffle(self.minor_indices) self.used_indices = 0 self.count = 0 self.n_samples = n_samples self.batch_size = self.n_samples * 2 def __iter__ (self): self.count = 0 while self.count + self.batch_size < len (self.major_indices): # 多数派データ(major_indices)からは順番に選び出し # 少数派データ(minor_indices)からはランダムに選び出す操作を繰り返す indices = self.major_indices[self.used_indices:self.used_indices + self.n_samples].tolist()\ + np.random.choice(self.minor_indices, self.n_samples, replace= False ).tolist() yield torch.tensor(self.features[indices]), torch.tensor(self.labels[indices]) self.used_indices += self.n_samples self.count += self.n_samples * 2 コメントでも触れられている↓の箇所のコードが今回の肝となる操作です。 indices = self.major_indices[self.used_indices:self.used_indices + self.n_samples].tolist()\ + np.random.choice(self.minor_indices, self.n_samples, replace= False ).tolist() 多数派データのインデックス(major_indices)からは順番に選び出し、少数派データのインデックス(minor_indices)からはランダムに選び出す操作をしています。それぞれのラベルから n_samples ずつ取り出して結合しています。 この操作は後でイテレータとして用いるので、 def __iter__(self) で定義しています。 yield の出力はたとえば下記のようになります。 test_iter = BinaryBalancedSampler(features=train_features, labels=train_labels, n_samples= 50 ) for i in test_iter: feature, label = i print (feature.shape) print (label.shape) print (np.bincount(label)) break > torch.Size([ 100 , 29 ]) > torch.Size([ 100 ]) > [ 50 50 ] 1イテレーションあたりに特徴量データと対応するラベルを返しています。ここでは n_samples=50 としたので2ラベル分で100個のデータが生成されています。ラベルの分布も [50 50] と均一になっているので、想定通りのミニバッチが作成できていそうです。 実験 不均衡データを普通に学習させた時と、均一になったミニバッチで学習させた場合の精度の差を見てみます。 学習結果の全容は こちらのgit に載せたので、ここでは簡単に説明していきます。 前処理 サンプルデータセットにはtensorflowの下記チュートリアルと同じものを用いました。 https://www.tensorflow.org/tutorials/structured_data/imbalanced_data こちらも不均衡データをどのように分類するかという内容になっています。 import pandas as pd from IPython.display import display raw_df = pd.read_csv( 'https://storage.googleapis.com/download.tensorflow.org/data/creditcard.csv' ) display(raw_df.head()) 読み込んだデータは上記のような中身になっていて、 Class 列が分類対象のラベルです。 このラベルの分布が偏っていることを確認します。 import numpy as np neg, pos = np.bincount(raw_df[ 'Class' ]) total = neg + pos print ( 'Examples: \n Total: {} \n Positive: {} ({:.2f}% of total) \n ' .format( total, pos, 100 * pos / total)) > Examples: > Total: 284807 > Positive: 492 ( 0.17 % of total) 1のラベルが全体の0.17%しかないという偏り具合で、かなりの不均衡データになっています。 続いてチュートリアルと同じように前処理し、訓練データとテストデータに分割します。 from sklearn.model_selection import train_test_split # Cleaning cleaned_df = raw_df.copy() # `Time` カラムは不要 cleaned_df.pop( 'Time' ) # `Amount` カラムは数値のレンジが広すぎるので対数化 eps = 0.001 cleaned_df[ 'LogAmount' ] = np.log(cleaned_df.pop( 'Amount' ) + eps) # split train and test. train_df, test_df = train_test_split(cleaned_df, test_size= 0.2 , random_state= 0 ) # split label and feature train_labels = np.array(train_df.pop( 'Class' )) test_labels = np.array(test_df.pop( 'Class' )) train_features = np.array(train_df) test_features = np.array(test_df) 使用するネットワークはかなりシンプルにしました。 import torch import torch.nn as nn import torch.nn.functional as F class SimpleNet (nn.Module): def __init__ (self): super (SimpleNet, self).__init__() self.fc1 = nn.Linear( 29 , 32 ) self.fc2 = nn.Linear( 32 , 16 ) self.fc3 = nn.Linear( 16 , 1 ) def forward (self, x): x = F.relu(self.fc1(x)) x = F.relu(self.fc2(x)) x = torch.sigmoid(self.fc3(x)) return x 入力される特徴量が29ユニット、最終的に予測するラベルは0 or 1なので出力は1ユニットにしています。 その他、損失関数とオプティマイザーは下記を用いました。 import torch.optim as optim criterion = nn.BCELoss() # BCELoss: Binary Crossentropy optimizer = optim.Adam(simple_net.parameters(), lr= 1e-3 ) 普通に学習させた結果 学習部分のコードは下記のようになっています。 PyTorch特有の Dataset, DataLoader を使ってて見慣れない部分があるかもしれませんが、単純にミニバッチサイズは1000、エポック数は2で訓練させただけの普段通りの処理です。読み飛ばして構いません。 from torch.utils.data import Dataset, DataLoader class MyDataset (Dataset): def __init__ (self, feature, label): self.feature = feature self.label = label self.data_num = feature.shape[ 0 ] def __len__ (self): return self.data_num def __getitem__ (self, idx): out_feature = self.feature[idx] out_label = self.label[idx] return out_feature, out_label BATCH_SIZE = 1000 trainset = MyDataset(feature=train_features, label=train_labels) trainloader = DataLoader(trainset, batch_size=BATCH_SIZE, shuffle= True ) testset = MyDataset(feature=test_features, label=test_labels) testloader = DataLoader(testset, shuffle= False ) # training for epoch in range ( 2 ): running_loss = 0.0 for i, data in enumerate (trainloader, 0 ): inputs, labels = data # zero the parameter gradients optimizer.zero_grad() # forward + backward + optimize outputs = simple_net(inputs.float()) loss = criterion(outputs, labels.view(- 1 , 1 ).float()) loss.backward() optimizer.step() # print statistics running_loss += loss.item() if i % 100 == 99 : # print every 100 mini-batches print ( '[%d, %5d] loss: %.3f' % (epoch + 1 , i + 1 , running_loss / 100 )) running_loss = 0.0 print ( 'Finished Training' ) 次に学習し終えたネットワークでテストデータのラベルを予想させます。 pred = [] Y = [] for x, y in testloader: with torch.no_grad(): output = simple_net(x.float()) pred += [ 1 if output > 0.5 else 0 ] Y += [ int (l) for l in y] テストデータの予測結果をconfusion matrixで可視化すると下記のようになりました。 全データをラベル0(多数派)と予測していて、うまい具合に不均衡データの罠にハマっています。 ミニバッチでunder samplingして予測した結果 実装例のコードで書いた BinaryBalancedSampler を用いて学習させます。 少数派ラベルのサイズを考慮して、ここではミニバッチサイズは100、エポック数は2にしています。 balanced_net = SimpleNet() balanced_loader = BinaryBalancedSampler(features=train_features, labels=train_labels, n_samples= 50 ) criterion = nn.BCELoss() # BCELoss: Binary Crossentropy optimizer = optim.Adam(balanced_net.parameters(), lr= 1e-3 ) # training for epoch in range ( 2 ): running_loss = 0.0 for i, data in enumerate (balanced_loader, 0 ): inputs, labels = data # zero the parameter gradients optimizer.zero_grad() # forward + backward + optimize outputs = balanced_net(inputs.float()) loss = criterion(outputs, labels.view(- 1 , 1 ).float()) loss.backward() optimizer.step() # print statistics running_loss += loss.item() if i % 1000 == 999 : # print every 1000 mini-batches print ( '[%d, %5d] loss: %.3f' % (epoch + 1 , i + 1 , running_loss / 1000 )) running_loss = 0.0 print ( 'Finished Training' ) このネットワークでテストデータを予測させると下記のようになりました。 多数派ラベルの巻き込みも少なく(187 サンプル)、少数派ラベルの9割弱(88サンプル)を当てることができました。 実験のまとめ サンプルに使った不均衡ラベルのデータセットについては、ミニバッチでunder samplingするアプローチは有効そうです。 感想 この方法はミニバッチごとには均一なデータセットが得られますが、バッチ全体で見れば少数派データを複数回ピックアップするので、ある意味オーバーサンプリングとなり過学習の心配があります。early stoppingなどを組み合わせる必要がありそうです。 (あと発表が2015年とやや古い...) とはいえ実装はとても簡単なため、実務でサクッと試せる点が個人的に気に入っています。
アバター
こんにちは。バックエンドエンジニアの岡本です。 ネットショップ作成サービス「BASE」の新規機能開発や既存機能の改修・運用を担当するShop Groupに所属しています。 今回は私が入社後初めてアサインされたプロジェクトであるメールマガジンApp(※)のアップデートを通して経験したこと・考えたことをバックエンドエンジニアの視点から振り返っていこうと思います。 ※ネットショップ作成サービス「BASE」の拡張機能であるBASE Appsの機能の一つ プロジェクトメンバー構成 ・プロジェクトマネージャー ・デザイナー ・フロントエンドエンジニア ・バックエンドエンジニア(私) メールマガジンAppアップデートの場合、上記4つの役割に対して1人ずつメンバーがアサインされました。1人ずつと言ってもそれぞれの担当を全て1人でやるということではなく、入社間もないメンバーにはメンターがアサインされるなどフォロー体制が整っています。設計レベルからメンターや所属グループメンバーにアドバイスをもらいつつ、プロジェクトの主担当として作業を進めて行きます。 プロジェクトの流れ プロジェクトの流れとしては大きく下記の7つのフェーズに分けられます。 キックオフミーティング 設計・見積もり 実装 QAテスト リリース KPT(振り返り) プロジェクトお疲れ様会 1. キックオフミーティング プロジェクトの始まりにまずプロジェクトマネージャー(以下、PM)からプロジェクトの方向性・意味・想定仕様についての説明があります。PMが事前に用意したドキュメントをベースにざっと説明を行い、その後プロジェクトメンバー全員で不明点の確認や仕様の妥当性を議論していきます。 もちろん1度のミーティングでは仕様が確定しないことがほとんどなので、都度メンバー間でコミュニケーションを取りながら議論を深めていきます。 2. 設計・見積もり 大方の仕様が確定したタイミングでエンジニアは設計と見積もりを行います。BASEでは基本的にフロントエンドとバックエンドで実装担当が分かれているので、APIのレスポンス形式やバリデーションロジックについてそれぞれ意見を出し合い実装の方向性を決めていきます。 実装の方向性が決まった後、次は見積もりも含めた設計ドキュメントの作成に取り掛かっていきます。APIドキュメントについては API blueprint を利用し、その他バリデーションやDB関連の設計資料等は Kibela 記事にまとめていきました。設計ドキュメントが完成したタイミングで所属グループのメンバーにレビューしてもらいます。レビュー後のフィードバック対応が完了したらいよいよ実装フェーズに進みます。 3. 実装 実装フェーズは、 API実装 プルリクエストを作成し所属グループメンバーにレビュー依頼 フィードバック対応 レビュアーからLGTMを貰いレビュー完了 という作業をひたすら繰り返していきます。設計においても同様ですが実装フェーズにおいても所属グループメンバーからAPI単位で都度レビューしてもらえる環境があり、所属グループが支えてくれる感覚が実感としてありとても心強かったです。 4. QAテスト 開発が一通り完了し、プロジェクトはいよいよ佳境に入ります。QAテストはプロジェクトメンバー全員で分担し、それぞれが最終的なチェックを責任を持って行っていきます。比較的大きなデザインの課題が発見されたりするなど多少の問題は生じましたが、それぞれのメンバーがMove Fastに対応することで大きな滞りなくQAテストを完了させることができました。不具合の修正が完了したら後はリリース予定日を待つのみです。 5. リリース そして遂に2月3日(月)に本番環境へリリースし、特に大きなトラブルはなく無事に完遂することができました。リリース後は自分自身が関わったサービスを通してユーザー(オーナーズ)の活動を支えることができたという実感があり、エンジニア冥利に尽きるいい仕事だなとしみじみ感じました。 6. KPTでプロジェクトを振り返り 余韻に浸りたいところですがリリースが完了してもまだプロジェクトは終わりません。今回のプロジェクトから学びを得るべくKPTを行いました。内容としてはKPT(Keep・Problem・Try)ごとに一定時間(20分くらい)で意見を出し合い、最終的に皆で共有し今後のプロジェクトに生かせるようにドキュメントにまとめました。 <メールマガジンAppアップデートのKPT> Keep スケジュール通りリリースを完遂できたこと メンバー間でスムーズなコミュニケーションが取れたこと Problem QAテストで比較的大きい仕様の修正が入ってしまったこと Try 仕様のマスタドキュメント更新を全てのメンバーが責任を持って都度行う QAテストの段階で大きい修正が入らないようにできるだけ早い段階でテスト環境に動くソースをデプロイし、全てのプロジェクトメンバーが現状の実装状況を把握できるようにする 7. プロジェクトお疲れさま会 BASEには懇親会費用の補助制度があり、プロジェクト完遂後の打ち上げにもこの制度を利用することができます。プロジェクトの打ち上げは社内でも恒例となっており、今回は鉄板ステーキ屋に食事に行きました。入社後初プロジェクトをやり切った達成感からかはたまた高級店だった為か、お肉がとても美味しかったです。 プロジェクトを通して特に印象深かった事 改めてコミュニケーション大事 開発において手戻りを無くす為にエンジニア間のコミュニケーションは大事ということはもう周知の事実だと思いますが、やはり1番大事だなとプロジェクトを通し改めて感じました。コミュニケーションを密にとることの効用は何も認識齟齬だけではなく、信頼関係の構築という意味においてもプロジェクトの成功にとって避けては通れないものだと思います。 上記を意識してコミュニケーションを取った結果、今回のプロジェクトマネージャーからは下記の発言があり、プロジェクトを通して一定の信頼関係が構築できたと感じています。 またプロジェクトを通して弊社のSpeak Openlyという行動指針は何度も私の背中をそっと押してくれました。 「ここで確認するのはちょっと余計かな?」「この仕様少し疑問が残るけどどうしよう。」などと思った際、この行動指針を思い出すことで発言できる機会がいくつもありました。そして私のその発言に対してプロジェクトのメンバーはいつも真摯にHRTの精神を持って向き合ってくれました。 <HRTの精神> 謙虚(Humility) 世界の中心は君ではない。君は全知全能ではないし、絶対に正しいわけでもない。常に自分を改善しよう。 尊敬(Respect) 一緒に働く人のことを心から思いやろう。相手を一人の人間として扱い、その能力や功績を高く評価しよう。 信頼(Trust) 自分以外の人は有能であり、正しいことをすると信じよう。そうすれば仕事を自分以外の誰かに任せることができる。 出典: Team Geek ―Googleのギークたちはいかにしてチームを作るのか オーナーシップを持ってプロダクトを開発できるエンジニアは幸せ BASEには役割に関係なくプロダクトをより良いものにする為に仕様やデザインに対して積極的に意見することができる環境があります。今回のプロジェクトでの最も大きい個人的な気づきとして、エンジニアがオーナーシップを持って開発に取り組むことの楽しさがありました。前職では受託開発を中心に担当していた私からすると、この自由でかつ責任感が伴う開発の現場はとてもスリリングで心地良いものでした。 サービスを自分自身の課題として捉えることは開発に対しての強いモチベーションにもなり得ると私は思います。そして何よりエンジニア・PM・デザイナーなどの役割を超えて1つのチームとして同じ課題を解決していく過程はとても楽しく幸せなものです。 最後に BASEを一緒に支えてくれるエンジニアを絶賛募集中です!より良いプロダクトの開発の為に一緒に働きませんか? open.talentio.com
アバター
Native Application Groupの 大木 です。BASEでは、購入者向けのショッピングアプリ「 BASE 」、「 BASEライブ 」、ショップオーナー向けのショップ運営管理アプリ「BASE Creator」の3つのスマホアプリをリリースしております。今回は、その中の一つBASE Creatorを、React Nativeで置き換え、リリースしてみての話を、お伝え出来ればと思います。 課題と動機 https://help.thebase.in/hc/ja/articles/206417201-BASE-Creator- とはなんですか- BASE Creatorは、基本的にはWebViewで画面を表示するいわゆるガワネイティブアプリというものです。Webアプリとの違いは何処にあるかといいますと、商品が売れたり、購入者からメッセージが届くと、Push通知でお知らせ出来る機能があるところです。 アプリを運用していて出て来た課題としては、下記の点です。 現状、問い合わせによる不具合報告に対応するために、iOS/Androidのコードベースそれぞれを確認する必要がある スマホアプリは合計6つあり、チームメンバーも少数のため、WebViewベースのこのアプリは極力共通化したい。 これらの課題を解決するために、クロスプラットフォーム開発フレームワークの検討をしました。 React Native か Flutter か 開発プラットフォームとして上がったのは、React NativeとFlutterでした。どちらを選んでも問題は無さそうですが、今回はReact Nativeを採用しました。 重要視するポイント 既存技術を流用出来るか(社内、一般的なNative/Web技術含め) クロスプラットフォーム開発のノウハウが蓄積されているか 特に重要視していないポイント 統一されたガイドラインのようなものがある 新しい技術で開発 Flutterの方が後発で、挑戦してみたい気持ちもありました。しかし、Reactやnpmの技術を流用出来て、場合によってはWebのフロントエンドエンジニアでも対応できる可能性があるため、React Nativeを選択しました。 置き換えてみてどうなったか 結論から言うと、色々な良い影響をもたらすことが出来ました。 アプリケーションロジックを共通化出来た 最大の課題であった、 iOS/Androidそれぞれのコードベースを一つにする ことが出来、アプリケーションロジックを全てTypeScriptで記述することに成功しました。ネイティブ側のObjective-CやJavaのコードは、起動時に必要なコードやFirebase SDK用の初期化時コードくらいです。 別々のスキルセットを持つエンジニアとのコミュニケーションが増えた React Nativeで開発するとなると様々なスキルセットを必要とするため、Pull-Requestベースでレビューをもらいながら開発していきました。 メンテナンス可能なReact Nativeアプリを開発し続けるための構成を模索し構築できた Expoで管理するJavaScript/TypeScriptのみ動作させる環境を含むワークフローは"Managed workflow"と呼ばれます。対して、それを使わずNativeのIDE/SDKを使い、自分たちでReact Nativeの環境を整えるのを、Expoでは"Bare workflow"と読んでます。今回はFirebaseのNative SDKを含んだReact Nativeのモジュールを使うため、最終的にはこの"Bare workflow"でリリースする必要がありました。ただ、Expoの"Managed workflow"も素早く動作確認できる検証用環境として捨てがたいです。 幸いなことに、2019年はReact Native Re-Architecture実践の年で様々な改善をしていました。その動きに呼応するようにExpoもSDKに同梱されていたモジュールを分割するようになり、Expoベースの"Managed workflow"を使わなくても、Expoの便利モジュールを使えるようになりました。 両方の動作環境を一つのプロジェクトで共存させるために、 monorepo という仕組みを導入しました。この仕組みは、 Babel や React でも採用されています。 リポジトリのディレクトリ構成は、最終的に次のようになりました。 . ├── @types │   ├── metro-config │   └── react-web-vector-icons ├── docker │   └── android ├── packages │   ├── app │   ├── cli │   ├── components │   ├── core │   ├── expo-starter │   ├── functions │   ├── native-starter │   ├── platform │   ├── rn-metro-configurator │   └── tsconfig-paths-transformer └── tools ├── bin └── docker-bin また、分割したモジュールをどのように使うかの簡易的な概念図は次の通りです。 図の native-starter というモジュールが、通常のネイティブのIDE/SDKの設定と実装が必要な"Bare workflow"で動作させるためのエントリポイントとなっております。このモジュールからDebugビルドのアプリを起動したり、ストアリリースのためにReleaseビルドを作成したりします。また、 expo-starter というモジュールが、検証用にExpoの"Managed workflow"で動作させるためのエントリポイントとなっています。 monorepo構成で、React Nativeアプリを動作させるためには、色々なテクニックがいるのですが、長くなりそうなため、機会があれば別途解説記事を書ければと思っています。 Storybookの導入 React Nativeのコンポーネントを作成するに当たって、アプリケーションの特定の状態でしか発生しないUIの状態があり、その状況を再現したUIを素早く実装するためにStorybookを導入しました。 CodePushの導入 CodePush は、ユーザー端末に直接アプリの更新を配信できるMicrosoftのApp Centerのサービスです。リリース後、特定の古いOSでアプリが動作しない不具合が発生した時に、これを導入していたおかげで緊急アップデートをすることができました。 iOS 12.1以下で動作しないメソッドを使ってしまったため、CodePushで修正アップデート iOS 12.2以上でないと、 Object.fromEntires というメソッドは使えません。次のように修正し、 リリースした当日 に緊急アップデートで対処することができました。 @@ -42,14 +42,23 @@ export class FirebaseEventTracker implements EventTracker { } eventParams<T extends { [x: string]: string | number } = {}>(params: T) { - return Object.fromEntries( - Object.entries(params).map(([k, v]) => { - if (typeof v === 'string' && v.length > 0) { - return [k, v.substr(0, 100)]; - } - return [k, v]; - }) - ); + const values = Object.entries(params).map(([k, v]) => { + if (typeof v === 'string' && v.length > 0) { + return [k, v.substr(0, 100)]; + } + return [k, v]; + }); + + if (Object.fromEntries) { + return Object.fromEntries(values); + } + return values.reduce<{ + [x: string]: string | number; + [x: number]: string | number; + }>((acc, [k, v]) => { + acc[k] = v; + return acc; + }, {}); } React Native環境で利用できるJavaScript/npmの機能はできる限り試した これはチーム開発関係なく、個人的な興味になるのですが、ブラウザ環境でもNode環境でもないReact Native環境でどの程度最新技術をサポートしているのか、限界はどこかということを知っておきたいと考え、色々な検証をしました。 TypeScriptのDecorators導入 JavaのAnnotationのようなものが使いたくなり、TypeScriptの Decorators を導入しました。API Clientで、pathを定義したり、HTTP MethodがGETなのかPOSTなのかを定義したりで、次のような感じで使っております。 export default class StableVersionRequestService { private requestBuilder: RequestBuilder ; constructor( requestBuilder: RequestBuilder ) { this .requestBuilder = requestBuilder ; } @post @path ( '/path/to/check-api' ) checkVersion ( parameters: ServiceRequest < Data >) { // [...] IoCコンテナ(スプラッシュ画面の例) 複数のReact Native動作環境に対応するのであれば、当然使えるモジュールも違ってくることがありうると思いました。実際、アプリのスプラッシュ画面表示は、Expoの"Managed workflow"と"Bare workflow"とでは、利用できるモジュール異なることもあります。そこで、I/Fと実装を紐付けるためにIoCコンテナを導入しました。 まず、実装すべき共通のインタフェースを定義します。 export default interface Splash { preventAutoHide () : void ; hide () : void ; } export const SplashSymbol = Symbol. for( 'Splash' ); expo-starter では、Expoの SplashScreen が使えるので、次のように実装します。 import { SplashScreen } from 'expo' ; import { Splash } from '@universal-webcreator/app' ; export const splash: Splash = { preventAutoHide: () => SplashScreen.preventAutoHide (), hide: () => SplashScreen.hide (), } ; native-starter では、 react-native-splash-screen を使うように実装します。 import SplashScreen from 'react-native-splash-screen' ; import { Splash } from '@universal-webcreator/app' ; export const splash: Splash = { preventAutoHide: () => { // ネイティブ側のコード(Objective-C, Java)でスプラッシュを表示するため何もしない } , hide: () => SplashScreen.hide (), } ; そして、IoC Containerを使い、I/Fと実装を紐づけます。 export const dependencies = new ContainerModule ( bind => { // [...] bind < Splash >( SplashSymbol ) .toConstantValue ( splash ); // [...] } ); その他 その他、試して導入した機能は、主に次の通りです。 TypeScript 3.7 Redux/Redux-Saga React Hooks TypeScript Compiler API TypeScript Generics React Native開発の一年 この1年でReact Nativeならではの辛さや問題にも遭遇しました。 React Native環境構築 (2019年3月) 他のタスクがひと段落したため、本格的に環境構築を開始しました。公式ドキュメントを参考にしながら、ほとんどつまづくことなく開発することができました。 Jestを使ったスナップショットテスト を導入したり、CircleCIでlintやスペルチェックを回して、少しづつ環境を整えていました。 この時に使用した技術は次の通りです。 TypeScript v3.3.3333 Expo v32.0.5 Node v11.10.1 Jest v24.1.0 WebViewベースのアプリを開発するのに致命的な不具合を発見(2019年3月後半) 初回起動画面の実装やある程度開発環境も整え、要のWebViewを使った実装を行っていこうと考え、React Native本体の WebViewコンポーネント を使って動作確認しました。予想に反して、このコンポーネントには深刻な不具合があり、それにより、プロジェクトの存続すら怪しくなっていました。 完成度が低いReact Native本体のコンポーネントが、Communityに移管され難を逃れる 開発開始直後は、React Native関連の状況を理解していなかったのですが、2018年の Lean Core という提案により、WebViewの開発は、React Native Communityに移管されることに決定していたようです。 今のReact Native動作環境は、Expoの"Managed workflow"を利用しているため、すぐには移管されたWebViewは使えません。そのため、この環境を諦めるかどうかの選択に迫られました。そんな時、 Expo Developers のSlackにJoinしてみると、次のExpoのアップデートで、 React Native Community版のWebView に差し替えるという話があったため、他の開発をしながら、リリースを待つことにしました。 Expoのアップデートを待つ間に、他の機能を開発(2019年4月-2019年6月) 要のWebViewは不具合で開発を進められなかったため、他の機能を開発していくことにしました。待っている間に実装していた昨日は次の通りです。 IoCコンテナの導入 Core/Platformモデル・ユースケースの定義 Redux/Redux-Saga導入 CI用Node環境の整備 APIクライアント実装 文言の管理のため、i18next導入 TypeScriptのpathsによるモジュールパス解決に試行錯誤 一番最後のモジュールパス解決は試行錯誤していましたが、調べてみても、React Nativeのバージョンが古い時の方法であったり、パス解決の仕組みがよく分かっておらず、この時は断念しました。後日、最終的にMetroやBabelのモジュールパス解決の方法を組み合わせることで解決することができ、monorepo構成に移行するまでうまく動作しておりました。 Expo v33リリース!(2019年6月) 待てど暮らせど次期バージョンアップデートのアナウンスがなく、Expo DevelopersのSlackを定期的に覗く日々でした。様々な人が次のリリースはいつ?のような発言をしていた印象です。 いつものようにSlackをのぞいていると、いくつかのIssueを片付けたらリリースできるよと発言がありました。GitHubのリポジトリを確認してみると、すでにExpo v33があり、インストール可能な状態になっていました。 公式アナウンス を待ちきれず試してみると、普通に動くWebViewと対面することができました。 未知との遭遇(2019年6月) 基本的には動作していたWebViewですが、2つほど問題がありました。 target=_blank の挙動に準拠せず、勝手に同一WebViewで開いてしまう。 React Native側からWebViewでJavaScriptを実行しても実行結果を受け取れない。 これらの挙動のため、HTMLの何処に target=_blank のリンクが存在するか自分で確かめなければなりません。WebViewのJavaScript環境は、ユーザーの端末に依存するため、出来るだけレガシーなJavaScriptを書いてWebViewで動くようにしました。(ブラウザをアップデートしていないユーザーは古い機能しか利用できない)。また、BASEの管理画面は、 Vue + CakePHPで構築 されており、機能内のRoutingはVue Routerによる画面遷移で react-native-webview では検知できません。 そこで、 JavaScript : コールバックがないならDOMの変更を監視する を参考に、MutationObserverを使ったDOM変更監視で、Vue Routerでの画面遷移時に、 target=_blank のリンク取得するようにしました。 目的のリンクは取得できるようにはなりました。しかし、WebViewでJavaScriptを実行しても、実行結果を返すことはありません。一応 window.ReactNativeWebView.postMessage を使うことで解決が可能です。しかし、 window.ReactNativeWebView となっているので、 window オブジェクトにいつこれが設定されるかが問題で、ユーザーがリンクをタップするのが早いか、React Native側に実行結果を送信するのが早いかわからないという問題には、完全に対処できませんでした。 Expoの"Managed workflow"と"Bare worflow"を共存させる試みの失敗(2019年6月-7月) 結論から言うとこの試みは失敗しました。最初次の構成にすればうまくいくと考えました。 共通の部分は"Managed workflow"ベースで実装 それぞれの実行環境で必要な部分は共通のinterfaceを定義し、それに準拠させる実装をする 上の定義と実装を、ejsのテンプレートにし、コードジェネレーターやスクリプトで切り替える しばらく進めてみたのですが、ふと冷静に考えてみて、今後のReact Nativeのアップデートに追従していくためには、こんな複雑な仕組みメンテナンスできないと思い、考え直すことにしました。 monorepo構成の導入(2019年7月) しばらく色々試して迷走していましたが、1つのプロジェクトで複数のモジュールを管理できるような仕組みがあれば良いのではと考え、調査したところmonorepoについて知りました。monorepoを実現するツールの一つである Lerna を導入し、試してみました。この構成は現在も使っており導入してよかったと思いました。 実行環境の共存も、エントリポイントとなるモジュールを分割することにより別々に実行できるようになりました。 実行環境共通で使えないモジュールの解決は、共通interfaceを定義しそれぞれの実行環境ごとに実装したものを、IoCコンテナを通じてアクセスすることによって利用できるようになりました。この辺は 前述のIoCコンテナ(スプラッシュ画面の例) に例を載せています monorepo構成への完全移行とネイティブIDE/SDKの設定(2019年7月-8月) monorepoへ移行してからも、TypeScriptのpathsを使ったモジュール解決を使っていましたが、エディター上でモジュールをインポートしようとすると、ローカルPCのフルパスでインポートしたり、importの重複が発生したり何かと問題が発生していました。 pathsを使ってはいたのですが、そもそも機能ごとにグループ化し擬似モジュールのように扱っていたに過ぎません。モジュールのように扱っていたのであれば、monorepoの中でモジュールとして分割した方が管理しやすくなると考え、pathsで管理していたディレクトリーをそのままモジュールとして切り出すことにしました。これによってモジュールのインポート問題は解決することができました。 monorepoでJavaScriptレイヤーの問題は解決することはできましたが、ネイティブ側の設定はnpmのエコシステムに自動的に対応している訳ではありません。最終的に解決はできましたが、様々な問題が発生しました。それに関しては長くなるためここでは省略します。 毎回アプリを起動してUI実装するの面倒なためStorybookを導入(2019年8月-9月) 基本機能はほぼ出来上がっていましたが、次はUIの微調整が待っていました。毎回アプリを削除して、目的の画面まで遷移して特定の状態にして確認という作業を、繰り返していたため、フラストレーションが溜まっていました。 そこで、UIの状態を固定で確認できれば、アプリの実行環境で確認しなくてもいいのでは?と考えStoryboardを導入しました。結果的に、ブラウザ上で素早く確認できるようになりました。ちなみに、Storybook for React Nativeは使っておらず、Storybook for Reactをベースに react-native-web を利用しました。 リリースビルドをCircleCIで成功させるのに四苦八苦(2019年9月-11月) アプリのテスト配信には、Microsoftの App Center を選びました。React Nativeアプリの場合、CodePushを使えるのが採用した大きな理由です。 さて、配信サービスが決まったので、CircleCIでリリースビルドを作成してみるとなかなか上手くいきません。しかも一回のビルド時間が40分以上かかり確認にも時間がかかります。ここをクリアしないとリリースもできません。 絶対にローカルPCでリリースビルドを作るという作業はしたくありません。 この作業では、次のような問題が発生しました。 Androidビルドで、Javaメモリーエラーが発生する ビルドは作成できたが、署名が異なるため、端末にインストールできない アイコンフォント対応ライブラリで、リリースビルドのみアイコンが表示されない ビルド時間が長い またこの時期、iOS 13へのアップデート対応で、他アプリの対応もしなくてはならず、なかなか時間が取れませんでしたが、一つ一つ解決していきました。 QA(2019年11月後半) iOS 13へのアップデートもひと段落し、やっとリリース準備に漕ぎ着けました。- Done is better than perfect - のようにまずは終わらせろの気持ちでいた私ですが、全体を通しての動作確認はしておりません。そう、QAをしなければなりません。 QAフェースでも色々問題が発生しました。これは主にAndroidの知識不足が原因でのことです。 Androidのバックボタンでホームに戻って、アプリを再度開くと、React Nativeのコンポーネントが再生成されるのに、UI周りの再初期化できてない 不要な権限をAndroidManifestで消せてない また、QAを行なっていると、利用しているライブラリに関しても色々と気づくこともあり、修正Pull-Requestを送ったりしました。 use lane_context for android mapping text [expo-web-browser] [ios] Fixed SFSafariViewController presentation style リリース(2019年11月末) まだまだ、改善するところはあったのですが、紆余曲折を経て、リリースすることができました。 2019/11/27 Play StoreでAndroid版リリース 2019/12/2 App StoreでiOS版リリース リリース後の色々(2019年12月-) リリース後、Play StoreやApp Storeに再申請するほどの深刻な不具合が発生しなかったことにまずは安心しました。ただし、ユーザーに影響のあるものや運用開発環境の考慮漏れなど、いくつかの問題は発生してしまいました。確認不足やAndroidの知識不足に起因するものが多かったです。いくつか上げると次のようなことです。 古いOSで動作しないJavaScriptの機能を使ってしまっていた Android Crashlyticsの設定ミス Android CodePushの設定ミス まとめ 今回のプロジェクトでは、本当に多種多様な技術に触れ、大変なことも多かったのですが、様々な人の助言もありなんとか終わらせることができました。また、長期的な運用やチームでの開発を考えた実装を深く考えられた良い機会でした。React Nativeだからネイティブのことを考えなくてよい訳ではなく、ネイティブ起因の様々な問題が発生しました。その時には同じチームのAndroidエンジニアにも度々助けられました。 今、React NativeやExpoを取り巻く環境は、2019年の一年間で大幅に変化しそして改善しています。先日、 Shopifyの記事 にReact Nativeのことが書かれていました。2020年の今なら、React Nativeを採用するのも悪くない選択肢かなと思います。
アバター
この度は、2/9(日)~2/11(火)に練馬区の産業プラザ Coconeri ホールにて開催された PHPerKaigi2020 にプラチナスポンサーとして協賛し、また4名のメンバーが登壇しました。 今回は上記メンバーの他に一般聴講者として参加した4名のメンバーからの参加レポートをお届けします! 参加レポート(栗田) 概要 Service Dev Sectionに所属しています 栗田 です。 2020年2月9日(日)〜2月11日(火・祝)に開催されたPHPerKaigi 2020に聴講者として参加しました。 この記事は2日目の 2月10日(月)の参加レポートとなります。 当日は弊社から2名のトークセッションがあったりと、自分の知り合いのトークセッションがあり 盛りだくさんでした。 セッションを聞いたり、おかしをもぐもぐしたり、参加者と交流したり 茶会に参加したりなど充実した1日を過ごしました! 本文 練馬で行われた会場に到着 ここで受付です。 色々な会社のノベルティや自分専用にカスタマイズされたトレーディングカードもいただきました。 まずはじめにオープニングトークに参加しました! カンファレンスに対する想いが心に残りました。 今回弊社はPlatinumスポンサーとして協賛しております。 自分の会社のロゴが大勢の場の前に出るのは純粋に嬉しいですね。 同僚の @o0h_ さんのトークセッションに参加!! 続いて同僚の @budougumi0617 さんのトークセッションに参加!! 弊社から連続のトークセッションになり勢いを感じました。 ひと休憩して、ランチセッションではお弁当をいただき 午後は↓などに参加しました。 レンサバけもの道 磯野ー、MySQLのロック競合を表示しようぜー もっと気軽にOSSにPRを出そう! 知らないWebアプリケーションの開発に途中からJOINしたとき、どこから切り込むか? LT... etc などを聞きました。様々なスタイルのセッションがありどれも為になるお話でした。 詳細はリンクよりご確認ください。 最後に茶会に参加して参加者と更に交流を深めました。 ※ GMOインターネット株式会社さまからのご提供でした。 茶会にはボドゲなどの交流グッズなどもあり 様々なスタイルで交流する事ができました。 名刺代わりに交換をしたトレーディングカードも大分集まりました! 最後に 自分はPHPカンファレンスの参加経験はありましたが、PHPerKaigiは初の参加でした。 一言で感想をいうとアットホームな温かい場だと感じました。 ・初心者、上級者、老若男女 誰でも楽しめる場 ・コミュニケーションの場 ・1年に1回、ともだちと会う場 とオープニングセッションで紹介がありましたが、まさにそのとおりだと感じ コミュニティの暖かさを感じました。 自己紹介がしやすいようにトレーディングカード風の名刺をいただいたり スタッフの方がフレンドリーに話しかけてくれたり いたる所で参加者同士で気軽に交流を行われたり PHPerトークンを探しだす、PHPerチャレンジイベントなどもあり 初めての参加でも参加しやすいカンファレンスだなと感じたのと コミュニティの熱量を感じた1日でした。 参加レポート(長澤) SREに所属していますngswです。得意な領域はインフラ側で、前職でPHPも触ってきました。 今回はインフラ側への言及があった以下の3トークについて感想を書きたいと思います。 レンサバけもの道 by uzulla | トーク | PHPerKaigi 2020 #phperkaigi - fortee.jp 知らないWebアプリケーションの開発に途中からJOINしたとき、どこから切り込むか? by 小山健一郎 | トーク | PHPerKaigi 2020 #phperkaigi - fortee.jp PHPシステムをコンテナで動かすための取り組みのすべて by きくもと | トーク | PHPerKaigi 2020 #phperkaigi - fortee.jp レンサバけもの道 uzullaさん (@uzulla) / Twitter レンサバけもの道 - Speaker Deck 僕は以前ホスティングサーバを提供する側にいたことがありますので「これは他人事ではない」というトークでした。 レンサバ == 共用サーバの旨味である「アップデート追従などのマネジメント」と、その反面にある「使い勝手に関わってくる各種制約条件をどう打破するか」という内容です。SSHから始まり最新のバイナリ利用のためのビルドのtipsなど、自由度の高いセルフマネージドなVPSを渡り歩いて来たからこそ語ることのできる内容が盛りだくさんで聴き応えがありました。このトークの背骨となる「モダンを諦めない」「レンサバでもここまでできるんです」という主張はとてもエンジニアしていて好感が持てました。 知らないWebアプリケーションの開発に途中からJOINしたとき、どこから切り込むか? k1LoWさん (@k1LoW) / Twitter 知らないWebアプリケーションの開発に途中からJOINしたとき、どこから切り込むか? / PHPerKaigi 2020 - Speaker Deck 「既存プロダクトにアサインされたとき、どうすれば開発参加の初動オーバーヘッドを小さくできるか」という主旨のトークでした。 どのようなサービスか どのようなアーキテクチャか どのようにデータを保持するか(=どのようなテーブル設計か) どのような開発環境か(本番環境・開発環境) どのようなコードか 開発 https://speakerdeck.com/k1low/phperkaigi-2020?slide=28 「上記の6つのレイヤの情報が揃えば、あとは本人のスキルにあわせて成果が出せるよね」ということでした。 本質に近いもの(発表では上述した数値の小さい事柄のもの)ほど広い理解を要求される点も共感できる内容でした。 トークの最中「われわれのようなインフラ側の人間だとどういう情報があれば初動オーバーヘッドを小さくできるのかなあ」と考えていました。 個人的な経験では「どのようなサービスか」ということにはあまり頓着しなかった思い出があります。自分が一番はじめにほしいなと思う情報は サービスに利用されるFQDN一覧 サービスが利用するクラウドサービスのエンドポイント一覧 インスタンスの構築手順(コード化されたものも含む)、ないしは相当のものが存在しなければ踏み台へのログイン情報 だと考えています。少なくともこれだけあれば、あとは稼働しているデーモンとポートの関係などはログインして調べてしまえばいいじゃないかと考えてしまう質です。 ただこれがまさにオーバーヘッドなのですよね。そういった意味ではk1Lowさんの発表は高みを目指されていて凄みを感じました。またこの「初動オーバーヘッドを小さくする」ことに成功すれば「採用計画もより柔軟にできるのではないか」という発展性も感じました。われわれの業界はただでさえ即戦力を求めがちなのですから、「単なる一トークではない業界への提言ととってもいいのかもしれない」という気持ちを持ちました。 PHPシステムをコンテナで動かすための取り組みのすべて kikumotoさん (@takakiku) / Twitter PHPシステムをコンテナで動かすための取組みのすべて - Speaker Deck さまざまなツールを駆使してコンテナ化を実現されたその手法と、実際の運用に基づく含蓄のあるアドバイスが詰まっていて、 AWS移行を目指す中、オンプレミスでどこまで移行コストを減らす形にできるかという工夫に満ちたトークでした。 個人的に一番興奮してしまったのは「LVS」の話題が上がったところで、そこでテンションが上がりすぎてしまった結果、最後に変な質問を投げかけてしまったのでその点は個人的な反省点です。 ただLVS氏はものすごく賢くて、当該レイヤでやってほしいこと、これできたら嬉しいなってことをほんとに全部実現してくれるツールなので、テンション上がってしまったことは許していただければ幸いです。 参加レポート(炭田) 概要 Service Dev Sectionに所属しています@tac_tandenです。PHPで3年ほどWebアプリケーションの開発をしています。 自分は2020年2月9日(日)〜2月11日(火・祝)の3日間に参加しました。 カンファレンス初心者の自分にとって、一般聴講者としての参加であっても、最初は入っていけるのか正直不安でした。 しかし、PHPerKaigi 2020は運営のスタッフの方や参加者のみなさんはとてもオープンで、親切で、すごく楽しく素晴らしい経験になりました。 また、今回のPHPerKaigiでは、弊社から4名登壇されていて、登壇がより身近になりました(自分もいつかカンファレンスで登壇してみたいと考えています。) 今回の記事では、アプリケーションエンジニアの自分にとって印象に残った以下の2つのトークについて感想を書きたいと思います。 Deep Module in PHP もっと気軽にOSSにPRを出そう! 本文 Deep Module in PHP いとしょさん (@itosho) / Twitter Deep Module in PHP - Speaker Deck アプリケーションの実装をしていて、実装期間や仕様、依存関係などの制約がありつつも、もっと良いコードを書けないかなと日々試行錯誤されている方も多いと思います。 自分もその一人で、その中で「そもそも良いコードってなんだろう」と自問自答して色々調べたりすることも多いのですが、itoshoさんの発表ではその「良いコード」の一つの指針として、「Deep Module」という考え方があるということを紹介されていました。 発表の中で、良いコード=複雑ではないコードと定義して、多くの機能を提供しながらインターフェースがシンプルなモジュール(=Deep Module)を理想とするという指針を提示されていました。また、実際にDeepなModuleを実装するためのポイントや具体例もあり、とても参考になりました。 設計としての理想を追い求めるよりは、より現実に即した適用しやすい指針なのではないかと個人的には感じます。 Deep Moduleという視点も持って、会社のアプリケーションコードを読む&開発してみたいと思います。 もっと気軽にOSSにPRを出そう! DQNEOさん (@DQNEO) / Twitter もっと気軽にOSSにPRを出そう! - Speaker Deck 日々会社や個人で開発をしていて、OSSにお世話にならない場面はほとんどないと思います。 しかし、OSSへのコミットやPRを出すとなると、自分としては途端にハードルが高くなる気がしていました。 発表の中で、DQNEOさんもハードルの高さを指摘されていましたが、以下の4つの視点を持つことでPRを出しやすくなるとご紹介されていました。 1. 作者の関心ゾーンの外側を見る 2. 使っていなくてもコントリビュート 3. 業務で得たノウハウを横展開 4. ダメ元でも送ってみる スキルアップなどももちろんですが、OSSに日頃お世話になっている「恩返し」として、自分でも微力ながら気軽にPRだしてみようと思います。 また、発表セッションのあとの懇親会でも、DQNEOさんには面白いお話をたくさん聞かせていただきました! ありがとうございました! 発表を聴く中で感じたこと 登壇に関して、人脈を作ったり市場価値を上げるなどいうよりも、単純にすごく楽しそうだし、沢山のエンジニアの皆さんとつながることができるのは、素晴らしい体験なんじゃないかなと感じました。 なにより、登壇されている皆さんが、発表をとても楽しんでいるのが印象的でした。 途中で「なにかネタはないだろうか」と色々頭を巡らせていましたが、皆さんの発表を聴く中で、こうすれば発表できるトピックが作れるのではないかと考えました。 OSSにPRをだしてみる 会社での経験・知識を一般化して横展開できないか考える 自分のサービス開発を運用する 技術ブログを書く また、登壇された皆さんの発表がすごくわかりやすく、楽しめたのですが、聴講者目線で印象に残った登壇テクニック?をまとめてみました 発表に入る前に、発表と関連する経歴や自己紹介があると、なんでこんなことをしているか、なんでこんな発表をしているかがわかりやすくて内容が入ってきやすくなる。 他の発表との関連にも言及してみる。他の発表の知識が使えてより理解しやすくなる気がする。 聴講者にフィードバックが欲しいところを発表の途中途中で伝えてもらえると、発表者にフィードバックしやすい。 発表の中で聴講者に手を上げてもらうような質問をちょいちょい入れていくと、ライブ感がでる。 今後、発表する機会があったらぜひ実践してみたいと思います! 参加レポート (tenkoma) 基盤グループ所属の田中(@tenkoma)です。 3回目のPHPerKaigi参加となります。 参加したセッションの感想とPHPer Code Golf についてレポートします。 知らないWebアプリケーションの開発に途中からJOINしたとき、どこから切り込むか? 知らないWebアプリケーションの開発に途中からJOINしたとき、どこから切り込むか? / PHPerKaigi 2020 - Speaker Deck セッションを聞いた理由 自分が途中からプロジェクトに参加するときにどんな情報をあつめればいいのか、最近考えたことがない気がしたから 新しくjoinしてくるメンバーをサポートするための良い方法が知りたかったから でした。そんな疑問を考えるヒントが得られるよいセッションでした。 今だからこそ振り返る register_globals 今だからこそ振り返る register_globals / PHPerKaigi 2020 - Speaker Deck セッションを聞いた理由 最近のPHPって型(カタ)すぎませんか!?(型を使いすぎている、とか思っているわけではありません) 「例えば、PHPを避ける」なんて言われていた時代に、少しの時間でも戻ってみたかった 僕がPHPを使い始めた頃は古くてPHP4.3 を使っていたくらいの時期で、 register_globals = Off が既定値でした。 register_globals = On のアプリケーションを扱ったことはありませんし、 register_globals = On はセキュリティ上問題があるのでOffにするように社内情報共有されていました。 そのためこのセッションは、仕事に生かすかどうかも期待せず、ほぼ息抜きとして聴いたセッションでしたが、PHPの思想に触れることができる味わい深いセッションでした。 PHPer Code Golf by Pixiv 前回は全然参加できなかったな、今回はがんばってみようと思い、 PHPerチャレンジ でコードを探し入力していました。 しかし、1日目にPHPer Code Golf が始まってからは、そちらへの参加がメインになってしまいました。3問ありましたが、正解後もずっとコードを縮めることに注力した結果、表彰いただくくとができました。 PHPer code golf 賞ゲットしました!!pixivさん、楽しい問題を用意していただいてありがとうございます! #PHPerKaigi pic.twitter.com/1v6zlRG7rE — /; SameSite=Tenkoma; Secure (@tenkoma) February 11, 2020 正解するだけで無く、スコアを改善するためには途中のコードをどこかに保管しながらになるのですが、社内の #times_tenkoma にコードスニペットを投稿しながら改善していました。 1日目、帰宅後にベッドの上でFizzBuzzを改善していたときのログ 2日目、クロージング(17:10〜)の直前まで、上級編のコードを改善していたときのログ PHPerチャレンジは11位という結果に終わり残念でしたが、こちらで結果が残せて良かったです。 PHPerKaigi はセッションやIRTはもちろん、懇親会、PHPerチャレンジなど、他の企画も力が入っていて楽しめました。 賞を頂いたときのコードについては個人のブログで ⛳ PHPer Code Golf by pixiv(PHPerKaigi 2020) 上級編の回答について解説 の記事を書きましたので、そちらもご覧ください。 登壇資料 めもりー @m3m0r7 金城 @o0h_ 清水 @budougumi0617 東口 @hgsgtk 謝辞 今回弊社は登壇者含めて計9名のメンバーで参加させていただきまして、その中にはカンファレンス初参加のメンバーもございました。それぞれに刺激を受けたり、持ち帰れるものを得たりして大変実りのある時間を過ごさせていただきました。 それも実行委員長である 長谷川さん をはじめ、実行委員会の皆様のおかげです。心より御礼申し上げます。来年の開催は未定とのことですが、心より楽しみにしております。誠にありがとうございました!
アバター
こんにちは、BASE BANK株式会社 Dev Divisionにて、Software Developerをしている東口です。先日、2020年2月16日に開催された、 Object-Oriented Conference にスピーカーとして参加してきました。このカンファレンスには、BASEがブロンズスポンサーとして協賛しています。 非常に高度な内容がタイムスケジュールに並んだ濃密なカンファレンスで、オブジェクト指向や設計話にどっぷり浸かる一日になりました。 Object-Oriented Conferenceとは お茶の水女子大学で開催されたこのカンファレンスは、オブジェクト指向をテーマにした技術カンファレンスです。 ooc.dev Object-Oriented Conference はオブジェクト指向をテーマに、あれこれ共有したり、セッションを聴講することで、みなさんの知見を深めるためのイベントです。 冒頭に書いたとおり、BASEは、ブロンズスポンサーとして協賛しました。 発表内容:デザインパターンの使い方を パタン・ランゲージとの比較から考える 冒頭に上げていますが、この内容の課題感は自分自身が感じる「デザインパターンに対するもやもや感」でした。 GoF(Gang of Four)のデザインパターンは、我々が日々行う設計行為において基礎的な要素を占めています。ですが、一方で「デザインパターンをなんとなく知っている」状態から「デザインパターンを正しく使う」状態へ向かうには、大きな壁があると感じています。 正しく使うためには、その対象を知らないといけないですが、その切り口として、「デザインパターンの前」を見てみようという内容です。それが、パタン・ランゲージであり、この発表はパタン・ランゲージを理解し、その違いを理解することで、デザインパターンとの付き合い方を整理しようとしています。 発表後、ありがたいことにさまざまフィードバックを頂きました。 人は解決策をすぐに求めがちだけど、コンテキスト・問題を正しく理解してどうやって自分が解決策を導くのか、が大事なんだよなぁ #ooc_2020 #ooc_e — isop (@isop) February 16, 2020 『オブジェクト指向のこころ』をまた読み返したくなる素敵なトークだった #ooc_2020 #ooc_e — null | undefined | (@cheezenaan) February 16, 2020 動画で見たのですが、すごく良いセッションでした!問題提起から、数々の文献にあたり、最後に東口さんなりの結論を導きだしたのがとても良かったです。おつかれさまでした :) — Masashi Shinbara (@shin1x1) February 16, 2020 裏話をすると、発表時間を15分〜20分で、パタン・ランゲージとデザイン・パターンを比較するという性質上、パタン・ランゲージとその発想の背景にあるクリストファー・アレグザンダーのデザインに対する考え方を、ぎゅっと短い時間でまとめ上げることが必須で、その整理に非常に工面し、最後の最後まで時間をさきました。 10分でわかるアレグザンダー感がある。いいな。 #ooc_2020 #ooc_e — きょん@アジャイルコーチ、システムアーキテクト (@kyon_mm) February 16, 2020 「クリストファー・アレグザンダーの考え方に精通されている」と僕が認識している @kyon_mm さんにこう言っていただけたので、なんとかうまくいったようで安堵しています。 カンファレンスでアウトプットすることの醍醐味の一つですが、聴講頂いた方のコメントから、さらに関連する知識もいただけました。 アレグザンダーの『ノート』におけるデザインの3段階のモデルとアラン・ケイの"Doing with Images makes Symbols” のスローガンの心理学的な根拠が両方ともジェロム・ブルーナーの子供の認知機能の発達の過程という発見がそういえば最近あったことを思い出した。 https://t.co/HLbiHkOzE4 — Akira Motomura (@akira_motomura) February 16, 2020 とてもよかった ところでパターンが先になくとも原則に従ってリファクタリングする中で自然にデザインパターンが現れてくることを、Uncle Bobはアジャイルソフトウェア開発の奥義の中で「コードがデザインパターンに回帰していく」と表現していました。 #ooc_2020 #ooc_e https://t.co/FjFKWMHb19 — takasek (@takasek) February 16, 2020 発表したことによって、関連する新しい情報をもらえるのは本当にありがたいことです。この他にもたくさんのフィードバックを頂きました。貴重なお時間をいただけたこと、この場を借りて御礼申し上げます。 まとめ Object-Oriented Conferenceは今年初開催のカンファレンスでした。オンラインでの視聴者込みで1000人超えの来場者数で、5トラックある非常に大規模なカンファレンスにもかかわらず、非常に楽しく1日過ごさせていただきました。主催者の @hirodragon112 さんを始め、運営スタッフのみなさま誠にありがとうございました。
アバター
2020 年 3 月 28 日に開催される YAPC::Kyoto に BASE 基盤グループのエンジニアである めもりー が登壇します。 YAPC とは YAPCはYet Another Perl Conferenceの略で、Perlを軸としたITに関わる全ての人のためのカンファレンスです。 Perlだけにとどまらない技術者たちが、好きな技術の話をし交流するカンファレンスで、技術者であれば誰でも楽しめるお祭りです! 弊社で使用している技術スタックではないカンファレンスでの登壇は数多くなく、珍しい試みとなります。 セッションについて 「Perl 初心者の私が YAPC のために Perl で JVM を実装してみた」 2020 年 3 月 28 日 登壇者名: めもりー 10:50 開始 ルーム1 トーク内容 みなさんは JVM (Java Virtual Machine) を実装したことをありますか?私はあります。 ただ、それは Perl ではなく PHP と JavaScript で、です。 YAPC のために今まで使ったことのない言語である Perl を使って JVM を実装してみようと思います。 本トークでは、 Perl でどうやって実装したか、苦労した点は何か、そして Hello World を出力するところまでデモンストレーションできればと思っています。 最後に 下記から YAPC への参加申し込みが可能です。 yapcjapan.org
アバター
こんにちは。BASE株式会社でProduct Management Groupのマネージャーをしている山田です。 12/1から始まった「BASEアドベントカレンダー2019」はお楽しみいただけましたか?私の記事が最後の記事となります。 今年は約50名のメンバーが執筆に参加しました。エンジニアだけでなくデザイナーやProduct Managementのメンバーも筆を執っています。個性豊かな記事が集まっているので、ぜひご覧になってみてください。 devblog.thebase.in さて、私が最後の記事「閉会式」を担当するということで、簡単に自己紹介をさせていただきます。私は2019年4月に入社し、BASEは3社目となります。簡単な経歴は、新卒でワークスアプリケーションズ > リクルート > BASEとなります。1社目はエンジニアとして、2社目ではUI/UX、プロダクトマネジメントを担当しておりました。 入社して半年がたち改めて振り返ってみると めちゃ楽しかったなー というところなのですが、なぜBASEでのプロダクト作りが楽しいのかをさくっと振り返ってみます。 オーナーズが圧倒的に素敵 BASEではショップを開いてくださっている方をオーナーズと呼んでいます thebase.in オーナーさん達は作っている商品だけでなく生き方も素敵で、自分たちが作っているプロダクトのユーザーをむちゃくちゃ好きになりながらプロダクトを作れることって楽しく尊いことだなと思いながらプロダクトを作っています。 社内でも「それってオーナーズのためになってるんだっけ?」とプロダクト作りをする上での主語が自分たちでなく、オーナーズになっていることも、当たり前のように根付いて、じんわりいいなーと日々感じています。 変化 「BASE」というプロダクトは、インターネットが当たり前にある世界となり、新たな生き方/働き方ができる人が増えたことで、成長しているプロダクトだなと思っています。その世の中の変化の先っちょを肌で感じながらプロダクトを作っています。 先っちょであるが故に、会社が成長し、プロダクトが成長が成長し、そして上場したり、競争環境が変わったりと色々な変化がすごいスピードで起きています。 その変化に追いつくためには、自分達自身も変化し続ける必要があり、それはなかなか出来ない体験で、大変なことも含めて日々めちゃくちゃ楽しいです。 BASEには明文化された行動指針があります。 Be Hopeful:楽観的でいること。期待した未来は実現すると信じて、勇気ある選択をしよう。 Move Fast:速く動くこと。多くの挑戦から多くを学ぶために、まずはやってみよう。 Speak Openly:率直に話すこと。より良い結論を得るために、その場で意思を伝えよう。 入社当初は文化が浸透していていいな、と思っていたくらいだったのですが、改めてこの変化の激しい環境に適応していくためには、この行動指針はすごく良いなと最近より実感するようになりました。 Move Fastかっこいい 終わりに そんな感じで、BASEでのプロダクト作りを楽しんでいます。 私たちが目指してる世界に僕らはまだ圧倒的にたどり着いていません。 まだまだプロダクトも僕らも世の中も変わり続けて、ただその変化の先っちょいい続けるために突き進んでいければと思います。 こんな環境でプロダクト作りしたい仲間を絶賛募集中です! binc.jp これでBASEアドベントカレンダー2019は閉会です。読んでくださったみなさんありがとうございました!
アバター
  アドベントカレンダー最終日です!BASEで代表を務めている鶴岡です。宜しくお願いします。 いつもこの時期になると、ありがたい事にメディアさんから来年はどんな年になると思いますか?といったアンケートを頂くことがあります。 そもそもこういった質問にお答えすること自体あまり得意ではないのですが、一番悩む質問が、今年読んだ本の中で一番良かった本を教えてください。というものです。 完全に僕の課題なのですが、僕は本を読むのが苦手で、いまいち周りの起業家のようなカッコいい答えが思い浮かばないのです… そう話すと、周りの人たちからどうやって情報をインプットしているのかとよく聞かれます。 ある時、自分でも確かになと思い、よくよく考えた結果、僕は周りのコミュニティに助けられて学ばせてもらっているのだと気がつきました。 僕は本当にコミュニティに恵まれています。 まずコミュニティに恵まれた第一歩はインターネットでした。インターネットに本当に多くの価値観を学びました。CAMPFIREでインターンというキッカケをくれたのもインターネットです。インターネットさいこう! その後は、CAMPFIREでインターンをしていた都合上、この業界に来た時からインターネットが好きで好きでたまらないスター達に可愛がっていただき、気がつかぬうちに、完全にインターネットが好きになって作る側に回りたいと思っていたし。 ある時からは、気がつかぬうちにプロダクトを作るコードを書くのが好きな人たちに囲まれて、一晩中コードを書いたりプロダクトの未来を考えるのが好きになっていたし。 ある時からは、投資家に囲まれ、ファイナンスの方法を学び、企業や組織の成長のさせ方に興味が持てていたし。 ある時からは、初めには出会えないような先輩経営者に会えるようになり、大きな夢を見る大切さや、1秒1秒の大切さを実感できていたし。 僕としては初めにCAMPFIREに行こうと意思決定して以降、ジェットコースターにただ乗っているような感覚で各コミュニティにここまで育てていただきました。   上記に関しては気がつかぬうちに入っていたコミュニティなのですが、昨年くらいにこのコミュニティという存在の大切さに気がつき、今年からは意図して自分が身につけたい思考や能力が身につきそうなコミュニティにも入るようにしました。 例えば、ネットショップ作成サービス「BASE」のユーザーさん達です。 正直なところ今まではユーザーであるショップオーナーさんにはあまり会わないようにしていました。それは中途半端にお会いしてしまうと、仮にそのユーザーさんがBASEのサービスに不満を持たれて、別のサービスを使いたいと思ったときに、僕への情だったり気を遣われて、自由に次の選択をすることがやりづらくなるのではないか。それなのに僕自身は満足に相談にも乗ることができないのではないかと思っていたからです。 去年くらいからは日常業務の大部分を他のメンバーに移譲できていたので、今年は凄く多くのショップオーナーさまに会わせていただいて、本当に多くの刺激をもらいました。 毎日のようにお会いさせていただいている中で、ものづくりの大変さ、楽しさなどなど生きていく上で大切な価値観を本当に多く教えていただきました。 We are Ownersというメッセージを出させていただいたのもこれがキッカケです。 https://thebase.in/weareowners ここで出会った一部の方々とは友達としてプライベートでの交友もできました。 友達の中には、服を作っている人、音楽を作っている人、写真を撮っている人、映像を作っている人、自分という商品を作っている人。 第一線で頑張っている人もいれば、まだまだこれからだと焦っている人もいて様々だけれど、そんな友達と話していたり、成果物を見せてもらったりしていると、自分の無力感や思考の浅さを実感できて、最近では一番成長意欲を刺激してもらっているコミュニティな気がします。 いかなるジャンルであっても、どんな人であっても、悩んでない人などいないんだなと実感できるし、なんと言っても仕事が楽しそうで好きなのが伝わってくるので、僕自身の生きる活力になっています。自慢の友達です。 コミュニティが人生にもたらす影響に気がついて、新たなところに自ら足を伸ばして本当に良かったなと思います。 起業して以来初めて地元の友達にも会うようにもなりました。 完全になんとなく、自分への課題として、起業してから会っていなかったのですが、ある意味新たなコミュニティとして7年ぶりに会いました。 みんな様々な人生を送っていて、ただただ飲みながらこの7年間の話をしているだけで死ぬほど楽しかったです。毎日会っていたような感覚すらありました。 本当に友達は大切ですね。 しょうもない話ができる大切さも30歳を目前してようやく理解しました。 成長というものを前提に置いたとすると、個人的にはコミュニティには2つの性質があると思います。 成長するための知識や劣等感を得れて頑張る意欲を貰えるコミュニティ。 もう1つが、安心してなんでも話すことができるホームコミュニティ。 1つ目に関しては恐らくどんどん変わっていくもので、自分がチヤホヤされだして、成長意欲を削がれてしまうように感じたら次に行く合図かもしれません。 ここまで何が言いたいのかよく分からないですが、まとめとしては、どんなコミュニティにいるのか、そこでどんな人たちとどんな時間を一緒に過ごしたかが自分の人生に影響を与えるという事です。 もしも今、毎日をいまいちパッとしないと思って過ごしていたり、いまいち学びたいこと学べてないなと思っていたら、適切なコミュニティを見つけて飛び込んでみることをお勧めします。 僕は、ずっと知識や意欲をもらってばかりだったので、最近はそういう刺激を提供できる側にもなりたいなと思っています。 いろんな世界やいろんな価値観を知りたいのでぜひお友達になってください。 https://twitter.com/0Q7 本を読めるようにも頑張ります!! それでは良いお年を。
アバター