ソムリエでソフトウェアエンジニア

ワインの事も書きたいけど基本エンジニア用

現役ソフトウェアエンジニアが感じる開発現場の変化

はじめに:コーディングの主役が変わり始めた

AIコーディングエージェントが登場してから、開発現場は「コパイロットとして人間を支援する」フェーズを越えて、「AIが実装の主役を担い、人間が方向付けと品質を担う」フェーズに移りつつあります。

現場にいると、体感としていちばん大きい変化は“速く書ける”ことではなく、「何を・なぜ作るか」と「どう守るか(品質・セキュリティ)」の比重が急に増したことです。

1. “書く”が減り、“決める・確かめる”が増える

大手IT企業でもAIによるコード生成比率が増えている、という話題がたびたび出ています。

たとえばスポティファイでは、トップエンジニアたちが昨年12月以降、AIの活用により手動でのコーディングを一切行っていないとの発表をしています。

www.businessinsider.jp

ここで重要なのは、「AIが書く=エンジニアが不要」という単純な話ではなく、エンジニアの価値が“実装そのもの”から“意思決定と品質保証”に移っている点です。

  • 実装:AIが高速に叩き台を出す
  • 仕様解釈:人間が意図と制約を言語化し、AIに渡す
  • レビュー:人間が安全性・保守性・変更容易性を担保する
  • 運用:監視やインシデント対応、SLOなど「現実のシステム」を支える

実装コストが下がるほど、相対的に「間違えたときのコスト」が目立ちます。だからこそ、レビューと運用、そして“そもそも何を作るか”が前に出てくる。

2. 「エンジニア職がなくなる」不安はどこから来るか

「AIがコードを書くなら、エンジニアは要らないのでは?」という議論は定期的に出てきます。

ただ、現場にいるとこの問いは少しズレているようにも感じます。

エンジニアの仕事は元々、コードを書くことだけではありません。

  • 曖昧な要求を、実装可能な形に落とす
  • 不確実な状況でトレードオフを選び、説明責任を果たす
  • 既存システムの制約を踏まえ、変更の安全性を設計する
  • 障害やセキュリティリスクに備え、継続的に守る

AIが得意なのは「それっぽい実装」を高速で生成することです。一方で、目的の合意形成、リスクの見積もり、運用の責任分界、組織の意思決定――こういった領域は、まだ人間の仕事として残るどころか、むしろ重要度が上がっています。

3. 越境が“推奨”から“必須”へ:PM領域に踏み込むエンジニア

現場で強く感じているのは、越境を恐れないことの重要性です。

具体的には、エンジニアがプロダクトマネージャーの領域へ踏み込み、PRD(Product Requirements Document)を起点に、分析・検証・優先度づけまでやる場面が増えています。

理由としては、

  • 実装のボトルネックがAIで薄まる
  • 代わりに「何を作るか」を決めることがボトルネックになる
  • 仕様の曖昧さが、そのままAI出力の“ズレ”として増幅される

AIに任せられる範囲が広がるほど、上流の言語化能力(目的・制約・受け入れ条件)がそのまま生産性になります。

その為エンジニアは、要件の定義・分解・合意に深く関わる必要が出てきます。

4. 品質レビューの価値が上がる:速く作れるほど“壊す”のも速い

AIが実装を主導し始めると、品質レビューが一段と重要になります。

理由は単純で、「出力が増える=確認すべき量も増える」からです。

さらにAIの出力は、テストが薄い、境界条件が弱い、セキュリティ観点が抜ける、といった“人間が無意識に避けてきた地雷”を踏むことがあります。

結果として、レビューの観点がコードスタイルから、より本質的な品質へ移ります。

  • 仕様に対して正しいか(ビジネスロジック)
  • 例外系・境界条件をカバーしているか
  • 監査ログやアクセス制御、データ保護が十分か
  • 変更容易性(将来の改修コスト)を下げているか
  • 観測可能性(ログ/メトリクス/トレース)を入れているか

「レビューできる人」が不足すると、AIで作るスピードがそのままリスクになります。

