🦁

主に機能開発をやっていて大事だと思うことのメモ(継ぎ足していく)

2022/05/10に公開

エンジニアになって半年たったので、機能開発の各工程で大切だと思うことをまとめておく

機能開発の工程

機能開発は下記のフェーズをループし続けていると考える

  • イシューの発見: 顧客が抱えているイシューを見つける
  • イシューの選定: インパクトが大きいかつ解決ができるイシューを選ぶ
  • ソリューションの勘案: 選んだイシューを解決する機能の概要を考える
  • 仕様の設計: 機能を実装する際の仕様を具体的に決める
  • データフローの設計: ユーザーインターフェースからデータストレージまで、各時点で用いるモデルと実施する処理の内容を決める
  • 機能の実装とテスト: 機能を実装し、仕様通りに動作することをテストにより担保する
  • 機能のリリース: 機能をプロダクトに追加し、ユーザーが使用できる状態にする
  • インパクトの評価: リリースされた機能がもたらしたインパクトを評価する

各工程で大事なこと

イシューの発見

イシューの選定

ソリューションの勘案

  • どんなニーズからどんなソリューションが生まれたのかをきちんとPdMなりに確認する。これが分かっていることで仕様の手戻りなどがだいぶ減る

仕様の設計

  • 仕様を考えながらデータフローを意識できているといい。誰がどのようなモデルをいつ、どのように生成して、どのような通信の後にどう処理されるのか。シーケンス図が思い浮かべられる/書きながら考えられると良い
  • 細かい詰めまで考え抜く。どうせ実装のときに考えることになるから、実装の想像とかもしながら限界まで考えてみる
  • 影響を受けるエンドポイントの洗い出し、他機能とのUIやデータの兼ね合い、そしてユビキタス言語の設定だけはしっかりやる。これだけはやらないと本当に二転三転する。これらのポイントだけは、意見が多い人にあらかじめ聞いておくのが大事
  • しょうもないプロトタイプでもいいから作ると良いことがある。作成イメージをFigmaじゃなくて適当な画像とか組み合わせて作れると早い
  • ブラウザの開発ツールでコードを書き換えるとプロトタイピングできることがある

データフローの設計

  • 誰がどのようなモデルをいつ、どのように生成して、どのような通信の後にどう処理されるのか、シーケンス図など書いて考える
  • DOAではなくOOAでいく。すなわち、まずはモデルを起点に、それから自然な永続化方法を考える。他の候補がないかは一つ固めてから考える。それから、根拠をつけておしまい。変に構造化とかは最初からしようと思うと遅くなる。あとで考えればいい
  • インターフェースのアラインはさっさと済ませる

機能の実装とテスト

一般論

  • 図を書いて考える
  • いまは考える時間なのか、作業する時間なのかを必ず問う。それをTODOに書いてもいいくらい。考える時間と作業する時間は必ず別で、考えるときにパソコンに向かわないし、作業するときに紙とペンは持たない。これは前職でも同じだったはず
    • コードをいきなり書かない。まずは紙とペンで考えて、固まってから、あとは手を動かすだけにする
  • コードにコメントを残すのは本当に大事。コメントがあるコードは読みやすい!
  • ADRを用いて非同期的に設計についての情報共有・意思決定ができると便利!
  • 既存のコードをいじるときは予期せぬ副作用や、周りの処理との順番に依存するモジュールがないかを確認する
  • リリース後にインパクトを評価できるような仕掛けをどこかに入れておく。または外から計測できるようにクエリを書いておくなどする
  • 特にフロントエンドなんかはそうだけど、まずは簡単に考え始める。FEでいうスタイルやBEでいうvalidationはあとから考えていく。問題を分解して考える
  • if-elseはelseに予期せぬケースが混ざることがある。ifがたくさん増えるような話もあるので、switchでdefaultを例外処理にするって感じにすると3ケースとかでも見やすい

