Rubellum fly light

ほぼPHP日記

戦術的プログラミングと戦略的プログラミング

A Philosophy of Software Designを読んで、「戦術的プログラミング」と「戦略的プログラミング」という用語を知ったので抜粋意訳メモ。

戦術的プログラミング(Tactical programming)

ほとんどのプログラマーがプログラミングするときに取り組む方式。

機能を追加したり、バグを修正したり、”動作する”ことに主眼を置く。

気持ち:最良の設計よりもできるだけ早く仕事を終わらせたい。

戦略的プログラミング(Strategic programming)

コードを書くだけでは十分ではないことを理解する

不必要に複雑さを導入せず、システムが長期的に変更に耐えうる、将来の拡張を容易にする設計を最も重要とする。

投資マインドが必要。現在のプロジェクトを終わらせるために最速の道を選ぶのではなく、システムの設計を改善するために時間を投資する。

気持ち:優れたデザインを生み出すこと。(それがたまたま機能すること)

所感

開発業務をプロジェクトと捉えると、ソフトウェアを動作させる(すなわち仕事を終わらせる)ことだけを考えてしまって、よわよわコードを生み出しがちである。

納期に追われてコードがぐちゃるのとは違い、そもそも将来の自分を助けるようにコードを最初から設計しようぜと。

負債となったコードが問題を起こすのがプロジェクトの終わったあとであるため、 プロジェクト形式の進め方がよろしくなくプロダクトに重点を置いて開発しようぜに繋がると感じた。 (プロジェクトの成功→開発の終了、プロダクトの成功→開発の”継続”)

まとめ

みんな戦略的プログラミングしようぜ!

参考

speakerdeck.com

qiita.com

engineers.ntt.com

GitHub Copilot 所感

所属する会社がGitHub Enterpriseを契約しており、少し前からGitHub Copilotを使えるようになっていました。

めちゃくちゃ便利だったので、プライベートでもめでたく(?)課金しました。

月なら$10、年なら$100です。おそらく使わなくなることはないので年間$100で支払いました。

ChatGPTとDeepLと合わせてAI版3種の神器ですね。

使用感

普通に使ってみて自分なりの使用感を書いてみます。

前提

  • お仕事はWebエンジニアです。
  • 基本的にJetBrains系のIDEを使っています。仕事ではPHPStorm、プライベートではGoLand, WebStormです。
  • Copilot便利記事をほとんど見ていないので、使い込んでいない人間の感想です。

①よりいい感じのコードの補完をしてくれる

通常のIDE補完よりも便利かつ長く補完をしてくれます。

構造体の値を埋めるだけのような人間が書くには面倒な記述をTabとEnterできました。

初めてCopilotの話を聞いたときはコードコメントからコードを書かせるといった形で省エネする印象でしたが、コーディング面でも速度が速くなて手間と疲れが減った印象です。

PHPを書くときひたすらコーディングが必要な瞬間があり、その際にとても力を発揮してくれています。

趣味golangではよくあるerrは何も考えずにTabとEnterで完結して嬉しいところ。(良し悪しは別として)

書き出しも通常の保管は if の iを書いて保管しますが、copilotはifのiを書く前に提案してくれるため、体感でコーディング速度がかなり上がりました

②今のところ間違ったコードの提案で事故ったりはしていない

Copilotの懸念事項でよく上がるであろうコードが合っているか確認しなければならない点については、 慣れている言語では今のところ問題なく感じています。

必ずしも適切なコードを出さないし、だいぶトンチンカンなコードも提案してきますが、無視すればいいだけで。

1にも書いたように定型系は強いので割り切って使っています。

補完の色を変えた

JetBrainsのGitHub Copilotの補完はグレーですが、見づらいのでパープルに変更しています。

その他

体感ですが、GitHub Copilotは明らかに作業者のコード生産のスループットを上げるので、現場の人間がよくわからないから使っていませんと言っていたらこれはもはや罪なレベルと言えるなと...。

少なくともチームの人間にはひとまずは無理にでも使ってもらわないといけないと感じたので、自分の仕事のチーム内でも布教活動をした次第でした。

rerunを使ったファイル変更検知と再実行:golang+echo編

rerun(ruby製)を使うとファイルの変更を検知してコマンドを再実行できます。

Webアプリケーションの開発中、ファイルを変更したらアプリケーションサーバーを再起動させています。

github.com

下記は.goファイルの変更を検知してgo run 〜コマンドを再実行するコマンドですが、うまくいきませんでした。

$ rerun -p "**/*.{go}" go run cmd/server.go

原因はgo run 〜の実行プロセスと、ビルド後のプログラムのプロセス番号が異なるからでした。

go run コマンドは、「Goのソースコードをコンパイルしてからすぐに実行する」という2つのステップを組み合わせたコマンドです

プロセス番号が異なる問題を回避する方法がうまく思いつかず、ひとまずビルドと実行フェーズをそれぞれrerunを実行することで実現できました。

# ビルド
$ rerun -c -p "**/*.{go}" -- go build -o app cmd/server.go

# 実行
$ rerun -c -p "app" -- ./app

Sequel で特定のカラムを指定するときは Sequel[:table][:column]

Ruby でサクッと DB にアクセスするときに Sequel をよく使っています。

同名カラムを持つテーブルをJOINするときはどちらのテーブルのカラムかを指定しますが、Sequelでの指定方法を忘れがちなので備忘録がてらメモを残します。