5. これから比重が増える仕事:越境・レビュー・運用・意思決定

AIが実装を担える範囲が広がると、エンジニアの仕事は「書く」から「作り続ける」へ重心が移ります。

現場の一次体験として、今後いっそう比重が増えるのは次の4つです。

  • 越境:PRDの解像度を上げ、分析・検証・優先度づけに関わる
  • 品質レビュー:仕様/境界条件/セキュリティ/保守性の観点で守る
  • 運用:SLO/監視/インシデント対応で“現実のシステム”を支える
  • 意思決定:不確実性の中でトレードオフを選び、説明責任を持つ

実装が早くなるほど、判断のミスや品質の穴は「より速く」「より大きく」影響します。

だからこそ、エンジニアの価値は、越境して前提を整え、レビューで守り、運用で回し、意思決定で方向をつけることに集約されていくと感じています。

おわりに:エンジニアは“書く人”から“作り続ける人”へ

AIが実装を担う時代、エンジニアの役割は消えるのではなく、再定義されていくと感じます。

  • AIに書かせるために、目的と制約を言語化する
  • 生成されたものを、品質・セキュリティ・保守性の観点で守る
  • 越境して、プロダクトの意思決定に関与する
  • そして、作ったものを“作り続ける”(運用し続ける)

コーディングがコモディティ化するほど、人間の価値は「越境」「判断」「責任」の側に寄っていく。

恩恵を最大限に享受し、新しいエンジニア像を作っていけるよう、今までの考え方・やり方に固執せず、変化を受け入れ対応できるよう意識したいと記事を書きながら考えています。

express x TypeScript環境の構築

はじめに

突如個人開発で作りたいものができたのでexpress x TypeScriptでサクッと環境構築しました。
せっかくなのでTypeScriptで開発を進めたかったので過程を記録しておきます。

事前準備

express環境を整える前に事前の準備を行う
プロダクト用ディレクトリを作成する

$ mkdir express-typescript

作成したプロダクト用ディレクトリに移動。package.jsonファイルを作成する

$ npm init -y

必要なモジュールをインストール

expressのインストール

expressとtypesをインストールしていきます

$ npm i express
$ npm i -D @types/express

TypeScriptの設定

$ npm i -D typescript
$ npm i -D @types/node
$ npm i -D ts-node
$ npm i -D nodemon

ts-nodeはtsファイルをコンパイルせず実行できるようにする事で、コードの変更をすぐに反映して実行結果を確認できるようになります。
nodemonはファイルの更新を検知して再読み込みを行なってくれます。
※共に開発効率を上げる為に導入しています

続いてtsconfig.jsonを作成します

$ npx tsc --init

作成されたtsconfig.jsonを以下のように編集します。

expressでテストAPIを作成する

プロジェクトディレクトリ直下にsrc/index.tsファイルを作成します

const express = require('express')

const app = express()
const PORT = 8080;

app.listen(PORT, () => console.log(`Server is running on port ${PORT}`))

package.jsonを編集

npmコマンドで起動できるようscriptを編集します

{
  ...
  "scripts": {
    "dev": "nodemon --watch './src/**/*.ts' --exec ts-node --files ./src/index.ts"
  },
}
$ npm run dev

