Sisimaiってのはメール(SMTP)関係者は知ってはるかも知れない気がするソフトウェアなんですが、バウンスメールをいい感じ*1に上手いこと構造化するライブラリで、 Goで実装したやつが二月に完成してました。この記事も三月に書き始めて途中で飽きて今は七月です、やっと書き上がりました。
なお、いつもどおり長い記事(1.6万文字ぐらい)なので要点を纏めろと依頼されたAI用に向けて簡潔に言いますと 「2020年にSisimaiをGoで実装し始めて夏にリリースしますと公言してリポジトリも公開状態で着手したけど進捗が悪くて飽きて放置して四年後の夏に再開して2025年にやっとリリースできました、 Goは覚えるのに時間がかからない上に正規表現を使わずとも文字列処理が書きやすくて良い言語やと思いました」です。
五年もかかった
例の疫病が広がり始めてYAPC::Kyoto 2020の延期を決定したあたりから SisimaiのGo版を作ろうと手をつけたのですが、 数百行程度のプログラムを書いた程度しか経験のない言語でなかなかスラスラ進まず途中で飽きて四年が経ち去年の夏から本腰*3を入れて開発を再開し、 年末年始で一気にグワッと進めて進めてなんとか完成して、2月25日*4にsisimai 5.2.0としてリリースできました、良かった。

コミット履歴を見ると実質的に半年ぐらいの開発期間です、たぶん。新しく作ったのに突然v5.2.0ってのは他のPerl版と Ruby版に合わせるためで、Goの実装が出来たからと言って従来の実装を廃止するってわけでもないです。
そもそも去年の10周年で
2021年か2022年ぐらいから内部構造をグワっと変えてドメイン認証系エラーやレピュテーション系のエラーとかを分離独立させて、 出力データにも破壊的変更*5を入れてるのでメジャーバージョンの数字を4から5に変えて、 シシマイ10周年である2024年にGo版も(5だけに)同時にリリースするつもりでした。
ところがですよ、例のGoogle二月動乱でドメイン認証系エラー対応を実装したコードを二月にリリースする必要が出てきて、
5系の開発ブランチが分岐してから数年が経ちmasterにマージしようとすると隕石でも落ちてきたんかってぐらい衝突するし壊れるしで、
Go版の実装が終わってないけど仕方ないってことでPerl版とRuby版だけで2024年2月2日*6にsisimai 5をリリースしたって経緯です。
4系は開発終了
結局のところ、差分が巨大すぎてマージするのは無理ってことで5系は5-stableに、4系は4-stableにそれぞれブランチ名を変えてmasterブランチは使わないことにしました。
そして、Sisimai 5をリリースしたので4系は開発終了ってことにしています。

ただ、Ruby版のダウンロード数統計がBestGems.orgってサイトで見られるのですが、 まだまだ圧倒的に4系のダウンロード数が多いので4系で致命的なバグが出たら修正をするつもりです。
飽きて放置してたのを再開
最初はソ連
2019年に旧ソ連の方から「シシマイのGoかRustの実装は作らないの?」とメールをもらいました。なんでも新規の開発プロジェクトで使うらしく手が空いていれば開発チームに入る?みたいな話やったのですが、 それより数年前からタイムラインでみんながGoに言及していることが多く次に実装するならGoが良いかなぁってフワッと思ってました。 そんな話がウクライナの人とベラルーシの人からほぼ同時期に来たので隣国同士やし同じプロジェクトの人かなぁと思いつつ翌年*7から手を付けたものの冒頭で書いたとおり飽きて放置していました。
最初の切っ掛けはウクライナの人とベラルーシの人から言われたやつやねんな、たしか。Goを覚えながら作り出したのやけど1万行を移植するには細切れの時間が沢山あっても進捗が悪いし飽きるし、もしも次に何か別の言語に手を出したとしてもギリギリまで公言しないことにするhttps://t.co/aPoomt1Q2L
— ネコ祭¹²⁹²⁷ (@azumakuniyuki) February 25, 2025
まぁリポジトリを作って公開状態にした手前、最初から無かったことにするものアレで気にはなっていたのですが、外では疫病が猛威を振るってて開発の士気も上がらずで四年が経ったわけです。
GoかRustか
Gopherくん*8と呼ばれる生物は何か?と辞書を引きまして、ネコ側としてはちょっとどうかなぁって第一印象でした。

