nazolabo

フリーランスのWebエンジニアが近況や思ったことを発信しています。

2025年まとめ

2025年はどんな1年でしたか?「仕事の思い出」を振り返って、Amazonギフトカードをゲット!

良かったことも、悪かったことも、全部ひっくるめてブログ&SNSでシェアしてください!

MEETS CAREER × Hatena Blog 特別お題キャンペーン #2025年仕事の思い出
MEETS CAREER_odai
by マイナビ転職

↑何かやってるらしいので追加してみました。これが入っていれば応募されている?

お仕事

今年はありがたいことに忙しい1年になりました。

レガシーシステムを Next.js を中心としたスタックでリニューアルするといった内容を行なっていました。

途中から参加しましたが、技術選定から仕様整理、チーム運営まで幅広く担当させていただきました。

特に AI との親和性の高い開発を行えるように、 openapi-typescript によるスキーマ生成や、 zod + react-hook-form + shadcn/ui での型安全な UI 設計などを推進させることができ、少人数でも高いパフォーマンスが出るようになったのではないかと思います。

レガシーシステムの立て直しはもはや私の専門ですが、レガシーシステムの立て直しには現場の深い協力やシステムの外の見直しが特に重要になると改めて感じました。また、Next.js に関する実績はなかったものの、日頃からの知見の蓄積によって Next.js のメリットをちゃんと生かした実装にできたのではないかと思います。

技術顧問

ひょんなことから技術顧問のお話がそこそこ来たので少しずつ始めることにしました。ありがたい話です。

現代は特に専門的な技術より「ちゃんと仕上げたことがあるか」というところが重要になってきており、なんだかんだで20年以上同じことをやっているとそのような情報に思ったより価値があるようです。特にどちらかといえば無理難題のような現場を切り抜けてきているので、そういう困った状況においてはお役に立てるかと思います。

まだまだこの分野では新人ですので、学ばせていただくことも多いです。継続して頂けているようなので、これからもお手伝いさせて頂ければと思います。

AIの1年

今年はAI活用が急激に実用的になった1年でした。AIによって「専門的な、特に経験より知識の網羅を必要とする内容」の価値が大きく下がったと思います。批判的な意見もありますが、プログラミング分野ではもはや手放せないものになっていると思います。

現在の生成AI事情はまだまだ混沌としているものの、現状でのできることというのは概ね固まってきたと思います。「できそうだけどやってない」ことはできる余地があると思いますが、「どうがんばってもできない」ことは今の技術ではどうやってもどうがんばってもできないと思います。ここの違いがわかっている人が一歩先に進めるかと考えています。

技術的なことももちろんですが、一般的だけどなかなか出てこないような内容を壁打ちするのにも役立っています。もちろんこの記事もAIのサポートを受けています。

Macを新調した

2020のM1 Macを使い続けていたのですが、ずっとTouch Barが壊れていたのと、そろそろメモリとか考えても限界かなと思って買い替えました。

外部モニターが2枚出力できるようになったのがとても快適です。メモリやストレージも増やしてメンテナンスの手間も減りました。

最近Windowsで開発している方も増えてきたように思えますが、実際WSLのおかげでWindowsでも困らないような気がします。Touch IDが特に難があるので、次回はもしかしたらWindowsに乗り換えるかもしれません。でもMacのまとまり感は好きです。

お仕事募集

来年からは現在の案件の規模は縮小するので、新しいお仕事を募集しています。具体的には以下のような内容が可能です。

こんなお悩みを解決できます

  • 既存システムがレガシーで立て直したいけどどうしたらいいのかわからない
  • 人数はいるけどなぜか進んでいる感じがしない
  • 客観的に技術的なアドバイスがほしい

こんなことができます

  • レガシーシステムの段階的モダン化
  • 複雑な技術課題の根本的解決
  • 現実的で持続可能な技術戦略の立案
  • 技術選定の判断支援
  • アーキテクチャ設計・レビュー
  • 開発組織の改善提案
  • AI時代の開発手法導入支援(Devin・Claude Codeなど)

対応技術

  • TypeScript、PHPRubyPython、Go などの開発
  • AWSGCP でのインフラ構築
  • CI/CD 整備・linter の整備などの開発効率化

対応可能な形態

技術顧問:

月1〜4回の定期相談を中心に、技術的な判断・設計をサポート

プロジェクト参画:

週1-3日程度、リードエンジニアとして技術統括

いずれもフルリモートでの対応となります。