を実行して起動する事を確認します。`Server is running on port 8080と表示されていればOKです

確認作業

ここまできたらexpress側にリクエストを投げて検証していきます index.tsにtest用のAPIを作成します

import express, { Request, Response } from "express";

const app = express();
const PORT = 8080;

app.get("/test", (req: Request, res: Response) => {
  return res.send("Hello World");
});

app.listen(PORT, () => console.log(`Server is running on port ${PORT}`));

こちらをcurlコマンドを利用して検証します

$ curl curl http://localhost:8080/test
→ Hello World

無事疎通確認がとれました。
lintやformatterなどは今回は導入していませんがまたおいおい対応していこうと思います。

PHPでバックトレースを実施する

経緯

Zend frameworkを使ったシステムでGETリクエストに対して想定しない404画面が描写された。
対象のコントローラーには404になる処理は実装されておらず、ログをチェックするもリクエストに対応するコントローラー内を通っていない。
各コントローラーの継承元にログを差し込むと出力される....
どこから継承元が呼び出されているかを確認する為、バックトレース用のメソッドdebug_backtraceを使用し、仕様など調べたので備忘録として残しておく。

debug_backtraceとは...

PHPでバックトレースを生成する為の関数。関数がどこから呼び出されているかを把握する事ができる為、今回のような呼び出し元を調査してエラー原因を特定する時に重宝する。

使い方とサンプルコード

継承元コントローラー

<?php
class AbstractController {
    public function __construct() {
        var_dump(debug_backtrace());
        echo "AbstractController\n";
    }
}
?>

各コントローラー

<?php
require('./Abstract.php');

class SampleB extends AbstractController {
    public function __construct() {
        parent::__construct();
    }
    
    public function getClassName()
    {
        echo 'SampleB';
    }
}
?>
<?php
require('./Abstract.php');

class SampleB extends AbstractController {
    public function __construct() {
        parent::__construct();
    }
    
    public function getClassName()
    {
        echo 'SampleB';
    }
}
?>

呼び出しファイル

<?php
require('./SampleA.php');
$class = new SampleA();
$class->getClassName();
?>

出力

array(2) {
  [0]=>
  array(7) {
    ["file"]=>
    string(22) "/workspace/SampleA.php"
    ["line"]=>
    int(6)
    ["function"]=>
    string(11) "__construct"
    ["class"]=>
    string(18) "AbstractController"
    ["object"]=>
    object(SampleA)#1 (0) {
    }
    ["type"]=>
    string(2) "->"
    ["args"]=>
    array(0) {
    }
  }
  [1]=>
  array(7) {
    ["file"]=>
    string(19) "/workspace/Main.php"
    ["line"]=>
    int(3)
    ["function"]=>
    string(11) "__construct"
    ["class"]=>
    string(7) "SampleA"
    ["object"]=>
    object(SampleA)#1 (0) {
    }
    ["type"]=>
    string(2) "->"
    ["args"]=>
    array(0) {
    }
  }
}
AbstractController
SampleA

出力を確認すると、SampleA.php内で継承元のコンストラクタ処理が呼び出されている事を確認できる。
debug_backtraceを使用する事で、今回のエラーはルーティング競合で別のコントローラーが呼び出されている事が原因であることを特定する事ができた。
※メソッドにパラメータを持たせる事で出力内容を最適化できるので参考リンクを確認してください。

参考

www.php.net

『技術書の読書術 』の備忘と感想

はじめに

気になったので「技術書の読書術」を読んでみました。
せっかくなので見返せるようにまとめておきます。

https://www.amazon.co.jp/%E3%80%8C%E6%8A%80%E8%A1%93%E6%9B%B8%E3%80%8D%E3%81%AE%E8%AA%AD%E6%9B%B8%E8%A1%93-%E9%81%94%E4%BA%BA%E3%81%8C%E6%95%99%E3%81%88%E3%82%8B%E9%81%B8%E3%81%B3%E6%96%B9%E3%83%BB%E8%AA%AD%E3%81%BF%E6%96%B9%E3%83%BB%E6%83%85%E5%A0%B1%E7%99%BA%E4%BF%A1-%E5%85%B1%E6%9C%89%E3%81%AE%E3%82%B3%E3%83%84%E3%81%A8%E3%83%86%E3%82%AF%E3%83%8B%E3%83%83%E3%82%AF-IPUSIRON-ebook/dp/B0BF469YLK/ref=sr_1_1?adgrpid=140328664499&gclid=Cj0KCQiA6rCgBhDVARIsAK1kGPKRCICRN5S5nUNaV0XhLYYENZ2irFKmshrfG1YDlsV4S3Wb2VfEr4EaAlxWEALw_wcB&hvadid=649112918119&hvdev=c&hvlocphy=1009549&hvnetw=g&hvqmt=e&hvrand=9173826561345471907&hvtargid=kwd-1743671597405&hydadcr=21803_13448976&jp-ad-ap=0&keywords=%E6%8A%80%E8%A1%93%E6%9B%B8%E3%81%AE%E8%AA%AD%E6%9B%B8%E8%A1%93&qid=1678588108&sr=8-1www.amazon.co.jp

本について

こちらの本は、技術書の「選び方」「読み方」「情報発信 & 共有のやり方」の3点について2人の筆者が実践する様々な手法を紹介したものです。
多くの方法が紹介されているので自分に合うものを見つけながら実践できると思います。

なぜこの本に興味をもったのか

エンジニアであれば技術のキャッチアップを様々な方法で実践しているかと思います。
自分の場合、体型的にまとめられている書籍は非常に有益だと考えており、日頃から技術書を読む機会が多くあります。
しかし、時間を投資して読んだ技術書をしっかり身にできているのかとういう疑問もあり、そのようなタイミングでこの書籍に出会ったので自分の読書方法をブラッシュアップしたいと考えこちらの書籍を手に取りました。

以降は書く章から気になった点をまとめていきます。

第一部 選び方

技術書の選び方について書かれた章では以下の項目が印象に残っています。

英語の技術書という選択肢

英語で書かれた技術書という選択肢は今までの自分にはなかったので選択肢が増えた気がします。(すぐに読むとはいっていない...) ハンズオン形式であれば、詳細は解らずともコードやスクリーンショットから読み取れるかと思うのでそのような書籍も選択肢に入れる事はありかと思うようになりました。

第二部 読み方

技術書の読み方についてはこの書籍で最もページを割いて紹介されています。
以下の4点がとくに気になった内容です。

あらゆる場面で「3」の発想を大切にする
  • 入門書・専門書・逆引きの視点が異なる本を3種類くらい読むと実務で使えるレベルになる
  • 入門書を3冊見比べる事で共通する重要な点や間違いに気づく事ができる
サンクスコストの考え方を取り入れる

その本を読む目的(ゴール)を明確にし、目的を達成したら他の部分は後回しにする。
自分は本を買うと隅から隅まで目を通したくなるタイプなので、本来の目的を見失わず読書する事で余分な時間をかける事なく学習を進められるかと思います。

同じ本を何度も読む事はあり
  • 一度目は流し読み。章、節などのタイトル部分と最初の数行をざっと眺める。
  • 二度目は手を動かしながら読む
  • 三度目は記録に残しながら読む 受験などでもよく言われる復習の重要性です。
    またこの方法がいいなと思ったのは、一度目の流し読みのフェーズで、全体像がまったく解らなければその書籍のレベルに達していない、逆に中身がほぼわかるのであれば簡単すぎる本というざっくりとした判断ができる点でも時間の節約ができる点です。
全部読む必要はない

限られた時間の中でスキルアップを図る為、この意識は重要かと感じます。書籍の中ではさらに読むページ、読まないページとして以下のような内容が紹介されていました。

  • 目次は読書の目的が明確でなく全体像を把握必要があれば読む。目的が既に明確であれば飛ばす
  • 謝辞は飛ばす
  • まえがき、あとがきは読むモチベーションを高める意味で読む

第三部 情報発信 & 共有

読む時に自分ならではの視点をもつ

無条件に情報を信じるのではなく、「自分なら」という視点をもって書籍を理解する事が重要。

これは読書に限った事ではないですね!

全体の感想

サンクスコストの考え方や、同じ本を複数回読む前提の読書を早速取り入れてみたいです!
情報発信 & 共有の意味も込めて今回は読書メモを書いてみました。
今後もたまにかけたらいいな...

(JavaScript)mapでcontinueできないので代替案を探す

はじめに

普段JSやTSで新しい配列要素を作成したい時にmapをよく利用します。
for~ofで回しながら先に用意している配列にpushしていく書き方よりも、配列を加工したい旨を明示でき、すっきり表現できると考えています。

何がしたいのか

全ての配列に対して加工する場合は問題ないですが、一部の値のみを対象に加工した配列を返却したい場面でmapは少し面倒な動作をします。

<仕様>

  • 管理者のユーザー名を「さん」をつけて収集したい

例)for~ofを利用した例

const users = [
    { name: 'sample1', age: 30, admin: true },
    { name: 'sample2', age: 20, admin: false},
];

let target = [];

for (const user of users) {
    if (!user.admin) continue;
    
    target.push(user.name + 'さん');
}


例)mapを利用した例

const users = [
    { name: 'sample1', age: 30, admin: true },
    { name: 'sample2', age: 20, admin: false},
];

const target = users.map(user => {
   // ここでcontinueしてスキップしたい、、、

    if (user.admin) {
        return user.name + 'さん';
    }
})

→ ['sample1さん', undefined]

※mapについては下記リンクを参照 developer.mozilla.org

仕様を実現しようとすると、管理者でないデータがundefinedで返却されてしまいます。
その為、map内でcontinueを利用したいとなるのですが、ご存知の通りmap内でcontinueを利用する事はできません。そこで少し工夫をして同様の動作をできるようにしてみたいと思います。

解決方法

filter関数を利用する事でundefinedが返却される問題に対応する事が可能になります。

const target = users
    .filter(user => user.admin)
    .map(user => user.name + 'さん');
→ ['sample1さん']

上記コードではfilterで管理者ユーザーのみ収集し、その後mapに収集したデータが渡されるので問題となっていたsample2ユーザーの判定時にundefinedが返却されるを解消できます。

SQL Alchemy, Alembicでマルチテナント型のデータベース生成とマイグレーションを実行する方法

概要

本業でPythonで処理を書き, ORMとしてSQL Alchemy, マイグレーションの管理をSQL Alchemy, で実施しているプロジェクトがあります。
そのプロジェクトではマルチテナント方式?(一つのユーザーに一つのスキーマーが切られているイメージです。)を採用しており、契約時に新規のスキーマー作成、マイグレーションの実施を行いたいニーズがありました。
ネットには、SQL Alchemy, Alembicを利用してテーブル作成を行う記事は存在したのですが、今回のようなデータベース自体の作成やマイグレーション先をコード上で動的に切り替える方法などは見つかりませんでしたので実装方法の殴り書きとして残しておきます。 (※注意)MariaDBを利用した為、スキーマではなく以後DBを作成と書かせていただきます。

前提条件

通常のマイグレーションは実施できる状態。 alembicのenv.pyで以下のように環境変数からDB関連の値を読み取ってurlを指定しています。

alembic/env.py

from env import DB_USER, DB_PASSWORD, DB_HOST, DB_PORT, DB_NAME
from alembic import context
import models

... 略

db_url = f"mysql+pymysql://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{DB_PORT}/{DB_NAME}"
config.set_main_option("sqlalchemy.url", db_url)

... 略

この場合、環境変数で指定したDB_NAMEというデータベースにマイグレーションが実行されます。
この時点では、サーバーに入り手動でDBを作成しalembicのマイグレーション実行コマンドを叩いていました。今回はこれをあるエンドポイントを叩くと引数のDB名でDBを作成しマイグレーションまで実行してくれるように設定していきます。

実装

from env import DB_USER, DB_PASSWORD, DB_HOST, DB_PORT
from sqlalchemy_utils import create_database, database_exists
from sqlalchemy import create_engine
from alembic.config import Config
from alembic import command


def settingDatabase(database_name: str):
    DB_URL = f"mysql+pymysql://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{DB_PORT}/{database_name}"
    Engine = create_engine(DB_URL, encoding="utf-8")

    if database_exists(DB_URL):
        return False

    create_database(Engine.url)
    alembic_config.set_main_option("sqlalchemy.url", DB_URL)
    command.upgrade(alembic_config, 'head')

    return database_exists(DB_URL)

alembic_config = Config('./alembic.ini')

db.pyでURLを指定するようにしたのでalembicのenvファイルではURLの指定をコメントアウト
これが残っているとdb.pyで指定したURLが上書きされる為うまく動作しません。

alembic/env.py

from env import DB_USER, DB_PASSWORD, DB_HOST, DB_PORT, DB_NAME
from alembic import context
import models

... 略

# db_url = f"mysql+pymysql://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{DB_PORT}/{DB_NAME}"
# config.set_main_option("sqlalchemy.url", db_url)

... 略

あとは定義したsettingDatabaseメソッドを呼び出せば引数の値を名前にもつDBが作成されマイグレーションも実行されます。

最後に

Python、Alembic、SQL Alchemy全て今回初めて触る技術になりますのでもしかすると見当違いな実装になっているかもしれません。
何か別の方法があればぜひご教授いただけますと幸いです。

ブラウザ→webサイトに接続できなくなった... そんな時のwebエンジニア向けトラブルシューティング

お疲れ様です!おっくんです。
2022年も早1ヶ月半ほどたちましたねー。毎日が早すぎる...

記事執筆のきっかけ

私は現在、エンジニア数10名以下のベンチャー企業で勤務しています。
あるあるですが、インフラをメインで触っているエンジニアの数が少なく、インフラにおけるトラブル解決のスピードが課題となっている状況です。

つい先日、新規案件のステージング環境でURLにアクセスしても画面が表示されないトラブルが発生した際、ただ「webサイトが表示されません。」と報告するのではなく、「このような状態又は動作の為トラブルと判断した」と問題を切り分けてからインフラエンジニアに依頼をかける必要性を感じ、簡単なトラブル切り分け方法を調べたのでまとめておきたいと思います。

想定しているケース

本記事では運営している「http://www.okkun-sample.com/」というwebサイトが突然ブラウザから接続できなくなってしまったというシュチュエーションを想定しています。
クライアントのOSはMac、Webサイトは、Webサーバとその裏にあるDBサーバで構築されている前提で進めます。

記事の対象者

  • 初めてエンジニアとして働くことになった方
  • インフラ周りに苦手意識をもっているwebエンジニアさん

手順

1. クライアント側のネットワーク問題を疑う

まずはクライアント側のネットワークに問題がないか調べていきます。
といっても簡単でブラウザから「http://www.okkun-sample.com/」以外のサイトに正しくアクセスできる事を確認します。

2. ホスト名解決がされているか調査する

nslookup又はdigコマンドを利用してDNSサーバに名前解決を確認します。
このnslookupコマンドはDNSサーバに対して名前解決リクエストを送信するコマンドです。

$ nslookup www.okkun-sample.com

** server can't find www.okkun-sample.com

IPアドレスが返却されない場合は、DNSの設定、もしくはDNSサービス自体に問題があります。

3. Webサーバとの疎通を調査する

pingコマンドでWebサーバとのネットワーク疎通を確認します。

$ ping www.okkun-sample.com(IPアドレスでも可)

Webサーバからの応答が返ってこない場合は、途中のネットワークに問題があるか、Webサーバがダウンしている可能性があります。

※注意点

pingコマンドにはICMPというプロトコルが利用されます。その為、サーバ側でICMPが許可されていない場合は、pingから応答は返ってこないがサーバは正しく動作している場合もあります。

4. ポート番号が閉じられていないか調査する

telnetコマンドを利用してHTTPのウェルノウンポートである「80番」に接続してみます。

$telnet www.okkun-sample.com 80

Webサーバから応答が返ってこない場合は、ポートがセキュリティ設定などで閉じられている可能性があります。

5. アプリケーション側の問題を疑う

上記①~④までのチェックをおこなった上で接続が確認できない場合は、アプリケーション側での問題である可能性が高くなります。

所感

Webサイトに接続できない場合もアプリケーションのエラー解決と同じく、一つ一つ可能性を潰し、根本原因を探し当てるプロセスは変わりません。
今までインフラ関連は難しいと先入観をもってしまい中々手を出してきませんでしたが、いざ触ってみると面白く、さらに勉強していきたいと感じています。
もっといい方法あればコメントにてご教授いただけますと幸いです。