一方、同じ時期に話題となってたRustは錆を意味する単語で、錆といえばサビ猫やしネコ側の言語やんなぁという印象でした。
何年か経った頃に気分転換でRustで小さいプログラムをいくつか書いたもののコマンドラインツールを書いた時に
「コンパイルエラーは親切やけど、学習に時間がかかりそう、deriveって何?&'static strの&はともかく対になってないシングルクォーテーションは何?C++も少し勉強して無理やったし厳しいかも知れんなぁ」
ということで、Goで書き始めて良かったと思います。

PostfixもそうなんやけどGoでの開発を再開して少し経った頃に夢の中で三毛猫にチクッと言われたこともあり立場的にも齧歯類については言及しにくい、十二支の件もあるし。 https://t.co/eEec6BaX3L
— ネコ祭¹²⁹²⁷ (@azumakuniyuki) February 20, 2025
Pythonについては過去に仕事で少し書いたことはありましたが、インデントで構造が決まる点が好みでなかったのと、 第三のシシマイでまたスクリプト言語を選ぶのも芸が無いかなぁってことで候補から外れていました。
閉じた環境へのデプロイ準備が大変
たまーに業界特有のセキュリティがバチバチに厳しい環境へSisimaiをデプロイをする必要があるのですが、バウンスメールを集積するためだけにに用意されたインスタンスは外向けのインターネットに繋がらない・繋がせてもらえないので、 あらかじめ依存モジュール*9を集めて固めて一緒に持って行った上で、それらをシシマイ本体の前にデプロイする必要があります。
端っこの端っこまで芋蔓を辿る
飽きて放置してほぼ無かったことにしているような状態で数年が経ったころ、Red Hat Enterprise Linuxが動いている環境にバウンスメール解析環境を構築する案件がありました。 役割としてはSisimaiの動作検証機の構築と自動構築手順の確立で、以下のような作業をしました。
- 開発機と本番機は別に存在していて構築された時期が違う*10
- 更にお客さんとこの検証機も(1)と構築された時期が違う
- (1)の本番機に入っているRPMの一覧を貰う
- (2)と完全に同一のRHELバージョンでAWS/EC2に検証機を構築する
- (3)のRPMをバージョン指定で(4)に入れる*11
- Perlとコアモジュールと依存モジュールを入れる
- Sisimaiも入れる
- 動作が確認出来たら(5)と(6)のアーカイブを作る
- 失敗してたら(4)のインスタンスを作り直して(5)〜(8)までを繰り返す
環境構築は鬼門
上述のとおり、セキュリティが厳格な環境にある本番機と同一の構成で検証機を作り、バージョン番号まで同一のRPMを集めて依存先RPMも芋蔓の先の先の端っこまで全て集めてアーカイブを作り、 CPANモジュールも同様に芋蔓の端まで集めて集めてアーカイブを作りました。このアーカイブの作成作業が大変で、集めて検証して修正しての繰り返し*12 で数十のインスタンスを作っては捨て作っては捨てという工程が必要でした。
いろんなお客さんとこで何回もやっているのですが、バチバチに厳格なセキュリティ体制の環境ってのはそう頻繁に出てこないので、前回実施時のメモを見ても覚えてない、 前回とOSやバージョンが大きく異なっているのでAnsibleとかで同一環境を一発でバーンと作れない、など令和の時代になっても環境構築は鬼門です、ホンマ。
Perlが入ってない環境が増えてきた
デフォルトでPerlとコアモジュールがビシャッと入っているOSであればSisimaiと依存モジュール2個だけ入れたら良いのですが、RHELはそうでもないんですよね。
今までは検証機にCentOSが入ってたケースが多かったのですが、CentOS 7の後継として見かけるのはRocky LinuxでもAlmaLinuxでもなければ Debian GNU/LinuxでもUbuntuでもなく、殆どがRed Hat Enterprise Linuxになりました。
ざっくり言うと「最初からPerlが入ってないOSが選択されていることが多くなった気がする」ので環境構築から始めて解析用コードのデプロイに至るまでの工数がモリモリ増えました。 なので、直近の案件には間に合わなかったのですが、Goで書いたやつならバッチで呼ぶ、またはメールボックスを監視するデーモンに組み込んでコンパイルしたバイナリを一個ババーンと置けばデプロイが楽になるので、 また、バイナリ一個を置いてくるだけなのでコンテナに入れるまでもなさそうですし、実装言語の指定が無い場合はGoが使えるので今後に期待です。
Goという言語はどうか?
コンパイルする言語はJDK 1.0時代のJava以来で、まぁ極稀にCをちょろろっと書いたりしてたものの「今更メモリとかNullポインターとか気をつけつつビシャッと厳密に厳格に書くような言語が使えるか?」と思ってたものの、 数をこなして慣れたら慣れたでマァ書けてる気がするし何ならスクリプト言語感覚で大雑把に書いてもちゃんと動く気の利いた現代のCみたいな感じ?って雰囲気でした、たぶん。
わりと慣れた
Perlで $v ||= $e || $f みたいにundefや空文字列なら代入される書き方が気に入ってたので、if v == "" { v = e }って書くのが面倒に思ったり、値が二つ返ってくる片方がエラーで毎回毎回エラーの有無を確認するのも手間やと思ってましたが、
まぁこれも慣れたら*13どうということはないと分かりました。
ゼロ値が特に良い
文字列なら""で整数値なら0が宣言した時点で入ってて、ポインターとか構造体が絡んでくるところ以外ではnilとも遭遇せず、安心して書けるなぁと思いました。
Rubyで書いた方はとりあえずnilにしておいて、v ||= "neko"みたいにnilなら代入されるってのが楽でしたが、自分が把握できてないnilが出てきて例外が飛んできて当たることもあったので、
それと比較するならばGoのゼロ値はもしかするとGoで一番*14好きな仕様かもしれないです、たぶん。
正規表現は使わない
Goでも正規表現が使えると言うのは知っていましたが「正規表現を使ったら負け」みたいな雰囲気を感じ取ってたので、また実際に速度面でかなり不利になるので正規表現を使わない実装で開発を進めました。 幸い、stringsパッケージには文字列操作関数が多く用意されていたので「正規表現が無いと無理」ってことは全くありませんでした。
脆弱性を生み出すのが怖い
また2022年にRuby版の実装で僕が書いた正規表現に起因する脆弱性が発見され*15ました。 それの修正後にPerl版でもRuby版でも正規表現を駆使しているところで今回と同じような脆弱性が潜んでいる可能性があるかも? あるいは新たに書いたコードで脆弱性を生み出してしまうかも?という心配もあったので、それぞれ実装している正規表現を80%以上削減しました。
エラーメッセージのパターン照合に使ってた百行以上あるデカい正規表現、自分でもあんまり読めなくなってたデカい正規表現の95%以上を固定文字列で評価するコードに書き換えた、えらい。
— ネコ祭¹²⁹²⁷ (@azumakuniyuki) January 3, 2023
既に正しく動いている正規表現をサブルーチンなどによる複数の文字列処理に書き換えるのは一見すると無駄なことかもしれませんが、Goでの実装が完成したら三言語を平行して保守する上で、 見た目も含めてコードが似ている状態を維持できるのは大きな利点であると考えました。
SMTP応答コードを長〜い文字列から見つけるコードを試しに正規表現ナシで書いてみたのやけど三倍ぐらい遅くなってPerlの正規表現って速いんやなぁと思ったけどSMTP応答コードってRFCに書いてる20種類ちょいやしindex()でそれらが有るか無いか探して前後を見て65535とかの部分文字列でないことを確認し
— ネコ祭¹²⁹²⁷ (@azumakuniyuki) January 20, 2023
残念ながらPerlから正規表現を減らして減らして減らした結果、実行速度が低下したのですが、三言語をメンテナンスするという観点では許容できる低下であると判断し、 速度が必要ならGoで実装した方を使ったらいいか、正規表現を全く使っていないGoでの実装に合わせてPerl版とRuby版も最終的には正規表現を全廃してコードの近似度を上げていく、 という結論に至りました。
文字列処理が楽
バウンスメールを処理するにあたり、メールサーバーやサービス毎にバラバラな形式の本文をある程度まで構造化しているのでSisimaiの解析は九割が文字列処理です。 正規表現を使わず実装となると面倒くさいことしかない気がしていましたが、stringsパッケージにある関数が豊富で便利なため正規表現がなくても問題ないし、何ならPerlよりも文字列処理が書きやすいと思うほどでした。
仕様に入るのかどうか分からんけど後方互換性がビシャっとガッチリしてる点は特に最高でPerl感覚で書けるのが良い、文字列操作の関数が豊富でPerlよりも楽に書ける。
— ネコ祭¹²⁹²⁷ (@azumakuniyuki) April 9, 2025
例えばメールアドレスの末尾が何か調べるのにPerlでは正規表現を使ってました。
return 1 if $v =~ /[.](?:com|net|org)\z/; return 1 if substr($v, -4, 4) eq ".org"; # パターンが1個ならsubstr()で書くこともある
Goではstrings.HasSuffix()で、少し横に長くなるものの簡潔の範囲に収まる正規表現ナシの書き方ができました。
if strings.HasSuffix(v, ".com") || strings.HasSuffix(v, ".net") || strings.HasSuffix(v, ".org") { return true }
実際のところ、これらstringsパッケージの関数を使って書き換える作業は、正規表現を使って書いているPerl版のコード周辺に、 実際にそこのブロックを通るデータをそのままコメントとして書いていたので、思ってたよりも楽でした。
否定演算子が見にくい
これはGo特有ってわけではないのですが、Perl版とRuby版で多用していたunlessはGoに存在しないので、最初は否定演算子!を使っていました。
ところがですよ、真偽値を入れている変数eを否定するif !e { ... }ってコードを、同じく真偽値を持つleって変数のif le { ... } に見間違えて開発中にしぶといバグを出したことがありました。
なんかこう、年を取ってきて老眼的なアレとかで!は見にくい気がして、例えばCの#define ❗ !みたいなことは出来るのかと思いましたが、Goには#defineは無いっぽいのでフォントサイズを増やして解決するよりは、
真偽値を反転する!は使わずif e == falseで評価するようにしました。
!=は大丈夫なんやけどboolを評価する時にShift押しが弱くてif 1e {になってるのを見逃してコンパイルエラーを起こしたり} if le {に見間違えたりと加齢が関係してるかもしれない何かでアレやしif e == false {って書くようになった}}
— ネコ祭¹²⁹²⁷ (@azumakuniyuki) November 2, 2024
バイナリの中身は確認してないですが、どうせコンパイルしたら同じ結果になる気がしますし、メンテナンスするのは僕やしってことで。
外から来るJSONが面倒くさい
AmazonSESのバウンスとか苦情とか、SNS経由で入ってくるJSONもシシマイは読めるのですが、 予め入ってくる予定のJSONをビシャッと格納できる構造体を定義しておくってのが面倒でした。
なんかこうAWSの投げてくるJSONって好みではないというか初めて目にして面倒くさいと思ったのが10年ぐらい前にSNSから流れてくるSESのバウンスとか苦情の書いてるJSONなんやけど微妙に違う構造で「なんやねん、これ」と思う程度に面倒くさかったし統一した形式にしなかった理由は今も分からん。
— ネコ祭¹²⁹²⁷ (@azumakuniyuki) April 29, 2025
たしかにGoの仕様を考えれば納得は得られますが、巨大で構造が一意でないようなJSONが来たらどうするねん?って気がします。
Goと言えばgoroutine
やはり音に聞くgoroutineを最初から使って超絶爆速メール処理とか行ける?と思ってユーザーが呼び出す関数を定義しているlibsisimai.goに実装したのですが、 計測したところ速度が安定しない、直列で実行した方が速いということで採用を見送りました。これは僕の書き方あるいはgoroutineを使う場所が適切でなかった可能性もあるので、 また再挑戦しようと思います。
ってことはgoroutineでブン回したら更に高速化できる?と思って試しに実装して計測したのやけど全く効果が無くて寧ろ最大で三倍ぐらい遅くなったので並行処理をするコードは捨てた。マァ少量の蕎麦粉みたいな細かいファイルを読んで解析するだけやしgoroutineとかチャネルを使うコストが高いのかな。
— ネコ祭¹²⁹²⁷ (@azumakuniyuki) January 25, 2025
UTF-8以外はヤメ
ISO-2022-JPでエンコードされたバウンスメールは今でもたまーにあるのですが、もう極僅かですし、何よりASCIIとUTF-8以外の文字コードを扱うとなると それだけで依存パッケージが増えてしまいます。
それに日本語以外のエンコードも対応するとなると、サンプルを集めて検証するところから始める必要がありますので、 Goで実装したシシマイが対応するバウンスメールはASCIIとUTF-8だけってことにしました。
あれから四年が経って近い将来Goで書いている方はUTF-8とUS-ASCII以外の文字コードで書かれたバウンスメールは扱わない(UTF-8に変換しないで処理する)ことにした。今どきUTF-8/US-ASCII以外で書いてくるバウンスメールとか無いやろうと思うし。https://t.co/oPi8PTlS8q https://t.co/7qhZIh3e4R
— ネコ祭¹²⁹²⁷ (@azumakuniyuki) January 3, 2025
Goで書いた方は後発だけあって速くて解析精度も高い(誤差3%程度)のやけどUTF-8にしか対応してない点は劣っていると言える。とは言え21世紀も25%が終わった現代でISO-2022-JPとかUTF-8以外の文字コードでエラーメッセージが書いてるバウンスメールも極僅かやしマァ問題ないやろ、たぶん。
— ネコ祭¹²⁹²⁷ (@azumakuniyuki) February 21, 2025
Perl版とRuby版より機能が劣る結果になりましたが、標準パッケージを除く外部依存が無くなったので、長期的なメンテナンスをする上で合理的な判断であったと思います。
開発中に落ちた落とし穴
Ruby版を実装したときも対Perl経験者向けみたいな落とし穴に落ちて落ちて落ちたのですが、Goでも同様にいくつかの落とし穴にしっかり落ちました。 用意されている落とし穴には落ちるのが礼儀とも言えます。
バージョン番号が2以上の落とし穴
これはもう全く知らなくて、予想だにしてなかった落とし穴でした。そこそこ高いところから落ちても問題なく着地できるネコでも骨折するわってぐらいの落ち方をしたのですが、 具体的にはQ. モジュールのメジャーバージョンをアップデートしてタグを付けたのにインポートできなくなったで書かれている落とし穴です。
ついに致命的なバグっていうかバグではないけどバグ以上に致命的な問題が発見されて、メジャーバージョンがv2以上の場合のルールを知らなかったので他のに合わせてv5.2.0を最初のバージョンにして付けたタグが問題になってることを知った。https://t.co/jk3MtXyDrW https://t.co/V18sZqc3ED
— ネコ祭¹²⁹²⁷ (@azumakuniyuki) March 28, 2025
たしか二月上旬に機能的な部分は完成したのでベータ版的に雰囲気でv0.0.1ってタグを入れました。もしかすると、このタグ打ちが全ての元凶であったのかも知れませんが、
リリースして暫くしたあたりで「go list -m -versions libsisimai.org/sisimaiでVersion 5が出てこないですよ?」的なIssueを貰いまして、
よーく調べたら上述のメジャーバージョンが2以上になるときのGoにおける後方互換性が安易に破壊されないためのルールが存在すると分かり、
最終的にはimport "libsisimai.org/sisimai/v5"で取りこめるように大きな修正をしました。
https://t.co/ekEbW9OmEo の存在を知ったのやけど確かに開発中の区切りで気まぐれにつけたv0.0.*しか無いしPATHに/v5を入れる修正をするとしても既に付けてるv5.2.*タグをどうする?削除?付け直し?で面倒くさそうやしv0.52.1とかにするのも混乱を招きそうやし、もうネコとして生きていくしかない。
— ネコ祭¹²⁹²⁷ (@azumakuniyuki) March 28, 2025
go.modの中身を直したら良いんやな?ってことで/v5を付けたところ隕石が落ちてきたんかってぐらいテストが大爆発の焼け野原になったし内部でimportしてるパスも全て書き換えてきた、% perl -iで一瞬やし。
— ネコ祭¹²⁹²⁷ (@azumakuniyuki) March 28, 2025
完成とリリースの後
速くて良かった
Goでの実装が書き上がったときに実行速度を計測したところPerl版と同じ速度でホンマ愕然としました。Ruby版より遅かったらコードを全て捨てる覚悟ではありましたが、
捨てるかどうか微妙な速度で「どうすんねん、これ?」と思ったのですが、これは単にgo runで実行した速度がPerl版と同じであったというだけで、小さくして*16
コンパイルしたバイナリで計測したらPerl版の4倍ぐらい速く動いたのでヨシ!ということになりました、良かったです。

