プログラミングを初めて1年ちょっとで指摘されたこと、ミスしたこと


去年の 4 月頃からプログラマーとして働きはじめて 1 年と 3 ヶ月が経ち、2021 年も半分が終わった。今でも会社で経理や労務の仕事をしたりビールサーバーを洗ったりする仕事も続けているが、プログラムを書く時間は次第に増え楽しくなってきた。

未経験で転職したこともあり自分が(趣味以外で)書いたコードのほぼ 100% が上司にレビューされているのだが、振り返るといろいろ初歩的なミスをやらかしてきたような気がする。

せっかく時間を割いてもらっているので、印象に残ったコードレビューやプログラミング、仕事に取り組む姿勢に関する指摘を書き残しておくことにした。

この記録がいつかどこかで誰かの役に立つと嬉しい。なお、この記事は私自分の認識に基づいているので、上司の知識や情報を完全に反映していない、もしくは誤った解釈をしている可能性がある。

基本

  • 何をするにしても公式ドキュメントを読む
    • よくある落とし穴(例: Nginx)とかだいたい書いてある
    • 初学者が踏みそうな穴は既に誰かが踏んでいると考えてよい
  • タスクを分解し、変更を確実に動作する小さな単位で積み重ねていく
    • 例えば React でフォームを作るなら…
      1. クライアント
        • ボタンを押したときにモーダルが出るようにする
        • モーダルが出たらフォームを表示してみる
        • フォームを submit したら console.log するだけの Hook を作ってみる
      2. サーバー
        • フォームの送付先のルーティングを追加し、 ok だけを返す API を作る
        • ↑ が動いていることを curl 等で確認
      3. 再びクライアント
        • console.log を ↑ で作った API に切り替える
        • レスポンスが ok であることを確認する
      4. ↑ ここまでできたら一通り動いたことになるので、フォームの要件に応じてビジネスロジックを作り込んでいく

Git/GitHub

  • コミットや PR は小さな単位で行う
    • Diff が大きすぎるとレビューに大きな時間的・精神的コストがかかる
    • 指摘があったときに軌道修正しやすい
  • 不要なコミットは squash でまとめてから出すべし
    • 基本は所属する開発チームの流儀に従うのがいいらしい
  • コミットメッセージは命令形で書く
  • 原則的に履歴改変はしない
    • 例えば rebase して履歴を書き換えると、その branch を他の人が触ってた場合 pull できなくなる
    • 改変はその branch を誰も触ってないという確信があるときのみやっても可
      • 例えばまだ remote に push してないとき
  • OSS に PR を出すときはまず Fork して自分のアカウント配下で修正版を作り、 upstream に PR を出す
    • 「Pull」を「Request」するから PR
    • GitLab だと Merge Request と呼ばれている
      • ワークフローが GitHub と違い、同一リポジトリ内でブランチを作成するから(GitHub も同じリポジトリでブランチ作る場合はあるけど、それはまぁ気にしなくていいらしい)

上司の Git のワークフローの基本的な考え方は下記の記事に近いので読むようにと言われた。

https://songmu.jp/riji/entry/2021-05-19-my-git-workflow.html

セキュリティ

  • パスワードは何があろうと平文で保存してはいけない
    • 入力を受け取ってからできるだけ早い段階で暗号化しなければならない
    • 世間的に実績のあるハッシュ関数を使って暗号化・ストレッチングする
  • SSH の port は well known port を避ける
    • 本質的にはめちゃくちゃ有効な対策ではない(パスワードログイン禁止して強度の高い rsa 鍵をきちんと管理するとかの方がよほど重要だ)が、余計な口を開けておく必要はない
      • けど、 SSH はだいたい何でもできてしまうので特に気をつける
      • 他にも使わない port があったら積極的に閉じる
  • あえて自分の開発環境で脆弱性を作ってそれを攻撃してみる
    • 気づかぬ内に脆弱性を作り込んでしまう可能性はいたるところにあるので、基本的な概念やどういう状態が危険かは理解しておく
      • たとえば SQL 組み立てるときにはプレースホルダを使うべき、とか
      • ログインが必要なページでは CSRF トークンを作るべき、とか
    • 一見ちゃんと動いているように見えて実は穴だらけ、というケースはよくある
      • 脆弱性のあるバージョンの WordPress で動いているウェブサイトとか…
    • きちんと対策をするには HTTP/HTTPS の仕組みとかブラウザが何をするかとか基本的な知識が重要

