Enludus
お問い合わせ

「動くコード」の先にあったもの ── インターンと個人開発で身についた 3 つの習慣

インターンでのコードレビュー、配信基盤やプラットフォームの開発、個人開発の運用で受けた指摘と失敗を棚卸ししたら、155 項目になりました。そこから、いまコードを書く前と後に必ずやっていることをまとめます。

これまでのインターン、短期のチーム開発、技術面接、個人開発で受けた指摘や失敗を、一度ぜんぶ書き出してみました。数えると 155 項目 になりました。

並べてみてわかったのは、2025 年 11 月に開発を始めてからの 1 年弱で伸びたのが「実装の速さ」ではなかったということです。変わったのは、コードを書く前と後に考える範囲 でした。

この記事では、155 項目をそのまま並べるのではなく、そこから見えてきた変化と、いま毎回やっている習慣を、実際の失敗と一緒に書きます。

以下は個人の振り返りです。会社名や個人名は出さず、社内のコードやレビューの具体的な内容にも触れていません。必要なところは抽象化しています。どの会社の公式な考え方でもありません。

5 つの変化

棚卸しを分野ごとにまとめると、大きく 5 つの変化に分かれました。

以前 いま
動くコードを書く 課題・利用者・業務ルール・制約に合ったシステムを考える
自分のコードを書く 既存のコードを調べ、チームとしての一貫性とレビューしやすさを守る
機能を完成させる テスト・CI・監視・ロールバックまで含めて、安全に変更を届ける
技術を選ぶ 何を守り、何を捨て、なぜそれを選んだのかを説明する
AI が出した答えを使う 速く作れるぶん、仕様・根拠・実際の動作を自分で確かめる

どれも言葉にすると当たり前に見えます。でも、どれも一度は失敗してから身についたものです。

習慣 1:処理を縦に追う

いちばん大きかった失敗は、ある長期インターンでテストを新しい書き方に移していたときのことです。

新しいヘルパーを作って移行を進めていたら、レビューで 「それ、もうあります」 と指摘されました。同じ役割のヘルパーやテスト基盤が、隣のディレクトリにすでにあったのです。調べ足りないまま作ったので、既存の責務をもう一度実装していました。

それ以来、変更の前に必ず 処理を縦に追う ようにしています。

  1. 入口(ハンドラーや画面)
  2. 呼び出される処理
  3. データの変換
  4. 入出力・外部 API
  5. データベース
  6. 後片付け

ここまで追って簡単にメモしてから、変更を始めます。1 つのリポジトリの中だけで追えないときは、gateway や別のサービスまで通信の経路をたどります。

縦に追うと、「名前を見て不要だと思った設定」が実は別のところで効いていた、という事故も防げます。配信基盤の開発では、使っていないように見えた出力を消しかけて、実はそれが音声を供給していたことがありました。

習慣 2:同じ責務の実装を横に比べる

縦に追ったら、次は 横に比べます。同じことをしている箇所がほかにないか、検索で探します。

  • 使おうとしているライブラリ・定数・型を、すでに使っている箇所
  • 似たテストの準備と後片付けの書き方
  • フレームワークに標準で用意されているメソッド
  • 隣のパッケージが公開している API

このとき、参照する側とされる側の 両方向 を検索します。片方向だけだと、もう用意されている仕組みを見落とします。

過去の Pull Request を参考にするときは、最初の差分ではなく 最後にマージされた状態 を読みます。レビューの途中で形が変わっていることがよくあるからです。

習慣 3:違いの理由を問う

横に比べて、似ているのに書き方が違うコードを見つけたら、「なぜ違うのか」 を問います。

理由があるなら、それに従います。理由が見つからないなら、既存のやり方に合わせます。既存の方式ではだめだという理由がないのに、新しい方式を増やさないようにしています。

逆に、似ているからといってすぐ共通化もしません。たまたま手順が似ているだけで、変更される理由が違う コードを 1 つにまとめると、片方を直したときにもう片方が壊れます。DRY は、変わる理由まで同じときにだけ効きます。