ご連絡

各種SNSのDMでお気軽にご連絡ください。

まとめ

今年は突然人の縁に恵まれていろいろなことがありました。私のような人間に声をかけていただけるのは本当に感謝しています。

2026年も引き続き、技術的な課題解決を通じて価値を提供していきたいと思います。よろしくお願いいたします。

AIコーディング所感(2025年5月まで)

あまり考えがまとまっていないのですが、書いてる途中にもいろいろ変化が出てきていつまでも完結しないので適当に書き出しておきます。来月にはもう別の考えになってそうです。

ほぼGitHub Copilot、補助でDevinです。趣味でClineを使うこともあります。Cursorは意図的に使ってません。Claude Codeはまだ手を出していません。

GitHub Copilot

なんだかんだで安定しています。Agentモード + Claude 3.7 Sonnet で結構な量のコードを書いています。

この記事を書き始めた時はAgentモードやNESの動作がまだ微妙でEditのほうが安定しているよねというような内容にしていたのですが、いつのまにかAgentモードで十分書けるようになっていたので、今はほぼAgentモードしか使っていません。NESも有効にしています。

ただしテスト結果のコンソール出力をちゃんと読んでくれないとかは相変わらずです。

Devin

縁があって使わせてもらっています。

Agentモードと同様の存在なのですが、モデルが違うせいか、Agentモードよりはコンテキストを読んでくれるし、生成内容はあまり外さない印象…でしたが、たまに無限ループします。

Devinのメリット

外部タスクなのでVSCodeを占領されないというメリットが一番大きいです。うまく使えると並行作業ができます。

テストの作成やコードレビューなどを中心に行ってもらっています。

注意点

あまり実行速度は早くないし、そこそこフィードバックを要求されるので、それなりに大きなタスクを要求したいところなのですが、大きなタスクだと失敗しがちなので、小さいタスクが複数ある状態で1つずつやってもらう、みたいなのがいいと思います。

テストコードの生成に関してだけ言えば、既存のファイルを修正させるより新規でファイルを作らせたほうが得意そうな印象です。

安くはないので個人開発ではあまり使う必要がないですが、チーム開発であればknowledgeの蓄積がしやすいのでチーム開発向きだと思います。

あとリモート環境になるので、環境セットアップがやや面倒です。ミドルウェア分離などが適切に行われている必要があります。

Cline

Clineは新規プロジェクトでどうにかできないか試行錯誤しています。

AIに適したアーキテクチャで作ると結構すんなり作ってくれるので、全体を整えるのに使ってみたりしています。

こちらは自腹なので最近はあまり触っていませんが、いいお題があればもっと試してみたいと思います。

AIが書きやすいコード

上から下に読めるコードは大体書きやすいです。したがって宣言的なIaCのコードやGitHub Actions、Dockerfileやシェルスクリプトといった1ファイルで完結するものにはかなり強い印象があります。

「知っていれば書けるけど知っているための量が多い」ような分野はほぼAIに任せていいと思います。知識量で戦う時代はよほどの専門家でなければ終わったと思います。

あと特にClaudeではコンテキストが長くなると爆発するので、なるべく内容は絞ってあげるのが良いです。

AIに頼りすぎると自分のコードではなくなってしまう問題

当たり前ですが、AIにずっと書かせていると、自分のコードではなくなっていくので、自分の思考力が低下していくのを感じます。

特に出力がいまいちな場合は、プロンプトを試行錯誤させるより、自分で直接書いたほうが理解もできるようになるので、AIに任せないようにしています。

そもそもコードがわからない人がAIに全任せすると無意味な時間を過ごすことになります。「最悪自分が手直ししてどうにかできる」状態は維持するようにしましょう。

ドキュメント生成させる

AIにコードからドキュメントを生成させると、「なんかそれっぽいことを言っているけど頭に入ってこない」内容が生成されがちな気がします。OSSのリファレンスには良いかもしれませんが、業務コードだと背景などを理解して書く必要があるせいか、いまいち肝心なところに触れてない印象になります。とはいえ理解の補助にはなります。

人間が本当は何が知りたいのかをAIは当然ながら理解できないので、プロンプトでそこまで書ければいいですが、そうでなければなんか全体を網羅しているけどよくわからないなとなりがちです。issueなどと連動させるとうまいいくかもしれませんが、手元のプロジェクトがそのあたりにフレンドリーなものではないのでちょっとわかりません。

コードレビュー