命名

  • プログラミング言語ごとの習慣やチームのやり方に合わせる
    • JavaScript であれば変数や関数の名は基本的に lowerCamelCase でクラス名は UpperCamelCase
    • 他の人が読んで感じる「この関数はこう動きそう、この変数にはこういうデータが入ってそう」という直感を裏切らないような名前にする
      • プロジェクト内で命名の法則に整合性がないと、読みづらいコードになってしまう

設計

  • 利用者が UI 上で何らかの入力をしてデータを取得・変更するまでの流れを意識しながら、責務ごとに層を分割する
    • 例えば…
      • 認証や入力値のチェックを行う層
      • 実現したいビジネスロジックを書く層
      • ビジネスロジックからデータを受け取ってデータベースに save する層
    • 実装にあたっては層ごとに何を引数として受け取って何を解決するかの役割を常に意識する
  • 設計の早い段階でレビューを受ける
    • 例えば…
      • データベースや API のスキーマを作ったらレビューしてもらう
      • ビジネスロジックやデータの操作をする関数をシグネチャだけ作ってレビューしてもらう
      • こうしておけば、何か見落としや勘違いがあったとき早めに軌道修正できる

コーディング

  • エディタやシェル、ターミナル環境は自分で使いやすいようにカスタマイズする
    • 使い込んでる人の様子を後ろから、もしくは画面共有してもらって眺める
    • 他人の設定ファイル(.vimrc とか .zshrc とか .tmux.conf のように dot . で始まるので dot files と呼ばれる)を参考にして自分のをメンテナンスしていく
  • キーボードから手を離さずにあらゆる操作ができることを目指す
    • 次の単語や特定の行にジャンプするとか、n 行削除するとか

上司は Vim を使っていたので当初は Vim を使うことにした。Vim には vimtutor というターミナルから起動できる公式チュートリアルがあって、30 分くらいで基本的な操作を学ぶことができる。

いまは VSCode を Vim キーバインドで使っているが、コードを編集するスピードをどうやって上げるかを考えるとき「Vim を少しでも触っててよかった」と思うことが多い。Vim にしろ他のエディタにしろ無限に機能があるので、自分が使うところから学んでいくのがよさそう。

デバッグ

  • エラーで動かないときは Stack Trace から自分が書いた行を拾う
    • ライブラリがバグってるより自分の書いたコードがバグってる可能性の方が高いので、まずは自分が書いた行を読む
  • 開発中は変数に何が入っているかをコンソールに出しながら進めるとよい
    • JavaScript なら debug を使うとどこで出してるデバッグのログなのかがコンソール上できれいに色分けされて見られるので便利

テスト

  • カバレッジを目的にしない
    • 全ての行にテストを書く必要はないけど、テストを書いたほうが変更しやすいのは間違いないのでどんどん書きましょう
  • ミスした箇所や複雑なビジネスロジックのテストを重点的に書く

CSS

  • 画面のレイアウトには flexbox を使う
  • とにかく練習あるのみ
    • Chrome Dev Tools で既存の Web サイトの CSS を書き換えたりすると楽しい
    • デザインに自信が無い場合はルールだけ覚えておくとよい(xx と yy の間は zz 以上開ける…とか)

JavaScript/TypeScript、その他一般的なプログラミングに関する指摘

  • 配列の繰り返し処理をするときは C 言語っぽい for 文ではなく map や filter 等の Array インスタンスに用意されたメソッドを使う
  • 非同期処理を書くときは Promise そのままではなく async/await を使う
    • Promise 自体を理解しているという前提
    • ほぼ async/await でいいけど、何でもかんでも async/await で書けばよいという訳ではない
      • 複数の非同期処理を並列で実行したい場合は Promise.all を使うだろうし
  • 繰り返し処理の中で SQL を発行するようなコードは N+1 Problem という典型的なアンチパターンなので避けるべき
    • 例えば、あるオブジェクトの配列から id というフィールドを取り出してそれで SELECT するなら、オブジェクトの map で SQL を発行するのではなく、まず id を map で取り出してからそれをまとめて一つの文で SELECT するのがよい
    • このケースに限らず、無駄に効率の悪い SQL 文を発行してないかは常に気にした方がよい