この 3 つ(縦に追う、横に比べる、違いを問う)が、155 項目の中でいちばん効いた習慣です。

テストは「何を保証するか」で考える

テストについては、正反対の指摘を両方受けました。

あるときは 「このケースが足りない」。新しく足した分岐や引数、エラーが複数あるとき、境界値のケースが抜けていました。

別のときは 「このテストは要らない」。下の層(変換処理など)ですでに確かめていることを、上の層でもう一度細かく確かめていました。

両方を経験して、テストは数ではなく 層ごとに保証することの対応表 だと考えるようになりました。

  • 変換処理は、変換が正しいことを保証する
  • サービスは、業務の流れが正しいことを保証する
  • ハンドラーは、入力と出力の約束を保証する

先に「この層は何を保証するか」を一文で書いてから、テストケースを変更の差分から表にしています。

ただし、削りすぎて約束を取りこぼさないようにもします。CSV を返す API なら、中身だけでなくヘッダ・ファイル名・Content-Type も利用者に見える約束です。

ほかに、具体的に効いたものを挙げておきます。

  • 日付で制度や税率が変わる処理は、時計を固定 して境界日の前後を確かめる
  • 冪等キーが同じでも本文が違うリクエストは 拒否する ことまでテストする
  • 「テストは独立している」は想像で済ませず、同じ ID を使うなどして実際に示す

Pull Request は「レビューできる単位」で分ける

Pull Request の分け方でも、正反対の指摘を受けました。フロントとバックと別機能の修正を 1 つに混ぜて「分けてほしい」と言われたこともあれば、テーブルだけを先に出して「これだけでは判断できない」と言われたこともあります。

いまは、1 つの Pull Request で、レビュワーが 1 つの目的を判断できる まとまりにしています。本文の Why には「変更したファイル」ではなく、「なぜ必要か、ないと何が困るか」を書きます。順番は、背景 → 判断 → 差分 → 検証です。

積み重ねた Pull Request は、下のものが変わるたびに上をリベースさせます。概念として関連していても、コードとして依存していない なら兄弟のブランチにしています。依存しているかどうかは、実際の import や参照をたどって判断します。

レビューの返信は、指摘を言い直すのではなく、採用したかどうかと理由、直した箇所へのリンクを返します。判断を任されたときは、自分で選んで理由を書きます。

測る前に、測り方を疑う

動画配信サービスの開発インターンでは、配信システムの負荷試験をしました。最初の結果は 数十 req/s でした。

サーバーが遅いのかと思って調べると、遅かったのは 負荷をかける側 でした。負荷の生成方法を変えて測り直すと、1,000 req/s を超える値 が出ました。

計測の数字は、それだけでは意味がありません。何を、どの条件で測ったのか と一緒に読みます。そして、測る道具そのものが限界になっていないかを先に確かめます。

同じインターンでは、PC では速いのに iPhone だけ配信が遅れる現象も追いました。「iPhone だから」で片づけずに、再生位置・バッファ・ライブの先頭からの距離を記録していくと、自分が書いた追従処理がそもそも呼ばれていない ことがわかりました。コードが正しそうに見えることより、実際に通った経路の記録を信じるようになりました。

障害の説明は、症状 → 仮説 → 観測 → 層の切り分け → 原因、の順にしています。「AWS だから」「端末だから」は原因ではありません。

忘れても壊れないようにする

同じ配信基盤で、本番用の Docker イメージにファイルが入っておらず、新しい機能が本番に出ていなかったことがありました。しかもデプロイの失敗でロールバックが走り、サイトは開くのに、動いていたのは古いバージョン でした。

開発用と本番用のステージが違い、ファイルを足すたびに手で追記しないといけない構造になっていたのが原因です。「次は忘れないようにする」では、また同じことが起きます。忘れても壊れない構造 に直しました。

再発防止は、注意力ではなく仕組みで塞ぐ。いまは、ミスが起きた経路を見て、それを CI やビルドの構造で防げないかを最初に考えます。

  • CI はプロジェクトの最初に、最小限の品質ゲートとして入れる
  • 個々の Pull Request が通っても、統合した状態 の main を確かめる
  • CI のイメージやツールは latest を使わず、バージョンを固定する
  • 生成ファイルは、同じコンテナ・同じツールで作り直す
  • 「サイトが開く」ではなく、動いているバージョン を確かめる

