TECH PLAY

BASE株式会社

BASE株式会社 の技術ブログ

576

本稿は BASE Advent Calendar 2019 🎅🎄 の14日目です! こんにちは、Product Divisionの金城( @o0h_ )と申します。 10月にBASEに入社しまして、ブログ投稿は本稿が初となります。よろしくおねがいします。 さて、早速掲題の件に入らせていただきます。 PSR, PHP-FIG 「PSRはPHP-FIGが策定し運用する一連の規格であり、その19号はPHPDoc Tagsについての決め事」です。 PSRには色々なレイヤーのルールが ─時には 「本来の主旨を超えている」という批判 をもさえ巻き起こしながら─ 策定されています。 1 PHP-FIGは、元々はオートローディング活用のための取り組み 2 を契機として作られた団体です 3 。つまり、元来から「PHPの書き方」に強い関心を持っていたとも言えます。 PSRの「0番目がAutoloading Standard」という事だけに留まらず、「続く1番目はBasic Coding Standard」「2番目がCoding Style Guide」であることからも「初期にフォーカスしていた領域がどこであるか?」というのを伺い知れるのではないでしょうか。 更に言い加えると、PSR-0,2のみが現状でDeprecatedステータスに配置されています 4 。 すなわち、「より洗練した規格を後から作り、採択している」のが、AutoloadingとConding Styleに関する話題であるという事です。 5 そして、2019年12月現在で「Draft」にあるのが「PSR-5: PHPDoc Standard」と「PSR-19: PHPDoc tags」です。 ある意味で「実装のし易さ」と同じくらいに「コードを読みやすく・違和感なく」運用していく事に重きを置いていた団体が、「doccomment/tagの書き方について未だに標準化できていない」・・というのは、ちょっと面白いと思いませんか?一体どんな経緯があったのでしょうね。 そんな訳で、本稿では「PSR-19なんなのよ」という事について見ていきたいと思います。 PHPと「doc comment」 一昔前まで、PHPでdoc commentというと「いろんな方言がある」という印象を抱いた方も多いと思います。私もその1人です。 (以下、doc commentについて「PHPDoc」 6 と表記することとします。) 「phpDocumentorのそれに従おう」「いやPhpStormに合わせりゃよくない?」「Phanが」「PHPStanが」といったソレです。 もちろん、それぞれ目的が違うものであり、自分達の目的を果たすために独自の拡張的な記法なども導入していく事には必然性があります。完全に統一され得るものではないでしょう。・・とはいえ、いち利用者的な観点からすると「似たような事は同じ書き方で出来ればいいのにな」と思う訳です。まさに「相互運用性のための決まり事」があると良かろうなぁという場面で、PSRに目が向きます。 なるほど、それがPSR-5です。 しかしこのPSR-5、提案自体は数年前になされたものの、「広範に支持されるような勧告」といった地位にまでは至りませんでした。結局は「色々な書き方がある内のいち方言」のような雰囲気のままです。そうして遂には議論が停止、草案は「STATUS:ABANDONED」 7 に・・・・ なったのですが、これが再提案されて「DRAFT」化 8 、今に至ります。そういった訳で「5番」などという非常に若い番号が未だに議論されている訳ですね。 そして新生PSR-5はEditor/Working group membersとして「phpDocumentetor」「PhpStorm」「Psalm」「PHPStan」の関係者をそれぞれ迎えています。これは現実的な落とし所を見つけつつ、各ツールのサポートの観点からも実用性を伴う状況が実現できるのではないでしょうか。期待ですね! 昨年時点でのPSR-5の状況については、こちらの記事に大変よくまとまっておりました。ご参照ください。 qiita.com PSR-19 PSR-5の「復活」にあたって、一緒に提出されているのがPSR-19です。 これは、PSR-5にまとまっていた内容を2つに分けようという提案 9 に基づくものになります。 PSR-5についてはPHPDocのフォーマットを、PSR-19についてはタグの種類を決める内容になります。二者が分割された動機については「PHPdocのフォーマット(PSR-5)を一般的な内容にしつつ、タグの内容(PSR-19)は拡充しやすくする。また、将来的にはPHPDoc外でも利用可能になることを見越して」と説明されています。 The rationale behind the splitting is to make it easier to add new tag dictionary PSRs in the future and also make PHPDoc more generic so it can be used for non-docblock standard such as Doctrine-style annotations. 例として Doctrine-style annotations が挙げられていますが、例えば <?php /** * @MyAnnotation(myProperty="value") */ private $ bar ; のようなものでしょうか。 ということで、本稿は(既にその内容には見慣れているであろうPSR-5よりも)PSR-19の中身はどうなっているのかな?と興味を持って調べてみるものです。 中身を見てみる 現時点でのPSR-19のproposalはこちらから確認できます。 fig-standards/phpdoc-tags.md at 2668020622d9d9eaf11d403bc1d26664dfc3ef8e · php-fig/fig-standards · GitHub また、GitHub上での議論(PR)についてはラベルで辿ってみるのが良いでしょう。 Pull Requests · php-fig/fig-standards · GitHub 中身を見てみると、5つのセクションに分かれています。 PHPDoc standard(PSR-5)を補完するのが主目的であること MUSTやSHOULDなどの語についてはRFC 2119に準ずること (いつものやつですね) 用語の定義等はPSR-5に準ずること inheritDocの扱いについて 改変無しで全体を引き継ぐ場合は @inheritDoc を、インラインで埋め込む場合は {@inheritDoc} を用いることなど そして第5セクションが「タグ」の定義です。 現時点では以下のリストが掲載されています @api @author @copyright @deprecated @internal @link @method @package @param @property @return @see @since @throws @todo @uses @var @version このうち、 @method @param @property @return @throws @var などは、すっかりお馴染みなのではないでしょうか。クラスやメソッドの機能に関連するもので、PhpStormなどのIDEやPHPStan・Phanといった静的解析を利用している場合には欠かせません。 残る内容について、その書式や意味を掻い摘んで見ていきましょう。 独断にて、関連するタグごとに以下の4つの群にまとめながら取り上げていきます。 利用者の想定・提供範囲に関わるもの 著作者・権利に関わるもの 関連情報・参照情報に関わるもの バージョン・更新に関わるもの 利用者の想定・提供範囲に関わるもの 論理的な空間・括りと、誰に向けて提供しているものか?に関する情報です @package @package は、「論理上の区分」を示すために用いるものと説明されています。 PHPの持つ言語機能であるNamespaceと近い概念を取り扱うものであり、実際に「階層は(Namespaceと同様に) \ で区切って記述する」と規定していたり、「Namespaceと完全に一致する場合に@packageタグを利用することは推奨しない」と言及されています。 あくまで Namespaceで表現される「機能的な区分」とは別に、論理上の区分を明らかにしておきたいという場合に利用される ものになります。 @api @api は、 「primary public API of a package」に付与されるものと説明されています。 publicなメソッドの中でも、 そのパッケージ外から利用される ようなものを区別するためのものです。 @internal @internal は、@apiと対象的に「only for use within the application, library or package to which it belongs.」と説明されています。 「primary public API of a package」に付与されるものと説明されています。 このタグが規定されている動機についても説明していて、 1つには「ライブラリの作者が、 ユーザーに対して"破壊的な変更を含む可能性がある"ということを示す ため」 2つには「静的解析時に、 パッケージ外からの利用を警告できるようにする ため」 というポイントを取り上げています。 実際に、PhpStormでは違反的な利用に対して警告を出すようになっています。 著作者・権利に関わるもの 該当箇所に関する権利等に関する情報です @author, @copyright @author は、その作者や重大な改修を行った人を示すものと説明されています。 @copyright はそのコードに対する著作権と説明されています。 (特異な内容はないので、あとは割愛) 関連情報・参照情報に関わるもの 該当箇所の実装の理解を助ける情報です @link @link は関連する情報を示すものと説明されています。 ( a custom relation between the associated "Structural Element" and a website, which is identified by an absolute URI. と書かれていますが、ここは「Webサイト」というより「情報の掲載されているWebサイト」といったニュアンスで解釈するほうが意味する所に近いのかな?と思います) @linkタグを利用する場合には、絶対URIで示すように指示されています。 また、インラインでの利用も想定されています。 <?php /** * This method counts the occurrences of Foo. * * When no more Foo ( {@link http://example.com/my/bar} ) are given this * function will add one as there must always be one Foo. * * @return int Indicates the number of items. */ function count () { <...> } @see @see は関連するリソースを示すものと説明されています。 @linkはURIだったのに対し、こちらはURI以外のリソースを利用することも可能 です。例えば他のクラスやメソッドがあり、そのような要素を指す場合には「FQSEN」を利用するようにと規定されています 10 。 また、関連先のリソースとの関連性(=なぜ see なのか?)という説明を載せるべきだとされています(SHOULD)。 @link が対象リソースの説明について「してもよい」とされているのに対して( The @link tag MAY have a description )、こちらが「べき」なのは、「ドキュメント」以外のリソースも扱えるからでしょうか。 @uses @uses は利用される要素やファイル名を示すものと説明されています。 ただし、記述できるのは同じプロジェクト内のリソースに限るとされています。そのために、URI等の利用は禁止です。(システム外部のリソースに言及する場合は、 @see を利用することが出来ます) 例として、「コントローラーのメソッドに対して、描画されるView/templateファイル名を示す」といった内容が挙げられています。 これは、どういった場合に嬉しいのでしょうか? 例えば 動的な関数の呼び出しを行っている場合やマジックメソッド経由で利用しているメソッド について、@usesを用いてエディタや静的解析ツールに「実際に使っているよ!」ということを知らせることが出来ます。 余談ですが、 @used-by については、現時点ではPSR上での規定はありませんでした。 バージョン・更新に関わるもの 現行の状態や更新履歴といった情報です @version @version は、現在のバージョン情報とそのバージョンで提供された内容を示すものと説明されています。 セマンティックバージョンを利用して言及すること、説明文を加えることを推奨されています(RECOMMENDED)。 @since @since は提供が開始された(もしくは内容が現行の状態に変更された)バージョンを示すものと説明されています。 (@versionと同様に)セマンティックバージョンを利用して言及すること、説明文を加えることを推奨されています(RECOMMENDED)。 例えば「2.0.4から、第3引数が追加された」といった内容などは@sinceで示すのが良さそうです。 「提供のタイミング」「バージョニング」に関わるということで、@versionと混同することもあるかもしれませんが、 「今がどのバージョンであるか」といった情報には@sinceによって提供されるべきではない(@versionを利用する) と説明されています。 @deprecated @deprecated は廃止予定・将来のバージョンにて削除されるものを示すものと説明されています。 廃止予定とされた理由について、説明文を加えることも可能とされています(MAY)。また、代替となる手段が用意されている場合には @see タグによって示すことが推奨されています(RECOMMENDED) @todo @todo は実施される(開発・改修される)べきことを示すものと説明されています。 このタグには必ず説明を入れなければならない(MUST)とのことです。 (todoが「バージョンに関するもの」か?というと、「将来的な変更に関するもの」という意味では、この群に属するのではないでしょうか) その他の提案内容について ここに列挙したタグは既にproposalにコミットされているもので、ある程度の同意を得たものという扱いになります。 もちろん、「今はリストに含まれている」というだけであり、最終的には削除されるかもしれないし他のタグが追加される可能性もあります。(そもそもPSR19自体が採択されていないですからね) また、興味深い議論としては、PSR-5の領域ですが PR#1191にて「 no-return type」についての提案がなされています。 github.com これは、TypeScriptでいう never に相当するもので、exit()やtrigger_error()の発火により「呼び出し元に処理を戻さない」性質を説明するものです。PHPStanやPsalmでも、それぞれ @return never , @return no-return という表記によって扱われています。 PSR-19についても、いくつかのタグの追加に関するPRが作成されている状況です。 まとめ ざっくりと、現時点でPSR-19として規定されているPHPDocのタグについて見ていきました。 多くのタグは「定番化」しているもので、既存のコードに対して衝撃を与える事はないように思います。実際に、PSR自らがInterfaceの定義等において既に利用している状況が見受けられます。 PSR自体は決して「PHPを書く際に従わなければならない」ものではなく、「フレームワークやツールを作る時に準拠しておくことで、何か良いことがあると良いよね」程度のものです。そのため、実質的なデファクトスタンダード(今だとphpDocumentorになるでしょうか)が存在すれば、大きく困ることもありません。実際に、過去に議論が停滞したのも「困らなかったから」という側面はあるでしょう。 ですが、個人的には、先述の通り「PSR自体がPHPDocを利用している」こともあり、やはり明確な定義を持つべきなのではないかな?と考えています。 繰り返しになりますが、PSR-5,PSR-19はその内容がまだ確定しておらず、現在進行系で議論がなされている状況にあります。なので、ここにまとめた内容も採択時には変更されているかもしれません。 それでも、その内容や議論に興味を持つことは、今っぽいPHPを考える上で多少なり意味のあることではないかと考えます。 PSRはその性質的に「最大公約数」を示す力も持っていると捉えているからです。 皆さんもぜひ、PSR-5/PSR-19の内容を眺めながら「ドキュメント性能の高いソースコード」について想いを馳せてみるのはいかがでしょうか! ここまで読んでいただきありがとうございました。 明日は、Data Strategy Groupの齋藤とFrontend Group加藤が更新予定です!✨ 前身となる「PHP Standards Group」は「PHP本体への提案」を目指して結成されたグループです。しかし「標準化」に際しては考慮すべき点が多く困難性があったため、「より柔軟な提案をするために、言語仕様そのものではなく、その上に乗るプラクティスとして規約を求めよう」と方向転換をして今のPHP-FIGが生まれました。そのため、「広い範囲を扱う」というのは、サジ加減はあれど、FIGにおける本質的な振る舞いだと私は解釈しています。 ↩ https://wiki.php.net/rfc/splclassloader ↩ 歴史については、こちらの記事に詳しいです The Past, Present and Future of the PHP-FIG — SitePoint ↩ PSRのステータスや議論の進行については、 https://www.php-fig.org/bylaws/psr-workflow/ をご覧ください ↩ と言いつつ、「PSR-3を踏まえて簡素化した(利便性を高めた)PSR-16:Simple Cache」や「PSR-7をコアとした一連のHTTP関連Interface(15,17,18)」もあるので、「特定の領域について深める・広める」という事自体は他にも積極的に行われています。 ↩ https://github.com/php-fig/fig-standards/blob/2668020622d9d9eaf11d403bc1d26664dfc3ef8e/proposed/phpdoc.md#3-definitions ↩ GItHub上でステータスを変更されたソースはこちらです https://github.com/mcneely/fig-standards/pull/1 ↩ https://github.com/php-fig/fig-standards/pull/1078 ↩ https://groups.google.com/d/msg/php-fig/5Yd0XGd349Q/w-uTRA2nEgAJ ↩ FQSENについては、PSR-5を参照してください https://github.com/Chofoteddy/fig-standards/blob/e89924d320c269678fe3f5822c9bf9ef95db1af0/proposed/phpdoc.md#3-definitions ↩
アバター
はじめまして、Owners Marketingグループの小林です。 Owners Marketingグループは、ショップオーナーさん向けに新規機能などを常に開発しているエンジニアのチームで、最近ではネットショップ作成サービス「BASE」の拡張機能である「BASE Apps」のInstagram販売App、予約販売Appや顧客管理Appなどを開発/リリースしました。 アドベントカレンダー14日目のこちらの記事では、BASE社内の部活動のひとつ、ランニング部の活動について紹介したいと思います。 devblog.thebase.in BASEの部活動 BASE社内には多くの部活動があり、5名以上のメンバーが集まれば、部活動として認定され、会社から活動費の一部を補助する制度があります。 (ランニング部以外に社内にある部活動) ビール部/ヨーガ部/日本酒部/筋トレ部/ボードゲーム部/ボルダリング部/サウナ部/LLVM部など 社内にあるコミュニティは多種多様で気軽に参加できるため、仕事ではあまり関わりのない人とも気軽にコミュニケーションできる場として、積極的に活用されている印象です。 過去には、ボードゲーム部が、ランサーズさんと合同大会などを実施し、社外交流の場としても活用されています。 basebook.binc.jp ランニング部 ランニング部では日々の活動として、週一回、仕事後にオフィスの近く走る活動を開催しています。皇居一周をメインコースとして、その日の参加メンバーの走力や体調に合わせて、6〜13kmくらいの距離を走っています。 ランニング部に参加する前まで、日々の業務では座りっぱなしで運動不足を感じる事が多く、また「運動したい。走りたい。」と思ってもなかなか継続できず三日坊主になりがちでした。ランニング部は、雨で中止になる日もあるのですが、基本的には毎週活動しているため、継続的に運動できる環境になり運動不足が解消できると思い、参加していました。 皇居周回コース 有名な皇居ラン。 オフィスがある六本木グランドタワーから皇居に向かい、皇居の周りを1周してグランドタワーまで戻るコースです。 皇居周辺は多少のアップダウンがあり、1周5kmで信号で止まる事がなく走りやすいコースです。 桜の時期では、夜桜を楽しみながら走れます。 マラソン大会 私がランニング部に参加し始めて1年半ですが、週一回の活動以外に、休日に開催されるマラソン大会に何度か出場してきました。それまでマラソン大会に参加したことがなかったので、最初は10kmのマラソン大会に出場することにしました。 10kmという目標と他のメンバーに負けたくないという気持ちもあり、週一回の活動以外でも、仕事後や休日にも自主練習。 日々のランニングとは違いマラソン大会に出場することで、各メンバーの目標設定ができ、Slack上でもランニング部のチャンネルでは話が尽きません。 小さい規模のマラソン大会でも、ゴールするとメンバー全員に達成感があり、大会後の打ち上げでは一体感が生まれます。 ただ、大会が終わるとすぐに次の目標を設定したくなり、終了後すぐに次の大会に申込みをするを繰り返し、出場を重ねる毎に自然と距離を延ばしてきました。 以下、私が出場したマラソン大会 2018年10月21日 10km 2018年10月27日 16km 2019年01月20日 21km 2019年06月23日 30km 2019年09月28日 21km 2019年11月10日 42.195km 2019年11月17日 42.195km 2019年12月09日 42.195km 2019年11月10日に参加した横浜マラソンでは、高速道路の一部区間が一部規制となり、高速道路を走る事ができました。奥にはベイブリッジも見えてとても気持ちがよかったです。 まとめ 仕事を一生懸命するのは勿論ですが、職場のメンバーと他の事にも挑戦できる環境であることは素晴らしいと思います。 最初は運動不足解消やコミュニケーションの場として、ランニング部を活用していましたが、少しずつ小さな達成を積み重ね、いつのまにかフルマラソンを走れるくらいの走力がついていました。 何事にも当てはまると思いますが、 THERE IS NO FINISH LINE. (そこにゴールはありません)大会後の打ち上げを楽しみつつ、継続した挑戦をしていきたいと思います。 最後まで読んでいただきありがとうございました! 明日は、Frontendグループの加藤さんとData Strategyチームの齋藤さんです!お楽しみに。
アバター
この記事はBASE Advent Calendar 2019 13日目の記事です。 devblog.thebase.in こんにちは。UIデザイナーの野村です。 2019年4月にBASEに入社し、主にショップオーナーさんが使う管理画面のUIデザインに携わっています。 デザインワークの下敷きになるようなトライをいくつか行っておりまして、今回はそのあたりの話をさせていただきたいと思います。 [これまでに実施したトライ] 2度のデザイン思考ワークショップ 非デザイナー向けのデザインツール講習会 サービスデザインの勉強会で組織デザインについて学ぶ(社外) 開発プロジェクト内での実践(途中) なぜそんなトライをしてるのか、という話 受託デザインの世界からインハウスへ移行して 私はBASE入社前はデザイン制作会社に勤めたりフリーランスで働いたりしていたのですが、携わった業務はほぼ全て受託案件でした。 業務委託の形で事業会社内で勤めたことはあったものの、インハウスデザイナーとしての経験はとても浅い状態で入社して参りました。 受託での仕事と、事業会社内(インハウス)でのデザインワークには色々と違いがあります。 受託デザイナーをしていると、基本的には「お客さん(依頼者)が提示する情報をベースにモノを作る」という形をとります。 実際のところ、プロジェクト開始段階でお客さんから十分な情報を提示されることは稀でして、追加で色々と調査・検討を行うこととなります。 もらった情報を精査した上で、不足を補うためにお客さんにヒヤリングをしたり、内外への調査を提案し計画したり、技術や周辺事情についての勉強会や講習会を行ったり... などなど、良いアウトプットに到達するための施策を打っていくわけですが、とはいえ基本的には情報収拾と取りまとめはお客さん側で担当することが主で、受託デザイナーとしての自分は「 お客さんの情報収拾活動をサポートしつつ、制作に注力する 」というのが基本スタンスとなります。 そうなると、情報の深いところまではイマイチ理解が浅いまま納期優先で業務を進行することになったりするのですが、それは「 仕方ないこと 」と飲み込んできました。 インハウスデザイナーとして仕事をしていると、自ら社内で動いて事業やユーザの深い情報を掻き集めながらプロダクトを作っていくことができます。 マネージャーが取りまとめてくれた情報のみから業務を進めていくことも可能ではありますが、社内の各部署で持っている知見や体験を掻き集めて自ら消化・整理して業務に反映することで、アウトプットの質は目に見えて向上します。大変ではありますが、インハウスデザイナーとして働くにあたっての面白いポイントの1つです。 効率よく、かつ深く情報を共有するには 自ら組織内のナレッジ掻き集めようとした時、新参者は 誰がどんな知識をもっているかわからない という事態に陥りがちです。 それらはドキュメントとして蓄積されてたりしますが、全てが明文化されているわけではありません。 また、ドキュメントには文章や図の形で知識を溜めていくことはできますが、そこに各人の想いや体験(ひっくるめて ナラティブ 、と呼ぶことにします)まで込めるのは難しいです。文章書くの苦手な人も居ますし。 ナレッジは有形(文章・図)化して蓄積可能だけど、ナラティブは無形のまま流動し続けることしかできない、とでも申しましょうか。 そして、ナラティブのようなバックグラウンドを踏まえてないと、せっかくの知識もその真の意味、深い意味が抜け落ちた上っ面だけのものになってしまうことがあります。非常に勿体ない事態ですね。 そんな想いから、部署の垣根を超えて各人のナラティブをそれとなく共有できるような、そんな場を組織内に作れないものか、と考えました。 プライベートなことを掘り下げようというものでなく、仕事やプロダクトについてのナラティブを共有するのが主なので、 プロダクトの企画をテーマにしたワークショップ をするのが良いかな、という思いに至り、企画と実施をしました。他、非デザイナー向けにデザインツールの講習会をしたり、デザイン組織醸成に関する社外の勉強会に出たり、といったトライをしました。 2度のワークショップ 「デザイン思考を体験するワークショップ」という名目で、2時間程度のショートワークショップを社内で2度実施しました。2回とも、参加人数は10名前後の、小規模なものです。 第1回ワークショップ まずはやってみよう、ということで7月にパイロット版的な形で実施しました。 内容としては、私が過去に参加した1DAYワークショップを下敷きに、2時間程度の尺で収まるよう圧縮したものです。 いくらかバタついたものの、10名強の参加者には楽しんでいただけたようで、パイロット版としては成功を納めたと思っています。 その時の様子は こちら 。 第2回ワークショップ 第2回のワークショップは、流れとしては概ね第1回と同じなのですが、第1回での反省点や、いただいたフィードバックなどを踏まえ、かつ段取りのバタついた箇所を整理するなど、いくつかのアレンジをしました。 その時の様子は以下の通り↓ ワークショップ振り返り 2回のワークショップの後には、参加者からはそれなりにポジティブなフィードバックをいただけました。(「楽しかった」「普段話せない人と話せてよかった」「良い頭の運動になった」など) 90〜120分程度のワークショップをスムーズに進めるための段取りも作れたと思っています。 自身のファシリテーション力向上にもつながり、良いトライができました。 他、ナラティブ共有についてのトライ 「他部署メンバーのナラティブを得る」という主旨からは少々逸れるのですが、以下のようなトライを行いました。 非デザイナー向けの、デザインツール講習会 これは私が企画したわけではなく、デザインチームメンバーの小山が主導したものなのですが、デザイナー以外へのデザインツール講習を行いました。業務の効率化を意図しての施策ですが、デザイナーから他部署メンバーへの良いナレッジ共有・ナラティブ(デザイナーの作業姿勢とか)共有になったと考えています。 組織ナラティブに関して勉強会参加(社外) 社外で行われたサービスデザインに関する勉強会にて、組織におけるナラティブ構築に関して整理・発表したりもしました。(この勉強会の影響で、ナラティブという単語を多用するようになった私です。口には出してないけど。) 開発プロジェクトの中にナラティブ収集のフェーズを組み込んでみるトライ ここまででご紹介したのは開発プロジェクトの外側で実施してきた内容なのですが、現在は「 プロトタイプ検証のための社内ヒヤリング 」という名目で、プロジェクト初期段階で他部署から意見を収集するフェーズを作ることにトライしています。 単なる意見収集に留まらず、ナラティブ共有の一助になっている、と感じています。 この件についてはまだプロジェクト進行中につき詳しくは書けないのですが、いずれ整理してまた記事化したいと思います。 整理途中のものをチラ見せすると以下のような具合です。 トライのまとめと今後 業務の傍でいくつか施策をトライしてみました。 それぞれ多くの知見を得られましたが、一方で、拡大していく組織の中で個々のメンバーの想いや体験を収集することの難しさを感じています。デザインワークに本当に役立っているのかは、正直まだよくわかりません。 最後に挙げたデザインプロセスのトライについては、進行中の開発プロジェクトに取り込みつつ試してるため、デザインワークに対してそれなりの効果があることを実感できています。まずはこのプロセスを整理して社内で共有し、トライを重ねていきたい次第です。 ワークショップについても参加者からはポジティブな感想をいただいていますので、こちらも続けていきたい想いでいます。 ただ、これまでに実施したワークショップよりも、もう少し業務との繋がりをイメージしやすいような形のワークショップを行いたいと思っています。 具体的に何かモノ(プロトタイプ)を作って検証するような形をとれたら良いのではないかと思い、現在はプロトタイピングにフォーカスしたワークショップについて学習・研究しているところです。 なんやかんやと、 チームを超えて存在(潜在)している集合知をデザインワークに繋げることを意図したトライをしています 、というお話でした。最後までお読みいただきありがとうございます。 明日はPlatform Devの金城さんとService Devの小林さんです!
アバター
はじめに この記事はBASE Advent Calendar 2019 13日目の記事です! devblog.thebase.in こんにちは。BASE株式会社 Product Design Divison でデザインリードをしている北村 ( @naomi_kun )です。 ふだんはオーナーさま向けの管理画面やショッピングアプリの改善、新機能の開発までいろんな部分でUI設計を担当しています。直近では、既存機能のフルリニューアルプロジェクトを進めており、てんてこ舞いになりながらもとても楽しみながら開発しています。 実は、9月にデザインマーケットのテーマ作者さん向けの機能が少しアップデートされました。デザインマーケットでテーマを作成しようと思っているデザイナーさんは、ぜひ BASE Developers からデザイナー申請してみてください。 https://developers.thebase.in/ 今日は、BASEに入社して2年半を迎えたので、BASEの考える「Move Fast」をデザイナー視点で振り返りながら、いろいろ書いてみようと思います。 BASEのMove Fastという文化 入社当初からBASEには「Move Fast」という文化が浸透しており、今現在のプロダクトの開発にもその考え方が強く根付いています。自分が携わってきたプロジェクトも、まずリリースしてみよう、ファーストリリースを恐れるな、という考えが強く、チャレンジングな会社の雰囲気を感じとったのを覚えています。 前Qのはじめにも、あらためて「Move Fastを体現したプロダクト作りを正とする」ということが全社的に語られ、各プロジェクトの開発現場では、「DAY1にこだわるために何をすべきか」という議論が飛び交っています。 プロジェクトが煮詰まるあまり、当初の要件から膨れ上がったり、リリースが遅れがちになるといったプロジェクトを経験していたこともあるので、このスピード感と意思決定の方法はとても「強い」なと思いました。(強い、という表現が適切かはわからないんですが、強さを持っている会社だなと感じてます。) 日々感じていることですが、「BASE」のショップオーナーさんは本当に成長速度が早く、気づいたらとてもおおきなブランドになっている、ということも多いです。 そんなオーナーさんたちの成長速度についていくための方法として、「DAY1にこだわる」という意思決定が言語化されたことは、(私個人としては)、腑に落ちた気がします。 入社当初を振り返りながら影響されたこと 入社した当初、私はアプリの開発チームにアサインされましたが、振り返ってみると現在のBASEのMove Fastを体現するような現場だったなあと思います。 その頃のチームにはディレクターがいなかったため、開発メンバー全員がオーナーシップをもって取り組んでおり、仕様決めやUI設計を一緒に進められたこと、不具合時の対応もリアルタイムで議論していたことなど、いろんなところに首をつっこめる環境のおかげもあって、結果的にサービスへの理解がとても深まったと思っています。 また、現在のVP of Product・CTO・テックリードなど、現在のBASEの開発の根幹を担っているメンバーが集まっていたため、彼らの決定力とスピード感にも多くの影響を受けたように思います。 このスピード感を保つには、粗が出ることもあるし、完璧なデザインや実装にはならなかった部分もあるのかもしれないですが、そのときにくだした意思決定は圧倒的に正しくて(あとから状況が変わってそれが最適ではなくなることももちろんありますが)、現在のBASEはそれを積み重ねてきた結果なんだなと思っています。 あとは、「Be Hopeful」な文化もあり、気負わず自然にお互いを後押しできるような空気があったのも、Move Fastを進められる大きな要因だと思いました。 Move Fastは難しい 上に書いたようなスピード感のある開発は、入社当初の規模感だからこそ実現できていたもので、今はそれが以前に比べて難しくなっているのも事実です。あとは単純にプロジェクトで開発する影響範囲の粒度が大きく、機能改修や新機能を考えるときにはやはり慎重になってしまう場面が増えました。ショップの数も増え、社内の開発メンバーも増え、考慮すべき部分も増えた今では、いろいろなパターンを考慮するあまり、仕様が過剰になり、時間もかかってしまうのは、この規模だとある程度仕方ないと思っています。 それを削ぎ落とすための判断軸として、「DAY1にこだわる」というスローガンがあるのですが、個人によって考え方や持っている背景が違ったりするので、軸として機能させるのもなかなか難しいなあと。 BASEのデザイナーとして「Day1にこだわる」という意思決定をするためには、仕様も、ブランディングも、市場での立ち位置も含め、「BASE」への理解が深くないと行えない、という、当たり前のところに立ち返っています。 これは自省として書くのですが、いつかのMTGで、VP of Productの神宮司 が「デザイナーが不安になると、プロジェクトマネージャーも、開発メンバーもみんな不安になる」ということを話していました。 今でもその言葉が残っていて、デザイナーが慎重に・不安になってしまうのには色々な要因があると思うのですが、私自身がチャレンジングな環境のBASEに助けられてきた部分があるので、自分のいるプロジェクトでは、なるべく「すごい!」とか「めっちゃいい!」とか「はやくリリースしたい!」っていうアクションをするように意識するようになりました。 Be HopefulになれればMove Fastもついてくる、という空気を、デザイナーが持てると良いよなあと思っています。 Move Fastとは、未来のオーナーズに選ばれること 3Qのはじめに、デザインチームのミッションを皆で考える時間がありました。 「BASE」の価値をMove Fastにサービスに具現化し、未来のオーナーズに選ばれ続ける 「はじめに」でも書きましたが、「BASE」のショップオーナーさんの成長速度に追い越されないよう、「BASE」ももっと成長していく必要があり、Move Fastとは、未来のオーナーズに選ばれるための最速手段だと感じています。 おわりに 2年半を振り返りながらいろいろ書きましたが、この先の未来にも、もっと新しい構想や、やりたいことなどが山積みで、その全てが未来のオーナーズのみなさんに選ばれるための決断なんだという部分は揺るぎないので、ワクワクしています。 いま開発中の機能もはやくリリースしたい!DAY1!という気持ちのほうが大きいです。 おわり 明日は基盤グループの金城さんと 、Owners Marketingグループの誠さんです!
アバター
Product Management Groupの坂東( @7auto )です。 この記事は BASE Advent Calendar 2019 の12日目の記事になります。 devblog.thebase.in 私はアートが好きでしょっちゅうギャラリーや美術館を巡っています。 会社のお昼休みや、フレックスなのでたまに17時頃など早めに退勤していそいそとギャラリーを巡っているのですが、昼休みにぶらりと行ってみるととても良い気分転換になりますよ! みなさんは行きますか?ギャラリー。 最近ではアート思考の書籍が多数発売されたり美術館が60万人を超える動員をしていたりと、アートに関する情報を耳にすることも多くなっているように感じます。 アートが話題に上がりやすくなったことはとても嬉しいことではあるのですが、 美術館などに展示されるアーティストはトップオブトップな人たちです。 その裏には今まさに制作活動を行い切磋琢磨しているアーティストやアーティストを支えるギャラリーがあります。 大変ありがたいことに、Eコマースプラットフォーム「BASE」はたくさんのそうしたギャラリーやアーティストの方々にもご利用いただいています。 ただ、そんなギャラリーやアーティストはまだまだ一般によく知られる存在ではないのかなと感じています。 そこでよりよく知ってもらうため、本稿ではその魅力をご紹介したいと思います。 ギャラリーってなに? そもそもギャラリーとはなんでしょう。 ざっくりと言えばアーティストの作品などを展示してる場所になります! ※ギャラリーにもレンタルギャラリーとコマーシャルギャラリーがありますが、ここでは後者を指します。 作品の展示と言われて思い浮かぶのは美術館ではないでしょうか? 123億円の作品が展示されていた展示が六本木ヒルズで開催されて話題になっていましたね。 そんな美術館とギャラリーの大きな違いは収益構造です。 美術館は入場料を取るのに対し、ギャラリーは(基本)入場料を取りません。 ではどこで収益を得るのかと言うと作品の売買によって収益を得ています。 なのでそれぞれで目的が大きく違っています。 ・美術館:入場してもらう ・ギャラリー:作品を販売する 目的は作品の販売ですがギャラリーには作家を露出させて価値を育てるという側面もあると思っているので、個人的には昼休みにぶらりと見に行き価値を感じるだけでも良いのではないかと思っています。 そんな作家の価値を見出し育てていく関係性は、起業家と投資家の関係に少し似ているのかなと感じたりします。 最近ちょうど「 ほぼ日刊イトイ新聞 」さんが作家とギャラリーの関係性についての記事を書かれています。 見たことや聞いたことのある作家の名前も出ているかもしれません。 天才もしくは狂人と、その伴走者 ギャラリーやアートの世界が少し身近になったでしょうか? 何が楽しいの? 私は若手作家のいわゆる現代アートにカテゴライズされる作品を好んで見に行きます。 難解だと言われがちな現代アートですが、とにかく表現に対する多種多様なアプローチや切り口おもしろいです。 小林健太 さんや 岡田舜 さんら好きな作家を上げ始めるときりがないのですが、いろいろな目線やアプローチで作られた表現の価値とは何なのか、情報の伝達とは何なのかみたいなことを考える良いきっかけになっています。 難しく考えすぎず、何か刺激を得られるといいな、くらいの気持ちで足を運んでみてはいかがでしょうか? ギャラリーってどこにあるの? そんな楽しいギャラリーですが、どこにであるのでしょう? 個人的に数えたことがあるのですが、東京都内で500件以上、オフィスのある六本木にも50件弱くらいありました。 ビルの地下など目立たないところにあったりするので、Googleマップで「ギャラリー」などのワードで調べてみると意外と身近なところに見つかるかもしれません。 Google マップで「ギャラリー」で検索してみる ギャラリーの営業日は火曜~土曜で19時頃には閉まることが多い印象です。 作品っていくらぐらいなの? 売買が目的だとすれば気になるのは値段です。 もちろん、アーティストの知名度や作品のサイズなどによって値段も様々ですが、私がこれまで作品を見てきた感覚でいうとPCやスマホと同じくらいという印象です。 作品は流行り廃りのあるものではないのでワクワクが持続するように感じます。(個人の印象です。) そう考えると案外手に入れてみるのもいいかも、と思えるかもしれません! また今はアート売買やレンタル、分散保有などのサービスも生まれてきていて、作品の所有に対する障壁は低くなってるように感じます。 私も AND ART というサービスを通して↓のKAWSの作品1点の0.23%を所有してみました。 KAWSを(0.23%だけ)所有していると言えることは、とてもプライスレスな体験です。 購入方法や所有方法も多種多様になってきているので、自分にあったスタイルで一度購入してみてはいかがでしょうか? 何か新しい発見があるかもしれません! 最後に ギャラリーや作家に少し興味を持っていただけましたでしょうか? BASEのミッション には下記のような記載があります。 ひとりひとりに眠る、想いが、感性が、才能が。 世界中の、必要な人に届くように。 そこから生まれる、作品に、アイデアに、活動に。 正当な対価を、受け取れるように。 今回ご紹介したような切磋琢磨して作品を世に送り出している作家やギャラリーの方々をサポートしていけるよう、「BASE」というサービスを磨き続けていきます。 明日はProduct Design Divison でデザインリードの北村さんとUIデザイナーの野村さんです!
アバター
この記事はBASE Advent Calendar 2019の12日目の記事です。 devblog.thebase.in こんにちは、BASE BANKインターンの河越( @heart_breakers2 )です。 この記事では、去年と同様に「鶴岡さん観察日記」と題して、BASE株式会社 代表取締役CEOおよびBASE BANK株式会社 代表取締役CEOである鶴岡の普段の様子をカジュアルに伝えていきたいと思います。 (みなさんにもカジュアルな雰囲気で読んでいただきたいので、記事内ではあえて「鶴岡さん」と表記しています。) 私がインターンとしてBASE BANKに入社してから、1年半が経ちました。 私の席は変わらず、鶴岡さんのななめ前にあります。 いつも近くにいるからこそ発見できた、鶴岡さんの素顔や、みなさんに知ってもらいたいBASEの好きなところをまとめてみました。 この記事を読んでいただき鶴岡さんやBASEに興味を持っていただけると嬉しいです。 ※ 去年の観察日記をまだ読んでない!もう一度読みたい!というかたはこちらからどうぞ! devblog.thebase.in はじめに 「鶴岡さん」 この記事を読んでいるみなさんなら、その名前を一度は聞いたことがあるのではないでしょうか? 鶴岡さんは、80万以上ものショップが利用する、ネットショップ作成サービス「BASE」を運営するBASE株式会社の代表です。 鶴岡さんと一度でも話したことがある方だと必ず、温和でコミュニケーション能力が高くて、優しい鶴岡さんに惹かれた経験があると思います。 また、先日BASE株式会社が東京証券取引所マザーズに新規上場をしたことから、代表である鶴岡さんがどんな人なのか気になっている方も多いかもしれません。 この記事では、鶴岡さんのことが大好きで、鶴岡さんみたいになりたい〜!と思っている私が書く「鶴岡さん観察日記」を5日分、みなさんにシェアしたいと思います。 みなさんにも鶴岡さんと、BASEの魅力が伝わりますように! 3/12 (火):鶴岡さんはなんでも知ってる 鶴岡さんは世の中で起こっていることのほとんどを知っているんじゃないかな?と思うことがあります。 顔を合わせるたび聞かれるこの質問ですが、最初は「え、特にないんだけどな、、」と思っていました。 鶴岡さんはとっても忙しいはずなのに、『テラスハウス』も毎週必ず火曜0時から見るし、『グランメゾン東京』も、『オオカミちゃんには騙されない』も、YouTubeも、女子高生に人気なインフルエンサーのInstagramも、IT界隈のTwitterも、スポーツマンガも、「BASE」を利用してくださっているショップさんの情報も、全てにおいてとっても詳しいんです。 BASEの哲学は Stay Geek ですが、鶴岡さんはインターネット上で起こっていること全てに興味が止まらない、「インターネットGeek」な人だな、と思います。 ちなみに今年の春、鶴岡さんはkemioさんにハマっていて、いつも「アゲアゲ修学旅行がやばいんですよ!」とえふしんさん(BASE株式会社 取締役EVP of Development)に教えていました。 今もkemioさん著書『ウチら棺桶まで永遠のランウェイ』は鶴岡さんのデスクに大切そうに飾ってあります。 今は、鶴岡さんが知らないことを見つけると、すごく嬉しくなります。 5/1 (水):鶴岡さんの笑い方 鶴岡さんがミーティングからデスクに帰ってきた時や、ごはんの場にいる時は、周りの雰囲気がパッと明るくなるのを感じます。鶴岡さんはよく笑う人で、鶴岡さんが見えない位置にいても鶴岡さんがいるのがわかるのです。 ちなみに鶴岡さんの笑い方は結構独特で、いつも「カカカカー」と笑います。 最近は、鶴岡さんのことが大好きな神宮司さんも、ちょっと鶴岡さんの笑い方を真似している気がします。 8/8(木):「食べて〜」 鶴岡さんはよく、「BASE」で販売されている、みんなでシェアできるスイーツやフルーツを差し入れしてくれます。 【秀品】ニューピオーネ 2kgを購入しました! https://t.co/ntUQpdOhyu #BASEec @BASEec より — 鶴岡 裕太 (@0Q7) 2019年8月28日 (こういうツイートがあるともうすぐスイーツ・フルーツシェアのチャンスです) ちなみにこの日はGAZTAさんのバスクチーズケーキ。 信じられないくらい美味しくて、鶴岡さんも終始にこにこ笑顔でした! また美味しいスイーツを買ってきてくれますように。 9/23 (月):鶴岡さんは聞き上手 鶴岡さんのすごいところは、なんと言っても傾聴力ではないでしょうか? 鶴岡さんは今まで会った人の中で一番聞き上手で、話していて(こちらは)とっても楽しいのです...! この聞き上手っぷりは今までも多くの人を魅了してきたはず。 誰と話していても気づいたら聞き役に回っているし、鶴岡さんのInstagramをみる限り、友達も多そうです。 鶴岡さんは、「人に興味があるし、そこから何か学べるかもしれないから、自分のために人の話を聞いてるんだよ」とビジネスライクなことを言っていました。大人ですね。 が、そんな鶴岡さんも話が長くて飽きてくるとこんな感じになります。 すご〜〜い。 オンとオフの切り替えがわかりやすいのも魅力ですね!😂 ずっと興味を持ってもらえるように話し上手になりたいなと思います! 11/8 (金):さくさくわいわい BASEのSlackには #sakusakuwaiwai というチャンネルがあります。 #sakusakuwaiwai は、仕事終わりにメンバーとサクッとご飯に行きたくなった時に声をかけるチャンネルです。 この日は、鶴岡さんがチャンネルに声をかけていました。 多忙そうだし、社員とプライベートで過ごす時間はないのでは? 社長とごはんだなんて、社員が萎縮してしまうのでは? と思うかたもいるかもしれませんが、鶴岡さんは違います。どんなに忙しくても、2週に1度くらいのペースで社員とのごはんの時間を大切にしてくれています。 やっぱりごはんに鶴岡さんが来てくださるのは嬉しいし、聞き上手な鶴岡さんが必ず、和やかでフランクな雰囲気を作ってくれます。そんな場にいるからこそ、社員同士も楽しく、"Speak Openly"に話しやすくなっているとも感じます。 鶴岡さんに「人数が増えても忙しくなっても、今もみんなとご飯に行くってすごいですね」って言うと、「僕が寂しくて、みんなとご飯食べたいだけだよ」って言ってました。 本当に素敵な人だなって思います。 おわりに 鶴岡さん観察日記第二弾、いかがだったでしょうか? 日記を書いていて、鶴岡さんみたいになるにはまだ遠いなと感じる日々です。 この記事を通して、鶴岡さんの優しい人柄が伝われば嬉しいです。 (一番気に入ってる鶴岡さんとのツーショット) 今日は坂東さんの記事「 昼休みに行くギャラリーは楽しい 」も公開されています!ぜひ読んでみてください〜!
アバター
この記事は BASE Advent Calendar 2019 の11日目の記事です。 devblog.thebase.in はじめまして!こんにちは! BASEのCommerve Dev Group所属の白数です! サーバサイドをメインで開発しています。 私は現在、新卒2年目で18卒と呼ばれる代となります。 BASEには今年の9月に入社しました。今月で4ヶ月目です。 入社してからは、Eコマースプラットフォーム「BASE」の社内向け管理画面の新規機能を開発しています。 そんな若手エンジニアの私がどのようにしてBASEでの働き方をキャッチアップしているのかについてご紹介しようと思います! 特に私と同世代や学生の方にBASEで若手がどのようにして働いているのかを少しでも伝わると幸いです。 BASEで働く上で意識していること・実践していること 1. タスクと進捗の共有 BASE では入社時に、日々の業務でのちょっとしたお困りごとについて質問したり、オフィス生活に関する質問対応をしてもらえるメンターが付く期間が1ヶ月間用意されています。 この時に社内では timesチャンネル と呼ばれる新入社員とメンターがメインでやり取りを行うチャンネルを作るのですが、メンターの他にグループのメンバー、他グループのメンバーが参加しています。 メンター期間はこのチャンネルで日報や困っている事を書くのですが、私の場合、メンター期間後もこのチャンネルを利用し続けています。 やっていることは出勤時には、 今日やること 退勤時には 今日やったこと いわゆるタスクと進捗を共有することです。 自分のタスクの内容をグループのメンバーに共有することで、 もし作業の方針や順序が間違っていたりしたらその場で指摘をもらうことができています。 同時に自分が作業するTODOリストとしても機能しており、タスクが明確化していて「次は何をやるんだっけ...?」といった状況にならず効率が良いです。 毎週、上長と 1on1 の面談をする時間が設定されているのですが、 毎日タスクを共有していることで上長は自分が今どんな事をしているのかを把握してくれていて、1on1が設定されている理由の本質である 思っていること 困っていること などの話に100%の時間を使うことができています。 また、作業内容のログとして後日確認する事もできるので週末に振り返りを行う際、より詳細な観点で良かった事、悪かった事について考えることができていると感じます。 2. 技術的な問題で詰まったときは、とりあえず共有 私は大学生の時に長期インターンで働いていた頃はRuby、そして新卒で入社した会社ではJavaを書いていました。 現在のBASEでは PHP で開発しています。 PHPは大学時代の授業で触ったことがある程度で実務で利用するのは初めてです。 書いている言語が頻繁に変わっていることもあり、 入社2ヶ月ぐらいはBASEのサーバサイドにおける仕様面やCakePHPの細かい使い方が理解できていないこと、 PHPUnitを書いた経験がない事などが理由で頻繁に原因不明のエラーに詰まっていました。 30分ほど悩んでも一向に進まない時は、自分のtimesチャンネルに投げるようにしていました。 BASEにはPHPに詳しいエンジニアがたくさんいます。 解決策や助け舟を出してもらい、開発をしていく上で必要となる最低限の知識を爆速でキャッチアップして実装に入ることができました。 3. タスクで困ったときや、方針を決めるときは直接話す タスクを進めていて進め方などに困ることがあります。 その時は当日、遅くて翌日中には上長へ直接相談するように心がけています。 Commerce Dev Groupでは、毎日12:00からスタンディング形式で タスク内容や困っている事を共有する場として昼会を実施しています。 いろんな人の見解を聞きたいときはこの時に共有して相談するようにしています。 Slack等で話すことも可能ですが、 直接相談した方が早いのと、より細かいところまで認識を擦り合わせることができます。 4. 糖分を摂取 オフィス内にBASEの3つの行動指針のうちの1つである「Be Hopeful」が描かれた飴が置いてあります。 プログラムを書いていて糖分が欲しいな〜という時はこの飴を摂取しています。 デザインが可愛いくて美味しいです。 おわりに BASEの若手エンジニアの私が働く上で意識していることを、あくまで一例ではありますが紹介してみました。 一番は大切なのは、 常に自分の現状を共有することで多方面からアドバイスを貰いやすくなっている印象です。 BASEは若手が成長できる場があると思います。 実際私もまだ入社4ヶ月ですが、エンジニアとして大きくレベルアップしている実感を持っており、 これからもプロダクトと共に成長してBASEを盛り上げていきます。 明日はBASE BANKの河越さんとProduct Managementの坂東さんです!お楽しみにー!
アバター
この記事はBASE Advent Calendar 2019の11日目の記事です。 devblog.thebase.in こんにちは!BASEのProduct Management Groupの船坂です。 2019年9月にBASEに入社し、ようやく4ヶ月目に入ったところです。プロジェクト単位でアサインされ、ディレクションを中心に業務を行っています。 今回は、BASEに入社してからEコマースプラットフォーム「BASE」というサービスをいろいろな角度から理解するためにしてきたことをご紹介します。 カスタマーサポート研修 最初は、自主的なものではなく研修ですが、、、 BASEには入社直後にカスタマーサポート業務を体験する研修があります。 この研修には、 まずはショップオーナーとしてネットショップ作成サービス「BASE」を実際に利用してみる。 次に、よくお問い合わせいただく質問内容に対し、模擬的に回答を作成する。(その後CSのメンバーに回答を添削してもらいます。) という2つのステップがあります。 これによって、「BASE」というサービスの利用シーンを深く理解すると同時に、現時点で初めてサービスを使う方がどの部分でつまづく可能性があるのかを短期間で把握することが出来ます。 また、1. の過程で気になったことを社内ドキュメントにまとめて記載するというステップが含まれています。利用に慣れてない視点だからこそ気づくことができる新鮮なフィードバックを社内にすることができて、とても良い仕組みだと感じています。 実地見学 僕自身、IT業界に携わっていると基本的なことは画面の中で出来てしまうのでおろそかにしてしまいがちなのですが、何かを理解するときはその場所に行き、自分の目、耳、触覚など、五感で経験することが一番大事だと思っています。現地に訪問し、オーナーさんやそのショップ、買ってくださるお客様などの様子を実際に感じることで、自分たちが作るサービスの画面の向こうにいる人たちの存在を確かめます。サービス理解という意味だけでなく、普段のモチベーションに直結します。結果、アウトプットの質も段違いに高くなると思っているので、とてもおすすめです。 BASEの場合、オフラインに展開されているものとして、渋谷マルイにある常設店舗「 SHIBUYA BASE 」を始めとする実店舗や、先日行われたオーナーさん向けイベント「 BASE OWNERS DAY 2019 」などのオフラインイベントがあります。入社直後から、これらの場所には実際に足を運んでいました。 経営陣や執行役員の考えを知る 経営陣や執行役員陣がこれまでどういう考えで事業やサービスづくりと組織づくりに携わっているかをなるべく深く理解するために、いろいろな情報をチェックしました。社内に向けて発信されているメッセージや外部で話しているインタビュー記事、普段の何気ない言動やSlackへの投稿などです。どういった人たちがどういうモチベーションでプロダクトを作っているのかを理解することは、どういったサービスなのかを理解することに直結すると思っています。 ちなみに、自分の役割はそこにスムーズに乗っかりつつ、僕にしか出来ないことを全力でやって更にプロダクト成長を加速させることなのかなと思っています。 社内プロジェクト調査 BASEはプロジェクト単位でチームが組まれ、サービスの開発が進んでいきます。当然、社内全体ではどういうプロジェクトを走らせるかが入念に検討されているのですが、一個人としては、自分のプロジェクトに入ってしまうと、意外と実際の業務の中では全体が見えにくくなってしまいます。 そこで、入社したタイミングで、社内ではどういうプロジェクトが走っていて、それはだれが、なぜ今やっているのかをヒアリングしながら考えていました。この作業で、大きな流れの中で今サービスがどういうフェーズなのか、理解できた気がします。 知り合いへのヒアリング ありがたいことにEコマースプラットフォーム「BASE」はたくさんの方にご利用いただいており、自分の知り合いでもネットショップを運営してくださっている方や、ショッピングアプリ「BASE」で商品を購入したことがある方が思った以上にいらっしゃいます。 生の声を聞ける方々は僕にとってとても貴重な存在です。BASEに入社してからは、「BASE」を利用したことがある知り合いと何度か飲みに行き、なぜ使っているのか、どんなところが好きか、どこが使いにくいのか、いろいろな話を聞かせてもらいました。 こういうことを繰り返していくと、なんとなく「サービスを形成しているコミュニティ」全体の雰囲気が見えてきます。 これがわかってくると、どのような施策が効果的なのかを感覚的につかめてくるのではないかと思っています。 データ分析 とはいえもちろん感覚だけでは話は進まず、詳細かつ確実な現状把握にいちばん効くのはデータだと思っています。Google Analyticsから自社で独自に蓄えているデータまで、どんどん調べてみています。BASEでは僕の所属するProduct Management Group含め、必要な従業員はRedash等で業務に必要なデータの分析を自由に行うことができます。全体像は上記のような方法で調べて、細部はデータで確証を得ながら理解を進めている形です。 まとめ 今回この内容にしたのは、経験上、理解が進めば進むほどそのサービスは"じぶんごと"になっていき、それがサービスを作るのに一番大事なことだと考えているからです。 極論、どんなスキルよりも当事者意識こそが、サービスに本当に必要な価値提案や、小さくても確信をついた修正案を産み出すのだと僕は信じています。 最後まで読んでいただきありがとうございました! 明日は、BASE BANKの河越さんとProduct Managementの坂東さんです!楽しみ!
アバター
この記事は BASE Advent Calendar 2019 の10日目の記事です。 devblog.thebase.in お久しぶりです。 BASEビール部部長(兼Data Strategyチーム)の氏原です。 1年ちょっと前に Yahoo!の近傍探索ツールNGTを使って類似商品APIをつくる という記事を書きました。あれからだいぶ経ちましたがその間に類似商品APIはコツコツと改善を続けています。例えばファッション系以外で精度が良くないという話を前の記事にも書きましたが、画像以外に商品のタイトルや説明文の特徴量も使うようにするなどしてそれも改善されています。 そうした取り組みのなかで近傍探索に使っていた NGT を別のものに切り替えましたので、それを紹介したいと思います。 類似商品APIについて 類似商品APIはBASEのアプリで各商品の詳細ページを開いたときに表示される「関連する商品」という部分などで利用されています。 その時開いた商品と似た商品を提示することで良い商品探しに役立つようにとAPIを開発しています。 NGTについて NGT はYahoo!Japanさんが開発している近傍探索用のツールです。 NGTは任意の密ベクトルに対して事前に登録した(同次元の)ベクトルから最も距離が近いベクトルの上位数件(k件)を高速に近似k最近傍探索(k-Nearest Neighbor Search)するためのソフトウエアです。 高次元ベクトルデータ検索技術「NGT」の性能と使い方の紹介 以前はこれで画像の特徴量vectorを保存して、商品ページが開かれた際に似た画像をもつ商品を関連商品として提示していました。 NGTはとても性能がよく、1024次元の特徴量vector350万個を登録しても数十msecでレスポンスを返してくれてオンデマンドで関連商品を出すのにとても重宝してました。 問題点 ただ運用を続けていると色々と問題もでてきました。特に辛いのはindexの構築にかかる時間とindexのサイズです。 NGTは登録したvectorを全てそのまま持っています。そのため1024次元の特徴量vector350万個を登録した時点でindexのサイズは20G程になります。これがメモリに全部載るので、AWSでサーバを立てると結構なサイズのメモリを持ったインスタンスが必要になります。 また、NGTは必要なvectorを全てinsertした後にbuild_indexでindexingをする必要があります。登録したvectorをdeleteするメソッドがなく、差分更新が(おそらく)できないため、daily batchで毎度indexを作り直していました。(※私の理解不足の可能性もあります。間違ってたらごめんなさい。) faiss 上記の問題を解決するために、現在では別のツールで近傍探索を行っています。 faiss です。 faissはNGTと同じくvectorの近傍探索のためのツールでFacebookが開発しています。 NGTと比べてなにが嬉しいかと言いますと、最終的に出来上がるindexのサイズです。 faissは高速化やindexの圧縮のための仕組みが用意されていて、いろいろ試した結果indexのサイズがNGTでやっていた時の1/200くらいになりました。衝撃です。 もちろん圧縮するがゆえにある程度の精度低下は覚悟しなくてはいけないんですが、目立つほどでもありませんでした。 使い方はこんな感じです。 index作成 import faiss import numpy as np import random dim = ... # 特徴量の次元数 nlist = ... # 特徴量が何個の空間に分割されるか m = ... # 近傍探索するときに対象の空間以外に隣の空間を何個まで利用するか nbits = ... # 特徴量をここで指定したbit数で表現するように圧縮する quantizer = faiss.IndexFlatL2(dim) index = faiss.IndexIVFPQ(quantizer, dim, nlist, m, nbits) vecs = [(item_id, vec), ...] # 商品IDと特徴量のセットのリスト # 圧縮のために特徴量の分布を学習する必要がある # 実際の運用ではここは事前に学習しておいて学習済みのindexをloadする assert not index.is_trained train_data = [v for _, v in vecs if random.random() < 0.01 ] index.train(train_data) assert index.is_trained # indexへの追加はまとめてではなくこんな感じで細切れでもよいので # vecsが大きすぎて全部をメモリに乗せられない場合は # generatorで必要な分だけ読み込みながらやったりしても大丈夫 batch_size = 10000 for i in range ( 0 , len (vecs), batch_size): input_vecs = [] input_ids = [] for item_id, vec in vecs[i:i+batch_size]: input_vecs.append(vec) input_ids.append(item_id) input_vecs = np.array(input_vecs, dtype=np.float32) input_ids = np.array(input_ids, dtype=np.int64) index.add_with_ids(input_vecs, input_ids) # 保存 faiss.write_index(index, "features.index" ) indexの利用 import faiss index = faiss.read_index( "features.index" ) vec = ... # 近傍探索のターゲット vecs = np.array([vec], dtype=np.float32) D, I = index.search(vecs, 1000 ) # 近傍1000個とってくる D = D[ 0 ] I = I[ 0 ] for item_id, distance in zip (I, D): # add_with_idsでindex作ってるとここでIDが返ってくる ... wiki が充実してるので使うにあたってあまり困ることはありませんでした。 差分更新 faissで作成したindexは、(サポートしているindexなら)登録されたvectorの部分削除が可能です。 import faiss index = faiss.read_index( "features.index" ) target_id = ... # 消したいitem_id index.remove_ids(np.array([target_id]) つまりdaily batchで差分更新ができるため、index構築時間を激減させることができます。これならdailyと言わずもっと更新頻度を上げられます。 ちなみにindexのmergeとかもできるそうなので、vectorが大量にある場合はN個に分けて、N個のjobでindexを作成して後でmergeするみたいな使い方もできるようです。 まとめ faissはFacebookが使ってるだけあって1兆個のvectorを登録して動かすなんてスケールまで想定しているようです。もちろんそのためには 色々工夫 が必要なようですが。 faissを導入したことでindexの構築やAPIの運用がかなり楽になり、インフラやアルゴリズムの改善に力をいれることができるようになりました。リコメンドのシステムを作る際などで近傍探索用のツールが必要であればfaissを検討してみてはいかがでしょうか。 明日はプロダクトマネジメントグループの船坂さんとエンジニアの白数さんです。お楽しみに。
アバター
この記事は BASE Advent Calendar 2019 の10日目の記事です。 devblog.thebase.in こんにちは、はじめまして。 SREの相原です。 BASEには2019年9月に入社し、今月で4ヶ月目に突入しました。 SREでは各々の改善業務のほか、日々の問い合わせや依頼業務、トラブル対応など、突発的に発生するタスクがあります(数はとても少ない)。 これはどのような業種、職種においても言えることかと思いますが、 突発的かつ迅速に対処しなければならないタスクは、 担当が特定の人(その問題に知見がある人や、問題解決力が高い人)に偏ってしまう傾向にあります。 幸いBASEでは、各メンバーが主体的にボールを拾っていく文化があったため、大きな問題にはなっていませんでしたが、 私が入社した時点でSREメンバーが6人と増えてきたこともあり、試しに当番botを導入することになりました。 当番botについて 当番は問い合わせなどに対して窓口としての機能を持つ(必ずしも1人で解決する必要はない) 当番にはメインとサブを設定し、メイン当番が休みの時や、手が回らないときはサブが対応する 当番は週ごとに切り替わる 当番の通知はslackでおこなう こんな感じで当番をお知らせしています。 良かったこと チームとして: 対応するタスクの偏りが少なくなった もともと自主的に拾う文化があったとはいえ、それぞれの得意領域や興味はバラバラなのでどうしても対応するタスクの種類は多少偏ります。 当番botを始めることによって、この偏りが少しなくなったと感じています。 個人として: 心理的なブレーキがなくなる これは入社したばかりの私だからこそ感じる事かもしれませんが、 問い合わせや突発的に発生するトラブルは初見のものが多く、中には解決までの糸口が見当もつかないものもあります。 そういった問題に手を挙げるのは、人によっては億劫になってしまうかと思います。 しかし、当番という役割があることによって手を挙げざるを得ない状況になるので、 「手を挙げたはいいが、解決できなかったらどうしよう」という心理的なブレーキがなくなります。 このブレーキがなくなったおかげで今では臆することなく、何か問題があればとりあえず手を挙げることができるようになったと感じています。 他メンバー、他チームとのコミュニケーション機会が増える 対応する問題の中には、1人で解決できないものももちろんあるので、他のメンバーに質問することになります。 入社初期の段階で、質問を通してメンバーとのコミュニケーションを図ることができました。 他にも、他チームの方と関わる機会が増えたりと、当番botを導入することで、結果自分のためになることが多くありました。 botの 改善 同じ組み合わせの担当だとマンネリ化してくるので、ランダムに組み合わせを変えていく仕組みを入れたいなと考えています。 また、今のbotはただ当番を報告するだけのものなので、 メッセージに遊び心を入れるなどして、見た人のモチベーションが少しでも上がるような工夫ができればと思っています。 最後に 私は主体的にボールを拾っていくBASEの文化が好きなので、当番botを導入することには少し懐疑的でした。 しかし前述した通り、結果として自分にとってためになったことが多くあったなと感じています。 BASEにはMove Fastという行動指針があります。 これは「速く動くこと。多くの挑戦から多くを学ぶために、まずはやってみよう。」という意味が込められていますが、 今回の当番botを通して、あれこれ悩む前に先に行動することで得られることは多々あるという学びになりました。 明日はプロダクトマネジメントグループの船坂さんとエンジニアの白数さんです。お楽しみに!
アバター
この記事はBASE Advent Calendar 2019の9日目の記事です。 devblog.thebase.in はじめまして。BASE株式会社のtatsuと申します。 最近、業務にて guzzle を使う機会がありました。結論から述べますと guzzle のみで実現することは出来ず Amazon sqs を併用するという形で落ち着いたのですが、いくつか知見を得ることも出来たのでその事について書きたいと思います。 主に guzzle/Pool と guzzle/RetryMiddleware の話になります。 最初の壁:ResponseがどのRequestの結果なのか分からん まず並列処理を実装しました。実際のものとは違いますが流れは一緒。 $urls = [ 'https://example_base.in/1', 'https://example_base.in/2', ]; $guzzle_client = new Client(); $requests = function ($urls) use ($guzzle_client) { foreach ($urls as $url) { yield function () use ($guzzle_client, $url) { return $guzzle_client- > postAsync($url); }; } }; (new Pool($guzzle_client, $requests($urls), [ 'concurrency' = > 10, 'fulfilled' = > function ($response, $index) { // dbに保存とか }, 'rejected' = > function ($reason, $index) { // dbに保存,ログ出力とか } ]))- > promise()- > wait(); レスポンスを受け取って保存という段階で 「どのリクエストの結果を受け取っているか分からん。」となりました。 送信は request の生成順に処理されますが、結果はもちろん順不同で返ってきます。 そりゃ並列処理ですからね… この問題の解決策は簡単で、request 生成部分を少し変えるだけ。 $urls = [ 'index1' = > 'https://example_base.in/1', 'index2' = > 'https://example_base.in/2', ]; $requests = function ($urls) use ($guzzle_client) { foreach ($urls as $index = > $url) { yield $index = > function () use ($guzzle_client, $url) { return $guzzle_client- > postAsync($url); }; } }; これで pool の成否処理の第2引数が'index1','index2'といった値になります。何もしないとrequest の生成順に整数が振られますが、上記の様に任意の値を渡すことも出来ます。 ちなみに失敗時の第1引数には基本的に exception が入ってきます。 下記を参考に処理を分けてあげると良いかもしれません。 http://docs.guzzlephp.org/en/latest/quickstart.html#exceptions 二つ目の壁:Retryの度に結果を保持したい 一応の並列処理は出来た。ということで retry についても考えてみます。 guzzle では RetryMiddleware というリトライの方法を標準で用意してくれています。 // clientに設定 $handler_stack = HandlerStack::create(new CurlHandler()); $handler_stack- > push(Middleware::retry(retryDecider(), retryDelay())); $guzzle_client = new Client(['handler' = > $handler_stack]); function retryDecider() { return function ( $retries, Request $request, Response $response = null, RequestException $ exception = null ) { // 最大5回 if ($retries > = 5) { return false; } // 4xx or 5xx はリトライ if ($response && $response- > getStatusCode() > = 400) { return true; } return false; }; } function retryDelay() { return function ($retries) { return (int) pow(2, $retries - 1) * 1000; }; } 上記の例はかなりシンプルですが、リトライの判断部分と待機時間を実装して Middleware::retry() に渡してあげれば良しなにリトライしてくれます。 注意点としては、delay は単位がミリ秒です。timeout は単位が秒なのに http://docs.guzzlephp.org/en/stable/request-options.html#delay ここでまた問題が生じました。ある理由から最終的な成否だけでなく、retry 時の結果も逐一保持したいとなりました。さて困った。 pool の成否へは request がすべき動作が終わってから到達します。つまり retry 時には実行されません。そうなると retry の中で保持を行う必要があります。 retryDecider() で行いたいところですが、どのリクエストの retry なのか判別する必要があります。上記の様に request が入ってきているので、request-header 等に識別子を設定して判別する事も出来ます。ただ、こちら側の都合を request に持たせるのはどうなのかという考えが浮かび別の方法を取ることにしました。 RetryMiddleware を自作してみました。 …こう言うと凄そうに聞こえるかもしれませんが、実際は標準のものをちょっと変えただけです。 全文は長いので変更箇所の辺りだけです。 private function onFulfilled(RequestInterface $req, array $options) { return function ($value) use ($req, $options) { if (!call_user_func( $this- > decider, //$options['retries'], $options $req, $value, null )) { return $value; } return $this- > doRetry($req, $options, $value); }; } 上記と同じ事を onRejected() にも deciderに $options['retries'] (リトライ回数)では無く $options ごと渡す様にしました。 これで request-options に識別子を持たせることでリクエストを判別する事ができる様になりました。request-ooptions は key を既存のものと被らない様にすれば影響はないですし、リクエスト先にも渡ることはありません。 $options['retries'] 自体は $options に同keyが無ければ __invoke() で追加されます。つまり RetryMiddleware の外から操作する事も出来るということでもあります。 少し長くなってしまいましたが、これで pool × retry の実装が出来た! …と思っていました。この時は 三つ目の壁:PoolとRetryMiddlewareって併用出来ないの&リトライ中にプロセス死んじゃう 開発の世界で三段オチを味わうことになろうとは…。 それぞれをさっくり解説しますと PoolとRetryMiddlewareって併用出来ないの リトライするテストを書いていたら何かおかしいということで検索したところ、「asyncRequest() と RetryMiddleware を併用すると同期してしまいます」といった情報を見つけました。いやいやいや、そんなはずは…と思いつつ試してみました。 方法は簡単でリクエスト毎にリトライの待機時間を変えてみます。 request1 は[2つ目の壁]にある様に回数に応じてべき秒で増えていき、request2 は2秒固定でリトライを繰り返してみます。並列処理が非同期で行われているのであれば、4回目で順番が入れ替わりrequest2が連続するはずです。結果は…? 綺麗にrequest1とrequest2が交互に並び続けました。ワーイ さらに追い打ちをかける様にもう一つ問題が発覚します。 リトライ中にプロセス死んじゃう これは一般的ではない環境での事象ですので詳細は省きますが、あるタイミングで実行中のプロセスが強制的に終了させられてしまうという事がわかりました。終了までに多少の猶予があるのですがリトライの待機時間によっては、待機中に強制終了されてしまい終了に備える事が出来ないのです。プロセスの管理側からの操作なので内部で対策をするにも限界がりそうでした。ちなみに強制終了後すぐに再起動されます。 上記二点の問題が浮上し悩んでいた所、同僚からこんな言葉が(実際にはgitのコメント)…。 「リトライをAmazon sqsに任せちゃうのは?」 …なるほど? 結果的にこれが自分がとった解決方法となりました。 sqs に関しての説明は長くなってしまうので「便利なメッセージキューイングサービス」とだけ言わせて頂きます。その sqs の機能の一つで遅延機能があります。 https://docs.aws.amazon.com/ja_jp/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-delay-queues.html これと実装してきた guzzle を組み合わせる事で上手くいきそうです。 リトライの判断と待機時間の算出はそのまま使い、失敗してリトライが必要な時に待機時間を持たせて sqs に投げる様にします。 待機自体は sqs でするので強制終了の猶予時間以上の処理は無いので、終了に備える事が出来ます。 イレギュラー中のイレギュラーとはいえ起こり得る事に対処出来て良かった。 まとめ この方法を採った大きな要因として、そもそも作っていたものが sqs からキューを受け取ってリクエストを送るというものだったという事があります。なので sqs の導入コストはほぼ0でした。 そもそも使っていたなら気づけよとも思いますが、こういう時って離れて視野を広くするのがなかなか難しかったりします。 そんな時に第三者の視点から意見を貰えるのは本当に有り難いですよね。同僚の言葉が無ければ時間を掛けて guzzle で頑張っていたと思います。それも不正解では無いと思いますが、今回は sqs との連携を選んで良かったと感じています。また guzzle で色々やったからこそ、それほど苦労せずに連携させられたという事もあるはずです。 長くなってしまいましたが、業務上の課題は積極的にオープンにすると良さそうというお話でした(guzzleどこいった)。 明日は id:beerbierbear と id:yk4o4 の記事です。お楽しみに!
アバター
この記事はBASE Advent Calendar 2019 9日目の記事です。 devblog.thebase.in こんにちは、BASE株式会社 ランニング部部長の元木です。 フルマラソンのサブスリー達成を目指して日々トレーニングに励む傍ら、Owners Marketingというチームでサーバーサイドエンジニアもやっております。 前書き 弊社が提供するネットショップ作成サービス「BASE」(以下「WEB」)とショッピングアプリ「BASE」(以下「アプリ」)では、 Amazon CloudSearch (以下「CloudSearch」)を利用して商品の検索機能を提供しております。 当記事では、 CloudSearch のインデックスを更新する処理を、どのようにバッチからワーカーに置き換えたのかをご紹介させていただきます。 この記事は、「 BASE Advent Calendar 2019 」の9日目の記事です。 バッチの何が問題だったのか 商品データは、ショップオーナーさんや購入者のアクションによって刻々と変化していきます。 BASEのシステムは、その商品データの変化を捉えて CloudSearch のインデックスを更新していくこととなります。 CloudSearch のインデックスを更新するのは比較的時間がかかる処理であることと、 CloudSearch には 「更新の頻度が10秒に1回を超えると、スロットリングが発生する場合がある」 という制限があるため、BASE ではバッチプログラムを用いて、非同期でこの処理を実行しておりました。 しかし、バッチ処理には インデクシングが必要な商品数は日々増えていくが、処理をスケールできない DBへの負荷が高い という問題がありました。 試験的に、アプリ側には Amazon SQS (以下「SQS」)をポーリングして CloudSearch のインデックスを更新するワーカーが導入されておりましたが、一部のアクション(商品の登録・更新・削除)しかカバーしておりませんでした。 以下が、切り替え前のシステム構成です。 弊社CTOの  @dmnlk と話した結果、これを以下のような構成に変更することが決まりました。 SQS の前段に Amazon SNS (以下「SNS」)を配置する。 WEB用の SQS を新たに追加する- アプリ側、WEB側ともに、バッチをワーカーに完全に置き換える 以下が、目標とするシステム構成です。 ちなみにこの時の私の Amazon SNS に対する理解度は、こんな感じでした。 次からは、どのような段取りでシステム構成を切り替えていったのかをご説明します。 バッチからワーカーへ 手順1 : Amazon SNS の配置   まず、SQS の前段に SNS を配置し、商品データが更新された際の通知先を SQS から SNS に切り替えました。 アプリケーションの変更点としては、インデックスの更新が必要になるアクションが発生した際の通知先を SQS から SNS に切り替えるだけです。 手順2 : WEB用のワーカーとSQSの配置 次に、WEB用のワーカーと SQS を配置し、ポーリングを開始しました。 まだ、一部のアクションしか SNS に通知されない状況ですので、引き続きワーカーとバッチを並行稼動させています。 WEB用のワーカーを実装する際には、バッチとワーカーが同じ動作をすることを保証するために バッチのテストコードを書く(それまでは無かった) バッチのテストコードとワーカーのテストコードとでデータプロバイダーを共通化し、入力と出力が一致することを保証する といった工夫も行ないました。 SNS と WEB用の SQS を連携させる際も、いきなり全てのイベントを通知させるのではなく、 SNS のメッセージ・フィルタ機能 を利用して通知するイベントを徐々に増やしていくようにしました。 手順3 : Amazon SNS に通知するアクションを増やしていく 次に、SNSに通知するアクションを徐々に増やしていき、最終的に CloudSearch のインデックスを更新する必要のある全てのアクションが SNS に通知されるようにしました。 ここでも、一気に全アクションを通知するよう変更するのではなく、段階的に通知するアクションを増やしていくようにしました。 実は、手順2, 3 を実施していく過程で何度かWEB用のワーカーに不具合が見つかり、改修を行なっております。 しかし、バッチとの並行稼動を続けていたことと、ワーカーの仕事量を少しずつ増やしていくようにしていたため、大きな問題に発展することはありませんでした。 手順4 : バッチの退役 全てのアクションが SNS に通知されるようになったところでバッチを退役させ、すべての作業が完了しました。 まとめ サービスが成長していくにつれて、以前は問題なかったシステムから問題が生じるようになることは往々にしてありえます。 一方で、システム構成の変更は比較的リスクが高い上に(何か新しい機能を提供するわけではないため)地味な作業であるため、ついつい、問題が大きくなるまで放置してしまいがちです。 私としては、作業の段取りを工夫することでリスクを最小限に抑えつつ、サービスの成長に応えられるシステムを構築し続けていきたいと考えております。
アバター
この記事はBASE dvent Calendar 2019 8日目の記事です。 devblog.thebase.in こんにちは、はじめまして。 2019年8月に入社したデザイナーの石井です。 入社してからEコマースプラットフォーム「BASE」のWebやアプリ、グラフィックなど、様々なデザインを制作してきました。 各プロダクトのデザイン制作時、UIを作成する際にチームのコミュニケーション、共通理解、制約条件をそろえてデザインしていくことが大変重要です。 それらをより活発に、より洗練されたものにするために最近ではデザインシステムの構築に励んでいます。 今回はショッピングアプリ「BASE」においてのデザイン言語システムの初期の段階(β Version)についてお話をさせていただければと思います。 デザイン言語システムの必要性 僕は過去数年間ほどWebとモバイルのアプリケーションの構築と設計を続けてきました。エンジニアやディレクターなど様々なメンバーと協力してプロダクトを作る経験をしてきた中で、より効率的にプロダクトを成長させるための設計システムを構築する方法を学んできました。 デザイン言語システムに求められる要素はたくさんあります。タイポグラフィ、レイアウトとグリッド、色、アイコン、コンポーネント、コーディング規約から、音声とトーン、スタイルガイド、ドキュメントなど多岐にわたります。 そして各デザイナーの考えをデザインシステム上で他のメンバーに適切に共有し、一貫性を保てるように設計する必要があります。そして、チーム全体でデザインシステムを改善していくことで、より良いプロダクト作りを支えていく基盤となります。 デザイン言語システムがあることで、個別のUIを考える時間を短縮し、どのようなユーザー体験を作るかを考えることに集中できます。また、デザイナー間の共通認識ができることで、複数のデザイナーが同じプロダクトの制作に関わる際にも一貫したユーザー体験の提供することにつながります。 ショッピングアプリ「BASE」のデザイン体制 僕が入社するまで、ショッピングアプリ「BASE」の担当デザイナーは1人でした。その時点ではデザイン言語システムはありませんでした。 入社後、僕もアプリのデザインを担当することになり、また今後新たに入社してくるデザイナーがアプリを担当する可能性も鑑み、デザイン言語システムの構築を始めました。 どうやって構築していったか デザイン言語システム構築を開始する前に、基本的なスタイルガイドを作成しました。ここで、タイポグラフィ、色、アイコン、情報アーキテクチャを大まかに定義しました。 こちらは、タイポグラフィーの定義、カラーの使用パターンと洗い出し、スペーシングの確立後のスタイルガイドです。 さらにiOSおよびAndroid共通のアイコンのガイドラインを確立したものがこちらです。 標準化されたコンポーネントの定義 従来、多くのスタイルガイドにおいて、コンポーネントをアトミックコンポーネントとして定義し、それを使用してより複雑な分子を構築されているのを見てきました。 理論的には、これは首尾一貫した柔軟なシステムを作成するためにうまく機能します。 しかし実際には、これらの再利用可能な原子がさまざまな方法で使用され、あらゆる種類の分子を作成できることがよくあります。これは一貫性のあるデザイン言語システムの維持を妨げ、一貫したユーザー体験の創出を阻害してしまう可能性があります。 これらのコンポーネントを作成する際、ライブラリと呼ばれるマスターファイルにそれらを集約しました。またマスターファイルの更新は僕自身のみで行えるようにしています。 ライブラリを構築している間に、個々のコンポーネントを類似のアイテムを含むアートボードに整理し始めました。 これらのアートボードは、一般的なカテゴリ別にナビゲーション、マーキー、コンテンツ、画像等に分類しました。 これらのコンポーネントをiOSおよびAndroid用に1セット作成しました。タブレット用のコンポーネントはモバイルのコンポーネントとほぼ同じなので、技術的なレベルでは、コードは2つの異なるスタイルがあれば済みます。 デザイナーは、共通のコンポーネントを使用して画面を設計することができ、iOSやAndroidだけでなくさまざまな画面サイズに簡単に適応させることができるようになります。 このコンポーネントスタイルガイドはまだ議論が始まったばかりでお見せできるものはありませんが、今までチーム内で共通のコンポーネントの共通理解や作業方法の一貫性がなかったのでそこをシステム化に現在取り組んでいます。 おわりに アプリのデザイン言語システムの発展はまだまだこれからです。引き続きMove Fastなプロダクト作りの基盤の整備に努めていきます。 明日はエンジニアの齋藤さんと元木さんです!
アバター
この記事はBASE Advent Calendar 2019の8日目の記事です。 devblog.thebase.in エンジニアの右京です! みなさん! Storybook は使っていますか?BASE では UIコンポーネントの社内展開 はもちろん、日々の業務の中でもサンプルの実装を共有したりするために Storybook が使われています。BASEではこれを「特定のリポジトリにコードをコミットすると、自動的に社内向けサーバーへデプロイされる仕組み (ようするに社内 GitHub Pages ですね)」を利用して社内共有しているのですが、毎度のセットアップが大変なので Gtihub Actions を使ってお手軽に設定できるようにしてみたよ、という内容です。 github.co.jp TL;DR 社内用向けドキュメントサーバーへのデプロイを他のリポジトリから使いやすいように Action 化して配信するようにしました プライベートリポジトリにある Action は直接参照することができないので Personal access token と npx を使います CircleCI から GitHub Actions に変更することで様々なイベントにフックすることができ、柔軟性のあるデプロイが可能になりました デプロイ(コミット)の Action 化 これまでこのサーバーを利用したい時は、利用側の CircleCI で専用のリポジトリを clone して commit を作って push して...といった処理を用意する必要がありました。利用するプロジェクトが増えていく中、この利用法のスケールのしにくさに問題を感じていたため、GitHub Actions でスパっといけないか?と思ったのが今回のスタート地点です。 このエントリでは今後はこのリポジトリのことを「static-pages-repo」と呼ぶことにします。 まず、前提として static-pages-repo は master に push されると public ディレクトリ以下が自動で社内向けのサーバーへデプロイされるようになっています。 └── public ├── webservice1/master/... ├── webservice1/hogefuga/... ├── webservice2/master/... ├── ... └── index.html // デプロイされているURLのリストが入っています 今回作る Action では、上記の構成に沿って以下のフローを実行します この配置に合わせて利用側から成果物をコピー コピーした成果物をコミット アクセスしやすいように public/index.html を更新 リモートへ push これを行うためには3つの引数が必要そうです。 public の配置を決定するためのプロジェクト名(prefix) public の配置を決定するためのブランチ名 成果物(コピー元)のパスの指定 つまり Action としてはこんな感じに利用できるのがよさそうです。 - uses : static-pages-repo with : project : webservice1 branch : feature/hogefuga source : dist 早速実装に...と行きたいところですが、気をつけるべき点が2つあります。 プライベートリポジトリのアクションは直接実行することはできない Workflow 内で他のリポジトリを扱う場合は secrets.GITHUB_TOKEN では権限不足 それぞれ見ていきます。 プライベートリポジトリのアクションは直接実行することはできない パブリックなリポジトリの場合は リポジトリ名やパスを指定することで 直接他のリポジトリから Action を参照して、利用することできます。ですが、プライベートリポジトリの場合はこの方法は使えないため、回避方法を考える必要があります。 static-pages-repo を clone...? と思いましたが、それだとあまり現状と変わらない...ので多少見栄えがよくなりそうだと npx で Action をインストールできるようにしてみることにしました。 一度 npx 経由で Action をコピーしてしまうことで、 自身のリポジトリ内の Action として実行します。 - run : npx https://github.com/baseinc/static-pages-repo install - uses : ./.github/actions/static-pages-repo Workflow 内で他のリポジトリを扱う場合は secrets.GITHUB_TOKEN では権限不足 GitHub Actions を使ったことがある人は知っているかもしれないのですが、元々 Workflow で使える変数として secrets.GITHUB_TOKEN が用意されています。通常であれば、このトークンを使えばプライベートリポジトリでも GitHub API を呼び出したり、リポジトリに Git での書き込みができるのですが、残念ながらこのトークンではリポジトリを跨いだ操作を行うことはできません。 これは代わりに Personal access token (以下PAT) を使用することで解決します。 actions/checkout にもこれに関する記述 があるので参考にしてみてください。 実際に Action を作る では、以上の2つの注意点に気をつけながら実際に Action を作っていきます。 Action の開発方法には Docker コンテナ版 と JavaScript 版 があり、今回は Git での操作がメインになりそうだなという理由でシンプルに実装できそうなDockerコンテナ版を選択しました。 公式ドキュメントにそってまず Dockerfile を作ります。alpine でもよいですが、 public/index.html を Node.js で作成しているため、 node:12 を選択しています。 ファイルの置き方ですが、これらを直接 Actions が実行するわけではないので(コピー元)、安直に action というディレクトリを作って配置することにしました。 FROM node:12 COPY entrypoint.sh /entrypoint.sh ENTRYPOINT ["/entrypoint.sh"] 次に action.yml を作ります。action.yml は引数や出力、実行方法を設定するためのファイルです。 name : 'static-pages-repo' description : 'deploy static pages' inputs : # ここに追加したキーが引数となります pat : description : 'Actionで必要になるPAT' required : true project : description : 'ディレクトリ名のprefix' required : true branch : description : 'ディレクトリ名' required : false default : 'master' source : description : '配信したいコンテンツへのパス' required : true outputs : url : description : 'デプロイ先となるURL' runs : using : 'docker' image : 'Dockerfile' args : - ${{ inputs.pat }} - ${{ inputs.project }} - ${{ inputs.branch }} - ${{ inputs.source }} 最後に entrypoint.sh を作って実際に動かすスクリプトを書いていきます。PAT を受け取って、static-pages-repoを操作していきます。 #!/bin/sh -l # 作業用のリポジトリをクローンします、PAT($1)を付加していることに気をつけてください git clone https:// $1 @github.com/baseinc/static-pages-repo.git .github/tmp/repo # project/branchなディレクトリを作って成果物をsourceからコピーしてきます mkdir -p .github/tmp/repo/public/ $2 / $3 cp -rT $4 .github/tmp/repo/public/ $2 / $3 cd .github/tmp/repo # 配置されたファイルへのリンクを含む public/index.html を生成します npm ci npm run build-index # 最終的に差分が生まれていれば git commit して push します git add . git config user.name actions git config user.email actions@binc.jp if git commit --dry-run > /dev/null ; then git commit -m " Update $2 / $3 " git push origin HEAD fi git push https:// $1 @github.com/baseinc/static-pages-repo.git master # このようにして値を出力しておくと、次の step でこれを元に検証したりすることができます echo " ::set-output name=url::https://static-pages-repo.com/ $2 / $3 / " Action を配信する npx スクリプトを作る npx にあまり馴染みのない方もいるかもしれませんが、簡単にいえば「インストールせずに一度だけ使えるパッケージ」という感じでしょうか。 package.json を作って以下のようなスクリプトで Action をローカルにコピーしています。 #!/usr/bin/env node const path = require( 'path' ) const fs = require( 'fs-extra' ) const src = path.resolve(__dirname, '../action' ) const out = path.resolve(process.cwd(), '.github/actions' ) ;(async() => { await fs.ensureDir(out) await fs.copy(src, `$ { out } / static -pages-repot`) } )() これを上のものと合わせると、最終的には他のリポジトリからこのような step で static-pages-repo デプロイすることができるようになります。 - run : npx https://${{secrets.PAT}}@github.com/baseinc/static-pages-repo install - uses : ./.github/actions/static-pages-repo with : pat : ${{ secrets.PAT }} project : webservice source : dist 便利そうに見えませんか?実際に PAT を設定するのは、利用側であることに気をつけてください。 これで static-pages-repo 側は完成です! ここまでのディレクトリ構造はこのようになっています。 ├── action │ ├── Dockerfile │ ├── action.yml │ └── entrypoint.sh ├── bin │ └── install.js // npx で実行されるスクリプト ├── webroot │ ├── webservice1/master/... │ ├── webservice1/hogefuga/... │ ├── webservice2/master/... │ ├── ... │ └── index.html ├── README.md ├── package-lock.json └── package.json 実際に Action を使ってデプロイする これで準備が整ったので、あとは各リポジトリに導入するだけです! 今回は、実際に BASE で運用しているパターンから3つをご紹介します。 master に push されたらデプロイ Pull Request が作られたらデプロイ Pull Request に特定のコメントがついたらデプロイ master に push されたらデプロイ 一番簡単でわかりやすい、馴染みのある感じだと思います。 ほとんどのリポジトリは、master にマージされたときに更新を実行するようにしています。 name : deploy master on : push : branches : - master jobs : build : runs-on : ubuntu-latest steps : - uses : actions/checkout@v1 - uses : actions/setup-node@v1 with : node-version : '12.x' - uses : actions/cache@v1 with : path : node_modules key : ${{ runner.os }}-node-${{ hashFiles('**/yarn.lock') }} restore-keys : | ${{ runner.os }}-node- - run : yarn install --frozen-lockfile - run : yarn storybook -o dist - run : npx https://${{secrets.PAT}}@github.com/baseinc/static-pages-repo install - uses : ./.github/actions/static-pages-repo with : pat : ${{ secrets.PAT }} project : webservice source : dist Pull Request が作られたらデプロイ UIコンポーネントのような基本的に Storyboard がセットになるものはこれを設定しています。 Pull Request が Open されると同時にデプロイされるため、レビューの際にはスムーズに Storybook を確認することができます。 name : open pull-request on : pull_request : types : [ opened, reopened ] jobs : build : runs-on : ubuntu-latest steps : - uses : actions/checkout@v1 - uses : actions/setup-node@v1 with : node-version : '12.x' - run : yarn install --frozen-lockfile - run : yarn storybook -o dist - run : npx https://${{secrets.PAT}}@github.com/baseinc/static-pages-repo install - id : deploy uses : ./.github/actions/static-pages-repo with : pat : ${{ secrets.PAT }} project : webservice branch : ${{ github.event.pull_request.head.ref }} # 実行時の event を受け取って使うことができるため、ここから branch 名を取得して決定しています source : dist - if : "steps.deploy.outputs.url" # 前の step が成功したときに発行される url があったら、PRのコメントにURLを書き込みます env : URL : ${{ steps.deploy.outputs.url }} API_ENDPOINT : ${{ github.event.pull_request.comments_url }} GITHUB_TOKEN : ${{ secrets.GITHUB_TOKEN }} # ここは同じリポジトリ内なので GITHUB_TOKEN でアクセスできます run : | curl -X POST -H "Authorization: token ${GITHUB_TOKEN}" -i ${API_ENDPOINT} -d "`printf '{ \" body \" : \" deploy to %s \" }' ${URL}`" Pull Request に特定のコメントがついたらデプロイ 必ずしも Storybook が必要ではないアプリケーションのリポジトリでは、Pull Request にコマンドコメントを書くことで任意のタイミングでデプロイが行えるようにしています。 これには issue_comment というイベントを使うのですが、これは特定ブランチには紐づかないイベントで、デフォルトブランチを起点に実行されるため少し複雑です。 name : on comment pull request on : issue_comment : types : [ created ] jobs : build : runs-on : ubuntu-latest steps : - id : check # コメントに deploy-storybook の文字列が含まれていて、pull_requrest なら実行します if : "contains(github.event.comment.body, 'deploy-storybook') && github.event.issue.pull_request" env : API_ENDPOINT : ${{ github.event.issue.pull_request.url }} GITHUB_TOKEN : ${{ secrets.GITHUB_TOKEN }} run : | # pull_request のデータを API 経由で取得して、ブランチ名を特定します branch=`curl -X GET -H "Authorization: token ${GITHUB_TOKEN}" ${API_ENDPOINT} | jq -r '.head.ref' ` echo ::set-output name=branch::$branch # このようにすることで id(=check) に紐づく outputs を step からも書き出すことができます # これ以降は branch が特定できていたら続行します - if : "steps.check.outputs.branch" uses : actions/checkout@v1 with : ref : ${{ steps.check.outputs.branch }} - if : "steps.check.outputs.branch" uses : actions/setup-node@v1 with : node-version : '12.x' - if : "steps.check.outputs.branch" run : | yarn install --frozen-lockfile yarn storybook -o dist npx https://${{secrets.PAT}}@github.com/baseinc/static-pages-repo install - id : deploy if : "steps.check.outputs.branch" uses : ./.github/actions/static-pages-repo with : pat : ${{ secrets.PAT }} project : webservice branch : ${{ steps.check.outputs.branch }} # 前の step で特定した branch 名を使用します source : dist - if : "steps.deploy.outputs.url" env : URL : ${{ steps.deploy.outputs.url }} API_ENDPOINT : ${{ github.event.issue.comments_url }} GITHUB_TOKEN : ${{ secrets.GITHUB_TOKEN }} run : | curl -X POST -H "Authorization: token ${GITHUB_TOKEN}" -i ${API_ENDPOINT} -d "`printf '{ \" body \" : \" deploy to %s \" }' ${URL}`" 一つ気をつけるべき点として、 job 自体にも if を設定でき もっと簡潔にかけそうなのですが、if が通らなかった場合に job が 0個となり Workflow 自体が失敗したことになってしまいます。 また、実際にはこれらと「 Pull Request が close された際にディレクトリを削除する Action 」をあわせて運用しています。 めでたしめでたし Storybook を Move Fast にデプロイできるようになったことで、 Speak Openlyな開発 にまた一歩近くことができたと思います。 お気づきの方もいると思いますが、単なる静的ファイルホスティングなので Storybook 以外にももちろん使うことができます! 明日は id:kmotoki と id:tatsuta2 です!
アバター
はじめに この記事はBASE Advent Calendar 2019の7日目の記事です。 devblog.thebase.in こんにちは、BASE投資部部長の菊地です!2019年は多くのBASE社員のマネーリテラシーを高めることができて、なかなか満足のいく一年となりました! さて、2019年業務の方がどうだったか振り返ってみると1月にエンジニアリングマネージャー(以下EM)に就任し、7月からは約20名のエンジニアが所属するService Devというセクションのマネージャーに就任しました。カレンダーを振り返ってみると今年は1on1を400回、採用面接は70回ほど行ったりとマネージャー業にどっぷりと浸かった一年となりました。 巷ではピープルマネジメントに費やす時間が増え、コードを書く時間が減ることが多いEMに対してネガティブな印象を持つエンジニアが多いようです。 しかし私はこの一年間EMとしての役割を果たすことでやりがいを感じることがたくさんありました。みなさんにEMって楽しそうだなと思って頂けたら嬉しいと思い、そのいくつかを紹介させて頂こうと筆を取りました。 BASEにおけるEMの役割 昨年、私の上司である@fshinが「 エンジニアとしてワクワクし続けるためのエンジニアリングマネージャという役割分担 」という記事を書いてEMの役割について言語化しています。 この記事にあるようにBASEのEMに求められる役割を一言で言うと 「事業、プロダクトに貢献しながら、チームのエンジニアの活躍にコミットすることで、メンバーの評価を上げること」 ということになります。 会社によってはEMが技術をリードする責任も求められる場合もあるようですが、BASEではエンジニアの上位職としてEMの他に技術でリードするテックリード(以下TL)という役職が存在するのでEMはピープルマネジメントだったりに専念することができます。(逆に言うとTLはピープルマネジメントを求められないので、技術に専念することができます) 私がやりがいを感じるとき さてここからは私がEMをやっていてやりがいを感じる瞬間を、それにまつわる取り組みなどを交えながら紹介させて頂きます。 チームメンバーを輝かせられたとき EMの仕事をマネジメントというとなんだか堅苦しい印象ですが、より強いエンジニア組織を作る、メンバーの成長を支援して市場価値を高めるという側面ではプロデューサーと呼んだ方が個人的にはしっくりきます。もっと輝けるのではと思った人をプロデュースして、それまで以上の輝きを放つようになったときに私はEMとしてのやりがいを感じます。 例えば黙々とコードを書いていてあまり目立たっていないと感じていたチームメンバーが、私が背中を押すことでグングンと成長し今では社内の誰からも信頼され頼られる存在に成長した人がいます。これぞEM冥利に尽きる瞬間です。私は彼の社内でのプレゼンスを上げるために次のような働きかけをしました。 アイコン重要 BASEではSlack, G Suite(GmailやGoogle カレンダー等), Asana, Kibelaなどなど多くのツールを使用しています。社員数が100人を超え社内の認知を得ることが難しいフェーズになっているので、アイコンにこだわりを持つことは社内で認知されるために非常に重要だと考えていました。 しかし当時彼はアイコンを何も設定していませんでした。そこで私は「アイコンは必ず設定しよう」、「各ツールでアイコンを統一しよう」、「アイコンはキャッチーなものにしよう」とアドバイスをしました。その結果今では社内のほとんどの人が彼のアイコンを見れば彼のことを想起できるようになり認知度が飛躍的に高まりました。 他部署の人との関わり重要 日常的にカスタマーサポートチーム等の他部署からお問い合わせ調査依頼がやってきます。こういう依頼があった際はなるべく拾いにいくように伝えました。理由はお問い合わせの調査をすることでBASEのサービスやシステムの理解が深まりますし、他部署のメンバーからの信頼を得ることができるからです。 最初の頃は積極的には拾いにいっていなかったので、私が拾って彼に振っていたのですが、いつしか抵抗がなくなったのか今では自ら進んで拾いに行ってくれるようになりました。自分からタスクを取りにいくような働き方になった結果、多くの人から頼られる存在になりました。 理想としているチームに近づいているのが目に見えたとき 突然ですが、 wevox というサービスをご存知でしょうか。wevoxとは「定期的なアンケートを実施することで、社員の仕事や会社とのエンゲージメントを数値化しよう」というサービスです。 BASEの場合は3ヶ月に一度全社員からアンケートをとって、「上司との関係性は10点中9点で世の平均よりも良好です」といったようにたくさんの項目が個人レベルで数値化されマネージャーに共有されます。(匿名と実名での回答が設定でき、BASEでは現在実名で回答する形式をとっています) 私は「明るく、楽しく、健康的」なチームを目指しているので、wevoxでそういった項目の数値が高かったり前回よりも改善されていると自分の日々の取り組みの成果が現れ、目指しているチームに近づいているのだなと喜びを感じます。 メンバーと頻度多くコミュニケーションを取ることでお互い何でも言い合える関係を築くことがEMにとって何よりも大事だと考えているので、そのためにいくつかの取り組みを行なっています。 1on1 もはや定番になっているかもしれませんが、1on1がEMにとって最も重要な業務であると考え、全ての業務より優先して全メンバーと毎週30分行なっています。主に1on1のバイブルとなっている『 ヤフーの1on1 』に書かれている下記のようなことを実践しています。 この本には1on1の目的は「メンバーの才能と情熱を解き放つこと」と書かれていますが、まずはシンプルに雑談でもいいからとにかく毎週30分行うということに専念しました。 定番の質問 自分が部下として1on1をしていた時を思い返しても「何かお困りごとはありますか?」といきなり聞かれてもなかなか思いつかないものでした。ですので「以下の質問を毎回聞くので何か考えてきてください」とチームメンバーに伝えるようにしています。これらの質問も『ヤフーの1on1』を参考にしたものです。 今日は何の話をしましょうか? 何かお困りごとはありますか? 私(EM)にできることはありますか? 毎回聞くからねと宣言することによって、1on1の時にメンバーが議題を用意してくれるということが増え、1on1の充実度が上がったように思います。 予習 1on1を行うにあたって1人につき15分ほど予習を行なっています。チームメンバーには毎週末に週報を書いてもらっているので週報を確認して一週間にどのようなことをしていたのか把握したり、タスクの進捗は順調そうか確認したり、これまでの1on1の議事録を見返したりすることで話したい内容をある程度箇条書きして臨むようにしています。そういった準備をすることでチームメンバーが今困っていることなどに気づきやすくなるといった効果があると感じています。 議事録 毎週10人近くのメンバーと1on1を行なっていると、誰が何を言っていたのか分からなくなることがあります。また自分が誰に何を言っていたのかも分からなくなることがあります。そのようでは信頼を積み重ねることは難しいと感じ、毎回議事録をとって、終わったら議事録の内容をSlackでDMするようにしています。信頼を積み重ねるにはとても大事だと認識しているのですがけっこうサボりがちなので反省しています、2020年はサボらないで頑張りたい。 ニチレイ(日例) 毎日12時からスタンディング形式でチームメンバーが集まって今やっていることや困っていることを5分ほどの時間を使って共有しあう時間をとっています。議論したり雑談をしたりとチーム内のコミュニケーションを毎日作れるのでニチレイの重要度はとても高いと考えています。 Unipos Unipos というのは社内のメンバー間で感謝を送り合うサービスです。 感謝することも、感謝されることもやりがいを持って業務に取り組むためにはとても重要であると考えているのでチームのメンバーには感謝のハードルを極力低く設定して、たくさん感謝を送り合うように伝えています。 チームメンバーを採用できたとき BASEではスクラム採用に取り組んでいて、エンジニアの採用にはEMがフルコミット(スカウト、カジュアル面談、書類選考、面接、オファー面談、採用会食など盛り沢山)しています。BASEのスクラム採用について興味のある方は弊社採用マネージャーの 米田が登場している記事 をご参照ください。 さて、採用活動がEMにとって最重要タスクであることは言うまでもありませんが、一方でなかなか成果に結びつきにくいものです。100人カジュアル面談を実施して2,3人入社して頂ければ御の字といったところではないでしょうか。 BASEから内定をお出ししている方は他社でも複数内定が出ていることがほとんどです。複数内定が出ている中からBASEを選んで頂けるように毎度毎度全社総動員でアトラクトをしています。そうやって最終的にBASEを選んで頂けた際にはみんなで喜びます。この瞬間がEMとしてやりがいを感じる瞬間です。 採用活動に携わるようになって良かったと思う点をいくつか紹介します。 視座が上がった 自分が面接官を担当するということはBASEのサービスであったり、ビジネスモデル、社内環境のことなどを魅力的に語れる必要があります。 また候補者からは以下のような質問をはじめ、様々な質問をされます。これらに自信を持って答えられなければ候補者に不安を与えてしまうことになり、BASEを選んでもらうことは難しくなります。 BASEはどんな会社ですか なぜBASEに転職したのですか 今抱えてる組織的な課題はなんですか 評価の方法を教えてください などなど 採用活動に携わるようになってから、プレイヤーの時と比べて一段高い視座を持てるようになったと感じています。 技術力が高まった 先日 「エンジニアリングマネジャーになってから技術力が伸びるパターン」 という記事が話題になりましたが共感を持って拝見しました。 候補者と面談をするにあたって職務経歴書に書いてある知らない用語を調べたりすることで、決して深くはありませんがプレイヤーの時には得られなかった広い知識が身についたと感じています。 相場が分かった スカウト等の採用活動をWantedly, LAPRAS, 転職ドラフト, Green, Findyなどたくさんのサービスを利用して毎日のように転職顕在層/潜在層のエンジニアをチェックしています。この経験を通じてスキルだったり会社だったり年収の相関が大体分かるようになり、今後の自分のキャリア設計に参考になると感じています。 最後に この一年間EMとして様々な経験をさせて頂きました。つらいなーと思うこともたくさんあった気がしますが、一年を振り返ってみるとつらかったことは思い出せずやりがいを感じたことばかりが思い出され不思議なものです。まだまだEMとして期待に応えられず不甲斐ないことが多くありますが、優秀なチームメンバーに支えられ何とか会社の成長にコミットしてこれたと思っています。2019年はチームメンバーに感謝の毎日でした。 2020年は「BASE」をより良いサービスに成長させられるように、エンジニア組織をまだまだ成長させていきます!2020年が今からとても楽しみです! 明日は、テックリードの右京さんとデザイナーの石井さんです!お楽しみに!
アバター
この記事は、「 BASEアドベントカレンダー2019 」7日目の記事です。 devblog.thebase.in BASE株式会社で採用をやっているaipon( @AiYoneda )です。 今回は、Slack Workflow Builderを使ったワークフロー自動化についてご紹介します。 プログラミング知識がなくとも、簡単に作成することができるので、まだ試したことのないという方はぜひお試しあれ! BASEでのSlack Workflow Builderの活用方法 人事関連のワークフローについて、労務回りはSmartHR、採用はHERPを使っていますが、利用サービスで吸収できない細かなワークフローについては主にSlack Workflow BuilderもしくはGoogleフォームを利用しています。 Slack Workflow BuilderとGoogleフォームは、下記のような使い分けをしています。 回答を一覧で見る必要がない・見返す頻度が少ない(Slackで回答が流れても困らない)、回答内容をもとにシート上で計算等を行う必要がない→Slack Workflow Builder 回答を一覧で見たり、見返す頻度が多い(Slackで回答が流れると困る)、回答内容をもとにシート上で計算を行う必要がある→Googleフォーム 回答をスプレッドシートで一覧で見たいもの、計算式を組んだりVLOOKUPなどあれこれしたいものに関してはGoogleフォームで回答してもらい、回答があれば特定のチャンネルで通知を飛ばしています。(Slackへの通知の飛ばし方は こちらの記事 をご参考にされてください。) 人事関連では、主に下記でSlack Workflow Builderを利用しています。 採用会食費用の補助申請 社内懇親会費用の補助申請 オフィスアンケート(社内向けサービスについての意見収集) 相談窓口(健康相談、働き方相談など) 人事制度やリファラル採用など、全社に関わる施策を実施する際は運用方法が全社に浸透するかがポイントになりますが、Slack Workflow Builderはフォームへの回答、回答の閲覧はSlack上でシームレスに行うことができ、全社の利用の障壁が低くなるというメリットがあります。 Workflowの作り方 ここからは具体的な利用方法についてのご紹介です。 今回は、採用会食費用補助の申請ワークフローを作る例とします。 採用会食補助のワークフローは下記です。 利用者(申請者)が補助申請 採用チームが内容を確認 問題なければ採用チームが稟議申請 稟議決裁がおりれば、採用チームが稟議を利用者(申請者)に共有 利用者(申請者)が会食実施 利用者(申請者)が経費精算 1から2のワークフローをこれまでGoogleフォームで行っていたのですが、URLが見つからなくなったり、Slackから遷移するのが少し面倒ではないかと懸念していたので、Slack Workflow Builderがリリースされたのを機に移行してみました。 1から2のワークフローをSlack Workflow Builderで実現する際のワークフローを細かく分解すると下記になります。 利用者(申請者)が補助申請 利用者(申請者)のDMに回答内容が送信 上記と同タイミングで、採用チームのチャンネルに回答内容を含むカスタマイズされたメッセージが届く 採用チームの担当者が内容を確認 それでは実際に作っていきます。 ワークフロービルダーを起動 メニューの一番上のワークスペース名をクリックすると、「ワークフロービルダー」があります。 これをクリック。 これまで作成したワークフローが表示されます。 ワークフローを新規作成 先ほどの画像の、右上の「作成ボタン」をクリック。 ワークフローに名前を付けます。 今回の例では、「採用会食費用補助申請フォーム」と入力します。 ワークフローの開始を設定できます。 今回は社内の誰かが申請しようと思ったときにワークフローを開始したいので、「アクションメニュー」を選択。 ワークフローを開始するチャンネルを選択します。プライベートチャンネルにも設定できます。 今回は #general とします。 「短い名前を追加」は、ワークフローを開始する際にチャンネルで表示される名称です。 利用する人が分かりやすい名称に設定するのがよいでしょう。 アクションメニューを設定するとこのように表示されます。 ワークフローの概要を設定したら具体的なステップを設定していきます。 最下部の「ステップを追加」からステップの追加を行うことができます。 ステップには「メッセージの送信」と「フォームを作成する」の2種類を設定することができます。 今回の場合、採用会食を実施する日時や参加するメンバー等の内容を申請者に入力してほしいので、フォームの作成を行います。 質問と回答の形式を設定してみました。 回答のタイプは入力形式のものやリスト形式、Slackのユーザー名やチャンネル名を選択する形式もあります。 上記はすべて「短い回答」でフォームを作成しています。 また回答者に回答内容を送っておきたいので、「提出された回答をチャンネルまたはDMで他のメンバーに送信する」にチェックを入れ、送信先を該当のワークフローをクリックしたユーザーに設定します。 ここまでで下記が完了しました。 利用者(申請者)が補助申請 利用者(申請者)のDMに回答内容が送信 あとは下記のステップです! 上記と同タイミングで、採用チームのチャンネルに回答内容を含むカスタマイズされたメッセージが届く 採用チームの担当者が内容を確認 特定のDMやチャンネルにメッセージを送信するステップを作成します。 冒頭と同じように、ワークフローの概要から「ステップを追加」をクリックします。 今度は「メッセージを送信」をクリックします。 回答の送信先チャンネルの指定と、メッセージを作ります。 メッセージには変数を入れられるので、それぞれの項目の回答内容や、フォームを提出したメンバーをメッセージに加えることができます。 Previewを見ながらメッセージを作成できるのもいいですね! メッセージができたら保存します。 ワークフローの作成はここまでです。 完成したら、ワークフローの概要から「公開する」をクリックしましょう! 稲妻アイコンをクリックしてフォームが意図した通りにできていれば完成です! 採用チームのチャンネルにはこのように通知が飛んできます。 おわりに 今回は誰でもできるワークフロー自動化の例をご紹介しました。 バックオフィスのメンバーが新しい制度や取り組みをする際に業務フローに手間がかかることが懸念の一つとしてあがることがあるかと思います。 こういった便利なツールを使うことで、制度起案者・運用者の心理的障壁がなくなるといいなーと思っています。 明日は、デザイナーの石井さんとテックリードの右京さんです!お楽しみに!
アバター
この記事はBASE Advent Calendar 2019の6日目の記事です。 devblog.thebase.in こんにちは、Product Management Group所属の 森本です。BASEには、2019年8月に入社し、Eコマースプラットフォーム「BASE」の企画やディレクションの業務に従事しています。 入社してからは、PayPal決済の導入のディレクションと、次期のプロジェクト企画などに携わっていたり、日が浅いうちからプロジェクトを任せてもらえる環境で、個人的には想像通りでとてもありがたいなと感じています。 BASEのProduct Managemantチームは、戦略、製品、事業数値まで責任をもちます。いろんなバックグラウンドをもっている方が在籍しているので、CRMに強い、計測に強い、など各々の強みを活かしてプロジェクトを牽引していることも多々あります。 私はエンジニアあがりのディレクターではなく、新卒で広告系のプロダクションでWEBディレクターを経験し、次に主にアパレル系のECサイト構築や外部連携・運用の要件定義や進行管理を担う PM / Dを経験してきました。 なので、あまりテクニカルなお話はできないのですが、BASE入社4ヶ月のディレクターがプロジェクトを進めていて良いな(今後も続けていきたい)と思ったものごとをご紹介したいと思います。 SQLをすこしかけるようになり、ほしい情報を得ることができた BASEではエンジニアやマーケッターだけでなく、全員にRedashを解放していて、みんな平等にデータを調べられる機会が与えられています。恥ずかしながら私は今までSQLの知識がなかったのですが、すこし勉強してRedashでほしいデータを見れるようになりました。まだまだほんの少しですが、以前から習得したかったことだったのでうれしい。 ちなみに、SQLの学習は Progate で、にんじゃわんこのデータベースをなんどもSELECTし、問題を実践で解いて勉強しました。失敗と成功をくりかえしていると、いつの間にか自信もつくので、環境を構築しなくてもすぐに学習ができるProgateに感謝です。(PR記事ではないです) 何より、新たなプロダクトに携わると、どんなテーブルで構成しているのか?どれくらいこの機能は利用されているのか?などをざっくりと把握したいときに、そこでエンジニアに聞いたり、過去の設計書などから情報を得る、といったことをせず、自らの手でシュッと調べることができるのも、とても効率的で前向きな姿勢でよいなと思います。(もちろん複雑な条件でお手上げの時は、詳しい人に聞くと教えてくれる環境でありがたいです...) UI/UXのレビューミーティングによる思考共有 各プロジェクトのデザインについて、毎週2回 UI/UX MTGという会議が開催され、そこでは各プロジェクトで進めているデザインのレビューが行われます。自分の携わっているプロジェクトはもちろんのこと、他のプロジェクトで進めているデザインなどの経緯やデザイン意図を聞けるので、考え方が常にチーム内で共有され、アップデートし続けることができるのはとてもよいなと思います。 法人向けのプロダクトでは、比較的優先度が下げられがちな管理画面のUI/UXですが、「BASE」は「誰でも簡単に使える」というコンセプトがサービスの根幹になるので、このミーティングはとても大切な時間であると思うのです。 個人的には、みんなのプロダクト愛を感じることができたり、まだまだ詳細を把握できていない機能を知ることができたりするので、そういった面でもこのミーティングが好きです。 このプロジェクト・機能は何のために?をしたためる シンプルでわかりやすく、ユーザーにどんな価値を提供できるのか、をプロジェクトごとや小さな出来事でも書く習慣があること。 日常的に、Slackなどでも「これはオーナーさんのどういう課題を解決できるのか」ということをよく目にすることがあり、BASEのメンバーは常にオーナーさんのことを考えている集団だなぁと感じることが多々ありますが、とくにプロジェクトの企画をたてるときにわかりやすく明文化することで、チームメンバーへ共有するときや、プロジェクトを進める中で判断に悩んだときに読み返すことができるブランケット(安心毛布)のような存在になります。 また、他のプロジェクトの記事(Kibelaを利用しています)を見て、めっちゃいいなと感じることは自分のプロジェクトにも反映することができるので、今後もしっかりと書いて、ブレずにいいプロダクトに反映していきたいです。 まとめ 上にあげたことすべてに言えるのですが、単発的に行うのではなく、日々コツコツと積み上げていくことができているからこそ価値があるのだと思います。 ただ、努力し続けていても気持ちがネガティブだと、どんどん悪い方へいってしまいがちなので、常にポジティブに取り組むことが大事です。行動指針のひとつに、「Be Hopeful」があるように、BASEのメンバーはポジティブな思考のひとが多いように思います。私もBe Hopefulにagreeです。 すこしルー大柴風になってしまいましたが、来年もコツコツと楽しくやっていきたいと思います! 明日のアドベントカレンダーは、Service Devマネージャーの菊地さんと人事の米田さんです!
アバター
この記事は BASE Advent Calendar 2019 の6日目の記事です devblog.thebase.in どうもこんにちは、Frontend Groupの青木です BASEではVue.js+TypeScriptを採用したフロントエンド開発を行っています 今回のブログでは、私が業務で直面したちょっとした課題を、Vue.jsのカスタムディレクティブを実装して解決し、npmのパッケージとして公開した話をします 何を作ったか vue-remove-whitespace - npm spanやaなどのinline要素内での改行による意図しないスペースを削除するVue.jsのカスタムディレクティブを作りました 挙動としては < p v-remove-whitespace> お問い合わせは < a href = "#" > こちらから </ a > お願いします </ p > というvueのtemplateが、レンダリングされる際には < p > お問い合わせは < a href = "#" > こちらから </ a > お願いします </ p > となります ディレクティブの指定がある場合と無い場合では、 お問い合わせはこちらからお願いします お問い合わせは こちらから お願いします のような見かけ上の違いが生じます この程度の文字数であれば、最初から改行を含めなければいいのでは?と思うところですが、各要素のattribute属性やイベント修飾子、propsなど入力するものが増えたりすると、改行したくなったりすることもあり、解決できるものを作ろうと思いました npmのpackageとして公開するまでにしたこと 公式のカスタムディレクティブのドキュメントを読む https://jp.vuejs.org/v2/guide/custom-directive.html 使っているライブラリのソースコードを読む https://github.com/logaretm/vee-validate/blob/v2/src/directive.js 現在公開されているnpmのカスタムディレクティブを探し、ソースコードを読む https://www.npmjs.com/search?q=keywords:vue https://www.npmjs.com/search?q=keywords:vue-directive 調べている過程で、 vue-focus というライブラリの開発構成がシンプルそうでいいなと思い、構成をforkして、機能実装を済ませ、初回のリリースを終えました テストの実装と、TypeScriptの開発環境で使いやすくする 公開して満足してしまい、追加で開発しようと思っていたことができていなかったのですが、今回のアドベントカレンダーを機会に、テストの実装とTypeScriptの開発環境で使いやすくしてみました テストを実装するまでにしたこと 公式の単体テストのドキュメントを読む https://jp.vuejs.org/v2/guide/unit-testing.html デモリポジトリをクローンし、テストを体験する https://vue-test-utils.vuejs.org/ja/ 実際にテストを書いてみる https://github.com/aokiken/vue-remove-whitespace/tree/master/ tests import { removeWhitespace } from '../index' export default { directives: { removeWhitespace } , template: ` <div v-remove-whitespace> <span>1</span> <span>2</span> <span>3</span> <span>4</span> <span>5</span> </div> `, } import { mount } from '@vue/test-utils' import InlineElementWithLineBreak from './InlineElementWithLineBreak' describe( 'InlineElementWithLineBreak' , () => { const wrapper = mount(InlineElementWithLineBreak) it( 'renders the correct markup' , () => { expect(wrapper.html()).toBe( '<div><span>1</span><span>2</span><span>3</span><span>4</span><span>5</span></div>' ) } ) } ) 簡単なテストですが、期待通りに動いていることが確認できました🎉 TypeScriptの開発環境で使いやすくするためにしたこと index.d.tsを書く https://github.com/aokiken/vue-remove-whitespace/blob/master/index.d.ts import { DirectiveOptions } from 'vue' export const version: string export const removeWhitespace: DirectiveOptions export const mixin: { directives: { [ key: string ] : DirectiveOptions , } , } package.jsonにtypesを追記する https://github.com/aokiken/vue-remove-whitespace/blob/master/package.json#L61 これらを追記したおかげで、TypeScriptのプロジェクトでも、型情報が参照できるようになりました🎉 最後に 今回のnpm公開は、実装の過程でコンパクトな機能が出来たので、これなら公開できそうと思ったことと、 まずはやってみよう という思いから挑戦してみました おまけ1: いくつか対応策はあった 閉じタグの>を改行すればスペースは入らない < div > < span > 1 </ span >< span > 2 </ span >< span > 3 </ span >< span > 4 </ span >< span > 5 </ span > </ div > 開始タグの<tag以下を改行すればスペースは入らない < div > < span > 1 </ span >< span > 2 </ span >< span > 3 </ span >< span > 4 </ span >< span > 5 </ span > </ div > コメントアウトを挟めばスペースは入らない < div > < span > 1 </ span > <!-- --> < span > 2 </ span > <!-- --> < span > 3 </ span > <!-- --> < span > 4 </ span > <!-- --> < span > 5 </ span > </ div > おまけ2: cssで工夫するとスペースや改行が入り込む https://codepen.io/aokiken/pen/vYBLMyv 気にならなければ良い気もしている おまけ3: 英語では困らない そもそも単語区切りでスペースが入るので、困らないのか、と実装してて気がつきました 逆に、日本語、中国語、タイ語などの言語では有効そう 明日はService Devマネージャーの菊地さんと人事の米田さんです!
アバター
この記事は BASE Advent Calendar 2019 の5日目の記事です。 devblog.thebase.in こんにちは、Data Strategyマネージャーの鈴木です。社内には鈴木が沢山いますので、普段は下の名前(りょう)で呼ばれています。 Data Strategyチームでは、BASEに存在するデータを活用する事によって、人手ではカバーできないようなきめ細かな価値や気付きを、データ分析や機械学習を中心とした取り組みを通じてプロダクトやサービスに提供しております。 中でも以下の課題については、Data Strategyチーム(以下DSチーム)の重要課題として取り組んでおります。 オーナーズ(ショップオーナー)の方々の成長をどのように支えていくか 購入者の方々が安心して購入できるプラットフォームをどう作るか オーナーズと購入者の接点を商品やショップを通じていかに増やすか 今回はその中から、いくつか具体的にユーザーの皆様の利便性向上のため活用している部分についてざっくりと紹介したいと思います。 (技術的側面につきましては、DSメンバーがこれからアドベントカレンダーで紹介していきますのでお楽しみに!) 1. オーナーズの方々の成長をどのように支えていくか オーナーズの成長を支えるには、様々なポイントがありますが、その中でも一番最初に行うべき事は、ショップをいかに数値で客観的に見る事が出来るようにする事と考えております。 そこで、DSチームではオーナーズの方々の様々なデータを提供するための仕組みづくりを進めてきました。 その一つが、今年の夏から進めてきているオーナーズの方々の管理画面にあるデータページ向けデータとなります。 データページの紹介はこちら: note.com 「BASE U」の記事はこちら: baseu.jp こちらはAWSのAurora/DynamoDBの他、ETLやLambda/Fargate/SQS/SNSなどを活用したプラットフォームとなっており、現在80万ショップの日次・週次・月次のデータを個別集計およびリクエストベースで表示する仕組みとなっております。 また、当日分データを表示するためのデータのリアルタイム取得に向けたプラットフォーム作りも並行して行っており、先日公開されました。 note.com 一方、データだけあってもその活用法がないと活かす事が難しいため、来年はそのデータ活用方法も含めて提案していければと思っております。 2. 購入者の方々が安心して購入できるプラットフォーム 「BASE」では販売が制限されている商品があります。 help.thebase.in 現在、各商品に対しての自動検知を行い、もし検知した場合に社内チームに連絡する仕組みがあります。 この通知には、上記の登録が制限された商品以外にも、話題になっている商品などの検知も行っており、ビジネスチームのサポートも同時に行っております。 3. ショップオーナーズと購入者の接点を商品やショップを通じていかに増やすか 「BASE」においてショップオーナーと購入者の間には、さまざまな接点が存在しますが、大きく分けて販売の場とブランディングの場に分かれます。これらは独立型ECや従来のSNSなど独立している場合もありますが、ポップアップやモール型ECなど場として混在しているケースもあります。 その中でもデータによって効率的に接点を増やす事が行える場所としてはモール型ECがあり、DSチームでは発足直後からショッピングアプリ「BASE」の機能改善を行ってきました。 ここではいくつかデータを活用した箇所を紹介したいと思います。 今日のおすすめ商品 アプリユーザーの興味の対象を推定し、 「BASE」でよく売れている商品の商品や、興味を引いている商品と、マッチングを行なっています。 買い物や参照傾向も一定の影響を及ぼしていますので、興味のある商品群を定期的に探していると、探さなくても興味がありそうな商品がよくでてくるようになります。 なお、類似商品も別軸で見ているので、あまりにも同じような商品が存在するときは、ショップの運営状態から優先的に紹介するような仕組みになっています。 おすすめショップ 「BASE」の人気ショップの中から、一定の基準を満たしたショップを紹介しています。 アプリを使い続けていくと、より好みのショップがおすすめされていくようになります。 ショップ側も、継続的に運営を続けていくと、購入者により近い存在になるような仕組みを取っています。 特集 パーソナライズされた情報と過去のトレンドを元に、購入者が興味が持ちそうなテーマを毎日作成しています。 タイトルは過去数年の間に手動で作成していたデータを元に自動生成しています。 こちらは、時代によって旬の言い回しが変わってくるため、定期的にモデルの更新を行っています。今の時期ですと、カレンダー・クリスマス・ギフト等と言った商品が紹介されています。 技術的な内容については、DSチームメンバーの齋藤( @pigooosuke )の記事に詳しく記載しております。 devblog.thebase.in まとめ いくつか取り上げてきましたが、BASEにおいては、データ活用を非常に重要視した取り組みをおこなっており、それを実現するためにメンバーも続々と加入しております。 今後とも、データ活用をさまざまな手法で行い、皆様の利便性に貢献していければと思います。 明日は、Frontendチームの青木さんとProduct Managementグループの森本さんです!お楽しみに!
アバター
この記事はBASE Advent Calendar 2019の5日目の記事です。 devblog.thebase.in こんにちは。BASE株式会社でOwners Growthチームのマネージャーをしている遠藤です。 Owners Growthチームのミッションは、「BASE」に登録しているショップの成長を支援したり、運営のサポートを行うことで「BASE」でのショップの開設・運営の体験を向上させ、ショップさんに継続的にご利用いただくことです。 今回はアドベントカレンダーということで、このミッションを遂行するために普段どんなことを行なっているのか、特にOwners Growthチームで行なっている戦略や戦術を決めるための仮説検証プロセスを簡単にご紹介したいと思います。 そもそもOwners Growthってどんなことを考えながら仕事をしているの?とか、どうやって仮説を検証しながら戦略を作るの?っていう疑問に答えられるような記事にしたいなと思いました。 ただし実際の社内データは直接お見せことができないので、身近に存在する問題を探した結果、投資というテーマを題材にしてみました。 目標から考えるKPI設計と仮説検証プロセス I君は最近、株に興味をもち投資を始めようと思い立ちましたが、どの会社の株を買えばいいのかわからず悩んでいます。 ここでのI君の目標は何をどのくらい買うのか、つまり 投資方針を決めること になります。 そこでI君はこの目標を達成するための要素として 株価が上がりやすいこと リスクが抑えられること の2つがあることに気づき、まずはこの株価の上がりやすさとリスクを定量化する必要があると考えました。 KPI設計 上がりやすさは過去の株価の変化率とし、リスクを株価の標準偏差と定義します。 つまり、過去の株価がプラスに変化してきた銘柄は今後も上がりやすく、上下の変動が大きかった銘柄はリスクが大きいと考えたのです。 そしてこの変化率(Return)が大きくてリスクが低くなるような以下のような指標を作ってKPIとし、そのKPIが最大になるようにします。 IT企業に勤めているI君は最近よく耳にするA社とB社に絞って、どちらに投資したらより大きなKPIが得られるのか調べようと思いました。 仮説1の作成 ここで、 どちらかKPIの高い銘柄に投資することが投資効率が高い という仮説を立てます。 この仮説を検証するために、それぞれの銘柄のKPI指標を計算して比較してみようと思います。 検証のための下準備 まず以下のように2社の株価の取得してcsvファイルに保存するようなスクリプトを実行しました。 getPrice.py #python from datetime import datetime from concurrent import futures import pandas as pd from pandas import DataFrame import pandas_datareader.data as web def download_stock(stock): try: print(stock) stock_df = web.DataReader(stock,"yahoo", start_time, now_time) stock_df['Name'] = stock output_name = stock + '_data.csv' stock_df.to_csv(output_name) except: bad_names.append(stock) print('bad: %s' % (stock)) if __name__ == '__main__': ticker_stocks = ["ticker_a","ticker_b"]#ticker array workers = len(ticker_stocks) with futures.ThreadPoolExecutor(workers) as executor: res=executor.map(download_stock, ticker_stocks) 仮説1の検証 上のスクリプトを実行して得られたcsvファイルを使って、それぞれの銘柄を評価していきます。 まずKPIを算出してみます。 getKpi.R #リターンを計算する a = read.csv("/Users/tsuyoshiendo/Documents/flask/stock/a_data.csv") b = read.csv("/Users/tsuyoshiendo/Documents/flask/stock/b_data.csv") colnames(a)[7] = "adjClose" colnames(b)[7] = "adjClose" a$ret = as.numeric(a$adjClose) / as.numeric(c(NA, a$adjClose[1:nrow(a)-1]))-1 b$ret = as.numeric(b$adjClose) / as.numeric(c(NA, b$adjClose[1:nrow(b)-1]))-1 #余分なデータを削除 a=a[!is.na(a$ret),c("Name", "Date", "ret")] b=b[!is.na(b$ret),c("Name", "Date", "ret")] #リターン ex_a = mean(a$ret) ex_b = mean(b$ret) #リスク sd_a = sd(a$ret) sd_b = sd(b$ret) #KPI kpi_a = ex_a/sd_ap#a社のKPI kpi_b = ex_b/sd_am#b社のKPI paste("a's KPI = ", kpi_a, ", b's KPI = ", kpi_b, sep="") 実行結果 a's KPI = 0.0549560106863018, b's KPI = 0.081528997872345 aのほうがKPIの値が高く、リスクに対して相対的にリターンが大きいといえます。 検証結果の深掘り しかし、ここで得られた結果に基づいて B社だけに投資しようと考えるのは拙速 です。 KPIを設計する際に作ったリスク指標は標準偏差、すなわち株価変動の大きさを表しているのですが、2社の株価が全く同じように連動しているとは考えづらいため、それぞれの組み合わせ方によってはリスクを抑えられる可能性があります。 そのため、2社の組み合わせパターンに応じたKPIを算出してみたのが次です。 twoAssets.R #covariance cov_a_b <- cov(a$ret, b$ret) weight_patterns <- seq(from = 0, to = 1, length.out = 1000) two_assets <- data.table(wx = weight_patterns) two_assets[, wy := 1 - wx] two_assets[, ':=' (ex_p = wx * ex_a + wy * ex_b,sd_p = sqrt(wx^2 * sd_a^2 + wy^2 * sd_b^2 + 2 * wx * wy * cov_a_b))] two_assets <- two_assets[wx >= 0 & wy >= 0] two_assets[, ir := ex_p / sd_p] ggplot() + geom_point(data = two_assets, aes(x = sd_p, y = ex_p, color = ir)) + geom_point(data = data.table(sd = c(sd_a, sd_b), mean = c(ex_a, ex_b)), aes(x = sd, y = mean), color = "red", size = 3, shape = 18) + theme_bw() + ggtitle("efficient frontier") + xlab("Standard Deviation") + ylab("Expected Returns") + scale_y_continuous(label = percent) + scale_x_continuous(label = percent) + scale_color_gradientn(colors = c("blue", "red"), name = "ir ratio", labels = percent) + theme_gray (base_family = "HiraKakuPro-W3") + expand_limits(x = 0, y = 0) two_assets[ir == max(two_assets$ir)] この結果、以下の曲線のような図が得られました。 この曲線はA社とB社の組入比率1,000通りのパターンに対して、縦軸にリターン、横軸にリスクをとってプロットしたものです。 そして曲線の両端がA社とB社の2社にそれぞれ単独に投資した場合になります。 軸の取り方から左上に行くほどKPI指標の大きな(=最適な)組み合わせであると言えますが、これを見ると両端の点ではなく、曲線上の中間地点の方がKPIの値が大きいことがわかります。 実際に最も高いKPIとなった組み合わせはA社に24%、B社に74%投資したパターンでそのKPI指標は0.0832と、それぞれ単独で投資をした場合の0.055と0.082よりも大きいです。 これら検証の結果、仮説1は誤っていた(棄却された)、すなわちどちらかだけに投資するのではなく、リターンとリスクに応じて投資比率を変えるべきであることがわかります。 ここで次の疑問が湧きます。 複数の会社に投資をすることで KPI指標をさらに高めることができるのではないか、です。 最初の仮説検証プロセスの結果、KPIをさらに高めるための次の仮説を検証していきます。 仮説2の作成 「組み合わせ方によって最も高いKPIを予測することができる」。 仮説2の検証 I君はこの仮説2を検証するため、C社とD社にも投資してみようと思いました。 まずC社を合わせた3社のパターンで先ほどの図を描いてみます。 threeAssets.R weight_patterns <- seq(from = 0, to = 1, length.out = 1000) #expected_return ex_a = mean(a$ret) ex_b = mean(b$ret) ex_c = mean(c$ret) ex_d = mean(d$ret) sd_a = sd(a$ret) sd_b = sd(b$ret) sd_c = sd(c$ret) sd_d = sd(d$ret) cov_b_a <- cov(a$ret, b$ret) cov_a_c <- cov(b$ret, c$ret) cov_c_b <- cov(c$ret, a$ret) three_assets <- data.table(wx = rep(weight_patterns, each = length(weight_patterns)),wy = rep(weight_patterns, length(weight_patterns))) three_assets[, wz := 1 - wx - wy] three_assets[, ':=' (ex_p = wx * ex_a + wy * ex_b + wz * ex_c, sd_p = sqrt(wx^2 * sd_a^2 + wy^2 * sd_b^2 + wz^2 * sd_c^2 + 2 * wx * wy * cov_b_a + 2 * wx * wz * cov_a_c + 2 * wy * wz * cov_c_b))] #four_assets[, ':=' (ex_p = ww*ex_a + wx*ex_b + wy*ex_c + wz*ex_d, sd_p = sqrt(ww^2 * sd_a^2 + wx^2 * sd_b^2 + wy^2 * sd_c^2 + wz^2*sd_d^2 +2 * ww * wx * cov_a_b + 2 * ww * wy * cov_a_c + 2 * ww * wz * cov_a_gg + 2 * wx * wy * cov_b_c + 2 * wx * wz * cov_b_gg + 2 * wy * wz * cov_c_gg))]#4資産以上の場合 three_assets <- three_assets[wx >= 0 & wy >= 0 & wz >= 0] three_assets[, ir := ex_p / sd_p] p = ggplot() + geom_point(data = three_assets, aes(x = sd_p, y = ex_p, color = ir)) + geom_point(data = data.table(sd = c(sd_a, sd_b, sd_c), mean = c(ex_a, ex_b, ex_c)), aes(x = sd, y = mean), color = "red", size = 3, shape = 18) + theme_bw() + ggtitle("frontier") + xlab("Standard Deviation") + ylab("Expected Returns") + scale_y_continuous(label = percent) + scale_x_continuous(label = percent) + scale_color_gradientn(colors = c("blue", "red"), name = "kpi", labels = percent) + theme_gray (base_family = "HiraKakuPro-W3") three_assets[ir == max(three_assets$ir)] 結果は3社の最適な組み合わせのKPI=0.0845となり、2社だけの場合よりさらに高くなりました。 同様にD社も加味して4社で試すと、KPI=0.0850となってさらに高くなります。 その場合、当初曲線だったグラフは以下のように、取りうる選択肢が広がった分だけ多くなって線から面となります。 この作業の繰り返しによって以下の結論が得られます。 3社の場合より4社のほうが伸び幅が逓減するため、 銘柄数が多くなるほどKPIは高くなるもののその値は一定値に収斂する という可能性を推測することができます。 したがって、仮説2は正しいと言えます。 仮説検証で得られた戦略の実行プラン 仮説1と仮説2の検証によって得られた知見から、投資対象の銘柄を増やすことによってKPI指標の最大化を図ることができ、このときの株式の保有割合は上で実施したような方法によって知ることができます。 しかしながら、実際の現場ではこうして得られた知見をそのまま使うことはしません。 最適解として得られた保有割合が何を示しているのか考え、データの裏にある真実を探る必要があるからです。 ここで得られる真実とは、株価(リターンとリスク)によって得られる最適な保有割合は一意に決まり、合理的な投資家であれば皆その比率を保有すると推測されます。 したがって、KPIの設計が正しく、市場参加者の大部分がこのKPIに対して合理的に投資するという前提に立てば、各投資家が今回得られた保有比率に応じて売買しているため、その結果として価格形成された現在の株価(正確には時価総額)はこの比率が反映されているはずです。 言い換えれば、最適な保有比率そのものが時価総額の比率によって決定されていると考えることができます。 結果的に、当初立てた目的を達成するためには、より多くの銘柄を時価総額の比率で重み付けされた指数(S&P500やTOPIXのETFなど)に投資する必要があるといえそうです。 さいごに ここまで、目標に対してKPIを設計してアクションを決定するまでのプロセスをご紹介しましたが、実際の業務ではこのように単純な前提を置くことはできないので、仮説と検証を繰り返して得られた結果の背後にある事実を正確に捉えながら戦略の精度を高めていく必要があります。 今回はアドベントカレンダーということで若干テクニカルな話をあえて盛り込んでみましたが、実際の業務ではそこまでスクリプトを書いて分析する時間は多くありません。 むしろ手元にあるデータや事象から論理的に戦略を決め、メンバーとコミュニケーションを取りながら円滑にプロジェクトのディレクションをすることも重要です。 もしこうした仕事に興味を持っていただけたら、採用窓口からのご応募をお願いいたします。 ぜひお待ちしております。
アバター