じぶん対策

日々学んだことをアウトプットして備忘録にしています。

DRY原則とそのトレードオフ

はじめに

おそらくもっとも有名なソフトウェア工学の原則の1つに、DRY(Don't Repeat Yourself)原則があります。DRY原則は、同じコードを何度も書かないようにすることを目指しています。この記事では、DRY原則の重要性とそのトレードオフについて考えてみます。

参考

ソフトウェア設計のトレードオフと誤り

DRY原則とは?

DRY原則(Don't Repeat Yourself)は、ソフトウェア開発における原則の一つです。

重複したコードを避け、同じコードを複数の場所で書かないようにすることを推奨しています。

DRY原則を遵守することで得られる主なメリットは次のとおりです。

  1. 保守性の向上: 同じコードが複数の場所に存在しないため、修正が必要な場合に一箇所だけ変更すれば済みます。これにより、コードの保守性が向上します。
  2. 可読性の向上: 重複するコードがないため、コードの構造が明確になり、読みやすくなります。
  3. バグの減少: 重複したコードを修正する際に、一部の場所を修正し忘れることがなくなるため、ケアレスミスによるバグが減少します。
  4. 開発効率の向上: コードの重複を避けることで、新しい機能の追加や既存機能の改善が容易になり、開発効率が向上します。

DRY原則トレードオフ

現在のソフトウェア開発において、コードの重複を減らそうとすると、いくつかのトレードオフが生じることがあります。
コンポーネント間が密結合いなったり、チームの開発速度が低下したりします。
例えば、同じコードを複数の場所で使い回すことで、コードの再利用性が向上する一方で、コードの変更による影響範囲が大きくなります。

DRY原則は非常に重要ですが、過度に適用すると逆に問題が生じることがあります。異なる文脈で同じコードを無理に使おうとしてしまい、適切な抽象化が行われないこともあります。

ここからは、コードの重複が合理的になるパターンと、逆にどのような場合に避けるべきなのかについて考えてみます。

アムダールの法則

今回参考にした書籍では、まずアムダールの法則について触れています。

アムダールの法則とは、

ある計算機システムとその対象とする計算についてのモデルにおいて、その計算機の並列度を上げた場合に、並列化できない部分の存在、特にその割合が「ボトルネック」となることを示した法則である

引用元: wikipedia

この法則をソフトウェア開発に置き換えて考えてみると、必要な同期処理が少ない(つまり並列処理部分が多い)ほど、問題解決のためにリソースを追加することで得られる利益が大きいということが言えます。もっと単純に、待ち状態が発生しにくいほどリソース追加の効果が大きいといってもいいでしょう。

例: 認証ライブラリ

参考書籍でも紹介されていますが、例えば、認証ライブラリを開発する場合を考えてみましょう。書籍の内容をできるだけ簡潔にしながらまとめるので、より詳細な説明が必要な場合は書籍を参照してください。

この例では、ビジネス的に分断され、開発チームとしても分かれている2つのプロジェクトがあります。それぞれのプロジェクトで認証機能が必要になりました。

先に紹介したアムダールの法則通り、それぞれのチームで認証機能を開発することを選択した場合のことを考えてみます。

この場合、認証機能はそれぞれのプロジェクトで独立して開発されるため、コードの重複が発生します。同じ認証という機能を独立して開発しているため、コードや作業に重複が存在します。

この時発生する事象として、下記のようなものがあります。

  • 多くのバグやミスにつながる
    • たとえば、片方のチームがバグを修正した場合、もう片方のチームも同じバグを抱えている可能性があります。
  • 知見は共有されない
    • 片方のチームがバグを発見し、彼らのコードベースでそのバグを修正したとします。しかし、もう片方のチームのコードベースにその修正が反映されることはありません。お互いのチームはそれぞれこのバグを修正する必要があります。
  • チーム間での調整なしの作業はより早く進みます

この例からわかるように、コードの重複は問題を引き起こす可能性があります。このような場合、DRY原則を適用することで、コードの重複を避けることが重要です。

続いて、この二つのチームは、コードベース間で重複している部分を別のライブラリに抽出することを決定しました。これにより、認証機能は共通のライブラリとして提供され、両方のプロジェクトで再利用されることになります。

重複を除くことで、コード全体の品質が向上します、共通のライブラリを蓄積することで、双方のチーム間での協力が可能になり、共通のコードベースが改善されます。

バグを修正する際の作業の重複もなくなります。

このライブラリとして抽出するアプローチを選択する場合のトレードオフについて考えてみましょう。

  • 抽出したライブラリ自身は元のコードベースとは別の様式、デプロイ方法、コードの慣例を持つ
  • そのため、最初に共有ライブラリを追加するコストは高くなるが、2つ目を追加するコストは低くなる
  • 共有ライブラリを選択した場合、ほとんどのケースではそれを利用するクライアントと同じ言語を利用する必要がある
    • ネイティブインターフェースのような技術でラップすることも可能だが、中間層が増え、例えばOS間での移植が難しくなったり、別の問題が発生する
  • ライブラリを開発する場合はバージョンに寄る互換性の維持やシンプルな設計など、ライブラリの品質にも注意が必要
  • ライブラリをインポートした場合、ライブラリのコードは利用者のコードの一部となり、責任が発生する

上記のライブラリの利用による問題をいくつか解決するために、参考書籍ではマイクロサービスのアーキテクチャを提案しています。

マイクロサービスとして切り出して、そのサービスをHTTPなどのプロトコルを通して利用する場合は、ライブラリを利用する場合に比べると結合が緩やかになります。言語による制限や、ライブラリの品質による問題をブラックボックスとして扱うことができ、APIのみが唯一の結合箇所になります。

ただし、現在のコードを実行するための新しい依存関係として、クライアントライブラリを追加する必要があります。

また、ライブラリの場合と同様に、マイクロサービスの場合も、サービスの品質やバージョン管理に注意する必要があり、独自のデプロイの仕組みが必要になります。また、サービス自体をデプロイするインフラの整備や、監視の仕組みなど、多くの追加の実装が必要になります。

DRY原則トレードオフバランス

適切なバランスを見つけるためには、以下の点を考慮することが重要です。

  1. 文脈の理解: コードを再利用する際には、そのコードが適用される文脈を理解することが重要です。異なる文脈で同じコードを無理に適用することは避けましょう。
  2. 抽象化の適切なレベル: 抽象化は適切なレベルで行う必要があります。過度に抽象化すると、コードの理解が難しくなります。
  3. 可読性と再利用性のバランス: 可読性と再利用性は両立しないことがあります。どちらを優先するかは、プロジェクトの状況やチームの方針によって異なります。

また、本質的な重複か、偶然の重複かを意識することが重要です。

参考書籍には、ソフトウェア開発者はパターンマッチングに過剰適合する傾向があります。という記述があります。これは、同じコードを見つけると、それを抽出して再利用しようとする傾向があるということです。2つのものが同一に見える場合には、それらが同じビジネス目標を解決する場合と、作業しているコード内における偶然の重複の場合に分けられます。

通常、2つの概念が異なることが判明したときに分離する場合と、2つの別の概念が同じことが判明した時に1つに統合する場合を比べると、後者のほうが簡単です。一度抽象化を行い、複数の場所で利用され始めると、分離する際に影響範囲が広がり、さまざまな調整が必要になります。

抽象化された状態から考え始めて、発生しうる全ての利用法を、決めた抽象化の枠組みに適合させていくのは、大抵のケースで最適ではない、と書籍内で記述がありますが、これは私個人の経験則とも合致するものです。

まずは独立したコンポーネントを作成し、多少コードの重複があったとしても、しばらくの間独立させたままシステムを実装できます。

このように、独立した実装を維持するか、ライブラリとして共有するか、マイクロサービスとして切り出すかは、それぞれのユースケーストレードオフが発生します。適切なバランスを見つけるためには、文脈の理解、適切な抽象化のレベル、可読性と再利用性のバランスを考慮することが重要です。

まとめ

DRY原則はソフトウェア開発において非常に重要な原則ですが、大抵の場合は分離するほうが難易度が高くなりがちなので過度に適用することは避けるべきです。適切なバランスを見つけることで、高品質で保守しやすいコードを実現できると思いました。

参考書籍の中で、まとめられている箇所がありましたので、引用します。

  • コードベース間での共通コードの共有は、ライブラリとして抽出することで実現できます。しかし、ライブラリ経由でのコードの再利用は密結合や柔軟性の低下などのさまざまな問題が伴います。
  • 共通のビジネスロジックを分離したサービスに抽出することは、より複雑な問題に対しては正しい選択かもしれませんが、保守コストが高くなります。
  • 継承は、コードの重複を取り除くことや子クラス間でのコードの共有に役立ちます。しかし、継承はコードの柔軟性を制限するなど、多くのトレードオフがあります。
  • 時として、重複したコードを残しておくことで柔軟性が維持され、チーム間の調整を減らす価値があります。

所感

DRY原則は、エンジニアになって一番最初に覚えた原則でしたが、その内容を本当の意味で理解するようになったのは意外と最近のことでした。
ソフトウェア開発は常に様々なトレードオフが存在するため、手法ごとにその適用範囲を理解し、トレードオフを可視化したうえで判断を繰り返していくことが重要だと感じました。
社内でもライブラリやマイクロサービスとしての切り出しは行われているため、それぞれのケースについて今一度トレードオフの内容を考え直すきっかけになりました。

「世界一流エンジニアの思考法」から学ぶエンジニアが持つべきメンタルモデル

はじめに

今回は「世界一流エンジニアの思考法」を読んで、エンジニアが持つべきメンタルモデルについて考えてみます。

結構流行りの本なので、要約記事も多く存在しますが、個人的に重要だと感じた部分をピックアップして個人的な所感をまとめてみます。

「理解に時間をかける」を実践する

私たち職業プログラマは、日々新しい技術と対峙し、複雑な問題を解決するために努力しています。この過程でしばしば採用されるのが、「とりあえず動くものを作る」というアプローチです。

新しい技術に触れ、手を動かしながら理解を深めることは重要です。しかし、急ぎ足で開発を進め、理解が浅いままにしてしまうと、将来同じ技術を使う際に再び同様の問題に直面するリスクが高まります。

技術の基礎をしっかりと理解しておくことは、その技術を幅広く応用するための土台となります。また、一度しっかりと理解しておけば、後々になって都度調べる手間が減り、長期的に見れば時間の節約にもつながります。

急がば回れ」ということわざが示すように、初期段階で時間をかけて理解を深めることが、結果的には効率的な道になると思います。

使用している言語やフレームワークに対する深い理解は、システム全体への理解を深めるのに役立ちます。しかし、実際には言語のドキュメントやフレームワークソースコードをじっくりと読む機会は少ないかもしれませんがこれらの知識を習得し、理解を深めることに時間をかけることは、将来的に大きなレバレッジとなり、効果を発揮します。

基礎の理解を深めておくことは、エラーが発生した場合に原因を特定しやすくなるというメリットもあります。

プログラミングにおけるエラーは避けられないものですが、その背景にある原理を理解していれば、問題の解決に向けたヒントを得ることが容易になります。これは、プログラミングにおけるストレスの軽減にもつながり、より快適な開発を実現することができます。

プログラミングにおいて「理解に時間をかける」ことの重要性は非常に高いです。基礎をしっかりと理解することで、長期的な視点で見たときに効果的な開発が可能となります。

「Be Lazy」というマインドセット

「より少ない時間で価値を最大化するという考え方」だと紹介されています。

いわゆるエンジニアの三大美徳の1つ、「怠惰」ですね。

書籍の中では2-8の法則を引用して、全体の2割の仕事が8割の価値を生むのだから、その2割に集中すべきだと説明されています。(参考: パレートの法則)

個人的にこの考え方に沿って生きてきましたが、改めて言語化するととても重要度が高いなと感じます。

特に、定時を超えての残業が当たり前のような環境で働いていると、どうしても「時間をかければ何とかなる」という考え方になりがちですが、それは間違いです。

以前の職場では比較的残業が多かったのですが、今と比較すると明らかに自分の出せている価値は低かったと感じます。

その理由は、重要なことはどれなのかを見極めることができていなかったからです。全体のタスクのうち、納期を絶対に守るべきだという機能は自社開発の場合はそこまで多くはならないはずで、少なくとも見積もる段階で残業を前提にすることはないはずです。

それでも残業が常態化している場合は、やらない場合の影響が少ないタスクを無理に抱えている可能性が高いと思います。

個人的には残業は突発的なタスクに備えて避けておき、その分の時間を自己投資に使うことで、将来的な生産性に投資することが大事だと感じています。

本当に価値を出したいなら、自分の持っている時間とエネルギーを注力するタスクは1つに絞ることが大事です。

書籍の中では、タイムボックスという考え方が紹介されています。これは、1日のうち、何時から何時までとタスクにかける時間を決めておくというものです。

たとえタスクの途中でも、その時間になったら切り上げるようにします。この方法のメリットは複数あります。

  • タスクにかける時間を明確にすることで、その時間内に集中できる
  • 生産性を上げるための学習の時間を確保しやすい
  • 脳を休める時間を確保しやすく、生産性の向上につながる

作者の体感ベースではあるので、個人差はあると思いますが、私自身も極力タイムボックスを意識して日々の業務を進めています。

ちょっとの残業、ちょっとの無理を当たり前にせず、しっかりと区切る習慣そのものが生産性を上げるための大事な要素だと感じています。

自己組織型チーム

書籍では、従来のリーダーシップと比較してサーバントリーダーシップを推奨しています。

違いとしては、従来の「コマンドアンドコントロール」制ではマネージャが部下に指示をする。「サーバントリーダーシップ」制ではメンバーが主体で動き、マネージャは彼らの障害を取り除く役割を担うというものです。

仕事を楽しみ、メンバーがそれぞれ自分の仕事と人生に対して責任を持つことができるような環境を作ることが大事だと感じます。

リーダーはメンバーに対して指示を出すのではなく、メンバーが自身でタスクを選択し、自分で進めていくなかで発生する障害を取り除くことに注力することが大事です。

これを実現するためには、リーダーに全責任があるというような従来の考え方を捨てて、メンバー全員が同じ責任をもつという意識作りをする必要があります。

メンバーは基本的にpull型、つまり必要な情報があればそれを知っている人から自ら情報を引き出す姿勢が求められます。

この姿勢は、価値を出すためには必要ですし、エンジニアとして成長するために必須の要素だと感じています。

書籍全体を通して、メンバーがそれぞれ主体的に動くことを重視していて、その環境作りの方法や文化づくりについても触れられていました。

運動習慣

あまりほかの要約記事で取り上げられていないんですが、運動習慣についても書かれていました。

個人的には、リモートワーク中心のエンジニアこそ能動的な運動習慣を持つことが大事だと感じています。

ブログを定期的に書きつつ、副業を始めたりこの一年間は自分の時間の使い方がかなり大きく変わりました。

その中で、新しく以下のような課題に直面しました。

  • 肩こり
  • 業務後に疲れが残っている
  • 集中力が続かない

これらの課題を解決したのは、運動習慣を持つことでした。

特に、リモートワーク中心のエンジニアは、運動不足になりがちです。個人的に取り組んだのは、ジムの契約と季節によっては昼休みに散歩をすることです。

定期的な運動が必要で、筋トレ、有酸素運動はストレス緩和にもかなり効果がありました。また、マッサージ機の導入も肩こりに効果がありました。

自宅のデスク環境は整えているつもりでも、一日10時間以上同じ姿勢でいると、どうしても肩こりが発生します。長時間稼働できること、それを維持できることは重要ですが、その時間の質を高める工夫が必要です。

運動を阻害する要因を考えてみましょう

  • 運動をする時間がない
    • 運動を全くしていない場合、運動しはじめは30分も運動すればしんどいので捻出できます
  • 運動をする場所がない
    • 自宅でできる運動も多いです。特に筋トレは自宅でできます
  • 運動をする習慣がない
    • 自宅での運動が続かないのは、習慣化が難しいからだと思います。私の場合はジムを契約することで行かないともったいない状況を作りました。

運動習慣を持つことで、集中力が続く時間が増え、業務後の疲れが減りました。また、肩こりも解消され、業務に集中できる時間が増えました。

ただ、肩こりについては一時的に緩和されますが、根本の原因は姿勢の悪さなので、デスク環境への投資も重要です。

まとめ

  • 理解に時間をかける
  • Be Lazyの精神
  • メンバーが主体的に動ける環境を作る
  • 運動習慣を持つ

所感

結局のところ、著者のような周りの優れた人のいいところを観察し、自分に取り入れることのできる著者の姿勢が何よりエンジニアに必要なんじゃないかという気がします。

チームメンバー全員が目的を見失わないように、効果的な手段を考えて選択することのできる組織、環境をエンジニア自身が作り主体的に動くことが大事だと感じます。

エンジニアとして、より仕事や人生を楽しむためには時間の使い方にメリハリをつけ、健康的な生活を送ることが大事だと感じます。

職業エンジニアの場合はパフォーマンスを出し続けることが一番重要なので、学習の習慣はもちろん、仕事を楽しめているか、健康で過ごせているかという視点を常に意識していくことが大事だと感じました。

駆け出しエンジニアからジュニア・ミドルレベルへの成長の道のり

はじめに

エンジニアとしてのキャリアをスタートしたばかりの方が、ジュニアレベルやミドルレベルのエンジニアとして活躍するために必要なスキルや知識について考えてみます。
この記事はWEB領域のエンジニアを対象としています。

前提

前提として、私の知っている範囲のことしか書けませんが、弊社スマレジのエンジニアとして活躍できるレベルを目標としています。
スマレジでは、バックエンド、フロントエンドの領域で担当が分かれておらず、どちらも担当します。
インフラについては専属のチームがありますが、プロダクトを担当しているエンジニアと相談しながらインフラ設計が行われることが多く、インフラについての知識がなくてもいいわけではありません。

ロードマップ

まずはロードマップとしてよく知られる図を確認してみます。 私自身が何を勉強すればいいのかわからなくなった時に参考にしていました。最近はあまり迷うことはないんですが、どうしてもエンジニアになりたてのときはわからないことが多すぎて、どれから勉強をすればいいのかわからず、わからないことがわかっていない状態に陥ることがありました。

Leval 1: 全エンジニア必須スキル

ここからはレベルを分けて、ロードマップ上のエンジニアのスキルを見ていきます。

個人的にはLevel 2までが実務に入るための最低限のスキルで、Level 3以降は必要になったタイミングで素早いキャッチアップが求められると感じています。

ロードマップの両方に存在する要素

  • Internet
    • How does the internet work? インターネットの仕組み
    • What is HTTP? HTTPとは何か?
    • Browses and how they work ブラウザとその仕組み
    • DNS and how it works DNSとその仕組み
    • What is Domain Name? ドメイン名とは?
    • What is hosting? ホスティングとは?
  • Git

Level 2: エンジニア基礎スキル

このレベルまでのスキルは、個人の趣味でも十分に習得可能なものが多いと思います。
それぞれの領域で本当に最低限の共通的な部分をLevel 2としてみました。

ロードマップの両方に存在はするが、温度感が違うもの

  • JavaScript
    • フロントエンドでは必須
    • バックエンドでは任意

バックエンドの必須スキル

フロントエンドの必須スキル

  • HTML
  • CSS
  • Package Manager
    • npm
    • yarn
    • pnpm
  • Pick a Framework
    • React
    • Vue
    • Angular 選択肢としてはあるが、採用される機会がやや少ないイメージ

Level 3: エンジニア実務スキル

ここからは実務で必要になるスキルを挙げてみます。
これらの技術を実務に入る前に全て習得している必要はないと思いますが、必要に応じて都度キャッチアップが必要です。

領域に関わらず学ぶべきスキル

  • Web Security Basics
    • HTTPS
    • CORS
    • Content Security Policy
    • OWASP Security Risks

バックエンド

  • Learn about APIs
  • Caching
  • Testing
    • Unit Testing
    • Integration Testing
    • Functional Testing
  • CI/CD
  • Scaling Database
    • Database Indexes

~ 個人的に感じるジュニアとミドルの壁 ~


  • More about Databases
    • ORMs
    • ACID
    • Transactions
    • N+1 Problem
    • Normalization
    • Failure Modes
    • Profiling Performance
  • Software Design & Architecture
    • GOF Design Patterns
    • Domain Driven Design
  • Design and Development Principles
    • Test Driven Development
    • CQRS
    • Event Sourcing

フロントエンド

  • TypeScript
  • Writing CSS
    • Tailwind
    • CSS in JS ロードマップの記載はない
    • CSS Modules ロードマップの記載はない
  • Build Tools
    • Module Bundlers
      • Webpack
      • Vite
      • esbuild
      • Rollup
      • Parcel
  • Linters and Formatters ロードマップ上の優先度は低いが個人的には重要
    • ESLint
    • Prettier
  • Testing your Apps
    • Vitest
    • Jest
  • Server Side Rendering
    • Next.js
    • Nuxt.js
  • Static Site Generators
    • Astro
    • Next.js
    • Nuxt.js

スマレジのエンジニアに要求されるスキル

ここからは弊社スマレジの採用要件を見ながら、どのようなスキルが求められているのかを確認してみます。

WEBエンジニア

必要要件

  • Webアプリケーション開発の実務経験(2年以上)
  • バージョン管理、とくにGitを使った開発の経験

現在はPHPを利用していますが、PHPの経験は必須ではなく他言語においてWebアプリケーション開発の経験をお持ちの方であれば問題ありません。

歓迎要件

  • リーダー経験
  • Laravel、CakePHP経験
  • Webアプリやスマホアプリでのパフォーマンスチューニング経験
  • UnixライクOS環境下でのWebアプリケーションの開発経験
  • AWSサービスの利用経験
  • スクラムでのチーム開発の経験
  • フロントエンド(Vue.js、またはReactを利用したUI開発経験、SPA開発経験)
  • WebAPIを使用した開発経験、またはWebAPIの設計・実装経験
  • Webアプリケーション開発に関係するインフラ知識・理解
  • DDDやアーキテクチャ設計の知識・設計・実装経験

リードエンジニア/テックリード

必要要件

  • Webアプリケーション開発経験(PHPJavaRubyなど)
  • 自社サービス/受託開発の開発・運用経験
  • システムの課題解決に対する推進力
  • 新技術における高速なキャッチアップ力

歓迎要件

  • 業務システムの開発・運用経験
  • 最新技術の習得および活用
  • テックリード/リードエンジニアとしてチームの技術判断をした経験
  • マイクロサービスのシステム運用経験

上記を比較すると、ジュニアレベルでは2年間の開発経験があれば要件を満たせそうです。2年間Webアプリケーションの開発の経験があれば、大抵の場合バージョン管理の要件はクリアできそうですね。
ほかに歓迎されるのは具体的な技術要素やスクラムの経験など、スマレジ社内の文化に近い経験が歓迎される傾向にあります。

一方、リードエンジニア/テックリードになってくると、言語の知識や具体的な技術要素の要件は減り、抽象度の高い要素を求められます。これは、弊社スマレジの「技術は道具」という価値観に基づいていると思われます。
どのような技術であれ、ユーザーに価値を届けるために必要であればキャッチアップするのが前提なので、具体的な技術要素よりも、技術を使ってどのような価値を提供できるかが重要になってくると思われます。

経験としても、技術判断ができるかや、アーキテクチャ面でも、DDDやアーキテクチャ設計から一つ視座が高いような要素が求められるようになります。

実際に必要だと感じるスキルとマインド

これまで見てきたように、エンジニアとして習得するべき技術は幅広く、具体的な技術要素の習得だけで十分なレベルになることはないと感じます。

必要に応じて素早いキャッチアップをしたり、より視座の高い戦略を立てるための知識としての技術の習得が求められます。

駆け出しから脱出するためには、これまであげたWEB技術のうち、あまり流行に左右されない基礎的な部分や、昔から存在し続けるアーキテクチャや設計手法とその批判意見などに目を向け、自分自身で戦略を立てる経験と、素早いキャッチアップのための訓練を積むことが重要だと感じます。

個人的につまづいたポイントとその解決方法

ここからは、個人的につまづいた箇所とその解決方法について書いていきます。

参考にした書籍や個人的な意見を書きます。

ただし、プログラミングの基礎(if,forなど)やユニットテスト、オブジェクト志向については前職の経験もあり、入門レベルで大きくつまづいたことはないので、割愛します。

1. 何を学べばいいのかわからない

エンジニアになりたての場合、そもそもどれが流行りで、どれが基礎的な技術なのかわからないことがあります。

解決方法

WEB技術の基礎的な部分は、あまり流行に左右されない部分が多いです。

私の場合はまず下記の書籍を読みました。

WEBを支える技術

この本でWEBの歴史、HTTP周辺、RESTful APIなどの基礎的な部分を学びました。

2. DB設計、SQL

組み込み業界にいた前職では、あまりDBを使用する業務がなく、RDB自体にほとんど初めて触れました。

そのため、SQL自体も学習が必要でしたし、DBの設計についてもキャッチアップする必要がありました。

コードと違って、DB設計は既存のDB設計がなぜその構造になっているかという理由の部分が読み取れずに苦労したことがあります。

解決方法

この問題を解決するのに役立ったのは、以下の書籍でした。

SQLアンチパターン

また、具体的なDB周りの要素についてはMySQLの公式ドキュメントを読むことが多かったです。

MySQL :: MySQL Documentation

内容としてはDBの基礎的な部分を知らないと少し難しい部分もありますが、何がわからないのかわからない状態だったので、公式ドキュメントから目を通し、出てきた単語を個別で調べ、QiitaやZennの記事にお世話になりました。

フレームワークの学習

業務で使用しているLaravel,Vueともに経験がなく、これらのキャッチアップは研修等もなかったため、苦労した覚えがあります。

この辺りはスマレジの採用要件が2年程度のWEBアプリ開発経験であることに対して、私は別業界からの転職だったので、かなり焦っていた部分です。

解決方法

業務以外で個人で取り組んだことの中で効果的だったのは、個人でToDoリストをフレームワークを使用して作成することでした。

フレームワークの知識だけでなく、アーキテクチャについても1から考えるいい機会になりました。

ちなみに、PHPやLaravel、Vueといった要素については技術書をほとんど読んでいません。

PHPについてはいくつか購入したものもありますが、いいと思える書籍は見つかっていないのでここでは紹介を控えます。

ソフトウェア設計

スマレジに入社してから担当しているプロダクトでは、ドメイン駆動設計の思想に基づいたクラス設計を行っており、設計思想についてもキャッチアップが必要でした。

この辺りは先輩エンジニアからの指導もありつつ、自身でも書籍やブログを読んで学習しました。

以下が参考になった書籍です。

ドメイン駆動設計入門 ボトムアップでわかる!ドメイン駆動設計の基本
エリック・エヴァンスのドメイン駆動設計

Java言語で学ぶデザインパターン入門第3版

ソフトウェア設計のトレードオフと誤り ―プログラミングの際により良い選択をするには

また、クリーンアーキテクチャについては下記ブログ、およびブログ内で紹介されているyoutube動画を繰り返し見て参考にしました。

実装クリーンアーキテクチャ

その他良かった書籍

以下に良かった書籍を紹介します。 書籍全般に言えることですが、書いてある内容全てが正しく、参考になるということはないので、6~7割程度自分にとって有意義な内容があれば「良かった」としています。

個人的には、書籍を読むことで知識を得ることよりも、自分が知らないことを知ることができるという点が大きいと感じています。知識の習得のみではなく、自分を顧みる機会が得られればそれは良い読書体験だと思っています。

まずはエンジニア全員読んでいると言っても過言ではないであろう書籍です。

リーダブルコード ―より良いコードを書くためのシンプルで実践的なテクニック

エンジニアとしての心構え的な部分は下記の本が良かったです。

プリンシプル オブ プログラミング 3年目までに身につけたい 一生役立つ101の原理原則

SOFT SKILLS ソフトウェア開発者の人生マニュアル 第2版

世界一流エンジニアの思考法

より実践的な開発手法への理解を深めるために

Googleのソフトウェアエンジニアリング ― 持続可能なプログラミングを支える技術、文化、プロセス

アジャイルサムライ――達人開発者への道

社内で勉強会を行ってみました

「エンジニアになってつまづいたポイント」をテーマに社内勉強会を行いました。

参加してくれたエンジニアの方々は比較的バックエンドに強みを持っているエンジニアが多く、つまづいたポイントとしては下記のようなものが挙がりました。

ただ、この辺りは業務で直面する課題ではあっても、なかなか個人の学習で習得するのは難しい印象がありました。理論として理解できても、実際の問題に遭遇する機会は業務のようなアクセスの多い環境でないとなかなか得られないと感じました。

これからもエンジニアとして成長していくために

上記のキャッチアップをしていくことと並行して、自分自身が好き、得意な分野を早めに見つけることができれば、それを専門性の軸として成長していくことができると思います。

なにより、わからないことだらけの技術の世界で、自分が自信を持てる分野を確立することは、メンタル面でも効果が大きいと感じています。

また、業務だと環境構築や技術選定がすでに終わっているプロジェクトに参加することのほうが多かったりするので、個人で開発を行なってみることも大切だと感じています。

まとめ

今回は自分がエンジニアになってからつまづいたポイントとその解決方法について書いてみました。

特に初めのうちはわからないことが多すぎて大変ですが、何がわからないのかを言語化しておき、ひとつずつ解消していくことが大切だと感じています。

  • 何を知らないのかを知ることが大切
    • 解消のためにキーワードを知る。ロードマップや記事を読む
  • 体系的に学びたいトピックは書籍を読む
    • ただし、書籍の内容全てを信じるのではなく、数冊同じトピックに関連した書籍を読んで自分の意見を持つ
  • 個人で開発を行なってみる
    • 環境構築やちょっとした技術検証等は業務でなかなかできないことが多いのですぐに手を動かしてみることが大切

参考

以下のQiitaの記事もロードマップの一つとして参考になります。

【フロントエンド】駆け出しエンジニアが目指すジュニアレベルのエンジニアとは【2024年版】

【バックエンド】駆け出しエンジニアが目指すジュニアレベルのエンジニアとは【2024年版】

Next.jsのServerActionsを試してみる

はじめに

ここ最近あまり手を動かして新しいものを作成することができていなかったので、今回はNext.jsのServerActionsを試して見たいと思います。

まずは簡単なTodoリストの作成から始めてみます。

開発方針

個人開発ですが、先にデプロイの方法を考えてから開発を始めてみます。

今回選択したデプロイ先はVercelのHobbyプラン + PlanetScaleです。

どちらも無料枠があるので、個人開発を手軽に始めるには良い選択肢かなと思いました。

利用するFW、ライブラリ

今回はServerActionsとNext.jsを使ってみたいので、Next.jsの14.0.4、app routerを利用します。

その他インストールするライブラリは以下の通りです。

"@prisma/client": "^5.10.2",
"next": "14.0.4",
"next-auth": "^4.24.5",
"prisma": "^5.10.2",
"react": "^18",
"react-dom": "^18"
"autoprefixer": "^10.0.1",
"biome": "^0.3.3",
"postcss": "^8",
"tailwindcss": "^3.3.0",
"typescript": "^5"

デプロイ先の設定

今回はとりあえずデプロイして動作すればOKを目標にするので、Vercel上で先に行っておく設定としては環境変数の設定くらいです。

ローカルでは.env.localに設定しておき、production環境用の環境変数はVercelのダッシュボードから設定します。

next-authを利用したGoogleOauth認証を作成したので、以下の環境変数も併せて設定しておきます。

DATABASE_URL= // planetScaleのDB URL
GOOGLE_CLIENT_ID= // Google OauthのClient ID
GOOGLE_CLIENT_SECRET= // Google OauthのClient Secret
NEXTAUTH_URL=
NEXTAUTH_SECRET=

データベースの設定

今回はORMとしてPrismaを利用するので、まずはPrismaの設定を行います。

この辺りは公式ドキュメントを参照しながら進めていきます。
公式ドキュメントではPlanetScaleに関する情報も記載されています。

公式ドキュメント

割とセットアップする機会が多いのでChatGPTの回答を参考にして進めても詰まることなく進めることができました。

インストール

npm install prisma @prisma/client

初期化

npx prisma init

この時点でprismaディレククトリが作成され、schema.prismaが作成されます。

また、.envファイルを作成していない場合は作成されるはずです。

先程のあげた内容の環境変数を設定し、DATABASE_URLにはPlanetScaleのURLを設定します。

以下のような形式でPlanetScaleからURLが提供されるので、それを設定します。

DATABASE_URL="mysql://<username>:<password>@<host>/<database>?sslaccept=strict"

データベースのマイグレーション

PlanetScaleはgitブランチのような仕組みでDBスキーマを管理するため、従来のマイグレーションとは少し異なる形でマイグレーションを行います。

まずはschema.prismaを編集して、テーブルを作成します。

Prismadb pushを利用してローカルのスキーマ変更をPlanetScaleのブランチにプッシュします。

そのうえでPlanetScale上のダッシュボードを変更内容を確認し、問題がなければメインブランチのDBスキーマとしてマージします。

この辺りはPrismaの従来の方法ではないので少し注意しながら作業する必要があります。

ServerActionsの作成

本題のServerActionsの作成です。

ServerActionsの概要

ざっくりとした概要をChatGPTに聞いてみました。

Next.jsのServerActionsは、サーバーサイドで非同期関数を実行するための機能です。これらは、サーバーおよびクライアントコンポーネントでフォームの送信やデータの変更を処理するために使用できます。

### 概要

- **定義と使用法**: ServerActionsは`async`関数として定義され、`"use server"`ディレクティブを使用してマークされます。これらのアクションは、サーバーコンポーネントおよびクライアントコンポーネントで使用でき、フォームの送信やデータの変更を処理するために使用されます。

- **動作**: ServerActionsは`<form>`要素の`action`属性を使用して呼び出され、サーバーコンポーネントではプログレッシブエンハンスメントがサポートされています。クライアントコンポーネントでは、JavaScriptがロードされるまでフォームの送信がキューに入れられ、ハイドレーション後にブラウザのリフレッシュが発生しないようになっています。

Next.jsのServerActionsは、サーバーサイドで非同期関数を実行するための機能です。これらは、サーバーおよびクライアントコンポーネントでフォームの送信やデータの変更を処理するために使用できます。

- **クライアントコンポーネントでの使用**: クライアントコンポーネントでは、モジュールレベルの`"use server"`ディレクティブを使用してインポートされたアクションのみを使用できます。ServerActionをクライアントコンポーネントにプロパティとして渡すこともできます。


### ServerActionsの利点

- **サーバーサイドのデータ取得**: ServerActionsを使用すると、データ取得をサーバーに移動し、データソースに近づけることができます。これにより、レンダリングに必要なデータの取得時間が短縮され、クライアントが行うリクエストの数が減り、パフォーマンスが向上します。

- **セキュリティ**: ServerActionsを使用すると、トークンやAPIキーなどの機密データやロジックをサーバーに保持できます。これにより、クライアントにこれらの情報が露出するリスクを回避できます。

- **キャッシュ**: サーバー上でレンダリングすることで、結果をキャッシュして後続のリクエストやユーザー間で再利用できます。これにより、パフォーマンスが向上し、各リクエストでのレンダリングおよびデータ取得の量が減少し、コストが削減されます。

ただ、今回の実装にあたってChatGPTの回答はあまり精度が高くなく、かえって混乱することが多かったので、Next.jsの公式ドキュメントを参照しながら進めて行った方が良さそうです。

技術要素でいうとPrismaに関する情報は比較的精度が高かったんですが、他の技術要素に関してはChatGPTの利用を避けた方が良さそうな印象を受けました。

ServerActionsの実装

app routerの場合はルーティング設定がpages routerの場合とは異なる点に留意しつつ、ServerActionsを実装していきます。

pages routerを触ったことがある場合は下記のようなZennの記事を見るとイメージ掴みやすいかもしれません。

ざっくりApp Router入門【Next.js】

pages routerにも自信がなかったりする場合は公式ドキュメントを端から読んでいくほうが近道かもしれません。

app/todos/action.ts

"use server";
import { PrismaClient } from "@prisma/client";
import type { NextRequest } from "next/server";

const prisma = new PrismaClient();

export async function getTodos() {
    "use server";
    const todos = await prisma.todo.findMany({
        orderBy: { createdAt: "desc" },
    });
    return todos;
}

export async function createTodo(formData: FormData) {
    "use server";
    const rawFormData = {
        title: formData.get("title") as string,
        description: formData.get("description"),
        completed: formData.get("completed") === "true",
    };
    const todo = await prisma.todo.create({
        data: { title: rawFormData.title, completed: false },
    });
}

このような形で、"use server"を文頭につけます。
また、引数にはformDataを受け取るとform内のinput要素の値を取得することができます。

今回は極力シンプルな形で実装しましたが、実際にはバリデーションやエラーハンドリングなども実装する必要があります。

参考: 【Next.js】Server Actionsを現場で使うテクニック

コンポーネント側の実装としてはform要素のactionに上記の関数を指定することで、ServerActionsを利用することができます。

  const handleCreate = async () => {
        const formData = new FormData();
        formData.append("title", newTodo);
        try {
            await createTodo(formData);
        } catch (e) {
            console.error(e);
        }
        // 作成完了後にリロードする
        window.location.reload();
    };

今回の場合はJSの関数を利用したかったので、クライアントコンポーネントから上記の呼び出しを行っています。

ただ、ServerActionsのメリットを活かせないような気もしているのでもう少しドキュメントを読みつつ試行錯誤してみたいと思います。

戸惑う点としては、フロントエンドの中でもクライアントサイドとサーバーサイドのコードを混在させることになるので、意識の切り替えが自分の中でうまくできていない感触がありました。

ServerActions、RSCあたりはまだなにもわからないので、思想を理解できるように手を動かしていきたいと思います。

参考一言で理解するReact Server Components

まとめ

今回はNext.jsのServerActionsを試しつつ、新しく個人開発を始めるにあたっての最小限のデプロイまで行いました。

フロントエンドはまだまだ知見が浅くベストプラクティスがわからない分野なので、悩みながらも楽しくすすめることができました。

個人的には先にデプロイをしやすい環境を用意しておくとモチベーションを保ったまま開発を進めることができているかなと思います。

今後も引き続きフロントエンドもキャッチアップをしつつ、個人開発を楽しみたいと思います。

最初からちゃんといろんなことを検討して設計しろ VS 爆速でプロトタイプ作った方がいろんな問題可視化されて結果的に早いだろ

はじめに

社内slackのtimesチャンネルでふと思ったことを書き込んだところ、複数の方から反応があったので個人的な意見をまとめてみました。

書き込んだ内容は

最初からちゃんといろんなことを検討して設計しろ VS 爆速でプロトタイプ作った方がいろんな問題可視化されて結果的に早いだろ

です。

この記事では、上記テーマについていただいた反応を踏まえて、私自身の考えを改めてまとめてみます。

本記事の要約

そもそも設計とプロトタイピングは対立するものではない: 設計とプロトタイピングは相互補完的なアプローチであり、どちらもプロジェクトの成功に重要です。

設計の重要性: 設計はビジネス仕様を顕在化させ、整理することであり、仕様の考慮漏れを防ぐために重要です。しかし、設計段階で全ての仕様を洗い出すことは困難であることが多いです。

プロトタイピングの重要性: プロトタイプを作成することで、実際にコードを書くことにより想像できていなかった課題が可視化され、早期にフィードバックを得ることができます。

プロトタイプの取り扱い: プロトタイプをリリースする場合は技術的負債が蓄積される可能性がありますが、プロトタイプを捨てて設計段階に戻る場合は初期の設計ミスが製品に影響を与えることがなく、より最適化された製品を作ることができます。

プロダクトのケースに応じたバランス: プロダクトによってはプロトタイピングを優先する場合もあれば、設計段階での仕様洗い出しを重視する場合もあります。プロジェクトの目的と状況に応じてバランスを取ることが重要です。

エンジニア個人としての能力: 設計の段階で仕様を洗い出す能力とプロトタイプ作成の能力の両方がエンジニアにとって重要であり、これらのスキルをバランス良く持つことが求められます。

テーマのよくない部分

そもそもなんですが、書き込んだ内容自体が議論を呼びやすい形になっています。
Twitter等で炎上しやすい特徴を持っているともいえて、「背景が特定できない」「設計という単語の範囲がわからない」「プロトタイプが何を指しているのかわからない」と言った受け取った人による解釈が異なる部分が大きい話題です。

発言した背景は実装を進めていて、「うわ、これくらいの課題だったら設計の段階でちゃんと詰めとけば気づけたなあ」というちょっとした後悔でした。

もらった反応

  • そもそも二つは対立するものではない
  • 設計 => 開発 のウォーターフォールのみでリリース、公開してしまうと微妙
  • プロトタイプを捨てることを前提としていないと微妙

さて、反応を踏まえて私自身もいろいろと考えるところがあるのでまとめてみます。

前提: 設計とは

このテーマを考える上ではじめに考えないといけないのは「設計」という言葉の定義です。
この前提がずれてしまうと以降の議論が成り立たなくなってしまうので、ここで前提条件として設計とは何かを考えてみます。

ここでは、プロトタイプの作成として、実際にコードを書くことと対比しているため、設計とは「コードを書く前に行う作業」として考えます。

特に、手戻りや考慮漏れといったことを防ぐことが設計でできるかどうかが焦点となってくるので、コーディングにおけるクラスの設計やメソッドの設計といった部分は対象外と言えそうです。

ここでは設計という言葉を

コードを書く前に行うビジネス仕様を顕在化させて整理すること

だという前提で考えてみます。

よく設計の失敗として挙げられるのが仕様の考慮もれです。
これが発生してしまうと、実装が進んでから気づいて修正するいわゆる手戻りが発生してしまいます。
私たちが行う開発の過程において素早く課題解決を行なってユーザーに価値を提供するためにはどうするべきかという観点で考察を進めます。

いろんなことを検討して設計しろ

こちらは、設計の段階でできるだけ課題を洗い出しておくことを指しています。

メリット

  • 仕様の考慮漏れをなくすことができる

デメリット

  • そもそも全ての仕様を漏れなく洗い出すことは難しい

「なるほど完璧な作戦っスね―――ッ
不可能だという点に目をつぶればよぉ~~~」という声が聞こえてきますね。

理想をいえば、この方法で完璧な設計を行なって、仕様の考慮漏れをなくし、コーディングに入ることができれば手戻りはゼロになるはずです。
ですが、現実問題として難易度の高い方法であることは否めません。

その要因は以下のようなものが挙げられます。

  • 仕様の考慮漏れをなくすためには深いビジネス知識が必要
  • 仕様の策定時点で全ての情報が出揃っている必要がある
  • そもそも全ての仕様を漏れなく洗い出せるかどうかは個人の能力に依存する

ここで、今回の考察ではどちらの手法を取るべきかという観点を重視して、個人の能力に依存する部分は考慮から外したいと思います。
この議論自体、すべてのエンジニアが設計段階で全ての仕様を洗い出せればプロトタイプを作る必要すらなく解決できてしまうため、個人の能力に依存する部分を考慮から外すことで、より一般的な議論になると考えます。
そのため、前提として「全ての仕様を漏れなく洗い出すことはできない」「エンジニアとしての個人の能力の有無は議論の対象外」として現実的なベターを模索したいと思います。

ただ、エンジニアとしてはできるだけ設計段階で仕様を洗い出しておくことができるようになるというのは大事なスキルだと思います。

爆速でプロトタイプ作った方がいろんな問題可視化されて結果的に早いだろ

こちらは、プロトタイプを作成することで、設計段階での仕様の考慮漏れをなくすことができるという考え方です。

はじめに触れた通り、プロトタイプの指す事象が明確になっていないため、個人によって解釈にブレが生じる部分です。

ここでいうプロトタイプは、実際にコードを書いて動く製品を作成することを指しています。
プロトタイプを作成することによる効果は以下のようなものが考えられます。

メリット

  • 実際に作成することで想像できていなかった課題が可視化される

デメリット

ここでは2つのパターンのプロトタイプを想定して考えてみます。

パターン1 プロトタイプをリリースする

プロトタイプを作成し、検証したうえで課題部分を修正しながらそのままリリースまで漕ぎ着けるパターンです。
厳密にはプロトタイプとは呼べないかもしれません。
このパターンでは、設計にかける工数を削減しつつコードを書く作業に移るんですが、その際に課題が可視化されることで手戻りが発生することがあります。

メリット

  • 既存のプロトタイプをベースに改善を重ねるため、初期段階の開発工数は削減できる。
  • ユーザーやステークホルダーのフィードバックをすぐにプロトタイプに反映していくことができる

デメリット

  • 初期のプロトタイプの設計が不十分だと、後の段階で技術的負債が蓄積される可能性がある

プロトタイプを捨てないということは、変更が難しい設計のプロトタイプコードに、見つかった課題による大きな仕様変更が必要になることを意味します。

つまり、当初から変更容易性を考慮したアーキテクチャ選定や設計を行なっていないと、プロトタイプを捨てる場合と比較すると技術的負債を抱えやすいということです。

パターン2 プロトタイプを捨てる

こちらは作成したプロトタイプから課題を発見した上で、プロトタイプを捨てて設計段階に戻るパターンです。

メリット

  • プロトタイプから得た知見を活かして、新しいプロダクトを最初から設計することで、より最適化された製品を作ることができる
  • プロトタイプを捨てるため、初期段階での設計ミスが製品に影響を与えることがない
  • プロトタイプを捨てるため、作成時に発生した課題を解決する別の技術やツールを取り入れやすくなる

デメリット

  • プロトタイプを作成するための工数が発生し、捨てるため開発にコストがかかる

二つを比較すると

プロトタイプをそのままリリースする方法だと、ユーザーへの提供は一時的に早く行うことができ、フィードバックを得ることができます。
一方で、技術的負債が蓄積される可能性があるため、後の段階での開発工数が増える可能性があります。
特にユーザーが利用を開始し、実際のデータが蓄積されていくとDB構造の変更など変更難易度が一気に上がります。

一方で、プロトタイプを捨てて設計段階に戻る方法だと、初期段階での設計ミスが製品に影響を与えることがないため、後の段階での開発工数が増える可能性が低くなります。
捨てることを前提にプロトタイプ作成を行うことができると、課題の発見という目的に集中できるため、プロトタイプ作成自体も効率的に行うことができるのかなと思います。

もう一度設計という営みについて考えてみる

設計という営みは、「ビジネス仕様を顕在化させて整理すること」だと冒頭で定義しました。
さて、ここで現実の開発では仕様変更というものが発生します。
つまり、設計は最初に完璧に行なったとしてもその後の仕様変更に対応するためのものであるとも言えます。

現代のWEBを前提とするとソフトウェアは絶えず仕様変更の影響を受け、そのたびに改修が必要になります。
設計という営み自体が仕様変更の度に発生します。

仕様を顕在化させたものを「モデル」と呼ぶとすると、モデルは仕様変更のたびに改修されることになります。

設計、というのはビジネス仕様を顕在化させたモデルを作成、洗練していく工程であり、プロトタイプ作成はそのモデルを検証するための手段とも言えます。
検証の結果、得られたフィードバックはモデルに反映されますが、コードとしてプロトタイプに再度反映する場合はコードの変更が発生します。

コードは変更する難易度が高く、0から作り直した方が早い場合が往々にしてあります。
また、のちにプロダクトとしてリリースされる場合は将来発生する仕様変更に耐えうる設計やテストの整備が必要です。

プロダクトのユースケースごとにバランスを取ることが重要

プロジェクトの目的、スケジュール、リソースに応じて、設計とプロトタイピングのバランスをどのように取るかが異なります。
新しい技術や不確実性が高いプロジェクトでは、プロトタイピングを重視することでリスクを軽減できます。一方、既存の技術を使用し、要件が明確なプロジェクトでは、詳細な設計を優先することであらかじめ課題を洗い出すことができると思います。

プロダクトA: 新規プロダクト

  • まだビジネス仕様が固まっていない
  • ソフトウェアの設計に関しても不確定要素が多い
  • 採用する技術も未定

このような場合はプロトタイプ作成の恩恵を効果的に受けることができると思います。

プロダクトB: 既存プロダクトの改修

  • 既存のビジネス仕様が固まっている
  • ソフトウェアの設計も固まっている
  • 既存の技術を使用する

このような場合はプロトタイプを作成しても捨てづらく、設計段階での仕様洗い出しを重視することが効果的であると思います。
ただ、追加する機能が大規模なものである場合は、プロトタイプ作成を行うことで課題を可視化することができるかもしれません。

エンジニアとしての能力としての観点

ここまでの話は手法としての観点でしたが、私自身エンジニアなので、個人として目指すべき姿としての観点も考えてみます。

設計の段階でできるだけ仕様を洗い出すことができるようになるには

設計の段階で仕様を洗い出すということは、言い換えれば「コードのかたちやユーザーのユースケースなどの未来を想像する」ことだと言えると思います。
この精度を高めることができれば、理論上はプロトタイプ作成を行うまでもなく、設計段階で全ての仕様を洗い出すことができるはずです。
言い換えれば、想像、考慮によって実際に工数をかけずにモデルにフィードバックを行うことができるようになるということです。

事前に仕様を洗い出して検討する工程はコーディング工程のショートカットとも捉えることができるかもしれません。

周りのエンジニアを見ても、シニアエンジニアほどこの能力が高いと感じます。
ただ、想像力、考慮力といったものは個人の能力に依存する部分が大きく、経験値や、個人の特性によっても変わる部分が大きいと思います。

プロトタイプ作成を行うことで課題を可視化することができるようになるには

こちらは想像力と比べると具体的なスキルです。
コーディングの速さ、設計手法という知識の習得、具体的な技術の習得といったものが該当してくると思います。

ただし、現実の業務の上で障害となるのは、プロトタイプを作成することによる技術的負債の解消方法です。
一度リリースしてしまうと、変更が難しくなる事柄が存在します。例えば、DBスキーマAPI仕様、フロントエンドのUIなどがそれに当たります。

つまり、プロトタイプを作成する場合は、きちんと捨てることができるかどうかが重要になります。
難易度が高いのはこの部分で、業務的にプロトタイプを捨てる工数を確保し、ステークホルダーにも理解を得ることができるかどうかが重要になります。
プロトタイプという方法で設計の精度を上げる場合は技術力以外にも、業務フロー、開発フローの見直しやステークホルダーとのコミュニケーションが重要になってくると思います。

エンジニアとして目指すべき姿

私たちエンジニアはコードを通してユーザーに価値提供します。

そのためには、ビジネス仕様を顕在化させて整理したうえで、コードを書き、システムとして具現化させて素早くユーザーに提供することが求められます。

また、日々変化していくユーザーからの要求に対応するために、ビジネス仕様を顕在化させた「モデル」を改善し、コード、システムに反映し続けていくことが求められます。

そのためには、設計の段階でできるだけ仕様を洗い出すことができる能力と、プロトタイプ作成を行うことで課題を可視化することができる能力の両方が求められます。

結論

  • 相補性: 設計とプロトタイピングは対立するものではなく相互補完的なプロセス。設計はプロジェクトの基盤を築き、プロトタイピングはその基盤の上で検証を行うこと。
  • フィードバックの重要性: プロトタイピングを通じて得られる早期のフィードバックは、設計を洗練させ、技術的負債の蓄積を防いだり、仕様の考慮漏れを防ぐことにつながる。プロトタイプを捨てることができる場合、よりクリーンで効率的な設計へとフィードバックを活用できる。
  • 柔軟性と適応性: プロジェクトの目的、リソース、スケジュールに応じて、設計とプロトタイピングのバランスを柔軟に調整することが重要。新しい技術や不確実性が高いプロジェクトではプロトタイピングを重視し、要件が明確なプロジェクトでは設計を重視すると効果的。
  • エンジニアのスキルセット: エンジニアは、設計の段階で仕様を洗い出す能力を高めることで製造時の課題の発見を「ショートカット」できる。プロトタイピングを通じて素早く課題を可視化する能力を高めることで技術選定の正確さや試行錯誤の回転を早めることにつながる。
  • 重要なのはプロトタイプの特性を活かせるかどうか: プロトタイプを作成することで課題を可視化することができるが、プロトタイプを捨てることができるかどうかが重要。プロトタイプを捨てることができる場合、技術的負債を抱えることなく設計へのフィードバックを得ることができる。そのためにはプロトタイプを捨てることを前提とした開発フローの確立やステークホルダーとのコミュニケーションが重要。

2024年の抱負

はじめに

ぼちぼちスマレジに入社して二年が経とうとしています。
入社してから、必死にいろんなことに取り組んできた二年間でしたが、今年の目標も言語化しておきたいと思います。

2024年の目標

今年達成したいなあと思っていることは、大きく分けると個人で取り組むことと、組織的に取り組むことの二種類に分けられます。 それぞれについて、具体的に取り組みたいことを言語化してみます。

個人で取り組むこと

  • 技術書を読むペースを上げる
  • 個人開発を習慣化する
  • カンファレンス等社外のイベントに参加する
  • 副業の単価を上げて継続する

組織的に取り組むこと

  • 採用への取り組み
  • 開発生産性の向上への取り組み
  • 担当プロダクト外も含めた、社内で影響範囲の大きい仕組みづくり

技術書を読むペースを上げる

この二年間、ある程度のペースで技術書を読んできました。
ただ、いろんなことに取り組んでいく中で、二年間で見えてくるものとそうでないものがあります。
継続した学習によって、自分の触れたことのある領域はもちろん、その隣接する領域もなんとなく想像がつくようになってきました。

それもあって、自分にわからないことがわからないような感覚に陥ることがあり、新しい技術領域に触れたくなるタイミングが時々ありました。
このような時に自分は技術書を買う傾向にあるんですが、今年はもっとペースを上げたいなと思っています。

具体的には、読書スタイルを変えてみようと思っています。

これまでは初学者だったこともあって、全てのページ、全ての文章を読み込んでいました。
そのため、ある程度まとまった時間を必要としていました。
ですが、ここ最近は、どの技術書にどんな内容が書いてあるのか、その本で伝えたい箇所はどこなのかといったざっくりした内容を把握しておけば、本が手元にある限りは大きく問題ないという考えになってきました。

今年は、自分の中の技術書のレパートリーを増やすことを目的に、どんどん新しい技術書を積んで、数をある程度読もうと思います。

個人開発を習慣化する

これまで、個人開発では興味のある仕組みの実装やブログの実装が中心でした。
これらの開発では基本的に数週間で作りきってしまって、継続的にメンテナンスすることがあまりありませんでした。
メンテナンスしたことといえば、ブログで使用しているAstroのバージョンアップくらいです。

今年は、個人開発としてミニマムにデプロイした後、継続的なメンテナンスを行っていきたいと思います。

自分自身の強みの多くは保守性や可読性、設計やテストといった長くメンテナンスすることを前提としたソフトウェア開発にあると思っています。
その具体的な実装を自分自身のGitHubに残していきたいと思います。

また、継続してフロントエンドの動向を追っていきたいので、副業での取り組み以外にも個人開発としてフロントエンド技術に触ることを習慣化していきたいと思います。

カンファレンス等社外のイベントに参加する

これも一つ目の技術書の目標に近いことが動機で、新しい技術や、知見に触れる機会をできるだけ増やしていきたいと思っています。
また、エンジニアとしての人脈を広げる意味でも、オフラインのイベントにも積極的に参加していきたいと思います。

PHPを主に使用しているんですが、私自身のエンジニアの歴よりもちろんPHPの歴史の方が長いため、他社でどのように使用されているか、どのような課題があるのかを知ることができると思っています。

副業の単価を上げて継続する

去年から始めた副業ですが、ありがたいことに継続して稼働できています。
フロントエンドの技術を多く使うため、なかなか自分のバリューを発揮できているか不安になることもあるんですが、勉強しながらなんとか取り組んでいる状態です。
今年はもっとフロントエンド技術への理解を深めていくことを目指していて、副業先でも十分なバリューを発揮して単価を少しでも上げていきたいなと考えています。

単純にお金を稼ぐという意味だけでなく、単価を上げるためにどうバリューを出すのかを主体的に考えていく機会を副業によって得ることができているので、引き続き継続していき、期待されていることをどうすれば上回れるのかを意識しながら継続して取り組んでいきたいと思います。

採用への取り組み

ここからが本業として、組織という観点での取り組みになります。

まず一つ目は採用です。
去年から一部の採用活動に参加するようになりました。自分自身がどのようなエンジニアを評価しているのか、どんなエンジニアと一緒に働きたいかを真剣に考える機会になりました。
チームの業務領域的には特定の業務経験を必要としないので、エンジニアとして伸び代のある人がチームにジョインしてくれれば、サポートしつつ、一緒に成長していけると思っています。

今年は、採用活動に力を入れていいメンバーを集めて、いい開発チームを作ることが一つの大きな目標となりそうです。

開発生産性の向上への取り組み

チームのかたちについて考えていくなかで、いい開発組織ってなんだろうと考えるようになりました。
ここ最近よく耳にする開発生産性がそのヒントになるのではないかと考えて、ここ最近はいくつかのオンラインイベントに参加したりしています。

いろんな指標の計測に対する取り組みと、指標の数値との向き合い方が取り組まれているそれぞれの会社で異なるので、自分たちの開発組織においてどのように活用していくかといった方向性を今年は考えていきたいと思います。

Four Keysのような指標を取り入れる場合は、例えばPRのマージまでの期間をいかに短くするかということを考えますが、このあたりは仕組みというより、組織内の文化づくりが大切なのではないかと考えています。
こういった文化の共通認識を社内に定着させていきたい、というのが今年の目標です。

担当プロダクト外も含めた、社内で影響範囲の大きい仕組みづくり

去年あたりから、担当プロダクト以外のタスクをいくつか任されるようになりました。
ちょっとしたスクリプトを書くことが多いんですが、今年は社内で影響範囲の大きい仕組みづくりにも積極的に取り組んでいきたいと思います。
共通基盤的な仕組みを作ることと、その開発で得られた知見を社内勉強会等で共有していくことの二つが目標です。

影響範囲が大きいということは、自分の与える影響が大きくなるので、必然的に出せるバリューも上がると考えています。
ただ、その分メンテナンスしやすく、品質の高いコードを書きたいという気持ちも強くなるので、そういった意識をもって一年間取り組んでいきたいと思います。

所感

以上が、2024年の目標です。
エンジニアとしてさらなる成長をしつつ、組織としてスケールしていくことを念頭において、今年も頑張っていきたいと思います。

プライベートではスポーツや音楽ライブ参戦など、コロナ禍でできなかったことを楽しめる状況が戻りつつあるので、全力で楽しんでいこうと思います。
あと猫が飼いたいので家を買いたい。30代が近づいているので人生設計も考え始めないといけないなとぼんやり思ってます。

「t-wadaさんが後世に残したい、実録レガシーコード改善」を聞いて得た気づきメモ

はじめに

個人的なエンジニアとしての強みとして、テストやレガシーコード改善を武器にしていきたいと考えて日々プログラミングと向き合っています。
そんな中、t-wadaさんが後世に残したい、実録レガシーコード改善というイベントの再放送に参加し、自身のテストに関する解像度がさらに上がりました。
また、テストの考え方を人に伝える上でとてもいい表現をされていたので、そのあたりをメモ的に残しておきます。
あくまで個人的に印象に残った部分をピックアップしているので、全体の流れや実際のコード例についてはぜひ元のスライドをご覧ください。

対象イベント

発表スライドはこちらです。

概要

実際のスマートスピーカースキルの開発を通して、非エンジニアが書いたコードを引き継ぎ、テストを用意しながらレガシーコードを改善していくという内容です。

ソフトウェア開発の3本柱

  • Version Control
  • Testing
  • Automation

この中の優先度について、Version Control > Automation > Testingという順番であるという話がありました。
理由は「早期に用意することでより多くのレバレッジが効く」からだそうです。テストを専門にしているt_wadaさんがテストより自動化を優先するというのが意外で印象に残っています。
とにかく手作業を減らすことで、単純作業の時間を安定して削減することができ、かつチーム開発の場合はさらに全員が利益を得られるので、より効果が高まります。

自動テスト

自動テストは安全にコードを変更するための前線基地となります。
最初は網羅性は不要で、能天気な正常系(Happy Path)のテストを書くことから始めるといいとのことでした。
この辺りは、自分自身レガシーなコードに対してどんなテストを用意するべきか悩むことも多かったので、新しい気づきが多かったです。
特に、リファクタリングへの耐性を高めるために、実装から間合いを取ったテストを書く、という表現はわかりやすく、他人に伝わりやすいなと感じました。

ここから個人的な考えをまとめておきます。
自分の書くコードではクラスごとにユニットテストを用意していることが多いです。

これは、API単位のテストのような実装から間合いを取ったテストと比べるとリファクタリングへの耐性は劣りますが、網羅のしやすさ、つまりはメンテナンスコストが抑えられると考えています。
ただ、テストがないレガシーなコードの場合はそもそもテストが書きづらかったり、テストを書くなかでより良い設計に気づくことが多いため、ある程度実装から間合いを取ったテストにしておくと、安全に、より良い設計に近づけるのかなと思いました。

私が自分で書くコードの場合はある程度自分やチームの中で設計の方針が共有できており、大幅なインターフェースの変更が少ないため、クラス単位のテストのメリットが大きくなるなと考えています。
反対に、慣れていない技術を使用しているものなど、より良い正解を模索しながらインターフェースを変更していく段階では、実装から間合いを取ったテストの方が良いのかなと思いました。
また、テストには開発者のためのテストと、品質を担保するためのテストに分かれると思っていて、ユニットテストは開発者のためのテストとしての役割が大きいと考えています。

以下、t_wadaさんのスライドでもレガシーコード改善ガイドから引用されている言葉ですが、テストを書く場合にはコードを変更が必要となり、例外は少ないです。

コードを変更するためにはテストを整備する必要がある。多くの場合、テストを整備するためには、コードを変更する必要がある。

テストとリファクタリング

スライドでは、テストを整備する準備ができたらTDDのサイクルを回しつつ、機能追加等を進めていくんですが、途中で設計への不安に気づく場面が出てきます。

リファクタリングしないと内部の質は上がらないのに、リファクタリングを行うという発想がそもそも無くなっちゃっている。

テストを書いて変更を安全に行うことはできるようになったものの、リファクタリングを行っていないため、コードの設計は改善されず、ただ単にテストコードが増えていくだけになっています。

テストでは品質は上がらないですよ。テストはあくまでも品質をあげるきっかけ。品質を上げるのはプログラミングです。これは大昔からそう。

この言葉はとても印象に残りました。自分の中でもテストがあるコードは漠然と品質が高いんじゃないかと思っていたんですが、よくよく考えると、コード自体に鎧を着せるようなイメージで、コードの中身自体は変わっていないんだから、そりゃ品質は変わらないよなあと思いました。

テストはリファクタリングのために必要ですが、テストだけで終わっていては品質は変わらないというのは肝に銘じておきたいです。

このあと、スライドではリファクタリングに話題が移っていくんですが、引用されている言葉が実際の場面によく当てはまるので、こちらもメモしておきます。

「あとでクリーンにすればいいよ。先に市場に出さなければ!」 開発者はそうやっていつもごまかす。だが、あとでクリーンにすることはない。 市場からのプレッシャーは止まらないからだ。「先に市場に出さなければ」ということは、後ろに競合他社が大勢いるということである。競合他社に追い抜かれないためには、これからも走り続けるしかない。
その結果、開発者はモードを切り替えることができない。次の機能、また次の機能、またまた次の機能を追加することになり、コードをクリーンにすることまで手が回らない。
そして、崩壊が始める。生産性がゼロに近づいていく。

いろんなプロダクトの話を聞いてもよくある話ですね。コードは基本的には書いた瞬間から将来の変更の際の生産性を下げてしまうので、常にリファクタリングによってそのコストを抑えていく必要があります。
これを日常的にできなかった場合は、プロダクトが崩壊するか、どこかで膨大な工数リファクタリングが必要となります。
リファクタリングに限らず、ソフトウェアに関する諸々の問題は早期に発見して早期に対応するのが最も効果がかからないというのは、よく言われています。
早期発見、かつ安全な変更のためにテストを書き、リファクタリングも日常的に行うことが大切です。

設計の改善

スライドでは、リファクタリングをExtractという手法を用いて進めていきます。

比較されるのはSproutと呼ばれる手法です。

  • Extract(書籍: リファクタリング)
    • 既存のコードにテストを書いて保護しながら、新しいコードを抽出していく
  • Sprout(書籍: レガシーコード改善ガイド)
    • 既存のコードにテストを書くことはあきらめるとしても、せめて新しく書くコードだけはテストを書きながら開発する

具体的な設計の改善は以下のような手順で進んでいきますが、ここでは割愛したいと思います。

  1. 詳細への依存を減らす
  2. 状態遷移もモデル自身が判断できるようにする
  3. 結合度をさらに減らし、抽象度の高い関数のみを公開する

結果として、さまざまな設計の原理原則を適用していくと、最終的にはクリーンアーキテクチャのような依存の方向性が整理されたコードに近づいていく、という点はクリーンアーキテクチャを他人に説明する際にもきちんと伝えないといけないなと思いました。

所感

今回のイベントを通して、テストや設計に対する考え方がさらに深まりました。
また、これらの思想を他人に伝える際の語彙がかなり増えたので、今後に活かせる発表でした。
参考文献として挙げられている書籍の大半はすでに読んでいるんですが、エンジニアになりたてのころに読んでちゃんと理解できていないものもありそうなので、改めて読み直すことで新しい気づきを得られそうです。