2025 年 9 月の出来事
- iPhone 17 Pro を買った
- 冷や汁をよく作って食べた
- 月蝕を見た
- コテンラジオで伊藤野枝のエピソードを聞いた(伊藤野枝は今宿出身)
- Audible で『国宝』を聞き始めた
- Docker イメージの手入れとカテゴリー・タグ検索の実装
- 8 月に引き続いてあまり走れず、月間走行距離は 123km
いろいろあって手元で docker build が通らなくなってしまったので ChatGPT に相談したら Alpine Linux はビルドが遅くなるので Debian Slim に変えた方がよいと言われてガチャガチャやった。BuildKit を使ってキャッシュせよとも言われたけど、逆にビルドにめっちゃ時間かかるようになったので BuildKit は使わずに普通にビルドしてる。 Apple Silicon の Mac で Linux で動かすように linux/arm64 でビルドしてたけどこれが遅い原因だと散々言われた。クロスプラットフォームで BuildKit のキャッシュを使うとめっちゃ遅くなるらしいので逆効果だった(半日以上ビルドしても終わらないてことがざらだった)。
Docker 、これまで Alpine ベースのイメージを使ってたせいで自前でいろいろコンパイルしたりインストールしたりしてたけど、ビルド済みのものをダウンロードしてくる運用はやっぱり楽。 Alpine 使っても最終的なイメージサイズは膨大になってたし、もっと早めに Debian ベースに変えればよかった。
ちなみにビルドが通らなくなったのは tantiny が依存する rayon という Rust のライブラリが Rust 1.8 以降でしか動かなくなったため。 tantiny は Rust 1.77 までしかサポートしてないので Rust のバージョンを 1.77 で固定していたが、このせいでビルドにこけるようになった。なので tantiny をフォークして rayon と rayon-core のバージョンを古いバージョンに固定した。
tantiny および tantivy は楽に運用できる非常に優れた全文検索ライブラリだと思うけど、 tantiny のメンテナーの人が仕事で使わなくなったそうでメンテナンスされてないのが悲しい。自分でできるならやりたいが、職業プログラマーではないし Rust わからんちんなのでどうしたもんか…。
バッチ処理を動かしている Docker コンテナ含めてすべての環境を Ruby 3 で動かせるようになった。以下の点に難儀した。
DATABASE_URL を環境変数で渡すことで DB 接続設定を上書きできなくなってしまった
ついでにキャッシュも効くように修正した。 sinatra-cache がおかしかったのは Haml の挙動が変わって - form_tag としていたところを = form_tag とする必要があるのと同様に、 - cache_fragment を = cache_fragment にする必要があった。再びキャッシュが効くようになって高速になったが、一部 HTML タグが混ざって表示されることがある。 sinatra-cache.gem が依存する sinatra-outputbuffe.gem の方に問題がありそう。この gem は 16 年以上更新されていない。どこかでキャッシュ依存はやめないといけないかもしれない。
Homebrew で入れた SQLite で load_extension() が動かなくなっていた。
どうも以前は --enable-loadable-extensions でビルドできていたらしいが、 Homebrew 全体でオプション指定をできなくする変更が 4 年前にあったみたいだ。
オプション指定できるのは UX として良くない(ビルド済みのバイナリをダウンロードできない)し、オプション指定を Homebrew のチームでテストしていないからだそう。
TF-IDF で関連記事を表示する機能は SQLite の拡張に依存していたので、最近 Homebrew で入れた SQLite だと機能が動かなくなってしまった(エラーが発生する)。
対策としては自分で SQLite をビルドするしかない。 Mac でそれやるのは面倒なのでこういうのは全部 Docker でやることにした。
Homebrew のウリはビルド済みのバイナリをサクッとダウンロードできてローカルでビルド不要なところではあると思うが、ちょっと凝ったことをしようとするとソースコードをダウンロードしてきて手元でビルドしないといけなくなってしまった。依存パッケージをまとめてインストールできてたことも便利だったんだけど、カスタムインストールをしたいときは昔みたいに依存関係を自分で調べて都度都度インストールしていかないといけなくなった。開発チームの考えもわからなくはないがちょっと残念だ。
ちなみに Homebrew のリポジトリ( homebrew-core )の過去のコミットログを調べるのがめっちゃ大変だった。 tig Formula/sqlite.rb しても 3 分くらい反応がなかった。 git log -Sextension Formula/sqlite.rb してそれっぽいコミットのハッシュ値を見つけて GitHub で検索して何とか上記の Pull Request と Issue に辿り着いた。超巨大なプロジェクトのソースコード管理は Git でやると大変そうだ。
Rust 製の全文検索システム Tantivy を Ruby から使える Tantiny を導入したことを書いた。
結構手軽に使えるのだがやはり日本語のトークナイズ(形態素解析)ができないのでいまいちなところがあった。 Tantivy には lindera-tantivy というものがあって、 Lindera は kuromoji のポートなので、これを使うと日本語や中国語、韓国語の形態素解析ができる。 Tantiny に導入できないか試してみたが、自分の Rust 力では到底無理だった。
ちなみに関連記事の表示でも日本語の形態素解析は行っている。
MeCab に neologd/mecab-ipadic-neologd を組み合わせてナウな日本語に対応させつつ形態素解析している。
この仕組みを作ってトークナイズは Ruby で自前で行い、 Tantiny および Tantivy にはトークナイズ済みの配列を食わせるだけにした( Tantiny はトークナイズ済みのテキストを受け付けることもできる)。トークナイズを自前で行うことで辞書ファイルで拾いきれないような固有名詞もカバーできる。例えば 山と道 なんかは MeCab と mecab-ipadic-neologd にトークナイズさせると 山 と 道 に分割されてしまう。自前のトークナイザーで単語として認識させていている。おかげで「山と道」をちゃんと検索できるようになっている。
なお、自前のトークナイザーはこんなコードになっている。
class Tokenizer
attr_reader :text
class << self
def run(text)
self.new(text).tokenize
end
end
def initialize(text)
@text = text
end
def cleansed_text
@cleansed_ ||= text.
gsub(/<.+?>/, '').
gsub(/!?\[(.+)?\].+?\)/, '\1').
gsub(%r{(?:```|<code>)(.+?)(?:```|</code>)}m, '\1')
end
def words_to_ignore
@words_to_ignore ||= %w[
これ こと とき よう そう やつ とこ ところ 用 もの はず みたい たち いま 後 確か 中 気 方
頃 上 先 点 前 一 内 lt gt ここ なか どこ まま わけ ため 的 それ あと
]
end
def preserved_words
@preserved_words ||= %w[
山と道 ハイキング 縦走 散歩 プログラミング はてブ 鐘撞山 散財 はてなブックマーク はてな
]
end
def nm
require 'natto'
@nm ||= Natto::MeCab.new
end
def words
@words ||= []
end
def tokenize
preserved_words.each do |word|
words << word if cleansed_text.match?(word)
end
nm.parse(cleansed_text) do |n|
next unless n.feature.match?(/名詞/)
next if n.feature.match?(/(サ変接続|数)/)
next if n.surface.match?(/\A([a-z][0-9]|\p{hiragana}|\p{katakana})\Z/i)
next if words_to_ignore.include?(n.surface)
words << n.surface
end
words
end
endpreserved_words が手製の辞書だ。 はてな や はてブ も辞書登録しておかないと MeCab だとバラバラに分割されてしまって検索できなかった。
難点としては記事更新後に自動でインデックスの更新が行われず、 cron によるバッチ処理でインデックス更新を行っている[1]。なので検索インデックスにデータが反映されるまでにタイムラグがある。 Tantiny でやれれば記事作成・更新時のコールバックとして処理できるのでリアルタイムに変更を検索インデックスに反映させることができるが、個人の日記なのでタイムラグありでも大きな問題にはならない。
本当は Tantiny で lindera-tantivy を使えるようにして Pull Request がカッチョイイのだが、とりあえずは自分は目的が達成できたので満足してしまった。 5 年くらい前から Rust 勉強したいと思っているが、いつまでも経っても Rust を書けるようにはならない。
[1]: mecab-ipadic-neologd を VPS 上でインストールできず(めっちゃメモリを使う)、手元の Mac で Docker コンテナ化して Docker Hub 経由でコンテナイメージを Pull して VPS 上で Docker 経由で動かしている(その辺について書いてる記事: ブログのコンテナ化を試みたけどやめた)
この記事は CircleCI Advent Calendar 2018 19 日目の記事ですが間に合わず一日遅れて書いております。すんません 🙇🏻
CircleCI を使った Rails アプリのデプロイフローみたいな話を書こうかなと思ったのですが、すでに他の方が書いてる内容とかぶりそうだし、自分自身ブログに過去何回も書いた話なんで今回はエモ方面の話を書くことにします。技術的な情報はないのでそっち方面を期待している方はすんません。
いまの職場で働き始めて 1 年半なんですが、当初は CI はなく、テストコードもありませんでした。いまはそこで当たり前のように CI が回り、テストのカバレッジもまぁまぁ高く、デプロイは CircleCI 経由でじゃんじゃん行われるような状況となっております。新しく会社に入った人も GitHub の Organization に入ってもらえたらその瞬間から deploy 実行できます。具体的な話は昔書いてますのでよかったらご覧下さい。
8 年くらい前の自分はどうやったら CI だとか自動デプロイだとかできるようになるのか皆目見当が付きませんでした。いま 8 年前の自分と同じような状況にいる人(回りにテストを書く習慣を持つ人がいない人、 CI 動かすためにどうすればよいかわからない人)に何か言いたいと思い筆をとりました。
まずは何はなくとも頑張って一つテストケースを書いてみましょう。最初からカバレッジ 100% とか目指さなくてもよいです。どれか一つ、テストが書きやすそうなコードを見つけてテストを書き、ローカルで実行してテストがパスするのを確認しましょう。テストファーストとかも最初から目指さなくてよいです。
手元でテストが通ることを確認したら、 CI 環境でもテストを実行できるようにしましょう。
昔は Jenkins しか選択肢がなく、 Jenkins が動く環境をセットアップする(サーバーを調達する、 VPS を借りてもらう、などなど)に社内調整が必要でしたが、 CircleCI ならプライベートリポジトリでも 1 プロセスなら無料で使えますので社内調整が非常に楽です(外部にコード出してはダメな職場だと厳しいですね…)。
最初にプロジェクトを追加して言語を選ぶと設定ファイルが自動生成されるので、それをコピペして .circleci/config.yml として保存し、リポジトリにコミットするだけでとりあえずビルドが実行されるようになります。
昔は難しかった CI 環境構築のうち、お金の問題、設定の難しさの問題を CircleCI は解決してくれます。あとはあなたが頑張るだけです。
CircleCI ならビルド終了ごとに結果を Slack などチャットシステムに通知させることができます。まずはテストケースが一つでもよいのでリポジトリへの push をトリガーにビルドが実行されたら結果を Slack に通知してみましょう。