React

  • コンポーネントは原則的にクラスではなく関数で書く
    • つまり、引数 props を受け取って JSX を返す関数として実装する
    • クラスが有効なケースは例外的・限定的(ほぼ無いと考えてよい)なので、それを理解できるまでは関数で書く
    • バケツリレーしない(特定の範囲に閉じている)状態の管理は Hooks を使う
      • グローバルの状態管理には Context API や Redux 等のライブラリを使う
  • データは基本的にコンポーネントの階層関係における「上」から渡していく
    • Flux Architecture と呼ばれている体系的な考え方があるらしい
      • 以下のようにデータを管理して流れる方向を統一する
        • 何かしらのユーザーの行動(ボタンをクリックしたり)に応じて何らかの処理を行う
        • それにより何かしらのデータが更新される
        • 更新されたデータに応じて画面の見た目を更新する
      • 機能的にはこの方針に従わなくても実現は可能だが、 Flux Architecture に従うことで、どこで何をやってるかが分かりやすくなる
  • コンポーネントはその役割や責務に応じて適切なサイズに分割する
    • 例えば…
      • 同じ画面を構成するコンポーネントであっても、「ビジネスロジックを呼び出すコンポーネント」と「データを受け取って描画するコンポーネント」は分ける
      • ちょっと古い記事だけどこの記事が分かりやすい

その他

  • できるだけスペックの高いマシンを使う
    • 例えば Docker for Mac はデフォルトで 2GB のメモリ(変更可)が割り当てられておりその範囲内で動くけど、メモリが足りなくてコンテナやサービスの起動に失敗するケースがあったりする
    • アプリやコンテナをたくさん立ち上げても余裕で動くような高いスペックのマシンを求めましょう

いい資料

ここ 1 年半くらいで特に勉強になった本や資料を挙げておく。いいエンジニアの人はいい資料を知っているのでどんどん教えてほしい。

まず MDN Web Docs。Web に関係することがだいたい何でも書いてある。

https://developer.mozilla.org/ja/

転職当初、これは絶対に読めと言われた「ふつうの Linux プログラミング」という本。強く勧められたので印象に残っている。 head とか grep のコマンドを作るのが楽しかった。

https://www.amazon.co.jp/dp/4797386479/

IPA(情報処理推進機構)の「安全なウェブサイトの作り方」という資料。 XSS、CSRF、SQL インジェクション等の一般的な脆弱性の仕組みやそれを防ぐ方法が書かれている。

https://www.ipa.go.jp/security/vuln/websecurity.html

JavaScript の Promise が全く理解できなかったときに読んだ「Promise の本」という Web サイト。これが無かったら挫折していたかもしれない。著者の azu さんという方に細々と毎月 GitHub Sponsor しています。

https://azu.github.io/promises-book/#introduction

↑ と同じ著者の方が書いている JavaScript Primer という Web サイトも素晴らしい。 JavaScript はアップデートが早いので、ググって出てきた情報が古いことがよくあり、初心者にとって信頼できる情報源はとても重要。これ無料でいいのか!?という気持ちが強い。

https://jsprimer.net/

React の公式ドキュメント。単に API の仕様だけでなく、 React がどのように動作するかの仕組み(例えばコンポーネントのライフサイクル等)が詳しく記述されている。

https://reactjs.org/docs/getting-started.html

Shopify の GraphQL Design Tutorial。 GraphQL のスキーマをどうやって作ればいいか全く分かってなかったので勉強になった。

https://github.com/Shopify/graphql-design-tutorial/blob/master/TUTORIAL.md

最近みつけた「仕事ですぐに使える TypeScript」という Web サイト。1 日で一通り読める分量で、一般的な JavaScript 周りの環境構築(テスト、フォーマッター/リンター、CI、Docker イメージ作成、React/Vue/Electron などの環境)の情報がまとまっているのがよい。

https://future-architect.github.io/typescript-guide/

HTTP の進化をたどる「Real World HTTP」。Go で簡単なクライアント/サーバーを実装しながら HTTP/HTTPS の仕組みを一通り学べる。分厚い本なので、連休中などで一気に進めてどこに何が書いてあるかを把握してから辞書的に使うのがよさそう。感想を前回のブログで書いた

https://www.amazon.co.jp/dp/4873119030

印象に残ったミス

自分が開発中にやった恥ずかしいミスたちの例。これ以外にも無限にあるけど思い出したやつを書いた。

  • Array.prototype.find メソッドを使って配列から特定の条件を満たす要素だけを返す処理を実装していたときに比較(===)を代入(=)で書いてしまった
  • 練習用の Web サイトを .dev ドメインで作ったが HTTPS 専用であることを知らず、強制的に HTTPS にされるのをがんばって HTTP でアクセスできるようにしようとした(できなかった)
  • Node.js の LTS (偶数バージョンの Long Term Support)の概念を理解しておらず、数字が大きいほど良いやつだと勘違いして LTS でない奇数バージョンを積極的に使っていた

終わりに

何とかクビにならずにやっています。リモートワークで物理的に人間に会うのが 2 週間に 1 回くらいなので、もともと低い社会性が更に落ちてきました。

これからもがんばります。