食べチョク開発者ブログ 食べチョクエンジニアによるプロダクト開発ブログ 2024-02-29T18:00:00+09:00 vividgarden-tech Hatena::Blog hatenablog://blog/10257846132669003979 デザインチームに新メンバーを迎えるオンボーディングのくふう hatenablog://entry/6801883189086732157 2024-02-29T18:00:00+09:00 2024-02-29T18:00:02+09:00 こんにちは、松久です。 昨年(2023 年)、デザインチームに新しいデザイナーが加わりました。新しいデザイナーがすぐ活躍しやすいようにオンボーディングを実施しました。 デザインチームでのオンボーディングとは デザインチームでのオンボーディングが久しぶりなので、改めてオンボーディングの目的を定めました。 入社してきた人が、早く実力を発揮しやすくすること(自分がチームでやっていけそうという感覚を手に入れる) 入社している人が当たり前と思っていたことを改めて確認する機会になること 目的を「入ってくる側」「受け入れ側」の 2 つに分けて、オンボーディングの実施計画を用意しました。 受け入れ側のオンボー… <p>こんにちは、松久です。</p> <p>昨年(2023 年)、デザインチームに新しいデザイナーが加わりました。新しいデザイナーがすぐ活躍しやすいようにオンボーディングを実施しました。</p> <h2 id="デザインチームでのオンボーディングとは">デザインチームでのオンボーディングとは</h2> <p>デザインチームでのオンボーディングが久しぶりなので、改めてオンボーディングの目的を定めました。</p> <ul> <li>入社してきた人が、早く実力を発揮しやすくすること(自分がチームでやっていけそうという感覚を手に入れる)</li> <li>入社している人が当たり前と思っていたことを改めて確認する機会になること</li> </ul> <p>目的を「入ってくる側」「受け入れ側」の 2 つに分けて、オンボーディングの実施計画を用意しました。</p> <p>受け入れ側のオンボーディングの目的は、ドキュメントにもなんもなっていないことを言語化してみることで、説明できる状態を目指しました。また、そもそも「当たり前」と思っていることについて、指摘をもらわないと気付けないことが増えているという認識で「指摘をもらおう」と心がけました。</p> <p>入ってくる側のオンボーディングは、受け入れ側が「お手並み拝見」にならず、チームでやっていける手応えを 3ヶ月で持ってもらい、チームメンバーとして活躍できている状態を目指すことにしました。</p> <p>オンボーディングでの「お手並み拝見」については下記を読み参考にしました。</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Ffujii-yuji.net%2F2022%2F12%2F02%2F001333" title="オンボーディングの作法。入社してくる仲間のお手並みを拝見するな。 - フジイユウジ::ドットネット" class="embed-card embed-blogcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 190px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://fujii-yuji.net/2022/12/02/001333">fujii-yuji.net</a></cite></p> <p>チームで成果を出すためには、環境や仕組みは大切な基盤です。理想には至らないですが、この機会にできる限りのことをしました。</p> <h2 id="事前準備できたこと">事前準備できたこと</h2> <p>オンボーディングに合わせて、いくつかの事前準備をしました。準備は大きく分けて、下記の 3 つです。</p> <ul> <li>情報の整理</li> <li>ツールの準備</li> <li>活動のガイドライン</li> </ul> <p>具体的には下記のような取り組みをしました。</p> <h3 id="ドキュメントesaを整備しました">ドキュメント(esa)を整備しました。</h3> <p>過去に書いて古くなったものを更新したり、削除したりしました。散乱しているのもあったので、ディレクトリーも整備しました。ただ、今も見つけては整備していますが、一度整備したので、だいぶ楽です。 あと、ブログも役立ちました。過去にチームで何をしたのか、ブログに書いておいて良かったです。</p> <h3 id="アカウントに関する情報確認をしました">アカウントに関する情報確認をしました。</h3> <p>エンジニア向けのアカウント準備手順はあったのですが、デザイナー向けはない事に気づいてドキュメントにしました。Adobe のことや素材集など、デザイナーしか使わない情報は足りていないことに気づき、書き起こしたり更新したりしました。</p> <h3 id="Figma-のデータを少し整理した">Figma のデータを少し整理した。</h3> <p>ファイル名とか、過去の制作物とかを整理しました。 整理していて、Figma の group や section の良い名前の付け方の方針などがあると良いかもと気づきました。今回はそこまではできていません。</p> <h3 id="習慣になっていることについて言語化した">習慣になっていることについて言語化した。</h3> <p>他の会社でもやっていそうだけれど、ちょっと違うかも、ということについてドキュメントを更新し、改めてチームに説明をしました。改めて説明をすると「そうだった、そうだった」と形骸化していないか確認する機会になりました。</p> <h3 id="事前準備できなかったこと">事前準備できなかったこと</h3> <p>事前準備したかったのですが、できていない点も有ります。 「食べチョク」らしさについてまとめることができませんでした。最も根幹なところですが、言葉にしきれないところがあり、まとめられずでした。</p> <h2 id="オンボーディングの成功のための-3-ヶ月目標">オンボーディングの成功のための 3 ヶ月目標</h2> <p>事前準備の 1 つが、新しいチームメンバーの 3 ヶ月目標です。 新しいチームメンバーが、なるべく早く活躍してほしいとは思いつつ、ある程度の時間は必要です。そこで、オンボーディングの期間を 3 ヶ月(100 日後)として、段階的に活躍の場を広げるような目標設定をしました。</p> <ul> <li>1 ヶ月目:会社やチームメンバーの理解</li> <li>2 ヶ月目:経験のある業務で何かしらの成果を出す</li> <li>3 ヶ月目:チームへの提案</li> </ul> <p>目標設定をする方法については下記を読み参考にしました。</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fachamixx.com%2F2022%2F12%2F06%2Fmf-joining-entry%2F" title="マネーフォワードにエンジニア採用広報として入社して3ヶ月間やってきたこと✌️ | achamixx club" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://achamixx.com/2022/12/06/mf-joining-entry/">achamixx.com</a></cite></p> <p>周りのことを知ってもらい、活動をした結果の影響範囲が時間を経て広くなるように目標を設定し、達成した状態を共有しました。</p> <h2 id="3-ヶ月目標達成のために支援する活動">3 ヶ月目標達成のために支援する活動</h2> <p>目標を設定して終わりにせず、目標達成のために、いくつか準備をしました。</p> <h3 id="1on1-の実施">1on1 の実施</h3> <p>最初の月は、私と毎週 30 分実施して話し相手となるようにしました。 また、デザインチームのメンバーともお話をする機会を作り、人となりを知ってもらう時間を作るようにしました。その後、チーム外の人とも 1on1 をすることで、「会社やチームメンバーの理解」を促進できるようにしました。</p> <h3 id="デザインチームのモットーの定義">デザインチームのモットーの定義</h3> <p>デザインチームは、フルリモートの人も多くいるチームなので、個別で動いてしまうことが多く、チーム感に欠けやすところがあります。そこで、活動のモットーを決めて、2 日に 1 回はチームに言うことで、どんなことを大切にしているのか全員で合わせるようにしました。</p> <h3 id="Slack-で広い領域に伝えられるような施策をお願いする">Slack で広い領域に伝えられるような施策をお願いする</h3> <p>知ってもらうためにも、色々なチームメンバーに知ってもらえるような施策をお願いしました。入社された人のことを知ってもらう機会となりました。</p> <p>他にも、デザインレビューを一緒にすることで、「らしさ」を共有したり、普段使っているツールや命名のクセなども知ってもらいました。</p> <h2 id="おわりに">おわりに</h2> <p>チームに入ってくれた人が早く活躍してくれるためにも、オンボーディングをキッカケにして足りていないことや、日頃の業務をやっている理由を改めて確認できました。 まだまだ至らないことも多いですが、オンボーディングをキッカケにチームが大きくなることで、「食べチョク」の改善が進みやすくなるようにしていきます。</p> vividgarden-tech 「ふりかえり」から始まる2024年デザインチームのカイゼンプロジェクト hatenablog://entry/6801883189079130812 2024-01-31T18:00:00+09:00 2024-01-31T18:00:03+09:00 こんにちは、松久です。 先月、デザインチームで 2023 年の「ふりかえり」をはじめました。はじめて、1 ヶ月経ちますが、まだ終わっていません。終わっていませんが、なかなか良さそうだと感じています。 「ふりかえり」をすることにした理由 2023 年は、チームでの「ふりかえり」をしてきていませんでした。してこなかった理由はいくつかあります。 「ふりかえり」からのカイゼン行動ができていないことがあるため デザイナーが複数のチームで活動しており、そのチームでの「ふりかえり」を重視した 目の前の期日がある仕事を優先してしまうので「ふりかえりをした」で終わっていました。カイゼンの実施まで辿り着くことが出… <p>こんにちは、松久です。</p> <p>先月、デザインチームで 2023 年の「ふりかえり」をはじめました。はじめて、1 ヶ月経ちますが、まだ終わっていません。終わっていませんが、なかなか良さそうだと感じています。</p> <h2 id="ふりかえりをすることにした理由">「ふりかえり」をすることにした理由</h2> <p>2023 年は、チームでの「ふりかえり」をしてきていませんでした。してこなかった理由はいくつかあります。</p> <ul> <li>「ふりかえり」からのカイゼン行動ができていないことがあるため</li> <li>デザイナーが複数のチームで活動しており、そのチームでの「ふりかえり」を重視した</li> </ul> <p>目の前の期日がある仕事を優先してしまうので「ふりかえりをした」で終わっていました。カイゼンの実施まで辿り着くことが出来ないので、デザインチームの「ふりかえり」は、しばらくしていませんでした。</p> <h2 id="再びふりかえりをした">再び「ふりかえり」をした</h2> <p>今回、再び「ふりかえり」をしています。 理由は、いくつかありますが、人が少し増え、目の前のこと以外も取り組めそうだったからです。本当に取り組めるかは、「ふりかえり」をしないと始まらないからです。「ふりかえり」で決めた改善策を実行できるなら「ふりかえり」の意味が出てきますし、改善できていないモヤモヤした気持ちを抱えなくてすみます。</p> <p>行った「ふりかえり」の方式は KPT に似ていますが、出来たところ、自慢したいところを出すことと、上手くいかなかったところを書き出し、次のアクションを 1〜2 つ決めて半年の間で取り組むことにしました。</p> <p>KPT について詳しくは「これだけ! KPT」を参考にしています。 <div class="hatena-asin-detail"><a href="https://www.amazon.co.jp/dp/B00EVHZPS0?tag=hatena-22&amp;linkCode=ogi&amp;th=1&amp;psc=1" class="hatena-asin-detail-image-link" target="_blank" rel="noopener"><img src="https://m.media-amazon.com/images/I/51-yl7uw+bL._SL500_.jpg" class="hatena-asin-detail-image" alt="これだけ! KPT 【これだけ!シリーズ】" title="これだけ! KPT 【これだけ!シリーズ】"></a><div class="hatena-asin-detail-info"><p class="hatena-asin-detail-title"><a href="https://www.amazon.co.jp/dp/B00EVHZPS0?tag=hatena-22&amp;linkCode=ogi&amp;th=1&amp;psc=1" target="_blank" rel="noopener">これだけ! KPT 【これだけ!シリーズ】</a></p><ul class="hatena-asin-detail-meta"><li><span class="hatena-asin-detail-label">作者:</span><a href="https://d.hatena.ne.jp/keyword/%C5%B7%CC%EE%20%BE%A1" class="keyword">天野 勝</a></li><li>すばる舎</li></ul><a href="https://www.amazon.co.jp/dp/B00EVHZPS0?tag=hatena-22&amp;linkCode=ogi&amp;th=1&amp;psc=1" class="asin-detail-buy" target="_blank" rel="noopener">Amazon</a></div></div></p> <p>「ふりかえり」は、何度かに分けて実施しました。社員の人もいれば、業務委託契約の人もいて、稼働している時間が異なるための対策です。契約は異なりますが、デザインチームであり、同じコトに向かっている一員なので、できるだけ参加してもらうようにしました。</p> <p><figure class="figure-image figure-image-fotolife" title="Miro に書き出した「ふりかえり」の項目"><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/v/vividgarden-tech/20240130/20240130102520.jpg" width="1200" height="909" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span><figcaption>Miro に書き出した「ふりかえり」の項目</figcaption></figure></p> <p>長い期間を対象としたふりかえりなので、多くの項目があがりました。 いきなりまとめるのではなく、出てきた項目 1 つ 1 つについて、じっくり話すことにしました。問題を色々な人が見て、把握して、根本解決を目指すのか、早く気付けるようにするなど予防策があるのかを検討できるのはチームらしい取り組みです。</p> <p><figure class="figure-image figure-image-fotolife" title="問題と解決・改善策を並べて、なにを GitHub issue にするのか決めたときの Miro の様子"><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/v/vividgarden-tech/20240130/20240130102555.jpg" width="1200" height="550" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span><figcaption>問題と解決・改善策を並べて、なにを GitHub issue にするのか決めたときの Miro の様子</figcaption></figure></p> <h2 id="おわりに">おわりに</h2> <p>今回は「ふりかえり」をしたところで終わっています。まだ改善策を実施するところまでは出来ていません。時間がかからなさそうなのは、GitHub の issue にして、実行していくことにしました。 「ふりかえり」から「食べチョク」の改善を引き続き実行していきます。</p> vividgarden-tech デザインチームの2023年のブログを振り返り!変化と継続、そして2024年への動き出し #デザイン #ブログ hatenablog://entry/6801883189070015000 2023-12-28T18:00:00+09:00 2024-01-30T13:43:28+09:00 こんにちは、松久です。 デザインチームの 2023 年のブログ記事をふりかえり執筆後から変わったことや、変わらなかったことを書き出すことで、2024 年に向けて動き出せるようにします。 Figma のファイル管理ルール( 2022 年 12 月 ) ファイル名については、デザインチームでは浸透が進み当たり前の状態になっています。変える話もいまのところなく、やってよかったです。 マスターデータの運用は、GitHub Projects で「🏁 マスターデータに反映待ち」を用意し、ここを通らないと作業完了とはしないことにしました。ただ、マスターデータへの反映が滞りがちなところがあるので、課題は残って… <p>こんにちは、松久です。</p> <p>デザインチームの 2023 年のブログ記事をふりかえり執筆後から変わったことや、変わらなかったことを書き出すことで、2024 年に向けて動き出せるようにします。</p> <h2 id="Figma-のファイル管理ルール-2022-年-12-月-">Figma のファイル管理ルール( 2022 年 12 月 )</h2> <p>ファイル名については、デザインチームでは浸透が進み当たり前の状態になっています。変える話もいまのところなく、やってよかったです。 マスターデータの運用は、GitHub Projects で「🏁 マスターデータに反映待ち」を用意し、ここを通らないと作業完了とはしないことにしました。ただ、マスターデータへの反映が滞りがちなところがあるので、課題は残っています。</p> <h2 id="Figma-の操作方法を学ぶ-2023-年-2-月-">Figma の操作方法を学ぶ( 2023 年 2 月 )</h2> <p>公式の YouTube から学んだことは生かされているし、YouTube で学ぶのは良い、と実感しています。 出来ていないことは、<a href="https://help.figma.com/hc/ja/articles/15336833927191-Config-2023%E3%81%AE%E6%9C%80%E6%96%B0%E6%83%85%E5%A0%B1">Figma のイベント Config 2023</a> で新機能がたくさん出ましたが、キャッチアップできている、とは言い難いことです。普段の業務でも嬉しい新機能はあるのですが、手慣れているところで終わらせることも多く、より効率的な使いこなしのために、チームで Figma のことを話すことを話す時間をもちたいです。</p> <h2 id="バナー制作をデザイナー以外でも作成する運用始めました-2023-年-3-月-">バナー制作をデザイナー以外でも作成する運用始めました( 2023 年 3 月 )</h2> <p>運用自体は、軌道に乗っています。でも、増やせてはいません。作れるスキルがある程度必要なことと、都度作るバナーが多くあるので、今以上に拡大することは難しそうです。</p> <h2 id="Figma-と-SCSS-で色の管理をあわせる-2024-年-4-月-">Figma と SCSS で色の管理をあわせる( 2024 年 4 月 )</h2> <p>Stylelint は役割をきちんと果たしてくれています。 ただ、色々な方法で色々な指定が生まれています。例えば下記のように使われることがあります。Figma では表記が異なるので、SCSS を書くときに少し面倒です。</p> <pre class="code lang-css" data-lang="css" data-unlink>@use <span class="synConstant">&quot;../Foundation/variable&quot;</span>; <span class="synIdentifier">.foo</span> <span class="synIdentifier">{</span> <span class="synType">color</span>: variable.$color-gray<span class="synConstant">0</span>; <span class="synIdentifier">}</span> </pre> <p>フォントについて、SCSS と Figma を近づけて、下記のように SCSS を書けるようにしています。</p> <pre class="code lang-css" data-lang="css" data-unlink>@use <span class="synConstant">&quot;../Foundation/mixin/typography&quot;</span>; <span class="synIdentifier">.foo</span> <span class="synIdentifier">{</span> @include typography<span class="synIdentifier">.body</span>; <span class="synIdentifier">}</span> </pre> <p>Figma の DevMode が出来て、CSS のカスタムプロパティが表示されるようになりました。Figma の表示を合わせて、実装を調整することも検討しています。</p> <h2 id="ふりかえりからデザイナー同士のミーティングを改善しました-2023-年-5-月-">ふりかえりからデザイナー同士のミーティングを改善しました( 2023 年 5 月 )</h2> <p>GitHub Projects に「🧵 Talking」を設けて、issue を検討する時間をとるようにしました。 現在、あまり上手くできていないです。話すためにも、準備することが多く、そのためには時間を確保する必要があります。話をする時間だけでは足りないことがわかりました。あと、夏ぐらいから業務量が増え続けて、この時間も作業の交通整理のミーティングに使われることが増えました。</p> <p>急ぎではないが重要なことを進める仕組みを試し続けないと、改善活動はされないままです。取り組める量は同じでもより効率的に進められるようにする取り組みをして、「急ぎではないが重要なことを進める仕組み」を手に入れようと検討しています。</p> <h2 id="似ているけれど少し違う色を減らす取り組み-2023-年-6-月-">「似ているけれど少し違う色」を減らす取り組み( 2023 年 6 月 )</h2> <p>増えることはなくなりましたが、既存の実装からは減らせていないです。 UI を変更するときに対応しているので、徐々に減っているハズです。</p> <h2 id="桃やぶどうのような同じ品目でバナーを作る前に事前準備したこと2023-年-7-月">桃やぶどうのような同じ品目でバナーを作る前に事前準備したこと(2023 年 7 月)</h2> <p>品目・品種でバナーを作る時から始めた色のトーンを事前に話すことは、続けています。 ここから発展して、年末年始の色など、季節やイベントでの色や表現はどうあったらいいのか、を話し始めています。</p> <h2 id="おいしそうが伝わる写真を選ぶ原則をチームで共有した-2023-年-8-月-">おいしそうが伝わる写真を選ぶ原則をチームで共有した( 2023 年 8 月 )</h2> <p>原則ができたことで、写真選びはしやすくなりました。思ったよりも、画像制作のスピードアップに効果がありました。</p> <h2 id="GitHub-Projects-でデザインチームのタスク管理をするノウハウ-2023-年-9-月-">GitHub Projects でデザインチームのタスク管理をするノウハウ( 2023 年 9 月 )</h2> <p>ここからは、あまり変化はありません。ただ、他のチームにも紹介して、使ってもらえるよう話しています。ノウハウのベースは「<a href="https://www.oreilly.co.jp/books/9784873117645/">カンバン仕事術</a>」です。私が休みでも、チームでプロダクトバックログの管理がされていたので、デザインチームには定着してきたようです。</p> <h2 id="デザインと上手くお付き合いする方法を社内で発表した-2023-年-10-月-">デザインと上手くお付き合いする方法を社内で発表した( 2023 年 10 月 )</h2> <p>少しでも早めに声をかけてもらえるチームになりたいです、という宣言でした。 まだ、実現できていないことのほうが多いですが、少しづつ声をかけてもらえるチームになるための行動をします。</p> <h2 id="画像制作依頼チェックリストから普段のヒアリング内容を確認した話-2023-年-11-月-">画像制作依頼チェックリストから普段のヒアリング内容を確認した話( 2023 年 11 月 )</h2> <p>画像の制作依頼は、日々あるのですが「画像をつくる」ということだけが決まっていることが多い状況です。なんのために作るか、を確認してもらえるように作ったチェックリストですが、なかなか使用されないことがわかりました。GitHub issue のテンプレートにチェックリストを入れたこともありますが、チェックリストは消されることがわかったので、デザインチームでチェックしていく方法に切り替えることを検討しています。</p> <h2 id="おわりに">おわりに</h2> <p>2023 年の棚卸しとして、一年のブログを見返しました。 ブログ記事から時間が経って、そのまま続いている事や、変化しつつ継続している事、継続できていない事があります。デザインチームが変化している証拠なので、変化を確認するためにも、ブログは継続して「食べチョク」の改善に活かしていきます。</p> vividgarden-tech 画像制作依頼チェックリストから普段のヒアリング内容を確認した話 hatenablog://entry/6801883189062477682 2023-11-30T18:30:00+09:00 2023-11-30T18:30:00+09:00 こんにちは、松久です。 食べチョクでは様々なところで画像が作られています。 これらの画像は、様々な部署からの依頼をうけてデザインチームで作ることが多いです。一部、テンプレートから画像を作っているのもありますが、まだ、デザインチームで作ることは多いです( テンプレートを使った画像作成は「バナー作成をデザイナー以外でも作成する運用をはじめました」参照)。 大雑把な画像の作成依頼の時があります。このときは、具体的な形にするまでにヒアリングをして制作を進めていきます。 制作前のヒアリングに時間がどうしても必要になっています。この時間を短縮したり、事前に検討してもらえるようにするため、社内のドキュメント… <p>こんにちは、松久です。</p> <p>食べチョクでは様々なところで画像が作られています。 これらの画像は、様々な部署からの依頼をうけてデザインチームで作ることが多いです。一部、テンプレートから画像を作っているのもありますが、まだ、デザインチームで作ることは多いです( テンプレートを使った画像作成は「<a href="https://tech.tabechoku.com/entry/2023/03/31/115000">バナー作成をデザイナー以外でも作成する運用をはじめました</a>」参照)。</p> <p>大雑把な画像の作成依頼の時があります。このときは、具体的な形にするまでにヒアリングをして制作を進めていきます。 制作前のヒアリングに時間がどうしても必要になっています。この時間を短縮したり、事前に検討してもらえるようにするため、社内のドキュメントツール( esa を使用しています )にチェックリストを作り確認してもらうことをお願いし始めました。</p> <p>具体的には、下記のようなチェックリストを esa に作って運用しています。</p> <h2 id="運用しているチェックリスト">運用しているチェックリスト</h2> <ul> <li>[ ] GitHub issue を作る。下記の項目は後で埋めても大丈夫</li> <li>[ ] どこにいつ出すか</li> <li>[ ] 何を伝えて、どうなってほしいか</li> <li>[ ] 伝えたいテキストの優先順は決めたか</li> <li>[ ] 必ず入れなくてはいけない要素があるか</li> <li>[ ] 写真が必要であればどんな写真なのか、だれが写真を探すのか</li> <li>[ ] サンプル例はあるか</li> </ul> <h2 id="チェックリストの回答具体例">チェックリストの回答具体例</h2> <h3 id="どこにいつ出すか">どこにいつ出すか</h3> <ul> <li>サイトトップや、メルマガなど</li> </ul> <h3 id="何を伝えてどうなってほしいか">何を伝えて、どうなってほしいか</h3> <ul> <li>コンシェルジュ野菜の日キャンペーンを伝えたい</li> </ul> <h3 id="伝えたいテキストの優先順は決めたか">伝えたいテキストの優先順は決めたか</h3> <ol> <li>今だけ 初回 980円(税込)</li> <li>野菜のある生活を始めよう!</li> </ol> <h3 id="必ず入れなくてはいけない要素があるか">必ず入れなくてはいけない要素があるか</h3> <ul> <li>キャンペーン期間を載せておきたい <ul> <li>8月1日から8月31日 15時まで</li> </ul> </li> </ul> <h3 id="写真が必要であればどんな写真なのかだれが写真を探すのか">写真が必要であればどんな写真なのか、だれが写真を探すのか</h3> <ul> <li>デザインチームに選んでほしい <ul> <li>🙆🏻‍♀️ 夏から秋にかけての野菜が届くこと</li> <li>🙅🏻‍♀️ サラダなど調理後ではない</li> </ul> </li> </ul> <h3 id="サンプル例はあるか">サンプル例はあるか</h3> <ul> <li>特になし</li> </ul> <h2 id="チェックリストのポイント">チェックリストのポイント</h2> <p>チェックリストは、2つのことを伝えたくて作りました。</p> <ul> <li>完成イメージ(だれに、いつ、どこで、なにを伝えているか)を持ちましょう</li> <li>画像が必要になったらすぐ GitHub issue を作ること</li> </ul> <p>画像がどんなふうに使われるのか完成イメージを持つことは大切です。 メルマガからサイトへ誘導する時と、商品をテーマに沿って紹介する時で大切にすることはことなります。画像に求める役割は異なるからです。チェックリストは、画像の役割を明文化するきっかけになるように心がけました。</p> <h2 id="おわりに">おわりに</h2> <p>画像を作る目的のヒアリングは大切ですが、確認したいことが、事前にわかると話もスムーズです。チェックリストは、普段のヒアリングはなんのために、なにを聞いていたのか確認するいい機会でした。</p> <p>普段の業務を棚卸しをして、なぜやるのかを確認することは定期的に行い「食べチョク」の改善に活かしていきます。</p> vividgarden-tech デザインと上手くお付き合いする方法を社内で発表した hatenablog://entry/6801883189054844104 2023-10-31T18:30:00+09:00 2023-10-31T18:30:00+09:00 こんにちは、松久です。 株式会社ビビッドガーデンでは、「週次 Sync & Share の会 」が開催されています。 (だいたい)毎週、各チームから全社に向けて、学んだことを伝えたり、聞いたりしています。 デザインチームから Sync & Share 会で「デザインと上手くお付き合いする話」というテーマで、デザインチームとより上手に仕事をする方法がわかることを目的とした発表をしました。 発表テーマを決めた気持ち 「デザインと上手くお付き合いする話」というテーマだと、今は「上手くできていない」と聞こえます。正直、すべてが上手くいっているとは言えません。上手くできているところもあるので、上手くでき… <p>こんにちは、松久です。</p> <p>株式会社ビビッドガーデンでは、「週次 Sync &amp; Share の会 」が開催されています。 (だいたい)毎週、各チームから全社に向けて、学んだことを伝えたり、聞いたりしています。</p> <p>デザインチームから Sync &amp; Share 会で「デザインと上手くお付き合いする話」というテーマで、デザインチームとより上手に仕事をする方法がわかることを目的とした発表をしました。</p> <h2 id="発表テーマを決めた気持ち">発表テーマを決めた気持ち</h2> <p>「デザインと上手くお付き合いする話」というテーマだと、今は「上手くできていない」と聞こえます。正直、すべてが上手くいっているとは言えません。上手くできているところもあるので、上手くできているところを増やすために伝えたい、という気持ちでした。</p> <p>「デザインチームはどんな事ができるのか」を伝えて、デザインの必要性や、どんな風に関わると事業に対してよい影響を与えることができるか社内の広報活動が必要だと感じていました。同様のことが、「<a href="https://www.amazon.co.jp/dp/B0BLTLCYLX/">銀行とデザイン</a>」という本に、インハウスデザイナーの取り組みとして書かれています。この本に後押しされて、実際にやってみました。</p> <p><div class="hatena-asin-detail"><a href="https://www.amazon.co.jp/dp/B0BLTLCYLX?tag=hatena-22&amp;linkCode=ogi&amp;th=1&amp;psc=1" class="hatena-asin-detail-image-link" target="_blank" rel="noopener"><img src="https://m.media-amazon.com/images/I/41VsADUVWDL._SL500_.jpg" class="hatena-asin-detail-image" alt="銀行とデザイン デザインを企業文化に浸透させるために" title="銀行とデザイン デザインを企業文化に浸透させるために"></a><div class="hatena-asin-detail-info"><p class="hatena-asin-detail-title"><a href="https://www.amazon.co.jp/dp/B0BLTLCYLX?tag=hatena-22&amp;linkCode=ogi&amp;th=1&amp;psc=1" target="_blank" rel="noopener">銀行とデザイン デザインを企業文化に浸透させるために</a></p><ul class="hatena-asin-detail-meta"><li><span class="hatena-asin-detail-label">作者:</span><a href="https://d.hatena.ne.jp/keyword/%B6%E2%DF%B7%20%CD%CE" class="keyword">金澤 洋</a>,<a href="https://d.hatena.ne.jp/keyword/%B6%E2%BB%D2%20%C4%BE%BC%F9" class="keyword">金子 直樹</a>,<a href="https://d.hatena.ne.jp/keyword/%CB%D9%20%CD%B4%BB%D2" class="keyword">堀 祐子</a></li><li>インプレス</li></ul><a href="https://www.amazon.co.jp/dp/B0BLTLCYLX?tag=hatena-22&amp;linkCode=ogi&amp;th=1&amp;psc=1" class="asin-detail-buy" target="_blank" rel="noopener">Amazon</a></div></div></p> <h2 id="発表内容">発表内容</h2> <p><figure class="figure-image figure-image-fotolife" title="「いい感じになーれ」を図解"><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/v/vividgarden-tech/20231031/20231031101556.jpg" width="1200" height="675" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span><figcaption>「いい感じになーれ」を図解</figcaption></figure></p> <p>当日は、スライドを作り、発表をしました。</p> <p>一番言いたいことは、デザイナーが魔法使いで「いい感じになーれ」と唱えたら「いい感じ」にしてくれるわけではなく、一緒に「いい感じ」を探しましょう、ということです。</p> <p><figure class="figure-image figure-image-fotolife" title="デザインチームが取り組むこと"><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/v/vividgarden-tech/20231031/20231031101642.jpg" alt="&#x8981;&#x4EF6;&#x3092;&#x6C7A;&#x3081;&#x308B;&#x524D;&#x306B;&#x547C;&#x3093;&#x3067;&#x3082;&#x3089;&#x3048;&#x306A;&#x3044;&#x306E;&#x306F;&#x3001;&#x7406;&#x7531;&#x304C;&#x3042;&#x308B;&#x3068;&#x601D;&#x3063;&#x3066;&#x3044;&#x308B;&#x3002;&#x5FD9;&#x3057;&#x305D;&#x3046;&#x3067;&#x8A71;&#x3059;&#x3068;&#x8FF7;&#x60D1;&#x305D;&#x3046;&#x3002;&#x5DE5;&#x6570;&#x3092;&#x304B;&#x3051;&#x305F;&#x304F;&#x306A;&#x3044;&#x3002;&#x8A71;&#x3059;&#x3068;&#x8CEA;&#x554F;&#x3055;&#x308C;&#x3066;&#x9762;&#x5012;&#x3002;&#x8A71;&#x3057;&#x3066;&#x3082;&#x4E0A;&#x624B;&#x304F;&#x3044;&#x304B;&#x306A;&#x3044;&#x7D4C;&#x9A13;&#x304C;&#x3042;&#x308B;&#x3002;&#x30C1;&#x30FC;&#x30E0;&#x3067;&#x8A71;&#x3057;&#x3066;&#x6C7A;&#x307E;&#x3063;&#x3066;&#x3044;&#x308B;&#x3002;&#x7684;&#x78BA;&#x306A;&#x8CEA;&#x554F;&#x3092;&#x3059;&#x308B;&#x529B;&#x304C;&#x5FC5;&#x8981;&#x3068;&#x3044;&#x3046;&#x8A8D;&#x8B58;&#x3002;&#x305D;&#x306E;&#x305F;&#x3081;&#x306B;&#x4F55;&#x304C;&#x3067;&#x304D;&#x308B;&#x304B;&#x306F;&#x6A21;&#x7D22;&#x4E2D;&#x3002;&#x8CEA;&#x554F;&#x306E;&#x4F5C;&#x308A;&#x65B9;&#x3068;&#x6570;&#x5B57;&#x306E;&#x7406;&#x89E3;&#x304C;&#x30C7;&#x30B6;&#x30A4;&#x30CA;&#x30FC;&#x306B;&#x5FC5;&#x8981;" width="1200" height="675" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span><figcaption>デザインチームが取り組むこと</figcaption></figure></p> <p>一緒に取り組むためには、デザインチームも取り組むことが必要です。 認識している問題はいくつか有り、解決策が見えていることもあれば、暗中模索のこともあります。</p> <p>発表した結果、どんな関わり方を持つとよいか、わかったと感想をいただいくことができました。発表することで、少しでも伝わったはずです。</p> <h2 id="おわりに">おわりに</h2> <p>会社の取り組みの 1 つである「Sync &amp; Share 会」を利用して、デザインとの関わり方を全社に伝える機会を得ることができました。 日々の活動で伝えることももちろん重要なのですが、業務ありきから始まってしまうので、業務で直接話したことがない人も含めて伝えられるいい機会があり助かりました。</p> <p>デザインとの関わり方を全社で行えるようになることで、「食べチョク」の改善に活かしていきます。</p> vividgarden-tech GitHub Projects でデザインチームのタスク管理をするノウハウ hatenablog://entry/820878482971008714 2023-09-29T18:00:00+09:00 2023-09-29T18:00:18+09:00 こんにちは、松久です。 デザインチームでは、GitHub Projects でタスク管理しています。 タスク管理する理由はいくつかあります。 「食べチョク」の改善を少しでも進めるため チームで効率的に改善を進めるため 他のチームと一緒に仕事をするため チームで成果を出せているのかを確認するため GitHub Projects を使って「カンバン」を参考にして管理をしています。 なぜ GitHub Projects を使っているのか GitHub が、職種関係なく導入されています。GitHub issue で、タスクを書き出して GitHub Projects に集約がある程度されている状況がす… <p>こんにちは、松久です。</p> <p>デザインチームでは、GitHub Projects でタスク管理しています。 タスク管理する理由はいくつかあります。</p> <ul> <li>「食べチョク」の改善を少しでも進めるため <ul> <li>チームで効率的に改善を進めるため</li> </ul> </li> <li>他のチームと一緒に仕事をするため</li> </ul> <p>チームで成果を出せているのかを確認するため GitHub Projects を使って「<a href="https://ja.wikipedia.org/wiki/%E3%82%AB%E3%83%B3%E3%83%90%E3%83%B3">カンバン</a>」を参考にして管理をしています。</p> <h2 id="なぜ-GitHub-Projects-を使っているのか">なぜ GitHub Projects を使っているのか</h2> <p>GitHub が、職種関係なく導入されています。GitHub issue で、タスクを書き出して GitHub Projects に集約がある程度されている状況がすでにありました。もし、GitHub Projects を使い続けて問題が発生すれば移行することを検討することにしました。</p> <h2 id="現在の-GitHub-Projects-の状態">現在の GitHub Projects の状態</h2> <p><figure class="figure-image figure-image-fotolife" title="GitHub Projects の運用状態です。"><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/v/vividgarden-tech/20230927/20230927114352.jpg" width="1200" height="659" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span><figcaption>GitHub Projects の運用状態です。</figcaption></figure></p> <p>いくつかのカラムに分けて運用しています。 カンバンと同じで、issue は、inbox から始まり、Done で完了します。 現在あるカラムと役割は下記のとおりです。</p> <ul> <li>inbox <ul> <li>issue ができたらまずはここに置く</li> <li>担当が決まっていない</li> </ul> </li> <li>Talking <ul> <li>デザインチームで検討したいことをここに置く</li> <li>定期的に話す時間を設けて話して解決したり、具体的な取り組みになれば、あたらしく issue を作る</li> </ul> </li> <li>Backlog <ul> <li>取り組む担当を決めた issue</li> <li>取り組める状態にするのもこの状態で行う</li> </ul> </li> <li>Expired Backlog <ul> <li>リリース日が決まって動かせない issue</li> <li>取り組む担当を決めた issue</li> <li>取り組める状態にするのもこの状態で行う</li> </ul> </li> <li>In Progress <ul> <li>取組中</li> </ul> </li> <li>開発中・連絡待ち <ul> <li>UI デザインが終わり、開発実装中</li> <li>社外からの連絡待ちで、動かせない issue</li> </ul> </li> <li>マスターデータに反映待ち <ul> <li>開発も終わり、利用者に届けられた状態だが、デザインデータをマスターデータ( Figma でブランチ機能が使えないため)に反映するの待ちの状態</li> </ul> </li> <li>Done <ul> <li>利用者に届けられたられた状態</li> </ul> </li> <li>icebox <ul> <li>いつか優先度が上がる可能性のある issue</li> </ul> </li> </ul> <p>運用を始めて、少しづつ変化して現在の形になっています。 今後も変化していきます。</p> <h2 id="GitHub-Projects-の運用ルール">GitHub Projects の運用ルール</h2> <p>運用しながら出来たルールがいくつかあります。</p> <ul> <li>issue を左から右に進める <ul> <li>チームで issue をゴール(完了/Done)に進めることを大切にしています</li> <li>issue が左から右に移動することで取り組む内容が具体的になります</li> </ul> </li> <li>Done に移動した(完了) issue をアーカイブするのは月曜日 <ul> <li>一定期間にどれぐらい完了したかチームで確認するため <ul> <li>大体、1 週間で 10〜15 ぐらいの issue がクローズされているのか肌感をもつようにしています。少ない時は、進めるための障害があった時だと気付けるからです</li> </ul> </li> <li>Projects に置いておける issue には限りがある</li> </ul> </li> <li>並べる順位は、取り組む・期限が近いもの順 <ul> <li>取り組む価値が大きいことを先にするのが基本です。ただし、取り組む内容が時間のかかるものの場合(例:冊子を作る)や、リリース日が近いのもあるので、取り組む順で並べています</li> </ul> </li> </ul> <h2 id="GitHub-Projects-の運用ノウハウ">GitHub Projects の運用ノウハウ</h2> <p>運用している中で、上手くいかなかったこともあります。</p> <ul> <li>毎日見る <ul> <li>チームでは月・水・金に見ています。取り組むことに困っていることはないかを確認しています</li> <li>私は毎日見ています。急ぎの issue が登録されていることがあるためです <ul> <li>急ぎの issue は、Slack のデザイン組織のチャンネルなどでメンションしてもらうなどの方法もありますが徹底は出来ていないです</li> </ul> </li> <li>issue での連絡が途絶えたときは「いかがでしょうか」と連絡することから始めます</li> </ul> </li> <li>担当者別のカラムは作らない <ul> <li>担当者のことが知りたいわけではなく、issue がどんな状態なのかを把握したいので、担当者(アサイン)別のカラムは用意しません <ul> <li>担当者の状態が知りたいときは、担当者別の View を作って対応しています</li> <li>ラベル別で View も作れるので、ラベルの管理を大事にしています</li> </ul> </li> </ul> </li> <li>カラムを増やしすぎない。 <ul> <li>カラムを増やしたいと感じる時は、1 つのカラムに issue が溜まっていて取りこぼすことが出てくる時のようです</li> <li>カラムを増やす前に、issue が止まっている要因を取り除くことを優先します <ul> <li>「左から右」に issue を進める方法が他に無いか模索します。例えば、他のチームにお願いできないか、人を増やせないか、取り組み方やゴールを変えられないかを検討します</li> </ul> </li> </ul> </li> </ul> <h2 id="issue-を作成する時のコツ">issue を作成する時のコツ</h2> <ul> <li>とりあえず issue にする <ul> <li>issue にしないのは、プライバシーに関わることなど、秘匿性が高いものだけです</li> <li>デザイナー採用の資料作成など、プロダクトに関係ないことも issue にしています</li> <li>Slack やミーティングで取り組むことを決めたら、issue にします。特に、Slack で話して取り組むことを決めたら忘れずに、issue にします</li> </ul> </li> <li>完了を定義する <ul> <li>「バナーを作る。中身未定」という issue はよくあります。そのときは、要望がでてくるまで待ちます。もしくは、声をかけて、取り組みを具体的にするところから始めます</li> <li>方法だけ決まっていて、何をすればいいのかわからないときは、わかるようにしていく取り組みをします。例えば、issue で質問したり、ミーティングしてみるなどをして結果を issue に記載します</li> </ul> </li> <li>issue の種類を把握する <ul> <li>急ぎ(ASAP)、Epic(複数の issue 達成で成立する )、不具合といった issue があり、別で管理できるようにラベルを付けています</li> </ul> </li> </ul> <h2 id="GitHub-Projects-を運用してみて">GitHub Projects を運用してみて</h2> <p>運用していくなかで、大切にできていることは 2 つです。</p> <ul> <li>見える化する</li> <li>フローを重視する</li> </ul> <p>チームがどれだけ改善を実現しているのか一週間ごとで確認できるようになりました。同時に、連絡待ちや、急ぎの発生回数があるかも把握できるようになりました。 チーム外の人が見ても、詳しくは分からなくても、どのカラムにどれだけの作業があるのかはわかるようになっているハズです。</p> <p>「左から右へ進める」ということを前提にしています。進めるための障害(デザインする時の悩み)が発生し、検知するためのイベントができ、検知したことを定例で話すようになってきたので、issue を進めることを大切にできるようになってきたと感じています。</p> <p>もちろん、出来ていないこともあります。<br/> また終わっていない issue があるのに、新しい issue に着手することがあります。取り組む issue を増やすのではなく、完了する issue を増やすことを優先できていないときがあります。終わらせることを優先したいのですが、確認待ちなどもあり、In Progress が増えている状態です。</p> <h2 id="おわりに">おわりに</h2> <p>GitHub Projects を使ったチームのタスク管理についてまとめました。 やっていて、取り組まないといけないことが漏れていることは減りましたし、困っていることを見つけて、解決に進める機会が増えたことも感じます。</p> <p>チームで成果をだせる体制を作り「食べチョク」の改善に活かしていきます。</p> vividgarden-tech おいしそうが伝わる写真を選ぶ原則をチームで共有した hatenablog://entry/820878482963068201 2023-08-31T18:30:00+09:00 2023-08-31T18:30:01+09:00 こんにちは、松久です。 「食べチョク」では、キャンペーンなどで使うバナー・キービジュアルを自社で作成しています。作成する時に使う写真は、素材サイトで配布・販売されている写真や、生産者さんが撮影された写真を使います。これらの写真から使用する写真を選ぶのは、時間がかかります。また、選んだとしても、デザインレビューで「もう少しシズル感のある写真がいい」という指摘をもらうこともあります。そこで、写真を選ぶ基準をデザインチームで認識を揃えることにしました。 企画によって写真選びの基準は違う 「【濃厚派?さわやか派?】あなた好みの「いちじく」集めました!」という企画を公開しました。品目・品種の違いを味わっ… <p>こんにちは、松久です。</p> <p>「食べチョク」では、キャンペーンなどで使うバナー・キービジュアルを自社で作成しています。作成する時に使う写真は、素材サイトで配布・販売されている写真や、生産者さんが撮影された写真を使います。これらの写真から使用する写真を選ぶのは、時間がかかります。また、選んだとしても、デザインレビューで「もう少しシズル感のある写真がいい」という指摘をもらうこともあります。そこで、写真を選ぶ基準をデザインチームで認識を揃えることにしました。</p> <h2 id="企画によって写真選びの基準は違う">企画によって写真選びの基準は違う</h2> <p>「<a href="https://www.tabechoku.com/feature_articles/fig2023">【濃厚派?さわやか派?】あなた好みの「いちじく」集めました!</a>」という企画を公開しました。品目・品種の違いを味わってもらいたい、という「今が旬」という企画です。この企画での写真選びの基準を用意することにしました。</p> <h2 id="何を伝える目的の写真なのかを決める">何を伝える目的の写真なのかを決める</h2> <p>一番最初に決めたのは「何を伝える役割の写真」なのかを決めました。 決めたことは以下のとおりです。</p> <ul> <li>おいしくみえる <ul> <li>シズル感がある <ul> <li>果汁が見える</li> <li>果物であれば切り口が見える</li> </ul> </li> <li>思わず食べたくなる写真</li> <li>きれいに食べている</li> </ul> </li> <li>届く商品を勘違いさせない</li> </ul> <p>逆の項目も決めました。</p> <ul> <li>調理後の写真は原則ダメ <ul> <li>美味しそうだけれど、知ってほしいこと(品目・品種の違い)ではない</li> <li>企画が「料理」である必要があるときのみ</li> </ul> </li> <li>収穫前の状態 <ul> <li>今回の企画で知ってほしいこと(品目・品種の違い)ではない</li> <li>産直であること、鮮度、生産者さんといった別の連想がされやすい</li> </ul> </li> </ul> <p>いくつか試作をしながら、写真の役割を言葉にして項目案を出しました。一度、言葉に出てくると、役割とは逆の項目も出てきやすくなりました。</p> <p>写真の役割が決まったので、写真を並べて、他に検討することがないかデザインチームで話し合いました。<br/> 実際に出来た原則が以下の図です。</p> <p><figure class="figure-image figure-image-fotolife" title="Figma で原則を作り、バナーを作成するファイルに入れるようにしました"><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/v/vividgarden-tech/20230830/20230830174511.jpg" width="1200" height="597" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span><figcaption>Figma で原則を作り、バナーを作成するファイルに入れるようにしました</figcaption></figure></p> <p>話し合いをする中で、食器があるときや、フォークが刺さった状態の時なども含めて話し合いました。写真での食材の写りだけではなく、食材の周囲の事も合わせて、どんなイメージを持たれそうか検討して、写真選びの原則の認識を揃えることができました。</p> <h2 id="まだ出来ていないこと">まだ出来ていないこと</h2> <p>あくまでもバナーに使う写真なので、これでクリック数・クリック率の変化は、まだ未集計です。写真だけでクリック数・クリック率が決まるわけではないですが、いくつか試しながら調整していく必要がありそうです。</p> <h2 id="おわりに">おわりに</h2> <p>写真選びの基準を持つことで、写真選びのノウハウがデザインチームに貯めることが出来ました。小さなことも少しづつためて「食べチョク」の改善に活かしていきます。</p> vividgarden-tech 桃やぶどうのような同じ品目でバナーを作る前に事前準備したこと hatenablog://entry/820878482954142637 2023-07-31T18:30:00+09:00 2023-07-31T18:30:00+09:00 こんにちは、松久です。 「食べチョク」では、桃、ぶどう、梨の特集をしています。 特集では、キービジュアルや「チャート」と呼ばれる品種の収穫・販売時期を 1 枚の画像にまとめています。他にも「桃占い」というコンテンツの作成もありました。これらの画像を作成するにあたり、いくつか悩んだことや、取り組んだことがあります。 同じ品目に関するビジュアルを数種類作る難しさ 桃について固さから品種を選ぶ企画がスタートすることになり、キービジュアルが必要という話と「チャート」の画像、LINE などの広告用の画像を作ることになり始めてみて気づいたことが 2 つあります。 1 つめは、同じ「桃」という品目で画像を数… <p>こんにちは、松久です。</p> <p>「食べチョク」では、<a href="https://www.tabechoku.com/feature_articles/peach2023">桃</a>、<a href="https://www.tabechoku.com/feature_articles/grape_2023">ぶどう</a>、<a href="https://www.tabechoku.com/feature_articles/pear2023">梨</a>の特集をしています。 特集では、キービジュアルや「チャート」と呼ばれる品種の収穫・販売時期を 1 枚の画像にまとめています。他にも「桃占い」というコンテンツの作成もありました。これらの画像を作成するにあたり、いくつか悩んだことや、取り組んだことがあります。</p> <h2 id="同じ品目に関するビジュアルを数種類作る難しさ">同じ品目に関するビジュアルを数種類作る難しさ</h2> <p>桃について固さから品種を選ぶ企画がスタートすることになり、キービジュアルが必要という話と「チャート」の画像、LINE などの広告用の画像を作ることになり始めてみて気づいたことが 2 つあります。</p> <p>1 つめは、同じ「桃」という品目で画像を数種類作るので、踏襲するところと違いを出すところを決めて作り始めます。今までとは作る手順が増えていることです。 2 つめは、同じ「桃」という品目で画像ごとに目的に合わせた表現はしつつ、同じ「桃」であることを伝えるために何を踏襲するのかを決める必要がありました。また、桃の「柔らかい」「かたい」の表現方法を最初に決める必要もありました。最初に決めておかないと「桃」というテーマで繋がらなくなるためです。</p> <h2 id="難しさに気づいたタイミング">難しさに気づいたタイミング</h2> <p>当初から作る難しさを担当のグラフィックデザイナーの日報から感じてました。 ハッキリとわかったのは「<a href="https://www.tabechoku.com/feature_articles/peach_lists">桃の世界を探検しよう</a>」という特集の画像作成の時です。 サイトトップにはピンク色の桃に関するバナー画像が複数個ならんでいる状態で、さらにピンク色の桃のバナー画像を追加しても、伝えたいことが伝わらないと予想されました。</p> <p><figure class="figure-image figure-image-fotolife" title="「桃の世界を探検しよう」では周囲のバナーに合わせて色を変更した"><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/v/vividgarden-tech/20230731/20230731091700.jpg" width="1200" height="414" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span><figcaption>「桃の世界を探検しよう」では周囲のバナーに合わせて色を変更した</figcaption></figure></p> <p>対策として、桃の色を引き立てつつ、他のバナーではあまり使っていなかった青を背景に使うことにしました。 食べチョクでは、夏ギフト以外で青を使うことが少ないので、他との違いが明確になりました。</p> <p>同じ品目でバナー画像を作り、同時期に使われると同じ色・雰囲気のバナーが並ぶことが予想されました。そこで、企画が始まる前に準備することが必要だとわかりました。</p> <h2 id="企画が始まる前に準備をする">企画が始まる前に準備をする</h2> <p>「桃」の次に「ぶどう」が始まることになりました。 「桃」で気づいたことについて対策として、色の設計と表現案を下調べしておくことにしました。「ぶどう」は、他のサービス・商品ではどんな表現をしているのかまとめていきました。</p> <p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/v/vividgarden-tech/20230731/20230731091743.jpg" width="1200" height="665" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span></p> <p>「梨」では、黄色ベースとなることはわかったのですが、他の黄色系の果物との違いも含めて認識を揃えることにしました。パイナップルやレモンなど「黄色」の果物がありますが、どんな色と組み合わせることがあるか確認しています。</p> <p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/v/vividgarden-tech/20230731/20230731091843.jpg" width="1200" height="395" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span></p> <p>制作に入る前に、準備した内容を、グラフィックデザイナー同士で話をして認識を揃えることにしました。準備をしておくことで、デザインレビューも表現方法について話すことは減り、より伝えられる方法についての議論が増えました。</p> <h2 id="まだ出来ていないこと">まだ出来ていないこと</h2> <p>準備をすることで、作る時間を少し短縮することは出来ました。 しかし、使うことにした色をデザイナー以外には共有ができておらず、メルマガなどでは異なる色が使われており、サイト上での印象と揃えることがまだ出来ていません。</p> <p>他にも、食べチョクで使われていると、色の印象が揃っているかももう少し確認が必要そうです。 あと、来年も同じ特集をするとしたら、最終的にどんな色設計にしたのかなどをまとめておくことも必要そうです。</p> <h2 id="おわりに">おわりに</h2> <p>事前準備をすることで、バナー画像の作成時間を減らすことは出来ました。 ですが、まだ出来ていないこともたくさんあることに気づきました。今回気づいたことも、今後の取り組みで活かして「食べチョク」の改善を進めていきます。</p> vividgarden-tech 「似ているけれど少し違う色」を減らす取り組み hatenablog://entry/820878482945075497 2023-06-30T11:50:00+09:00 2023-06-30T11:50:00+09:00 こんにちは、松久です。 UI を作るときに、色は定義されている中から選ぶのを基本にしています。 色の定義が Figma で行われ、実装に反映され、違う場合は Stylelint で指摘される運用がされています。 色の運用が進んできたので、過去に定義した色や、デザイナー間で色が共有されていなかったために起きた「似ているけれど少し違う色」を減らす対応をすすめています。 似ている少し違う色がある問題 「似ているけれど少し違う色」がある状態は、使っている人に「なんとなく、まとまりに欠ける」印象を与えます。ブランドイメージを下げたり、いい買い物した、という印象を下げることもあります。 デザイナーや実装者… <p>こんにちは、松久です。</p> <p>UI を作るときに、色は定義されている中から選ぶのを基本にしています。 色の定義が Figma で行われ、実装に反映され、違う場合は Stylelint で指摘される運用がされています。</p> <p>色の運用が進んできたので、過去に定義した色や、デザイナー間で色が共有されていなかったために起きた「似ているけれど少し違う色」を減らす対応をすすめています。</p> <h2 id="似ている少し違う色がある問題">似ている少し違う色がある問題</h2> <p>「似ているけれど少し違う色」がある状態は、使っている人に「なんとなく、まとまりに欠ける」印象を与えます。ブランドイメージを下げたり、いい買い物した、という印象を下げることもあります。</p> <p>デザイナーや実装者にとっては、色の違いの意味がわからず実装のコストが増える原因の 1 つになります。</p> <p>この問題を解消するために、少し違う色が実装されているのかを確認します。 CSS に設定されている色の確認方法は、Chrome の CSS Overview 機能を使います。</p> <p><figure class="figure-image figure-image-fotolife" title="似ているけれど少し違う色を目視で見つける"><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/v/vividgarden-tech/20230627/20230627182754.jpg" width="1072" height="864" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span><figcaption>似ているけれど少し違う色を目視で見つける</figcaption></figure></p> <p>確認してみると、似ているが少し違う色が見つかります。 これを、1 つずつ減らしていきます。</p> <h2 id="少し違う色を減らしていく取り組み">少し違う色を、減らしていく取り組み</h2> <p>見つけた「似ているけれど少し違う色」を、減らしていく取り組みを今しています。 取り組みは、4 つに分けて取り組むことにしました。</p> <h3 id="1新しい色の定義の変数に置き換える">1.新しい色の定義の変数に置き換える</h3> <p>直接色コードが記述されている箇所を SCSS の変数を使えるところは、置き換えをします。 今後、色の調整をするとしても、変数を変えれば調整ができるからです。</p> <h3 id="2過去の-SCSS-の色定義の利用を辞める">2.過去の SCSS の色定義の利用を辞める</h3> <p>過去の色の定義の SCSS を新しいものに置き換えていきます。 新しく使われないように、Stylelint でも指摘がされるようにしています。 最後に、利用がなければ過去の SCSS の定義を消して終わりです。</p> <h3 id="3少し違う色を新しい定義に寄せて置き換える">3.少し違う色を新しい定義に寄せて置き換える</h3> <p>まだこれは実施をしていません。 少し違う色なので、対応箇所が 100 箇所ぐらいの色もあります。そのため、少し違う色の方が標準になっている色もあるためです。</p> <h3 id="4色の-Utility-Class-を置き換える">4.色の Utility Class を置き換える</h3> <p>色に関する Utility Class がいくつかあります。設定されている色を新しく定義された色にしていく取り組みをしています。</p> <p>難しいのは同じ Utility Class が複数あることです。 同じクラス名だけれど、色の定義が違うこともあります。</p> <pre class="code lang-css" data-lang="css" data-unlink><span class="synIdentifier">.text-gray</span> <span class="synIdentifier">{</span> <span class="synType">color</span>: <span class="synConstant">#555</span>; <span class="synIdentifier">}</span> // 同じクラス名だが、別の色が使われている <span class="synIdentifier">.text-gray</span> <span class="synIdentifier">{</span> <span class="synType">color</span>: <span class="synConstant">#b8b8b8</span>; <span class="synIdentifier">}</span> </pre> <p>バラバラになっていますが、定義に近い色がある場合は、一気に統一をします。</p> <p>問題なのは、バラバラだけれど似ていない色が出てくる場合もあります。 この問題については、一気に統一をするのを避けています。</p> <pre class="code lang-css" data-lang="css" data-unlink><span class="synIdentifier">.text-red</span> <span class="synIdentifier">{</span> <span class="synType">color</span>: <span class="synConstant">#ff7355</span> <span class="synSpecial">!important</span>; <span class="synIdentifier">}</span> <span class="synIdentifier">.text-red</span> <span class="synIdentifier">{</span> <span class="synType">color</span>: <span class="synConstant">#d0555a</span>; <span class="synType">font-weight</span>: <span class="synConstant">600</span>; <span class="synIdentifier">}</span> <span class="synIdentifier">.text-red</span> <span class="synIdentifier">{</span> <span class="synType">font-size</span>: <span class="synConstant">1.3rem</span>; <span class="synIdentifier">}</span> </pre> <p>新しく使われるのを防ぎ、使われている箇所を置き換えるしかなさそうです。 上記の例にある .text-red では 100 数箇所あるので、一旦統一するか、置き換えるかを検討しているところです。</p> <h2 id="おわりに">おわりに</h2> <p>似ているけれど、少し違う色は、サービスを提供するのであれば、サービスが動かなくなるような存在ではありません。提供するサービスの質や、サービスを運用するための手間が増えてしまう存在です。</p> <p>時間はかかりそうですが、少しつづ減らしてくことで、「食べチョク」の改善を進めます。</p> vividgarden-tech ふりかえりからデザイナー同士のミーティングを改善しました hatenablog://entry/820878482936926641 2023-05-31T12:00:00+09:00 2023-05-31T12:00:16+09:00 こんにちは、松久です。 以前、「デザイナー同士で行う 3 つのミーティング設計」というお話を書きました。そこから、半年ぐらい運用していくなかで、変えたことがあります。 変わらないこと 大きく 3 つのミーティングがあることは変わりません。 プランニング・交通整理 Designer Team Discussion Monthly Update ミーティングの数も変わらず、時間も変わっていません。 変わったこと Designer Team Discussion が少し変わりました。 以前は話したことを「📝🍵🍡」の絵文字をつけて、Slack のデザイナーチャンネルにメモしていました。そこから、取り組… <p>こんにちは、松久です。</p> <p>以前、「<a href="https://tech.tabechoku.com/entry/2022/10/31/113000">デザイナー同士で行う 3 つのミーティング設計</a>」というお話を書きました。そこから、半年ぐらい運用していくなかで、変えたことがあります。</p> <h2 id="変わらないこと">変わらないこと</h2> <p>大きく 3 つのミーティングがあることは変わりません。</p> <ul> <li>プランニング・交通整理</li> <li>Designer Team Discussion</li> <li>Monthly Update</li> </ul> <p>ミーティングの数も変わらず、時間も変わっていません。</p> <h2 id="変わったこと">変わったこと</h2> <p>Designer Team Discussion が少し変わりました。 以前は話したことを「📝🍵🍡」の絵文字をつけて、Slack のデザイナーチャンネルにメモしていました。そこから、取り組むことを <a href="https://esa.io/">esa</a> に書き出して Monthly Update で進捗を確認していました。この方法だと、頻度が月 1 回のため成果を出し続けることが難しかったです。 月に 1 回の進捗確認だと、忘れてしまったり、進まなかったと気づくのが 1 ヶ月後になる可能性があります。そこから対応策を決めると、さらに 1 ヶ月かかり、対応までの時間がかかります。デザイナー同士で検討するのにも時間が必要な場合は特に時間がかかります。 このことに、Monthly Update という月 1 回のふりかえりで気づきました。</p> <p>この問題の対応として、 Designer Team Discussion で気になり話し合いたいことも、見つけた不具合も全て GitHub の issue にすることにしました。issue にしたことで、週 3 回の「プランニング・交通整理」のミーティングで確認することができるようになり、進み具合や、進めるのに障害になっていることを以前より早く把握できるようになりました。</p> <p>GitHub Project に「🧵Talking」というレーンを作りました。このレーンには、話し合いが中心となる issue を入れておくことにしました。</p> <p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/v/vividgarden-tech/20230530/20230530094559.png" width="1200" height="128" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span></p> <p>このレーンにある issue を Designer Team Discussion の月曜日に話しています。月曜日は、全員揃う日であること、週の始まりで週の予定を調整しやすいことが理由です。</p> <p>現在は、この方法で少しづつですが、確実に「カイゼン」を進められています。</p> <h2 id="おわりに">おわりに</h2> <p>自分たちのやり方に、より良い方法がないか Monthly Update というふりかえりから、気づいて別な方法に切り替えていくことができました。</p> <p>ふりかえりから、自分たちのやり方を定期的に見直して「食べチョク」の改善を進めていきます。</p> vividgarden-tech RubyKaigi参加者向け飲食店情報 hatenablog://entry/4207575160647651135 2023-05-10T16:00:00+09:00 2023-05-10T16:00:00+09:00 ようこそ!松本へ! こんにちは!ビビッドガーデンの美味しいものハンター兼エンジニアのy-hakutakuです。 弊社は今週5月11日〜13日にかけて開催される RubyKaigi 2023 で ローカルフードスポンサー として協賛しています。 オフライン会場ではブース出展をしており、地元生産者さんの食材やプレゼントありのクイズ企画を実施していますのでぜひお立ち寄りください! せっかく松本まできたし、美味いものを食べたい おっと、ちょっと当日自分がどういう言い方をしたか覚えてないんですが、正確に言うと、若干数のお弁当の配布の用意もあります。が、全員分の食数も食べるスペースも全然ないので、参加者の… <h1 id="ようこそ松本へ">ようこそ!松本へ!</h1> <p>こんにちは!ビビッドガーデンの美味しいものハンター兼エンジニアの<a href="https://twitter.com/y_hakutaku">y-hakutaku</a>です。 弊社は今週5月11日〜13日にかけて開催される <a href="https://rubykaigi.org/2023/">RubyKaigi 2023</a> で <a href="https://rubykaigi.org/2023/sponsors/#local-food">ローカルフードスポンサー</a> として協賛しています。 オフライン会場ではブース出展をしており、地元生産者さんの食材やプレゼントありのクイズ企画を実施していますのでぜひお立ち寄りください!</p> <h2 id="せっかく松本まできたし美味いものを食べたい">せっかく松本まできたし、美味いものを食べたい</h2> <p><blockquote data-conversation="none" class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">おっと、ちょっと当日自分がどういう言い方をしたか覚えてないんですが、正確に言うと、若干数のお弁当の配布の用意もあります。<br>が、全員分の食数も食べるスペースも全然ないので、参加者の皆さんには基本的には外でのランチを推奨します。という感じです。 <a href="https://twitter.com/hashtag/rubykaigi?src=hash&amp;ref_src=twsrc%5Etfw">#rubykaigi</a> <a href="https://t.co/NB8hVSFGtQ">https://t.co/NB8hVSFGtQ</a></p>&mdash; Akira Matsuda (@a_matsuda) <a href="https://twitter.com/a_matsuda/status/1655540483572248577?ref_src=twsrc%5Etfw">2023年5月8日</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> </p> <p>今年は外のランチ推奨とオーガナイザーの松田さんもおっしゃっていたので、お店の情報はあればあるほど助かると思い、安曇野にルーツがある<a href="https://twitter.com/y_hakutaku">y-hakutaku</a>が紹介していきます!</p> <h2 id="そば">そば</h2> <h3 id="榑木野">榑木野</h3> <p>昔からの有名店。松本駅側にもあるので、あずさに乗る前にいかがでしょうか。 <a href="https://www.kurekino.co.jp/tenpo/ekisha/">https://www.kurekino.co.jp/tenpo/ekisha/</a></p> <h3 id="みよ田">みよ田</h3> <p>松本、上高地周辺の郷土料理に「とうじそば」を提供しているお店。ご当地グルメハンターにおすすめです! <a href="https://www.ohtaki-gp.jp/brand/brand14/">https://www.ohtaki-gp.jp/brand/brand14/</a></p> <h2 id="ワインバー">ワインバー</h2> <h3 id="peg">peg</h3> <p>学生時代の先輩のおすすめのお店。ワインで酔いたい方はこちらへ🍷 <a href="https://tabelog.com/nagano/A2002/A200201/20020569/">https://tabelog.com/nagano/A2002/A200201/20020569/</a></p> <h2 id="居酒屋">居酒屋</h2> <h3 id="山女や">山女や</h3> <p>地酒を飲みながら、串はいかがでしょうか。南信地方の「ザザムシ」や「蜂の子」などもあります🐛 <a href="https://tabelog.com/nagano/A2002/A200201/20000078/">https://tabelog.com/nagano/A2002/A200201/20000078/</a></p> <h3 id="松本つなぐ横丁">松本つなぐ横丁</h3> <p>複数の店舗が併設されているタイプ施設。色々楽しみたい方はいかがでしょうか。 STORESさん主催のアフターパーティの会場でもあります。 <a href="https://hey.connpass.com/event/277763/">https://hey.connpass.com/event/277763/</a> <a href="https://matsumoto-yokocho.jp/">https://matsumoto-yokocho.jp/</a></p> <h2 id="大衆食堂">大衆食堂</h2> <h3 id="たけしや焼きそば">たけしや(焼きそば) </h3> <p>焼きそば通にも一目置かれる名店です。わざわざここに来るために松本に来る焼きそばマニアもいらっしゃるそうです。 <a href="https://tabelog.com/nagano/A2002/A200201/20001590/">https://tabelog.com/nagano/A2002/A200201/20001590/</a></p> <h3 id="白雪食堂">白雪(食堂)</h3> <p>松本周辺って普通サイズが大盛りということも。今はそういうお店もだいぶ減っていますがこちらは現役です! ガッツリいきたい方はぜひ! <a href="https://tabelog.com/nagano/A2002/A200201/20005440/">https://tabelog.com/nagano/A2002/A200201/20005440/</a></p> <h3 id="せいこうえん-中華料理店">せいこうえん (中華料理店)</h3> <p>駅地下の昔ながらの中華料理店です。飲んだ後の〆にもお食事にもいかがでしょうか! <a href="https://tabelog.com/nagano/A2002/A200201/20011076/">https://tabelog.com/nagano/A2002/A200201/20011076/</a></p> <h2 id="焼き鳥">焼き鳥</h2> <h3 id="鳥心">鳥心</h3> <p>「焼き鳥を食べるならここ!」と学生時代の先輩に教えてもらったお店です。 <a href="https://tabelog.com/nagano/A2002/A200201/20003244/">https://tabelog.com/nagano/A2002/A200201/20003244/</a></p> <h2 id="洋食">洋食</h2> <h3 id="どんぐり">どんぐり</h3> <p>シルバーが金属バケツで置かれ、一緒に入っているどんぐり通信を読みながら注文を待ちます。 メニューの名前にはマスターのご家族の名前が冠されたものもあり、あたたかさを感じるお店です。 <a href="https://tabelog.com/nagano/A2002/A200201/20000428/">https://tabelog.com/nagano/A2002/A200201/20000428/</a></p> <h2 id="カレー">カレー</h2> <h3 id="メーヤウ">メーヤウ</h3> <p>カレーの名店。ここのカレーを食べに松本まで来たこともありました。辛味が苦手な方は非推奨です。 また、会場からは少し離れているので、向かう方はタクシーで。満席の時は近くの桐店へGO! <a href="https://www.facebook.com/maeyaomatsumoto">https://www.facebook.com/maeyaomatsumoto</a></p> <h2 id="ラーメン">ラーメン</h2> <h3 id="おおぼし">おおぼし</h3> <p>こってりとあっさりのラーメンのある名店。ばりこてが個人的にはおすすめです! <a href="https://tabelog.com/nagano/A2002/A200201/20025262/">https://tabelog.com/nagano/A2002/A200201/20025262/</a></p> <h2 id="うなぎ">うなぎ</h2> <h3 id="山勢">山勢</h3> <p>うなぎのみのお店、地元の工場などで繁忙期に差し入れとして利用されたりもします! <a href="https://tabelog.com/nagano/A2002/A200201/20021045/">https://tabelog.com/nagano/A2002/A200201/20021045/</a></p> <h2 id="焼肉">焼肉</h2> <h3 id="明月館">明月館</h3> <p>松本の焼き肉の名店。地元民に愛される名店です。私もいきたい! <a href="https://tabelog.com/nagano/A2002/A200201/20003701/">https://tabelog.com/nagano/A2002/A200201/20003701/</a></p> <h2 id="和食">和食</h2> <h3 id="蔵">蔵</h3> <p>ちょっと高価格帯ですが、和食や郷土料理をじっくり味わいたい方へいかがでしょうか。 <a href="https://www.matsumotokura.com/">https://www.matsumotokura.com/</a></p> <h2 id="ビストロ">ビストロ</h2> <h3 id="オークリヨードヴァン">オー・クリヨー・ド・ヴァン</h3> <p>個人的名物はステーキとフライドポテト。パリのビストロ感を強く感じるのは外のラタンチェアからかも。 <a href="https://crieur-japon.com/menu/">https://crieur-japon.com/menu/</a></p> <h2 id="スイーツ">スイーツ</h2> <h3 id="マサムラ">マサムラ</h3> <p>シュークリーム。懐かしい感じ。地元民なら知っている名店。会場近くにあるので糖分の補給にどうぞ。 <a href="https://tabelog.com/nagano/A2002/A200201/20000074/">https://tabelog.com/nagano/A2002/A200201/20000074/</a></p> <h2 id="喫茶店">喫茶店</h2> <h3 id="珈琲美学-アベ">珈琲美学 アベ</h3> <p>全国区になった純喫茶。サイフォンコーヒーやパフェが有名。電車待ちにいかがでしょうか。 <a href="https://tabelog.com/nagano/A2002/A200201/20001420/">https://tabelog.com/nagano/A2002/A200201/20001420/</a></p> <h2 id="お土産">お土産</h2> <h3 id="開運堂">開運堂</h3> <p>祖父母は必ずここでお土産を買っていました。 <a href="https://www.kaiundo.co.jp/shop">https://www.kaiundo.co.jp/shop</a> 「これはうまい」という商品名のくるみ饅頭がオススメです。 <a href="https://www.kaiundo.co.jp/products/detail/2510">https://www.kaiundo.co.jp/products/detail/2510</a></p> <h3 id="竹風堂">竹風堂</h3> <p>北信地域の和菓子屋さん。有名なお布施の栗を使った商品が有名です。 栗鹿の子や栗羊羹など、マストバイだと思います。 <a href="https://chikufudo.com/shop/491/">https://chikufudo.com/shop/491/</a></p> <p>以上、ざっと紹介いたしました。 マップも合わせて公開します。</p> <iframe src="https://www.google.com/maps/d/embed?mid=14lxLxIYlkjtZZhZIjKY-x_We9SUkRxU&hl=ja&ehbc=2E312F" width="640" height="480"></iframe> <p>また、松本駅周辺の飲食エリアの大体のイメージも載せます。 <figure class="figure-image figure-image-fotolife" title="会場周辺地図"><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/v/vividgarden-tech/20230510/20230510140553.png" width="1200" height="929" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span><figcaption>会場周辺地図</figcaption></figure></p> <p>まとめ 松本駅周辺は飲食店が密集しており、夜の散策もきっと参加者同士の交流においても楽しい場所かと思います。 <a href="https://rubykaigi.org/2023/">RubyKaigi 2023</a>、ご参加の方はぜひ松本グルメをご堪能ください。</p> <p>私も初日だけですがブースでスタッフとしておりますので、是非お話しさせてください。 松本名物のお話や、食べチョクの美味しい商品などもご紹介できるかと思います。</p> <p>それでは会場でお待ちしております!</p> vividgarden-tech FigmaとSCSSで色の定義を合わせるための運用方法 hatenablog://entry/4207112889984623301 2023-04-28T17:50:00+09:00 2023-04-28T17:50:00+09:00 こんにちは、松久です。 食べチョクでは、デザインツールとして Figma を使っています。色の定義を Figma の Color Styles で管理しています。 管理はしているものの SCSS には反映できていない状態でした。このままだと何が管理されている色なのかエンジニアとデザイナーの認識が揃わなくなります。そこで、Figma の Color Styles と、SCSS で用意した色の変数を合わせられる状態を作っていくことにしました。 色を定義するまで 1 年前ぐらいに UI の色の定義の再確認をして、一部曖昧なところは残りつつも SCSS の変数の定義をしました。 決まっていない部分があ… <p>こんにちは、松久です。</p> <p>食べチョクでは、デザインツールとして Figma を使っています。色の定義を Figma の Color Styles で管理しています。 管理はしているものの SCSS には反映できていない状態でした。このままだと何が管理されている色なのかエンジニアとデザイナーの認識が揃わなくなります。そこで、Figma の Color Styles と、SCSS で用意した色の変数を合わせられる状態を作っていくことにしました。</p> <h2 id="色を定義するまで">色を定義するまで</h2> <p>1 年前ぐらいに UI の色の定義の再確認をして、一部曖昧なところは残りつつも SCSS の変数の定義をしました。 決まっていない部分があったままだったのですが、最近、未確定部分も定義して運用されはじめました。</p> <h2 id="SCSS-の色の変数をまとめる">SCSS の色の変数をまとめる</h2> <p>当初、variable.scss ファイルに色の定義だけではなく、いろいろな変数がまとまっていました。 今後、@use を使うことを想定して、役割ごとのファイルに分割しておくことにしました。また、Stylelint などで指摘するときも、どのファイルを確認すると良いか伝えやすくなることを配慮しました。</p> <h2 id="Stylelint-で色の指摘をする">Stylelint で色の指摘をする</h2> <p>定義済みの色を直接指定しようとした場合は、Stylelint のプラグインを作って指摘することにしました。 定義済みの SCSS を json にすることも検討しましたが、追加や変更することは多くないので、そのまま書きました。</p> <pre class="code lang-javascript" data-lang="javascript" data-unlink><span class="synStatement">const</span> stylelint = require(<span class="synConstant">&quot;stylelint&quot;</span>); <span class="synStatement">const</span> ruleName = <span class="synConstant">&quot;tabechoku/use-ui-color&quot;</span>; <span class="synStatement">const</span> messages = stylelint.utils.ruleMessages(ruleName, <span class="synIdentifier">{</span> reject: (color, definedColor) =&gt; <span class="synConstant">`</span><span class="synSpecial">${color}</span><span class="synConstant"> は </span><span class="synSpecial">${definedColor}</span><span class="synConstant"> が定義済みです。定義されているSCSS変数を利用してください。`</span>, <span class="synIdentifier">}</span>); <span class="synStatement">const</span> meta = <span class="synIdentifier">{}</span>; <span class="synStatement">const</span> colors = <span class="synIdentifier">{</span> <span class="synConstant">&quot;#ffffff&quot;</span>: <span class="synConstant">&quot;$color-gray0&quot;</span>, <span class="synConstant">&quot;#fff&quot;</span>: <span class="synConstant">&quot;$color-gray0&quot;</span>, <span class="synIdentifier">}</span>; <span class="synStatement">const</span> ruleFunction = (primary) =&gt; <span class="synIdentifier">{</span> <span class="synStatement">return</span> (root, result) =&gt; <span class="synIdentifier">{</span> <span class="synStatement">const</span> validOptions = stylelint.utils.validateOptions(result, ruleName, <span class="synIdentifier">{</span> actual: primary, <span class="synIdentifier">}</span>); <span class="synStatement">if</span> (!validOptions) <span class="synIdentifier">{</span> <span class="synStatement">return</span>; <span class="synIdentifier">}</span> root.walkDecls((decl) =&gt; <span class="synIdentifier">{</span> <span class="synStatement">const</span> matched = decl.value.match(/#.*/); <span class="synStatement">if</span> (!matched) <span class="synIdentifier">{</span> <span class="synStatement">return</span>; <span class="synIdentifier">}</span> <span class="synStatement">const</span> definedColor = colors<span class="synIdentifier">[</span>decl.value.toLowerCase()<span class="synIdentifier">]</span>; <span class="synStatement">if</span> (definedColor) <span class="synIdentifier">{</span> stylelint.utils.report(<span class="synIdentifier">{</span> ruleName, result, message: messages.reject(decl.value, definedColor), node: decl, <span class="synIdentifier">}</span>); <span class="synIdentifier">}</span> <span class="synIdentifier">}</span>); <span class="synIdentifier">}</span>; <span class="synIdentifier">}</span>; ruleFunction.ruleName = ruleName; ruleFunction.messages = messages; ruleFunction.meta = meta; module.exports = stylelint.createPlugin(ruleName, ruleFunction); </pre> <h2 id="指摘されるのがうっとうしい">指摘されるのがうっとうしい</h2> <p>Figma の Inspect に表示されるのをコピペすることが多く、Stylelint に指摘されるのはうっとうしく感じそうです。</p> <p><figure class="figure-image figure-image-fotolife" title=""><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/v/vividgarden-tech/20230426/20230426100537.png" width="1130" height="196" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span><figcaption>「/<em> Primary/$primary2 </em>/」から$primary2が使われていることがわかる</figcaption></figure></p> <p>Figma の Inspect では Color Style が使われているところには定義名が表示されるので、SCSS の定義名と合わせて気付けるようにしています。 理想は、Zeplin のように Inspect に変数名で表示されることですが、できないのが残念です。あとは、Prettier にフォーマットしてもらう方法もあるのですが未着手です。</p> <h2 id="おわりに">おわりに</h2> <p>Figma の Color Style で定義した色を運用するために、Stylelint で指摘することにした話でした。まだ問題はありますが、いろいろな色が実装されることは減らすことができました。</p> <p>個々のデザイナーの力をより発揮しやすくする取り組みをすることで「食べチョク」の改善に向き合っていきます。</p> vividgarden-tech 今年もやります!! RubyKaigi 2023の"Local Food Sponsor"。長野の絶品食材をご堪能あれ hatenablog://entry/4207112889984905027 2023-04-27T12:37:35+09:00 2023-04-27T13:20:11+09:00 こんにちは。プロダクト開発チームの Engineering Manager 兼 人事の @hirashun です。 今年もビビッドガーデンはRubyKaigiに"Local Food Sponsor"として協賛します。Rubyコミュニティと長野の地に感謝を込めて、地元長野の絶品食材をお届けします! 産直通販サイト「食べチョク」が「RubyKaigi 2023」にLocal Food Sponserとして協賛。当日収穫した新鮮なりんごなど長野県のこだわり食材を会場で配布。|(株)ビビッドガーデン/食べチョクのプレスリリース 昨年に続き、やっていきます! 利用率No.1の産直通販サイト「食べチョク… <p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/v/vividgarden-tech/20230427/20230427122138.png" width="1200" height="630" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span> こんにちは。プロダクト開発チームの Engineering Manager 兼 人事の <a href="https://twitter.com/hirashunshun">@hirashun</a> です。</p> <p>今年もビビッドガーデンはRubyKaigiに"Local Food Sponsor"として協賛します。Rubyコミュニティと長野の地に感謝を込めて、地元長野の絶品食材をお届けします!</p> <p><a href="https://prtimes.jp/main/html/rd/p/000000217.000025043.html">&#x7523;&#x76F4;&#x901A;&#x8CA9;&#x30B5;&#x30A4;&#x30C8;&#x300C;&#x98DF;&#x3079;&#x30C1;&#x30E7;&#x30AF;&#x300D;&#x304C;&#x300C;RubyKaigi 2023&#x300D;&#x306B;Local Food Sponser&#x3068;&#x3057;&#x3066;&#x5354;&#x8CDB;&#x3002;&#x5F53;&#x65E5;&#x53CE;&#x7A6B;&#x3057;&#x305F;&#x65B0;&#x9BAE;&#x306A;&#x308A;&#x3093;&#x3054;&#x306A;&#x3069;&#x9577;&#x91CE;&#x770C;&#x306E;&#x3053;&#x3060;&#x308F;&#x308A;&#x98DF;&#x6750;&#x3092;&#x4F1A;&#x5834;&#x3067;&#x914D;&#x5E03;&#x3002;&#xFF5C;(&#x682A;)&#x30D3;&#x30D3;&#x30C3;&#x30C9;&#x30AC;&#x30FC;&#x30C7;&#x30F3;&#xFF0F;&#x98DF;&#x3079;&#x30C1;&#x30E7;&#x30AF;&#x306E;&#x30D7;&#x30EC;&#x30B9;&#x30EA;&#x30EA;&#x30FC;&#x30B9;</a></p> <h1 id="昨年に続きやっていきます">昨年に続き、やっていきます!</h1> <p>利用率No.1の産直通販サイト「食べチョク」を開発/運用するビビッドガーデンではリリース当初からバックエンドをすべてRubyで開発しており、ユーザーに迅速なサービス提供ができる恩恵を最大限にあずかっています。</p> <p>昨年スポンサーとしてRubyKaigiに初参加しコミュニティの熱の高さととそこに薪をくべ続ける価値を再認識しました。<strong>ビビッドガーデンとしても引き続きRubyとそれを支えるコミュニティに貢献し続けたいと考えています。</strong></p> <p>そしてRubyKaigiは開催される都市自体を楽しむお祭りでもあります。その土地の魅力を伝えることも大事なポイントです。</p> <p><blockquote data-conversation="none" class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">食べチョクさんのブースのおかげで三重の美味しいもの沢山知ることができました! 帰ってハチミツ食べるの楽しみ<br> <a href="https://twitter.com/hashtag/rubykaigi?src=hash&amp;ref_src=twsrc%5Etfw">#rubykaigi</a> <a href="https://twitter.com/hashtag/%E9%A3%9F%E3%81%B9%E3%83%81%E3%83%A7%E3%82%AF?src=hash&amp;ref_src=twsrc%5Etfw">#食べチョク</a> <a href="https://t.co/XE5Yk0rsuO">https://t.co/XE5Yk0rsuO</a></p>&mdash; yana-gi (@yana_gis) <a href="https://twitter.com/yana_gis/status/1568595656725626886?ref_src=twsrc%5Etfw">2022年9月10日</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> </p> <p><blockquote data-conversation="none" class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr"><a href="https://twitter.com/hashtag/rubykaigi?src=hash&amp;ref_src=twsrc%5Etfw">#rubykaigi</a> <a href="https://twitter.com/hashtag/%E9%A3%9F%E3%81%B9%E3%83%81%E3%83%A7%E3%82%AF?src=hash&amp;ref_src=twsrc%5Etfw">#食べチョク</a> 牛と鰻と海老しかしらなかったけど配布いただいた三重県の特産品、とても美味しかったです。三重の魅力を教えてくださってありがとうございました! <a href="https://t.co/sFolrL2RbB">https://t.co/sFolrL2RbB</a></p>&mdash; 😺 (@neko314_) <a href="https://twitter.com/neko314_/status/1568501649358090241?ref_src=twsrc%5Etfw">2022年9月10日</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> </p> <p>昨年は想像以上に反響があり、多くのRubyistに三重の魅力とおいしさをお届け出来たという実感がありました。 感想を教えてくれた皆さん、お話させていただいた皆さん、本当にありがとうございました!</p> <p>ビビッドガーデンとしては活動の継続に踏み切りました。やっていく!</p> <h1 id="より多くの人に楽しんでもらいたい">より多くの人に楽しんでもらいたい</h1> <p>オフライン参加の方だけでなく、オンライン参加の方にも楽しんでいただけるように、以下の2つのスポンサー内容を準備しました。</p> <ul> <li><strong>日替わりで長野の食材を会場にて配布</strong></li> <li><strong>長野の特産品を抽選で毎日5名の方にプレゼント</strong></li> </ul> <p>それぞれ説明していきます!</p> <h2 id="日替わりで長野の食材を配布します">日替わりで長野の食材を配布します</h2> <p>昨年は大変多くの方に楽しんでいただきました! <blockquote data-conversation="none" class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">にんじんジュースマジで美味しかった。生のにんじん苦手だけど、これはごくごく飲めた🥕<br>こんな飲み物があるって初めて知ったし、美味しすぎて最高すぎたー٩(๑❛ᴗ❛๑)۶<a href="https://twitter.com/hashtag/%E9%A3%9F%E3%81%B9%E3%83%81%E3%83%A7%E3%82%AF?src=hash&amp;ref_src=twsrc%5Etfw">#食べチョク</a> <a href="https://twitter.com/hashtag/rubykaigi?src=hash&amp;ref_src=twsrc%5Etfw">#rubykaigi</a> <a href="https://t.co/7ijVyoJN03">pic.twitter.com/7ijVyoJN03</a></p>&mdash; みっきー (@mikiko_bridge) <a href="https://twitter.com/mikiko_bridge/status/1567801832050929665?ref_src=twsrc%5Etfw">2022年9月8日</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> </p> <p>が、反省として「初回の休憩ですべて配りきってしまう」「秒で売り切れる」というのがありました。 なので今年は1.5倍です。より多くの皆さんに届けたいと思います!</p> <h3 id="Day1幻の林檎ピンクレディ︎とフリーズドライセット">Day1「幻の林檎」ピンクレディ®︎とフリーズドライセット</h3> <p><figure class="figure-image figure-image-fotolife" title="長野と言えば"><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/v/vividgarden-tech/20230427/20230427122228.png" width="1200" height="630" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span></figure></p> <pre class="code" data-lang="" data-unlink>シャンパンのような食味、重厚感のある食感。 懐かしさと新しさのハイブリッド。 ぜひお召し上がりください。</pre> <p><a href="https://www.tabechoku.com/producers/21528">&#x5B89;&#x66C7;&#x91CE;&#x30D5;&#x30A1;&#x30DF;&#x30EA;&#x30FC;&#x8FB2;&#x7523;&#x3078;&#x306E;&#x30EC;&#x30D3;&#x30E5;&#x30FC;&#x30FB;&#x5546;&#x54C1;&#xFF1A;&#x9577;&#x91CE;&#x770C;&#xFF5C;&#x98DF;&#x3079;&#x30C1;&#x30E7;&#x30AF;&#xFF5C;&#x7523;&#x5730;&#x76F4;&#x9001;(&#x7523;&#x76F4;)&#x304A;&#x53D6;&#x308A;&#x5BC4;&#x305B;&#x901A;&#x8CA9; - &#x8FB2;&#x5BB6;&#x30FB;&#x6F01;&#x5E2B;&#x304B;&#x3089;&#x65EC;&#x306E;&#x98DF;&#x6750;&#x3092;&#x76F4;&#x9001;</a></p> <h3 id="Day2食べる宝石有機ドライほおずき">Day2「食べる宝石」有機ドライほおずき</h3> <p><figure class="figure-image figure-image-fotolife" title="ほおずき、知っていますか?"><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/v/vividgarden-tech/20230427/20230427122304.png" width="1200" height="625" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span></figure></p> <pre class="code" data-lang="" data-unlink>見た目もかわいく、ビタミンA、ビタミンCやビタミンB群のイノシトール、カロテン、鉄分などが含まれ、 いわゆる「スーパーフード」と呼ばれて注目されています。 見ても良し、食べても美味しいほおずきはきっと皆さまをとりこにするでしょう。</pre> <p><a href="https://www.tabechoku.com/producers/20124">&#x767E;&#x7B11;&#x8FB2;&#x623F;&#x3078;&#x306E;&#x30EC;&#x30D3;&#x30E5;&#x30FC;&#x30FB;&#x5546;&#x54C1;&#xFF1A;&#x9577;&#x91CE;&#x770C;&#xFF5C;&#x98DF;&#x3079;&#x30C1;&#x30E7;&#x30AF;&#xFF5C;&#x7523;&#x5730;&#x76F4;&#x9001;(&#x7523;&#x76F4;)&#x304A;&#x53D6;&#x308A;&#x5BC4;&#x305B;&#x901A;&#x8CA9; - &#x8FB2;&#x5BB6;&#x30FB;&#x6F01;&#x5E2B;&#x304B;&#x3089;&#x65EC;&#x306E;&#x98DF;&#x6750;&#x3092;&#x76F4;&#x9001;</a></p> <h3 id="Day3最終日にうれしいたっぷり野菜が溶け込んだ玄米入り無添加スープ">Day3「最終日にうれしい」たっぷり野菜が溶け込んだ玄米入り無添加スープ</h3> <p><figure class="figure-image figure-image-fotolife" title="最終日くらいにあるとうれしい"><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/v/vividgarden-tech/20230427/20230427122338.png" width="649" height="462" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span></figure></p> <pre class="code" data-lang="" data-unlink>種をまいて畑で実った野菜を丸ごといただく感覚を味わってほしいと思います。 食欲がないときや風邪気味のときにもおすすめです!</pre> <p><a href="https://www.tabechoku.com/producers/20150">&#x306E;&#x3089;&#x304F;&#x3089;&#x8FB2;&#x5834;&#x3078;&#x306E;&#x30EC;&#x30D3;&#x30E5;&#x30FC;&#x30FB;&#x5546;&#x54C1;&#xFF1A;&#x9577;&#x91CE;&#x770C;&#xFF5C;&#x98DF;&#x3079;&#x30C1;&#x30E7;&#x30AF;&#xFF5C;&#x7523;&#x5730;&#x76F4;&#x9001;(&#x7523;&#x76F4;)&#x304A;&#x53D6;&#x308A;&#x5BC4;&#x305B;&#x901A;&#x8CA9; - &#x8FB2;&#x5BB6;&#x30FB;&#x6F01;&#x5E2B;&#x304B;&#x3089;&#x65EC;&#x306E;&#x98DF;&#x6750;&#x3092;&#x76F4;&#x9001;</a></p> <p>是非毎日お越しください!ブースで待ってます。</p> <h3 id="長野の特産品を抽選で毎日5名の方にプレゼント">長野の特産品を抽選で毎日5名の方にプレゼント</h3> <p>その場ではお渡しが難しいけどこれだけは味わってほしいという長野の逸品をいずれか1品プレゼントします。 オンラインの方にもお届けできればと思い、Twitterで応募、その後当選された方に食べチョクを通じて生産者さんより直接発送します!</p> <p>一例を載せますが・・・ <figure class="figure-image figure-image-fotolife" title="トロリとろける信州サーモン"><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/v/vividgarden-tech/20230427/20230427121856.png" width="960" height="720" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span><figcaption>トロリとろける信州サーモン</figcaption></figure> <figure class="figure-image figure-image-fotolife" title="ナガノパープル&シャインマスカットの宝石箱"><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/v/vividgarden-tech/20230427/20230427121942.png" width="960" height="720" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span><figcaption>ナガノパープル&シャインマスカットの宝石箱</figcaption></figure> <figure class="figure-image figure-image-fotolife" title="信州白馬豚燻製詰め合わせ"><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/v/vividgarden-tech/20230427/20230427122021.png" width="960" height="720" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span><figcaption>信州白馬豚燻製詰め合わせ</figcaption></figure></p> <p><strong>欲しい。</strong></p> <p>こちらは3日間、毎日当たります!ぜひご応募いただければと思います。</p> <p>応募方法は「食べチョク開発部アカウントのフォロー」と「会期中に #rubykaigi と #食べチョク を付けてツイート」です。</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Ftwitter.com%2Ftabechoku_dev" title="食べチョク_開発部 (@tabechoku_dev) / Twitter" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://twitter.com/tabechoku_dev">twitter.com</a></cite></p> <p>RubyKaigiの感想、欲しい食材等、思い思いのつぶやきをお待ちしております。<br/> ※当選された方には食べチョク開発部アカウントよりDMをお送りさせていただきます<br/> ※対象者はRubyKaigiに参加している方とさせていただきます<br/> ※本当に当たります!届きます!</p> <p><blockquote data-conversation="none" class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">鯛、届きました〜!ありがとうございます✨<br><br>下処理済みなのが、ありがたい!<br>鯛めしとお刺身にしようかな〜😋<a href="https://twitter.com/hashtag/%E9%A3%9F%E3%81%B9%E3%83%81%E3%83%A7%E3%82%AF?src=hash&amp;ref_src=twsrc%5Etfw">#食べチョク</a> <a href="https://twitter.com/hashtag/rubykaigi?src=hash&amp;ref_src=twsrc%5Etfw">#rubykaigi</a> <a href="https://t.co/ObIIRI8wuP">https://t.co/ObIIRI8wuP</a> <a href="https://t.co/ZvARpSxYxq">pic.twitter.com/ZvARpSxYxq</a></p>&mdash; かわかみ (@kota_syan) <a href="https://twitter.com/kota_syan/status/1572903469635506176?ref_src=twsrc%5Etfw">2022年9月22日</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> </p> <p><blockquote data-conversation="none" class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">届きました!ありがとうございます!<a href="https://twitter.com/hashtag/rubykaigi?src=hash&amp;ref_src=twsrc%5Etfw">#rubykaigi</a> <a href="https://twitter.com/hashtag/%E9%A3%9F%E3%81%B9%E3%83%81%E3%83%A7%E3%82%AF?src=hash&amp;ref_src=twsrc%5Etfw">#食べチョク</a> <a href="https://t.co/aM3XrXyq2p">https://t.co/aM3XrXyq2p</a> <a href="https://t.co/kn43yKHrGM">pic.twitter.com/kn43yKHrGM</a></p>&mdash; えむ。 (@takkanm) <a href="https://twitter.com/takkanm/status/1571670775756914688?ref_src=twsrc%5Etfw">2022年9月19日</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> </p> <h1 id="おわりに">おわりに</h1> <p>RubyKaigiが終わった後も、長野の生産者の方々が作ったこだわり食材に思いを馳せていただけると幸甚です。<br/> 気になった方がいましたら是非こちらを覗いていってください。<br/> <a href="https://www.tabechoku.com/areas/nagano">https://www.tabechoku.com/areas/nagano</a></p> <p>ビビッドガーデンは「生産者のこだわりが正当に評価される世界へ」というビジョンのもと、一次産業の課題解決を推し進める会社です。<br/> 引き続きRubyをはじめとした技術を活用しながら、日本中の生産者の方々のこだわりがより多くの人に伝わるように、プロダクトを改善し続けようと思います。</p> <p>ビビッドガーデンのことが気になりましたらこちらも是非ご覧くださいませ!<br/> <a href="https://note.com/vividgarden_corp/">https://note.com/vividgarden_corp/</a></p> <p>それでは皆様、当日スポンサーブースもしくはTwitterにてお会いしましょう!</p> vividgarden-tech バナー作成をデザイナー以外でも作成する運用をはじめました hatenablog://entry/4207112889976179750 2023-03-31T11:50:00+09:00 2023-03-31T11:50:01+09:00 こんにちは。松久です。 キャンペーン、ニュースリリース、LINE など様々な場面で使われる画像は、デザイナーのみが制作していました。デザイナーだけで制作するには限界があり、作成の一部をデザイナー以外でできるようにする取り組みを行っているお話です。 画像を作成することが増えてきた いろいろな施策が実施される体制になり画像の作成数が増えてきました。 急ぎの画像作成も増加し、一ヶ月間で UI 以外のデザイナーの取り組みの半分が画像作成になっていることもあります。 種類、目的が複数種あり、以下のような画像を作成しています。 サイトトップのバナー ニュースリリースの画像 LINE のリッチメッセージ L… <p>こんにちは。松久です。</p> <p>キャンペーン、ニュースリリース、LINE など様々な場面で使われる画像は、デザイナーのみが制作していました。デザイナーだけで制作するには限界があり、作成の一部をデザイナー以外でできるようにする取り組みを行っているお話です。</p> <h2 id="画像を作成することが増えてきた">画像を作成することが増えてきた</h2> <p>いろいろな施策が実施される体制になり画像の作成数が増えてきました。 急ぎの画像作成も増加し、一ヶ月間で UI 以外のデザイナーの取り組みの半分が画像作成になっていることもあります。 種類、目的が複数種あり、以下のような画像を作成しています。</p> <ul> <li>サイトトップのバナー</li> <li>ニュースリリースの画像</li> <li>LINE のリッチメッセージ</li> <li>LINE のリッチメニュー</li> <li>各種広告</li> <li>メルマガ</li> </ul> <p>キャンペーンとなれば、これらの画像の作成が必要となります。 キャンペーンではなくても、普段の運用でも必要となる時があり、デザイナーでは作成しきれない量が発生している状態です。</p> <h2 id="発生した問題">発生した問題</h2> <p>デザイナーだけでは作成しきれないため、いくつかの問題が発生しました。</p> <ul> <li>開始したいタイミングに間に合わない</li> <li>制作期限がはっきりしているので他の施策よりも優先順位が上がり、プロダクトについての施策の優先順位が下がる</li> </ul> <p>そこで、マーケティング担当の方や、いくつかの部署の方に画像を作ってもらうことにしました。問題の一部は解決しましたが、新しい問題が出てきました。</p> <ul> <li>様々な人が様々なツールで作成しているため共通した色などの管理が困難</li> <li>いろいろな「食べチョクらしさ」が発生する</li> <li>デザイナーに画像制作に関する質問や、確認依頼がくるが、急ぎの話になる(確認しきれない・作ったほうが早い)</li> </ul> <p>これらの問題解決のため、デザイナーチームであらかじめ Figma でテンプレートを作り、テキストや画像の差し替えを各担当者にしてもらう体制に移行をはじめました。</p> <h2 id="Figma-でテンプレートを用意した">Figma でテンプレートを用意した</h2> <p>最初から、全種類の画像のテンプレートを作ることは難しいので、定型があるところから作り始めました。</p> <p>ツールは Figma にしました。</p> <ul> <li>担当者に edit 権限があったこと</li> <li>デザイナー側で共有している色のライブラリーなどがそのまま使えること</li> </ul> <p>今回は、Figma の edit 権限を持っている人がマーケティングチームにいたので、そこから今回は始めることにしました。 Figma から始めることで、デザイナーがすでに共有している色のライブラリーなども使うことができ、試すための敷居は低めでした。</p> <p>テンプレートを用意して、担当者に説明をして調整をして運用に進んでいます。</p> <p><figure class="figure-image figure-image-fotolife" title="Figmaで用意したテンプレート"><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/v/vividgarden-tech/20230330/20230330154035.jpg" width="1200" height="750" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span><figcaption>Figmaで用意したテンプレート</figcaption></figure></p> <h2 id="おわりに">おわりに</h2> <p>問題が 0 になったわけではなく、Figma について知ってもらう必要や、edit 権限を持つ人を増やす取り組みも必要そうです。運用していくなかで見つかる問題にも対応を続けていく予定です。</p> <p>今後もいろいろな取り組みで、個々のデザイナーの力をより発揮できる組織になり「食べチョク」の改善に向き合っていきます。</p> vividgarden-tech Figma の操作を方法学んでデザインデーターができるまでの過程を知る hatenablog://entry/4207112889966869323 2023-02-28T11:50:00+09:00 2023-02-28T11:50:00+09:00 こんにちは、松久です。 食べチョクでは Figma をデザインツールとして使っています。デザイナーが作った Figma のデザインデータを見てエンジニアはHTML/CSS を書いています。このとき、わかりにくいデザインデータを見かけるときがあります。解決の糸口をみつけるために、Figma の使い方を知ろうと取り組んでみた話です。 何から始めるかを決める 新しい技術を学ぶ時は、チュートリアルがあればチュートリアルから始めるのを基本にしています。 Figma のチュートリアルとして、公式のYouTube動画を今回は使いました。このYouTube動画だけでチュートリアルとしては十分な量があります。全… <p>こんにちは、松久です。</p> <p>食べチョクでは Figma をデザインツールとして使っています。デザイナーが作った Figma のデザインデータを見てエンジニアはHTML/CSS を書いています。このとき、わかりにくいデザインデータを見かけるときがあります。解決の糸口をみつけるために、Figma の使い方を知ろうと取り組んでみた話です。</p> <h2 id="何から始めるかを決める">何から始めるかを決める</h2> <p>新しい技術を学ぶ時は、チュートリアルがあればチュートリアルから始めるのを基本にしています。 Figma のチュートリアルとして、公式のYouTube動画を今回は使いました。このYouTube動画だけでチュートリアルとしては十分な量があります。全部はできませんでしたが、必要に応じて学ぶことができそうです。</p> <p><a href="https://www.youtube.com/playlist?list=PLXDU_eVOJTx6zk5MDarIs0asNoZqlRG23">Tutorials: Explore design features in Figma - YouTube</a></p> <p>私はここから始めて一通りの操作を学ぶことができました。</p> <iframe width="560" height="315" src="https://www.youtube.com/embed/dXQ7IHkTiMM" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe> <p><a href="https://www.youtube.com/watch?v=dXQ7IHkTiMM">www.youtube.com</a></p> <p>Figma のようなソフトウェアは、動画で操作しているところを見ることができるため理解がしやすいです。公式の動画なので、Figma が想定している使い方の基本を知ることができるのも安心です。 動画は、英語なのですが、YouTube には自動翻訳機能があるので英語が苦手でも、日本語訳を表示させながら学ぶことができました。</p> <p>残念なのは、全体像の説明がないので全体のどれぐらいを学んでいるのかはわかりません。学びたいことは、なんとなく思い浮かんでいたのですが、全体像の説明があると、後どれぐらい時間が必要なのか想定ができるのでありがたいです。</p> <h2 id="UI-のトレースをしてどうやってデザインデータを作るか試してみる">UI のトレースをしてどうやってデザインデータを作るか試してみる</h2> <p>操作方法について学んだあとに、いくつかのサイトの UI をトレースしてみました。 トレースの目的は、どんなことに気をつけて UI を作っているのか観察し、トレースすることで、デザインデータをどんなふうにすると良いのか感じ取ることを目的として行いました。</p> <p>行ってみて、Figma の AutoLayout、コンポーネント、Variants が、マークアップではどんなふうに置き換えられるのか想像しながらデータを作ってみることができたのは大きな体験でした。</p> <p>YouTube にあるチュートリアルでは、レイヤー名を常につけています。見ているときは「こまめに名前つけてるなあ」と見ていました。トレースをしてみると、名付けがあとになっている自分がいました。そして、あとからつけようとすると面倒くさい…。時々ある「Group 831」という数字のレイヤー名にとても納得しました。</p> <h2 id="実務のデザインデータを作る">実務のデザインデータを作る</h2> <p>トレースをしたあとに、実務でも UI のデザインデータを作ることになりました。自分が作ったデザインデータでエンジニアがマークアップをするのを見るのは新しい体験でした。</p> <p><figure class="figure-image figure-image-fotolife" title="実際のUIデザインデータの一部"><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/v/vividgarden-tech/20230227/20230227094302.jpg" width="1200" height="589" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span><figcaption>実際のUIデザインデータの一部</figcaption></figure></p> <p>作ってみると、話し合いからデザインデータを作る流れで実装まで一通りのデザイナーの体験をしてみて、見えていなかった作業もわかりました。 例えば、モブデザインの後は、データの整理をしないと使いにくいデータ(例えば、レイヤー名から表示個所がわかりにくい、テキストのサイズが複数混ざっているなど)ができること。margin/padding についてデザイナーが理解していないとデザインデータとして表現できないこと(例えば、レイヤー同士の距離で margin を表現することと AutoLayout で padding を表現すること)を感じました。</p> <h2 id="おわりに">おわりに</h2> <p>デザインデータはどうやってできるのか、実務で使ってみることで頭の中で知っていたこと以上のことを感じることができました。情報として知るのではなく、上手くいえないですが、実感のある情報となりました。</p> <p>今後もいろいろな取り組みで、個々のデザイナーの力をより発揮できる組織になり「食べチョク」の改善に向き合っていきます。</p> vividgarden-tech ペアデザインとモブデザインを行いデザインデータを作る hatenablog://entry/4207112889953654980 2023-01-16T19:00:00+09:00 2023-01-16T19:00:04+09:00 こんにちは。松久です。 デザインデータを作るとき、デザイナーが一人で作業をすることもありますが、複数のデザイナーや、デザイナー以外の職能の人と一緒に取り組む事も少しづつ増えています。 現在、どのようにデザインデータを作っているのかを紹介します。 デザインデータができるまで 食べチョクで、デザインデータができるまでの大まかな行程は下記の通りです。 施策の目的を整理する。資料を集める。 施策の目的にそったデザインの初稿を作る 関係者で見ながら作って確認する( モブデザイン・ペアデザイン ) デザイナー同士で確認・検討をする(デザインレビュー) 関係者で合意する( デザインが確定 ) 今回は 3〜4… <p>こんにちは。松久です。</p> <p>デザインデータを作るとき、デザイナーが一人で作業をすることもありますが、複数のデザイナーや、デザイナー以外の職能の人と一緒に取り組む事も少しづつ増えています。 現在、どのようにデザインデータを作っているのかを紹介します。</p> <h2 id="デザインデータができるまで">デザインデータができるまで</h2> <p>食べチョクで、デザインデータができるまでの大まかな行程は下記の通りです。</p> <ol> <li>施策の目的を整理する。資料を集める。</li> <li>施策の目的にそったデザインの初稿を作る</li> <li>関係者で見ながら作って確認する( モブデザイン・ペアデザイン )</li> <li>デザイナー同士で確認・検討をする(デザインレビュー)</li> <li>関係者で合意する( デザインが確定 )</li> </ol> <p>今回は 3〜4 の工程で取り組んでいることを紹介していきます。</p> <h2 id="UI-のためのデザインデータは認識合わせのためのドキュメント">UI のためのデザインデータは認識合わせのためのドキュメント</h2> <p>デザイナーが作っている「デザインデータ」の役割は、バナーなどのグラフィックとUIでは異なります。 バナーなどではデザインデータは完成品となりますが、UI を作るときのデザインデータは、プロダクトを作るための中間成果物で、意図を伝えるドキュメントです。 デザインデータを使い、プロダクトマネージャーと成果物(= プロダクト)について共通の認識を持つために使います。エンジニアとは、HTML や CSS などを作るための設計図のような役割( 1px を正確に表した図面ではなく意図を伝えるための図)で、認識を合わせるために用意しています。</p> <h2 id="職能を制限せず複数人でデザインをするモブデザイン">職能を制限せず複数人でデザインをするモブデザイン</h2> <p>デザインデータを作るとき、エンジニア、プロダクトマネージャなど複数人で Figma を見ながらデザインすることを少しづつ増やしています。</p> <p>GitHub の issue でデザインデータを依頼者に連絡しても意見交換で数日経ってしまうことがあります。複数人集まって見ながらデザインする方法であれば、何ができるのか伝わり、納得感を関係者で持つことができ、複数視点で確認できるようになります。 デメリットは、複数人の時間をいっぺんに使うのでコスト面とスケジュール調整が必要なことです。</p> <p>進めるコツは、デザイナーは話しながらデザインをして黙って作業をしないことです。話を聞いて受け身で作業だけをするのが目的ではないからです。 もう1つのコツは、1時間を目安に終わらせること。話を聞きつつ、Figma を操作して画面を作るのは頭と手を同時に動かすので疲労が大きいです。話している方も目の前でできていくので白熱しやすく疲れてきます。ファシリテーションと Figma の操作は別な人がする方のが良い可能性もありますが、デザイナーの理解としては話しながら Figma で作るのが良いと感じています。</p> <h2 id="デザイナー同士複数人でデザインする">デザイナー同士複数人でデザインする</h2> <p>デザイナーは毎日「Designer Team Discussion」というミーティングをしています。ここで取り組んでいるデザインについて相談したり、一緒に作ることがあります。 目的を達成するためのデザインになっているか、他の人へ説明する機会になっていることが、一番大きいメリットです。あと、他の人の Figma の操作方法を見ることができるのもいい点です。</p> <h2 id="デザインレビューをする">デザインレビューをする</h2> <p>UI については、「Designer Team Discussion」で確認して終わりになることも多いのですが、バナーなどはデザインレビューを必須にしています。 Slack に専用のチャンネルがあり、Workflow から依頼できます。デザイナー全員にメンションされますが、レビューはレビューワーを指定してレビューすることが多いです。</p> <p><figure class="figure-image figure-image-fotolife" title="Slackのワークフローを利用したデザインレビュー依頼"><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/v/vividgarden-tech/20230112/20230112230503.png" width="1016" height="1200" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span><figcaption>Slackのワークフローを利用したデザインレビュー依頼</figcaption></figure></p> <p>デザインレビューの本もありノウハウが増えてきていますが、デザイナー以外がするのはまだ未挑戦です。今はデザイナー2人、もしくは、3人以上ですることが多いです。</p> <p><div class="hatena-asin-detail"><a href="https://www.amazon.co.jp/dp/B01J2OEYLU?tag=hatena-22&amp;linkCode=ogi&amp;th=1&amp;psc=1" class="hatena-asin-detail-image-link" target="_blank" rel="noopener"><img src="https://m.media-amazon.com/images/I/51mwwYiQ6qL._SL500_.jpg" class="hatena-asin-detail-image" alt="みんなではじめるデザイン批評―目的達成のためのコラボレーション&amp;コミュニケーション改善ガイド" title="みんなではじめるデザイン批評―目的達成のためのコラボレーション&amp;コミュニケーション改善ガイド"></a><div class="hatena-asin-detail-info"><p class="hatena-asin-detail-title"><a href="https://www.amazon.co.jp/dp/B01J2OEYLU?tag=hatena-22&amp;linkCode=ogi&amp;th=1&amp;psc=1" target="_blank" rel="noopener">みんなではじめるデザイン批評―目的達成のためのコラボレーション&amp;コミュニケーション改善ガイド</a></p><ul class="hatena-asin-detail-meta"><li><span class="hatena-asin-detail-label">作者:</span><a href="http://d.hatena.ne.jp/keyword/%A5%A2%A1%BC%A5%ED%A5%F3%A1%A6%A5%A4%A5%EA%A5%B6%A5%EA%A1%BC%A1%CAAaron%20Irizarry%A1%CB" class="keyword">アーロン・イリザリー(Aaron Irizarry)</a>,<a href="http://d.hatena.ne.jp/keyword/%A5%A2%A5%C0%A5%E0%A1%A6%A5%B3%A5%CA%A1%BC%A1%CAAdam%20Connor%A1%CB" class="keyword">アダム・コナー(Adam Connor)</a></li><li>ビー・エヌ・エヌ新社</li></ul><a href="https://www.amazon.co.jp/dp/B01J2OEYLU?tag=hatena-22&amp;linkCode=ogi&amp;th=1&amp;psc=1" class="asin-detail-buy" target="_blank" rel="noopener">Amazon</a></div></div></p> <h2 id="おわりに">おわりに</h2> <p>デザイナーだけでデザインを行うのは難しいと感じています。 取り組む業務の知識、どんな表現が技術的に可能なのか、ビジネスとして取り組む事のバランスの落とし所を探すために、デザイナー以外を巻き込んでデザインに取り組むことが大切になってきていると感じています。</p> <p>今は、Figma などのツールを土台にデザインについてのコミュニケーションを促進させていくことで、プロダクトをより良い状態にリードすることを、デザイナーに求めている状況になってきました。 今後もこのような取り組みで、組織でデザインを行えるようになり「食べチョク」の改善に向き合っていきます。</p> vividgarden-tech Figma のファイル管理ルールを決めて業務を円滑にする取り組み hatenablog://entry/4207112889942640069 2022-12-07T17:50:00+09:00 2022-12-07T17:50:00+09:00 こんにちは。食べチョクの松久です。 今回は、複数人で Figma のファイルを管理するために工夫していること、悩んでいることも含めてどのようにしているかを紹介します。 ファイル管理をする目的 ファイルを管理する目的は、なんでしょうか。 複数人でデザインを行う体制のために、下記のことを実現するためだと捉えました。 過去の積み重ねによる一貫性(UIや、らしさ・ブランディング)を保つため デザイナーが過去のファイルを見つけやすくする デザイナー以外もデザインに関われる状態を作り、全員でプロダクトを作るため デザイナー以外もファイルを見られる状態を作る 上記の目的のために、今までの管理方法を踏まえなが… <p>こんにちは。食べチョクの松久です。</p> <p>今回は、複数人で Figma のファイルを管理するために工夫していること、悩んでいることも含めてどのようにしているかを紹介します。</p> <h2 id="ファイル管理をする目的">ファイル管理をする目的</h2> <p>ファイルを管理する目的は、なんでしょうか。 複数人でデザインを行う体制のために、下記のことを実現するためだと捉えました。</p> <ul> <li>過去の積み重ねによる一貫性(UIや、らしさ・ブランディング)を保つため <ul> <li>デザイナーが過去のファイルを見つけやすくする</li> </ul> </li> <li>デザイナー以外もデザインに関われる状態を作り、全員でプロダクトを作るため <ul> <li>デザイナー以外もファイルを見られる状態を作る</li> </ul> </li> </ul> <p>上記の目的のために、今までの管理方法を踏まえながら、ルールとして明確にしていきました。</p> <h2 id="プロジェクトを端末ごとに分ける">プロジェクトを端末ごとに分ける</h2> <p>食べチョクのデザイナーは、Web / iOS / Android といった端末でのサービスや、広告、キャンペーンなどのデザイン作成を担当しています。そのため、端末、媒体に合わせてプロジェクトを分けて管理することにしました。</p> <ul> <li>Library</li> <li>Web</li> <li>LP</li> <li>Mobile(iOS・Android)</li> </ul> <p>上記の様なプロジェクトを作っています(上記が全てではありません)。 プロジェクトには、About を書くことができるので、プロジェクトの役割とファイルの命名ルールを書いています。</p> <p>プロジェクトではなく、チームを複数に分けて管理する方法もありますが、今は1つのチームで管理しています。 理由は、例えば Web、Mobile でチームを分けると、「食べチョク」でウェブでもMobileでもキャンペーンを行う時にどこで管理するのがいいのか悩むからです。 もう1つの理由は、利用している Professional プランでは、作れるチームに限りがあり、細かく分けることができないためです。</p> <h2 id="ファイルの管理">ファイルの管理</h2> <p>ファイルの管理として、ファイル名に規則をもうけています。また、カバー画像についてもルールを設定しています。ルールを設けているのは、ファイルを一覧した時に見つけやすく、状態の把握がしやすくなる様にしたいためです。</p> <h3 id="プロダクトに関するファイル名のルール">プロダクトに関するファイル名のルール</h3> <p>ファイル名のルールは下記の通りです。</p> <pre class="code" data-lang="" data-unlink>状態の絵文字 + GitHub のリポジトリ名 + issue番号 + issue のタイトル</pre> <p>取り組むことは、GitHub の issue を使って管理しているので、issue の情報をファイル名に含めました。そのため、issue 番号で検索してファイルを見つけることができます(被る時もあるので、リポジトリ名も入れています)。</p> <p>状態の絵文字は以下としています。</p> <ul> <li>✍️ デザイン中</li> <li>🔧 実装中 <ul> <li>開発でエンジニアにファイルを渡して実装中の時</li> </ul> </li> <li>✅ 完了 <ul> <li>デザイン、実装も完了している状態。これ以上の変化が起きない時</li> </ul> </li> <li>⏸ ペンディング <ul> <li>施策を進めることが止まっている時</li> </ul> </li> <li>🚩 ピン留め <ul> <li>テンプレート用ファイル</li> </ul> </li> </ul> <p>ファイルをリスト表示すると作業中のファイルが固まって表示されるようになります。issue 番号がすぐに出てこない時も、絵文字で目視による絞り込みができる様になります(付けていないファイルがまだあったりしますが、少しづつルールを適用している途中です)。</p> <h3 id="LP-などのファイル名管理">LP などのファイル名管理</h3> <p>プロダクトに関するファイルは、命名管理を徹底していますが違う管理もあります。 キャンペーンや、サービス紹介のためのLPについては、サービス名やキャンペーン名をファイル名にしています。 理由はいくつかあります。</p> <ul> <li>サービス紹介のための LP は、Facebook 広告、LINE のリッチメッセージ、メルマガでの紹介画像などの複数のクリエイティブを作って成立する。</li> <li>複数のキャンペーンがあって1つのキャンペーンとなることもあり、前回のキャンペーンと比較することがある。 <ul> <li>この時、issue は分けたいが、Figma のファイル内では比較をしたい。</li> </ul> </li> </ul> <p>以上の理由から別ルールとなっています。</p> <h3 id="カバー画像の設定">カバー画像の設定</h3> <p>カバー画像はグリッドで見た時に、見つけやすくするために設定してます。 特に、キャンペーンや、サービス紹介の LP は、カバー画像をキービジュアルを設定しています。ファイル名でも見つかるのですが、見た目で覚えていることも多く、カバー画像を設定しています。</p> <p><figure class="figure-image figure-image-fotolife" title="Figma のカバー画像の例"><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/v/vividgarden-tech/20221205/20221205234112.png" width="1200" height="782" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span><figcaption>Figma のカバー画像の例</figcaption></figure></p> <h2 id="ファイルの中の管理">ファイルの中の管理</h2> <p>Pages を以下のとおりに分けるのを基本にしています。</p> <ul> <li>💻 実装用デザインマスター <ul> <li>デザインが確定した時に渡すデーターです</li> </ul> </li> <li>🎨 Flow <ul> <li>制作中。さらに分割されることもあります</li> </ul> </li> <li>📝 メモ <ul> <li>資料などを集める時に使います</li> </ul> </li> <li>🗂 Cover <ul> <li>カバー画像の設定をしています</li> </ul> </li> </ul> <p>上記を基本としますが、追加される Page もあります。多い Page の追加事例は、広告や、og:imageサイズの画像用の Page です。キャンペーンの告知のためのクリエイティブをまとめる Pages を作ることがあります。</p> <p>継続的なキャンペーンなのかは、最初わからないのもありますし、お歳暮・お中元のように毎年実施しているキャンペーンもあります。毎年実施であっても、違いがあります。前回との違いと共通点を気をつけることが多いので、今は1つのファイルに、クリエイティブごとの Page を作ることが多いです。</p> <h2 id="まだ悩んでいること">まだ悩んでいること</h2> <p>管理方法を決めたことで、ファイルを探しやすくなり、過去の施策も見つけやすくなってきました。以前より、状況はよくなりましたが、管理方法だけでは足りないことにも気づきました。</p> <h3 id="マスターデーターを運用すべきか">マスターデーターを運用すべきか</h3> <p>多くの施策が実施されると、マスターデーターへの反映が追いつかなくなってきました。また、知らず知らず変更されていることもあります。 マスターデーターはあった方がいいのですが、運用コストと対価が見合う方法を見つけられていない状況です。 施策でエンジニアが機能追加・変更をすることもあるので、デザイナーだけで解決できないのではないか、と感じています。</p> <h3 id="印刷データーの管理はどうするか">印刷データーの管理はどうするか</h3> <p>印刷データーは、Illustrator で作るので、Figma 上では管理していません。専用のルールが存在しています。 GitHub の issue で、印刷に関しても管理しているので、Figma で issue 番号を検索して「あ、違った」となることがあります。</p> <h2 id="おわりに">おわりに</h2> <p>書き出してみると、まだまだ問題があることに気づいてしまいました。 特にマスターデーターの運用問題は、頑張るしかなさそうな気持ちと頑張り切らないと意味がなさそうと想像しているところです。</p> <p>今後もいろいろな取り組みで、個々のデザイナーの力をより発揮できる組織になることで、「食べチョク」の改善に向き合っていきます。</p> vividgarden-tech テスト改善チーム活動の軌跡(総集編)とチームの進め方 hatenablog://entry/4207112889940537525 2022-11-30T12:30:00+09:00 2022-11-30T12:30:02+09:00 みなさま、お久しぶりです。QA エンジニアのujeです。 今回、テスト改善チームの活動が一段落したので総集編としてこれまでの取り組みを振り返ります。 改善チームの始まりはこちらの食べチョクの自動テスト改善活動 〜これまでとこれから〜をお読みください。 現在、チームは目標達成を契機に 10 月末で解散となり、メンバーは食べチョク開発業務に集中しています。 私を含めて 8 名で食べチョクの開発業務も並行だったので、1 人あたり週に数時間の作業時間でした。 そのため、少しずつの活動にはなりましたが多岐に渡って遂行できたと感じています。 ここではその成果一覧とその時のブログ記事をまとめました。 成果 … <p>みなさま、お久しぶりです。QA エンジニアの<a href="https://twitter.com/uje49538710">uje</a>です。 今回、テスト改善チームの活動が一段落したので総集編としてこれまでの取り組みを振り返ります。 改善チームの始まりはこちらの<a href="https://tech.tabechoku.com/entry/2022/05/06/155749">食べチョクの自動テスト改善活動 〜これまでとこれから〜</a>をお読みください。</p> <p>現在、チームは目標達成を契機に 10 月末で解散となり、メンバーは食べチョク開発業務に集中しています。</p> <p>私を含めて 8 名で食べチョクの開発業務も並行だったので、1 人あたり週に数時間の作業時間でした。 そのため、少しずつの活動にはなりましたが多岐に渡って遂行できたと感じています。</p> <p>ここではその成果一覧とその時のブログ記事をまとめました。</p> <h2 id="成果">成果</h2> <ul> <li>RSpec カバレッジ率 <ul> <li>カバレッジ率の集計が手動から自動に改善 <a href="https://tech.tabechoku.com/entry/2022/06/28/123000">RSpec 実行時のレポート情報をクエリで可視化する</a></li> <li>Controller のカバレッジ率が 67% から 81% に改善 <a href="https://tech.tabechoku.com/entry/2022/11/16/123000">テストのカバレッジをコツコツ上げた話</a></li> <li>全体のカバレッジ率が 74% から 80% に改善</li> </ul> </li> <li>テスト実行時間 <ul> <li>テスト実行時間の集計が手動から自動に改善</li> <li>テスト実行時間が平均 13 分 17 秒から平均 4 分 45 秒に改善 <a href="https://tech.tabechoku.com/entry/2022/07/07/123000">食べチョクの自動テスト実行速度を 2 倍以上速くした</a></li> </ul> </li> <li>未使用コード <ul> <li>現在使われていないコード精査、一部削除が行われた</li> </ul> </li> <li>ブログ <ul> <li>記事をチームで 5 本執筆(本記事を含む)</li> </ul> </li> </ul> <p>4 月〜10 月の半年間でしたが、形骸化せず目標を達成できたので上手くいったチームになったかなと感じています。 自分なりにですが次の項目では上手くいった要因について振り返って見たいと思います。</p> <h2 id="チームが上手くいったと考えている要因">チームが上手くいったと考えている要因</h2> <ul> <li><p>メンバーが改善活動に対して意欲的だったこと</p> <ul> <li>改善した方が良い箇所という意見出しを実施してから、テスト改善について重要だと感じているメンバーに集まってもらったので意欲が高かった</li> </ul> </li> <li><p>ミーティングが意味あるものだったこと</p> <ul> <li>ミーティングは「レビュー」→「レトロスペクティブ」→「プランニング」を短時間で進めて意思決定を重視した設計していた</li> <li>メンバーからもらった意見は抽象化して議題にしてやった方が良いものはプランニングで取り入れていった</li> </ul> </li> <li><p>目標の設定ができていたこと</p> <ul> <li>フワッとしたまま進めないことを前提に、まずは目標を数値を出す部分を対応した</li> <li>数値の取得が可能になったら最終目標を設定した</li> <li>目標に対して毎週どれくらいで達成できるか週次目標を算出していた</li> </ul> </li> </ul> <h2 id="筆者個人が特に重要視していた点">筆者個人が特に重要視していた点</h2> <p>「チームを動かすこと」「チームが意思決定しやすいように自身が動くこと」の 2 点です。</p> <p>冒頭でもお話ししたとおり改善チームは食べチョク開発と並行した改善チームだったのでみんな本来の開発業務があります。</p> <p>そのため、そちらが優先となりどんどん作業が後ろ倒しになって形骸化してしまう といった結果にならないよう注意していたことを箇条書きにしてみました</p> <ul> <li>重要視していたこと <ul> <li>ミーティングの準備、ファシリテーションなどを担当</li> <li>必ずネクストアクションを作る</li> <li>数値目標を作る</li> <li>締め切りを決める</li> <li>次ミーティングまでに出してほしいアウトプットや資料を提示して記載してほしい場所に誘導する</li> <li>1 施策にたいしてなるべく担当を 2 名アサインする</li> <li>問題が提起された場合や上手くいってないと感じたら早めの変更をする(ミーティングや進まない要因の解決を図る)</li> </ul> </li> </ul> <p>振り返ってみると筆者自身は毎週何かしら運営関連するアクションを取っていたように思います。 一番良くないのは、動かないことで忘れられてしまうことだと思っていたので、Slack で細かく発信していたように思います。</p> <h2 id="最後に">最後に</h2> <p>正直、かなりメンバーの意欲的な部分に助けられたと実感しています。一番早く業務が進むときって心のどこかに安心感や連帯感があることだと思ってます。 また私自身も次も同じようにいくとは限らないです。新しい改善チームを運営する場合は、手を変え品を変え試していく意識を大事にしていきます。</p> <p>改善が新しい改善を呼び、結果として開発やお客様にとって安定したサービスになるよう尽力していきます。</p> <p>最後になりますが食べチョクでは仲間を募集しています。 ご興味がある方は是非、<a href="https://vivid-garden.co.jp/#RECRUIT">RECRUIT</a>からご応募ください。</p> vividgarden-tech テストのカバレッジをコツコツ上げた話 hatenablog://entry/4207112889936397153 2022-11-16T12:30:00+09:00 2022-11-16T12:30:00+09:00 こんにちは、endo と kawabata です。 今回はプロダクトチーム内の自動テスト改善チームでコツコツカバレッジを上げた取り組みと振り返りのお話をしたいと思います。 ここでテストのカバレッジを上げているのは、RSpec でのお話になります。 テストのカバレッジを上げていこうというお話は、こちらの食べチョクの自動テスト改善活動 〜これまでとこれから〜のお話からきています。 ゴールを設定せずに活動するのは良くないので、10 月末までにカバレッジを 80%まで上げるということを目標に設定しました。 80%まで上がれば広範囲をカバーできているだろうというざっくりとした見立てです。 今回の取り組み… <p>こんにちは、endo と kawabata です。 今回はプロダクトチーム内の自動テスト改善チームでコツコツカバレッジを上げた取り組みと振り返りのお話をしたいと思います。 ここでテストのカバレッジを上げているのは、RSpec でのお話になります。 テストのカバレッジを上げていこうというお話は、こちらの<a href="https://tech.tabechoku.com/entry/2022/05/06/155749">食べチョクの自動テスト改善活動 〜これまでとこれから〜</a>のお話からきています。</p> <p>ゴールを設定せずに活動するのは良くないので、10 月末までにカバレッジを 80%まで上げるということを目標に設定しました。 80%まで上がれば広範囲をカバーできているだろうというざっくりとした見立てです。 今回の取り組みでは、大きく分けて以下の 3 点を実施しました。</p> <ul> <li>どのテストを追加するか決める</li> <li>コツコツテストを追加する</li> <li>不要なコードを削除する</li> </ul> <h2 id="どのテストを追加するか決める">どのテストを追加するか決める</h2> <p>テストのカバレッジを上げていこう!というのはいいのですが、闇雲にやっても改善幅が見えません。 そこで、改善幅を見つけるために<a href="https://tech.tabechoku.com/entry/2022/06/28/123000">RSpec 実行時のレポート情報をクエリで可視化する</a>で現状の状態を可視化しました。</p> <p>その結果、どこから着手するかが見えてきました。</p> <p><figure class="figure-image figure-image-fotolife" title="RSpecのグループ毎のカバレッジ"><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/v/vividgarden-tech/20221114/20221114092733.png" width="748" height="408" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span><figcaption>RSpecのグループ毎のカバレッジ</figcaption></figure></p> <ul> <li>Controller / Mailer / Jobs のカバレッジが全体よりも低い</li> <li>3 つのグループの中で Controller は実際のエンドユーザー側へのコンテンツの表示に関わる割合が多い</li> </ul> <p>ということで、Controller のカバレッジを改善しようという話になりました。</p> <p>では、どのファイルからやるのか?というところも闇雲にやっても改善が見えづらいです。 カバレッジが低いものから着手しようということで、カバレッジが低いものを抽出しました。 これでテストを追加する対象が明確になりました。</p> <p><figure class="figure-image figure-image-fotolife" title="Controllerのカバレッジが50%以下のもの"><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/v/vividgarden-tech/20221114/20221114092831.png" width="1200" height="414" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span><figcaption>Controllerのカバレッジが50%以下のファイル</figcaption></figure></p> <h2 id="コツコツテストを追加する">コツコツテストを追加する</h2> <p>目標達成までに対応が必要なファイル数を算出し、目標達成までの日にちから逆算して毎週各メンバーに割り当てました。 平均すると 1 人毎週 1 ファイル追加するペースで進めば目標達成となる計算です。 テストを追加する対象が決まりマイルストーンも設定できたので、あとはコツコツと追加していくのみです。</p> <p>しかし、ここからが大変でした。 最初のうちは順調に進んでいたのですが、途中からペースが鈍化し思うように進まない期間もありました。 各メンバーの業務状況により、テストを追加するための時間が確保できないというのが大きな要因でした。 これに関しては優先順位の兼ね合いで仕方ない部分があり、解決策がないので「先週出来なかったので今週は 2 つ追加しよう」みたいな感じで進めました。</p> <h2 id="不要なコードを削除する">不要なコードを削除する</h2> <p>テストの追加と並行して不要な機能やデッドコードを削除する活動も進めました。 使われていない処理のテストを追加しても無駄になりますし、テストがない不要はコードを削除すればカバレッジも上がるだろうという想定でした。 その結果、想定通りカバレッジ向上に効果的でした!</p> <p><figure class="figure-image figure-image-fotolife" title="不要コード削除後の50%未満Controller数"><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/v/vividgarden-tech/20221114/20221114093001.png" width="739" height="388" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span><figcaption>不要コード削除後の50%未満Controller数</figcaption></figure></p> <p><figure class="figure-image figure-image-fotolife" title="不要コード削除後のカバレッジ"><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/v/vividgarden-tech/20221114/20221114093026.png" width="415" height="279" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span><figcaption>不要コード削除後のカバレッジ</figcaption></figure></p> <h2 id="最終結果">最終結果</h2> <p>コツコツテストを追加したり不要なコードを削除した結果、全体カバレッジが 80%を超えました!</p> <p><figure class="figure-image figure-image-fotolife" title="最終全体カバレッジ"><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/v/vividgarden-tech/20221114/20221114093105.png" width="931" height="310" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span><figcaption>最終全体カバレッジ</figcaption></figure></p> <p>Controller 単体でも 80%を超えました!</p> <p><figure class="figure-image figure-image-fotolife" title="最終Controllerカバレッジ"><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/v/vividgarden-tech/20221114/20221114093207.png" width="923" height="295" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span><figcaption>最終Controllerカバレッジ</figcaption></figure></p> <p>目標も達成したので、自動テスト改善チームは解散となります。</p> <h2 id="振り返り">振り返り</h2> <p>今回の活動を振り返ってみて良かった点と課題が見えてきました。</p> <h3 id="良かったこと">良かったこと</h3> <h4 id="グラフによるカバレッジの可視化">グラフによるカバレッジの可視化</h4> <p>日々テストを追加してもカバレッジの増加はごくわずかです。 成果が見えづらいとモチベーション維持も難しくなります。 カバレッジをグラフ化していたことで日々右肩上がりに増えていくのを確認でき、 活動のモチベーション維持にも役立っていたと思います。</p> <h4 id="設定した目標を達成できた">設定した目標を達成できた</h4> <p>業務状況により進捗が少ない時期もありましたが、自然消滅せず目標を達成できたことも成果の 1 つだと思っています。 あまり進捗しない状況でも継続することの大切さを改めて実感しました。</p> <h3 id="課題">課題</h3> <h4 id="安定した作業時間の確保">安定した作業時間の確保</h4> <p>上でも書きましたが、各メンバーの業務状況によりテストを追加する時間を確保できないという状況が発生しました。 今回の取り組みだけのことではなく、こういった改善活動では起こり得ることです。 これに関しては良い解決方法が見つかっていませんが、 定期的にミーティングを開催して進捗状況の共有を行なっていたのは、良かったと思っています。 (個人的にミーティングのたび『頑張って進めないと!』と感じ、モチベーション維持に役立っていました)</p> <h2 id="おわりに">おわりに</h2> <p>80%を超えたカバレッジですが、日によっては 80%を下回っています。 日々システムが進化していてコードは増え続けていますが、それに対応したテストが不足している場合があるということですね。 カバレッジが高いから良いというものではありませんが、必要なテストが不足していることは問題です。 自動テスト改善チームは解散となりますが、プロダクトチーム全体としてテストを書くということを意識していきます。</p> <p><figure class="figure-image figure-image-fotolife" title="最終全体カバレッジ2"><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/v/vividgarden-tech/20221114/20221114093132.png" width="931" height="310" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span><figcaption>目標達成後のある日の全体カバレッジ</figcaption></figure></p> vividgarden-tech デザイナー同士で行う3つのミーティング設計 hatenablog://entry/4207112889931302172 2022-10-31T11:30:00+09:00 2022-10-31T11:30:00+09:00 こんにちは。食べチョクの松久です。 今回は、デザイナーの活動を支えるミーティングをどうやっているか紹介します。デザイナーが関係するミーティングは3つあり、それぞれのミーティングの目的を説明していきます。 プランニング・交通整理 月・水・金曜日に施策の整理をしています。1回 30分程度です。 デザイナーが行うことは、ユニットに所属する施策以外にも、マーケティングや広報など他の部署・組織と一緒に行う施策があります。そこで、施策の状況の確認や、新しい施策の話について確認する必要があります。 施策は全て、GitHub issue に記載され、GitHub Projects にある ボードで管理をしてい… <p>こんにちは。食べチョクの松久です。</p> <p>今回は、デザイナーの活動を支えるミーティングをどうやっているか紹介します。デザイナーが関係するミーティングは3つあり、それぞれのミーティングの目的を説明していきます。</p> <h2 id="プランニング交通整理">プランニング・交通整理</h2> <p>月・水・金曜日に施策の整理をしています。1回 30分程度です。</p> <p>デザイナーが行うことは、ユニットに所属する施策以外にも、マーケティングや広報など他の部署・組織と一緒に行う施策があります。そこで、施策の状況の確認や、新しい施策の話について確認する必要があります。</p> <p>施策は全て、GitHub issue に記載され、GitHub Projects にある ボードで管理をしています。ボードを見ながら、進捗の状況や新しい施策の対応を決めます。</p> <p><figure class="figure-image figure-image-fotolife" title="デザイナーのGitHub Projectsの様子"><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/v/vividgarden-tech/20221027/20221027185320.jpg" alt="" width="1200" height="540" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span><figcaption>デザイナーのGitHub Projectsの様子</figcaption></figure></p> <p>さまざまな施策があり、人数に限りがあるのでどうしてもアサイン(割り当て)をすることになっている問題があります。理想としては issue を読み、施策に自発的に取り組める状況が理想ですが、まだたどり着いていない、というのが現実です。</p> <h2 id="Designer-Team-Discussion">Designer Team Discussion</h2> <p>「Designer Team Discussion」という名前でカレンダーに登録していますが、堅苦しくはなく、ざっくばらんに話すための時間を、毎日1時間とっています。 話していることは下記のようなことです。</p> <ul> <li>取り組んでいる施策の UI について相談(雑なデザインレビュー)</li> <li>デザイン界隈の話題( Figma のアップデートとか )</li> <li>最近の困ったこと、良かったこと</li> <li>雑な話</li> </ul> <p>です。</p> <p>色々な施策があり、1ユニットに複数のデザイナーがいるのは少ない状況なので、一人で抱え込まないように「定期的になんでも話せる時間があること」を大切にしています。そのため、これといって議題は決めていません。決める時もあるのですが、決めたら決めたで議題について、話せない時もあります。ここは改善の余地があるところです。</p> <p>コミュニケーションの場でもあるので、デザインとは全然関係ない話になることもありますし、Figma などツールの話になることもあります。例えば下記のようなことを話しています。</p> <ul> <li>写真素材のダウンロード履歴が美味しそうな写真だらけで困る</li> <li>いわき市に良さそうなゲストハウスがあるので、ワーケーションにいいのでは?</li> <li>Figma でマスターデーターを管理し続けるのは現実的か?</li> </ul> <p>などです。</p> <p>このミーティングで話したことを「📝🍵🍡」という絵文字をつけて、Slack のデザイナーのパブリックなチャンネルにメモをしています。メモなのでツッコミは歓迎です。 ここで話したことから、業務の改善できそうなことを見つけて、取り組むこととして esa にまとめて、時々対応しています。具体的には、印刷物に関する資料をアップデートしたり、デザインシステムのアップデートをしたりしています。</p> <h2 id="Monthly-Update">Monthly Update</h2> <p>プロダクトチームと呼ばれる組織にデザイナーは所属しています。この組織では「Monthly Update」というミーティングが、月1回あります。この時に合わせて、この一ヶ月間で何を取り組んだか?確認しています。</p> <p>このMonthly Update実施前にデザイナーで何を取り組んだのか成果を確認をするミーティングをしています。何を取り組んだのか確認するのと一緒に下記のようなチェックリストを実施することにしました(議事録は esa で管理しています)。</p> <pre class="code" data-lang="" data-unlink>- [ ] Figma のアカウントの確認 - [ ] 📝🍵🍡 のリスト確認 - [ ] スタイルガイドの進捗確認</pre> <p>Figma のアカウント管理・確認は定期的に行う必要があるので月一のイベントは都合が良かったです。</p> <p>デザイナーの成果を確認するのですが、書き出してみるのは時間がかかります。時間はかかるのですが、自分たちで何をしたのか?どんな成果だっけ?を確認する時間は必要だと思っています。ただ、確認しきれないこともあり、改善が必要です。</p> <h2 id="おわりに">おわりに</h2> <p>ご紹介したミーティング以外に、1on1 や、プロダクトチームの定例ミーティング、全社でのふりかえりなどがあり、ミーティングは少なくありません。 ミーティングは、効率的に目的を持って行えるのが理想です。ただ、デザイナーが複数人いる状態になってまもないので、デザイナー間でのコミュニケーションを目的としたミーティングをしています。そのため、全体として少し多めの状態が続いています。</p> <p>今回、半年ぐらい運営した結果を書き出してみることで、変えたほうが良い点に気づくこともできました。</p> <p>今後もいろいろな取り組みで、個々のデザイナーの力をより発揮できる組織になることで、「食べチョク」の改善に向き合っていきます。</p> vividgarden-tech デザイナーへの依頼数を GitHub CLI と jq を使って集計する hatenablog://entry/4207112889918355568 2022-09-29T10:32:01+09:00 2022-09-29T10:32:01+09:00 こんにちは。食べチョクの松久です。 今回は、デザイナーへの依頼数を GitHub CLI と jq を使って集計している話を紹介します。 どうして、依頼数を計測することになったのか、どうやって GitHub CLI と jq を使って集計しているのか、を順番に説明していきます。 組織の中でのデザイナーの位置付け 最初に、「食べチョク」を作る組織の中でのデザイナーと、エンジニア、プロダクトマネージャーの関係は下記の図の通りです。 2022年9月現在の組織図 「食べチョク」を作る組織は、チームトポロジーを原則としています。 2022年9月現在、「プロダクトチーム」にミッションを持った「ユニット」が… <p>こんにちは。食べチョクの松久です。</p> <p>今回は、デザイナーへの依頼数を <a href="https://cli.github.com/">GitHub CLI</a> と jq を使って集計している話を紹介します。 どうして、依頼数を計測することになったのか、どうやって GitHub CLI と jq を使って集計しているのか、を順番に説明していきます。</p> <h2 id="組織の中でのデザイナーの位置付け">組織の中でのデザイナーの位置付け</h2> <p>最初に、「食べチョク」を作る組織の中でのデザイナーと、エンジニア、プロダクトマネージャーの関係は下記の図の通りです。</p> <p><figure class="figure-image figure-image-fotolife" title="2022年9月現在の組織図"><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/v/vividgarden-tech/20220928/20220928120903.jpg" width="1200" height="595" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span><figcaption>2022年9月現在の組織図</figcaption></figure></p> <p>「食べチョク」を作る組織は、チームトポロジーを原則としています。 2022年9月現在、「プロダクトチーム」にミッションを持った「ユニット」がいくつかあります。ユニットには、プロダクトマネージャー、エンジニア、デザイナーなどで構成されています。ただ、ユニットによっては、デザイナーがいないこともあります。詳しくは「<a href="https://tech.tabechoku.com/entry/2021/12/26/165044">食べチョクのプロダクトチームとチームトポロジー</a>」を参照してください。</p> <p>デザイナーは、ストリームアラインドチームに所属するのを基本にしています。デザイナー(職能)だけの組織はありません。 組織図には存在していませんが、デザイナー同士で集まって組織的に活動する場面も多くあります。理由は、「食べチョク」でのキャンペーンや、株式会社ビビッドガーデンの運営に必要なクリエイティブ作業も担当するため、組織的な対応が必要となるためです。</p> <p><figure class="figure-image figure-image-fotolife" title="キャンペーン、バナー、広告の事例"><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/v/vividgarden-tech/20220915/20220915190409.png" alt="" width="1200" height="597" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span><figcaption>キャンペーン、バナー、広告の事例</figcaption></figure></p> <p>具体的には以下の仕事があります。</p> <ul> <li>キャンペーンページのデザイン</li> <li>キャンペーンに関するバナー類の作成</li> <li>ディスプレイ広告などの作成</li> <li>広報に関する画像</li> <li>コーポレートサイトの運用</li> <li>Tシャツ</li> </ul> <p>などがあります。</p> <p>2021年末頃、デザイナーも、マーケティング、広報担当も今より少ない人数で、なんとか対応していた状態でした。 その後、デザイナーの人数は増え、マーケティング、広報担当の人数も増えました。人数が増えた分、取り組む「食べチョク」や、株式会社ビビッドガーデンの運営に必要な「クリエイティブ作業」も増えて、対応が追いつかなくなってきました。</p> <h2 id="バナー作成など依頼数が増えてきたがどれぐらい増えたのか計測したい">バナー作成など依頼数が増えてきたがどれぐらい増えたのか計測したい</h2> <p>本来であれば、依頼を受けて対応するよりも、一緒に目的のために活動できる状態( = ゴール)が望ましいのですが、まだその状態には達していないです。ゴールに向けて、今の状態を把握するための取り組みから始めました。</p> <p>最初の取り組みは、Slack などで行われていた依頼を全て GitHub issues で残すことにしました。 プロダクトチーム(ストリームドアラインチーム)はもちろんですが、マーケティング、広報なども issue に書き起こしてもらうことを徹底しました。issue には、部署ごとのラベルをつけて分類しています。 issue は複数のリポジトリで管理されていますが、デザイナーが関わるリポジトリは限られているので(いまは 3つぐらい)、そこはあまり負荷になっていません。</p> <h2 id="GitHub-CLI-と-jq-を使って集計する">GitHub CLI と jq を使って集計する</h2> <p>GitHub CLI ( gh )を使い、issue のデータを取得していきます。 issue のデータは、json 形式で取得できるので、jq コマンドで tsv 形式にしてダウンロードします。</p> <pre class="code :bash" data-lang=":bash" data-unlink>$ gh issue list -s all -L 1000 --search &#34;label:&#34;design:UI&#34;,&#34;design:ASAP&#34;,&#34;design:PS&#34;,&#34;design:PR&#34;,&#34;design:service&#34;,&#34;design:marketing&#34;,&#34;design:biz&#34;&#34; --json &#34;createdAt,closedAt,number,title,url,labels,state&#34;| jq -r &#39;.[]|[.createdAt, .closedAt, .number, .title, .url, .state, ([.labels[].name] | join(&#34;/&#34;))]|@tsv&#39; &gt; issue.tsv</pre> <p>gh コマンドには、色々なオプションがあるのですが、ラベルを複数指定することが難しかったので、search オプションでラベルを指定して検索しています。 検索結果を jq コマンドに渡して、tsv ファイルにして保存しています。 この後は、tsv ファイルを Google Sheets にいれて、集計します。</p> <h2 id="集計結果を活用する">集計結果を活用する</h2> <p>集計結果は、毎月、プロダクトチームで成果を共有する場で、プロダクトチーム外で起きたプロダクトのこととして数値共有を 発表して esa に残しています。 また、完了数や、時間の配分も他の集計結果と合わせてわかってきたこともあるので、業務の見直しに活用できました。</p> <h2 id="おわりに">おわりに</h2> <p>組織が大きくなり、デザイナーが色々な場面で求められるようになってきました。 求められるのは、嬉しいですが、残念ながら人数の限界もあります。どんなことが今できているのか数値で整理することで、他のチームに理解を求めたり、活動を可視化できました。</p> <p>今後もいろいろな取り組みで、個々のデザイナーの力をより発揮できる組織になって「食べチョク」の改善に向き合っていきます。</p> vividgarden-tech RubyKaigi 2022で"Mie Food Sponsor"をします。持ってけ楽しめ三重食材! hatenablog://entry/4207112889913357645 2022-08-31T09:46:27+09:00 2022-08-31T10:08:42+09:00 ビビッドガーデンは"Mie Food Sponsor"として三重のこだわり食材を携えてRubyKaigi 2022に協賛します。 こだわりをもってRubyで開発を続けるみなさんに、三重の生産者の方々のこだわりをたっぷりお届けします。 <p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/v/vividgarden-tech/20220831/20220831094242.png" width="1200" height="630" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span> こんにちは。プロダクト開発チームの Engineering Manager 兼 人事の <a href="https://twitter.com/hirashunshun">@hirashun</a> です。</p> <p>利用率No.1の産直通販サイト「食べチョク」を開発/運用するビビッドガーデンではリリース当初からバックエンドをすべてRubyで開発しており、ユーザーに迅速なサービス提供ができる恩恵を最大限にあずかっております。</p> <p>この度RubyKaigiが3年ぶりのオフライン開催@三重県ということで、Rubyコミュニティーに何か恩返しは出来ないことか・・・と考えた結果、<br /> <strong>ビビッドガーデンは"Mie Food Sponsor"として三重のこだわり食材を携えて協賛することにしました!</strong></p> <p>こだわりをもってRubyで開発を続けるみなさんに、三重の生産者の方々のこだわりをたっぷりお届けします!是非RubyKaigiで三重の魅力もお楽しみください。</p> <h3 id="三重のこだわりお届けします">三重のこだわりお届けします</h3> <p>オフライン参加の方だけでなく、オンライン参加の方にも楽しんでいただけるように、以下の2つのスポンサー内容を準備しました。</p> <ul> <li><strong>日替わりで三重の食材を会場にて配布</strong></li> <li><strong>三重の特産品を抽選で毎日5名の方にプレゼント</strong></li> </ul> <p>それぞれ説明していきます!</p> <h4 id="日替わりで三重の食材を会場にて配布">日替わりで三重の食材を会場にて配布</h4> <p>「せっかく三重に来たんやで、〇〇食べてみ!」と言いたいところですが、昨今の情勢もありなかなか手軽に食べ物を提供することが難しくなってしまいました。<br /> しかしそれでも生産者の方々のこだわりをRuby開発者に届けたい!そんな想いで「個包装」「その場で味わえる」「持ち帰り可能」という条件を満たす三重食材を厳選しました。</p> <p>会場にて配布します。こちらがその一部です!</p> <h5 id="疲れた脳に染み渡る無添加無加工の天然はちみつ">疲れた脳に染み渡る「無添加・無加工の天然はちみつ」</h5> <p><img width="480" alt="image.png (236.0 kB)" src="https://img.esa.io/uploads/production/attachments/13624/2022/08/29/63951/afb7ab14-6728-49bd-bb56-c5534d4f24c5.png"></p> <p>脳の活性化に効果があるブドウ糖・果糖・ビタミン類・ミネラルを多数配合し、消化吸収に手間がかからずダイレクトに体のエネルギー源になるはちみつが連日Ruby尽くしの皆様を強くサポートします。</p> <blockquote><p>採れたままのはちみつに一切加工を施さず瓶詰めすることで、風味や栄養価をそこなわず、はちみつ本来の魅力を楽しんでいただけます。</p> <p>ぜひ一度、無添加・無加工の天然はちみつを味わってみてください。</p></blockquote> <p><a href="https://www.tabechoku.com/producers/20508">舘養蜂場本店</a></p> <h5 id="こだわり抜いた味と香りでリラックス希少かぶせ茶">こだわり抜いた味と香りでリラックス「希少かぶせ茶」</h5> <p><img width="480" alt="image.png (185.3 kB)" src="https://img.esa.io/uploads/production/attachments/13624/2022/08/29/63951/040107e7-4c94-4ffe-85cc-ef30ed4195a8.png"></p> <p>かぶせ茶生産量日本一の三重県。旨味・甘味成分が増加し、香りも良く深緑色でとても鮮やかな希少なかぶせ茶が一日の疲れを癒やすリラックスタイムを演出します。</p> <blockquote><p>新芽の本数を意図的に減らし、新芽1本当たりの栄養分が豊富になっています。 お茶の摘み取り2週間前に黒い覆いを被せていて、旨味・甘味が増加し、まろやかな味わいになります。手間はかかりますが、おいしいお茶作りのためには欠かせない作業です。</p> <p>新芽のすべての部位(茎、芽、粉)が入った本物のお茶(荒茶)をお届けします。</p></blockquote> <p><a href="https://www.tabechoku.com/producers/21321">かぶせ茶ファーム</a></p> <p>私も欲しいです。<br /> 全員分は準備出来ませんでしたが、そこそこの数を準備しています。ぜひ楽しみにしていてください!</p> <p>詳細の配布方法は追って<a href="https://twitter.com/tabechoku_dev">Twitterの「食べチョク開発部」アカウント</a>にてご案内します。</p> <h4 id="三重県の特産品を抽選で毎日5名の方にプレゼント">三重県の特産品を抽選で毎日5名の方にプレゼント</h4> <p>その場ではお渡しが難しいけどこれだけは味わってほしいという三重の逸品をいずれか1品プレゼントします。<br /> オンラインの方にもお届けできればと思い、今回はTwitterで応募、その後当選された方に食べチョクを通じて生産者さんより直接発送します!</p> <p>一例を載せますが・・・ <figure class="figure-image figure-image-fotolife" title="三重県南伊勢町のブランド真鯛『あなたに逢い鯛。』"><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/v/vividgarden-tech/20220831/20220831093103.png" width="480" height="360" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span><figcaption>三重県南伊勢町のブランド真鯛『あなたに逢い鯛。』</figcaption></figure> <figure class="figure-image figure-image-fotolife" title="肉の芸術・松阪牛"><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/v/vividgarden-tech/20220831/20220831093210.png" width="480" height="360" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span><figcaption>肉の芸術・松阪牛</figcaption></figure></p> <p>おわかりいただけただろうか。私も欲しいです。</p> <p>3日間、毎日5名の方に当たります!ぜひご応募いただければと思います。<br /> 応募方法は「食べチョク開発部アカウントのフォロー」と「会期中に #rubykaigi と #食べチョク を付けてツイート」です。</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Ftwitter.com%2Ftabechoku_dev" title="食べチョク_開発部 (@tabechoku_dev) / Twitter" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://twitter.com/tabechoku_dev">twitter.com</a></cite></p> <p>RubyKaigiの感想、欲しい食材等、思い思いのつぶやきをお待ちしております。<br /> ※当選された方には食べチョク開発部アカウントよりDMをお送りさせていただきます<br /> ※対象者はRubyKaigiに参加している方とさせていただきます</p> <h3 id="CTO西尾より">CTO西尾より</h3> <p>先日CTOに就任した <a href="https://twitter.com/nishio_dens">@nishio_dens</a> のコメントです。</p> <blockquote><p>このたび「RubyKaigi 2022」にフードスポンサーとして協賛できることを、大変嬉しく思っております。</p> <p>ビビッドガーデン(食べチョク)は、サービス開始当初からRubyを用いて開発を続けてきました。Rubyは私たちのようなスタートアップには非常に相性が良い言語です。<br /> 素早くサービスを立ち上げられること、直感的にコードを記述できること、開発に必要なライブラリやツールがひと通り揃っていることが魅力的な言語です。</p> <p>私自身、エンジニアとしてのキャリアの多くをRubyとともに過ごしてきました。<br /> Rubyは他のプログラミング言語に比べて自由度が高い言語です。この自由さが魅力であり、コードを書いていて楽しい気持ちになれる言語だと思っています。<br /> 複数のプログラミング言語を使ってこれまで開発をしてきましたが、私はRubyが一番好きな言語であると自信を持って言えます。</p> <p>長い間お世話になっているRubyとRubyコミュニティーに貢献できることは感慨深いものがあります。<br /> 今回の「RubyKaigi 2022」はオフライン開催も予定されており、三重県の生産者のこだわり食材を会場にて配布いたします。</p> <p>皆様とお会いできることを楽しみにしております。</p></blockquote> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fprtimes.jp%2Fmain%2Fhtml%2Frd%2Fp%2F000000189.000025043.html" title="産直通販サイト「食べチョク」が「RubyKaigi 2022」にMie Food Sponserとして協賛。来場者にはちみつやお茶など三重のこだわり食材を提供。" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;" loading="lazy"></iframe><cite class="hatena-citation"><a href="https://prtimes.jp/main/html/rd/p/000000189.000025043.html">prtimes.jp</a></cite></p> <h1 id="おわりに">おわりに</h1> <p>今まで様々な都市での開催を重ねるたびに、RubyKaigi運営の方々の「その都市ごと楽しんでいけよ」というメッセージを感じずにはいられませんでした。<br /> この度、参加される皆様に三重のこだわりが詰まった食材を楽しんでいただくと共に、このような形でRubyKaigiのお手伝いができることを嬉しく思います。</p> <p>RubyKaigiが終わった後も、三重の生産者の方々が作ったこだわり食材に思いを馳せていただけると幸甚です。<br /> 気になった方がいましたら是非三重の食材を覗いていってください。<br /> <a href="https://www.tabechoku.com/areas/mie">https://www.tabechoku.com/areas/mie</a></p> <p>ビビッドガーデンは「生産者のこだわりが正当に評価される世界へ」というビジョンのもと、一次産業の課題解決を推し進める会社です。<br /> 引き続きRubyをはじめとした技術を活用しながら、日本中の生産者の方々のこだわりがより多くの人に伝わるように、プロダクトを改善し続けようと思います。</p> <p>ビビッドガーデンのことが気になりましたらこちらも是非ご覧くださいませ!<br /> <a href="https://note.com/vividgarden_corp/">https://note.com/vividgarden_corp/</a></p> <p>それでは皆様、当日スポンサーブースもしくはTwitterにてお会いしましょう!</p> vividgarden-tech 食べチョクの自動テスト実行速度を2倍以上速くした hatenablog://entry/4207112889896534897 2022-07-07T12:30:00+09:00 2022-07-07T12:30:04+09:00 皆さんこんにちは、エンジニアの久保と金田一です。 今回は、食べチョクの自動テスト改善チームが取り組んでいるテスト実行速度の改善についてお話しいたします。 自動テスト改善チームとは何か?について知りたい方は、以下のエントリーをご覧ください。 食べチョクの自動テスト改善活動 〜これまでとこれから〜 食べチョクにおける自動テストの現状 食べチョクでは、GitHub Actions のワークフローを使って、push をトリガーに自動テストを実行しています。食べチョクのリポジトリには System Spec までを含んでいて、E2E は別リポジトリとなっています。 メンバーが増えて体制が整ってきたことも… <p>皆さんこんにちは、エンジニアの<a href="https://twitter.com/yuskubo">久保</a>と<a href="https://twitter.com/kindai_dai">金田一</a>です。</p> <p>今回は、食べチョクの自動テスト改善チームが取り組んでいるテスト実行速度の改善についてお話しいたします。</p> <p>自動テスト改善チームとは何か?について知りたい方は、以下のエントリーをご覧ください。</p> <ul> <li><a href="https://tech.tabechoku.com/entry/2022/05/06/155749">食べチョクの自動テスト改善活動 〜これまでとこれから〜</a></li> </ul> <h2>食べチョクにおける自動テストの現状</h2> <p>食べチョクでは、GitHub Actions のワークフローを使って、push をトリガーに自動テストを実行しています。食べチョクのリポジトリには System Spec までを含んでいて、E2E は別リポジトリとなっています。</p> <p>メンバーが増えて体制が整ってきたこともあり、この 1 年くらいに実装した機能についてはテストがしっかり書かれていることが多いです。一方で、それ以上前に作られた機能については、まだまだカバレッジが低い箇所もあり、みんなで手分けして少しずつテストを追加しています。</p> <p>不足していたテストの追加だけではなく、新しい機能の開発でもテストは増えていきます。これらのテスト追加に伴い出てきた課題がテスト実行時間の増加です。徐々にテストの実行時間が伸び、結果として CI 全体の時間が伸びてしまい、開発者体験が悪くなりつつあると感じ始めていました。具体的には、push してから merge するまで時間がかかる、push 後しばらく別の作業を実施してからテストの失敗を検知して前の作業に戻るので非効率、などの意見がありました。</p> <p>このままでは開発効率が悪くなる一方なので、自動テストの速度改善をしよう!ということになりました。</p> <h2>CI 上でのテスト実行時間の計測と観察</h2> <p>単にテストが遅いと言っても、どこに問題があるかを明確にしないとどういった改善が有効か判断することができません。まずはボトルネックを突き止めるために、CI 上でのテスト実行時間の計測と観察をしました。</p> <p>テスト実行時間をランダムで 20 回分集計してみたところ、最短で 11 分 44 秒、最長で 16 分 3 秒、平均で 13 分 17 秒かかっていました。</p> <p><figure class="figure-image figure-image-fotolife" title="改善前のRSpec実行時間"><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/v/vividgarden-tech/20220706/20220706104419.png" alt="rspec_execution_time_before" width="1030" height="441" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span><figcaption>改善前のRSpec実行時間</figcaption></figure></p> <p>この全体の実行時間を「テスト自体の実行時間」と「ファイルの読み込み時間」に分けて確認したところ、「ファイルの読み込み時間」が全体の 30% 前後を占めていることがわかりました。 例えば、全体の実行時間が約 14 分だった場合では、「テスト自体の実行時間」が約 9 分 36 秒、「ファイルの読み込み時間」が 4 分 30 秒といった具合です。</p> <pre class="code bash" data-lang="bash" data-unlink>Finished in 9 minutes 36 seconds (files took 4 minutes 29.8 seconds to load) 4846 examples, 0 failures, 15 pending</pre> <p>次に「テスト自体の実行時間」をさらに分析するために、実行が遅いテスト TOP30 を抽出してみました。</p> <pre class="code bash" data-lang="bash" data-unlink>$ bundle exec rspec --profile 30</pre> <p>その結果、1 秒以上かかる example が 6 つあり、最も遅いテストでは 8.4 秒かかっていました。この 6 つテストだけで約 25 秒かかっており、改善の余地がありそうでした。</p> <p>ただし、6 つのうち 4 つは System Spec で、この 4 つについては時間がかかるのも仕方がない部分もあります。</p> <p>また、他のテストの実行時間は 1 秒未満だったので、全体の実行時間からすると 1 つ 1 つのテストを少しずつ改善しても大幅な改善は見込めませんでした。</p> <p>ここまでの分析から次のことがわかりました。</p> <ul> <li>改善には「テストの実行時間の改善」と「ファイルの読み込み時間の改善」の 2 つの軸がありそう</li> <li>1 つ 1 つのテストの改善は、最初に取り組むべき改善策ではなさそう</li> </ul> <h2>速くするための改善策</h2> <p>実行時間の計測と観察の結果から、「テストの実行時間の改善」と「ファイルの読み込み時間の改善」の 2 つの軸が見えてきました。</p> <p>まず「テストの実行時間の改善」については、テストを並列実行するという案が出ました。他社事例などを調べた結果、食べチョクでも並列実行することで大幅な時間短縮が見込めそうだったため、チャレンジしてみることにしました。</p> <p>次に「ファイルの読み込み時間の改善」についてです。これは意図せず解決に至ったのですが、Ruby 3.0 系へのバージョンアップが有効でした。Ruby 2.7.2 から 3.0.4 にバージョンアップした結果、ファイルの読み込み時間が約 4 分 30 秒から約 60 秒まで短縮されました。</p> <p>以上のことから、テスト実行速度の改善策としては「テストの並列実行」に取り組むことになりました。</p> <h2>テストの並列実行</h2> <p>テストの並列化は、主に以下のような実装をしました。</p> <ul> <li>フロントエンドのテスト(Jest)とバックエンドのテスト(RSpec)の実行の分離</li> <li>RSpec の実行を 10 個に並列化</li> </ul> <p>Jest と RSpec の実行の分離は、ジョブを分けて並列実行にしただけなので、ここでは RSpec の実行を 10 個に並列化した実装について解説していきます。 以下は、RSpec 実行のジョブと、並列化をするためのファイル分割スクリプトです。</p> <pre class="code lang-yaml" data-lang="yaml" data-unlink> <span class="synIdentifier">backend</span><span class="synSpecial">:</span> <span class="synIdentifier">runs-on</span><span class="synSpecial">:</span> ubuntu-latest <span class="synIdentifier">needs</span><span class="synSpecial">:</span> <span class="synStatement">- </span>backend-prepare <span class="synIdentifier">timeout-minutes</span><span class="synSpecial">:</span> <span class="synConstant">10</span> <span class="synIdentifier">strategy</span><span class="synSpecial">:</span> <span class="synIdentifier">fail-fast</span><span class="synSpecial">:</span> <span class="synConstant">false</span> <span class="synIdentifier">matrix</span><span class="synSpecial">:</span> <span class="synIdentifier">NODE_INDEX</span><span class="synSpecial">:</span> <span class="synSpecial">[</span><span class="synConstant">0</span>, <span class="synConstant">1</span>, <span class="synConstant">2</span>, <span class="synConstant">3</span>, <span class="synConstant">4</span>, <span class="synConstant">5</span>, <span class="synConstant">6</span>, <span class="synConstant">7</span>, <span class="synConstant">8</span>, <span class="synConstant">9</span><span class="synSpecial">]</span> <span class="synIdentifier">env</span><span class="synSpecial">:</span> <span class="synIdentifier">NUMBER_OF_NODES</span><span class="synSpecial">:</span> <span class="synConstant">10</span> <span class="synIdentifier">steps</span><span class="synSpecial">:</span> ...buildステップ... <span class="synStatement">- </span><span class="synIdentifier">name</span><span class="synSpecial">:</span> Exec RSpec <span class="synIdentifier">run</span><span class="synSpecial">:</span> | SPEC_FILES=$(./bin/rspec_splitter.rb --glob <span class="synConstant">'spec/**/*_spec.rb'</span> --node-count <span class="synConstant">&quot;${NUMBER_OF_NODES}&quot;</span> --node-index <span class="synConstant">&quot;${MATRIX_NODE_INDEX}&quot;</span>) bundle exec rspec --format progress --format RSpec::Github::Formatter --format RspecJunitFormatter --out <span class="synConstant">&quot;tmp/junit-rspec-${MATRIX_NODE_INDEX}.xml&quot;</span> ${SPEC_FILES} <span class="synIdentifier">env</span><span class="synSpecial">:</span> <span class="synIdentifier">MATRIX_NODE_INDEX</span><span class="synSpecial">:</span> ${{ matrix.NODE_INDEX }} ...その他のenv... <span class="synStatement">- </span><span class="synIdentifier">name</span><span class="synSpecial">:</span> Rename coverage result ...カバレッジを送信するステップ... </pre> <p>rspec_splitter.rb</p> <pre class="code lang-ruby" data-lang="ruby" data-unlink><span class="synPreProc">#!/usr/bin/env ruby</span> <span class="synPreProc">require</span> <span class="synSpecial">&quot;</span><span class="synConstant">optparse</span><span class="synSpecial">&quot;</span> options = <span class="synIdentifier">ARGV</span>.getopts(<span class="synSpecial">&quot;&quot;</span>, *<span class="synSpecial">%w[</span><span class="synConstant">glob: node-index: node-count:</span><span class="synSpecial">]</span>) glob_pattern = options[<span class="synSpecial">&quot;</span><span class="synConstant">glob</span><span class="synSpecial">&quot;</span>] <span class="synIdentifier">$node_count</span> = options[<span class="synSpecial">&quot;</span><span class="synConstant">node-count</span><span class="synSpecial">&quot;</span>].to_i node_index = options[<span class="synSpecial">&quot;</span><span class="synConstant">node-index</span><span class="synSpecial">&quot;</span>].to_i <span class="synType">SpecFile</span> = <span class="synType">Struct</span>.new(<span class="synConstant">:path</span>, <span class="synConstant">:index</span>, <span class="synConstant">keyword_init</span>: <span class="synConstant">true</span>) <span class="synStatement">do</span> <span class="synPreProc">def</span> <span class="synIdentifier">group</span> index % <span class="synIdentifier">$node_count</span> <span class="synPreProc">end</span> <span class="synStatement">end</span> files = <span class="synType">Dir</span>[glob_pattern] .sort_by { |<span class="synIdentifier">path</span>| [<span class="synType">File</span>.size(path), path] } .reverse_each .with_index .map { |<span class="synIdentifier">path</span>, <span class="synIdentifier">index</span>| <span class="synType">SpecFile</span>.new(<span class="synConstant">path</span>: path, <span class="synConstant">index</span>: index) } puts files.select { |<span class="synIdentifier">f</span>| f.group == node_index }.map(&amp;<span class="synConstant">:path</span>) </pre> <p>まず、GitHub Actions 上で、ジョブの並列実行をするためには、<a href="https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstrategy">Matrix Strategy</a>を使用します。例えば、複数の OS や言語などのバージョンの組み合わせに対する Job を記述することが出来るとても便利な機能です。</p> <p>ここでは、NODE_INDEX という変数にそれぞれ 0 から 9 までの値を保持しておきます。NODE_INDEX のみ定義しているので、ここで 10 個のジョブが生成されることになります。 <a href="https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstrategyfail-fast">fail-fast</a>を false にしておくと、ジョブが fail しても他のジョブの処理が継続します。</p> <p>次に、RSpec の分割実行です。テストファイルを分割し、各ジョブで実行するようにしました。テストファイルのサイズを取得し、並列にした時の実行時間が平準化するようにしています。</p> <p>具体的には、テストファイルのサイズ比較で降順にした配列に対して、ファイルパスと配列のインデックスを持ったオブジェクトを作成します。<code>配列のインデックス % node_count</code> と NODE_INDEX を比較し、一致した NODE_INDEX のジョブ番号で実行するという実装にしています。</p> <h2>結果</h2> <p>テストの並列実行を実現した後に、再度テストの実行時間をランダムで 20 回分集計してみたところ、最短で 3 分 8 秒、最長で 7 分 46 秒、平均で 4 分 45 秒という結果になりました。 改善前の時点では、最短が 11 分 44 秒、最長が 16 分 3 秒、平均が 13 分 17 秒だったので、以前の半分以下の時間でテストを実行できるようになりました。</p> <p><figure class="figure-image figure-image-fotolife" title="改善後のRSpec実行時間"><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/v/vividgarden-tech/20220706/20220706104719.png" alt="rspec_execution_time_after" width="1030" height="441" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span><figcaption>改善後のRSpec実行時間</figcaption></figure></p> <h2>さらなる改善に向けて</h2> <p>テストの並列実行によりテストの実行速度は向上し、一旦は開発者体験も良くなりました。</p> <p>しかしながら、プロダクトは日々成長し、機能もテストも追加されていきます。また、既存のテストに改善の余地があることも認識しています。</p> <p>よって、テスト実行速度の改善についてはこれで終わりではなく、今後は以下のような地道な改善にも取り組んでいくつもりです。</p> <ul> <li>並列実行のチューニング <ul> <li>今回並列実行は実現したが、並列数の調整や実行時間単位で分割するなどで効率を up できる可能性がある</li> </ul> </li> <li>テストの重複排除 <ul> <li>どのレイヤーで何をテストするかなどを整理して、無駄に複数のレイヤーで同じテストをしないようにする</li> <li>ロジックのテストを Request Spec のような高コストなレイヤーでやらない(必要な場合を除く)</li> </ul> </li> <li>作成するテストデータを削減する <ul> <li>DB アクセスが関係ない部分のテストでは、FactoryBot で <code>create</code> せずに <code>build</code> や <code>build_stubbed</code>を使う</li> <li>テストに関係ないデータが<code>after(:create)</code>でたくさん作られがちなので、作らないようにする</li> <li>1 example につき 1 expect で書くとわかりやすいが、DB レコードが必要なテストは毎回レコード作成することになるので、RSpec の aggregate failures を活用して example を統合する</li> </ul> </li> </ul> <h2>最後に</h2> <p>食べチョクでは仲間を募集しています。 ご興味がある方は是非、<a href="https://vivid-garden.co.jp/#RECRUIT">RECRUIT</a>からご応募ください。</p> vividgarden-tech RSpec実行時のレポート情報をクエリで可視化する hatenablog://entry/4207112889893605265 2022-06-28T12:30:00+09:00 2022-06-28T12:30:03+09:00 どうもはじめまして。 muryoimpl です。 前回のエントリ 食べチョクの自動テスト改善活動 〜これまでとこれから〜 で、自動テスト改善チームが発足したことを書きましたが、今回はその活動の中で実施した、RSpec による自動テストのカバレッジのデータ収集の自動化と、そのデータを利用した可視化について書きたいと思います。 これまではどう可視化していたか 食べチョクは Ruby on Rails で動いており、バックエンドの自動テストは RSpec を使って書いています。 テストカバレッジは定番の SimpleCov で計測して結果を HTML に出力し、テストケースごとの実行情報は RSpe… <p>どうもはじめまして。 muryoimpl です。</p> <p>前回のエントリ <a href="https://tech.tabechoku.com/entry/2022/05/06/155749">食べチョクの自動テスト改善活動 〜これまでとこれから〜</a> で、自動テスト改善チームが発足したことを書きましたが、今回はその活動の中で実施した、<a href="https://relishapp.com/rspec/">RSpec</a> による自動テストのカバレッジのデータ収集の自動化と、そのデータを利用した可視化について書きたいと思います。</p> <h2>これまではどう可視化していたか</h2> <p>食べチョクは Ruby on Rails で動いており、バックエンドの自動テストは <a href="https://relishapp.com/rspec/">RSpec</a> を使って書いています。</p> <p>テストカバレッジは定番の <a href="https://github.com/simplecov-ruby/simplecov">SimpleCov</a> で計測して結果を HTML に出力し、テストケースごとの実行情報は <a href="https://github.com/sj26/rspec_junit_formatter">RSpec JUnit Formatter</a> を使って XML として出力して、GitHub Actions でそれらの情報を <a href="https://codeclimate.com/">Code Climate</a> に送信していました。</p> <p>また、可視化という点では、以前<a href="https://qiita.com/muryoimpl/items/206e31039778aada873e#%E3%82%84%E3%81%A3%E3%81%A6%E3%81%BF%E3%81%9F%E5%8A%B9%E6%9E%9C">ビビッドガーデン Advent Calendar 2021 で私が書いたエントリ</a>の画像のように、テストカバレッジを Google スプレッドシートに記録してグラフ化し、変化が見れるようにしていました。</p> <p>これまでも現状のテストカバレッジは確認できてはいたのですが、自動テスト改善チームが発足したときのミーティングで以下のような意見が出て、改善することにしました。</p> <ul> <li>テストカバレッジをクエリで見えるようにしたい <ul> <li>クエリを書いてデータを取り出せるようにしておけばいろいろな分析ができる</li> <li>テストの足りていない箇所の洗い出しが誰でもできる</li> </ul> </li> <li>手作業で記録するのは飽きた <ul> <li>属人性や反映タイミングもまちまち</li> <li>自動化していつでも誰でも確認できるようにしたい</li> </ul> </li> </ul> <h2>Redash で可視化できるようにデータを取り込めるようにする</h2> <p>食べチョクでは <a href="https://redash.io/">Redash</a> を既に利用しているので、ここにデータを投入さえできれば後はクエリを書いてグラフ化するのも組み合わせてダッシュボード化するのも自由にできます。ではデータをどう準備するかということになるのですが、以下の戦略を採用することにしました。</p> <ol> <li>GitHub Actions でテストが実行されたら、<a href="https://github.com/simplecov-ruby/simplecov">SimpleCov</a> で算出したテストカバレッジの情報をCSV化し、<a href="https://cloud.google.com/bigquery?hl=ja">BigQuery</a> に送信する</li> <li>GitHub Actions でテストが実行されたら、<a href="https://github.com/sj26/rspec_junit_formatter">RSpec JUnit Formatter</a> で出力した XML からテストの実行時間情報を取り出してCSV化し、これを <a href="https://cloud.google.com/bigquery?hl=ja">BigQuery</a> に送信する</li> <li><a href="https://cloud.google.com/bigquery?hl=ja">BigQuery</a> のデータを <a href="https://redash.io/">Redash</a> と連携する</li> </ol> <p>3 については、<a href="https://cloud.google.com/bigquery?hl=ja">BigQuery</a> をデータソースとして <a href="https://redash.io/">Redash</a> で扱う仕組みが既にあるので、1 と 2 の仕組みを用意すればよいことになります。</p> <p>1 と 2 についても、元となる情報は既にあるので、必要な情報を読み出して加工し、CSV にするだけです。</p> <h3>1. SimpleCov が収集したテストカバレッジを CSV 化する</h3> <p><a href="https://github.com/simplecov-ruby/simplecov">SimpleCov</a> には収集したカバレッジ情報を出力する <a href="https://github.com/simplecov-ruby/simplecov#using-your-own-formatter">Formatter</a> の仕組みが用意されています。この Formatter を独自に用意して CSV を出力します。</p> <p>今回はテスト関連ということで <code>spec/support</code> 以下に <code>simpelcov_bq_formatter.rb</code> というファイル名で Formatter を配置しています。</p> <p>format メソッドが <a href="https://github.com/simplecov-ruby/simplecov">SimpleCov</a> から受け取る引数の result には、全体、ファイル毎、グループ毎にまとめたテストカバレッジの情報が詰まっています。これを利用して CSV を作ります。今回は全体のテストカバレッジ情報と、各ファイル毎のテストカバレッジ情報をそれぞれ出すことにしたので、全体のほうのファイル名は "All Files" という名前にしてレコードを作っています。</p> <pre class="code lang-ruby" data-lang="ruby" data-unlink><span class="synPreProc">require</span> <span class="synSpecial">&quot;</span><span class="synConstant">csv</span><span class="synSpecial">&quot;</span> <span class="synPreProc">module</span> <span class="synType">SimpleCov</span> <span class="synPreProc">module</span> <span class="synType">Formatter</span> <span class="synPreProc">class</span> <span class="synType">BqFormatter</span> <span class="synType">ALL_FILES_NAME</span> = <span class="synSpecial">&quot;</span><span class="synConstant">All Files</span><span class="synSpecial">&quot;</span>.freeze <span class="synPreProc">def</span> <span class="synIdentifier">format</span>(result) job_id = <span class="synIdentifier">ENV</span>[<span class="synSpecial">&quot;</span><span class="synConstant">JOB_ID</span><span class="synSpecial">&quot;</span>] created_at = bq_datetime_format(result.created_at) <span class="synType">CSV</span>.open(<span class="synType">File</span>.join(output_path, <span class="synSpecial">&quot;</span><span class="synConstant">coverages_for_bq.csv</span><span class="synSpecial">&quot;</span>), <span class="synSpecial">&quot;</span><span class="synConstant">wb</span><span class="synSpecial">&quot;</span>) <span class="synStatement">do</span> |<span class="synIdentifier">csv</span>| <span class="synComment"># 形式 (covered% は (lines covered / relevant lines * 100).round(2) )</span> <span class="synComment"># job_id,branch name,created_at,group_name,filename,covered%,lines,relevant lines,lines covered,lines missed</span> <span class="synComment"># 全体の結果 は一行として出力する</span> csv &lt;&lt; [ job_id, branch_name, created_at, <span class="synSpecial">&quot;</span><span class="synConstant">All Files</span><span class="synSpecial">&quot;</span>, <span class="synSpecial">&quot;</span><span class="synConstant">All Files</span><span class="synSpecial">&quot;</span>, percentage(result.source_files.covered_percent), result.source_files.never_lines, result.source_files.lines_of_code, result.source_files.covered_lines, result.source_files.missed_lines, ] <span class="synComment"># ファイル毎の情報を一行として出力する</span> result.groups.each <span class="synStatement">do</span> |<span class="synIdentifier">group_name</span>, <span class="synIdentifier">source_files</span>| source_files.each <span class="synStatement">do</span> |<span class="synIdentifier">f</span>| csv &lt;&lt; [ job_id, branch_name, created_at, group_name, short_filename(f.filename), percentage(f.covered_percent), f.lines.size, f.lines_of_code, f.covered_lines.size, f.missed_lines.size, ] <span class="synStatement">end</span> <span class="synStatement">end</span> <span class="synStatement">end</span> <span class="synPreProc">end</span> <span class="synPreProc">def</span> <span class="synIdentifier">output_path</span> <span class="synType">SimpleCov</span>.coverage_path <span class="synPreProc">end</span> <span class="synStatement">private</span> <span class="synComment"># full path を app/**/*.rb 形式にする</span> <span class="synPreProc">def</span> <span class="synIdentifier">short_filename</span>(filename) filename.sub(<span class="synType">SimpleCov</span>.root, <span class="synSpecial">&quot;</span><span class="synConstant">.</span><span class="synSpecial">&quot;</span>).gsub(<span class="synSpecial">%r{^\.</span><span class="synConstant">/</span><span class="synSpecial">}</span>, <span class="synSpecial">&quot;&quot;</span>) <span class="synPreProc">end</span> <span class="synPreProc">def</span> <span class="synIdentifier">bq_datetime_format</span>(datetime) datetime.strftime(<span class="synSpecial">&quot;</span><span class="synConstant">%Y-%m-%d %H:%M:%S</span><span class="synSpecial">&quot;</span>) <span class="synPreProc">end</span> <span class="synPreProc">def</span> <span class="synIdentifier">percentage</span>(percent) percent.round(<span class="synConstant">2</span>) <span class="synPreProc">end</span> <span class="synPreProc">def</span> <span class="synIdentifier">branch_name</span> <span class="synIdentifier">ENV</span>[<span class="synSpecial">&quot;</span><span class="synConstant">GITHUB_REF</span><span class="synSpecial">&quot;</span>]&amp;.sub(<span class="synSpecial">%r{</span><span class="synConstant">refs/</span><span class="synSpecial">.+</span><span class="synConstant">/</span><span class="synSpecial">}</span>, <span class="synSpecial">&quot;&quot;</span>) <span class="synPreProc">end</span> <span class="synPreProc">end</span> <span class="synPreProc">end</span> <span class="synPreProc">end</span> </pre> <p><code>JOB_ID</code> という環境変数は、<a href="https://docs.github.com/ja/actions/learn-github-actions/contexts#github-context">GitHub Actions の github コンテキストの情報</a>で構成されています。どの PR と紐づくのか判別できるように <code>github.run_id</code>、<code>github.run_number</code> を使っていますが、GitHub Actions の Job を再実行しても これらは変更されないので、<code>github.run_attempt</code> を加えて一意性を出しています。</p> <p><a href="https://relishapp.com/rspec/">RSpec</a> の実行は並列で行っているので、扱いやすいようにカバレッジの結果を 1 つにマージします。GitHub Actions の step として、<a href="https://github.com/simplecov-ruby/simplecov">SimpleCov</a> の結果をマージするための設定を書いた Ruby のファイルを実行して集約しています。このときに、先ほど登場した環境変数 <code>JOB_ID</code> を設定して渡します。</p> <pre class="code lang-yaml" data-lang="yaml" data-unlink><span class="synIdentifier">jobs</span><span class="synSpecial">:</span> ... <span class="synIdentifier">report</span><span class="synSpecial">:</span> ... <span class="synIdentifier">steps</span><span class="synSpecial">:</span> ... <span class="synStatement">- </span><span class="synIdentifier">name</span><span class="synSpecial">:</span> Merge coverage files <span class="synIdentifier">env</span><span class="synSpecial">:</span> <span class="synIdentifier">RAILS_ENV</span><span class="synSpecial">:</span> test <span class="synIdentifier">JOB_ID</span><span class="synSpecial">:</span> ${{ github.run_id }}-${{ github.run_number }}-${{ github.run_attempt }} <span class="synIdentifier">run</span><span class="synSpecial">:</span> bundle exec ./bin/merge_simplecov_results.rb ... </pre> <p>bin/merge_simplecov_results.rb では出力先と複数の Formatter を指定しています。</p> <pre class="code lang-ruby" data-lang="ruby" data-unlink><span class="synPreProc">#!/usr/bin/env ruby</span> <span class="synPreProc">require</span> <span class="synSpecial">&quot;</span><span class="synConstant">simplecov</span><span class="synSpecial">&quot;</span> <span class="synPreProc">require</span> <span class="synSpecial">&quot;</span><span class="synConstant">simplecov-html</span><span class="synSpecial">&quot;</span> <span class="synPreProc">require</span> <span class="synSpecial">&quot;</span><span class="synConstant">simplecov_json_formatter</span><span class="synSpecial">&quot;</span> <span class="synPreProc">require_relative</span> <span class="synSpecial">&quot;</span><span class="synConstant">../spec/support/simplecov_bq_formatter</span><span class="synSpecial">&quot;</span> <span class="synType">SimpleCov</span>.collate <span class="synType">Dir</span>[<span class="synSpecial">&quot;</span><span class="synConstant">coverage/.result-*.json</span><span class="synSpecial">&quot;</span>], <span class="synSpecial">&quot;</span><span class="synConstant">rails</span><span class="synSpecial">&quot;</span> <span class="synStatement">do</span> formatter <span class="synType">SimpleCov</span>::<span class="synType">Formatter</span>::<span class="synType">MultiFormatter</span>.new([ <span class="synType">SimpleCov</span>::<span class="synType">Formatter</span>::<span class="synType">HTMLFormatter</span>, <span class="synType">SimpleCov</span>::<span class="synType">Formatter</span>::<span class="synType">JSONFormatter</span>, <span class="synType">SimpleCov</span>::<span class="synType">Formatter</span>::<span class="synType">BqFormatter</span>, ]) add_group <span class="synSpecial">&quot;</span><span class="synConstant">Forms</span><span class="synSpecial">&quot;</span>, <span class="synSpecial">&quot;</span><span class="synConstant">app/forms</span><span class="synSpecial">&quot;</span> add_group <span class="synSpecial">&quot;</span><span class="synConstant">Services</span><span class="synSpecial">&quot;</span>, <span class="synSpecial">&quot;</span><span class="synConstant">app/services</span><span class="synSpecial">&quot;</span> add_group <span class="synSpecial">&quot;</span><span class="synConstant">Decorators</span><span class="synSpecial">&quot;</span>, <span class="synSpecial">&quot;</span><span class="synConstant">app/decorators</span><span class="synSpecial">&quot;</span> <span class="synStatement">end</span> </pre> <p>これで <a href="https://github.com/simplecov-ruby/simplecov">SimpleCov</a> の収集した情報を CSV として出力するようになりました。</p> <h3>2. RSpec JUnit Formatter で出力した XML から CSV を作成する</h3> <p><a href="https://github.com/sj26/rspec_junit_formatter">RSpec JUnit Formatter</a> を導入して、<a href="https://relishapp.com/rspec/">RSpec</a> を実行するコマンドに <code>--out</code> をつけてテストケース毎の結果を XML として出力しています。これを <a href="https://github.com/simplecov-ruby/simplecov">SimpleCov</a> のときと同様に、GitHub Actions 上で読み込んで CSV に加工します。</p> <p>先ほど出力した <a href="https://github.com/simplecov-ruby/simplecov">SimpleCov</a> の CSV (coverages_for_bq.csv) を使うので、テストカバレッジの CSV を作成した後の step で実行するようにしています。</p> <pre class="code lang-yaml" data-lang="yaml" data-unlink> <span class="synStatement">- </span><span class="synIdentifier">name</span><span class="synSpecial">:</span> Merge coverage files <span class="synIdentifier">env</span><span class="synSpecial">:</span> <span class="synIdentifier">RAILS_ENV</span><span class="synSpecial">:</span> test <span class="synIdentifier">JOB_ID</span><span class="synSpecial">:</span> ${{ github.run_id }}-${{ github.run_number }}-${{ github.run_attempt }} <span class="synIdentifier">run</span><span class="synSpecial">:</span> bundle exec ./bin/merge_simplecov_results.rb <span class="synStatement">- </span><span class="synIdentifier">name</span><span class="synSpecial">:</span> Create CSV file for BQ <span class="synIdentifier">run</span><span class="synSpecial">:</span> | bundle exec ./bin/export_spec_execution_time.rb <span class="synIdentifier">env</span><span class="synSpecial">:</span> <span class="synIdentifier">JOB_ID</span><span class="synSpecial">:</span> ${{ github.run_id }}-${{ github.run_number }}-${{ github.run_attempt }} </pre> <p>XML の path を <a href="https://docs.ruby-lang.org/ja/latest/method/Dir/s/=5b=5d.html">Dir.glob</a> で取得して <a href="https://github.com/sparklemotion/nokogiri">Nokogiri</a> で読み込み、内容を CSV として出力しています。</p> <p>以下の処理では、XML に含まれる spec ファイル名から対応する app のファイル名を算出するために coverages_for_bq.csv を読み込んだり変換したりしていますが、単純に結果を CSV に出力するためならば不要でしょう。実際、変換処理が長くなったので、ここでは省略しています。</p> <pre class="code lang-ruby" data-lang="ruby" data-unlink><span class="synPreProc">#!/usr/bin/env ruby</span> <span class="synPreProc">require</span> <span class="synSpecial">&quot;</span><span class="synConstant">csv</span><span class="synSpecial">&quot;</span> <span class="synPreProc">require</span> <span class="synSpecial">&quot;</span><span class="synConstant">nokogiri</span><span class="synSpecial">&quot;</span> <span class="synPreProc">require</span> <span class="synSpecial">&quot;</span><span class="synConstant">simplecov</span><span class="synSpecial">&quot;</span> <span class="synPreProc">require_relative</span> <span class="synSpecial">&quot;</span><span class="synConstant">../spec/support/simplecov_bq_formatter</span><span class="synSpecial">&quot;</span> <span class="synPreProc">class</span> <span class="synType">SpecExecutionTimeExporter</span> <span class="synType">NODE_REGEXP</span> = <span class="synSpecial">/\A</span><span class="synConstant">junit-rspec-</span><span class="synSpecial">(\d+)\.</span><span class="synConstant">xml</span><span class="synSpecial">\z/</span> <span class="synPreProc">def</span> <span class="synIdentifier">initialize</span> <span class="synIdentifier">@xmls</span> = <span class="synType">Dir</span>.glob(xml_glob_path).map <span class="synStatement">do</span> |<span class="synIdentifier">xml_path</span>| <span class="synType">NODE_REGEXP</span> =~ <span class="synType">File</span>.basename(xml_path) node_number = <span class="synType">Regexp</span>.last_match(<span class="synConstant">1</span>) [node_number, <span class="synType">Nokogiri</span>::Slop(<span class="synType">File</span>.read(xml_path))] <span class="synStatement">end</span> <span class="synIdentifier">@csv</span> = <span class="synType">CSV</span>.read(csv_path) <span class="synPreProc">end</span> <span class="synPreProc">def</span> <span class="synIdentifier">to_csv</span> <span class="synType">CSV</span>.open(<span class="synType">File</span>.join(output_path, <span class="synSpecial">&quot;</span><span class="synConstant">spec_execution_time_for_bq.csv</span><span class="synSpecial">&quot;</span>), <span class="synSpecial">&quot;</span><span class="synConstant">wb</span><span class="synSpecial">&quot;</span>) <span class="synStatement">do</span> |<span class="synIdentifier">csv</span>| <span class="synIdentifier">@xmls</span>.each <span class="synStatement">do</span> |<span class="synIdentifier">node_number</span>, <span class="synIdentifier">xml</span>| suite = xml.testsuite suite.testcase.each <span class="synStatement">do</span> |<span class="synIdentifier">c</span>| app_fname = app_filename(c[<span class="synSpecial">&quot;</span><span class="synConstant">file</span><span class="synSpecial">&quot;</span>]) <span class="synComment"># appファイル名</span> csv &lt;&lt; [ job_id, app_fname, <span class="synComment"># appファイル名</span> node_number, <span class="synComment"># node 番号</span> filename_dict.key?(app_fname), <span class="synComment"># 該当appファイルの有無</span> c[<span class="synSpecial">&quot;</span><span class="synConstant">name</span><span class="synSpecial">&quot;</span>], <span class="synComment"># テストケース名</span> c[<span class="synSpecial">&quot;</span><span class="synConstant">time</span><span class="synSpecial">&quot;</span>], <span class="synComment"># 実行時間</span> ] <span class="synStatement">end</span> <span class="synStatement">end</span> <span class="synStatement">end</span> <span class="synPreProc">end</span> <span class="synPreProc">def</span> <span class="synIdentifier">filename_dict</span> <span class="synIdentifier">@_filename_dict</span> ||= <span class="synIdentifier">@csv</span>.each_with_object({}) <span class="synStatement">do</span> |<span class="synIdentifier">row</span>, <span class="synIdentifier">acc</span>| app_filename = row[<span class="synConstant">4</span>] <span class="synComment"># filename</span> acc[app_filename] = <span class="synConstant">0.0</span> <span class="synStatement">end</span> <span class="synPreProc">end</span> <span class="synPreProc">def</span> <span class="synIdentifier">job_id</span> <span class="synIdentifier">@_job_id</span> = <span class="synIdentifier">ENV</span>.fetch(<span class="synSpecial">&quot;</span><span class="synConstant">JOB_ID</span><span class="synSpecial">&quot;</span>, <span class="synConstant">nil</span>) <span class="synPreProc">end</span> <span class="synPreProc">def</span> <span class="synIdentifier">xml_glob_path</span> <span class="synType">File</span>.join(<span class="synType">SimpleCov</span>.coverage_path, <span class="synSpecial">&quot;</span><span class="synConstant">..</span><span class="synSpecial">&quot;</span>, <span class="synSpecial">&quot;</span><span class="synConstant">tmp</span><span class="synSpecial">&quot;</span>, <span class="synSpecial">&quot;</span><span class="synConstant">junit-rspec-*.xml</span><span class="synSpecial">&quot;</span>) <span class="synPreProc">end</span> <span class="synPreProc">def</span> <span class="synIdentifier">csv_path</span> <span class="synType">File</span>.join(<span class="synType">SimpleCov</span>.coverage_path, <span class="synSpecial">&quot;</span><span class="synConstant">coverages_for_bq.csv</span><span class="synSpecial">&quot;</span>) <span class="synPreProc">end</span> <span class="synPreProc">def</span> <span class="synIdentifier">output_path</span> <span class="synType">SimpleCov</span>.coverage_path <span class="synPreProc">end</span> <span class="synStatement">private</span> <span class="synComment"># system spec の app ファイルへの変換は諦める。</span> <span class="synComment"># それ以外は 対応する app のファイルへの変換を試みる。</span> <span class="synPreProc">def</span> <span class="synIdentifier">app_filename</span>(spec_filename) <span class="synStatement">return</span> spec_filename.sub(<span class="synSpecial">%r{^\.</span><span class="synConstant">/spec</span><span class="synSpecial">}</span>, <span class="synSpecial">&quot;</span><span class="synConstant">spec</span><span class="synSpecial">&quot;</span>) <span class="synStatement">if</span> spec_filename.start_with?(<span class="synSpecial">&quot;</span><span class="synConstant">./spec/system</span><span class="synSpecial">&quot;</span>) name = spec_filename.sub(<span class="synSpecial">%r{^\.</span><span class="synConstant">/spec/</span><span class="synSpecial">}</span>, <span class="synSpecial">&quot;</span><span class="synConstant">app/</span><span class="synSpecial">&quot;</span>).sub(<span class="synSpecial">/</span><span class="synConstant">_spec</span><span class="synSpecial">\.</span><span class="synConstant">rb</span><span class="synSpecial">$/</span>, <span class="synSpecial">&quot;</span><span class="synConstant">\.rb</span><span class="synSpecial">&quot;</span>) -- ファイル名変換が長いため、 中略 -- name <span class="synPreProc">end</span> <span class="synPreProc">end</span> exporter = <span class="synType">SpecExecutionTimeExporter</span>.new exporter.to_csv </pre> <h3>3. 出力した CSV を BigQuery に送信する</h3> <p>CSV ファイルを作成したので、後は <a href="https://cloud.google.com/bigquery?hl=ja">BigQuery</a> のテーブルにロードするだけです。 <a href="https://github.com/google-github-actions/auth">google-github-actions/auth</a> と、<a href="https://github.com/google-github-actions/setup-gcloud">google-github-actions/setup-gcloud</a> を使って、認証し、bq コマンドで CSV をロードします。</p> <pre class="code lang-yaml" data-lang="yaml" data-unlink><span class="synIdentifier">steps</span><span class="synSpecial">:</span> ... <span class="synStatement">- </span><span class="synIdentifier">name</span><span class="synSpecial">:</span> Setup Google Cloud Auth <span class="synIdentifier">if</span><span class="synSpecial">:</span> ${{ success() <span class="synType">&amp;&amp;</span> github.ref_name == <span class="synConstant">'develop'</span> }} <span class="synIdentifier">uses</span><span class="synSpecial">:</span> google-github-actions/auth@v0 <span class="synIdentifier">with</span><span class="synSpecial">:</span> <span class="synIdentifier">credentials_json</span><span class="synSpecial">:</span> <span class="synConstant">'${{ secrets.GOOGLE_CREDENTIALS }}'</span> <span class="synStatement">- </span><span class="synIdentifier">name</span><span class="synSpecial">:</span> Setup Google Cloud SDK <span class="synIdentifier">if</span><span class="synSpecial">:</span> ${{ success() <span class="synType">&amp;&amp;</span> github.ref_name == <span class="synConstant">'develop'</span> }} <span class="synIdentifier">uses</span><span class="synSpecial">:</span> google-github-actions/setup-gcloud@v0 <span class="synStatement">- </span><span class="synIdentifier">name</span><span class="synSpecial">:</span> Publish Spec Report to BQ <span class="synIdentifier">if</span><span class="synSpecial">:</span> ${{ success() <span class="synType">&amp;&amp;</span> github.ref_name == <span class="synConstant">'develop'</span> }}<span class="synComment"> # developブランチのみ記録する</span> <span class="synIdentifier">run</span><span class="synSpecial">:</span> | bq load --source_format CSV tabechoku_dev_tools.tabechoku_rspec_coverages ./coverage/coverages_for_bq.csv bq load --source_format CSV tabechoku_dev_tools.tabechoku_rspec_execution_times ./coverage/spec_execution_time_for_bq.csv </pre> <p>全てのテスト実行の結果を取り込んでいるとさすがに煩雑になるので、default branch にマージされたときのみロードするようにしています。</p> <h2>Redash でクエリを作ってほしい情報を見られるようにする</h2> <p>データを取り込んでしまえば、後は SQL を書いてグラフ化するだけです。各種グラフを Dashboard 化していつでも結果を確認できるようにしています。</p> <p>食べチョクでは、テストの実行時間、グループ毎のテストカバレッジに加え、テストカバレッジがしきい値より低い app ファイルの一覧を出力して、テスト追加対象のファイルが見えるようにしています。これにより、テスト追加対象のファイルと、テスト追加による成果の認知が容易になりました。</p> <p><figure class="figure-image figure-image-fotolife" title="今回のデータを使った Redash の Dashboard"><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/v/vividgarden-tech/20220627/20220627101954.png" alt="&#x4ECA;&#x56DE;&#x306E;&#x30C7;&#x30FC;&#x30BF;&#x3092;&#x4F7F;&#x3063;&#x305F; Redash &#x306E; Dashboard" width="1200" height="591" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span><figcaption>今回のデータを使った Redash の Dashboard</figcaption></figure></p> <p>既存のデータをクエリで取捨選択、加工を可能にしたことで、単純なテストカバレッジや実行時間だけでなく、目的に沿った情報を自由に取得・表示することができ、有効活用できるようになりました。</p> <p>今後は対応の成果差分がわかりやすくなるようにグラフを改善したり、通知を活用することでモチベーションが上がるかたちでテスト改善を進めていければと考えています。</p> <h2>まとめ</h2> <p>既存のテストカバレッジの情報とケース毎の実行情報を GitHub Actions 上で加工して CSV 化し、<a href="https://cloud.google.com/bigquery?hl=ja">BigQuery</a> にロードすることで、<a href="https://redash.io/">Redash</a> と連携して現状を可視化、分析できるようにしました。</p> <p>元々ある仕組みと情報を利用してデータの形式を変換してクエリを書けるようにしたことで、要求に合った情報を取り出すことができるようになり、可視化も容易になりました。このデータを活用しつつ、テストカバレッジ向上のモチベーションが上がるかたちでテスト改善活動を進めていければと考えています。</p> <h2>最後に</h2> <p>食べチョクを運営しているビビッドガーデンでは、一緒に働く仲間を募集しています。ご興味のある方はこちらの <a href="https://vivid-garden.co.jp/#RECRUIT">RECRUIT</a> からどうぞ。</p> vividgarden-tech 食べチョクの自動テスト改善活動 〜これまでとこれから〜 hatenablog://entry/13574176438089755197 2022-05-06T15:57:49+09:00 2022-05-06T17:44:01+09:00 みなさん初めまして。 QAエンジニアのujeです。 食べチョクでは、2022年4月から正式に自動テスト改善チームが発足いたしました。 チームメンバーは機能開発との掛け持ちにはなりますが、Webエンジニア5名・QA1名で取り組んでいます。 自動テスト改善チームの発足に伴い、食べチョクのテストにまつわる振り返りと、改善チームが取り組んでいることをお話しいたします。 これまで 食べチョクはサービスリリースから5年がたつプロダクトです。 特にここ1年半は食べチョクに参加してくれる仲間が増えており、サービス開発のスピードも上がっています。 食べチョクは2020年まで、テストカバレッジが低く、またなかなか… <p>みなさん初めまして。 QAエンジニアの<a href="https://twitter.com/uje49538710">uje</a>です。</p> <p>食べチョクでは、2022年4月から正式に自動テスト改善チームが発足いたしました。 チームメンバーは機能開発との掛け持ちにはなりますが、Webエンジニア5名・QA1名で取り組んでいます。</p> <p>自動テスト改善チームの発足に伴い、食べチョクのテストにまつわる振り返りと、改善チームが取り組んでいることをお話しいたします。</p> <h2>これまで</h2> <p>食べチョクはサービスリリースから5年がたつプロダクトです。 特にここ1年半は食べチョクに参加してくれる仲間が増えており、サービス開発のスピードも上がっています。</p> <p>食べチョクは2020年まで、テストカバレッジが低く、またなかなか向上しない状態でした。 システム全体に影響がある改修をする際は、毎回全画面を一通り手作業で触りテストするなど労力のかかる状態でした。 この状態を脱するため、2020年末から少しずつ自動テスト改善の動きが始まりました。</p> <h3>20年11月-バックエンドのRSpec積極拡張方針が決まる</h3> <p>これまでは機能開発が優先されていて重要なところだけ自動テストが書かれている状況でした。 そのため、この時期から実装した機能のRSpecによるテスト実装を後回しにせず、並行して増やしていく方針に変わっていきました。</p> <h3>21年8月-カバレッジをデータ化して見える化が始まる</h3> <p>開発方針が変わったものの増やしていくだけとなっていたので、どの程度カバーされているかは把握できていませんでした。 そのためこの時期からデータ化して定期的に展開するようになりました。 数字が出たことで意欲が向上したためか、拡張速度が上がったと実感するのもこの時期でした。</p> <p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/v/vividgarden-tech/20220506/20220506152337.png" width="1200" height="513" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span></p> <h3>21年9月-E2Eテストの本格導入が始まる</h3> <p>9月からWeb版のE2E導入、10月からAndroid版のE2E導入が始まりました。 現在はリリース前にユニットテストの他に、1日1回の定期実行とリリース直前にE2Eが成功することを確認する仕組みになっています。 なお、iOSについては今後、本格導入を検討しています。</p> <h3>22年1月からJestの本格導入が始まる</h3> <p>一時期フロントにまつわる不具合が増加しつつあったことに起因して、フロントエンドの単体テストの用途としてJest導入に踏み切りました。 E2Eはシステムテストの位置付けで外側から目的が達成できるか確認することとしています。 Jestはフロントエンドに特化した単体テストとしてパラメータ受け渡しなどのUI影響がある要素に影響がないかを重視して実装していっています。</p> <h2>これから</h2> <p>冒頭でお話した通り4月から正式に自動テスト改善活動チームが発足いたしました。 各メンバー機能開発との掛け持ちしながらになりますが、webエンジニア5名・QA1名という体制になっています。</p> <h3>発足理由</h3> <p>まだまだ古くからあるコードは1テストすらないことが多いです。 テストのない箇所が開発速度に影響が出たこともあり、解消を意識したのが1点目です。</p> <p>またQCDバランスを意識したリリースするには自動テストが必要不可欠だと判断したことが2点目です。 現在、アプリに関わらないバックエンド・フロントエンド要素は1日1回リリースしています。 さらにメンバー拡大に伴い同時並行で進行する機能開発が増えてきました。 この2つから、壊れた際の検知を早めることが格段に大事になってきました。</p> <p>これらの要素を考慮した結果、組織的な積極対策を目的としてチーム発足へと繋がりました。</p> <h3>主な活動</h3> <p>自動テストの速度向上・自動テストの充実化を目標に取り組んでいます。 ただし掛け持ちなので、やることはみんなで分担する&週1日など無理のない範囲で改善活動に時間を当てるような形になっています。</p> <p>具体的には実行速度の監視・速度改善の対策案を相談したり、充実化でもデータ化・方針策定などを始めています。</p> <h3>チーム運営</h3> <p>QAである私は主にMTG進行の役割を担っており以下のようなことに注意を払って運営しています。(書いてあることは基本的なことばかりです)</p> <ul> <li>事前資料の準備&事前展開・MTG近づいたらリマインド・事前に資料記載のお願い</li> <li>レビューと計画を1セットにして1時間以内のMTG(時短できるなら時短を目指す)</li> <li>OODAループを利用してネクストアクションを決める <ul> <li>※Observe、Orient、Decide、Actの4項目を軸にループを回す方法です。別手法にはPDCAサイクルがあります</li> </ul> </li> </ul> <h3>まだまだこれから</h3> <p>今のところネクストアクションが無いなどの事態はなく、少しずつ前に進んでいるかなと感じています。 とはいえ、掛け持ちで割ける時間も決まっていることから成果を出していくのはこれからです。</p> <p>メンバーと意見を出し合いより良いテスト体制作りを目指します。</p> <h2>最後に</h2> <p>食べチョクでは仲間を募集しています。 ご興味がある方は是非、<a href="https://vivid-garden.co.jp/#RECRUIT">RECRUIT</a>からご応募ください。</p> vividgarden-tech 食べチョクのプロダクトチームとチームトポロジー hatenablog://entry/13574176438046162910 2021-12-26T16:50:44+09:00 2021-12-26T16:50:44+09:00 食べチョクのプロダクトチームとチームトポロジー この記事はビビッドガーデン Advent Calendar 2021 最終日の記事です。 皆さんこんにちは、エンジニアの西尾です。 2019年、食べチョクのプロダクトチームは数名だけでした。 2020年から少しずつ、2021年頭からは一気にメンバーが増加し、12月現在は30名近いメンバーが所属しています。 プロダクトチームはもともと大きな1チームでしたが、2021年からチーム分割を検討し、6月頃から本格的に分割を始め、12月現在は7つのチームでプロダクト開発をすすめています。 組織設計、チーム分割にあたり参考にしたのが、チームトポロジーの概念です… <h2>食べチョクのプロダクトチームとチームトポロジー</h2> <p>この記事は<a href="https://qiita.com/advent-calendar/2021/vivid-garden">ビビッドガーデン Advent Calendar 2021</a> 最終日の記事です。</p> <p>皆さんこんにちは、エンジニアの<a href="https://github.com/nishio-dens">西尾</a>です。</p> <p>2019年、食べチョクのプロダクトチームは数名だけでした。 2020年から少しずつ、2021年頭からは一気にメンバーが増加し、12月現在は30名近いメンバーが所属しています。</p> <p>プロダクトチームはもともと大きな1チームでしたが、2021年からチーム分割を検討し、6月頃から本格的に分割を始め、12月現在は7つのチームでプロダクト開発をすすめています。</p> <p>組織設計、チーム分割にあたり参考にしたのが、<a href="https://teamtopologies.com/">チームトポロジー</a>の概念です。 今回はチームトポロジーの一部を食べチョクでの実例を踏まえて紹介しつつ、運用してみての感想や今後の課題について紹介します。</p> <p>本記事を書くにあたり、<a href="https://pub.jmam.co.jp/smp/book/b593881.html">日本語版チームトポロジー</a>を大いに参考にしています。</p> <h1>チームファースト</h1> <p>食べチョクのプロダクトチームは、もともと1つだけでした。 徐々にメンバーが増えていき、分割前には10〜15名ほどが所属する、大きな1チームとなっていました。</p> <p>大きすぎるチームでは以下の問題が発生していました。</p> <ul> <li>プロダクトが1つのチームで把握するには大きく、メンバーのプロダクトに対する認知負荷がとても高い状態になっていた</li> <li>プロダクトの端から端まで把握できるメンバーは極めて少ない。そのため仕事がその分野に詳しい個人に張り付く状態となっていた <ul> <li>例えば、定期便の担当はMさん、商品検索はKさん、注文画面はFさん担当といった具合になっていた</li> </ul> </li> <li>仕事が個人に張り付くことにより、計画がメンバー個人に左右される状態になっていた <ul> <li>今週中に最優先の施策、機能のデリバリーができるかどうかは、担当する個人の能力や状況に左右される</li> </ul> </li> <li>チームとしてサポートできず、大きなプレッシャーが個人にのしかかる状態であった <ul> <li>今週中に最優先の施策、Aさんどうか体を壊さずに無理して実装してくれ!</li> </ul> </li> </ul> <p>プロダクトチームのメンバーは続々と増えている状態でした。 増員したのだから、その分チームとしてのケイパビリティを高め成果を出していくことが求められます。 メンバー数名でうまくいっていたときの開発方法や構成のまま増員しても、チームのパフォーマンスはどんどん落ちていってしまいます。</p> <p>プロダクトが大きく複雑化したことにともない、個人が気合でプロダクトを開発するフェーズは終わりを迎えました。 組織がスケールするために、より大きな成果をだしていくために、2021年は<strong>チームとして</strong>成果をだしていかなければならない、すなわち<strong>物事をすべてチームファーストで考える</strong>フェーズに変わりました。</p> <h1>チーム分割のアンチパターン</h1> <p>チームとして成果を出していくためには、チームサイズは重要です。 効率よく動くためには、大きな1チームから複数の5〜9名が所属する小さなチームに分割する必要があります。</p> <p>どう分割したらよいのでしょうか。 職能別に分ければよいのか、顧客別に分ければよいのか、機能ベースでわければよいのか、はたまたミッションベースでチームをわければよいのか。</p> <p>いろいろなアプローチが浮かび、どれも一長一短だと思います。ただし、アンチパターンは存在します。</p> <p><a href="https://teamtopologies.com/">チームトポロジー</a>では、頻繁にメンバーが入れ替わるような<strong>プロジェクトベースのチームは良くない</strong>と紹介しています。 機能A開発プロジェクトが立ち上がり、メンバーがプロジェクトごとに集められ、プロジェクトが終了したら解散するようなチームはよくありません。 それはチームに知識が蓄積されず結局は個人に作業が紐付いてしまいます。また、メンバーは頻繁にコンテクストスイッチを求められパフォーマンスが出せない恐れがあります。</p> <p>場当たり的で無策のままチームを分割するのもよくありません。単に情報が分断されただけの開発のしにくい組織になってしまいます。</p> <h1>変更のフローに最適化した組織</h1> <p>チーム分割のアンチパターンはわかりました。ではどうやって分割すればよいのでしょうか。 <a href="https://teamtopologies.com/">チームトポロジー</a>では、変更のフローに最適化した組織を作るのがよいと紹介しています。</p> <p>なかなか難しい言葉ですが、要は<strong>チーム間の引き継ぎを極力少なくし、自分たちのチームの中でできるだけ仕事が完結する</strong>チームにするのが良いとのことです。</p> <p>エンジニアであれば、ときにはすべて自分ひとりで考えて作って運用したほうが速いのでは、と感じたことはないでしょうか。 なぜ速いと思うのか。それは仮説出し、機能検討、デザイン、設計、開発、テスト、デリバリー、運用保守とそこからのフィードバックがすべて自分だけで完結し、 他のメンバーとの調整が発生しなくて楽だからではないでしょうか。</p> <p>チームにも同じことが言えます。 企画は企画チームが考えて、開発チームに引き継ぐ。開発チームは機能を作りテストチームに引き継ぐ。テストチームの許可がでたらデプロイし運用チームに引き継ぐ。 運用チームは気になる点がありフィードバックはしたいけど他のチームと調整するのは面倒だな。 このように引き継ぎが多いとプロダクトを素早く改善しチームとしてのパフォーマンスを出すことはできません。 重要なフィードバックや気付きも得にくく、良いプロダクトを作ることはできません。</p> <h1>ストリームアラインドチーム</h1> <p><a href="https://teamtopologies.com/">チームトポロジー</a>では、モダンなソフトウェアの開発と運用に必要なのは、たった4つのチームタイプであると紹介しています。 ストリームアラインドチーム、イネイブリングチーム、コンプリケイテッド・サブシステムチーム、プラットフォームチームの4つです。</p> <p>このうち、組織の根幹となるのがストリームアラインドチームです。</p> <p>ストリームとはなんでしょうか。 チームトポロジーでは、「ビジネスドメインや組織の能力に沿った仕事の継続的な流れ」と呼んでいます。 またまた難しい言葉ですが、例えばプロタクト開発においては、顧客にどんな価値を届けるかを考え、 企画検討からデリバリー、その後のフィードバックをもとにした改善までの一連の仕事の流れのことをストリームと呼びます。</p> <p>ストリームアラインドチームは、単一のストリームに沿って動くチームです。 例えば、「カート機能を通じて、顧客がほしいと思った複数の商品を送料を押さえつつ購入できる体験を提供する」といった価値ストリームに沿って動くチームです。 そもそも顧客の課題は何なのか、何をしたら改善するのか、どういう設計にしたらよいか、本番運用はどうしたらよいか、 ストリームアラインドチームは自分たちの中で作業が完結する能力を備えている必要があります。</p> <p>モダンなソフトウェア組織では、ほとんどのチームがストリームアラインドチームとなるとのことです。 食べチョクのプロダクトチームでは、上記の考えを踏まえつつ、2021年12月現在は3つのストリームアラインドチームを作っています。</p> <h1>ストリームアラインドチームを支える3つのチーム</h1> <p>ストリームアラインドチームはすべての根幹となるチームです。 このチームタイプの他に、ストリームアラインドチームを支える以下3つのチームタイプが定義されています。 今回はチームタイプの紹介は割愛します。詳しくは<a href="https://teamtopologies.com/">チームトポロジー</a>を参照ください。</p> <p>食べチョクのプロダクトチームでは、2021年12月現在、以下3つのチームを設置しています。</p> <ul> <li>イネイブリングチーム <ul> <li>1つのチーム、5名のメンバーが所属しています。内訳は開発全般を担当するメンバーが2名、フロントエンドエンジニアが2名、QAエンジニアが1名です。</li> </ul> </li> <li>プラットフォームチーム <ul> <li>1つのチーム、4名のメンバーが所属しています。</li> </ul> </li> <li>コンプリケイテッド・サブシステムチーム <ul> <li>1つのチーム、3名のメンバーが所属しています。</li> </ul> </li> </ul> <h1>チームインタラクションモード</h1> <p><a href="https://teamtopologies.com/">チームトポロジー</a>では、チーム構成について述べているだけでなく、チーム間の関わり方、インタラクションについても紹介しています。 インタラクションモードは、コラボレーション、X-as-a-Service、ファシリテーションの3つを定義しています。この概念も詳しくは<a href="https://teamtopologies.com/">チームトポロジー</a>を参照ください。</p> <h1>感想と今後の課題</h1> <p>ここまではチームトポロジーの基本的な概念を、プロダクトチームの実例を踏まえつつ紹介しました。 ここからは実際に採用してみて直面した問題や感想、今後の課題について紹介します。</p> <h4>チームインタラクションはまだまだこれから</h4> <p>インタラクションモードに関しては、食べチョクではうまく実践できてはいません。</p> <p>コラボレーションについては、2021年12月現在は1つのストリームアラインドチームとコンプリケイテッド・サブシステムチームが行っています。 これはまあ2つのチームが密に協力して機能開発に取り組んでいるというだけです。 朝会や日々の開発、レトロスペクティブは2つのチームで合同実施していますが、プランニングはそれぞれのチームで行っています。</p> <p>ファシリテーションに関してはイネイブリングチームが行ってはいますが、うまく機能しているのかと聞かれるとそんなことはなく、まだまだこれからです。 ファシリテーションという概念になじみがなく、そもそも何をすればよいのか、チーム内でもうまく認識はとれていないのが実情です。</p> <p>X-as-a-Serviceでのインタラクションは今の所できていません。</p> <blockquote><p>X-as-a-Serviceモデルがうまく機能するのは、サービス境界が正しく選択、実装され、サービスを提供するチームが優れたサービスマネジメントを実践している場合に限られる</p></blockquote> <p>とありますが、食べチョクではサービスの境界がまだまだ曖昧なために、運用はできていません。</p> <h4>イネイブリングチームは概念がわかりづらく、運用が難しい</h4> <p>イネイブリングチームは設置していますが、まだ名前だけという状態です。運用が難しい理由は2つあります。</p> <ul> <li>ファシリテーションがそもそも難しい</li> <li>イネイブリングチームとはどういうチームなのか? チーム内にもチーム外にもうまく理解されていない</li> </ul> <p>ファシリテーションについては前述したとおり、まだまだこれからです。</p> <p>イネイブリングチームとはそもそも何なのか、組織内で学習ができていないというのもあります。 2021年12月に<a href="https://pub.jmam.co.jp/smp/book/b593881.html">日本語版チームトポロジー</a>が出るまでは、英語の文献と一部の日本語サイトでの紹介しかされていなかったことも大きいと思っています。 今は日本語で読めるようになったため、12月からはチーム内でのTeam Topologies読み合わせ会をすすめて、少しずつ概念の学習をすすめています。</p> <p>今はファシリテーション先のチームでは、手が回らない施策や機能実装、テストを変わりに担当する、便利なお助けチームになってしまっていることが多いのが実情です。</p> <h4>ストリームアラインドチームが複数のストリームを持っている</h4> <p>ストリームアラインドチームは、<strong>単一の仕事のストリームに沿って動く</strong>と定義されていますが、現状は複数のストリームを1つのチームが持ってしまっています。</p> <p>これはストリームの境界がまだまだ曖昧であること、 すべてのストリーム分のチームが用意できるほどメンバーがいないこと、 やることが多くてフォーカスが絞れていないなどのプロダクトマネジメント上の課題など、さまざまな要因が重なっての結果です。</p> <h4>チームの認知負荷を下げるアーキテクチャになっていない</h4> <p>これからの技術課題です。 個人だけでなくチームの認知負荷に注意し、チームが扱うコードベース、アーキテクチャも扱いやすいよう適切に整理していく必要があります。</p> <p>食べチョクは、今はまだ大きなモノリシックアプリケーションです。 「会員登録の機能を修正したら、なぜか注文画面が壊れた」といった問題が発生することもありました。 ドメインの境界が曖昧で、今は幅広くコードベースを把握していないと怖くて手を入れられない状態にあります。</p> <p>チームがデリバリーしやすいように、認知負荷を下げて安全に手を加えられるようにするために、チームに合わせてアーキテクチャを整理していかなければなりません。</p> <h2>おわりに</h2> <p>わたしたちはまだチームトポロジーの概念を参考に、チームを整理しただけという段階です。 道半ば、チームとして組織としてスケールし成果を出していくためには、まだまだ改善の余地があります。</p> <p>チームトポロジーは銀の弾丸ではないので、導入したから組織がうまくまわるというものではありません。 チームのあり方を変えるだけでなく、健全な組織文化の構築、技術やアーキテクチャの整理、プロダクトロードマップの整理やマネジメントの強化などもあわせて行っていく必要があります。</p> <p>2022年も、よりよい価値のあるプロダクトを生産者・ユーザーに届けられるよう、そして食べチョクがさらなる成長を遂げられるよう、これからも精進していく次第です。</p> vividgarden-tech 「あ、面白そう」と思ってもらいたい。カジュアル面談の中身をザクッと公開します hatenablog://entry/13574176438039933990 2021-12-07T11:00:03+09:00 2021-12-07T11:00:03+09:00 応募者の方に「ビビッドガーデンで働くとどんな価値が得られるか」を理解してもらうこと、さらに砕けた言い方をすると「あ、面白そう」と思ってもらうことがカジュアル面談のゴールだと定義しています。 <p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/v/vividgarden-tech/20211206/20211206111602.png" alt="f:id:vividgarden-tech:20211206111602p:plain" width="1200" height="630" loading="lazy" title="" class="hatena-fotolife" itemprop="image"></span> この記事は<a href="https://qiita.com/advent-calendar/2021/vivid-garden">ビビッドガーデン Advent Calendar 2021</a> の4日目の記事です。</p> <p>こんにちは。ビビッドガーデンのプロダクト開発チームで採用と組織開発を担当している<a href="https://twitter.com/hirashunshun">平野</a>です。 簡単に自己紹介をすると、「webエンジニア歴8年」「ビビッドガーデンには3年前に入社してここ1年半は採用にコミット」「好きなものはビールとハロプロと<a href="https://youtu.be/I21f_k6djv8?t=54">パデル</a>」という人です。</p> <p>ビビッドガーデンではプロダクト開発に関わる人の採用において、<strong>候補者の方との相互理解</strong>というのを重視しています。 すなわち候補者の方にはビビッドガーデンの良いところと課題となっているところを「そんなとこまで?」というレベルまで知ってもらおうと思っており、その起点となる場がまさにカジュアル面談だと捉えています。</p> <p>この記事ではプロダクト組織のすべてのカジュアル面談に出席している私がカジュアル面談の<strong>弊社における立ち位置</strong>と<strong>実際にどんな話をしているのか</strong>という部分を書くことで、応募者の方にとってカジュアル面談がより価値のある時間になる手助けができれば・・・と思っています。</p> <h1>カジュアル面談にかけてる思い</h1> <p>我々としては応募者の方に<strong>「ビビッドガーデンで働くとどんな価値が得られるか」を理解してもらう</strong>こと、さらに砕けた言い方をすると<strong>「あ、面白そう」と思ってもらうこと</strong>がカジュアル面談のゴールだと定義しています。</p> <p>応募者の方に時間を割いてもらっているので基本は応募者の方が聞きたい話を丁寧にさせて頂くのですが、「そもそもどんな働き方をしたい方なのか」「どのくらいビビッドガーデンのことを知っているのか」ということ応募者の方にお聞きして、それに合わせて弊社からさまざまな情報をお伝えしています。</p> <p>しかし今やプロダクト開発に関わるメンバーの採用はどんな職種でも「選考する側」「選考される側」というものはなく<strong>「お互いがお互いを選考している」という状況</strong>です。 その上選考フェーズにおいては候補者の方とお話できる時間は非常に限られています。</p> <p>限られた時間の中で弊社で働くことに価値を感じて頂く為に、ついついポジティブな面だけを強調してしまいがちですが、キャリアの方向性などにミスマッチの懸念があればお互いのために必ずそれをお伝えするようにしています。 相手をリスペクトしながら飾ることなくストレートに話を伝えあう弊社の雰囲気を私自身が体現すべきですし、そのためにカジュアル面談は「あくまでカジュアルなので〜」という感じではなく本気で、誠意を持って取り組んでいます。</p> <p><strong>こう書いてしまうと堅苦しい印象もありますが、お話自体はカジュアルに楽しくさせていただいております。カジュアル面談への応募は本当に、本当にカジュアルにしてください〜! </strong></p> <h1>カジュアル面談の実際の流れ</h1> <h2>ごあいさつ</h2> <p>カジュアル面談は主に私と現場メンバーのペア体制でやっており、最初に我々の自己紹介をさせてもらっています。応募者の方には自己紹介は求めていません。</p> <p>そのままアイスブレイクを兼ねて「お互いのカジュアル面談のゴール」「お聞きしたいこと」を確認させてもらいます。「転職を考えていて、開発体制などの内情を詳しく知りたい」「転職はあまり考えてないが興味はある会社なので話を聞いてみたかった」「実家が農家でビジネスモデルについて知りたかった」などのお答えをいただきます。<strong>なんでもござれです。</strong> あわせて「これは選考ではないので我々から何かしら判断をするということはありません」ということをお伝えしています。</p> <h2>弊社について</h2> <p>応募者の方が「お聞きしたいこと」次第で順番は左右しますが、カジュアル面談が終わったタイミングではすべての応募者の方が弊社に関する理解度が一定レベルになるようにお話するよう心がけています。 基本セットとしては以下内容をお伝えしています。</p> <ul> <li>何をしたい会社なのか <ul> <li>会社のミッションとビジョンについて</li> </ul> </li> <li>そのために何を作っているのか <ul> <li>食べチョクというプロダクトの詳細説明</li> </ul> </li> <li>それをどう作っているのか <ul> <li>開発組織の話(ユニット制、技術スタック)</li> </ul> </li> <li>どんな人が関わっているのか <ul> <li>メンバーの紹介</li> </ul> </li> <li>今後どうしていきたいのか <ul> <li>今期と中長期に何をしていきたいのか</li> </ul> </li> <li>そのためにどのような人を増やしたいのか <ul> <li>職種に応じてお伝えします</li> </ul> </li> </ul> <p>ここでは専用のスライド資料(今月中に公開予定です!!)を使って説明しながら、所々止まって「気になったところありますか?」「もっと深く知りたいところはありますか?」「逆にこちらから聞いてもいいですか?」という形で対話形式で話を進めています。事業の話、開発の話、会社の話、<strong>なんでもござれです。</strong></p> <p>また、具体的に理解してもらうということに重点を置いていて、実際のプロダクトの画面、Slack(社内の雰囲気理解)、GitHub(プロダクトチーム以外も使っています)、ユーザーからの反響(Twitterなど)も画面共有して見てもらうようにしています。(機密情報は伏せています)</p> <p>多くはこの過程で応募者の方の「聞きたかったこと」をカバーするのですが、必ず「気になっていた点はクリアになりましたか?」と確認させてもらっています。</p> <h2>締め</h2> <p>全体を通じて大体45分で終了となります。最後に聞き逃したことが無いかを確認させてもらいます。面談終了後でもいつでも連絡いただければすぐに回答しますともお伝えしています。</p> <p>興味がある方には今後の選考フローに関する説明をしますが、この場で選考に進むかどうかの意向確認は行っておりません。持ち帰っていただいて一言「選考に進みたい」とご連絡いただければすぐに次の選考へとご案内しています。</p> <p>しかしこの場で選考に進む意思を口頭で伝えていただく方、面談終盤に「今チャット送りました」と言ってくれる方、面談終了1分後にチャットを送ってくれる方も多いです。素直に大変うれしい気持ちになります。</p> <p>お時間頂いたことに感謝して終了となります。</p> <h1>よく聞かれること</h1> <h2>ビジネスモデルが農協とバッティングしてそうだけど、大丈夫なの?</h2> <p>提供しているサービスやターゲットには違いがあるものの、生産者の方への価値貢献という側面では<strong>同じ方向を向いている同志</strong>というお話をしています。</p> <p>代表秋元のブログに詳しく記載があるので、気になる方は是非そちらも読んでみてください。一部引用します。</p> <blockquote><p>・提供しているサービス価値は異なり、すみ分けができていると認識しています。結果として農協さんに呼んでいただいて講演させていただくことも多々あります。</p> <p>・皆さんが「一次産業を良くしていきたい」という共通の想いを持っています。 <iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fnote.com%2Fakirina%2Fn%2Fn8b580161eb86" title="農協って敵ですよね?とあまりに聞かれるので…|秋元里奈@食べチョク代表|note" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;"></iframe><cite class="hatena-citation"><a href="https://note.com/akirina/n/n8b580161eb86">note.com</a></cite></p></blockquote> <h2>サービスの成長と共にプロダクト組織はどう変わりましたか?</h2> <p>1年半前まではwebエンジニア3-4人という体制でやっていましたが、今となっては以下の各職種のメンバーが在籍しており40名に迫る組織となっています。</p> <ul> <li>webエンジニア</li> <li>モバイルエンジニア</li> <li>インフラエンジニア</li> <li>QAエンジニア</li> <li>データアナリスト</li> <li>UIデザイナー</li> <li>PdM</li> </ul> <p>組織体制という観点では1つのチームがすべての開発を受け持っていた形から、プロダクトに対して定められたミッションを<strong>複数のユニットがそれぞれ自律して取り組むという体制</strong>に。</p> <p>開発方針という観点では「作る、リリースする」ということが目的になりつつあった状態から、どのくらい価値提供ができたかという軸での分析を元に仮説検証を重ねて<strong>探求と学習を進める体制</strong>へと変わりました。</p> <p>この部分は1年前から<a href="https://twitter.com/kakutani">角谷さん</a>に手伝ってもらっており、一緒に議論し続けて今の形になっています。現在も継続議論中です。 <iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Foffers.jp%2Fmedia%2Finterviews%2Fcompany-interview%2Fa_1910" title="新たな課題は「開発組織のあり方」。急成長スタートアップが「CleanAgile」訳者・角谷氏を迎えた狙いとは? | Offers Magazine" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;"></iframe><cite class="hatena-citation"><a href="https://offers.jp/media/interviews/company-interview/a_1910">offers.jp</a></cite></p> <p>体制に関してはゴールは無いので、今後も常にアップデートし続ける、<strong>変化を歓迎する</strong>という姿勢を取っていきます。</p> <h2>エンジニアの評価制度はありますか?</h2> <p>昨年末に制定したものがあります。 簡単に説明するとにグレード毎に期待成果と年収が記載されており、メンバー各々がそれに沿った形で目標設定をしています。 半期ごとに評価が行われますが評価者との中間評価と毎月の1on1、私や各チームのリードメンバーとの毎週の1on1で軌道修正を図っています。</p> <p>しかしこの評価制度はまだまだベータ版だと思っており、エンジニアが出す価値をちゃんと評価できているだろうかという観点ではまだまだ改善の余地があると思っています。 今月に評価制度のアップデートがありましたが、引き続きアップデートを重ねていく予定です。</p> <h2>どんな人がいますか?</h2> <p>プロダクト組織は全員中途入社で、出身企業はベンチャーからSIなどひとくくりにはできないくらい多様です。 しかしマインドという観点では皆下記2つの考えを持っています。</p> <ul> <li>自分の手でプロダクトを作り、それが誰かの課題を解決し、その様子が手にとるように分かる環境で働きたい</li> <li>より良いプロダクトを作るために、慣れた体制や技術に固執せず、変化やアップデートすることを歓迎する</li> </ul> <p>このマインドに加え<strong>「生産者の方への価値貢献」が全メンバー自分事化している</strong>点がチーム全体が一体感を持って前に進める秘訣だと感じています。</p> <p>「実家が農家でした」などの農業バックグラウンドがあるメンバーは多くないのですが、 それでも生産者の方に関わるという事が自分事化するのは<strong>日常的に生産者さんの声やフィードバックが聞こえるプロダクト</strong>という特性のおかげだと思っています。</p> <h1>おわりに</h1> <p>ご覧いただきありがとうございました。 ビビッドガーデンは11月をもって6期目に突入しました。</p> <p>1年前はエンジニア数人とデザイナーでがむしゃらに作っていた感じでしたが、1年で複数の少人数チームが自律的にデリバリーと仮説検証を進める体制へと大きく進化しました。 6期目はさらなる価値提供のために<strong>「より深くより広い領域で仮説検証をする」、「デリバリーを安定的に行う開発基盤を強固にする」</strong>という2点を実現させたいと思っています。</p> <p>会社としても立ち上げ期から拡大期に一気に変わってきた状態です。ドラスティックに環境が変わり続ける現場にてより多くのメンバーと一緒にお仕事を楽しみたいと思っています。</p> <p>カジュアル面談は以下媒体より申し込みが可能です。</p> <p><a href="https://meety.net/matches?q=%E3%83%93%E3%83%93%E3%83%83%E3%83%89%E3%82%AC%E3%83%BC%E3%83%87%E3%83%B3">Meetyでカジュアル面談</a></p> <p>選考フローも必ず最初にカジュアル面談をはさみますのでこちらでエントリー頂いても大丈夫です。エントリーの時点では履歴書等必須ではありません。 <iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fherp.careers%2Fv1%2Fvividgarden%2Frequisition-groups%2Fd69f3cff-ec87-47ba-b442-713d77e661d2" title="プロダクト開発(Product) の求人一覧 - 株式会社ビビッドガーデン" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;"></iframe><cite class="hatena-citation"><a href="https://herp.careers/v1/vividgarden/requisition-groups/d69f3cff-ec87-47ba-b442-713d77e661d2">herp.careers</a></cite></p> <p>それでは、カジュアル面談にてお会いしましょう!</p> vividgarden-tech 監査ログをファイルに記録するためのGemを公開しました hatenablog://entry/26006613708419334 2021-03-30T15:51:59+09:00 2021-03-30T15:51:59+09:00 こんにちは。 食べチョクの開発を副業でお手伝いしているプログラマーの花村です。 監査ログをJSONL(JSON Lines)のファイルに記録するためのGemのAuditLoggableを作成してrubygems.orgで公開しました。 ソースコードもGitHubで公開しています。 ​ rubygems.org github.com なぜ開発したのか 食べチョクでは監査ログを記録するためにAuditedを利用していました。 AuditedはActiveRecordのコールバックを利用してモデルの変更を手軽にRDBに記録してくれる大変便利なGemです。 しかしRDBに記録するためテーブルサイズが肥… <p>こんにちは。<br /> 食べチョクの開発を副業でお手伝いしているプログラマーの<a href="https://github.com/yujideveloper">花村</a>です。</p> <p>監査ログをJSONL(JSON Lines)のファイルに記録するためのGemのAuditLoggableを作成して<a href="https://rubygems.org/gems/audit_loggable">rubygems.orgで公開</a>しました。<br /> <a href="https://github.com/vividgarden/audit_loggable">ソースコードもGitHubで公開</a>しています。 ​ <iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Frubygems.org%2Fgems%2Faudit_loggable" title="audit_loggable | RubyGems.org | your community gem host" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;"></iframe><cite class="hatena-citation"><a href="https://rubygems.org/gems/audit_loggable">rubygems.org</a></cite> <iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fgithub.com%2Fvividgarden%2Faudit_loggable" title="vividgarden/audit_loggable" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;"></iframe><cite class="hatena-citation"><a href="https://github.com/vividgarden/audit_loggable">github.com</a></cite></p> <h3>なぜ開発したのか</h3> <p>食べチョクでは監査ログを記録するために<a href="https://github.com/collectiveidea/audited">Audited</a>を利用していました。<br /> AuditedはActiveRecordのコールバックを利用してモデルの変更を手軽にRDBに記録してくれる大変便利なGemです。</p> <p>しかしRDBに記録するためテーブルサイズが肥大化しパフォーマンスに影響を与える場合があるというデメリットもあります。<br /> 食べチョクでは注文数の増加に伴ってこの問題に直面しました。</p> <p>これを解決するにあたり以下のようなアプローチが思いつきます。</p> <ol> <li>監査ログを定期的に消す</li> <li>監査ログを記録するDBを分ける</li> <li>監査ログをRDBとは別のストレージに記録する</li> </ol> <p>1では過去分のデータが必要になった際に困りますし、2では延命措置にしかなりません。<br /> ということで今回は3を採用しました。</p> <h3>記録したログファイルの扱い</h3> <p>AuditLoggableで記録したログファイルはCloudWatch Agentを利用してCloudWatch Logsに連携しています。<br /> CloudWatch Logsには1か月分ほど保持する設定にしており、毎日CloudWatch LogsからS3へバックアップしています。<br /> このときにCloudWatch LogsのExportTask機能を使ってエクスポートするとJSONのメッセージに余分な情報(CloudWatch Logsに取り込んだ日時の情報)がついてしまうので、S3のイベント通知を使いLambdaで整形処理を走らせています。</p> <p>CloudWatch Logsに保持し続けずにS3に移しているのはコスト圧縮のためです。 1カ月より過去分を参照したいときはAthenaで検索できるようにしてあります。</p> <h3>なぜログファイルに記録するのか</h3> <p>監査ログの記録先としてRDB以外を使うのであればDynamoDBなどのNoSQLなDBに記録することも考えられます。<br /> しかし以下の理由で記録先はファイルとしました。</p> <ol> <li>記録の失敗しにくさ</li> <li>リアルタイムに参照できる必要はない​</li> </ol> <p>Web API経由での記録では失敗することも多いのでそのためのハンドリングが煩雑になることが予想されます。<br /> また監査ログを記録された瞬間から参照したいことはないので収集にラグがあっても構わないという判断がありました。</p> <h3>使い方</h3> <p><code>config/initializers/audit_loggable.rb</code> など初期化ファイルで出力先のファイル名などを設定します。</p> <pre class="code lang-ruby" data-lang="ruby" data-unlink><span class="synType">AuditLoggable</span>.configure <span class="synStatement">do</span> |<span class="synIdentifier">config</span>| <span class="synStatement">if</span> <span class="synType">Rails</span>.env.test? config.auditing_enabled = <span class="synConstant">false</span> <span class="synStatement">end</span> config.audit_log_path = <span class="synType">Rails</span>.root.join(<span class="synSpecial">&quot;</span><span class="synConstant">log</span><span class="synSpecial">&quot;</span>, <span class="synSpecial">&quot;</span><span class="synConstant">audits.log</span><span class="synSpecial">&quot;</span>) <span class="synStatement">end</span> </pre> <p><code>ApplicationRecord</code> など必要なモデルで <code>AuditLoggable::Extension</code> でActiveRecodのモデルに機能を追加します。</p> <pre class="code lang-ruby" data-lang="ruby" data-unlink><span class="synPreProc">class</span> <span class="synType">ApplicationRecord</span> &lt; <span class="synType">ActiveRecord</span>::<span class="synType">Base</span> <span class="synPreProc">extend</span> <span class="synType">AuditLoggable</span>::<span class="synType">Extension</span> <span class="synPreProc">end</span> </pre> <p>監査ログを記録したいモデルで <code>log_audit</code> を呼び出して記録対象に設定します。</p> <pre class="code lang-ruby" data-lang="ruby" data-unlink><span class="synPreProc">class</span> <span class="synType">Post</span> &lt; <span class="synType">ApplicationRecord</span> log_audit <span class="synPreProc">end</span> </pre> <p><code>ApplicatoinController</code> などで以下のように情報集する機能を有効化することで「操作したユーザーID(とクラス名)」、「リモートIPアドレス」、「リクエストID」を監査ログに記録できるようになります。</p> <pre class="code lang-ruby" data-lang="ruby" data-unlink><span class="synPreProc">class</span> <span class="synType">ApplicationController</span> &lt; <span class="synType">ActionController</span>::<span class="synType">Base</span> around_action <span class="synType">AuditLoggable</span>::<span class="synType">Sweeper</span>.new(<span class="synConstant">current_user_methods</span>: <span class="synConstant">%i[current_user]</span>) <span class="synPreProc">end</span> </pre> <h3>Auditedとの違い</h3> <p>Auditedと似た挙動をしますが主に以下の違いがあります。</p> <ul> <li>記録先はJSONLのログファイル <ul> <li>RDBではなくJSONLのログファイルに記録します</li> </ul> </li> <li>ActiveRecordのモデルへのメソッド追加は最小限 <ul> <li>Auditedでは <code>audit_xxx</code> のようなインスタンスメソッドが複数追加されますが、AuditLoggableでは1組のaccessorの追加のみです</li> <li>またクラスメソッドは機能の有効化用メソッド1つを追加するのみです</li> </ul> </li> <li>Associated Auditsは未サポート <ul> <li>食べチョクでは不要であったことと複雑になってしまうので未サポートとしました</li> </ul> </li> <li>条件付き記録は未サポート <ul> <li>食べチョクでは不要であったことと複雑になってしまうので未サポートとしました</li> </ul> </li> </ul> <h3>おわりに</h3> <p>似たような課題を抱えている方の解決の一助になれば幸いです。 もしバグを見つけたり機能を追加したいなどありましたらGitHubでPull RequestやIssueをお待ちしています。</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fgithub.com%2Fvividgarden%2Faudit_loggable" title="vividgarden/audit_loggable" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;"></iframe><cite class="hatena-citation"><a href="https://github.com/vividgarden/audit_loggable">github.com</a></cite></p> vividgarden-tech map,filter,reduce関数内で状態を書き換えてはいけないのは、なぜですか hatenablog://entry/26006613682102115 2021-01-24T15:14:51+09:00 2021-01-24T15:14:51+09:00 皆さんこんにちは、エンジニアの西尾です。 あなたは今、コードレビューをしています。 以下コードに直面したとき、何を指摘しますか。 修正してほしい点を、どのようにレビュイーに伝えますか。 // これはJavaScriptのコードです。 // 商品の在庫を1つ減らし、売り切れになったものを抽出したい、と思っています。 const soldOutProducts = products.filter(product => { product.quantity -= 1; return product.quantity <= 0; }); よくないコードレビューの例 問題は表題の通り、filterの中で… <p>皆さんこんにちは、エンジニアの<a href="https://github.com/nishio-dens">西尾</a>です。</p> <p>あなたは今、コードレビューをしています。</p> <p>以下コードに直面したとき、何を指摘しますか。 修正してほしい点を、どのようにレビュイーに伝えますか。</p> <pre class="code lang-javascript" data-lang="javascript" data-unlink><span class="synComment">// これはJavaScriptのコードです。</span> <span class="synComment">// 商品の在庫を1つ減らし、売り切れになったものを抽出したい、と思っています。</span> <span class="synStatement">const</span> soldOutProducts = products.filter(product =&gt; <span class="synIdentifier">{</span> product.quantity -= 1; <span class="synStatement">return</span> product.quantity &lt;= 0; <span class="synIdentifier">}</span>); </pre> <h1>よくないコードレビューの例</h1> <p>問題は表題の通り、filterの中で状態を書き換えているのが、よくありません。</p> <p>関数型言語を学んだことがある方なら、このコードの違和感に気がつきます。 filterは純粋関数であるべきだ、副作用を起こしてはいけない。そう認識しているからです。</p> <p>しかし、それをコードレビューで指摘したとして、相手に伝わるでしょうか。 書いている言語は関数型ではないし、副作用とか言われても意味がわからないし、複雑なコードでもないし、動くからいいんじゃないの。</p> <p>map,filter,reduce内で状態を書き換えるコードは、思いの外よく見かけます。 そのたびに、私は以下のような指摘をしてしまっていました。</p> <p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/v/vividgarden-tech/20210123/20210123215718.png" alt="f:id:vividgarden-tech:20210123215718p:plain" title="" class="hatena-fotolife" itemprop="image"></span></p> <p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/v/vividgarden-tech/20210123/20210123215721.png" alt="f:id:vividgarden-tech:20210123215721p:plain" title="" class="hatena-fotolife" itemprop="image"></span></p> <p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/v/vividgarden-tech/20210123/20210123215725.png" alt="f:id:vividgarden-tech:20210123215725p:plain" title="" class="hatena-fotolife" itemprop="image"></span></p> <h1>問題は何なのか?</h1> <p>filter内で状態を書き換えてはいけません、と伝えるだけでは、レビューを受けた側も意味がわかりません。 このコードが良くない理由を説明しないと、相手には伝わりません。</p> <p>なぜ状態を書き換えてはいけないのか。 言われてみると、私自身もすんなり理由を説明できませんでした。</p> <p>そこで、自分なりにこのコードの問題点を、今一度考えてみました。</p> <h3>複数の目的をもったコードは、わかりづらい</h3> <p>UNIXの基本的な考えに、「ひとつのプログラムには、ひとつのことをうまくやらせる」というものがあります。 目的を最小限に抑えた小さなプログラムは、誰にとってもわかりやすく、保守も容易です。</p> <p>プログラムに限らず、この考えは1つの関数・1つのブロック・1行のコードにも当てはまります。 複数の目的を持った処理は理解しづらいものです。</p> <p>今回の例では、「在庫を減らす」操作と「売り切れの商品を抽出する」操作が同時に行われているため、 複雑なコードに見えます。</p> <h3>本来の目的と違う使い方をしているから、わかりづらい</h3> <p>filter関数は、与えられた条件に当てはまるデータを抽出(フィルタリング)するために利用します<sup id="fnref:1"><a href="#fn:1" rel="footnote">1</a></sup>。 「フィルターをする」処理をお願いしたのに、在庫を減らす処理も同時にされてしまうのは、驚きがあるロジックです。</p> <p>mapやreduceも同様で、「渡されたリストを別のものにマッピングする」「渡されたリストを畳み込む」以外の挙動をさせるのは、理解しづらいコードです。</p> <h3>広い範囲に影響を及ぼすロジックは、わかりづらい</h3> <p>呼び出すたびに中身が書き変えられるロジックは、わかりにくいです。</p> <p>例えば、次のようなメソッド呼び出しで中身も書き換えられてしまったら、直感に反する理解しづらいコードではないでしょうか。</p> <pre class="code lang-javascript" data-lang="javascript" data-unlink>console.log(product); <span class="synComment">// { name: &quot;商品1&quot;, quantity: 3 }</span> <span class="synStatement">const</span> quantity = getQuantity(product); console.log(product); <span class="synComment">// { name: &quot;商品1&quot;, quantity: 2 } !? まさか中身が書き換わっているとは</span> </pre> <pre class="code lang-javascript" data-lang="javascript" data-unlink><span class="synIdentifier">function</span> getQuantity(product) <span class="synIdentifier">{</span> product.quantity -= 1; <span class="synComment">// 渡された引数の中身を破壊している</span> <span class="synStatement">return</span> product.quantity; <span class="synIdentifier">}</span> </pre> <p>これは極端な例ですが、今回の例にあるfilterの使い方も、同じようなわかりづらさがあります。</p> <h1>じゃあ、どうすればいいの?</h1> <p>どのように修正すればよいでしょうか。 コード断片だけを見て、適切なアドバイスをするのは難しいです。 在庫を減らすという処理は、別のところで、あらかじめしておくべきでしょうか。設計から見直す必要があるかもしれません。</p> <p>それでも直すとしたら、以下のようになるでしょうか。</p> <pre class="code lang-javascript" data-lang="javascript" data-unlink>products.forEach(product =&gt; <span class="synIdentifier">{</span> <span class="synComment">// どうしても値を書き換えたかったら、eachを利用する</span> product.quantity -= 1; <span class="synIdentifier">}</span>); <span class="synStatement">const</span> soldOutProducts = products.filter(product =&gt; product.quantity &lt;= 0); </pre> <p>値を書き換える必要があるのなら、forやforEachを使います。 もちろん、書き換えずにすむなら、それに越したことはありません。</p> <h1>おわりに</h1> <p>map,filter,reduce関数内で値を書き換えてしまう違和感を、言語化してみました。 これらの関数をfor(each)と同じ感覚で使ってしまう方が、案外多いように思えます。</p> <p>今回紹介したメソッドは、あくまで一例です。 例えば、Rubyの<a href="https://docs.ruby-lang.org/ja/latest/class/Enumerable.html">Enumerable</a>にあるようなメソッドで値を書き換えるのは、避けるべき実装です。</p> <p>この問題は、コードレビューの時点で、私自身もすんなり理由を回答できませんでした。 あたりまえだと思っていることを見直すことで、自分自身の理解もより一層深めることができました。</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fwww.wantedly.com%2Fprojects%2F448840" title="急成長のプロダクト、食べチョクのWebエンジニアをwanted! by 株式会社ビビッドガーデン" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;"></iframe><cite class="hatena-citation"><a href="https://www.wantedly.com/projects/448840">www.wantedly.com</a></cite></p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fwww.wantedly.com%2Fprojects%2F570150" title="大注目の農業系ITスタートアップで、ひとり目のQAエンジニアを大募集! by 株式会社ビビッドガーデン" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;"></iframe><cite class="hatena-citation"><a href="https://www.wantedly.com/projects/570150">www.wantedly.com</a></cite></p> <div class="footnotes"> <hr/> <ol> <li id="fn:1"> <p>余談ですが、この記事を書くためにリーダブルコードを読み返していました。第3章に、filterという名前は「選択する」のか「除外する」のかあいまいだから避けるべき、との指摘をみつけて、確かに!と納得しました。ただ、JavaScriptにselectはないので、しょうがない。<a href="#fnref:1" rev="footnote">&#8617;</a></p></li> </ol> </div> vividgarden-tech 会社の支給PCがMacBook Pro M1なので、新しく開発環境を構築した話 hatenablog://entry/26006613675773575 2021-01-15T15:01:03+09:00 2021-01-15T15:01:03+09:00 こんにちは。 今年の年始からジョインした遠藤です。 さて、入社したところ会社支給のMacBook ProがM1チップのものでした。 はい、現状は開発環境で苦労するとか色々噂を聞くやつです。 実際に試したのですが、 現状の開発環境構築スクリプト、手順書が一切使えない VitualBox, Vagrantは利用不可 Dockerは利用可能ではあるが、一部イメージが対応されてない 古いパッケージは動かす手段がない などなど、通常ではぶつからない問題にぶつかります。 食べチョクでは、 Ruby Node.js MySQL Redis ElasticSearch Kibana を利用しています。 この辺… <p>こんにちは。</p> <p>今年の年始からジョインした遠藤です。</p> <p>さて、入社したところ会社支給のMacBook ProがM1チップのものでした。</p> <p>はい、現状は開発環境で苦労するとか色々噂を聞くやつです。</p> <p>実際に試したのですが、</p> <ul> <li>現状の開発環境構築スクリプト、手順書が一切使えない</li> <li>VitualBox, Vagrantは利用不可</li> <li>Dockerは利用可能ではあるが、一部イメージが対応されてない</li> <li>古いパッケージは動かす手段がない</li> </ul> <p>などなど、通常ではぶつからない問題にぶつかります。</p> <p>食べチョクでは、</p> <ul> <li>Ruby</li> <li>Node.js</li> <li>MySQL</li> <li>Redis</li> <li>ElasticSearch</li> <li>Kibana</li> </ul> <p>を利用しています。</p> <p>この辺りをメインに話つつ、Intel版とこんな風に違うのかっていう辺りの雰囲気を感じ取っていただければと思います。</p> <h2>どこに開発環境を構築するか</h2> <p>まず、どこで開発環境を構築するかを考えてみたいと思います。</p> <ul> <li>ローカルで開発環境を作る</li> <li>仮想化ソフトウェア上で作る <ul> <li>VirtualBox🙅‍♂️</li> <li>Docker</li> </ul> </li> <li>サーバー立ててその中に開発環境を作成して、リモートで作業する</li> </ul> <p>この3種類になると思いました。</p> <p>支給されたPCだとローカルに開発環境を構築される方が多いのかなって印象が多いのですが、どうなのでしょうか。</p> <p>私は支給されたPCだとローカルに開発環境を構築したい派なので、ローカルでの開発環境構築をひとまず目標にしました。</p> <p>今回は「ローカルで開発環境を作る」「仮想化ソフトウェア上で作る」で試しました。</p> <p>では、開発環境の構築をしていきます。</p> <h2>ローカルで開発環境を作る</h2> <p>ここから実際に行ったことを記述していきたいと思います。</p> <h3>homebrew</h3> <p>みんな大好きのhomebrewですが、きちんと<strong>公式のドキュメント</strong>を読みましょう。</p> <p>インストールする場所がm1 macの場合は別になっています。</p> <pre class="code lang-sh" data-lang="sh" data-unlink><span class="synStatement">mkdir</span> /opt/homebrew &amp;&amp; curl <span class="synSpecial">-L</span> https://github.com/Homebrew/brew/tarball/master | tar xz <span class="synSpecial">--strip</span> <span class="synConstant">1</span> <span class="synSpecial">-C</span> homebrew <span class="synStatement">echo</span><span class="synConstant"> </span><span class="synStatement">&quot;</span><span class="synConstant">export PATH=/opt/homebrew/bin:</span><span class="synPreProc">$PATH</span><span class="synStatement">&quot;</span><span class="synConstant"> </span><span class="synStatement">&gt;&gt;</span> ~/.zshrc <span class="synStatement">source</span> .zshrc </pre> <blockquote><p>However do yourself a favour and install to /usr/local on macOS Intel, /opt/homebrew on macOS ARM, and /home/linuxbrew/.linuxbrew on Linux. Some things may not build when installed elsewhere. One of the reasons Homebrew just works relative to the competition is because we recommend installing here. Pick another prefix at your peril!</p></blockquote> <p><a href="https://docs.brew.sh/Installation">Installation &mdash; Homebrew Documentation</a></p> <p>Intel版ならディレクトリを明示するとかなかったので、いつもと違うなっていうことを、最初から感じさせてもらえます。</p> <h3>rbenv</h3> <p>Rubyのバージョン管理ツールのrbenvです。</p> <p>こちらがないとRubyのバージョン管理で困るので、導入しましょう。</p> <p><a href="https://github.com/rbenv/rbenv#homebrew-on-macos">GitHub - rbenv/rbenv: Groom your app&rsquo;s Ruby environment</a></p> <pre class="code lang-sh" data-lang="sh" data-unlink>brew install rbenv rbenv init curl <span class="synSpecial">-fsSL</span> https://github.com/rbenv/rbenv-installer/raw/master/bin/rbenv-doctor | bash </pre> <p>ここまでは問題なくいきます。</p> <p>さて、待望のインストールをします!?</p> <pre class="code lang-sh" data-lang="sh" data-unlink>rbenv install <span class="synConstant">2</span>.<span class="synConstant">5</span>.<span class="synConstant">7</span> Downloading openssl-1.<span class="synConstant">1</span>.1i.tar.gz... -<span class="synStatement">&gt;</span> https://dqw8nmjcqpjn7.cloudfront.net/e8be6a35fe41d10603c3cc635e93289ed00bf34b79671a3a4de64fcee00d5242 Installing openssl-1.<span class="synConstant">1</span>.1i... Installed openssl-1.<span class="synConstant">1</span>.1i to /Users/xxx/.rbenv/versions/<span class="synConstant">2</span>.<span class="synConstant">5</span>.<span class="synConstant">7</span> Downloading ruby-2.<span class="synConstant">5</span>.<span class="synConstant">7</span>.tar.bz2... -<span class="synStatement">&gt;</span> https://cache.ruby-lang.org/pub/ruby/<span class="synConstant">2</span>.<span class="synConstant">5</span>/ruby-2.<span class="synConstant">5</span>.<span class="synConstant">7</span>.tar.bz2 Installing ruby-2.<span class="synConstant">5</span>.<span class="synConstant">7</span>... WARNING: ruby-2.<span class="synConstant">5</span>.<span class="synConstant">7</span> is nearing its end of life. It only receives critical security updates, no bug fixes. ruby-build: using readline from homebrew BUILD FAILED <span class="synPreProc">(</span><span class="synSpecial">macOS </span><span class="synConstant">11</span><span class="synSpecial">.</span><span class="synConstant">0</span><span class="synSpecial">.</span><span class="synConstant">1</span><span class="synSpecial"> using ruby-build </span><span class="synConstant">20201225</span><span class="synPreProc">)</span> Inspect or clean up the working tree at /var/folders/np/m8mjm0q52njgyqc6kp0b_zw80000gn/T/ruby-build.<span class="synConstant">20210110220451</span>.<span class="synConstant">39283</span>.BzZl54 Results logged to /var/folders/np/m8mjm0q52njgyqc6kp0b_zw80000gn/T/ruby-build.<span class="synConstant">20210110220451</span>.<span class="synConstant">39283</span>.log Last <span class="synConstant">10</span> log lines: compiling ../.././ext/psych/yaml/reader.c <span class="synConstant">3</span> warnings generated. compiling ../.././ext/psych/yaml/emitter.c compiling ../.././ext/psych/yaml/parser.c linking shared-object date_core.bundle <span class="synConstant">5</span> warnings generated. linking shared-object zlib.bundle <span class="synConstant">1</span> warning generated. linking shared-object psych.bundle make: *** <span class="synStatement">[</span>build-ext<span class="synStatement">]</span> Error <span class="synConstant">2</span> </pre> <p>ログファイルを確認します。</p> <pre class="code lang-sh" data-lang="sh" data-unlink> compiling qsort.c linking shared-object <span class="synSpecial">-test-</span>/vm/at_exit.bundle linking shared-object <span class="synSpecial">-test-</span>/wait_for_single_fd.bundle compiling closure.c compiling nofree.c compiling conversions.c compiling zlib.c installing default libraries compiling fiddle.c compiling psych_to_ruby.c closure.c:264:14: error: implicit declaration of <span class="synIdentifier">function</span> <span class="synStatement">'</span><span class="synConstant">ffi_prep_closure</span><span class="synStatement">'</span> is invalid <span class="synError">in</span> C99 <span class="synStatement">[</span>-Werror,-Wimplicit-function-declaration<span class="synStatement">]</span> result <span class="synStatement">=</span> ffi_prep_closure<span class="synPreProc">(</span><span class="synSpecial">pcl, cif, callback, </span><span class="synPreProc">(</span><span class="synSpecial">void *</span><span class="synPreProc">)</span><span class="synSpecial">self</span><span class="synPreProc">)</span>; ^ <span class="synConstant">1</span> error generated. make<span class="synStatement">[</span><span class="synConstant">2</span><span class="synStatement">]</span>: *** <span class="synStatement">[</span>closure.o<span class="synStatement">]</span> Error <span class="synConstant">1</span> make<span class="synStatement">[</span><span class="synConstant">2</span><span class="synStatement">]</span>: *** Waiting <span class="synStatement">for</span> unfinished <span class="synStatement">jobs</span>.... </pre> <p>なんか今までにあまり起こったことのないエラーに遭遇します。</p> <p>ffiでエラーが起こっているのですが、libffiを除外すればインストールできます。</p> <p><a href="https://andre.arko.net/2020/06/30/building-ruby-on-arm64-macos/">Building Ruby on arm64 macOS</a></p> <p>この問題が2020/6/30に解決されていることに感動を受けつつ、除外しましょう。</p> <pre class="code lang-sh" data-lang="sh" data-unlink><span class="synIdentifier">RUBY_CFLAGS</span>=-DUSE_FFI_CLOSURE_ALLOC rbenv install <span class="synConstant">2</span>.<span class="synConstant">5</span>.<span class="synConstant">7</span> </pre> <p>コマンド自体はissueを参考にさせていただきました。(先人に感謝)</p> <p><a href="https://github.com/rbenv/ruby-build/issues/1699#issuecomment-752124220">2.6.6 on ARM64 &middot; Issue #1699 &middot; rbenv/ruby-build &middot; GitHub</a></p> <p>Ruby3.0の場合はなんの問題もなくインストールできます。</p> <h3>MySQL</h3> <pre class="code lang-sh" data-lang="sh" data-unlink>brew install mysql@<span class="synConstant">5</span>.<span class="synConstant">7</span> <span class="synStatement">echo</span><span class="synConstant"> </span><span class="synStatement">'</span><span class="synConstant">export PATH=&quot;/opt/homebrew/opt/mysql@5.7/bin:$PATH&quot;</span><span class="synStatement">'</span><span class="synConstant"> </span><span class="synStatement">&gt;&gt;</span> ~/.zshrc brew services <span class="synStatement">start</span> mysql@<span class="synConstant">5</span>.<span class="synConstant">7</span> </pre> <p>正直ここは何も問題がないので、休憩ゾーンになります。</p> <h3>Redis</h3> <pre class="code lang-sh" data-lang="sh" data-unlink>brew install redis brew services <span class="synStatement">start</span> redis </pre> <p>これも特に問題はありません。</p> <h3>Node.js</h3> <p>Node.jsは今のところversion 15以上じゃないと対応されてない状態なので、ここは素直にversion 15を使いました。</p> <p>webpackのコンパイルでしか利用しないので、まぁ、いいかという気持ちもあり。。。</p> <p><a href="https://github.com/nodejs/node/issues/36161">Nodejs 14.x doesn&#39;t compile on ARM OSX (M1) &middot; Issue #36161 &middot; nodejs/node &middot; GitHub</a></p> <p>弊社ではnodebrewを利用していたので、下記を参考にさせていただきました。</p> <pre class="code lang-sh" data-lang="sh" data-unlink>brew install nodebrew <span class="synStatement">echo</span><span class="synConstant"> </span><span class="synStatement">&quot;</span><span class="synConstant">export PATH=</span><span class="synPreProc">$HOME</span><span class="synConstant">/.nodebrew/current/bin:</span><span class="synPreProc">$PATH</span><span class="synStatement">&quot;</span><span class="synConstant"> </span><span class="synStatement">&gt;&gt;</span> ~/.zshrc </pre> <pre class="code lang-sh" data-lang="sh" data-unlink>vim <span class="synPreProc">$(</span><span class="synSpecial">which nodebrew</span><span class="synPreProc">)</span> sub system_info <span class="synSpecial">{</span> my <span class="synPreProc">$arch</span><span class="synStatement">;</span> my <span class="synPreProc">($sysname</span>, <span class="synPreProc">$machine)</span> <span class="synStatement">=</span> <span class="synPreProc">(</span>POSIX::uname<span class="synPreProc">)</span><span class="synStatement">[</span><span class="synConstant">0</span>, <span class="synConstant">4</span><span class="synStatement">];</span> <span class="synStatement">if </span> <span class="synPreProc">($machine</span> <span class="synStatement">=</span>~ m/x86_64<span class="synStatement">|</span>arm64/<span class="synPreProc">)</span> <span class="synSpecial">{</span> <span class="synPreProc">$arch</span> <span class="synStatement">=</span> <span class="synStatement">'</span><span class="synConstant">arm64</span><span class="synStatement">';</span> <span class="synSpecial">}</span> elsif <span class="synPreProc">($machine</span> <span class="synStatement">=</span>~ m/i<span class="synSpecial">\d</span>86/<span class="synPreProc">)</span> <span class="synSpecial">{</span> ... nodebrew compile v15.<span class="synConstant">5</span>.<span class="synConstant">0</span> </pre> <p><a href="https://codehex.hateblo.jp/entry/2020/12/03/194230">M1 Mac &#x3092;&#x8CFC;&#x5165;&#x3057;&#x3066; arm64 &#x7E1B;&#x308A;&#x3067;&#x30A4;&#x30F3;&#x30B9;&#x30C8;&#x30FC;&#x30EB;&#x3057;&#x305F;&#x3082;&#x306E; (&#x66F4;&#x65B0;&#x4E2D;) - &#x30A2;&#x30EB;&#x30D1;&#x30AB;&#x4E09;&#x9283;&#x58EB;</a></p> <h3>ElasticSearchのインストール</h3> <p>弊社では検索エンジンにElasticSearchを利用しているので、ElasticSearchをインストールします。</p> <pre class="code lang-sh" data-lang="sh" data-unlink>brew install <span class="synSpecial">--build-from-source</span> elasticsearch ... <span class="synStatement">==&gt;</span> Installing dependencies <span class="synStatement">for</span> elasticsearch: openjdk and gradle <span class="synStatement">==&gt;</span> Installing elasticsearch dependency: openjdk <span class="synStatement">==&gt;</span> Patching <span class="synStatement">==&gt;</span> Applying f80a6066e45c3d53a61715abfe71abc3b2e162a1.patch patching file src/hotspot/share/runtime/sharedRuntime.cpp Hunk <span class="synComment">#1 succeeded at 2850 (offset -6 lines).</span> <span class="synStatement">==&gt;</span> Applying 4622a18a72c30c4fc72c166bee7de42903e1d036.patch patching file src/java.desktop/macosx/native/libawt_lwawt/awt/CSystemColors.m <span class="synStatement">==&gt;</span> ./configure <span class="synSpecial">--without-version-pre</span> <span class="synSpecial">--without-version-opt</span> <span class="synSpecial">--with-version-build=9</span> <span class="synSpecial">--with-toolchain-path=/usr/bin</span> <span class="synSpecial">--with-sysroot=/Library/Developer/Com</span> Last <span class="synConstant">15</span> lines from /Users/endo/Library/Logs/Homebrew/openjdk/<span class="synConstant">01</span>.configure: checking <span class="synStatement">for</span> stdlib.h... yes checking <span class="synStatement">for</span> string.h... yes checking <span class="synStatement">for</span> memory.h... yes checking <span class="synStatement">for</span> strings.h... yes checking <span class="synStatement">for</span> inttypes.h... yes checking <span class="synStatement">for</span> stdint.h... yes checking <span class="synStatement">for</span> unistd.h... yes checking stdio.h usability... yes checking stdio.h presence... yes checking <span class="synStatement">for</span> stdio.h... yes checking size of int *... <span class="synConstant">8</span> configure: The tested number of bits <span class="synStatement">in</span> the target <span class="synPreProc">(</span><span class="synConstant">64</span><span class="synPreProc">)</span> differs from the number of bits expected to be found <span class="synStatement">in</span> the target <span class="synPreProc">(</span><span class="synConstant">32</span><span class="synPreProc">)</span> configure: error: Cannot <span class="synStatement">continue</span>. /private/tmp/openjdk-20210110-26180-4xtn4y/jdk15u-jdk-15.<span class="synConstant">0</span>.1-ga/build/.configure-support/generated-configure.sh: line 82: 5: Bad file descriptor configure exiting with result code <span class="synConstant">1</span> Do not report this issue to Homebrew/brew or Homebrew/core<span class="synStatement">!</span> These open issues may also help: Cassandra <span class="synConstant">3</span>.<span class="synConstant">11</span>.9_1 crashes <span class="synStatement">in</span> openjdk@<span class="synConstant">8</span> JVM https://github.com/Homebrew/homebrew-core/issues/<span class="synConstant">66462</span> openjdk: Add support <span class="synStatement">for</span> Apple silicon https://github.com/Homebrew/homebrew-core/pull/<span class="synConstant">65670</span> OpenJDK is somewhat broken on newer MacOS instances, console is flooded with errors when using JMeter, AdoptOpenJDK has no issues https://github.com/Homebrew/homebrew-core/issues/<span class="synConstant">66953</span> </pre> <p>案の定エラーになります。</p> <p>原因はOpenJDKがサポートされてないので、利用できません。</p> <p><a href="https://github.com/Homebrew/homebrew-core/pull/65670">https://github.com/Homebrew/homebrew-core/pull/65670</a></p> <p>このPR早くマージしてくれ!という気持ちもありつつ、OpenJDKはZuluがM1対応しているので、そちらを利用します。</p> <p><a href="https://www.azul.com/press_release/azul-announces-support-of-java-builds-of-openjdk-for-apple-silicon/">Support of Java Builds of OpenJDK for Apple Silicon | Azul</a></p> <p><a href="https://www.azul.com/downloads/zulu-community/?package=jdk">Java Download | Java 8, Java 11, Java 13 - Linux, Windows &amp; macOS</a></p> <p><strong>※記事執筆中にマージされていましたので、この問題は解決していましたが、<a href="https://github.com/Homebrew/homebrew-core/pull/69029">https://github.com/Homebrew/homebrew-core/pull/69029</a>で別の問題が発生しています</strong></p> <p>ElasticSearchはソースコードからビルドするように変更します。</p> <pre class="code lang-sh" data-lang="sh" data-unlink>wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-7.<span class="synConstant">10</span>.1-darwin-x86_64.tar.gz wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-7.<span class="synConstant">10</span>.1-darwin-x86_64.tar.gz.sha512 shasum <span class="synSpecial">-a</span> <span class="synConstant">512</span> <span class="synSpecial">-c</span> elasticsearch-7.<span class="synConstant">10</span>.1-darwin-x86_64.tar.gz.sha512 tar <span class="synSpecial">-xzf</span> elasticsearch-7.<span class="synConstant">10</span>.1-darwin-x86_64.tar.gz <span class="synStatement">cd</span> elasticsearch-7.<span class="synConstant">10</span>.<span class="synConstant">1</span>/ </pre> <p>Javaの参照先をZuluに追加します。</p> <p><a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/setup.html">Set up Elasticsearch | Elasticsearch Reference [7.10] | Elastic</a></p> <pre class="code lang-sh" data-lang="sh" data-unlink><span class="synStatement">echo</span><span class="synConstant"> </span><span class="synStatement">&quot;</span><span class="synConstant">export JAVA_HOME=/Library/Java/JavaVirtualMachines/zulu-15.jdk/Contents/Home</span><span class="synStatement">&quot;</span><span class="synConstant"> </span><span class="synStatement">&gt;&gt;</span> ~/.zshrc </pre> <p>これで<code>bin/elastic search</code>で動くのですが、xpackがサポートされないので、コンフィグから外しましょう。</p> <pre class="code lang-sh" data-lang="sh" data-unlink>org.elasticsearch.ElasticsearchException: X-Pack is not supported and Machine Learning is not available <span class="synStatement">for</span> <span class="synStatement">[</span>darwin-aarch64<span class="synStatement">];</span> you can use the other X-Pack features <span class="synPreProc">(</span>unsupported<span class="synPreProc">)</span> by setting xpack.ml.enabled: <span class="synStatement">false</span> <span class="synStatement">in</span> elasticsearch.yml </pre> <h3>Kibana</h3> <p>ElasticSearchを入れるので、Kibanaも入れましょう。</p> <p>homebrew経由でインストールしようとすると、案の定エラーになるのでソースコードからビルドします。</p> <pre class="code lang-sh" data-lang="sh" data-unlink>curl <span class="synSpecial">-O</span> https://artifacts.elastic.co/downloads/kibana/kibana-7.<span class="synConstant">10</span>.1-darwin-x86_64.tar.gz curl https://artifacts.elastic.co/downloads/kibana/kibana-7.<span class="synConstant">10</span>.1-darwin-x86_64.tar.gz.sha512 | shasum <span class="synSpecial">-a</span> <span class="synConstant">512</span> <span class="synSpecial">-c</span> - tar <span class="synSpecial">-xzf</span> kibana-7.<span class="synConstant">10</span>.1-darwin-x86_64.tar.gz <span class="synStatement">cd</span> kibana-7.<span class="synConstant">10</span>.1-darwin-x86_64/ bin/kibana </pre> <p>ここまで揃うと、なんとか<code>bundle exec rails server</code>までは辿りつけると思います。</p> <p>ここまでがローカル開発環境編です。</p> <h3>仮想化ソフトウェア上で作る</h3> <p>現状はVirtual Boxが対応していないので、Docker Previewが頼みの綱になります。</p> <p>Docker Previewはrosettaが現状は必要になるので、rosettaをインストールします。</p> <pre class="code lang-sh" data-lang="sh" data-unlink>softwareupdate <span class="synSpecial">--install-rosetta</span> </pre> <p>Docker Previewのダウンロードは下記にリンクがあるので、そちらかダウンロードします。</p> <p><a href="https://www.docker.com/blog/download-and-try-the-tech-preview-of-docker-desktop-for-m1/">Download and Try the Tech Preview of Docker Desktop for M1 - Docker Blog</a></p> <p>ここではうまくいきづらいものだけ紹介します。</p> <p>docker-composeを利用することを前提にしていきます。</p> <h3>MySQL</h3> <p>設定ファイルです。</p> <pre class="code lang-yaml" data-lang="yaml" data-unlink> <span class="synIdentifier">services</span><span class="synSpecial">:</span> <span class="synIdentifier">mysql</span><span class="synSpecial">:</span> <span class="synIdentifier">image</span><span class="synSpecial">:</span> mysql:5.7 </pre> <pre class="code lang-sh" data-lang="sh" data-unlink>docker-compose up Pulling mysql <span class="synPreProc">(</span><span class="synSpecial">mysql:5.</span><span class="synConstant">7</span><span class="synPreProc">)</span>... <span class="synConstant">5</span>.7: Pulling from library/mysql ERROR: no matching manifest <span class="synStatement">for</span> linux/arm64/v8 <span class="synStatement">in</span> the manifest list entries </pre> <p>ということで、イメージとして対応していないです。</p> <p><a href="https://github.com/docker/for-mac/issues/5142">no matching manifest for linux/arm64/v8 in the manifest list entries &middot; Issue #5142 &middot; docker/for-mac &middot; GitHub</a></p> <p>が、issueを見ると<code>MariaDB</code>を使うか、特定のIntelのやつを使えばいけるということで、変更します。</p> <pre class="code lang-sh" data-lang="sh" data-unlink>services: mysql: image: mysql:5.<span class="synConstant">7</span>@sha256:b3b2703de646600b008cbb2de36b70b21e51e7e93a7fca450d2b08151658b2dd </pre> <h3>Kibana</h3> <p>arm64のイメージがないので、利用できませんでした。</p> <pre class="code lang-sh" data-lang="sh" data-unlink>docker-compose up WARNING: Found orphan containers <span class="synPreProc">(</span><span class="synSpecial">tabechoku_elasticsearch, tabechoku_redis, tabechoku_mysql</span><span class="synPreProc">)</span> <span class="synStatement">for</span> this project. If you removed or renamed this service <span class="synStatement">in</span> your compose file, you can run this <span class="synStatement">command</span> with the <span class="synSpecial">--remove-orphans</span> flag to clean it up. Pulling kibana <span class="synPreProc">(</span><span class="synSpecial">kibana:7.</span><span class="synConstant">10</span><span class="synSpecial">.</span><span class="synConstant">1</span><span class="synPreProc">)</span>... <span class="synConstant">7</span>.<span class="synConstant">10</span>.1: Pulling from library/kibana ERROR: no matching manifest <span class="synStatement">for</span> linux/arm64/v8 <span class="synStatement">in</span> the manifest list entries </pre> <p><a href="https://hub.docker.com/_/kibana">Docker Hub</a></p> <p>ElasticSearch/Redisに関しては問題なかったので、割愛させていただきます。</p> <h2>結論</h2> <p>正直なんとか開発状態までは漕ぎ着けれるのですが、どっちかというと現場で作業を行う場合に<strong>最新のバージョン</strong>になっていないことが多いので、そこをどうするかというのが、ポイントになるかと思います。</p> <p>明らかに対応外なら、割り切って最新のを使うようにするしかないです。</p> <p>Dockerの場合は、利用できない可能性もあるのでそこも割り切るしかないのかなと思います。</p> <p>この辺りで本番環境と乖離が起こる可能性が高いので、どこまでを許容とするか、あるいは本番環境を合わせるかになるのかなと思います。</p> <p>また、今回は先人の知恵があり、なんとかなりました。</p> <ul> <li>解決方法を書いてくれている方</li> <li>M1に対応しようとOSSで対応している方</li> </ul> <p>など、実際に対応されている方には、感謝しかありません。</p> <p>よく言われていることですが、<strong>開発環境、ライブラリは常日頃から最新化しておかないと苦労する </strong>を痛感します。</p> <p>こういった問題に解決するためにも今はチームとして頑張っています。</p> <p><strong>※この記事を書いている間にもRuby 2.6.6にアップデートし、今はRuby 2.7系にアップデートに取り組んでいます。</strong></p> <p>弊社ではM1 Macbook Proで開発をするエンジニアを募集しております。</p> <p><iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fwww.wantedly.com%2Fprojects%2F448840" title="急成長のプロダクト、食べチョクのWebエンジニアをwanted! by 株式会社ビビッドガーデン" class="embed-card embed-webcard" scrolling="no" frameborder="0" style="display: block; width: 100%; height: 155px; max-width: 500px; margin: 10px 0px;"></iframe><cite class="hatena-citation"><a href="https://www.wantedly.com/projects/448840">www.wantedly.com</a></cite></p> vividgarden-tech