Devinにコードレビューをさせていますが、概ね見ておいたほうがいいところが網羅されてとても良いです。テストの漏れやTODO残しなどもちゃんとキャッチアップしてくれます。やっておいて損はないです。

ただしDevinは仕様を知らない(プロジェクトによっては仕様まで理解してくれるかも)ので、仕様そのものが正しいかどうかは人間が判断する必要があります。

とにかく型とlintとスキーマが重要

AIコーディングではガードレールが最重要です。私もこれまで何度もlintの重要性について語ってきましたが、AIコーディングでは必須条件になります。

ただし型が強いと言ってもRustは苦手で、これは恐らく上から下に読んだだけでそれを理解するのが難しい言語だからではないかと思います。同様の理由でゲームプログラミング系や非同期処理なんかもまだまだ難しいと思います。

「非同期処理が難しい」や「上から下に読むだけで理解できる」という点を見ると、偶然かどうかは知りませんが近年のReact界隈の対応はそういう方向に進んでいるので、これもAIとの親和性が高いと言えます。

スキーマも重要で、入出力の定義があらかじめ決まっていればそこから脱線することが減るので、なるべく「決まったところを埋めていく」ような開発スタイルになっていることが望ましいです。

人間にも言えることですが、とにかく「個人の判断を最小限にする」ことが重要です。誰が書いても同じような結果になるプログラミング環境を用意しましょう。

まとめ

AIコーディングなしではもう開発が回らないですが、過信しないようにするのと、ガードレール整備(いわゆるrulesも含む)が重要かと思います。

2024年まとめ

メイン案件が不本意ながら突然契約終了になってしまい、いい機会なので休んでいたらコロナ(割と重症)になり更にがっつり休まざるを得ない状況になっていました。

貯金がだいぶ切り崩されてだいぶまずい状況ですが、3月まではとりあえずお仕事がありそうなので、4月以降の案件を募集しています。正社員でもいいですが条件が難しいのでなかなか大変です。

以下書こうとして放置していたことを特にとりとめもなく書きます。下書きに大量にあるのですがこういう時に出しておかないといつまでも書かないので…。

広告ブロック

自宅に NextDNS を導入しました。

残念ながら Web 広告業界は完全に悪い方向へ進んでしまったので、もう全てブロックするしかないと思い導入しました。閲覧できないので仕事として関わることもないと思います。健全化を進めているみたいな話も数年前に少し出てきた気がするのですが、どう考えても悪質なもののほうが多く、こちらでそれを判断するのは無理な状態です。

多少不便なこともありますが、気分を害するものを目にするよりはよほど良いです。現代は賛否両論なビジネスが増えてきていますが、Web 広告業界の悪質さはどうにかならないのでしょうか。

前職の技術ブログを消された

自分が関わっていた技術ブログが消えるのは恐らく2回目なのですが、前職の技術ブログが消されてしまったようです。

企業運営で更新されてなくても残っている古い記事みたいなものはたくさんあるのでなぜ消すのか私にはわかりませんし、後から消すようなものをなぜ立ち上げたのかも私には理解できませんが、大手テック企業のような強い記事を継続的に書けないのであれば、現代では Zenn の Publication だけにしておくほうが良いと思います。

技術ブログだろうと現代では企業が情報を発信するということはそれなりに責任が大きいものだと思うので、会社経営の視点からしっかり考えて発信を行うべきかと思います。

AI プログラミング所感

去年より少しずつ良くなったものの、根本の能力はあまり変わってないかなという感じがします。組み合わせて使う系が増えた感じですね。

ただ覚えた量が多いだけみたいなものをどんどん置き換えてくれることに期待します。AI ができることは AI に任せましょう。

SNS うんたら

SNS 断ってたらあまりにも存在が消えてしまったので復帰することにしました。まあ現代では無理ですよね。個人的なことに関してはあまり発信しないと思います。

X とかいうインターネット格闘技場に問題意識は皆あるようで、特に今年は mixi2 のインパクトが大きかったように思えます。元々 SNS の会社とはいえ今あれを出せたのはすごいと思います。

元々一つの SNS だけで全ての連絡が完結することはないので、しばらくは併用先がどんどん増える流れかなと思います。そろそろ根本的にひっくり返してくれる何かが出てほしいです。

最後に

アウトプットはほとんどなかったものの、表に出ないコードはいろいろ書いていたりします。

改めて、4月以降のお仕事または就職先を募集しています。連絡先は各種 SNS からお願いします。