下記のようなフォーマットで指定します。

Sequel[:table][:column]

tag テーブルの slug カラムなら↓のようになります。

Sequel[:tag][:slug]

サンプル

@db[:tag].left_join(:tag_items, tag_id: :id, type: "source")
                 .select(Sequel[:tag][:id], Sequel[:tag_items][:id])
                 .where(name: source_tags)
                 .sql

生成するSQL

SELECT
  `tag`.`id`,
  `tag_items`.`id`
FROM
  `tag`
LEFT JOIN
  `tag_items` ON ((`tag_items`.`tag_id` = `tag`.`id`) AND (`tag_items`.`type` = 'source'))
WHERE
  ((`name` IN ('hoge'))

HTTPリクエストを手順に送るだけの小さなワークフローエンジンを作って試行錯誤中

ライティングソフトウェアを読んで、サービス指向なアーキテクチャでクローラを作っています。

サービス指向なアーキテクチャの簡易図
サービス指向なアーキテクチャの簡易図

この中で〜Managerはワークフローエンジンによって処理を順に実行するだけのサービスとなっています。

処理自体は主に下位の〜Accessに対して手順通りにリクエストを投げるだけです。

このワークフローエンジンは自前実装でなく3rd Partyツールを使うのがライティングソフトウェア的には筋ですが、仕組み的に素朴なものがほしかったのでRubyで自前実装することにしました。

※コードは今のところ公開していません。

元々はGoogle Workflows、その後VM移動した都合でOSSのn8nを使っていました。

自前な素朴ワークフローエンジンは次のようなyamlを定義してリクエストを順に送ります。

miniflow:
  # ソースクロールキューを生成する
  - name: search-source_crawl_queue
    url: http://localhost:8080/v1/source/search
    params:
      condition:
        type: rss
        schedule_with:
          type: hourly
        limit: 100
      format:
        type: crawl_job
        opts:
          skip_enqueue_if_exists_entry: false
    result: crawl_job_get_result

  # ソースクロールキューを追加する
  - name: crawl_job-add
    url: http://localhost:8080/v1/crawl_job/add
    params:
      crawl_jobs: ${result.crawl_job_get_result.result.items}

${〜}で変数を埋め込めるようにして、前の実行結果を引き継げるようにしました。

リクエストの方法は今のところPOSTのみでHTTPリクエストのBODYにJSONを記述して送るだけの仕組みになっていますが、 このあたりはパラメータでいくらでも拡張できるので必要になったら作ればいいと考えています。

APIをうまく設計すればHTTPリクエストを順に送るだけで柔軟に機能を実現できます。

まだ、サービスが4個、APIが2〜30本くらいの規模感のソフトウェアしか作っていませんが、設計の感触はなかなかいいです。

お試し GitOps

GitOps を試すために Kubernetes のクラスタと ArgoCD をVPS(VM4台)に構築し、自前サイトをのっけてみた。 そもそも k8s を触ったことなかったので、AWSやGCPのマネジメントクラスタを利用せず、自前で小さいクラスタを構築した。

ひとまず GitOps できる環境構築までは完了。 git リポジトリにプッシュすると k8s クラスタにデプロイできるようになった。 git リポジトリの中身はまだ素のnginx+HTMLファイルみたいな静的サイトなので、次はPHPアプリケーションあたりを試したい。

git リポジトリは GitLab.com (Saas) を利用。Private Container Registry が無料で使えたので。 GitLab.com は yaml をgitに入れておくだけで、プッシュのタイミングでイメージをビルドしてくれて神だった。 kojole.hatenablog.com はじめ registry への認証ができず、どうやら2段階認証がONになっていると docker login が通らないっぽかったのでOFFにした。 ちゃんと認証する方法はありそうなもののサボった。

type: LoadBalancer をこの環境でも使えるように MetaLB(L2), Nginx Ingress Controller を追加でセットアップした。なお type: LoadBalancer が何者かはよくわかっていない。

Nginx Ingress Controller のインストールはかなり詰まって、ここが一番大変だった。 最初はずっとエラーが出て解決しなかったので NodePort でお茶を濁していたものの、マニュアルの Bare-Metal の手順でなく helm でインストールしたらうまく行った。なんでだろう。

エラー内容を保存し忘れたが、たしかこんな感じのやつ(github issueから引っ張ってきた)。

Error from server (InternalError): error when creating "〜": Internal error occurred: failed calling webhook "validate.nginx.ingress.kubernetes.io": Post https://ingress-nginx-controller-admission.ingress-nginx.svc:443/extensions/v1beta1/ingresses?timeout=30s: context deadline exceeded

↓の手順を見て、 helm インストールした。Install nginx-ingress controller with Helm の見出しの部分(upgradeとなっているが初回だったのでinstallで実行) www.brightbox.com

外部IPをセットしていないので、今は k8s クラスタの前段に素の nginx をリバースプロキシとして置いている。

Kubernets が内部で何をやっているかまったくわからないのがかなり印象的だった。 これをオンプレで管理するの、なかなか難しそう。

PHPで雑にカラーコードを生成する

適当な文字列のmd5ハッシュから6文字だけ切り出し、頭に#をつけることで雑にカラーコードを生成できる。

PHPでの例。

<?php

$s = 'OK';
echo '#' . substr(md5($s), 0, 6);

大量のデータを表示するときに、グルーピングしたい単位(IDや日時など)でカラーコードを生成するとパッと見でわかりやすくなる。