ドメイン駆動設計

  • ドメインモデルにプリミティブ型を使わない。独自の定義型でドメインモデルを固めてvalidationをする。ビジネスロジックに適した型が絶対にあるはずなんだから、きちんと作る。あんたのとこの会社はint型の金額を扱うのか??
  • 各層の定数やメソッドをプライベートにすべきか、グローバルにすべきか意識する。スコープをきちんと狭めることが大事。例えばドメインモデルが持つべき定数やメソッドは大抵の場合プライベートにしておくべき
  • ドメインモデルの中に外部パッケージなどは使わない。ドメインモデルはビジネスの中核となる概念の置き場所であり、そこに外部の情報はないはず

BE開発

  • 2種類のエラーハンドリングを適切に実施する。まずはレスポンスを適切に返す。そして内部ログおよびアラートを適切に設定する。何かあったときに気づき、かつ追えることが大事
    • 主要なドメインエラーを定義しておき、それがエラーログに出るようにしつつ、ハンドラーで最後に一般的なエラーコードに対応させるという戦略が採りやすい
  • DBのレコードに「状態」を持たせない。状態が分かれるなら、テーブルごと分けてしまったほうがいい。例えば削除はフラグではなく、削除テーブルの用意をする
  • 集約を意識する。どのデータからどのデータまでをまとめて扱っておくべきなのか。例えばあるデータをDBから取ってくるときに、そのデータの翻訳データもともにDBから取ってきてまとめるなど
  • ループごとにDBへのアクセスをするなんて馬鹿げている。一括でまとめて持ってくる
  • 計算量を意識して実装できるといい。データベースへのアクセスの所要時間など、典型的な量について平均的な値をおさえておくといい
  • エンドポイントの定義の仕方について、RESTとはいえ、リソース単位ではメソッドだけで表現しきれないことがある。複数のドメインモデルをまたぐ処理なんかは、リソース+アクションになることも避けられないことがある

FE開発

  • コンポーネントのモードを考えるといい。モードによってコンポーネントごと分けると楽になる。例えばものによっては、編集中の状態と非編集時の状態は見た目もロジックもだいぶ違うことがある。そういうときはモードでコンポーネント単位の切り分けをするといい
  • FEは1msを争う世界。パフォーマンスが命だから、必ずチューニングまでやるようにする。そもそものレンダー数や再描画のきっかけ、遅いアニメーションを減らす。そのためにブラウザ開発ツールでメモリ状態をみたり、一時停止させたり、レンダーの理由をみたりといったことをする
  • 基本的にはデータ構造はBEに揃えたほうが良い。これはBEとの言語共通化にも役に立つ
  • コンポーネントのカラーも定数化して、ドメインレイヤーに定義しておくといい
  • CSSに慣れるためにflexとbox-sizingをとりあえずやってみるといい
  • ブラウザの開発ツールでコードを書き換えてから、本番コードを修正する。FEはこのフォードバック高速化ができるから強いということを忘れないようにする
  • JSでthisを使うとちょっと難しくなりがち。thisは使うとしてもthisが指したいものの直下で使えるといい。別で定義した関数にbindを使い始めるとバグが起きやすいのでやめたい!
  • ReactのErrorBoundaryをうまく使えるといい
  • フロントエンドの場合は特に、エラーが起きた時にユーザーに情報を表示し続けるのか、間違った情報が含まれるから全部もう消しちゃうのか、とかエラーハンドリングに気を遣わないといけない

テスト作成・実施

  • 単体・結合テストの題名は、何のどのような動作を担保しているのか具体的に書く
    • 特にsnapshotテストは何をテストしているのかわからなくなるときつい。snapshotテストでも、サボらずにちゃんと書こう
  • テストカバレッジは100ならいいってわけでもない。例えばブランチやラインのカバレッジまで100にしようとしたら余計なテストも多くなってしまうはず。無難なのは、最初の段階で納得できる網羅性をもったテスト群を書き、そのカバレッジから下がるたびに本当に下げて良いのかを考える運用
  • 必要条件でしかないテストを書いてしまっていないか確認する。テスト総体が仕様と「必要十分」にならないとダメ(厳しすぎてもいけない。ゆるすぎるよりは全然いいけど)
  • 本来、エンジニアはプログラムが正しく動くことを「証明」すべき。テストが数学的に必要十分であると構成されていることが理想
  • mockはmock化されているモジュールの内部状態を理想的なものとして仮定して作られる。なので、当たり前だがmockを使うと、mock化した向こう側の状態と結合したテストはできていないことになる。そのため、mockによるテストがあるなら、結合テストもまた必須である
    • 例えば、repositoryのmockはDBの遷移を想定して作られる。よってrepositoryメソッドを呼ぶ順番を間違えていてDBの状態がおかしくなるといったバグには気づけなくなる