長期休暇中&お仕事募集中です

去年くらいから何か変な感じがあったのですが、いよいよ疲れてしまってそろそろ休みたいなと思ってたところで仕事が途切れてしまったので、療養も兼ねて結構前から長期休暇しています。 しっかりリフレッシュしようと思っているので、まだしばらくお休みさせて頂く予定です。

とはいえ、働かないわけにもいかないのでお仕事募集中です。 お仕事じゃなくても久々に話したいとかでも大丈夫なので気軽にご連絡下さい。 直近はPythonとGo書いたり大規模データをBigQueryに入れたり出したりしていました。

転職サイトでもオープンにしてはいますが、なかなか難しそうなので控えめに対応しています。 フリーランスも難しいことが多くて継続できるのかどうか不安ですが、引き続き両面で考えていこうと思います。

エディタが便利になりすぎた話(続・git の pre-commit hook はなるべく使わないほうがいいのでは)

nazo.hatenablog.com

書いた時点から特に意見は変わってないので Zenn ではなくこちらで書きますが、なぜかこの記事だけ反響が多いようです。

概ね肯定的な意見が多いようですが、反響が多いといろんな人が現れますね。そもそもタイトルから「なるべく使わないほうがいいのでは」なのですが…。

現代の話

前の記事を書いてたあたりからエディタ+ LSP が進化しすぎて、各言語のまともな最新環境なら保存した時点で linter が走ってオートフォーマットされる(できないのは即時にエラーが出る)のが当たり前になったので、linter やコードフォーマッタ系に関してはそもそも pre-commit hook も手動実行も必要なくなりました。VSCode + Error Lens で書きましょう。

これを書いていた当時は LSP が動かないコードを書いていたので、linter の指定を丸暗記して書いていたのですが、もうそんなことをする必要もなくなりました。もうタブ派だの2スペース派だの4スペース派だの(そんな話もうしなくない?)で争うこともありません。特に GitHub Copilot の登場により、linter で自動修正できないコードの大半もそれっぽく修正してくれるようになりました。コードの書き方に脳のリソースを割くことは大幅に減ったので、コードの内容に集中しましょう。

また、これらのエディタの進化により、より「デフォルトでルールを厳しくする」ことにデメリットが少なくなりました。むしろ自動修正がバリバリ効くほうがメリットが多いし特にデメリットもないです。もちろん不要なルールもあるので、話し合って削ったり足したりしましょう。最近の linter は書き方からもう一歩踏み込んで、速度的に難のあるコードやセキュリティ的に難のあるコードを指摘してくれたり、各々のローカルルールを簡単に作ることもできます。

「エディタがなるべく自動でどうにかしてくれる」状態にするのが現代的な開発だと思います。GitHub Copilot でコメントを先に書いてコードを生成させる、みたいなのも含まれます。人間がどうにかするのはもうやめましょう。「ルールを守りましょう」ではなく「何も考えなくてもルール通りになる」状態を目指しましょう。

LSP があまり良くない言語やバージョンで書いている方々は、大変だとは思いますが今まで通りがんばってください。

テストはちゃんと試す必要がありますが、そもそも通らないテストをコミットするほうが悪いので、ちゃんとローカルで試しましょう。テストを pre-commit hook でやるのはいくらなんでも時間がかかりすぎるので元々やらないと思います。

それはそれとして CI は最終防衛ライン

これは変わっていないのでちゃんと CI で最終確認しましょう。あくまで「最終確認」です。リポジトリに push する時は CI が一発で通るだろう、という前提で push しましょう。

このブログとZennの使い分けについて

技術的な内容はこのブログと Zenn で2重投稿していたのですが、ほぼ Zenn のほうしか見られていないし単純に面倒なので、明確に使い分けることにしました。

少し前から以下のような運用にしています。

  • Zenn:技術的な内容、特にコードが中心になるような内容
  • このブログ:近況報告や雑記、技術的な内容でも思想とかに寄っている内容

更新通知は引き続き X (Twitter) または Bluesky 、またはそれぞれフォローするなり RSS なりでチェックしていただければと思います。

gcloud-aio-bigqueryでasync/awaitを使ってBigQueryのクエリを叩く

PythonからBigQueryを叩くには公式の google-cloud-bigquery があるのですが、asyncioに対応していません。