リポジトリに GitHub を使っているなら Pull Request にビルド結果が表示されるようになるはずです。

これらで「なんかようわからんけどやっとる感」を出していきましょう。
そして過去のコードのことは一旦無視して、あなたが新しく追加する部分に関してはテストコードをセットで書くようにしていきましょう。あなたがコードレビューを依頼するときには必ずテストがグリーンな状態で依頼するようにするのです。
そうこうしているうちに他の人が出した Pull Request でテストが失敗するケースが発生します。 Slack の #circleci チャンネルに赤色の Failure 通知が届き社内が騒然とするかもしれません。しかしこれはチャンスです。
「よかった、これでバグが未然に防げましたね」
あなたのこの一言でテストや CI がもたらす開発効率の向上がチームの皆さんに伝わるはずです。こうなったらもう一押しです。あなたがテストと CI の伝道師になりましょう。テストを書くことが当たり前になってきたら、 CircleCI からの deploy や定型処理を CircleCI でやらせるような使い方にチャレンジしていきましょう。どんどん周囲を巻き込んで、 CI 文化を定着させていって下さい。
何はともあれ、最初は一つのテストコードを書くことから始まります。変更に強いコードを書いてじゃんじゃん deploy し、じゃんじゃん Money making していきましょう🤑
職業プログラマーになって 8 年の間に随分とジョブホッピングを繰り返してきたが、どこの会社でも評価が良くない。上司(非技術者であったことがほとんど)からは全く評価されない。なので在職時に昇給したということはほぼない。ペパボ時代にエンジニア評価制度が導入されてシニアエンジニア1になったときにががっと給料が上がったことはあったけど、あれは直属の上司ではなく技術責任者から評価されるという仕組みだったのでイレギュラーケースといえると思う。その後は転職時に増えた以外では全然給料が増えたことはない。据え置きが続いたりむしろ下がったことすらある。
しかし一度退職すると、同じチームの同僚や隣のチームの偉い人とかから良く思われていたことが判明することが多い。戻ってきてほしいとか、いなくなって困ってるとか。当然社交辞令の部分もあると思うが、全く役に立たないクソ野郎だったらそんなことを社交辞令で言われることもないはずなんで、ある程度は評価されていたといえるんだと思う。
なぜ在職中に上司から評価されないのか考えると、当時の上司がやってほしいと思ってることをやらないからだと思う。自分はチーム全体で効率が良くなるようにツールをあれこれしたり bot でガチャガチャやったりとか( Developer Productivity の向上)が好きなんで、自分の動きは会社がやりたいこと、上司がやらせたいこと(売り上げが増える何か、アプリのダウンロード数が増える何か)に直接寄与していないよう見える。
前職や現職で新しくプロジェクトが動き始めるときに何は無くともユニットテストを実行できる仕組みと Docker や CI 環境の構築をガガっとやってて、誰もがテスト書いて Pull Request 出して CI パスしてからレビュー依頼してデプロイも確認環境には自動でじゃんじゃんデプロイされて( Continuous Delivery )、誰も(新人やプログラマーでない人含む)が簡単に本番にもデプロイできる仕組みを作ったという自負がある。こういうのは直接売り上げは増やさないけど開発サイクルを早めたりチームの生産性を高めたりしていると思うが、1秒でも早く依頼したソフトウェアを納品してほしい人からはなんか勝手なことをやってるように見えるし、それが当たり前になると特にありがたみを感じられない種類のものなので評価の対象となりにくい。問題が起こって仕組みが使えなくなったときに初めてありがたみがわかる。なので在職中には評価されない。少なくとも上司からは。
加えて、自分には理想主義的なところがあって、訳の分からない指示が来たときに「そもそもそれいまやることですか」とか「会社がやるべきなのは違うことじゃないですか」と言ってしまう。そもそも論を言うので、ミーティングに参加していても荒れたり脱線してしまったりして「こいつ今これ言うのかよ…」みたいな冷たい視線を浴びることがよくある。場をかき乱す発言をするので「こいつはわかっていない」という烙印を押されてしまう。
評価が良くないと、長く同じ会社に留まり続けるメリットが得られず転職を繰り返してしまう。正直なところ転職は面接を受けに行ったり、給与交渉をしたり、退職を打診したり、仲の良い同僚と別れたり、有給がリセットされたり、人間関係を作り直したり、健康診断の履歴が失われたり、ローンを組むとき不利になったりでしんどいことが多い。できる限り同じ職場で働き続けることがハッピーだろうなぁとは思うんだけど、会社から評価されないのは不満が溜まるし、自分のような人間(間接的にだが結構会社の役に立ってる)が評価されないのはその組織にとっても良くないと思えて転職するという選択をしてしまう。良くない。会社が自分の評価を良くせざるを得ない状況に持ち込めるくらいに腕力(プログラミング能力)や胆力(度胸)をつけなければならないんだろうなぁとは思ってる。
※ なおこの記事は退職エントリーではありません。
今にして思うと当時の自分は全くシニアではなくジュニアだった https://portalshit.net/2018/10/02/we-should-hire-junior-engineers ↩