壊れたときに分かるようにする

プラットフォームエンジニアリングのインターンでは、構造化ログ、リクエスト ID の伝播、Readiness Probe を入れました。

構造化ログは、JSON にすることが目的ではありません。tenant やリクエスト ID で 検索できること が目的です。proxy からサービスまでリクエスト ID を引き回すと、1 つのリクエストを境界をまたいで追えます。

これが実際に役に立ちました。マウントするパスの食い違いで起動しない問題を、Readiness とログからすぐに特定できたのです。

Liveness と Readiness の違いも、ここで学びました。プロセスが 200 を返していても、トラフィックを処理できるとは限りません。かといって、共有している依存先が落ちたときに全部の Pod を Not Ready にすると、被害を広げてしまうこともあります。

可観測性や復旧のしやすさは、あとから足す便利機能ではなく 設計の一部 です。

自分の端末は、利用者の端末ではない

ここからは個人開発の話です。

StudyReel では、自分の iPhone の設定だけで確かめていたせいで、画面の消灯・ダークモード・OS の違いで起きる不具合を見落としていました。公開して実際に使われてから、初めてわかった不具合です。

いまは、端末・表示モード・設定・使う場所を並べた 確認表 を作っています。そして、公開をゴールではなく、使い続けてもらえるかを確かめるスタートだと考えるようにしました。

job match では、会社名や職種から目的の求人が見つからなかったり、探し終わる前に「見つかりません」と出てしまったりしました。探す・取ってくる・分析する・失敗を分類する・キャッシュする、を分けて確かめるようにしてから、どこで失敗しているのかを利用者にも伝えられるようになりました。

AI に書かせても、説明する責任は残る

いまは実装も調査も、AI に任せられることが増えました。それでも、要件・制約・優先順位・根拠の確認は自分の仕事です。

AI は、既存のコードを調べる工程を飛ばして新しい実装を作りがちです。習慣 1 と 2 を飛ばして書かれたコードは、まさに冒頭の「それ、もうあります」になります。だから、生成させる前に検索と既存 API の確認をします。

技術面接で、AI と一緒に書いたコードの設計を聞かれたことがあります。検索処理をどこに置いたか、時計をどう注入したか、責務の境目はどこか。自分の言葉で説明できないなら、理解した設計とは言えない と実感しました。

AI が出したクラウドの構成や費用の見積もりは、実測と一次情報で裏づけます。「その数字はどこから来たのか」を必ず確かめます。

作る速度が上がるほど、テスト、実機での確認、実データでの検証を厚くする。品質の基準は「AI に書かせられたか」ではなく、自分が保守できる状態か だと考えています。

実装のたびに見るチェック

最後に、いま実装のたびに見ているチェックを載せておきます。

  • 書く前:目的・期待する動き・範囲・前提が合っているか。縦の経路と横の既存実装を見たか
  • 設計:責務・依存・変更の小ささ(行数ではなく、増える概念の数)・読みやすさ・エラー・DB の制約
  • テスト:層ごとに何を保証するか、既存のテスト基盤、正常・異常・境界、独立性、CI で動くか
  • 説明:Pull Request が存在する理由、レビュワーに合った範囲、自明でない判断の理由、参照した資料
  • レビューのあと:指摘の意図、根本の原因、同じ種類のコードへの横展開、再レビューの依頼

これから

まだ身についていないこともあります。

データベースの選び方や構成の妥当性を、一般論ではなく実際の読み書きのパターンから説明すること。理解の浅いテーマを、単語ではなく具体例・反例・実行経路で確かめること。どちらもこれからの課題です。

155 項目の原本は、自分用のチェックリストとして持ち続けます。テストの責務、Pull Request の分け方、壊れても分かる設計など、それぞれのテーマは、別の記事でもう少し掘り下げるつもりです。