Upgrade to Pro — share decks privately, control downloads, hide ads and more …

事業の試行錯誤を支える コードを捨てやすくして システムをシンプルに保つ設計と工夫

zuckey_17
October 27, 2023

事業の試行錯誤を支える コードを捨てやすくして システムをシンプルに保つ設計と工夫

Kaigi on Rails 2023 での発表資料です。

https://kaigionrails.org/2023/talks/zuckey/

zuckey_17

October 27, 2023
Tweet

More Decks by zuckey_17

Other Decks in Programming

Transcript

  1. 2 松村 和輝 @zuckey_17 🙅「ざっきー」 🙆「ずっきー」 ⾃⼰紹介 https://twitter.com/zuckey_17 https://github.com/zuckeyM-17 https://blog.zuckey17.org/

    https://medium.com/@kazuki.matsumura 2020年1⽉~現在 株式会社スタディスト • ⼩売企業向けの新規事業 『ハンクラ』の⽴ち上げリードエンジニアとして⼊社 • 現在は開発チームのマネージャーとプロダクトマネージャーを兼務
  2. 5 新規事業でRuby on Railsを採⽤したときの頭の中(2020年当時) • 少⼈数での開発でも⾼い⽣産性を出しやすい • 企業向けで、特定業種向けのサービスのため、爆発的に、想定以上にトラフィックが 伸びる可能性は低そう •

    すでに他プロダクトでRailsで使われており、⼈の追加やスワップが容易そう • Ruby on Rails が zuckeyが最も慣れているかつ、好きなフレームワークだ スピード感を持って 事業の試⾏錯誤を⽀えることができる
  3. • パイロット顧客(2, 3社)を⾒つける ◦ = プロダクトのコンセプトに共感してくれて、実際に現場で利⽤してくれる顧客 • 「この⼈たちの業務を楽にするために開発していくぞ!!」 ◦ 実業務の利⽤による貴重なフィードバックがありがたい

    ◦ 実際に楽になった、便利だったという声が嬉しい ◦ 売上が⽴つ、嬉しい • 特定顧客への特殊(かもしれない)業務に寄り添う ◦ ⽴ち上げ初期では継続的な進化(機能追加)への期待が⼤きい ◦ ただ、顧客⾃⾝もその機能が本当に必要かどうかは知らない 新規事業⽴ち上げ初期の開発の様⼦ 6
  4. 変化に強いソフトウェアをつくるための多くの概念やキーワードを⾒かける。 • SOLID原則 • 結合度‧凝集度 • DDD(Domain Driven Design) •

    進化的アーキテクチャ • など チームでの共有‧教育には時間がかかる。 営業‧カスタマーサクセスのチームを含めた場で伝えるのが難しい。 変化に強いソフトウェア 10 Design Principles and Design Patterns 構造化設計 Domain-Driven Design: Tackling Complexity in the Heart of Software 進化的アーキテクチャ 絶え間ない変化を⽀える
  5. 事例1. 閲覧する⼈、閲覧タイミングによって得たい情報は異なる 22 時間軸 施策終了 【商品部】 商品が売れ、売上が上が ることに責任を持つ 陳列までのリードタイムが 気になる

    【店舗運営部】 店舗の健全な運営、コストの 削減に責任を持つ 作業負荷が想定通りで 再提出が異常に多くないか 【実施中】 思いもよらない不具合があり、 緊急対応をする可能性 督促をする閾値を越えないか 【終了後】 売上実績と比較してどうか 他の施策と比べてどうか
  6. 23 事例1. ⾒せたい場所に応じてAPIは分けたい SchemeStats Contoroller SchemeStat Serializer SchemeSummary < AR::Base

    UpdateSchemeSummaryJob scheme_summaries SchemeProgresses Contoroller SchemeProgress Serializer SchemeSalesReports Contoroller SchemeSalesReport Serializer Sales 系 Model すべてのデータが⼊った scheme_sumamries からそ れぞれの利⽤箇所で必要なデータを取り出して使う
  7. 24 事例1. 画⾯が不要になったら部分的に削除 SchemeStats Contoroller SchemeStat Serializer SchemeSummary < AR::Base

    UpdateSchemeSummaryJob scheme_summaries SchemeProgresses Contoroller SchemeProgress Serializer SchemeSalesReport Serializer SchemeSalesReports Contoroller Sales 系 Model どのカラムが必要なくなったのかが ⾃明ではなく、不要になったロジックの 消し忘れが発⽣してしまう可能性もある 売上系のデータは顧客の基幹システムから 落として、Excelで分析するので、この画⾯はいらない
  8. 事例1. 1つのテーブルを使い回すのではなく⽤途に対してテーブルを作る SchemeStats Contoroller SchemeStat Serializer SchemeProgress < AR::Base scheme_progresses

    SchemeProgresses Contoroller SchemeProgress Serializer SchemeSalesReports Contoroller SchemeSalesReport Serializer Salesのデータ SchemeStat < AR::Base scheme_stats SchemeSalesReport < AR::Base scheme_sales_reports UpdateScheme SalesReportJob UpdateSchemeStatJob UpdateSchemeProgressJob
  9. 26 事例1. こんな感じ Web Server 提出 enqueue UpdateSchemeStatJob UpdateSchemeProgressJob scheme_stats

    scheme_progresses scheme_sales_reports update Web Server Worker Batch enqueue UpdateSchemeSales ReportJob API create evidence
  10. 事例1. 画⾯が不要になったらテーブルごと削除 SchemeStats Contoroller SchemeStat Serializer SchemeProgress < AR::Base scheme_progresses

    SchemeProgresses Contoroller SchemeProgress Serializer SchemeSalesReports Contoroller SchemeSalesReport Serializer Salesのデータ SchemeStat < AR::Base scheme_stats SchemeSalesReport < AR::Base scheme_sales_reports UpdateScheme SalesReportJob UpdateSchemeStatJob UpdateSchemeProgressJob 売上系のデータは顧客の基幹システムから 落として、Excelで分析するので、この画⾯はいらない ファイルごと 削除すればOK
  11. 28 事例1. テーブルを中⼼に作ると削除に踏み切りやすい • 1つのテーブルを使い回すのではなく⽤途 に対してテーブルを作る • 素直に作ることができていれば、テーブル を削除したら更新‧参照しているController や

    Job もその単位で削除できる • ActiveRecord が中⼼にあるRails はこの考え ⽅があっていそう Contoroller Serializer Model (ActiveRecord::Bas e) Job Table Contoroller Serializer Model (AR::Base) Job Table
  12. 29 事例1. テーブルを中⼼に作ると削除に踏み切りやすい • 1つのテーブルを使い回すのではなく⽤途 に対してテーブルを作る • 素直に作ることができていれば、テーブル を削除したら更新‧参照しているController や

    Job もその単位で削除できる • ActiveRecord が中⼼にあるRails はこの考え ⽅があっていそう Contoroller Serializer Model (ActiveRecord::Bas e) Job Table Contoroller Serializer Model (AR::Base) Job Table ファイルやディレクトリ単位で削除できるようにする 影響範囲の考慮漏れのリスクが少ない => 削除のハードルを下げる
  13. 32 • 企業テーブルに企業種別(⼩売企業 or メー カー)をつけて、画⾯上では種別ごとに情報 を適宜出し分ける • ⼩売本部ユーザー /

    メーカーユーザーでテー ブル、認証などのロジックは共有して省エネ 事例2. 最短で実現したいなと思ったとき
  14. • 「“ほとんど”⾒るものは同じ」というのは、⽢すぎるヨミ ◦ 実際に使ってみたら、⼩売だけ、メーカーだけに表⽰したいものが多々あった • もし、カラムの企業種別で分けていたら... ◦ View / Serializer

    などの表⽰部分に分岐が増える ◦ v-if=”retailer.isRetailer(or Maker)”とかになるのはつらい... ▪ メーカーを削除するとき、削除漏れが発⽣する可能性が⾼まる 35 事例2. ユーザーが違う = 表⽰部分に分岐が増える
  15. 37 • メーカー関連のコードをディレクトリ / ファイルごと削除する ◦ frontend/maker ◦ app/controllers|serializers|jobs|views|mailers/maker ◦

    spec/requests|jobs/maker ◦ app|spec/models|factories/maker.rb|maker_user.rb|scheme_m aker.rb|maker_user_authentication.rb ◦ config/routes/maker.rb 事例2. メーカーと名のつくものをほぼノールックで削除
  16. 38 • メーカー関連のコードをディレクトリ / ファイルごと削除する ◦ frontend/maker ◦ config/routes/maker.rb ◦

    app/controllers|serializers|jobs|views|mailers/maker ◦ spec/requests|jobs/maker ◦ app|spec/models|factories/maker.rb|maker_user.rb|scheme_m aker.rb|maker_user_authentication.rb 事例2. メーカーと名のつくものをほぼノールックで削除 表示部分も含めて冗長だなと思っていても 別々に作っていれば、 この単位で削除することができる。
  17. 40 • そもそも混ぜるな危険なものを混ぜない ◦ ⽬的ごちゃ混ぜのサマリテーブルを作ったり ◦ retailers テーブルに 企業種別で ⼩売

    or メーカーのカラムをもたせたり • 短期的に記述量が少なく⾒えて速そうでも、削除まで考えるとむしろ遅い ◦ 分けるべき概念を、コードの分岐を増やさずに根っこから分ける ◦ 冗⻑に⾒えることもあるが分岐が増えそうであれば疑ってみる 削除を前提に設計する
  18. 41 • そもそも混ぜるな危険なものを混ぜない ◦ ⽬的ごちゃ混ぜのサマリテーブルを作ったり ◦ retailers テーブルに 企業種別で ⼩売

    or メーカーの情報をもたせたり • 短期的に記述量が少なく⾒えて速そうでも、削除まで考えるとむしろ遅い ◦ 分けるべき概念を、コードの分岐を増やさずに根っこから分ける ◦ 冗⻑に⾒えることもあるが分岐が増えそうであれば疑ってみる 削除を前提に設計する 削除することを織り込んでおけば 設計⼿法のエッセンスは取り⼊れられている。
  19. • minispec • 開発着前、詳細な設計の前に⽤意する 「仕様案」のようなもの ◦ 解決すべき課題、規模感を事前に把握 • KPIに活⽤度などを⼊れることによって、 思った通りにいかなかったときにとるアク

    ションを決めておく • 閲覧数‧利⽤頻度が思ったように増えなけ れば削除や⼿を⼊れることも検討 開発着⼿前に minispec を利⽤して期待値を調整する 48