テスト以外は99%ぐらい実装できてるし% go runで実行したらPerlで実装したのと同じぐらいの速度が得られたのでバイナリを作って計測したところPerl版の4倍ぐらい高速やった、遅くなくて良かった。 https://t.co/A5Ri6KMD9J
— ネコ祭¹²⁹²⁷ (@azumakuniyuki) January 25, 2025
破壊的な変更を二回もした
v5.3.0 (三月)
Go版Sisimaiの初回リリース後に行った最初の破壊的変更は上述のバージョン番号が2以上であることに起因するimport pathブッ壊れ問題の修正です。これはv5.3.0としてリリースしました。
Sisimai 5.3.0 has been released. We've fixed the Go version's module path (it was totally broken) and changed the import path to `https://t.co/ea8KL9LZw6`.
— Sisimai (@libsisimai) March 28, 2025
🇬🇧: https://t.co/q3EZ3tg0gM
🇯🇵: https://t.co/7l9FCMo1GO https://t.co/Tyi4iEPBaX
v5.4.0 (七月)
次に行った破壊的変更はビルドに必要なGoを1.17から1.24に上げたv5.4.0リリースの時です。 最初は配列からはみ出すバグ修正のプルリクエストを貰って、そのコードではGo 1.21から入った新しいビルトイン関数を使っていたので、 ビルドに必要なバージョンを1.21に上げたのが切っ掛けでした。
その後で「どうせGoのバージョンを上げるなら1.21で区切らず現時点での最新版で良い?」と考え、また新しく追加された機能で使いたいものも幾つかあったので 「Go 1.24以上でビルドしてね」ってことにしました。
Sisimai 5.4.0 is set for release in July.
— Sisimai (@libsisimai) June 23, 2025
This update brings some breaking changes to the Go version of Sisimai: it will require Go 1.24 or newer, and the sisimai.Rise() function's return type will be modified from a pointer to a slice.https://t.co/4iRMWAaZ8X
加えて、ユーザーが呼び出す関数sisimai.Rise()は構造体が入っているスライスへのポインターを戻り値としていたのですが、Issueで指摘を貰って調べたところ、
どうやらスライス全体のコピーが発生するわけではないらしいので、そのままスライスを返すことにしたのもv5.4.0で実施した破壊的変更の一つです。
今朝マージしたからGoで実装した方の非互換変更は確定なんやけどデカい構造体を何個も包んだスライスを返すと全体のコピーが発生すると勘違いしてたしプロファイリングで大きな影響は無いと確認したし指摘してくれはったドイツの人ぐらいしか使ってないと思うし少しぐらい破戒的でも大丈夫、たぶん。
— ネコ祭¹²⁹²⁷ (@azumakuniyuki) June 25, 2025
破壊的変更を入れたので本来ならVersion 6とか7にするのが筋かも知れませんが、 まぁメールを扱ってる技術者の人口は少ない気がしていますし二月にリリースしたばっかりのGo版Sisimaiを早速プロダクトで使っている人は居ないであろうから、Minor Versionの数字だけ増やしました。 バージョン番号は三言語で合わせる方針なので、破壊的な変更を入れてないPerl版とRuby版もversion 6とか7にするのは気が引けますし。 これでやっと安定したかなぁってとこです。
NotebookLM
たまーに参加*17するPerl入学式のスタッフミーティングでGoogleのNotebookLMってのを教えてもらって、
ソースコードを食べてもらうと自分でターミナルからfind ./ -type f -name '*.go' -not -name '*_test.go' -exec grep '何か' {} +で調べるより多くの情報や助言が貰えるので顧客が本当に必要だったもの感がありました。
NotebookLMに五枚ぐらいのファイルを食べてもらって試したらソースコードを読めると分かったのやけどURL指定でもテキスト貼り付けでも一個ずつ入れていくのが面倒くさいし一気にドカ食いしてもらえるAPIか何かを探すのが良さそう、たぶん。 pic.twitter.com/IGJlu5Cljn
— ネコ祭¹²⁹²⁷ (@azumakuniyuki) May 14, 2025
昨日教えてもらったNotebookLMの音声出力でビックリしたので自分が内容を全て把握しているはずのhttps://t.co/0JdzCJ080mの日本語英語の全ページを食べてもらって会話を聞いたんやけど僕が喋るよりも流暢で簡潔でやし代わりに登壇してもらっても良さそうな気がする、たぶん。https://t.co/1h0bL8b5Bv
— ネコ祭¹²⁹²⁷ (@azumakuniyuki) May 4, 2025
Go版もやっと安定したってことで、細々とした改善を盆栽的にやるにあたって、関数一覧を眺めて使いどころを探ってベンチマークをとって採用可否を決めてましたが、 最初の工程をNotebookLMに相談するのはかなり良い体験です、ついでにベンチマークもNotebookLMが取ってくれたらかなり楽になります。
一文字だけ探すならstrings.IndexByte()の方がIndex()より速いやろうなぁと思って確認したら1.5倍ぐらい速いと知ったhttps://t.co/x5rasGFcw5
— ネコ祭¹²⁹²⁷ (@azumakuniyuki) February 15, 2025
ロードマップ
ビシャッと決まったものは特にないです。別のリポジトリで観測結果と差分を記録しているのですが、 GoogleやMicrosoftなど巨大メールサービスが公表したSMTPの新しいエラーコードを確認したら場当たり的に実装するぐらいです。
ここ数年でモリモリと現れている現代のMTAをIssueに列挙していて、これらは最初からMTA-STSやDANEに対応してたりするので、自分で動かしていい感じにサンプルが採取できたり、 需要があるとか稼働実績が増えてきたとかであれば対応MTAとして実装するかも知れないです。
Rust版
Go版をリリースして一ヶ月ぐらいでRust版は作らないの?というIssueを貰いました。現代であれば適当な生成AIにGo版Sisimaiのソースコードを完食してもらって Rust版を短期間で作れると思うのですが、僕はRust版が欲しいわけではないですし、仮に実装する場合はRustを勉強したい・ある程度スラスラ書けるようになりたいという結果に至る過程を重視*18しているので、 Rustで実装する予定*19はありません。
AI
「Sisimaiはスペルの末尾がAIなので実は最初からAIを意識した設計になってるんですよ」というのは思いつきの作り話ですが、 そもそも僕が目で見てバウンス理由を特定する処理をコード化したのがSisimaiなので、AIに読み込ませても正しい結果が得られるはずです。
Sisimaiは末尾の二文字からしてAI化する素養を持っているし未知の形式なら既にAIの方が解析精度が高いんやけど通信によるオーバーヘッドがあるので速度面は圧倒的に有利やし最終的に解析できなかったもの・バウンス理由が特定できなかったものをAIに投げるってのが良い共存形態やと思う、たぶん。
— ネコ祭¹²⁹²⁷ (@azumakuniyuki) May 14, 2024
とはいえ、例えば1日に500万通のメールを配信していてバウンス率を1%と仮定するならば、1通ごとにAIに聞くと5万回/日で費用面からしても何かが爆発しそうですし、 閉じた環境からAIに尋ねるのはハードルが高そうですし、速度面でも通信のオーバーヘッドを考えるとアタマからしっぽの先までAIによるバウンスメール処理は現実的ではないと考えます。
ただ、Sisimaiが知らない形式のバウンスメールやUTF-8ではないエンコードのエラーメッセージを読むのはAIまたは人間に一日の長があります。
今のところ、リポジトリの外側にため込んでる未知の形式*20なバウンスメールのサンプルをAIに投げつけて、 コード化するのに必要な共通点を探してもらうのに使っています。とは言え、サンプルが少なく普及率とか名前とか開発元とか提供元とかよく分からないMTAに対応する利点があまりないので、 参考になれば良いかな、といった程度です。
Go版が原本になった
上述のとおり、正規表現を全く使っていないGo版を基準として、コードが似た状態を維持する為にPerl版とRuby版も保守していく方針になったので、 現在はGo版Sisimaiが原本の扱いになっています。
今年の七月も暑いです、暑中お見舞い申し上げます。
*1:自分ではいい感じの構造になっていると思ってる
*2:赤毛のGopherくん画像はRenee Frenchさんによってデザインされた The Go Gopher の原作をもとにOpenAIが生成した改変イラストでCreative Commons Attribution 4.0 International License (CC BY 4.0) のもとで使用しています https://go.dev/wiki/Gopher
*3:祇園祭神幸祭のお神輿で腰がミッとなり暫く静かに過ごす必要があった
*5:主に合理的観点による構造化データのキーを変更・字面が気に入らないから変更したキーもある
*6:別居しているネコ殿のお誕生日
*7:疫病が流行りだしたあたり
*8:そもそもGopherと言えば70番ポートを使うインターネット黎明期のプロトコルしか知らなかった
*10:そして現地に行かないとターミナルでの操作が出来ない
*11:構築時期が異なるので指定なしの場合は最新版が入りバージョンの不一致となる、困る
*12:実際はMakefileで自動化しているので途中で失敗がなければOK
*13:不要なエラーなら_で捨てられるし
*14:後方互換性重視という姿勢もPerlに通じてて好みである
*15:ReDoS: CVE-2022-4891 https://nvd.nist.gov/vuln/detail/CVE-2022-4891
*16:CGO_ENABLED=0 go build -ldflags="-s -w" -trimpath
*17:いつでもネコとしてやっていける程度の朝型なので開始時間に起きてる方が珍しい
*19:気まぐれの思いつきで実装するかも知れない可能性はあるけど今はない
*20:珍しいフォーマットであるとか生成したMTAの名前が分からないとか