機能のリリース

  • システムがダウンする場合、どのくらいの時間かを見積もる。サーバーの処理は、基本的に絶対に線形なんかではないと思ったほうが良い
  • 本番環境と全く同じ環境があるといい。これは開発環境とは違うもの。そこでリリースを本番と同じ手順で試したほうが良い
  • リリースログを残す。具体的には、リリースにあたっての手順を全て抜けなく書いたもの。これはあとで振り返るときにも役立つし、リリース時の作業における精神的な負荷も減る。cdコマンドとかも書くレベルで漏れなく書く

インパクトの評価

すべての工程で大事なこと

  • 自分の知的好奇心を満たして、創りたいものを面白いものを楽しいものを創れる楽しい仕事であることを忘れないようにする
  • ハッカーらしく生きる。憧れるかっこいいエンジニアみたいな感じで生きる。これはcoolなのか?professionalなのか?という審美眼を持つ
  • 技術者として、仕事が楽しくないなら何かおかしいと気づけるようにする。技術で解決できることはする
  • ダラダラ仕事せずに、朝のタスク整理と時間見積もり、優先順位付けをいままで通りやる
  • よくわからないコードは書かないし、よくわからないことは言わないようにする。これは本当に大事だし、難しいことだから気をつける
  • 謎の手作業や「もっと便利になるはず」「なんでこんなことになったんだ」という思いを逃さずに握っておく
  • スクラムはリリースにおいて自立している。CSレビューは承認ではなく、今後に向けたアドバイスでしかない
  • レビューコメントはHRTに気を配りながら書く
  • フィードバックサイクルを早く早くまわしていく。1日に多くの進捗を出すリズムをキープして働くし、リンターやデバッガーもとにかく早く回るようにする。CIも早く。フォードバックが回れば回るほどものはよくなる。リンターとか非対人のフィードバックを極限まで早くする
    • PRも小さく切って出していくべき。プロトタイプ的な段階で見せるのもあり
    • 実装に限らず、どんなこともとにかくいい感じのひとまとまりで大きくしすぎずに確認をとっていく。とにかく、フィードバックループを早くまわして確定させていく
  • これ絶対必要だろってもの、あったらいいなと思うものはまず検索してみる。9割ある
  • バグ修正は仮説を持ってから取り組むようにする。すなわち、それぞれのレイヤーで、なんのコンポーネントがどう連携して処理が行われてるかをまず思い浮かべ、どこで何が起こると現象が再現されるか考える
  • FEなど、いろいろな仕事をどんどんやるといい。エンジニアを始めたばかりなのだから、色々な世界を体験したほうがいい
  • なんだかんだドメイン知識が相当大事よ
  • 使っているソフトウェアやライブラリのバージョンに気をつける(特にMySQLとか)
  • 技術の議論でもイシューを見失わないことが大事なのはビジネスと同じだと思う。まあそりゃそうなんだけど
  • 言葉の粒度を極限まで細かくしながら、ファクトをもとに議論することが大事。例えば「バックエンドのコードはなんか読みづらい」と言うのではなく、「usecase層のロジックを中心に、ドメインモデルのメソッドの命名が実際の処理とずれていること、また、メソッドが適切な範囲で分割されていないことがあり、読みづらい」と言うべき
  • みんなで協力的に議論すれば、多少イシューの言語化方法や議論の進め方に違和感があっても、途中でいくらでも方向修正できることが実感できた。最初から完璧にやろうとするよりも、みんなで楽しく議論していろいろな発想が出ることのほうが大事なのかもしれない。バランスだと思う
GitHubで編集を提案

Discussion