ジュニアエンジニアはAIを使うべきか?
結論:理解を深めるために、AIを「積極的に利用すべき」
最近のAIの進化によって、コードをAIエージェントが書き、エンジニアはレビューして調整するという開発スタイルが現実味を帯びてきました。
たとえば、開発フローが以下のように変化していく未来は十分に想定できます。
- AIエージェントがコードを書く
- エンジニアがレビューする
- 必要に応じてコードを調整・最適化する
このような未来では、エンジニアには「AIが書いたコードを評価し、適切にチューニングする力」が求められます。
なぜ今、AIに頼りすぎるのが危険なのか?
AIは非常に優秀ですが、完璧ではありません。
生成されるコードは以下のような課題を含むことがあります。
- 要件よりオーバースペックな実装
- 意図と若干ズレた挙動
- 冗長・非効率なアルゴリズム
これらを見抜き、「正しく」修正できる能力がなければ、AIを使いこなすことはできません。
深い知識がなければ、AIは“ブラックボックス”になる
数年後、もしAIエージェントが企業の開発環境においてインフラのように当たり前の存在になっていた場合、
そこで活躍できるエンジニアは、AIの出力を理解し、修正できる人材です。
クラウド時代において、サーバーやネットワークの中身を触らずにインフラを「構築した気」になっていたように、
AIに任せきりになると、プログラミングの中身を理解する機会を失う可能性があります。
今後どうすべきか?
今こそ「AIを壁打ち相手にして」知識を深めるべき
今後1~2年は、AIを使って学習効率を高めるために最大限活用すべきタイミングです。
具体的な活用例
- 技術書を読むときに「なんとなく」しか理解できない箇所を、AIと対話して補う
- 未知の領域の本を読むとき、サンプルコードを全て自分で試す時間がなくても、AIに仮説をぶつけて検証する
- コマンドや低レイヤーな処理を、都度AIに聞いて確認する
たとえば、私自身もインフラの理解を深めたいときに、休日を丸一日潰して環境構築していました。
しかし今は、AIに確認しながら仮想環境や構成の違いをサクッと確認できます。
まとめ
- AIは便利だが、それに頼りきるのは危険
- 今後求められるのは「AIと同じかそれ以上の理解力」
- 今のうちに「AIを学習の補助」として最大限使って、自力を上げるべき
あと1〜2年、AIを使って楽をするのではなく、
AIを使って“学ぶ速度を上げる”ことに集中しよう。
今こそ、未来に生き残るための土台作りの時期です。
N+1問題とは?基礎から対策まで解説
この記事を読むと得られること
- ✅ N+1とは何か をしっかり理解できる
- ✅ N+1の対策 を自信を持って打てるようになる
- ✅ データ取得時に意識すべきパフォーマンス観点 を身につけられる
データベースとアプリケーションの間でやりとりされるSQLクエリ。
その中でも開発初期によくある落とし穴が「N+1問題」です。
この問題を放置すると、リリース後にアプリが遅くなるなど重大な影響を及ぼします。
この記事では、N+1がどういう場面で発生しやすいのか、そしてどのように対策を打つべきかを丁寧に解説していきます。
N+1問題とは何か?どんな時に発生するのか?
N+1問題とは、データ取得の際に 不要に多くのSQLが実行されてしまう状態 を指します。
たとえば、以下のような処理を見てみましょう。
// ユーザー一覧を取得 users := db.Find(&[]User{}) for _, user := range users { // 各ユーザーのTodoを取得 db.Where("user_id = ?", user.ID).Find(&[]Todo{}) }
この場合、ユーザー一覧を取得する1回のクエリに加え、ユーザーごとに関連するTodoを取得しており、ユーザーの数だけクエリが追加で実行されます。
つまり、N人のユーザーに対してN+1回のクエリが発行されることになります。
データ取得で気をつけるべき2つのポイント
N+1を理解するためには、まずパフォーマンスに影響する基本的な観点を押さえることが大切です。
1. SQLのリクエスト回数
クエリ数が増えるほど、アプリケーションとデータベースの間の通信が増え、処理速度に悪影響を及ぼします。
2. テーブル結合(JOIN)のコスト
複数のテーブルを結合するとDB内での処理が重くなります。大量データをJOINする場合は特に注意が必要です。
N+1の何が問題なのか?
要点は「SQLのリクエスト回数が増えてしまう」という点に尽きます。
1件ずつ関連データを取得することで、ユーザー数に比例してクエリ数が増加。
その結果、処理が遅くなったり、DBへの負荷が急増したりと、アプリケーションのパフォーマンスが著しく悪化します。
N+1問題の対策方法
N+1問題を回避するためには、以下のような方法があります。
● Preload(GORMなどのORマッパーで使用)
関連データをあらかじめ一括で取得しておく方法です。
db.Preload("Todos").Find(&users)
これにより、ユーザー一覧と各ユーザーのTodoを 2クエリで一気に取得 できます。
● JOIN を使う(用途に応じて)
場合によっては JOIN を使って関連データをまとめて取得する方法も有効です。ただし、JOINを多用するとデータ量が膨らみ、パフォーマンスに影響する可能性もあるため、慎重に使う必要があります。
💡 PreloadとJOIN、どちらを選ぶべきか?
この判断は場面ごとに異なり、データ構造や用途に応じた知識が必要になります。
このテーマは中級者以上向けの内容になるため、別記事で詳しく解説します。
まとめ:中級者への第一歩は「パフォーマンス意識」
今回は「N+1問題」について基本から対策までを紹介しました。
重要なのは、単にコードを書くのではなく、クエリの数や処理の重さに対する意識を常に持つことです。
- クエリが何回実行されているか?
- 1回のクエリでどれだけのデータを持ってくるか?
- PreloadやJOINは適切に使われているか?
こうした視点を身につければ、日々の開発で「なんとなく書いている処理」が一段レベルアップし、パフォーマンスを意識できる中級者へのステップアップになります。
📘 次回予告:PreloadとJOINの違いと選び方を徹底解説!
さらに深く知りたい方のために、別記事で実際の使い分けパターンやベンチマーク結果も紹介する予定です。
🧑💻 バックエンドエンジニアの独り立ちに必要な知識まとめ
前提: フロントとAPIが分離された構成(SPA + REST APIなど)を想定。
1. 🔐 認証・認可の理解と実装
✅ 認証(Authentication)
- 基本:
email+passwordによる認証 - パスワードのハッシュ化(bcryptやargon2など)
- セッション管理の考え方(クッキー or トークン)
- ※余裕があれば: OAuth2やGoogleログインなどのソーシャルログインも理解しておくと◎
✅ 認可(Authorization)
2. 📋 要件定義と設計力
✅ 要件定義
✅ 詳細設計
- 画面ワイヤーフレーム(フロントとの連携を意識)
- APIインターフェース設計(REST or GraphQL)
- OpenAPI(Swagger)による仕様書管理ができると◎
- DB設計
- ER図が描ける
- 正規化・非正規化の理解
- インデックス設計
- N+1問題の回避
3. 💻 実装力
✅ コードの質
✅ 言語習熟
✅ SQL
4. 🧪 テストと品質保証
5. 🧯 エラーハンドリング・ロギング
- 適切なHTTPステータスコードの返却
- エラー構造体を使った統一的なレスポンス
- ログ出力(zerolog, zap, logrusなど)
- リクエストIDを入れる
- 構造化ログの出力
- エラートラッキングツールの導入(Sentryなど)
6. 🛡 セキュリティの基本
- SQLインジェクション対策(プリペアドステートメント)
- XSS対策(出力時のエスケープ)
- パスワードの安全な取り扱い
- 脆弱性スキャンツール(Dependabotやtrivyなど)
7. 🚢 運用・デプロイ
- Dockerでのローカル開発環境構築
- デプロイの流れ(GitHub Actions + VPS/AWS/Heroku など)
- 簡単なインフラの知識(DNS, TLS, Webサーバなど)
- APIの監視・メトリクス収集(Prometheus + Grafana)
- エラーログの通知(Slack連携など)
✅ おまけ: こんな状態なら独り立ち!
- 要件を聞いて自分でAPIを設計・実装できる
- DBスキーマを自分で設計し、マイグレーションを管理できる
- バグが起きてもログから原因を追える
- 他の人のコードを読んで、レビュー・改善提案ができる
- 小さくても本番環境にデプロイした経験がある
✍️ まとめ
バックエンドエンジニアの独り立ちに必要なのは、
「設計 → 実装 → テスト → デプロイ」までを自分の力でやり切れる力。
本質は「一人でシステムを回せるか」ではなく
「チームで困っているときに自分が支えられるか」だから、
広く浅くじゃなくて、「一点突破で深く」でもOK!
未経験からエンジニアを目指す人へ:まずは業態と職種の違いを知ろう
はじめに
エンジニアを目指している方へ、最初に強く伝えたいことがあります。
エンジニアになりたいなら、まず「企業の業態」と「エンジニアの種類」を理解しましょう。
なぜそれが大事なのか?
努力の方向を間違えてしまうからです。
例えば、少しプログラミングを学んだだけで知識が浅い状態でSES企業に入社してしまうと、開発業務に関われないままキャリアが停滞することがあります。
これは本当によくある話です。
エンジニアを目指す前に確認すべき「2つの軸」
1. 企業の業態(どのような会社で働くか)
企業には様々な業態があり、それによってエンジニアとしてのキャリアの積み方も大きく異なります。以下に主な業態を紹介します。
1. 自社開発エンジニア
自社サービスの運用・改善・新規開発に関わります。プロダクトの成功が自社の成長に直結するため、エンジニアの影響力が大きいです。
→ 参考1, 参考2
2. SES(System Engineering Service)
クライアント企業に常駐し、システム開発や保守を行います。柔軟な人員配置が可能で、案件によって働く場所や内容が変わります。
→ 参考, 参考, 参考
3. SIer(システムインテグレーター)
要件定義から設計、開発、導入、保守まで一貫してITシステムを請け負う企業。プロジェクトマネジメント色が強いです。
→ 参考, 参考
4. 社内SE
自社の業務効率化のために社内システムを開発・運用します。内製化が進んでいる会社では開発スキルも求められます。
→ 参考
5. コーポレートエンジニア
会社全体の課題をITで解決することに重きを置き、経営に近い視点を持つことが求められるエンジニアです。
→ 参考
2. エンジニアの職種(どんな技術に携わるか)
もう1つの軸は、エンジニアとしての「専門分野」です。ここを間違えると、やりたい仕事に辿り着けません。
| 職種 | 概要 |
|---|---|
| フロントエンド | Webページやアプリの「見える部分」を作るエンジニア。HTML/CSS/JavaScript などを使います。 |
| バックエンド | サーバー側のロジックを担当。APIの設計やデータベースとの連携など。主にGo、Java、Pythonなど。 |
| インフラ | サーバーやネットワークの設計・構築・運用を担当。クラウド(AWS, GCPなど)の知識も必要。 |
| ネットワーク | 通信機器の設定やネットワークのセキュリティ対策を行う。物理的な機器に近いレイヤーを扱います。 |
| モバイル | iOS/Androidアプリの開発。SwiftやKotlinが主な言語。 |
| データエンジニア | ビッグデータやデータ分析の基盤を作る。ETLパイプライン構築、SQLやPythonなどを多用。 |
🔰 未経験から始めるならここ!
未経験からエンジニアを目指すなら、フロントエンド・バックエンド・モバイルのいずれかから始めるのがおすすめです。
その理由は以下の通りです:
- 実際に手を動かしてアプリやWebサイトが動く体験が得られるため、学習のモチベーションを維持しやすい
- ITの専門知識が少なくても始めやすい
- 求人数が多く、需要が高い職種である
例えば、フロントエンドでは簡単なWebページを作ることから始められ、モバイル開発ではスマホアプリを自分で作ってストアに公開することも可能です。
これらの分野は「成果物を見せやすい」という点でも、ポートフォリオを作って転職活動に活かしやすいのが特徴です。
結論:事業会社を目指すべし
SNSやYouTubeでよく見かける「キラキラしたエンジニア像」の多くは、事業会社か良質なSESに属する人たちです。
その中でも、未経験からエンジニアになるなら、事業会社を目指すのが圧倒的におすすめです。
理由1:転職ルートが確立されている
未経験から事業会社に転職するためのロードマップや教材が豊富にあります。
学習方法やポートフォリオの作り方については、別記事で詳しく解説予定です。
理由2:SIerは「マネジメント職」が中心
SIerは開発というよりプロジェクト管理が主な業務です。そのため、技術力を高めたいなら不向きかもしれません。
理由3:SESには"ガチャ"要素がある
SESでは、案件にアサインされた時点で売上が立つため、スキルのマッチよりも「配置できるかどうか」が重視されがちです。
もちろん良いSES企業もありますが、未経験のうちは見極めが非常に難しいため、信頼できる知人やエンジニアの助言が必要です。
まとめ:まずは事業会社を目指して戦略を立てよう
エンジニアになりたいなら、まずは事業会社のエンジニアを目指すことをおすすめします。
そして、最初の狙い目はベンチャー企業です。
ベンチャー企業が未経験でも採用する理由や、狙い方の戦略については、また別の記事で詳しく解説します。
今後も、未経験からエンジニアになるためのリアルな戦略を発信していきます。
少しでも参考になったら、ぜひ他の記事もチェックしてみてください!
AI頼りのジュニアエンジニアが増加する現状への警鐘
はじめに
近年、AIツールに依存するジュニアエンジニアが増加傾向にあります。AIの活用自体は有効な手段ですが、キャリアの初期段階にあるエンジニアは、まずは基礎力を養うことに注力すべきではないでしょうか。
現状の課題
AIの予測変換を使って同じようなコードを10回書くよりも、試行錯誤しながら数回書く方が、確実に技術力として身につきます。
なぜ今、自力でスキルを磨くべきなのか
1. AIによる市場価値の変化
- AIの普及により、未経験者でも一定レベルのポートフォリオ作成が可能に
- ジュニアエンジニアの差別化が困難に
- 表面的なスキル基準の上昇により、本質的な技術力向上の時間が減少
- 今後数年が、自力での技術力向上の重要な期間
2. 信頼性の問題
- AIに依存したコード作成は、コードレビューで説明が困難
- チーム内でのコミュニケーションや技術的な議論で支障をきたす
- 複雑な実装では、AI単独では対応できない場面が発生
3. シニアエンジニアとの競争
- AIの効果的な活用には、適切なプロンプトエンジニアリングが重要
- シニアエンジニアがAIを本格活用し始めた場合、経験値の差が更に顕著に
- ジュニアエンジニアの仕事機会が減少するリスク
結論
ジュニアエンジニアにとって、今後数年間が技術力向上の重要な機会となります。AIツールを補助的に活用しつつ、基礎的な技術力の向上に注力することが、長期的なキャリア形成において重要だと思います。 エンジニアは、年数が上がると給料が割と上がりやすい反面、年数に見合ったスキルがないと仕事がしんどくなってしまうので、離脱してしまう人も多いのではないかなと思っています。
結構SNSで見かけるのが、ジュニアエンジニアでスキルを培うべき時間を、SNSで小遣い稼ぎすることに時間を割いている人をよくみます。 その類の人は割と高い確率で、エンジニアを辞めてしまっているイメージです
AWS におけるサブネットの切り方
AWS におけるサブネットの切り方
AWS でのネットワーク構成を考える際に、VPC(Virtual Private Cloud)とサブネットの設計は非常に重要です。本記事では、細かい技術的な話ではなく、イメージがつきやすい形で AWS におけるサブネットの考え方を調べてみました
AWS でサブネットを切る目的
AWS でサブネットを切る目的は、サーバーやデータベースなどのリソースを配置する空間を作ることです。サブネットは、VPC 内で IP アドレスの範囲を分割し、管理しやすくする役割を持っています。
例えば、VPC を一つの "建物" に例えると、サブネットは "部屋" に相当します。部屋ごとに用途を決めることで、管理しやすくなり、セキュリティ面でも役立ちます。
なぜ 10.0.0.0/16 を使うのか?
AWS では、VPC のデフォルトの CIDR(IP アドレス範囲)として 10.0.0.0/16 がよく使われます。これは、次のような理由から選ばれることが多いです。
- 十分な IP アドレス数:
10.0.0.0/16は 65,536 個(216)の IP アドレスを確保できます。企業内ネットワークでも十分な数です。 - プライベート IP アドレス:
10.x.x.xはプライベート IP アドレスとして予約されており、インターネット上で重複しても問題がありません。 - 管理しやすい:
10.0.0.0/16を基準にして、用途ごとに10.0.x.0/24のサブネットを作るのが一般的です。
なぜサブネットは 10.0.0.0/24 で切られることが多いのか?
サブネットを 10.0.0.0/24 で切る理由は、
- 適度な IP アドレス数:
/24のサブネットでは 256 個(28)の IP アドレスを確保できます。 - 管理がしやすい:用途ごとに
10.0.1.0/24、10.0.2.0/24など、規則的に区分できるため、シンプルなネットワーク構成に適しています。 - AWS の推奨:AWS のドキュメントでも、一般的な用途では
/24を推奨しており、設定時のデフォルト値にもなっています。
/25 でサブネットを切ると管理が細かくなる理由
/25 のサブネットは、128 個(27)の IP アドレスを持ちますが、管理が煩雑になりやすいです。
IP アドレスの管理が複雑になる
/24なら 256 個の IP をひとつのまとまりとして扱えますが、/25にすると 128 個ずつに分かれ、管理する範囲が増えます。- 例:
10.0.1.0/25と10.0.1.128/25の 2 つのサブネットができ、それぞれでルーティングやセキュリティグループを設定する必要があります。
繰り上がりの管理が必要になる
/24なら 10.0.1.0 → 10.0.2.0 → 10.0.3.0 とシンプルに増えていきます。/25では 10.0.1.0 → 10.0.1.128 → 10.0.2.0 → 10.0.2.128 というように、途中で繰り上がりを考慮する必要が出てきます。
セキュリティグループやルートテーブルの管理が煩雑になる
/25で細かく分けると、サブネットごとにルートテーブルやファイアウォールルールを個別に設定する必要があり、運用が複雑になります。
シンプルなネットワーク構成の例
例えば、API サーバーと DB サーバーを設置するシンプルな構成を考えてみます。
ネットワーク構成のイメージ
VPC: 10.0.0.0/16 ├── Public Subnet: 10.0.1.0/24 (API サーバー配置) ├── Private Subnet: 10.0.2.0/24 (DB サーバー配置)
なぜこの構成にするのか?
- API サーバーをパブリックサブネットに配置: インターネットからアクセスできるようにするため。
- DB サーバーをプライベートサブネットに配置: 外部から直接アクセスできないようにし、セキュリティを確保するため。
- NAT ゲートウェイの利用: DB サーバーがインターネットに接続する必要がある場合は、NAT ゲートウェイをパブリックサブネットに配置。
まとめ
AWS でのサブネットの切り方は、シンプルな構成にすることで管理しやすくなります。
- VPC は
10.0.0.0/16を使うことが多い - サブネットは
10.0.x.0/24で切ると管理しやすい /25で分けると管理が煩雑になるため、適切な設計が必要- API サーバーと DB サーバーを配置する場合、パブリックサブネットとプライベートサブネットを分ける
この考え方を元に、自身のシステムに最適なネットワーク構成を考えてみてください。
Go における `make` の使うタイミング
Go における make の使うタイミング
Go でスライスやマップ、チャネルを作成する際に make を使うべきタイミングについて調べてみました
make のメリット
make を使う最大のメリットは 動的に変数を定義できること です。しかし、スライスに関しては make を使わず []T{} のリテラルで定義することも可能です。では、なぜ make を使う必要があるのでしょうか?
make を使う理由: メモリ再割り当てを避ける
スライスの append による動的な拡張時には、キャパシティ(capacity)を超えるとメモリの再割り当てが発生 します。これにより、
- 新しいメモリ領域の確保
- 既存の要素のコピー
といったコストが発生し、パフォーマンスが低下する可能性があります。
append によるメモリ再割り当ての発生ケース
package main import "fmt" func main() { s := []int{} // 初期キャパシティなし fmt.Println("初期:", len(s), cap(s)) // len: 0, cap: 0 for i := 0; i < 10; i++ { s = append(s, i) fmt.Printf("len: %d, cap: %d\n", len(s), cap(s)) } }
実行結果(キャパシティ増加の様子)
初期: 0 0 len: 1, cap: 1 len: 2, cap: 2 len: 3, cap: 4 len: 4, cap: 4 len: 5, cap: 8 len: 6, cap: 8 len: 7, cap: 8 len: 8, cap: 8 len: 9, cap: 16 len: 10, cap: 16
キャパシティが足りなくなるたびに 2倍に拡張 されているのが分かります。要素が増えるたびにメモリ再割り当てが発生し、コピー処理が行われるため、append のコストが上がってしまいます。
make を使った改善
make を使って あらかじめキャパシティを確保 すれば、メモリの再割り当てを減らし、パフォーマンスを改善できます。
package main import "fmt" func main() { s := make([]int, 0, 10) // キャパシティを10に設定 fmt.Println("初期:", len(s), cap(s)) // len: 0, cap: 10 for i := 0; i < 10; i++ { s = append(s, i) fmt.Printf("len: %d, cap: %d\n", len(s), cap(s)) } }
実行結果(キャパシティが一定で再割り当てなし)
初期: 0 10 len: 1, cap: 10 len: 2, cap: 10 len: 3, cap: 10 len: 4, cap: 10 len: 5, cap: 10 len: 6, cap: 10 len: 7, cap: 10 len: 8, cap: 10 len: 9, cap: 10 len: 10, cap: 10
make([]int, 0, 10) を使うことで、最初からキャパシティ 10 のスライスを確保し、不要なメモリの再割り当てを防ぐことができました。
まとめ
| 方法 | メリット | デメリット | 使いどころ |
|---|---|---|---|
[]T{} (リテラル) |
簡潔に定義できる | append 時にメモリ再割り当てが発生しやすい |
要素数が少なく、頻繁に変更しない場合 |
make([]T, len, cap) |
キャパシティを事前確保できる | 初期値を持てない | 要素を追加予定で、メモリ管理を最適化したい場合 |
スライスのサイズが事前に分かっている場合は、make を使うことで パフォーマンスの最適化 が可能です。特に append を多用する場合、make([]T, 0, cap) を使ってキャパシティを適切に設定するのが推奨されます。
Go の make はメモリ管理を考慮すると非常に便利なツールなので、適切に使い分けましょう。