一部のライブラリではv2側でasyncioに対応しているのですが、BigQueryはv2でも対応していないように見えます。ちなみにv1はほとんどの実装でREST APIを叩いて、v2はほとんどの実装でRPC APIを叩いています(どちらも全部確認したわけではありません)。一部のライブラリでv2のほうを叩くとサーバーエラーが多発したこともあり、今のところなるべくv1でasyncioを使用したいです。

GCPPythonライブラリをasyncioで動くように書き換えたものが gcloud-aio で用意されており、その中に gcloud-aio-bigquery があります。あまり使われているのを見かけませんが、AirflowのGCP周りの一部で使われているようです。

ただしREST APIそのままの実装になっているため、REST APIの知識が必要です。手軽に使えるとは言い難いです。

REST APIでの流れ

まずクエリを実行するには jobs.query を呼びます。

代表的なパラメーターは以下の通りです。

  • query : 必須。SQLクエリを指定する。
  • maxResults : 1レスポンスに含まれる行数。指定しなくてもバイト数でページングされるが、指定しておいたほうが安全。
  • timeoutMs : タイムアウトの秒数
  • useLegacySql : BigQueryのレガシーSQLを使用するかどうか。デフォルトtrueなのでfalseを指定しておいたほうがいい。

実行するとジョブが作成されます。

レスポンスを確認してデータが完結しているか確認します。 jobComplete が true の場合、ジョブが完了して結果の行を取得できる状態です。この場合、 rows に結果の行が含まれています。また、 schemaスキーマが含まれています。 rows だけ見てもどの列の何のデータがの判別が難しいので、 schema と合わせて確認します。

jobComplete が true で、 pageToken が含まれている場合、まだ読み込むべきデータが残っているので続きを読み込みます。 pageToken が無ければ終了です。

データが完結していない場合は、レスポンスの jobReferencejobId を拾って、( pageToken があればそれも一緒に) jobs.getQueryResults を実行します。結果は jobs.query と同じです。

全体の流れをまとめると以下のようになります。

  • jobs.query でジョブを作成
  • jobComplete が true なら
    • rows がない(totalRows=0)→終了
    • rows がある→そのデータを使う
    • pageToken がない→終了
  • jobComplete が false 、または pageToken がある→ jobs.getQueryResults を実行し、上の判定ロジックに戻る

実際に使ってみる

実際に動くものが以下になります。

import asyncio
from collections.abc import AsyncGenerator

import aiohttp
from gcloud.aio.bigquery import Job
from google.cloud import bigquery
from google.cloud.bigquery._helpers import _rows_from_json
from google.cloud.bigquery.table import _parse_schema_resource  # type:ignore


async def query(
    sql: str, max_results: int = 100
) -> AsyncGenerator[list[bigquery.Row], None]:
    loop = asyncio.get_running_loop()
    async with aiohttp.ClientSession(loop=loop) as session:
        job = Job(session=session)
        query_request = {
            "query": sql,
            "maxResults": max_results,
            "useLegacySql": "false",
        }
        response = await job.query(query_request=query_request)
        while True:
            errors = response.get("errors")
            page_token = response.get("pageToken")
            if errors is not None:
                raise RuntimeError(errors)
            if response.get("jobComplete"):
                if int(response.get("totalRows", 0)) == 0:
                    return
                schema = _parse_schema_resource(response.get("schema", {}))
                yield _rows_from_json(response.get("rows", ()), schema)
                if page_token is None:
                    return
            else:
                await asyncio.sleep(1.0)  # 終わってなさそうなので少し待つ
            # ジョブの結果を確認
            job = Job(
                job_id=response["jobReference"]["jobId"],
                project=response["jobReference"]["projectId"],
                session=session,
            )
            params = {
                "location": response["jobReference"]["location"],
                "maxResults": max_results,
                "pageToken": page_token,
            }
            response = await job.get_query_results(params=params)


async def main() -> None:
    async for rows in query("SELECT * FROM `project_id.dataset_id.table`"):
        for row in rows:
            # row["key"] で値を取得可能
            for key, value in row.items():
                print(f"{key}={value}")


if __name__ == "__main__":
    asyncio.run(main())

雑に書いているので必要に応じて書き換えてください。タイムアウトの設定や例外の処理などが別途必要になります。

結果行の整形に公式の google-cloud-bigquery も使っています。また、 aiohttp パッケージも必要です。

プロジェクトIDを指定したい場合は Job のコンストラクタで指定してもいいですし、環境変数 GOOGLE_CLOUD_PROJECT などで指定しても反映されます。

まとめ

公式で対応してほしいです。